336a03a1740d7139545d2612ff5a9f757532c475
[reactos.git] / reactos / win32ss / printing / monitors / localmon / main.c
1 /*
2 * PROJECT: ReactOS Local Port Monitor
3 * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation
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 continue;
227 }
228
229 // Add it to the list.
230 InsertTailList(&pLocalmon->RegistryPorts, &pPort->Entry);
231
232 // Don't let the cleanup routine free this.
233 pPort = NULL;
234 }
235
236 // Return our handle and the Print Monitor functions.
237 *phMonitor = (HANDLE)pLocalmon;
238 pReturnValue = &_MonitorFunctions;
239 dwErrorCode = ERROR_SUCCESS;
240
241 Cleanup:
242 if (pPort)
243 DllFreeSplMem(pPort);
244
245 SetLastError(dwErrorCode);
246 return pReturnValue;
247 }