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 LocalmonAddPort, // pfnAddPort moved to localui.dll since w2k, but~ 28 LocalmonAddPortEx, // pfnAddPortEx 29 LocalmonConfigurePort, // pfnConfigurePort moved to localui.dll since w2k, but~ 30 LocalmonDeletePort, // pfnDeletePort moved to localui.dll since w2k, but~ 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 PLIST_ENTRY pEntry; 118 119 TRACE("LocalmonShutdown(%p)\n", hMonitor); 120 121 pLocalmon = (PLOCALMON_HANDLE)hMonitor; 122 123 if ( pLocalmon->Sig != SIGLCMMON ) 124 { 125 ERR("LocalmonShutdown : Invalid Monitor Handle\n",hMonitor); 126 return; 127 } 128 129 // Close all virtual file ports. 130 if (!IsListEmpty(&pLocalmon->FilePorts)) 131 { 132 for (pEntry = pLocalmon->FilePorts.Flink; pEntry != &pLocalmon->FilePorts; pEntry = pEntry->Flink) 133 { 134 pPort = CONTAINING_RECORD(&pLocalmon->FilePorts.Flink, LOCALMON_PORT, Entry); 135 LocalmonClosePort((HANDLE)pPort); 136 } 137 } 138 139 // Do the same for the open Xcv ports. 140 if (!IsListEmpty(&pLocalmon->XcvHandles)) 141 { 142 for (pEntry = pLocalmon->XcvHandles.Flink; pEntry != &pLocalmon->XcvHandles; pEntry = pEntry->Flink) 143 { 144 pXcv = CONTAINING_RECORD(pEntry, LOCALMON_XCV, Entry); 145 LocalmonXcvClosePort((HANDLE)pXcv); 146 } 147 } 148 149 // Now close all registry ports, remove them from the list and free their memory. 150 if (!IsListEmpty(&pLocalmon->RegistryPorts)) 151 { 152 for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink) 153 { 154 pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry); 155 if ( LocalmonClosePort((HANDLE)pPort) ) continue; 156 RemoveEntryList(&pPort->Entry); 157 DllFreeSplMem(pPort); 158 } 159 } 160 161 // Finally clean the LOCALMON_HANDLE structure itself. 162 DeleteCriticalSection(&pLocalmon->Section); 163 DllFreeSplMem(pLocalmon); 164 } 165 166 PMONITOR2 WINAPI 167 InitializePrintMonitor2(PMONITORINIT pMonitorInit, PHANDLE phMonitor) 168 { 169 DWORD cchMaxPortName; 170 DWORD cchPortName; 171 DWORD dwErrorCode; 172 DWORD dwPortCount; 173 DWORD i; 174 HKEY hKey; 175 PMONITOR2 pReturnValue = NULL; 176 PLOCALMON_HANDLE pLocalmon; 177 PLOCALMON_PORT pPort = NULL; 178 179 TRACE("InitializePrintMonitor2(%p, %p)\n", pMonitorInit, phMonitor); 180 181 // Create a new LOCALMON_HANDLE structure. 182 pLocalmon = DllAllocSplMem(sizeof(LOCALMON_HANDLE)); 183 pLocalmon->Sig = SIGLCMMON; 184 InitializeCriticalSection(&pLocalmon->Section); 185 InitializeListHead(&pLocalmon->FilePorts); 186 InitializeListHead(&pLocalmon->RegistryPorts); 187 InitializeListHead(&pLocalmon->XcvHandles); 188 189 // The Local Spooler Port Monitor doesn't need to care about the given registry key and functions. 190 // Instead it uses a well-known registry key for getting its information about local ports. Open this one. 191 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", 0, KEY_READ, &hKey); 192 if (dwErrorCode != ERROR_SUCCESS) 193 { 194 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode); 195 goto Cleanup; 196 } 197 198 // Get the number of ports and the length of the largest port name. 199 dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwPortCount, &cchMaxPortName, NULL, NULL, NULL); 200 if (dwErrorCode != ERROR_SUCCESS) 201 { 202 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode); 203 goto Cleanup; 204 } 205 206 // Loop through all ports. 207 for (i = 0; i < dwPortCount; i++) 208 { 209 // Allocate memory for a new LOCALMON_PORT structure and its name. 210 pPort = DllAllocSplMem(sizeof(LOCALMON_PORT) + (cchMaxPortName + 1) * sizeof(WCHAR)); 211 if (!pPort) 212 { 213 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 214 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError()); 215 goto Cleanup; 216 } 217 218 pPort->Sig = SIGLCMPORT; 219 pPort->pLocalmon = pLocalmon; 220 pPort->hFile = INVALID_HANDLE_VALUE; 221 pPort->pwszPortName = (PWSTR)(pPort+1); 222 223 // Get the port name. 224 cchPortName = cchMaxPortName + 1; 225 dwErrorCode = (DWORD)RegEnumValueW(hKey, i, pPort->pwszPortName, &cchPortName, NULL, NULL, NULL, NULL); 226 if (dwErrorCode != ERROR_SUCCESS) 227 { 228 ERR("RegEnumValueW failed with status %lu!\n", dwErrorCode); 229 goto Cleanup; 230 } 231 232 // pwszPortName can be one of the following to be valid for this Port Monitor: 233 // COMx: - Physical COM port 234 // LPTx: - Physical LPT port (or redirected one using "net use LPT1 ...") 235 // FILE: - Opens a prompt that asks for an output filename 236 // C:\bla.txt - Redirection into the file "C:\bla.txt" 237 // \\COMPUTERNAME\PrinterName - Redirection to a shared network printer installed as a local port 238 // 239 // We can't detect valid and invalid ones by the name, so we can only exclude empty ports and the virtual "Ne00:", "Ne01:", ... ports. 240 // Skip the invalid ones here. 241 if (!cchPortName || _IsNEPort(pPort->pwszPortName)) 242 { 243 DllFreeSplMem(pPort); 244 pPort = NULL; 245 continue; 246 } 247 248 // Add it to the list. 249 InsertTailList(&pLocalmon->RegistryPorts, &pPort->Entry); 250 TRACE("InitializePrintMonitor2 Port : %s \n",debugstr_w(pPort->pwszPortName)); 251 252 // Don't let the cleanup routine free this. 253 pPort = NULL; 254 } 255 256 // Return our handle and the Print Monitor functions. 257 *phMonitor = (HANDLE)pLocalmon; 258 pReturnValue = &_MonitorFunctions; 259 dwErrorCode = ERROR_SUCCESS; 260 261 Cleanup: 262 if (pPort) 263 DllFreeSplMem(pPort); 264 265 SetLastError(dwErrorCode); 266 return pReturnValue; 267 } 268