[MSPORTS] When the LPT port number gets changed, update the PortName and FriendlyName...
[reactos.git] / dll / win32 / msports / parallel.c
index 45b6bbc..b06fd9e 100644 (file)
@@ -8,7 +8,343 @@
 
 #include "precomp.h"
 
-WINE_DEFAULT_DEBUG_CHANNEL(msports);
+
+typedef struct _PORT_DATA
+{
+    HDEVINFO DeviceInfoSet;
+    PSP_DEVINFO_DATA DeviceInfoData;
+
+    WCHAR szPortName[16];
+    DWORD dwPortNumber;
+    DWORD dwFilterResourceMethod;
+    DWORD dwLegacy;
+
+} PORT_DATA, *PPORT_DATA;
+
+
+static
+VOID
+GetUsedPorts(
+    PDWORD pPortMap)
+{
+    WCHAR szDeviceName[64];
+    WCHAR szDosDeviceName[64];
+    DWORD dwIndex, dwType, dwPortNumber;
+    DWORD dwDeviceNameSize, dwDosDeviceNameSize;
+    PWSTR ptr;
+    HKEY hKey;
+    DWORD dwError;
+
+    *pPortMap = 0;
+
+    dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                            L"Hardware\\DeviceMap\\PARALLEL PORTS",
+                            0,
+                            KEY_READ,
+                            &hKey);
+    if (dwError == ERROR_SUCCESS)
+    {
+        for (dwIndex = 0; ; dwIndex++)
+        {
+            dwDeviceNameSize = ARRAYSIZE(szDeviceName);
+            dwDosDeviceNameSize = sizeof(szDosDeviceName);
+            dwError = RegEnumValueW(hKey,
+                                    dwIndex,
+                                    szDeviceName,
+                                    &dwDeviceNameSize,
+                                    NULL,
+                                    &dwType,
+                                    (LPBYTE)szDosDeviceName,
+                                    &dwDosDeviceNameSize);
+            if (dwError != ERROR_SUCCESS)
+                break;
+
+            if (dwType == REG_SZ)
+            {
+                TRACE("%S --> %S\n", szDeviceName, szDosDeviceName);
+                if (_wcsnicmp(szDosDeviceName, L"\\DosDevices\\LPT", wcslen(L"\\DosDevices\\LPT")) == 0)
+                {
+                     ptr = szDosDeviceName + wcslen(L"\\DosDevices\\LPT");
+                     if (wcschr(ptr, L'.') == NULL)
+                     {
+                         TRACE("Device number %S\n", ptr);
+                         dwPortNumber = wcstoul(ptr, NULL, 10);
+                         if (dwPortNumber != 0)
+                         {
+                             *pPortMap |=(1 << dwPortNumber);
+                         }
+                     }
+                }
+            }
+        }
+
+        RegCloseKey(hKey);
+    }
+
+    TRACE("Done\n");
+}
+
+
+static
+VOID
+ReadPortSettings(
+    PPORT_DATA pPortData)
+{
+    DWORD dwSize;
+    HKEY hKey;
+    DWORD dwError;
+
+    TRACE("ReadPortSettings(%p)\n", pPortData);
+
+    pPortData->dwFilterResourceMethod = 1; /* Never use an interrupt */
+    pPortData->dwLegacy = 0;               /* Disabled */
+    pPortData->dwPortNumber = 0;           /* Unknown */
+
+    hKey = SetupDiOpenDevRegKey(pPortData->DeviceInfoSet,
+                                pPortData->DeviceInfoData,
+                                DICS_FLAG_GLOBAL,
+                                0,
+                                DIREG_DEV,
+                                KEY_READ);
+    if (hKey != INVALID_HANDLE_VALUE)
+    {
+        dwSize = sizeof(pPortData->szPortName);
+        dwError = RegQueryValueExW(hKey,
+                                   L"PortName",
+                                   NULL,
+                                   NULL,
+                                  (PBYTE)pPortData->szPortName,
+                                  &dwSize);
+        if (dwError != ERROR_SUCCESS)
+        {
+            ERR("RegQueryValueExW failed (Error %lu)\n", dwError);
+        }
+
+        dwSize = sizeof(pPortData->dwFilterResourceMethod);
+        dwError = RegQueryValueExW(hKey,
+                                   L"FilterResourceMethod",
+                                   NULL,
+                                   NULL,
+                                   (PBYTE)&pPortData->dwFilterResourceMethod,
+                                   &dwSize);
+        if (dwError != ERROR_SUCCESS)
+        {
+            ERR("RegQueryValueExW failed (Error %lu)\n", dwError);
+        }
+
+        RegCloseKey(hKey);
+    }
+
+    dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                            L"SYSTEM\\CurrentControlSet\\Services\\Parport\\Parameters",
+                            0,
+                            KEY_READ,
+                            &hKey);
+    if (dwError == ERROR_SUCCESS)
+    {
+        dwSize = sizeof(pPortData->dwLegacy);
+        dwError = RegQueryValueExW(hKey,
+                                   L"ParEnableLegacyZip",
+                                   NULL,
+                                   NULL,
+                                   (PBYTE)&pPortData->dwLegacy,
+                                   &dwSize);
+        if (dwError != ERROR_SUCCESS)
+        {
+            ERR("RegQueryValueExW failed (Error %lu)\n", dwError);
+        }
+
+        RegCloseKey(hKey);
+    }
+}
+
+
+static
+DWORD
+ChangePortNumber(
+    _In_ PPORT_DATA pPortData,
+    _In_ DWORD dwNewPortNumber)
+{
+    WCHAR szDeviceDescription[256];
+    WCHAR szFriendlyName[256];
+    WCHAR szNewPortName[16];
+    HKEY hDeviceKey = INVALID_HANDLE_VALUE;
+    DWORD dwError;
+
+    /* We are done if old and new port number are the same */
+    if (pPortData->dwPortNumber == dwNewPortNumber)
+        return ERROR_SUCCESS;
+
+    /* Build the new port name */
+    swprintf(szNewPortName, L"LPT%lu", dwNewPortNumber);
+
+    /* Open the devices hardware key */
+    hDeviceKey = SetupDiOpenDevRegKey(pPortData->DeviceInfoSet,
+                                      pPortData->DeviceInfoData,
+                                      DICS_FLAG_GLOBAL,
+                                      0,
+                                      DIREG_DEV,
+                                      KEY_READ | KEY_WRITE);
+    if (hDeviceKey == INVALID_HANDLE_VALUE)
+    {
+        return GetLastError();
+    }
+
+    /* Set the new 'PortName' value */
+    dwError = RegSetValueExW(hDeviceKey,
+                             L"PortName",
+                             0,
+                             REG_SZ,
+                             (LPBYTE)szNewPortName,
+                             (wcslen(szNewPortName) + 1) * sizeof(WCHAR));
+    if (dwError != ERROR_SUCCESS)
+        goto done;
+
+    /* Save the new port name and number */
+    wcscpy(pPortData->szPortName, szNewPortName);
+    pPortData->dwPortNumber = dwNewPortNumber;
+
+    /* Get the device description... */
+    if (SetupDiGetDeviceRegistryProperty(pPortData->DeviceInfoSet,
+                                         pPortData->DeviceInfoData,
+                                         SPDRP_DEVICEDESC,
+                                         NULL,
+                                         (PBYTE)szDeviceDescription,
+                                         sizeof(szDeviceDescription),
+                                         NULL))
+    {
+        /* ... and use it to build a new friendly name */
+        swprintf(szFriendlyName,
+                 L"%s (%s)",
+                 szDeviceDescription,
+                 szNewPortName);
+    }
+    else
+    {
+        /* ... or build a generic friendly name */
+        swprintf(szFriendlyName,
+                 L"Parallel Port (%s)",
+                 szNewPortName);
+    }
+
+    /* Set the friendly name for the device */
+    SetupDiSetDeviceRegistryProperty(pPortData->DeviceInfoSet,
+                                     pPortData->DeviceInfoData,
+                                     SPDRP_FRIENDLYNAME,
+                                     (PBYTE)szFriendlyName,
+                                     (wcslen(szFriendlyName) + 1) * sizeof(WCHAR));
+
+done:
+    if (hDeviceKey != INVALID_HANDLE_VALUE)
+        RegCloseKey(hDeviceKey);
+
+    return dwError;
+}
+
+
+static
+VOID
+WritePortSettings(
+    HWND hwnd,
+    PPORT_DATA pPortData)
+{
+    DWORD dwDisposition;
+    DWORD dwFilterResourceMethod;
+    DWORD dwLegacy;
+    DWORD dwPortNumber;
+    DWORD dwPortMap;
+    HKEY hKey;
+    DWORD dwError;
+
+    TRACE("WritePortSettings(%p)\n", pPortData);
+
+    dwFilterResourceMethod = 1;
+    if (Button_GetCheck(GetDlgItem(hwnd, IDC_TRY_INTERRUPT)) == BST_CHECKED)
+        dwFilterResourceMethod = 0;
+    else if (Button_GetCheck(GetDlgItem(hwnd, IDC_NEVER_INTERRUPT)) == BST_CHECKED)
+        dwFilterResourceMethod = 1;
+    else if (Button_GetCheck(GetDlgItem(hwnd, IDC_ANY_INTERRUPT)) == BST_CHECKED)
+        dwFilterResourceMethod = 2;
+
+    if (dwFilterResourceMethod != pPortData->dwFilterResourceMethod)
+    {
+        hKey = SetupDiOpenDevRegKey(pPortData->DeviceInfoSet,
+                                    pPortData->DeviceInfoData,
+                                    DICS_FLAG_GLOBAL,
+                                    0,
+                                    DIREG_DEV,
+                                    KEY_WRITE);
+        if (hKey != INVALID_HANDLE_VALUE)
+        {
+            dwError = RegSetValueExW(hKey,
+                                     L"FilterResourceMethod",
+                                     0,
+                                     REG_DWORD,
+                                     (PBYTE)&dwFilterResourceMethod,
+                                     sizeof(dwFilterResourceMethod));
+            if (dwError != ERROR_SUCCESS)
+            {
+                ERR("RegSetValueExW failed (Error %lu)\n", dwError);
+            }
+
+            RegCloseKey(hKey);
+            pPortData->dwFilterResourceMethod = dwFilterResourceMethod;
+        }
+    }
+
+    dwLegacy = 0;
+    if (Button_GetCheck(GetDlgItem(hwnd, IDC_PARALLEL_LEGACY)) == BST_CHECKED)
+        dwLegacy = 1;
+
+    if (dwLegacy != pPortData->dwLegacy)
+    {
+        dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
+                                  L"SYSTEM\\CurrentControlSet\\Services\\Parport\\Parameters",
+                                  0,
+                                  NULL,
+                                  REG_OPTION_NON_VOLATILE,
+                                  KEY_WRITE,
+                                  NULL,
+                                  &hKey,
+                                  &dwDisposition);
+        if (dwError == ERROR_SUCCESS)
+        {
+            dwError = RegSetValueExW(hKey,
+                                     L"ParEnableLegacyZip",
+                                     0,
+                                     REG_DWORD,
+                                     (LPBYTE)&dwLegacy,
+                                     sizeof(dwLegacy));
+            RegCloseKey(hKey);
+
+            if (dwError == ERROR_SUCCESS)
+            {
+                FIXME("Notify the driver!\n");
+
+                pPortData->dwLegacy = dwLegacy;
+            }
+        }
+    }
+
+    dwPortNumber = ComboBox_GetCurSel(GetDlgItem(hwnd, IDC_PARALLEL_NAME));
+    if (dwPortNumber != LB_ERR)
+    {
+        dwPortNumber++;
+        if (dwPortNumber != pPortData->dwPortNumber)
+        {
+            GetUsedPorts(&dwPortMap);
+
+            if (dwPortMap & 1 << dwPortNumber)
+            {
+                ERR("Port LPT%lu is already in use!\n", dwPortNumber);
+                return;
+            }
+
+            ChangePortNumber(pPortData,
+                             dwPortNumber);
+        }
+    }
+}
 
 
 static
