/* * PROJECT: ReactOS Local Port Monitor * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) * PURPOSE: Main functions * COPYRIGHT: Copyright 2015 Colin Finck (colin@reactos.org) */ #include "precomp.h" // Global Variables DWORD cbLocalMonitor; DWORD cbLocalPort; PCWSTR pwszLocalMonitor; PCWSTR pwszLocalPort; // Local Constants static MONITOR2 _MonitorFunctions = { sizeof(MONITOR2), // cbSize LocalmonEnumPorts, // pfnEnumPorts LocalmonOpenPort, // pfnOpenPort NULL, // pfnOpenPortEx LocalmonStartDocPort, // pfnStartDocPort LocalmonWritePort, // pfnWritePort LocalmonReadPort, // pfnReadPort LocalmonEndDocPort, // pfnEndDocPort LocalmonClosePort, // pfnClosePort LocalmonAddPort, // pfnAddPort moved to localui.dll since w2k, but~ LocalmonAddPortEx, // pfnAddPortEx LocalmonConfigurePort, // pfnConfigurePort moved to localui.dll since w2k, but~ LocalmonDeletePort, // pfnDeletePort moved to localui.dll since w2k, but~ LocalmonGetPrinterDataFromPort, // pfnGetPrinterDataFromPort LocalmonSetPortTimeOuts, // pfnSetPortTimeOuts LocalmonXcvOpenPort, // pfnXcvOpenPort LocalmonXcvDataPort, // pfnXcvDataPort LocalmonXcvClosePort, // pfnXcvClosePort LocalmonShutdown, // pfnShutdown NULL, // pfnSendRecvBidiDataFromPort }; /** * @name _IsNEPort * * Checks if the given port name is a virtual Ne port. * A virtual Ne port may appear in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports and can have the formats * Ne00:, Ne01:, Ne-02:, Ne456: * This check is extra picky to not cause false positives (like file name ports starting with "Ne"). * * @param pwszPortName * The port name to check. * * @return * TRUE if this is definitely a virtual Ne port, FALSE if not. */ static __inline BOOL _IsNEPort(PCWSTR pwszPortName) { PCWSTR p = pwszPortName; // First character needs to be 'N' (uppercase or lowercase) if (*p != L'N' && *p != L'n') return FALSE; // Next character needs to be 'E' (uppercase or lowercase) p++; if (*p != L'E' && *p != L'e') return FALSE; // An optional hyphen may follow now. p++; if (*p == L'-') p++; // Now an arbitrary number of digits may follow. while (*p >= L'0' && *p <= L'9') p++; // Finally, the virtual Ne port must be terminated by a colon. if (*p != ':') return FALSE; // If this is the end of the string, we have a virtual Ne port. p++; return (*p == L'\0'); } static void _LoadResources(HINSTANCE hinstDLL) { LoadStringW(hinstDLL, IDS_LOCAL_MONITOR, (PWSTR)&pwszLocalMonitor, 0); cbLocalMonitor = (wcslen(pwszLocalMonitor) + 1) * sizeof(WCHAR); LoadStringW(hinstDLL, IDS_LOCAL_PORT, (PWSTR)&pwszLocalPort, 0); cbLocalPort = (wcslen(pwszLocalPort) + 1) * sizeof(WCHAR); } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(hinstDLL); _LoadResources(hinstDLL); break; } return TRUE; } void WINAPI LocalmonShutdown(HANDLE hMonitor) { PLOCALMON_HANDLE pLocalmon; PLOCALMON_PORT pPort; PLOCALMON_XCV pXcv; PLIST_ENTRY pEntry; TRACE("LocalmonShutdown(%p)\n", hMonitor); pLocalmon = (PLOCALMON_HANDLE)hMonitor; if ( pLocalmon->Sig != SIGLCMMON ) { ERR("LocalmonShutdown : Invalid Monitor Handle\n",hMonitor); return; } // Close all virtual file ports. if (!IsListEmpty(&pLocalmon->FilePorts)) { for (pEntry = pLocalmon->FilePorts.Flink; pEntry != &pLocalmon->FilePorts; pEntry = pEntry->Flink) { pPort = CONTAINING_RECORD(&pLocalmon->FilePorts.Flink, LOCALMON_PORT, Entry); LocalmonClosePort((HANDLE)pPort); } } // Do the same for the open Xcv ports. if (!IsListEmpty(&pLocalmon->XcvHandles)) { for (pEntry = pLocalmon->XcvHandles.Flink; pEntry != &pLocalmon->XcvHandles; pEntry = pEntry->Flink) { pXcv = CONTAINING_RECORD(pEntry, LOCALMON_XCV, Entry); LocalmonXcvClosePort((HANDLE)pXcv); } } // Now close all registry ports, remove them from the list and free their memory. if (!IsListEmpty(&pLocalmon->RegistryPorts)) { for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink) { pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry); if ( LocalmonClosePort((HANDLE)pPort) ) continue; RemoveEntryList(&pPort->Entry); DllFreeSplMem(pPort); } } // Finally clean the LOCALMON_HANDLE structure itself. DeleteCriticalSection(&pLocalmon->Section); DllFreeSplMem(pLocalmon); } PMONITOR2 WINAPI InitializePrintMonitor2(PMONITORINIT pMonitorInit, PHANDLE phMonitor) { DWORD cchMaxPortName; DWORD cchPortName; DWORD dwErrorCode; DWORD dwPortCount; DWORD i; HKEY hKey; PMONITOR2 pReturnValue = NULL; PLOCALMON_HANDLE pLocalmon; PLOCALMON_PORT pPort = NULL; TRACE("InitializePrintMonitor2(%p, %p)\n", pMonitorInit, phMonitor); // Create a new LOCALMON_HANDLE structure. pLocalmon = DllAllocSplMem(sizeof(LOCALMON_HANDLE)); pLocalmon->Sig = SIGLCMMON; InitializeCriticalSection(&pLocalmon->Section); InitializeListHead(&pLocalmon->FilePorts); InitializeListHead(&pLocalmon->RegistryPorts); InitializeListHead(&pLocalmon->XcvHandles); // The Local Spooler Port Monitor doesn't need to care about the given registry key and functions. // Instead it uses a well-known registry key for getting its information about local ports. Open this one. dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", 0, KEY_READ, &hKey); if (dwErrorCode != ERROR_SUCCESS) { ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode); goto Cleanup; } // Get the number of ports and the length of the largest port name. dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwPortCount, &cchMaxPortName, NULL, NULL, NULL); if (dwErrorCode != ERROR_SUCCESS) { ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode); goto Cleanup; } // Loop through all ports. for (i = 0; i < dwPortCount; i++) { // Allocate memory for a new LOCALMON_PORT structure and its name. pPort = DllAllocSplMem(sizeof(LOCALMON_PORT) + (cchMaxPortName + 1) * sizeof(WCHAR)); if (!pPort) { dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); goto Cleanup; } pPort->Sig = SIGLCMPORT; pPort->pLocalmon = pLocalmon; pPort->hFile = INVALID_HANDLE_VALUE; pPort->pwszPortName = (PWSTR)(pPort+1); // Get the port name. cchPortName = cchMaxPortName + 1; dwErrorCode = (DWORD)RegEnumValueW(hKey, i, pPort->pwszPortName, &cchPortName, NULL, NULL, NULL, NULL); if (dwErrorCode != ERROR_SUCCESS) { ERR("RegEnumValueW failed with status %lu!\n", dwErrorCode); goto Cleanup; } // pwszPortName can be one of the following to be valid for this Port Monitor: // COMx: - Physical COM port // LPTx: - Physical LPT port (or redirected one using "net use LPT1 ...") // FILE: - Opens a prompt that asks for an output filename // C:\bla.txt - Redirection into the file "C:\bla.txt" // \\COMPUTERNAME\PrinterName - Redirection to a shared network printer installed as a local port // // We can't detect valid and invalid ones by the name, so we can only exclude empty ports and the virtual "Ne00:", "Ne01:", ... ports. // Skip the invalid ones here. if (!cchPortName || _IsNEPort(pPort->pwszPortName)) { DllFreeSplMem(pPort); pPort = NULL; continue; } // Add it to the list. InsertTailList(&pLocalmon->RegistryPorts, &pPort->Entry); TRACE("InitializePrintMonitor2 Port : %s \n",debugstr_w(pPort->pwszPortName)); // Don't let the cleanup routine free this. pPort = NULL; } // Return our handle and the Print Monitor functions. *phMonitor = (HANDLE)pLocalmon; pReturnValue = &_MonitorFunctions; dwErrorCode = ERROR_SUCCESS; Cleanup: if (pPort) DllFreeSplMem(pPort); SetLastError(dwErrorCode); return pReturnValue; }