1 /*
2 * PROJECT: ReactOS Local Port Monitor
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Functions related to ports
5 * COPYRIGHT: Copyright 2015-2017 Colin Finck (colin@reactos.org)
6 */
7
8 #include "precomp.h"
9
10 // Local Constants
11 static const WCHAR wszNonspooledPrefix[] = L"NONSPOOLED_";
12 static const DWORD cchNonspooledPrefix = _countof(wszNonspooledPrefix) - 1;
13
14 static DWORD dwPortInfo1Offsets[] = {
15 FIELD_OFFSET(PORT_INFO_1W, pName),
16 MAXDWORD
17 };
18
19 static DWORD dwPortInfo2Offsets[] = {
20 FIELD_OFFSET(PORT_INFO_2W, pPortName),
21 FIELD_OFFSET(PORT_INFO_2W, pMonitorName),
22 FIELD_OFFSET(PORT_INFO_2W, pDescription),
23 MAXDWORD
24 };
25
26
27 /**
28 * @name _GetNonspooledPortName
29 *
30 * Prepends "NONSPOOLED_" to a port name without colon.
31 *
32 * @param pwszPortNameWithoutColon
33 * Result of a previous GetPortNameWithoutColon call.
34 *
35 * @param ppwszNonspooledPortName
36 * Pointer to a buffer that will contain the NONSPOOLED port name.
37 * You have to free this buffer using DllFreeSplMem.
38 *
39 * @return
40 * ERROR_SUCCESS if the NONSPOOLED port name was successfully copied into the buffer.
41 * ERROR_NOT_ENOUGH_MEMORY if memory allocation failed.
42 */
43 static __inline DWORD
_GetNonspooledPortName(PCWSTR pwszPortNameWithoutColon,PWSTR * ppwszNonspooledPortName)44 _GetNonspooledPortName(PCWSTR pwszPortNameWithoutColon, PWSTR* ppwszNonspooledPortName)
45 {
46 DWORD cchPortNameWithoutColon;
47
48 cchPortNameWithoutColon = wcslen(pwszPortNameWithoutColon);
49
50 *ppwszNonspooledPortName = DllAllocSplMem((cchNonspooledPrefix + cchPortNameWithoutColon + 1) * sizeof(WCHAR));
51 if (!*ppwszNonspooledPortName)
52 {
53 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
54 return ERROR_NOT_ENOUGH_MEMORY;
55 }
56
57 CopyMemory(*ppwszNonspooledPortName, wszNonspooledPrefix, cchNonspooledPrefix * sizeof(WCHAR));
58 CopyMemory(&(*ppwszNonspooledPortName)[cchNonspooledPrefix], pwszPortNameWithoutColon, (cchPortNameWithoutColon + 1) * sizeof(WCHAR));
59
60 return ERROR_SUCCESS;
61 }
62
63 /**
64 * @name _IsLegacyPort
65 *
66 * Checks if the given port name is a legacy port (COM or LPT).
67 * This check is extra picky to not cause false positives (like file name ports starting with "COM" or "LPT").
68 *
69 * @param pwszPortName
70 * The port name to check.
71 *
72 * @param pwszPortType
73 * L"COM" or L"LPT"
74 *
75 * @return
76 * TRUE if this is definitely the asked legacy port, FALSE if not.
77 */
78 static __inline BOOL
_IsLegacyPort(PCWSTR pwszPortName,PCWSTR pwszPortType)79 _IsLegacyPort(PCWSTR pwszPortName, PCWSTR pwszPortType)
80 {
81 const DWORD cchPortType = 3;
82 PCWSTR p = pwszPortName;
83
84 // The port name must begin with pwszPortType.
85 if (_wcsnicmp(p, pwszPortType, cchPortType) != 0)
86 return FALSE;
87
88 p += cchPortType;
89
90 // Now an arbitrary number of digits may follow.
91 while (*p >= L'0' && *p <= L'9')
92 p++;
93
94 // Finally, the legacy port must be terminated by a colon.
95 if (*p != ':')
96 return FALSE;
97
98 // If this is the end of the string, we have a legacy port.
99 p++;
100 return (*p == L'\0');
101 }
102
103 /**
104 * @name _ClosePortHandles
105 *
106 * Closes a port of any type if it's open.
107 * Removes any saved mapping or existing definition of a NONSPOOLED device mapping.
108 *
109 * @param pPort
110 * The port you want to close.
111 */
112 static void
_ClosePortHandles(PLOCALMON_PORT pPort)113 _ClosePortHandles(PLOCALMON_PORT pPort)
114 {
115 PWSTR pwszNonspooledPortName;
116 PWSTR pwszPortNameWithoutColon;
117
118 // A port is already fully closed if the file handle is invalid.
119 if (pPort->hFile == INVALID_HANDLE_VALUE)
120 return;
121
122 // Close the file handle.
123 CloseHandle(pPort->hFile);
124 pPort->hFile = INVALID_HANDLE_VALUE;
125
126 // A NONSPOOLED port was only created if pwszMapping contains the current port mapping.
127 if (!pPort->pwszMapping)
128 return;
129
130 // Free the information about the current mapping.
131 DllFreeSplStr(pPort->pwszMapping);
132 pPort->pwszMapping = NULL;
133
134 // Finally get the required strings and remove the DOS device definition for the NONSPOOLED port.
135 if (GetPortNameWithoutColon(pPort->pwszPortName, &pwszPortNameWithoutColon) == ERROR_SUCCESS)
136 {
137 if (_GetNonspooledPortName(pwszPortNameWithoutColon, &pwszNonspooledPortName) == ERROR_SUCCESS)
138 {
139 DefineDosDeviceW(DDD_REMOVE_DEFINITION, pwszNonspooledPortName, NULL);
140 DllFreeSplMem(pwszNonspooledPortName);
141 }
142 DllFreeSplMem(pwszPortNameWithoutColon);
143 }
144 }
145
146 /**
147 * @name _CreateNonspooledPort
148 *
149 * Queries the system-wide device definition of the given port.
150 * If such a definition exists, it's a legacy port remapped to a named pipe by the spooler.
151 * In this case, the function creates and opens a NONSPOOLED device definition to the most recent mapping before the current one (usually the physical device).
152 *
153 * @param pPort
154 * Pointer to the LOCALMON_PORT structure of the desired port.
155 *
156 * @return
157 * TRUE if a NONSPOOLED port was successfully created, FALSE otherwise.
158 * A more specific error code can be obtained through GetLastError.
159 * In particular, if the return value is FALSE and GetLastError returns ERROR_SUCCESS, no NONSPOOLED port is needed for this port.
160 */
161 static BOOL
_CreateNonspooledPort(PLOCALMON_PORT pPort)162 _CreateNonspooledPort(PLOCALMON_PORT pPort)
163 {
164 const WCHAR wszLocalSlashes[] = L"\\\\.\\";
165 const DWORD cchLocalSlashes = _countof(wszLocalSlashes) - 1;
166
167 const WCHAR wszSpoolerNamedPipe[] = L"\\Device\\NamedPipe\\Spooler\\";
168 const DWORD cchSpoolerNamedPipe = _countof(wszSpoolerNamedPipe) - 1;
169
170 BOOL bReturnValue = FALSE;
171 DWORD cchPortNameWithoutColon;
172 DWORD dwErrorCode;
173 HANDLE hToken = NULL;
174 PWSTR p;
175 PWSTR pwszDeviceMappings = NULL;
176 PWSTR pwszNonspooledFileName = NULL;
177 PWSTR pwszNonspooledPortName = NULL;
178 PWSTR pwszPipeName = NULL;
179 PWSTR pwszPortNameWithoutColon = NULL;
180
181 // We need the port name without the trailing colon.
182 dwErrorCode = GetPortNameWithoutColon(pPort->pwszPortName, &pwszPortNameWithoutColon);
183 if (dwErrorCode == ERROR_INVALID_PARAMETER)
184 {
185 // This port has no trailing colon, so we also need no NONSPOOLED mapping for it.
186 dwErrorCode = ERROR_SUCCESS;
187 goto Cleanup;
188 }
189 else if (dwErrorCode != ERROR_SUCCESS)
190 {
191 // Another unexpected failure.
192 goto Cleanup;
193 }
194
195 cchPortNameWithoutColon = wcslen(pwszPortNameWithoutColon);
196
197 // The spooler has usually remapped the legacy port to a named pipe of the format in wszSpoolerNamedPipe.
198 // Construct the device name of this pipe.
199 pwszPipeName = DllAllocSplMem((cchSpoolerNamedPipe + cchPortNameWithoutColon + 1) * sizeof(WCHAR));
200 if (!pwszPipeName)
201 {
202 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
203 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
204 goto Cleanup;
205 }
206
207 CopyMemory(pwszPipeName, wszSpoolerNamedPipe, cchSpoolerNamedPipe * sizeof(WCHAR));
208 CopyMemory(&pwszPipeName[cchSpoolerNamedPipe], pwszPortNameWithoutColon, (cchPortNameWithoutColon + 1) * sizeof(WCHAR));
209
210 // QueryDosDeviceW is one of the shitty APIs that gives no information about the required buffer size and wants you to know it by pure magic.
211 // Examples show that a value of MAX_PATH * sizeof(WCHAR) is usually taken here, so we have no other option either.
212 pwszDeviceMappings = DllAllocSplMem(MAX_PATH * sizeof(WCHAR));
213 if (!pwszDeviceMappings)
214 {
215 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
216 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
217 goto Cleanup;
218 }
219
220 // Switch to the SYSTEM context, because we're only interested in creating NONSPOOLED ports for system-wide ports.
221 // User-local ports (like _some_ redirected networked ones) aren't remapped by the spooler and can be opened directly.
222 hToken = RevertToPrinterSelf();
223 if (!hToken)
224 {
225 dwErrorCode = GetLastError();
226 ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode);
227 goto Cleanup;
228 }
229
230 // QueryDosDeviceW returns the current mapping and a list of prior mappings of this legacy port, which is managed as a DOS device in the system.
231 if (!QueryDosDeviceW(pwszPortNameWithoutColon, pwszDeviceMappings, MAX_PATH))
232 {
233 // No system-wide port exists, so we also need no NONSPOOLED mapping.
234 dwErrorCode = ERROR_SUCCESS;
235 goto Cleanup;
236 }
237
238 // Check if this port has already been opened by _CreateNonspooledPort previously.
239 if (pPort->pwszMapping)
240 {
241 // In this case, we just need to do something if the mapping has changed.
242 // Therefore, check if the stored mapping equals the current mapping.
243 if (wcscmp(pPort->pwszMapping, pwszDeviceMappings) == 0)
244 {
245 // We don't need to do anything in this case.
246 dwErrorCode = ERROR_SUCCESS;
247 goto Cleanup;
248 }
249 else
250 {
251 // Close the open file handle and free the memory for pwszMapping before remapping.
252 CloseHandle(pPort->hFile);
253 pPort->hFile = INVALID_HANDLE_VALUE;
254
255 DllFreeSplStr(pPort->pwszMapping);
256 pPort->pwszMapping = NULL;
257 }
258 }
259
260 // The port is usually mapped to the named pipe and this is how we received our data for printing.
261 // What we now need for accessing the actual port is the most recent mapping different from the named pipe.
262 p = pwszDeviceMappings;
263
264 for (;;)
265 {
266 if (!*p)
267 {
268 // We reached the end of the list without finding a mapping.
269 ERR("Can't find a suitable mapping for the port \"%S\"!\n", pPort->pwszPortName);
270 goto Cleanup;
271 }
272
273 if (_wcsicmp(p, pwszPipeName) != 0)
274 break;
275
276 // Advance to the next mapping in the list.
277 p += wcslen(p) + 1;
278 }
279
280 // We now want to create a DOS device "NONSPOOLED_<PortName>" to this mapping, so that we're able to open it through CreateFileW.
281 dwErrorCode = _GetNonspooledPortName(pwszPortNameWithoutColon, &pwszNonspooledPortName);
282 if (dwErrorCode != ERROR_SUCCESS)
283 goto Cleanup;
284
285 // Delete a possibly existing NONSPOOLED device before creating the new one, so we don't stack up device definitions.
286 DefineDosDeviceW(DDD_REMOVE_DEFINITION, pwszNonspooledPortName, NULL);
287
288 if (!DefineDosDeviceW(DDD_RAW_TARGET_PATH, pwszNonspooledPortName, p))
289 {
290 dwErrorCode = GetLastError();
291 ERR("DefineDosDeviceW failed with error %lu!\n", dwErrorCode);
292 goto Cleanup;
293 }
294
295 // This is all we needed to do in SYSTEM context.
296 ImpersonatePrinterClient(hToken);
297 hToken = NULL;
298
299 // Construct the file name to our created device for CreateFileW.
300 pwszNonspooledFileName = DllAllocSplMem((cchLocalSlashes + cchNonspooledPrefix + cchPortNameWithoutColon + 1) * sizeof(WCHAR));
301 if (!pwszNonspooledFileName)
302 {
303 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
304 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
305 goto Cleanup;
306 }
307
308 CopyMemory(pwszNonspooledFileName, wszLocalSlashes, cchLocalSlashes * sizeof(WCHAR));
309 CopyMemory(&pwszNonspooledFileName[cchLocalSlashes], wszNonspooledPrefix, cchNonspooledPrefix * sizeof(WCHAR));
310 CopyMemory(&pwszNonspooledFileName[cchLocalSlashes + cchNonspooledPrefix], pwszPortNameWithoutColon, (cchPortNameWithoutColon + 1) * sizeof(WCHAR));
311
312 // Finally open it for reading and writing.
313 pPort->hFile = CreateFileW(pwszNonspooledFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
314 if (pPort->hFile == INVALID_HANDLE_VALUE)
315 {
316 dwErrorCode = GetLastError();
317 ERR("CreateFileW failed with error %lu!\n", dwErrorCode);
318 goto Cleanup;
319 }
320
321 // Store the current mapping of the port, so that we can check if it has changed.
322 pPort->pwszMapping = AllocSplStr(pwszDeviceMappings);
323 if (!pPort->pwszMapping)
324 {
325 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
326 goto Cleanup;
327 }
328
329 bReturnValue = TRUE;
330 dwErrorCode = ERROR_SUCCESS;
331
332 Cleanup:
333 if (hToken)
334 ImpersonatePrinterClient(hToken);
335
336 if (pwszDeviceMappings)
337 DllFreeSplMem(pwszDeviceMappings);
338
339 if (pwszNonspooledFileName)
340 DllFreeSplMem(pwszNonspooledFileName);
341
342 if (pwszNonspooledPortName)
343 DllFreeSplMem(pwszNonspooledPortName);
344
345 if (pwszPipeName)
346 DllFreeSplMem(pwszPipeName);
347
348 if (pwszPortNameWithoutColon)
349 DllFreeSplMem(pwszPortNameWithoutColon);
350
351 SetLastError(dwErrorCode);
352 return bReturnValue;
353 }
354
355 static PLOCALMON_PORT
_FindPort(PLOCALMON_HANDLE pLocalmon,PCWSTR pwszPortName)356 _FindPort(PLOCALMON_HANDLE pLocalmon, PCWSTR pwszPortName)
357 {
358 PLIST_ENTRY pEntry;
359 PLOCALMON_PORT pPort;
360
361 for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
362 {
363 pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
364
365 if (wcscmp(pPort->pwszPortName, pwszPortName) == 0)
366 return pPort;
367 }
368
369 return NULL;
370 }
371
372 static void
_LocalmonGetPortLevel1(PLOCALMON_PORT pPort,PPORT_INFO_1W * ppPortInfo,PBYTE * ppPortInfoEnd,PDWORD pcbNeeded)373 _LocalmonGetPortLevel1(PLOCALMON_PORT pPort, PPORT_INFO_1W* ppPortInfo, PBYTE* ppPortInfoEnd, PDWORD pcbNeeded)
374 {
375 DWORD cbPortName;
376 PCWSTR pwszStrings[1];
377
378 // Calculate the string lengths.
379 if (!ppPortInfo)
380 {
381 cbPortName = (wcslen(pPort->pwszPortName) + 1) * sizeof(WCHAR);
382
383 *pcbNeeded += sizeof(PORT_INFO_1W) + cbPortName;
384 return;
385 }
386
387 // Set the pName field.
388 pwszStrings[0] = pPort->pwszPortName;
389
390 // Copy the structure and advance to the next one in the output buffer.
391 *ppPortInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPortInfo), dwPortInfo1Offsets, *ppPortInfoEnd);
392 (*ppPortInfo)++;
393 }
394
395 static void
_LocalmonGetPortLevel2(PLOCALMON_PORT pPort,PPORT_INFO_2W * ppPortInfo,PBYTE * ppPortInfoEnd,PDWORD pcbNeeded)396 _LocalmonGetPortLevel2(PLOCALMON_PORT pPort, PPORT_INFO_2W* ppPortInfo, PBYTE* ppPortInfoEnd, PDWORD pcbNeeded)
397 {
398 DWORD cbPortName;
399 PCWSTR pwszStrings[3];
400
401 // Calculate the string lengths.
402 if (!ppPortInfo)
403 {
404 cbPortName = (wcslen(pPort->pwszPortName) + 1) * sizeof(WCHAR);
405
406 *pcbNeeded += sizeof(PORT_INFO_2W) + cbPortName + cbLocalMonitor + cbLocalPort;
407 return;
408 }
409
410 // All local ports are writable and readable.
411 (*ppPortInfo)->fPortType = PORT_TYPE_WRITE | PORT_TYPE_READ;
412 (*ppPortInfo)->Reserved = 0;
413
414 // Set the pPortName field.
415 pwszStrings[0] = pPort->pwszPortName;
416
417 // Set the pMonitorName field.
418 pwszStrings[1] = (PWSTR)pwszLocalMonitor;
419
420 // Set the pDescription field.
421 pwszStrings[2] = (PWSTR)pwszLocalPort;
422
423 // Copy the structure and advance to the next one in the output buffer.
424 *ppPortInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPortInfo), dwPortInfo2Offsets, *ppPortInfoEnd);
425 (*ppPortInfo)++;
426 }
427
428 /**
429 * @name _SetTransmissionRetryTimeout
430 *
431 * Checks if the given port is a physical one and sets the transmission retry timeout in this case using the value from registry.
432 *
433 * @param pPort
434 * The port to operate on.
435 *
436 * @return
437 * TRUE if the given port is a physical one, FALSE otherwise.
438 */
439 static BOOL
_SetTransmissionRetryTimeout(PLOCALMON_PORT pPort)440 _SetTransmissionRetryTimeout(PLOCALMON_PORT pPort)
441 {
442 COMMTIMEOUTS CommTimeouts;
443
444 // Get the timeout from the port.
445 if (!GetCommTimeouts(pPort->hFile, &CommTimeouts))
446 return FALSE;
447
448 // Set the timeout using the value from registry.
449 CommTimeouts.WriteTotalTimeoutConstant = GetLPTTransmissionRetryTimeout() * 1000;
450 SetCommTimeouts(pPort->hFile, &CommTimeouts);
451
452 return TRUE;
453 }
454
455 BOOL WINAPI
LocalmonClosePort(HANDLE hPort)456 LocalmonClosePort(HANDLE hPort)
457 {
458 PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
459
460 TRACE("LocalmonClosePort(%p)\n", hPort);
461
462 // Sanity checks
463 if (!pPort)
464 {
465 SetLastError(ERROR_INVALID_PARAMETER);
466 return FALSE;
467 }
468
469 // Close the file handle, free memory for pwszMapping and delete any NONSPOOLED port.
470 _ClosePortHandles(pPort);
471
472 // Close any open printer handle.
473 if (pPort->hPrinter)
474 {
475 ClosePrinter(pPort->hPrinter);
476 pPort->hPrinter = NULL;
477 }
478
479 // Free virtual FILE: ports which were created in LocalmonOpenPort.
480 if (pPort->PortType == PortType_FILE)
481 {
482 EnterCriticalSection(&pPort->pLocalmon->Section);
483 RemoveEntryList(&pPort->Entry);
484 LeaveCriticalSection(&pPort->pLocalmon->Section);
485 DllFreeSplMem(pPort);
486 }
487
488 SetLastError(ERROR_SUCCESS);
489 return TRUE;
490 }
491
492 BOOL WINAPI
LocalmonEndDocPort(HANDLE hPort)493 LocalmonEndDocPort(HANDLE hPort)
494 {
495 PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
496
497 TRACE("LocalmonEndDocPort(%p)\n", hPort);
498
499 // Sanity checks
500 if (!pPort)
501 {
502 SetLastError(ERROR_INVALID_PARAMETER);
503 return FALSE;
504 }
505
506 // Ending a document requires starting it first :-P
507 if (pPort->bStartedDoc)
508 {
509 // Close all ports opened in StartDocPort.
510 // That is, all but physical LPT ports (opened in OpenPort).
511 if (pPort->PortType != PortType_PhysicalLPT)
512 _ClosePortHandles(pPort);
513
514 // Report our progress.
515 SetJobW(pPort->hPrinter, pPort->dwJobID, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER);
516
517 // We're done with the printer.
518 ClosePrinter(pPort->hPrinter);
519 pPort->hPrinter = NULL;
520
521 // A new document can now be started again.
522 pPort->bStartedDoc = FALSE;
523 }
524
525 SetLastError(ERROR_SUCCESS);
526 return TRUE;
527 }
528
529 BOOL WINAPI
LocalmonEnumPorts(HANDLE hMonitor,PWSTR pName,DWORD Level,PBYTE pPorts,DWORD cbBuf,PDWORD pcbNeeded,PDWORD pcReturned)530 LocalmonEnumPorts(HANDLE hMonitor, PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
531 {
532 DWORD dwErrorCode;
533 PBYTE pPortInfoEnd;
534 PLIST_ENTRY pEntry;
535 PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
536 PLOCALMON_PORT pPort;
537
538 TRACE("LocalmonEnumPorts(%p, %S, %lu, %p, %lu, %p, %p)\n", hMonitor, pName, Level, pPorts, cbBuf, pcbNeeded, pcReturned);
539
540 // Windows Server 2003's Local Port Monitor does absolutely no sanity checks here, not even for the Level parameter.
541 // As we implement a more modern MONITOR2-based Port Monitor, check at least our hMonitor.
542 if (!pLocalmon)
543 {
544 dwErrorCode = ERROR_INVALID_HANDLE;
545 goto Cleanup;
546 }
547
548 // Begin counting.
549 *pcbNeeded = 0;
550 *pcReturned = 0;
551
552 EnterCriticalSection(&pLocalmon->Section);
553
554 // Count the required buffer size and the number of ports.
555 for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
556 {
557 pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
558
559 if (Level == 1)
560 _LocalmonGetPortLevel1(pPort, NULL, NULL, pcbNeeded);
561 else if (Level == 2)
562 _LocalmonGetPortLevel2(pPort, NULL, NULL, pcbNeeded);
563 }
564
565 // Check if the supplied buffer is large enough.
566 if (cbBuf < *pcbNeeded)
567 {
568 LeaveCriticalSection(&pLocalmon->Section);
569 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
570 goto Cleanup;
571 }
572
573 // Copy over the Port information.
574 pPortInfoEnd = &pPorts[*pcbNeeded];
575
576 for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
577 {
578 pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
579
580 if (Level == 1)
581 _LocalmonGetPortLevel1(pPort, (PPORT_INFO_1W*)&pPorts, &pPortInfoEnd, NULL);
582 else if (Level == 2)
583 _LocalmonGetPortLevel2(pPort, (PPORT_INFO_2W*)&pPorts, &pPortInfoEnd, NULL);
584
585 (*pcReturned)++;
586 }
587
588 LeaveCriticalSection(&pLocalmon->Section);
589 dwErrorCode = ERROR_SUCCESS;
590
591 Cleanup:
592 SetLastError(dwErrorCode);
593 return (dwErrorCode == ERROR_SUCCESS);
594 }
595
596 /*
597 * @name LocalmonGetPrinterDataFromPort
598 *
599 * Performs a DeviceIoControl call for the given port.
600 *
601 * @param hPort
602 * The port to operate on.
603 *
604 * @param ControlID
605 * The dwIoControlCode passed to DeviceIoControl. Must not be zero!
606 *
607 * @param pValueName
608 * This parameter is ignored.
609 *
610 * @param lpInBuffer
611 * The lpInBuffer passed to DeviceIoControl.
612 *
613 * @param cbInBuffer
614 * The nInBufferSize passed to DeviceIoControl.
615 *
616 * @param lpOutBuffer
617 * The lpOutBuffer passed to DeviceIoControl.
618 *
619 * @param cbOutBuffer
620 * The nOutBufferSize passed to DeviceIoControl.
621 *
622 * @param lpcbReturned
623 * The lpBytesReturned passed to DeviceIoControl. Must not be zero!
624 *
625 * @return
626 * TRUE if the DeviceIoControl call was successful, FALSE otherwise.
627 * A more specific error code can be obtained through GetLastError.
628 */
629 BOOL WINAPI
LocalmonGetPrinterDataFromPort(HANDLE hPort,DWORD ControlID,PWSTR pValueName,PWSTR lpInBuffer,DWORD cbInBuffer,PWSTR lpOutBuffer,DWORD cbOutBuffer,PDWORD lpcbReturned)630 LocalmonGetPrinterDataFromPort(HANDLE hPort, DWORD ControlID, PWSTR pValueName, PWSTR lpInBuffer, DWORD cbInBuffer, PWSTR lpOutBuffer, DWORD cbOutBuffer, PDWORD lpcbReturned)
631 {
632 BOOL bOpenedPort = FALSE;
633 DWORD dwErrorCode;
634 PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
635
636 TRACE("LocalmonGetPrinterDataFromPort(%p, %lu, %p, %p, %lu, %p, %lu, %p)\n", hPort, ControlID, pValueName, lpInBuffer, cbInBuffer, lpOutBuffer, cbOutBuffer, lpcbReturned);
637
638 // Sanity checks
639 if (!pPort || !ControlID || !lpcbReturned)
640 {
641 dwErrorCode = ERROR_INVALID_PARAMETER;
642 goto Cleanup;
643 }
644
645 // If this is a serial port, a temporary file handle may be opened.
646 if (pPort->PortType == PortType_PhysicalCOM)
647 {
648 if (_CreateNonspooledPort(pPort))
649 {
650 bOpenedPort = TRUE;
651 }
652 else if (GetLastError() != ERROR_SUCCESS)
653 {
654 dwErrorCode = GetLastError();
655 goto Cleanup;
656 }
657 }
658 else if (pPort->hFile == INVALID_HANDLE_VALUE)
659 {
660 // All other port types need to be opened already.
661 dwErrorCode = ERROR_INVALID_PARAMETER;
662 goto Cleanup;
663 }
664
665 // Pass the parameters to DeviceIoControl.
666 if (!DeviceIoControl(pPort->hFile, ControlID, lpInBuffer, cbInBuffer, lpOutBuffer, cbOutBuffer, lpcbReturned, NULL))
667 {
668 dwErrorCode = GetLastError();
669 ERR("DeviceIoControl failed with error %lu!\n", dwErrorCode);
670 goto Cleanup;
671 }
672
673 dwErrorCode = ERROR_SUCCESS;
674
675 Cleanup:
676 if (bOpenedPort)
677 _ClosePortHandles(pPort);
678
679 SetLastError(dwErrorCode);
680 return (dwErrorCode == ERROR_SUCCESS);
681 }
682
683 BOOL WINAPI
LocalmonOpenPort(HANDLE hMonitor,PWSTR pName,PHANDLE pHandle)684 LocalmonOpenPort(HANDLE hMonitor, PWSTR pName, PHANDLE pHandle)
685 {
686 DWORD dwErrorCode;
687 PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
688 PLOCALMON_PORT pPort;
689
690 TRACE("LocalmonOpenPort(%p, %S, %p)\n", hMonitor, pName, pHandle);
691
692 // Sanity checks
693 if (!pLocalmon || !pName || !pHandle)
694 {
695 dwErrorCode = ERROR_INVALID_PARAMETER;
696 goto Cleanup;
697 }
698
699 EnterCriticalSection(&pLocalmon->Section);
700
701 // Check if this is a FILE: port.
702 if (_wcsicmp(pName, L"FILE:") == 0)
703 {
704 // For FILE:, we create a virtual port for each request.
705 pPort = DllAllocSplMem(sizeof(LOCALMON_PORT));
706 pPort->pLocalmon = pLocalmon;
707 pPort->PortType = PortType_FILE;
708 pPort->hFile = INVALID_HANDLE_VALUE;
709
710 // Add it to the list of file ports.
711 InsertTailList(&pLocalmon->FilePorts, &pPort->Entry);
712 }
713 else
714 {
715 // Check if the port name is valid.
716 pPort = _FindPort(pLocalmon, pName);
717 if (!pPort)
718 {
719 LeaveCriticalSection(&pLocalmon->Section);
720 dwErrorCode = ERROR_UNKNOWN_PORT;
721 goto Cleanup;
722 }
723
724 // Even if this API is called OpenPort, port file handles aren't always opened here :-P
725 // Windows only does this for physical LPT ports here to enable bidirectional communication with the printer outside of jobs (using ReadPort and WritePort).
726 // The others are only opened per job in StartDocPort.
727 if (_IsLegacyPort(pName, L"LPT"))
728 {
729 // Try to create a NONSPOOLED port and open it.
730 if (_CreateNonspooledPort(pPort))
731 {
732 // Set the transmission retry timeout for the ReadPort and WritePort calls.
733 // This also checks if this port is a physical one.
734 if (_SetTransmissionRetryTimeout(pPort))
735 {
736 // This is definitely a physical LPT port!
737 pPort->PortType = PortType_PhysicalLPT;
738 }
739 else
740 {
741 // This is no physical port, so don't keep its handle open.
742 _ClosePortHandles(pPort);
743 }
744 }
745 else if (GetLastError() != ERROR_SUCCESS)
746 {
747 LeaveCriticalSection(&pLocalmon->Section);
748 dwErrorCode = GetLastError();
749 goto Cleanup;
750 }
751 }
752 else if (_IsLegacyPort(pName, L"COM"))
753 {
754 // COM ports can't be redirected over the network, so this is a physical one.
755 pPort->PortType = PortType_PhysicalCOM;
756 }
757 }
758
759 LeaveCriticalSection(&pLocalmon->Section);
760
761 // Return our fetched LOCALMON_PORT structure in the handle.
762 *pHandle = (PHANDLE)pPort;
763 dwErrorCode = ERROR_SUCCESS;
764
765 Cleanup:
766 SetLastError(dwErrorCode);
767 return (dwErrorCode == ERROR_SUCCESS);
768 }
769
770 /*
771 * @name LocalmonSetPortTimeOuts
772 *
773 * Performs a SetCommTimeouts call for the given port.
774 *
775 * @param hPort
776 * The port to operate on.
777 *
778 * @param lpCTO
779 * Pointer to a COMMTIMEOUTS structure that is passed to SetCommTimeouts.
780 *
781 * @param Reserved
782 * Reserved parameter, must be 0.
783 *
784 * @return
785 * TRUE if the SetCommTimeouts call was successful, FALSE otherwise.
786 * A more specific error code can be obtained through GetLastError.
787 */
788 BOOL WINAPI
LocalmonSetPortTimeOuts(HANDLE hPort,LPCOMMTIMEOUTS lpCTO,DWORD Reserved)789 LocalmonSetPortTimeOuts(HANDLE hPort, LPCOMMTIMEOUTS lpCTO, DWORD Reserved)
790 {
791 BOOL bOpenedPort = FALSE;
792 DWORD dwErrorCode;
793 PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
794
795 TRACE("LocalmonSetPortTimeOuts(%p, %p, %lu)\n", hPort, lpCTO, Reserved);
796
797 // Sanity checks
798 if (!pPort || !lpCTO)
799 {
800 dwErrorCode = ERROR_INVALID_PARAMETER;
801 goto Cleanup;
802 }
803
804 // If this is a serial port, a temporary file handle may be opened.
805 if (pPort->PortType == PortType_PhysicalCOM)
806 {
807 if (_CreateNonspooledPort(pPort))
808 {
809 bOpenedPort = TRUE;
810 }
811 else if (GetLastError() != ERROR_SUCCESS)
812 {
813 dwErrorCode = GetLastError();
814 goto Cleanup;
815 }
816 }
817 else if (pPort->hFile == INVALID_HANDLE_VALUE)
818 {
819 // All other port types need to be opened already.
820 dwErrorCode = ERROR_INVALID_PARAMETER;
821 goto Cleanup;
822 }
823
824 // Finally pass the parameters to SetCommTimeouts.
825 if (!SetCommTimeouts(pPort->hFile, lpCTO))
826 {
827 dwErrorCode = GetLastError();
828 ERR("SetCommTimeouts failed with error %lu!\n", dwErrorCode);
829 goto Cleanup;
830 }
831
832 dwErrorCode = ERROR_SUCCESS;
833
834 Cleanup:
835 if (bOpenedPort)
836 _ClosePortHandles(pPort);
837
838 SetLastError(dwErrorCode);
839 return (dwErrorCode == ERROR_SUCCESS);
840 }
841
842 BOOL WINAPI
LocalmonReadPort(HANDLE hPort,PBYTE pBuffer,DWORD cbBuffer,PDWORD pcbRead)843 LocalmonReadPort(HANDLE hPort, PBYTE pBuffer, DWORD cbBuffer, PDWORD pcbRead)
844 {
845 BOOL bOpenedPort = FALSE;
846 DWORD dwErrorCode;
847 PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
848
849 TRACE("LocalmonReadPort(%p, %p, %lu, %p)\n", hPort, pBuffer, cbBuffer, pcbRead);
850
851 // Sanity checks
852 if (!pPort || (cbBuffer && !pBuffer) || !pcbRead)
853 {
854 dwErrorCode = ERROR_INVALID_PARAMETER;
855 goto Cleanup;
856 }
857
858 // Reading is only supported for physical ports.
859 if (pPort->PortType != PortType_PhysicalCOM && pPort->PortType != PortType_PhysicalLPT)
860 {
861 dwErrorCode = ERROR_INVALID_HANDLE;
862 goto Cleanup;
863 }
864
865 // If this is a serial port, a temporary file handle may be opened.
866 if (pPort->PortType == PortType_PhysicalCOM)
867 {
868 if (_CreateNonspooledPort(pPort))
869 {
870 bOpenedPort = TRUE;
871 }
872 else if (GetLastError() != ERROR_SUCCESS)
873 {
874 dwErrorCode = GetLastError();
875 goto Cleanup;
876 }
877 }
878
879 // Pass the parameters to ReadFile.
880 if (!ReadFile(pPort->hFile, pBuffer, cbBuffer, pcbRead, NULL))
881 {
882 dwErrorCode = GetLastError();
883 ERR("ReadFile failed with error %lu!\n", dwErrorCode);
884 goto Cleanup;
885 }
886
887 Cleanup:
888 if (bOpenedPort)
889 _ClosePortHandles(pPort);
890
891 SetLastError(dwErrorCode);
892 return (dwErrorCode == ERROR_SUCCESS);
893 }
894
895 BOOL WINAPI
LocalmonStartDocPort(HANDLE hPort,PWSTR pPrinterName,DWORD JobId,DWORD Level,PBYTE pDocInfo)896 LocalmonStartDocPort(HANDLE hPort, PWSTR pPrinterName, DWORD JobId, DWORD Level, PBYTE pDocInfo)
897 {
898 DWORD dwErrorCode;
899 PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo; // DOC_INFO_1W is the least common denominator for both DOC_INFO levels.
900 PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
901
902 TRACE("LocalmonStartDocPort(%p, %S, %lu, %lu, %p)\n", hPort, pPrinterName, JobId, Level, pDocInfo);
903
904 // Sanity checks
905 if (!pPort || !pPrinterName || (pPort->PortType == PortType_FILE && (!pDocInfo1 || !pDocInfo1->pOutputFile || !*pDocInfo1->pOutputFile)))
906 {
907 dwErrorCode = ERROR_INVALID_PARAMETER;
908 goto Cleanup;
909 }
910
911 if (Level > 2)
912 {
913 dwErrorCode = ERROR_INVALID_LEVEL;
914 goto Cleanup;
915 }
916
917 // Calling StartDocPort multiple times isn't considered a failure, but we don't need to do anything then.
918 if (pPort->bStartedDoc)
919 {
920 dwErrorCode = ERROR_SUCCESS;
921 goto Cleanup;
922 }
923
924 // Open a handle to the given printer for later reporting our progress using SetJobW.
925 if (!OpenPrinterW(pPrinterName, &pPort->hPrinter, NULL))
926 {
927 dwErrorCode = GetLastError();
928 ERR("OpenPrinterW failed with error %lu!\n", dwErrorCode);
929 goto Cleanup;
930 }
931
932 // We need our Job ID for SetJobW as well.
933 pPort->dwJobID = JobId;
934
935 // Check the port type.
936 if (pPort->PortType == PortType_PhysicalLPT)
937 {
938 // Update the NONSPOOLED mapping if the port mapping has changed since our OpenPort call.
939 if (!_CreateNonspooledPort(pPort) && GetLastError() != ERROR_SUCCESS)
940 {
941 dwErrorCode = GetLastError();
942 goto Cleanup;
943 }
944
945 // Update the transmission retry timeout as well.
946 _SetTransmissionRetryTimeout(pPort);
947 }
948 else if(pPort->PortType == PortType_FILE)
949 {
950 // This is a FILE: port. Open the output file given in the Document Info.
951 pPort->hFile = CreateFileW(pDocInfo1->pOutputFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
952 if (pPort->hFile == INVALID_HANDLE_VALUE)
953 {
954 dwErrorCode = GetLastError();
955 goto Cleanup;
956 }
957 }
958 else
959 {
960 // This can be:
961 // - a physical COM port
962 // - a non-physical LPT port (e.g. with "net use LPT1 ...")
963 // - any other port (e.g. a file or a shared printer installed as a local port)
964 //
965 // For all these cases, we try to create a NONSPOOLED port per job.
966 // If _CreateNonspooledPort reports that no NONSPOOLED port is necessary, we can just open the port name.
967 if (!_CreateNonspooledPort(pPort))
968 {
969 if (GetLastError() == ERROR_SUCCESS)
970 {
971 pPort->hFile = CreateFileW(pPort->pwszPortName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
972 if (pPort->hFile == INVALID_HANDLE_VALUE)
973 {
974 dwErrorCode = GetLastError();
975 goto Cleanup;
976 }
977 }
978 else
979 {
980 dwErrorCode = GetLastError();
981 goto Cleanup;
982 }
983 }
984 }
985
986 // We were successful!
987 dwErrorCode = ERROR_SUCCESS;
988 pPort->bStartedDoc = TRUE;
989
990 Cleanup:
991 SetLastError(dwErrorCode);
992 return (dwErrorCode == ERROR_SUCCESS);
993 }
994
995 BOOL WINAPI
LocalmonWritePort(HANDLE hPort,PBYTE pBuffer,DWORD cbBuf,PDWORD pcbWritten)996 LocalmonWritePort(HANDLE hPort, PBYTE pBuffer, DWORD cbBuf, PDWORD pcbWritten)
997 {
998 BOOL bOpenedPort = FALSE;
999 DWORD dwErrorCode;
1000 PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
1001
1002 TRACE("LocalmonWritePort(%p, %p, %lu, %p)\n", hPort, pBuffer, cbBuf, pcbWritten);
1003
1004 // Sanity checks
1005 if (!pPort || (cbBuf && !pBuffer) || !pcbWritten)
1006 {
1007 dwErrorCode = ERROR_INVALID_PARAMETER;
1008 goto Cleanup;
1009 }
1010
1011 // If this is a serial port, a temporary file handle may be opened.
1012 if (pPort->PortType == PortType_PhysicalCOM)
1013 {
1014 if (_CreateNonspooledPort(pPort))
1015 {
1016 bOpenedPort = TRUE;
1017 }
1018 else if (GetLastError() != ERROR_SUCCESS)
1019 {
1020 dwErrorCode = GetLastError();
1021 goto Cleanup;
1022 }
1023 }
1024 else if (pPort->hFile == INVALID_HANDLE_VALUE)
1025 {
1026 // All other port types need to be opened already.
1027 dwErrorCode = ERROR_INVALID_PARAMETER;
1028 goto Cleanup;
1029 }
1030
1031 // Pass the parameters to WriteFile.
1032 if (!WriteFile(pPort->hFile, pBuffer, cbBuf, pcbWritten, NULL))
1033 {
1034 dwErrorCode = GetLastError();
1035 ERR("WriteFile failed with error %lu!\n", dwErrorCode);
1036 goto Cleanup;
1037 }
1038
1039 // If something was written down, we consider that a success, otherwise it's a timeout.
1040 if (*pcbWritten)
1041 dwErrorCode = ERROR_SUCCESS;
1042 else
1043 dwErrorCode = ERROR_TIMEOUT;
1044
1045 Cleanup:
1046 if (bOpenedPort)
1047 _ClosePortHandles(pPort);
1048
1049 SetLastError(dwErrorCode);
1050 return (dwErrorCode == ERROR_SUCCESS);
1051 }
1052
1053 BOOL WINAPI
LocalmonAddPortEx(HANDLE hMonitor,LPWSTR pName,DWORD Level,LPBYTE lpBuffer,LPWSTR lpMonitorName)1054 LocalmonAddPortEx( HANDLE hMonitor, LPWSTR pName, DWORD Level, LPBYTE lpBuffer, LPWSTR lpMonitorName )
1055 {
1056 PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
1057 PLOCALMON_PORT pPort;
1058 HKEY hKey;
1059 DWORD dwErrorCode, cbPortName;
1060 PORT_INFO_1W * pi = (PORT_INFO_1W *) lpBuffer;
1061
1062 FIXME("LocalmonAddPortEx(%p, %lu, %p, %s) => %s\n", hMonitor, Level, lpBuffer, debugstr_w(lpMonitorName), debugstr_w(pi ? pi->pName : NULL));
1063
1064 // Sanity checks
1065 if ( !pLocalmon )
1066 {
1067 dwErrorCode = ERROR_INVALID_PARAMETER;
1068 goto Cleanup;
1069 }
1070
1071 if ( ( lpMonitorName == NULL ) ||
1072 ( lstrcmpiW( lpMonitorName, L"Local Port" ) != 0 ) ||
1073 ( pi == NULL ) ||
1074 ( pi->pName == NULL ) ||
1075 ( pi->pName[0] == '\0' ) )
1076 {
1077 ERR("Fail Monitor Port Name\n");
1078 SetLastError(ERROR_INVALID_PARAMETER);
1079 return FALSE;
1080 }
1081
1082 if ( Level != 1 )
1083 {
1084 SetLastError(ERROR_INVALID_LEVEL);
1085 return FALSE;
1086 }
1087
1088 dwErrorCode = RegOpenKeyW( HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", &hKey );
1089 if ( dwErrorCode == ERROR_SUCCESS )
1090 {
1091 if ( DoesPortExist( pi->pName ) )
1092 {
1093 RegCloseKey( hKey) ;
1094 FIXME("Port Exist => FALSE with %u\n", ERROR_INVALID_PARAMETER);
1095 SetLastError(ERROR_INVALID_PARAMETER);
1096 return FALSE;
1097 }
1098
1099 cbPortName = (wcslen( pi->pName ) + 1) * sizeof(WCHAR);
1100
1101 // Create a new LOCALMON_PORT structure for it.
1102 pPort = DllAllocSplMem(sizeof(LOCALMON_PORT) + cbPortName);
1103 if (!pPort)
1104 {
1105 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
1106 RegCloseKey( hKey );
1107 goto Cleanup;
1108 }
1109
1110 pPort->Sig = SIGLCMPORT;
1111 pPort->hFile = INVALID_HANDLE_VALUE;
1112 pPort->pLocalmon = pLocalmon;
1113 pPort->pwszPortName = wcscpy( (PWSTR)(pPort+1), pi->pName );
1114
1115 // Insert it into the Registry list.
1116 InsertTailList(&pLocalmon->RegistryPorts, &pPort->Entry);
1117
1118 dwErrorCode = RegSetValueExW( hKey, pi->pName, 0, REG_SZ, (const BYTE *) L"", sizeof(L"") );
1119 RegCloseKey( hKey );
1120 }
1121
1122 Cleanup:
1123 if (dwErrorCode != ERROR_SUCCESS) SetLastError(ERROR_INVALID_PARAMETER);
1124
1125 FIXME("LocalmonAddPortEx => %u with %u\n", (dwErrorCode == ERROR_SUCCESS), GetLastError());
1126
1127 return (dwErrorCode == ERROR_SUCCESS);
1128 }
1129
1130 // Fallback Throw Back code....
1131 //
1132 // This is pre-w2k support, seems to be moved into LocalUI.
1133 //
1134 //
1135
1136 BOOL WINAPI
LocalmonAddPort(HANDLE hMonitor,LPWSTR pName,HWND hWnd,LPWSTR pMonitorName)1137 LocalmonAddPort( HANDLE hMonitor, LPWSTR pName, HWND hWnd, LPWSTR pMonitorName )
1138 {
1139 DWORD res, cbPortName;
1140 HKEY hroot;
1141 PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
1142 PLOCALMON_PORT pPort;
1143 WCHAR PortName[MAX_PATH] = {0}; // Need to use a Dialog to get name.
1144
1145 FIXME("LocalmonAddPort : %s\n", debugstr_w( (LPWSTR) pMonitorName ) );
1146
1147 res = RegOpenKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", &hroot);
1148 if (res == ERROR_SUCCESS)
1149 {
1150 if ( DoesPortExist( PortName ) )
1151 {
1152 RegCloseKey(hroot);
1153 FIXME("=> %u\n", ERROR_ALREADY_EXISTS);
1154 res = ERROR_ALREADY_EXISTS;
1155 goto Cleanup;
1156 }
1157
1158 cbPortName = (wcslen( PortName ) + 1) * sizeof(WCHAR);
1159
1160 // Create a new LOCALMON_PORT structure for it.
1161 pPort = DllAllocSplMem(sizeof(LOCALMON_PORT) + cbPortName);
1162 if (!pPort)
1163 {
1164 res = ERROR_NOT_ENOUGH_MEMORY;
1165 RegCloseKey( hroot );
1166 goto Cleanup;
1167 }
1168
1169 pPort->Sig = SIGLCMPORT;
1170 pPort->hFile = INVALID_HANDLE_VALUE;
1171 pPort->pLocalmon = pLocalmon;
1172 pPort->pwszPortName = wcscpy( (PWSTR)(pPort+1), PortName );
1173
1174 // Insert it into the Registry list.
1175 InsertTailList(&pLocalmon->RegistryPorts, &pPort->Entry);
1176
1177 res = RegSetValueExW(hroot, PortName, 0, REG_SZ, (const BYTE *) L"", sizeof(L""));
1178 RegCloseKey(hroot);
1179 }
1180
1181 FIXME("=> %u\n", res);
1182
1183 Cleanup:
1184 SetLastError(res);
1185 return (res == ERROR_SUCCESS);
1186 }
1187
1188 BOOL WINAPI
LocalmonConfigurePort(HANDLE hMonitor,LPWSTR pName,HWND hWnd,LPWSTR pPortName)1189 LocalmonConfigurePort( HANDLE hMonitor, LPWSTR pName, HWND hWnd, LPWSTR pPortName )
1190 {
1191 //// See ConfigurePortUI
1192 FIXME("LocalmonConfigurePort : %s\n", debugstr_w( pPortName ) );
1193 return FALSE;
1194 }
1195
1196 BOOL WINAPI
LocalmonDeletePort(HANDLE hMonitor,LPWSTR pName,HWND hWnd,LPWSTR pPortName)1197 LocalmonDeletePort( HANDLE hMonitor, LPWSTR pName, HWND hWnd, LPWSTR pPortName )
1198 {
1199 DWORD res;
1200 HKEY hroot;
1201 PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
1202 PLOCALMON_PORT pPort;
1203
1204 FIXME("LocalmonDeletePort : %s\n", debugstr_w( pPortName ) );
1205
1206 res = RegOpenKeyW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", &hroot);
1207 if ( res == ERROR_SUCCESS )
1208 {
1209 res = RegDeleteValueW(hroot, pPortName );
1210
1211 RegCloseKey(hroot);
1212
1213 pPort = _FindPort( pLocalmon, pPortName );
1214 if ( pPort )
1215 {
1216 EnterCriticalSection(&pPort->pLocalmon->Section);
1217 RemoveEntryList(&pPort->Entry);
1218 LeaveCriticalSection(&pPort->pLocalmon->Section);
1219
1220 DllFreeSplMem(pPort);
1221 }
1222
1223 FIXME("=> %u with %u\n", res, GetLastError() );
1224 }
1225
1226 SetLastError(res);
1227 return (res == ERROR_SUCCESS);
1228 }
1229