@@ -17,11 +353,119 @@ OnInitDialog(HWND hwnd,
              WPARAM wParam,
              LPARAM lParam)
 {
-    TRACE("Port_OnInit()\n");
+    WCHAR szBuffer[256];
+    WCHAR szPortInUse[64];
+    PPORT_DATA pPortData;
+    HWND hwndControl;
+    DWORD dwPortMap;
+    UINT i;
+
+    TRACE("OnInitDialog()\n");
+
+    pPortData = (PPORT_DATA)((LPPROPSHEETPAGEW)lParam)->lParam;
+    if (pPortData == NULL)
+    {
+        ERR("pPortData is NULL\n");
+        return FALSE;
+    }
+
+    SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)pPortData);
+
+    /* Read and parse the port settings */
+    ReadPortSettings(pPortData);
+
+    /* Set the 'Filter Resource Method' radiobuttons */
+    hwndControl = GetDlgItem(hwnd,
+                             IDC_TRY_INTERRUPT + pPortData->dwFilterResourceMethod);
+    if (hwndControl)
+        Button_SetCheck(hwndControl, BST_CHECKED);
+
+    /* Disable the 'Enable legacy PNP detection' checkbox */
+    hwndControl = GetDlgItem(hwnd, IDC_PARALLEL_LEGACY);
+    if (hwndControl)
+    {
+        Button_SetCheck(GetDlgItem(hwnd, IDC_PARALLEL_LEGACY),
+                        pPortData->dwLegacy ? BST_CHECKED : BST_UNCHECKED);
+    }
+
+    LoadStringW(hInstance, IDS_PORT_IN_USE, szPortInUse, ARRAYSIZE(szPortInUse));
+
+    /* Fill the 'LPT Port Number' combobox */
+    hwndControl = GetDlgItem(hwnd, IDC_PARALLEL_NAME);
+    if (hwndControl)
+    {
+        GetUsedPorts(&dwPortMap);
+
+        for (i = 1; i < 4; i++)
+        {
+            swprintf(szBuffer, L"LPT%lu", i);
+
+            if ((dwPortMap & (1 << i)) && (pPortData->dwPortNumber != i))
+                wcscat(szBuffer, szPortInUse);
+
+            ComboBox_AddString(hwndControl, szBuffer);
+
+            if (_wcsicmp(pPortData->szPortName, szBuffer) == 0)
+                pPortData->dwPortNumber = i;
+        }
+
+        if (pPortData->dwPortNumber != 0)
+        {
+            ComboBox_SetCurSel(hwndControl, pPortData->dwPortNumber - 1);
+        }
+    }
+
     return TRUE;
 }
 
 
