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