1 /* 2 * PROJECT: ReactOS Local Port Monitor 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Main functions 5 * COPYRIGHT: Copyright 2015 Colin Finck (colin@reactos.org) 6 */ 7 8 #include "precomp.h" 9 10 // Global Variables 11 DWORD cbLocalMonitor; 12 DWORD cbLocalPort; 13 PCWSTR pwszLocalMonitor; 14 PCWSTR pwszLocalPort; 15 16 // Local Constants 17 static MONITOR2 _MonitorFunctions = { 18 sizeof(MONITOR2), // cbSize 19 LocalmonEnumPorts, // pfnEnumPorts 20 LocalmonOpenPort, // pfnOpenPort 21 NULL, // pfnOpenPortEx 22 LocalmonStartDocPort, // pfnStartDocPort 23 LocalmonWritePort, // pfnWritePort 24 LocalmonReadPort, // pfnReadPort 25 LocalmonEndDocPort, // pfnEndDocPort 26 LocalmonClosePort, // pfnClosePort 27 NULL, // pfnAddPort 28 NULL, // pfnAddPortEx 29 NULL, // pfnConfigurePort 30 NULL, // pfnDeletePort 31 LocalmonGetPrinterDataFromPort, // pfnGetPrinterDataFromPort 32 LocalmonSetPortTimeOuts, // pfnSetPortTimeOuts 33 LocalmonXcvOpenPort, // pfnXcvOpenPort 34 LocalmonXcvDataPort, // pfnXcvDataPort 35 LocalmonXcvClosePort, // pfnXcvClosePort 36 LocalmonShutdown, // pfnShutdown 37 NULL, // pfnSendRecvBidiDataFromPort 38 }; 39 40 41 /** 42 * @name _IsNEPort 43 * 44 * Checks if the given port name is a virtual Ne port. 45 * A virtual Ne port may appear in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports and can have the formats 46 * Ne00:, Ne01:, Ne-02:, Ne456: 47 * This check is extra picky to not cause false positives (like file name ports starting with "Ne"). 48 * 49 * @param pwszPortName 50 * The port name to check. 51 * 52 * @return 53 * TRUE if this is definitely a virtual Ne port, FALSE if not. 54 */ 55 static __inline BOOL 56 _IsNEPort(PCWSTR pwszPortName) 57 { 58 PCWSTR p = pwszPortName; 59 60 // First character needs to be 'N' (uppercase or lowercase) 61 if (*p != L'N' && *p != L'n') 62 return FALSE; 63 64 // Next character needs to be 'E' (uppercase or lowercase) 65 p++; 66 if (*p != L'E' && *p != L'e') 67 return FALSE; 68 69 // An optional hyphen may follow now. 70 p++; 71 if (*p == L'-') 72 p++; 73 74 // Now an arbitrary number of digits may follow. 75 while (*p >= L'0' && *p <= L'9') 76 p++; 77 78 // Finally, the virtual Ne port must be terminated by a colon. 79 if (*p != ':') 80 return FALSE; 81 82 // If this is the end of the string, we have a virtual Ne port. 83 p++; 84 return (*p == L'\0'); 85 } 86 87 static void 88 _LoadResources(HINSTANCE hinstDLL) 89 { 90 LoadStringW(hinstDLL, IDS_LOCAL_MONITOR, (PWSTR)&pwszLocalMonitor, 0); 91 cbLocalMonitor = (wcslen(pwszLocalMonitor) + 1) * sizeof(WCHAR); 92 93 LoadStringW(hinstDLL, IDS_LOCAL_PORT, (PWSTR)&pwszLocalPort, 0); 94 cbLocalPort = (wcslen(pwszLocalPort) + 1) * sizeof(WCHAR); 95 } 96 97 BOOL WINAPI 98 DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 99 { 100 switch (fdwReason) 101 { 102 case DLL_PROCESS_ATTACH: 103 DisableThreadLibraryCalls(hinstDLL); 104 _LoadResources(hinstDLL); 105 break; 106 } 107 108 return TRUE; 109 } 110 111 void WINAPI 112 LocalmonShutdown(HANDLE hMonitor) 113 { 114 PLOCALMON_HANDLE pLocalmon; 115 PLOCALMON_PORT pPort; 116 PLOCALMON_XCV pXcv; 117 118 TRACE("LocalmonShutdown(%p)\n", hMonitor); 119 120 pLocalmon = (PLOCALMON_HANDLE)hMonitor; 121 122 // Close all virtual file ports. 123 while (!IsListEmpty(&pLocalmon->FilePorts)) 124 { 125 pPort = CONTAINING_RECORD(&pLocalmon->FilePorts.Flink, LOCALMON_PORT, Entry); 126 LocalmonClosePort((HANDLE)pPort); 127 } 128 129 // Do the same for the open Xcv ports. 130 while (!IsListEmpty(&pLocalmon->XcvHandles)) 131 { 132 pXcv = CONTAINING_RECORD(&pLocalmon->XcvHandles.Flink, LOCALMON_XCV, Entry); 133 LocalmonXcvClosePort((HANDLE)pXcv); 134 } 135 136 // Now close all registry ports, remove them from the list and free their memory. 137 while (!IsListEmpty(&pLocalmon->RegistryPorts)) 138 { 139 pPort = CONTAINING_RECORD(&pLocalmon->RegistryPorts.Flink, LOCALMON_PORT, Entry); 140 LocalmonClosePort((HANDLE)pPort); 141 RemoveEntryList(&pPort->Entry); 142 DllFreeSplMem(pPort); 143 } 144 145 // Finally clean the LOCALMON_HANDLE structure itself. 146 DeleteCriticalSection(&pLocalmon->Section); 147 DllFreeSplMem(pLocalmon); 148 } 149 150 PMONITOR2 WINAPI 151 InitializePrintMonitor2(PMONITORINIT pMonitorInit, PHANDLE phMonitor) 152 { 153 DWORD cchMaxPortName; 154 DWORD cchPortName; 155 DWORD dwErrorCode; 156 DWORD dwPortCount; 157 DWORD i; 158 HKEY hKey; 159 PMONITOR2 pReturnValue = NULL; 160 PLOCALMON_HANDLE pLocalmon; 161 PLOCALMON_PORT pPort = NULL; 162 163 TRACE("InitializePrintMonitor2(%p, %p)\n", pMonitorInit, phMonitor); 164 165 // Create a new LOCALMON_HANDLE structure. 166 pLocalmon = DllAllocSplMem(sizeof(LOCALMON_HANDLE)); 167 InitializeCriticalSection(&pLocalmon->Section); 168 InitializeListHead(&pLocalmon->FilePorts); 169 InitializeListHead(&pLocalmon->RegistryPorts); 170 InitializeListHead(&pLocalmon->XcvHandles); 171 172 // The Local Spooler Port Monitor doesn't need to care about the given registry key and functions. 173 // Instead it uses a well-known registry key for getting its information about local ports. Open this one. 174 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", 0, KEY_READ, &hKey); 175 if (dwErrorCode != ERROR_SUCCESS) 176 { 177 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode); 178 goto Cleanup; 179 } 180 181 // Get the number of ports and the length of the largest port name. 182 dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwPortCount, &cchMaxPortName, NULL, NULL, NULL); 183 if (dwErrorCode != ERROR_SUCCESS) 184 { 185 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode); 186 goto Cleanup; 187 } 188 189 // Loop through all ports. 190 for (i = 0; i < dwPortCount; i++) 191 { 192 // Allocate memory for a new LOCALMON_PORT structure and its name. 193 pPort = DllAllocSplMem(sizeof(LOCALMON_PORT) + (cchMaxPortName + 1) * sizeof(WCHAR)); 194 if (!pPort) 195 { 196 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 197 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); 198 goto Cleanup; 199 } 200 201 pPort->pLocalmon = pLocalmon; 202 pPort->hFile = INVALID_HANDLE_VALUE; 203 pPort->pwszPortName = (PWSTR)((PBYTE)pPort + sizeof(LOCALMON_PORT)); 204 205 // Get the port name. 206 cchPortName = cchMaxPortName + 1; 207 dwErrorCode = (DWORD)RegEnumValueW(hKey, i, pPort->pwszPortName, &cchPortName, NULL, NULL, NULL, NULL); 208 if (dwErrorCode != ERROR_SUCCESS) 209 { 210 ERR("RegEnumValueW failed with status %lu!\n", dwErrorCode); 211 goto Cleanup; 212 } 213 214 // pwszPortName can be one of the following to be valid for this Port Monitor: 215 // COMx: - Physical COM port 216 // LPTx: - Physical LPT port (or redirected one using "net use LPT1 ...") 217 // FILE: - Opens a prompt that asks for an output filename 218 // C:\bla.txt - Redirection into the file "C:\bla.txt" 219 // \\COMPUTERNAME\PrinterName - Redirection to a shared network printer installed as a local port 220 // 221 // We can't detect valid and invalid ones by the name, so we can only exclude empty ports and the virtual "Ne00:", "Ne01:", ... ports. 222 // Skip the invalid ones here. 223 if (!cchPortName || _IsNEPort(pPort->pwszPortName)) 224 { 225 DllFreeSplMem(pPort); 226 pPort = NULL; 227 continue; 228 } 229 230 // Add it to the list. 231 InsertTailList(&pLocalmon->RegistryPorts, &pPort->Entry); 232 233 // Don't let the cleanup routine free this. 234 pPort = NULL; 235 } 236 237 // Return our handle and the Print Monitor functions. 238 *phMonitor = (HANDLE)pLocalmon; 239 pReturnValue = &_MonitorFunctions; 240 dwErrorCode = ERROR_SUCCESS; 241 242 Cleanup: 243 if (pPort) 244 DllFreeSplMem(pPort); 245 246 SetLastError(dwErrorCode); 247 return pReturnValue; 248 } 249