+static
+VOID
+OnNotify(
+    HWND hwnd,
+    WPARAM wParam,
+    LPARAM lParam)
+{
+    PPORT_DATA pPortData;
+
+    TRACE("OnNotify()\n");
+
+    pPortData = (PPORT_DATA)GetWindowLongPtr(hwnd, DWLP_USER);
+    if (pPortData == NULL)
+    {
+        ERR("pPortData is NULL\n");
+        return;
+    }
+
+    if (((LPNMHDR)lParam)->code == (UINT)PSN_APPLY)
+    {
+        TRACE("PSN_APPLY!\n");
+        WritePortSettings(hwnd, pPortData);
+    }
+}
+
+
+static
+VOID
+OnDestroy(
+    HWND hwnd)
+{
+    PPORT_DATA pPortData;
+
+    TRACE("OnDestroy()\n");
+
+    pPortData = (PPORT_DATA)GetWindowLongPtr(hwnd, DWLP_USER);
+    if (pPortData == NULL)
+    {
+        ERR("pPortData is NULL\n");
+        return;
+    }
+
+    HeapFree(GetProcessHeap(), 0, pPortData);
+    SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)NULL);
+}
+
+
 static
 INT_PTR
 CALLBACK
@@ -36,6 +480,14 @@ ParallelSettingsDlgProc(HWND hwnd,
     {
         case WM_INITDIALOG:
             return OnInitDialog(hwnd, wParam, lParam);
+
+        case WM_NOTIFY:
+            OnNotify(hwnd, wParam, lParam);
+            break;
+
+        case WM_DESTROY:
+            OnDestroy(hwnd);
+            break;
     }
 
     return FALSE;
@@ -50,10 +502,23 @@ ParallelPortPropPageProvider(PSP_PROPSHEETPAGE_REQUEST lpPropSheetPageRequest,
 {
     PROPSHEETPAGEW PropSheetPage;
     HPROPSHEETPAGE hPropSheetPage;
+    PPORT_DATA pPortData;
 
     TRACE("ParallelPortPropPageProvider(%p %p %lx)\n",
           lpPropSheetPageRequest, lpfnAddPropSheetPageProc, lParam);
 
+    pPortData = HeapAlloc(GetProcessHeap(),
+                          HEAP_ZERO_MEMORY,
+                          sizeof(PORT_DATA));
+    if (pPortData == NULL)
+    {
+        ERR("Port data allocation failed!\n");
+        return FALSE;
+    }
+
+    pPortData->DeviceInfoSet = lpPropSheetPageRequest->DeviceInfoSet;
+    pPortData->DeviceInfoData = lpPropSheetPageRequest->DeviceInfoData;
+
     if (lpPropSheetPageRequest->PageRequested == SPPSR_ENUM_ADV_DEVICE_PROPERTIES)
     {
         TRACE("SPPSR_ENUM_ADV_DEVICE_PROPERTIES\n");
@@ -63,19 +528,19 @@ ParallelPortPropPageProvider(PSP_PROPSHEETPAGE_REQUEST lpPropSheetPageRequest,
         PropSheetPage.hInstance = hInstance;
         PropSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_PARALLELSETTINGS);
         PropSheetPage.pfnDlgProc = ParallelSettingsDlgProc;
-        PropSheetPage.lParam = 0;
+        PropSheetPage.lParam = (LPARAM)pPortData;
         PropSheetPage.pfnCallback = NULL;
 
         hPropSheetPage = CreatePropertySheetPageW(&PropSheetPage);
         if (hPropSheetPage == NULL)
         {
-            TRACE("CreatePropertySheetPageW() failed!\n");
+            ERR("CreatePropertySheetPageW() failed!\n");
             return FALSE;
         }
 
         if (!(*lpfnAddPropSheetPageProc)(hPropSheetPage, lParam))
         {
-            TRACE("lpfnAddPropSheetPageProc() failed!\n");
+            ERR("lpfnAddPropSheetPageProc() failed!\n");
             DestroyPropertySheetPage(hPropSheetPage);
             return FALSE;
         }