ReactOS  0.4.11-dev-791-gf6f1255
ports.c
Go to the documentation of this file.
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_";
13 
16  MAXDWORD
17 };
18 
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 
43 static __inline DWORD
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());
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 
78 static __inline BOOL
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 
112 static void
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 
143  DllFreeSplMem(pwszPortNameWithoutColon);
144  }
145 }
146 
162 static BOOL
164 {
165  const WCHAR wszLocalSlashes[] = L"\\\\.\\";
166  const DWORD cchLocalSlashes = _countof(wszLocalSlashes) - 1;
167 
168  const WCHAR wszSpoolerNamedPipe[] = L"\\Device\\NamedPipe\\Spooler\\";
169  const DWORD cchSpoolerNamedPipe = _countof(wszSpoolerNamedPipe) - 1;
170 
171  BOOL bReturnValue = FALSE;
172  DWORD cchPortNameWithoutColon;
173  DWORD dwErrorCode;
174  HANDLE hToken = NULL;
175  PWSTR p;
176  PWSTR pwszDeviceMappings = NULL;
177  PWSTR pwszNonspooledFileName = NULL;
178  PWSTR pwszNonspooledPortName = NULL;
179  PWSTR pwszPipeName = NULL;
180  PWSTR pwszPortNameWithoutColon = NULL;
181 
182  // We need the port name without the trailing colon.
183  dwErrorCode = GetPortNameWithoutColon(pPort->pwszPortName, &pwszPortNameWithoutColon);
184  if (dwErrorCode == ERROR_INVALID_PARAMETER)
185  {
186  // This port has no trailing colon, so we also need no NONSPOOLED mapping for it.
187  dwErrorCode = ERROR_SUCCESS;
188  goto Cleanup;
189  }
190  else if (dwErrorCode != ERROR_SUCCESS)
191  {
192  // Another unexpected failure.
193  goto Cleanup;
194  }
195 
196  cchPortNameWithoutColon = wcslen(pwszPortNameWithoutColon);
197 
198  // The spooler has usually remapped the legacy port to a named pipe of the format in wszSpoolerNamedPipe.
199  // Construct the device name of this pipe.
200  pwszPipeName = DllAllocSplMem((cchSpoolerNamedPipe + cchPortNameWithoutColon + 1) * sizeof(WCHAR));
201  if (!pwszPipeName)
202  {
203  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
204  ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
205  goto Cleanup;
206  }
207 
208  CopyMemory(pwszPipeName, wszSpoolerNamedPipe, cchSpoolerNamedPipe * sizeof(WCHAR));
209  CopyMemory(&pwszPipeName[cchSpoolerNamedPipe], pwszPortNameWithoutColon, (cchPortNameWithoutColon + 1) * sizeof(WCHAR));
210 
211  // 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.
212  // Examples show that a value of MAX_PATH * sizeof(WCHAR) is usually taken here, so we have no other option either.
213  pwszDeviceMappings = DllAllocSplMem(MAX_PATH * sizeof(WCHAR));
214  if (!pwszDeviceMappings)
215  {
216  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
217  ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
218  goto Cleanup;
219  }
220 
221  // Switch to the SYSTEM context, because we're only interested in creating NONSPOOLED ports for system-wide ports.
222  // User-local ports (like _some_ redirected networked ones) aren't remapped by the spooler and can be opened directly.
223  hToken = RevertToPrinterSelf();
224  if (!hToken)
225  {
226  dwErrorCode = GetLastError();
227  ERR("RevertToPrinterSelf failed with error %lu!\n", dwErrorCode);
228  goto Cleanup;
229  }
230 
231  // 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.
232  if (!QueryDosDeviceW(pwszPortNameWithoutColon, pwszDeviceMappings, MAX_PATH))
233  {
234  // No system-wide port exists, so we also need no NONSPOOLED mapping.
235  dwErrorCode = ERROR_SUCCESS;
236  goto Cleanup;
237  }
238 
239  // Check if this port has already been opened by _CreateNonspooledPort previously.
240  if (pPort->pwszMapping)
241  {
242  // In this case, we just need to do something if the mapping has changed.
243  // Therefore, check if the stored mapping equals the current mapping.
244  if (wcscmp(pPort->pwszMapping, pwszDeviceMappings) == 0)
245  {
246  // We don't need to do anything in this case.
247  dwErrorCode = ERROR_SUCCESS;
248  goto Cleanup;
249  }
250  else
251  {
252  // Close the open file handle and free the memory for pwszMapping before remapping.
253  CloseHandle(pPort->hFile);
254  pPort->hFile = INVALID_HANDLE_VALUE;
255 
256  DllFreeSplStr(pPort->pwszMapping);
257  pPort->pwszMapping = NULL;
258  }
259  }
260 
261  // The port is usually mapped to the named pipe and this is how we received our data for printing.
262  // What we now need for accessing the actual port is the most recent mapping different from the named pipe.
263  p = pwszDeviceMappings;
264 
265  for (;;)
266  {
267  if (!*p)
268  {
269  // We reached the end of the list without finding a mapping.
270  ERR("Can't find a suitable mapping for the port \"%S\"!", pPort->pwszPortName);
271  goto Cleanup;
272  }
273 
274  if (_wcsicmp(p, pwszPipeName) != 0)
275  break;
276 
277  // Advance to the next mapping in the list.
278  p += wcslen(p) + 1;
279  }
280 
281  // We now want to create a DOS device "NONSPOOLED_<PortName>" to this mapping, so that we're able to open it through CreateFileW.
282  dwErrorCode = _GetNonspooledPortName(pwszPortNameWithoutColon, &pwszNonspooledPortName);
283  if (dwErrorCode != ERROR_SUCCESS)
284  goto Cleanup;
285 
286  // Delete a possibly existing NONSPOOLED device before creating the new one, so we don't stack up device definitions.
287  DefineDosDeviceW(DDD_REMOVE_DEFINITION, pwszNonspooledPortName, NULL);
288 
289  if (!DefineDosDeviceW(DDD_RAW_TARGET_PATH, pwszNonspooledPortName, p))
290  {
291  dwErrorCode = GetLastError();
292  ERR("DefineDosDeviceW failed with error %lu!\n", dwErrorCode);
293  goto Cleanup;
294  }
295 
296  // This is all we needed to do in SYSTEM context.
297  ImpersonatePrinterClient(hToken);
298  hToken = NULL;
299 
300  // Construct the file name to our created device for CreateFileW.
301  pwszNonspooledFileName = DllAllocSplMem((cchLocalSlashes + cchNonspooledPrefix + cchPortNameWithoutColon + 1) * sizeof(WCHAR));
302  if (!pwszNonspooledFileName)
303  {
304  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
305  ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
306  goto Cleanup;
307  }
308 
309  CopyMemory(pwszNonspooledFileName, wszLocalSlashes, cchLocalSlashes * sizeof(WCHAR));
310  CopyMemory(&pwszNonspooledFileName[cchLocalSlashes], wszNonspooledPrefix, cchNonspooledPrefix * sizeof(WCHAR));
311  CopyMemory(&pwszNonspooledFileName[cchLocalSlashes + cchNonspooledPrefix], pwszPortNameWithoutColon, (cchPortNameWithoutColon + 1) * sizeof(WCHAR));
312 
313  // Finally open it for reading and writing.
314  pPort->hFile = CreateFileW(pwszNonspooledFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
315  if (pPort->hFile == INVALID_HANDLE_VALUE)
316  {
317  dwErrorCode = GetLastError();
318  ERR("CreateFileW failed with error %lu!\n", dwErrorCode);
319  goto Cleanup;
320  }
321 
322  // Store the current mapping of the port, so that we can check if it has changed.
323  pPort->pwszMapping = AllocSplStr(pwszDeviceMappings);
324  if (!pPort->pwszMapping)
325  {
326  dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
327  goto Cleanup;
328  }
329 
330  bReturnValue = TRUE;
331  dwErrorCode = ERROR_SUCCESS;
332 
333 Cleanup:
334  if (hToken)
335  ImpersonatePrinterClient(hToken);
336 
337  if (pwszDeviceMappings)
338  DllFreeSplMem(pwszDeviceMappings);
339 
340  if (pwszNonspooledFileName)
341  DllFreeSplMem(pwszNonspooledFileName);
342 
343  if (pwszNonspooledPortName)
344  DllFreeSplMem(pwszNonspooledPortName);
345 
346  if (pwszPipeName)
347  DllFreeSplMem(pwszPipeName);
348 
349  if (pwszPortNameWithoutColon)
350  DllFreeSplMem(pwszPortNameWithoutColon);
351 
352  SetLastError(dwErrorCode);
353  return bReturnValue;
354 }
355 
356 static PLOCALMON_PORT
357 _FindPort(PLOCALMON_HANDLE pLocalmon, PCWSTR pwszPortName)
358 {
359  PLIST_ENTRY pEntry;
360  PLOCALMON_PORT pPort;
361 
362  for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
363  {
364  pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
365 
366  if (wcscmp(pPort->pwszPortName, pwszPortName) == 0)
367  return pPort;
368  }
369 
370  return NULL;
371 }
372 
373 static void
375 {
376  DWORD cbPortName;
377  PWSTR pwszStrings[1];
378 
379  // Calculate the string lengths.
380  if (!ppPortInfo)
381  {
382  cbPortName = (wcslen(pPort->pwszPortName) + 1) * sizeof(WCHAR);
383 
384  *pcbNeeded += sizeof(PORT_INFO_1W) + cbPortName;
385  return;
386  }
387 
388  // Set the pName field.
389  pwszStrings[0] = pPort->pwszPortName;
390 
391  // Copy the structure and advance to the next one in the output buffer.
392  *ppPortInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPortInfo), dwPortInfo1Offsets, *ppPortInfoEnd);
393  (*ppPortInfo)++;
394 }
395 
396 static void
398 {
399  DWORD cbPortName;
400  PWSTR pwszStrings[3];
401 
402  // Calculate the string lengths.
403  if (!ppPortInfo)
404  {
405  cbPortName = (wcslen(pPort->pwszPortName) + 1) * sizeof(WCHAR);
406 
407  *pcbNeeded += sizeof(PORT_INFO_2W) + cbPortName + cbLocalMonitor + cbLocalPort;
408  return;
409  }
410 
411  // All local ports are writable and readable.
412  (*ppPortInfo)->fPortType = PORT_TYPE_WRITE | PORT_TYPE_READ;
413  (*ppPortInfo)->Reserved = 0;
414 
415  // Set the pPortName field.
416  pwszStrings[0] = pPort->pwszPortName;
417 
418  // Set the pMonitorName field.
419  pwszStrings[1] = (PWSTR)pwszLocalMonitor;
420 
421  // Set the pDescription field.
422  pwszStrings[2] = (PWSTR)pwszLocalPort;
423 
424  // Copy the structure and advance to the next one in the output buffer.
425  *ppPortInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPortInfo), dwPortInfo2Offsets, *ppPortInfoEnd);
426  (*ppPortInfo)++;
427 }
428 
440 static BOOL
442 {
443  COMMTIMEOUTS CommTimeouts;
444 
445  // Get the timeout from the port.
446  if (!GetCommTimeouts(pPort->hFile, &CommTimeouts))
447  return FALSE;
448 
449  // Set the timeout using the value from registry.
451  SetCommTimeouts(pPort->hFile, &CommTimeouts);
452 
453  return TRUE;
454 }
455 
456 BOOL WINAPI
458 {
459  PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
460 
461  TRACE("LocalmonClosePort(%p)\n", hPort);
462 
463  // Sanity checks
464  if (!pPort)
465  {
467  return FALSE;
468  }
469 
470  // Close the file handle, free memory for pwszMapping and delete any NONSPOOLED port.
471  _ClosePortHandles(pPort);
472 
473  // Close any open printer handle.
474  if (pPort->hPrinter)
475  {
476  ClosePrinter(pPort->hPrinter);
477  pPort->hPrinter = NULL;
478  }
479 
480  // Free virtual FILE: ports which were created in LocalmonOpenPort.
481  if (pPort->PortType == PortType_FILE)
482  {
484  RemoveEntryList(&pPort->Entry);
486  DllFreeSplMem(pPort);
487  }
488 
490  return TRUE;
491 }
492 
493 BOOL WINAPI
495 {
496  PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
497 
498  TRACE("LocalmonEndDocPort(%p)\n", hPort);
499 
500  // Sanity checks
501  if (!pPort)
502  {
504  return FALSE;
505  }
506 
507  // Ending a document requires starting it first :-P
508  if (pPort->bStartedDoc)
509  {
510  // Close all ports opened in StartDocPort.
511  // That is, all but physical LPT ports (opened in OpenPort).
512  if (pPort->PortType != PortType_PhysicalLPT)
513  _ClosePortHandles(pPort);
514 
515  // Report our progress.
517 
518  // We're done with the printer.
519  ClosePrinter(pPort->hPrinter);
520  pPort->hPrinter = NULL;
521 
522  // A new document can now be started again.
523  pPort->bStartedDoc = FALSE;
524  }
525 
527  return TRUE;
528 }
529 
530 BOOL WINAPI
531 LocalmonEnumPorts(HANDLE hMonitor, PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
532 {
533  DWORD dwErrorCode;
534  PBYTE pPortInfoEnd;
535  PLIST_ENTRY pEntry;
536  PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
537  PLOCALMON_PORT pPort;
538 
539  TRACE("LocalmonEnumPorts(%p, %S, %lu, %p, %lu, %p, %p)\n", hMonitor, pName, Level, pPorts, cbBuf, pcbNeeded, pcReturned);
540 
541  // Windows Server 2003's Local Port Monitor does absolutely no sanity checks here, not even for the Level parameter.
542  // As we implement a more modern MONITOR2-based Port Monitor, check at least our hMonitor.
543  if (!pLocalmon)
544  {
545  dwErrorCode = ERROR_INVALID_HANDLE;
546  goto Cleanup;
547  }
548 
549  // Begin counting.
550  *pcbNeeded = 0;
551  *pcReturned = 0;
552 
553  EnterCriticalSection(&pLocalmon->Section);
554 
555  // Count the required buffer size and the number of ports.
556  for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
557  {
558  pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
559 
560  if (Level == 1)
561  _LocalmonGetPortLevel1(pPort, NULL, NULL, pcbNeeded);
562  else if (Level == 2)
563  _LocalmonGetPortLevel2(pPort, NULL, NULL, pcbNeeded);
564  }
565 
566  // Check if the supplied buffer is large enough.
567  if (cbBuf < *pcbNeeded)
568  {
569  LeaveCriticalSection(&pLocalmon->Section);
570  dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
571  goto Cleanup;
572  }
573 
574  // Copy over the Port information.
575  pPortInfoEnd = &pPorts[*pcbNeeded];
576 
577  for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
578  {
579  pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
580 
581  if (Level == 1)
582  _LocalmonGetPortLevel1(pPort, (PPORT_INFO_1W*)&pPorts, &pPortInfoEnd, NULL);
583  else if (Level == 2)
584  _LocalmonGetPortLevel2(pPort, (PPORT_INFO_2W*)&pPorts, &pPortInfoEnd, NULL);
585 
586  (*pcReturned)++;
587  }
588 
589  LeaveCriticalSection(&pLocalmon->Section);
590  dwErrorCode = ERROR_SUCCESS;
591 
592 Cleanup:
593  SetLastError(dwErrorCode);
594  return (dwErrorCode == ERROR_SUCCESS);
595 }
596 
597 /*
598  * @name LocalmonGetPrinterDataFromPort
599  *
600  * Performs a DeviceIoControl call for the given port.
601  *
602  * @param hPort
603  * The port to operate on.
604  *
605  * @param ControlID
606  * The dwIoControlCode passed to DeviceIoControl. Must not be zero!
607  *
608  * @param pValueName
609  * This parameter is ignored.
610  *
611  * @param lpInBuffer
612  * The lpInBuffer passed to DeviceIoControl.
613  *
614  * @param cbInBuffer
615  * The nInBufferSize passed to DeviceIoControl.
616  *
617  * @param lpOutBuffer
618  * The lpOutBuffer passed to DeviceIoControl.
619  *
620  * @param cbOutBuffer
621  * The nOutBufferSize passed to DeviceIoControl.
622  *
623  * @param lpcbReturned
624  * The lpBytesReturned passed to DeviceIoControl. Must not be zero!
625  *
626  * @return
627  * TRUE if the DeviceIoControl call was successful, FALSE otherwise.
628  * A more specific error code can be obtained through GetLastError.
629  */
630 BOOL WINAPI
631 LocalmonGetPrinterDataFromPort(HANDLE hPort, DWORD ControlID, PWSTR pValueName, PWSTR lpInBuffer, DWORD cbInBuffer, PWSTR lpOutBuffer, DWORD cbOutBuffer, PDWORD lpcbReturned)
632 {
633  BOOL bOpenedPort = FALSE;
634  DWORD dwErrorCode;
635  PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
636 
637  TRACE("LocalmonGetPrinterDataFromPort(%p, %lu, %p, %p, %lu, %p, %lu, %p)\n", hPort, ControlID, pValueName, lpInBuffer, cbInBuffer, lpOutBuffer, cbOutBuffer, lpcbReturned);
638 
639  // Sanity checks
640  if (!pPort || !ControlID || !lpcbReturned)
641  {
642  dwErrorCode = ERROR_INVALID_PARAMETER;
643  goto Cleanup;
644  }
645 
646  // If this is a serial port, a temporary file handle may be opened.
647  if (pPort->PortType == PortType_PhysicalCOM)
648  {
649  if (_CreateNonspooledPort(pPort))
650  {
651  bOpenedPort = TRUE;
652  }
653  else if (GetLastError() != ERROR_SUCCESS)
654  {
655  dwErrorCode = GetLastError();
656  goto Cleanup;
657  }
658  }
659  else if (pPort->hFile == INVALID_HANDLE_VALUE)
660  {
661  // All other port types need to be opened already.
662  dwErrorCode = ERROR_INVALID_PARAMETER;
663  goto Cleanup;
664  }
665 
666  // Pass the parameters to DeviceIoControl.
667  if (!DeviceIoControl(pPort->hFile, ControlID, lpInBuffer, cbInBuffer, lpOutBuffer, cbOutBuffer, lpcbReturned, NULL))
668  {
669  dwErrorCode = GetLastError();
670  ERR("DeviceIoControl failed with error %lu!\n", dwErrorCode);
671  goto Cleanup;
672  }
673 
674  dwErrorCode = ERROR_SUCCESS;
675 
676 Cleanup:
677  if (bOpenedPort)
678  _ClosePortHandles(pPort);
679 
680  SetLastError(dwErrorCode);
681  return (dwErrorCode == ERROR_SUCCESS);
682 }
683 
684 BOOL WINAPI
686 {
687  DWORD dwErrorCode;
688  PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
689  PLOCALMON_PORT pPort;
690 
691  TRACE("LocalmonOpenPort(%p, %S, %p)\n", hMonitor, pName, pHandle);
692 
693  // Sanity checks
694  if (!pLocalmon || !pName || !pHandle)
695  {
696  dwErrorCode = ERROR_INVALID_PARAMETER;
697  goto Cleanup;
698  }
699 
700  EnterCriticalSection(&pLocalmon->Section);
701 
702  // Check if this is a FILE: port.
703  if (_wcsicmp(pName, L"FILE:") == 0)
704  {
705  // For FILE:, we create a virtual port for each request.
706  pPort = DllAllocSplMem(sizeof(LOCALMON_PORT));
707  pPort->pLocalmon = pLocalmon;
708  pPort->PortType = PortType_FILE;
709  pPort->hFile = INVALID_HANDLE_VALUE;
710 
711  // Add it to the list of file ports.
712  InsertTailList(&pLocalmon->FilePorts, &pPort->Entry);
713  }
714  else
715  {
716  // Check if the port name is valid.
717  pPort = _FindPort(pLocalmon, pName);
718  if (!pPort)
719  {
720  LeaveCriticalSection(&pLocalmon->Section);
721  dwErrorCode = ERROR_UNKNOWN_PORT;
722  goto Cleanup;
723  }
724 
725  // Even if this API is called OpenPort, port file handles aren't always opened here :-P
726  // Windows only does this for physical LPT ports here to enable bidirectional communication with the printer outside of jobs (using ReadPort and WritePort).
727  // The others are only opened per job in StartDocPort.
728  if (_IsLegacyPort(pName, L"LPT"))
729  {
730  // Try to create a NONSPOOLED port and open it.
731  if (_CreateNonspooledPort(pPort))
732  {
733  // Set the transmission retry timeout for the ReadPort and WritePort calls.
734  // This also checks if this port is a physical one.
735  if (_SetTransmissionRetryTimeout(pPort))
736  {
737  // This is definitely a physical LPT port!
738  pPort->PortType = PortType_PhysicalLPT;
739  }
740  else
741  {
742  // This is no physical port, so don't keep its handle open.
743  _ClosePortHandles(pPort);
744  }
745  }
746  else if (GetLastError() != ERROR_SUCCESS)
747  {
748  LeaveCriticalSection(&pLocalmon->Section);
749  dwErrorCode = GetLastError();
750  goto Cleanup;
751  }
752  }
753  else if (_IsLegacyPort(pName, L"COM"))
754  {
755  // COM ports can't be redirected over the network, so this is a physical one.
756  pPort->PortType = PortType_PhysicalCOM;
757  }
758  }
759 
760  LeaveCriticalSection(&pLocalmon->Section);
761 
762  // Return our fetched LOCALMON_PORT structure in the handle.
763  *pHandle = (PHANDLE)pPort;
764  dwErrorCode = ERROR_SUCCESS;
765 
766 Cleanup:
767  SetLastError(dwErrorCode);
768  return (dwErrorCode == ERROR_SUCCESS);
769 }
770 
771 /*
772  * @name LocalmonSetPortTimeOuts
773  *
774  * Performs a SetCommTimeouts call for the given port.
775  *
776  * @param hPort
777  * The port to operate on.
778  *
779  * @param lpCTO
780  * Pointer to a COMMTIMEOUTS structure that is passed to SetCommTimeouts.
781  *
782  * @param Reserved
783  * Reserved parameter, must be 0.
784  *
785  * @return
786  * TRUE if the SetCommTimeouts call was successful, FALSE otherwise.
787  * A more specific error code can be obtained through GetLastError.
788  */
789 BOOL WINAPI
791 {
792  BOOL bOpenedPort = FALSE;
793  DWORD dwErrorCode;
794  PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
795 
796  TRACE("LocalmonSetPortTimeOuts(%p, %p, %lu)\n", hPort, lpCTO, Reserved);
797 
798  // Sanity checks
799  if (!pPort || !lpCTO)
800  {
801  dwErrorCode = ERROR_INVALID_PARAMETER;
802  goto Cleanup;
803  }
804 
805  // If this is a serial port, a temporary file handle may be opened.
806  if (pPort->PortType == PortType_PhysicalCOM)
807  {
808  if (_CreateNonspooledPort(pPort))
809  {
810  bOpenedPort = TRUE;
811  }
812  else if (GetLastError() != ERROR_SUCCESS)
813  {
814  dwErrorCode = GetLastError();
815  goto Cleanup;
816  }
817  }
818  else if (pPort->hFile == INVALID_HANDLE_VALUE)
819  {
820  // All other port types need to be opened already.
821  dwErrorCode = ERROR_INVALID_PARAMETER;
822  goto Cleanup;
823  }
824 
825  // Finally pass the parameters to SetCommTimeouts.
826  if (!SetCommTimeouts(pPort->hFile, lpCTO))
827  {
828  dwErrorCode = GetLastError();
829  ERR("SetCommTimeouts failed with error %lu!\n", dwErrorCode);
830  goto Cleanup;
831  }
832 
833  dwErrorCode = ERROR_SUCCESS;
834 
835 Cleanup:
836  if (bOpenedPort)
837  _ClosePortHandles(pPort);
838 
839  SetLastError(dwErrorCode);
840  return (dwErrorCode == ERROR_SUCCESS);
841 }
842 
843 BOOL WINAPI
845 {
846  BOOL bOpenedPort = FALSE;
847  DWORD dwErrorCode;
848  PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
849 
850  TRACE("LocalmonReadPort(%p, %p, %lu, %p)\n", hPort, pBuffer, cbBuffer, pcbRead);
851 
852  // Sanity checks
853  if (!pPort || (cbBuffer && !pBuffer) || !pcbRead)
854  {
855  dwErrorCode = ERROR_INVALID_PARAMETER;
856  goto Cleanup;
857  }
858 
859  // Reading is only supported for physical ports.
860  if (pPort->PortType != PortType_PhysicalCOM && pPort->PortType != PortType_PhysicalLPT)
861  {
862  dwErrorCode = ERROR_INVALID_HANDLE;
863  goto Cleanup;
864  }
865 
866  // If this is a serial port, a temporary file handle may be opened.
867  if (pPort->PortType == PortType_PhysicalCOM)
868  {
869  if (_CreateNonspooledPort(pPort))
870  {
871  bOpenedPort = TRUE;
872  }
873  else if (GetLastError() != ERROR_SUCCESS)
874  {
875  dwErrorCode = GetLastError();
876  goto Cleanup;
877  }
878  }
879 
880  // Pass the parameters to ReadFile.
881  if (!ReadFile(pPort->hFile, pBuffer, cbBuffer, pcbRead, NULL))
882  {
883  dwErrorCode = GetLastError();
884  ERR("ReadFile failed with error %lu!\n", dwErrorCode);
885  goto Cleanup;
886  }
887 
888 Cleanup:
889  if (bOpenedPort)
890  _ClosePortHandles(pPort);
891 
892  SetLastError(dwErrorCode);
893  return (dwErrorCode == ERROR_SUCCESS);
894 }
895 
896 BOOL WINAPI
897 LocalmonStartDocPort(HANDLE hPort, PWSTR pPrinterName, DWORD JobId, DWORD Level, PBYTE pDocInfo)
898 {
899  DWORD dwErrorCode;
900  PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo; // DOC_INFO_1W is the least common denominator for both DOC_INFO levels.
901  PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
902 
903  TRACE("LocalmonStartDocPort(%p, %S, %lu, %lu, %p)\n", hPort, pPrinterName, JobId, Level, pDocInfo);
904 
905  // Sanity checks
906  if (!pPort || !pPrinterName || (pPort->PortType == PortType_FILE && (!pDocInfo1 || !pDocInfo1->pOutputFile || !*pDocInfo1->pOutputFile)))
907  {
908  dwErrorCode = ERROR_INVALID_PARAMETER;
909  goto Cleanup;
910  }
911 
912  if (Level > 2)
913  {
914  dwErrorCode = ERROR_INVALID_LEVEL;
915  goto Cleanup;
916  }
917 
918  // Calling StartDocPort multiple times isn't considered a failure, but we don't need to do anything then.
919  if (pPort->bStartedDoc)
920  {
921  dwErrorCode = ERROR_SUCCESS;
922  goto Cleanup;
923  }
924 
925  // Open a handle to the given printer for later reporting our progress using SetJobW.
926  if (!OpenPrinterW(pPrinterName, &pPort->hPrinter, NULL))
927  {
928  dwErrorCode = GetLastError();
929  ERR("OpenPrinterW failed with error %lu!\n", dwErrorCode);
930  goto Cleanup;
931  }
932 
933  // We need our Job ID for SetJobW as well.
934  pPort->dwJobID = JobId;
935 
936  // Check the port type.
937  if (pPort->PortType == PortType_PhysicalLPT)
938  {
939  // Update the NONSPOOLED mapping if the port mapping has changed since our OpenPort call.
941  {
942  dwErrorCode = GetLastError();
943  goto Cleanup;
944  }
945 
946  // Update the transmission retry timeout as well.
948  }
949  else if(pPort->PortType == PortType_FILE)
950  {
951  // This is a FILE: port. Open the output file given in the Document Info.
953  if (pPort->hFile == INVALID_HANDLE_VALUE)
954  {
955  dwErrorCode = GetLastError();
956  goto Cleanup;
957  }
958  }
959  else
960  {
961  // This can be:
962  // - a physical COM port
963  // - a non-physical LPT port (e.g. with "net use LPT1 ...")
964  // - any other port (e.g. a file or a shared printer installed as a local port)
965  //
966  // For all these cases, we try to create a NONSPOOLED port per job.
967  // If _CreateNonspooledPort reports that no NONSPOOLED port is necessary, we can just open the port name.
968  if (!_CreateNonspooledPort(pPort))
969  {
970  if (GetLastError() == ERROR_SUCCESS)
971  {
973  if (pPort->hFile == INVALID_HANDLE_VALUE)
974  {
975  dwErrorCode = GetLastError();
976  goto Cleanup;
977  }
978  }
979  else
980  {
981  dwErrorCode = GetLastError();
982  goto Cleanup;
983  }
984  }
985  }
986 
987  // We were successful!
988  dwErrorCode = ERROR_SUCCESS;
989  pPort->bStartedDoc = TRUE;
990 
991 Cleanup:
992  SetLastError(dwErrorCode);
993  return (dwErrorCode == ERROR_SUCCESS);
994 }
995 
996 BOOL WINAPI
997 LocalmonWritePort(HANDLE hPort, PBYTE pBuffer, DWORD cbBuf, PDWORD pcbWritten)
998 {
999  BOOL bOpenedPort = FALSE;
1000  DWORD dwErrorCode;
1001  PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
1002 
1003  TRACE("LocalmonWritePort(%p, %p, %lu, %p)\n", hPort, pBuffer, cbBuf, pcbWritten);
1004 
1005  // Sanity checks
1006  if (!pPort || (cbBuf && !pBuffer) || !pcbWritten)
1007  {
1008  dwErrorCode = ERROR_INVALID_PARAMETER;
1009  goto Cleanup;
1010  }
1011 
1012  // If this is a serial port, a temporary file handle may be opened.
1013  if (pPort->PortType == PortType_PhysicalCOM)
1014  {
1015  if (_CreateNonspooledPort(pPort))
1016  {
1017  bOpenedPort = TRUE;
1018  }
1019  else if (GetLastError() != ERROR_SUCCESS)
1020  {
1021  dwErrorCode = GetLastError();
1022  goto Cleanup;
1023  }
1024  }
1025  else if (pPort->hFile == INVALID_HANDLE_VALUE)
1026  {
1027  // All other port types need to be opened already.
1028  dwErrorCode = ERROR_INVALID_PARAMETER;
1029  goto Cleanup;
1030  }
1031 
1032  // Pass the parameters to WriteFile.
1033  if (!WriteFile(pPort->hFile, pBuffer, cbBuf, pcbWritten, NULL))
1034  {
1035  dwErrorCode = GetLastError();
1036  ERR("WriteFile failed with error %lu!\n", dwErrorCode);
1037  goto Cleanup;
1038  }
1039 
1040  // If something was written down, we consider that a success, otherwise it's a timeout.
1041  if (*pcbWritten)
1042  dwErrorCode = ERROR_SUCCESS;
1043  else
1044  dwErrorCode = ERROR_TIMEOUT;
1045 
1046 Cleanup:
1047  if (bOpenedPort)
1048  _ClosePortHandles(pPort);
1049 
1050  SetLastError(dwErrorCode);
1051  return (dwErrorCode == ERROR_SUCCESS);
1052 }
#define ERROR_INVALID_PARAMETER
Definition: compat.h:91
PWSTR WINAPI AllocSplStr(PCWSTR pwszInput)
Definition: memory.c:56
#define TRUE
Definition: types.h:120
#define CloseHandle
Definition: compat.h:398
static __inline BOOL _IsLegacyPort(PCWSTR pwszPortName, PCWSTR pwszPortType)
Definition: ports.c:79
#define ERROR_SUCCESS
Definition: deptool.c:10
LIST_ENTRY Entry
Definition: precomp.h:50
BOOL WINAPI SetCommTimeouts(HANDLE hComm, LPCOMMTIMEOUTS lptimeouts)
Definition: comm.c:1060
static PLOCALMON_PORT _FindPort(PLOCALMON_HANDLE pLocalmon, PCWSTR pwszPortName)
Definition: ports.c:357
#define PORT_TYPE_WRITE
Definition: winspool.h:683
__wchar_t WCHAR
Definition: xmlstorage.h:180
#define DDD_REMOVE_DEFINITION
Definition: winbase.h:505
#define ERROR_TIMEOUT
Definition: winerror.h:941
#define _countof(array)
Definition: fontsub.cpp:30
BOOL WINAPI DllFreeSplMem(PVOID pMem)
Definition: memory.c:112
#define ERROR_INVALID_HANDLE
Definition: compat.h:88
LPWSTR pOutputFile
Definition: winspool.h:557
BOOL WINAPI LocalmonClosePort(HANDLE hPort)
Definition: ports.c:457
_Check_return_ _CRTIMP int __cdecl _wcsnicmp(_In_reads_or_z_(_MaxCount) const wchar_t *_Str1, _In_reads_or_z_(_MaxCount) const wchar_t *_Str2, _In_ size_t _MaxCount)
PVOID *typedef PWSTR
Definition: winlogon.h:66
BOOL WINAPI LocalmonWritePort(HANDLE hPort, PBYTE pBuffer, DWORD cbBuf, PDWORD pcbWritten)
Definition: ports.c:997
#define ERROR_NOT_ENOUGH_MEMORY
Definition: dderror.h:7
#define INVALID_HANDLE_VALUE
Definition: compat.h:391
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1059
_IRQL_requires_same_ typedef _In_ ULONG _In_ UCHAR Level
Definition: wmitypes.h:55
DWORD cbLocalPort
Definition: main.c:12
#define FILE_SHARE_WRITE
Definition: nt_native.h:681
void WINAPI EnterCriticalSection(LPCRITICAL_SECTION)
#define InsertTailList(ListHead, Entry)
BOOL WINAPI LocalmonSetPortTimeOuts(HANDLE hPort, LPCOMMTIMEOUTS lpCTO, DWORD Reserved)
Definition: ports.c:790
unsigned const void const unsigned long cbBuffer
Definition: t2embapi.h:116
DWORD DWORD
Definition: winlogon.h:84
#define FILE_SHARE_READ
Definition: compat.h:125
FORCEINLINE BOOLEAN RemoveEntryList(_In_ PLIST_ENTRY Entry)
Definition: rtlfuncs.h:105
DWORD GetLPTTransmissionRetryTimeout(VOID)
Definition: tools.c:85
#define FALSE
Definition: types.h:117
#define GENERIC_WRITE
Definition: nt_native.h:90
#define ERROR_UNKNOWN_PORT
Definition: winerror.h:1103
BOOL WINAPI LocalmonGetPrinterDataFromPort(HANDLE hPort, DWORD ControlID, PWSTR pValueName, PWSTR lpInBuffer, DWORD cbInBuffer, PWSTR lpOutBuffer, DWORD cbOutBuffer, PDWORD lpcbReturned)
Definition: ports.c:631
static DWORD dwPortInfo2Offsets[]
Definition: ports.c:19
WINBOOL WINAPI ClosePrinter(HANDLE hPrinter)
Definition: printers.c:12
BOOL WINAPI LocalmonOpenPort(HANDLE hMonitor, PWSTR pName, PHANDLE pHandle)
Definition: ports.c:685
smooth NULL
Definition: ftsmooth.c:416
PVOID pBuffer
DWORD WINAPI QueryDosDeviceW(LPCWSTR lpDeviceName, LPWSTR lpTargetPath, DWORD ucchMax)
Definition: dosdev.c:308
#define MAXDWORD
BOOL bStartedDoc
Definition: precomp.h:58
BOOL WINAPI GetCommTimeouts(HANDLE hComm, LPCOMMTIMEOUTS lptimeouts)
Definition: comm.c:1018
HANDLE WINAPI RevertToPrinterSelf(VOID)
Definition: context.c:64
static void _ClosePortHandles(PLOCALMON_PORT pPort)
Definition: ports.c:113
PFLT_MESSAGE_WAITER_QUEUE CONTAINING_RECORD(Csq, DEVICE_EXTENSION, IrpQueue)) -> WaiterQ.mLock) _IRQL_raises_(DISPATCH_LEVEL) VOID NTAPI FltpAcquireMessageWaiterLock(_In_ PIO_CSQ Csq, _Out_ PKIRQL Irql)
Definition: Messaging.c:560
BOOL WINAPI LocalmonStartDocPort(HANDLE hPort, PWSTR pPrinterName, DWORD JobId, DWORD Level, PBYTE pDocInfo)
Definition: ports.c:897
static const DWORD cchNonspooledPrefix
Definition: ports.c:12
BOOL WINAPI WriteFile(IN HANDLE hFile, IN LPCVOID lpBuffer, IN DWORD nNumberOfBytesToWrite OPTIONAL, OUT LPDWORD lpNumberOfBytesWritten OPTIONAL, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:24
LIST_ENTRY FilePorts
Definition: precomp.h:38
struct _LIST_ENTRY * Flink
Definition: typedefs.h:119
static BOOL _CreateNonspooledPort(PLOCALMON_PORT pPort)
Definition: ports.c:163
LIST_ENTRY RegistryPorts
Definition: precomp.h:39
#define TRACE(s)
Definition: solgame.cpp:4
static LPSTR pName
Definition: security.c:75
unsigned int BOOL
Definition: ntddk_ex.h:94
BOOL WINAPI LocalmonEnumPorts(HANDLE hMonitor, PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
Definition: ports.c:531
PBYTE WINAPI PackStrings(PWSTR *pSource, PBYTE pDest, const DWORD *DestOffsets, PBYTE pEnd)
Definition: tools.c:39
PWSTR pwszPortName
Definition: precomp.h:64
static void _LocalmonGetPortLevel2(PLOCALMON_PORT pPort, PPORT_INFO_2W *ppPortInfo, PBYTE *ppPortInfoEnd, PDWORD pcbNeeded)
Definition: ports.c:397
PLOCALMON_HANDLE pLocalmon
Definition: precomp.h:62
#define MAX_PATH
Definition: compat.h:26
#define CopyMemory
Definition: winbase.h:1633
PWSTR pwszMapping
Definition: precomp.h:63
struct _PORT_INFO_2W PORT_INFO_2W
#define SetLastError(x)
Definition: compat.h:409
CRITICAL_SECTION Section
Definition: precomp.h:37
ULONG PVOID Reserved
Definition: ntimage.h:533
static const WCHAR L[]
Definition: oid.c:1087
PVOID *typedef PHANDLE
Definition: ntsecpkg.h:414
#define JOB_CONTROL_SENT_TO_PRINTER
Definition: winspool.h:335
static __inline DWORD _GetNonspooledPortName(PCWSTR pwszPortNameWithoutColon, PWSTR *ppwszNonspooledPortName)
Definition: ports.c:44
#define GENERIC_READ
Definition: compat.h:124
Definition: typedefs.h:117
DWORD dwJobID
Definition: precomp.h:59
static const WCHAR Cleanup[]
Definition: register.c:80
HANDLE hFile
Definition: precomp.h:60
#define WINAPI
Definition: msvc.h:20
BOOL WINAPI DefineDosDeviceW(DWORD dwFlags, LPCWSTR lpDeviceName, LPCWSTR lpTargetPath)
Definition: dosdev.c:77
PCWSTR pwszLocalPort
Definition: main.c:14
#define ERR(fmt,...)
Definition: debug.h:109
_Check_return_ _CRTIMP int __cdecl wcscmp(_In_z_ const wchar_t *_Str1, _In_z_ const wchar_t *_Str2)
WINBOOL WINAPI OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
Definition: printers.c:635
#define CREATE_ALWAYS
Definition: disk.h:72
static DWORD dwPortInfo1Offsets[]
Definition: ports.c:14
DWORD *typedef HANDLE
Definition: winlogon.h:61
enum _LOCALMON_PORT::@4112 PortType
BOOL WINAPI DeviceIoControl(IN HANDLE hDevice, IN DWORD dwIoControlCode, IN LPVOID lpInBuffer OPTIONAL, IN DWORD nInBufferSize OPTIONAL, OUT LPVOID lpOutBuffer OPTIONAL, IN DWORD nOutBufferSize OPTIONAL, OUT LPDWORD lpBytesReturned OPTIONAL, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: deviceio.c:136
#define DDD_RAW_TARGET_PATH
Definition: winbase.h:504
#define PORT_TYPE_READ
Definition: winspool.h:684
_In_ DWORD _Out_ PDWORD pcbNeeded
Definition: winddi.h:3827
static void _LocalmonGetPortLevel1(PLOCALMON_PORT pPort, PPORT_INFO_1W *ppPortInfo, PBYTE *ppPortInfoEnd, PDWORD pcbNeeded)
Definition: ports.c:374
PCWSTR pwszLocalMonitor
Definition: main.c:13
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:254
HANDLE hPrinter
Definition: precomp.h:61
DWORD * PDWORD
Definition: pedump.c:68
BOOL WINAPI DllFreeSplStr(PWSTR pwszString)
Definition: memory.c:129
#define CreateFileW
Definition: compat.h:400
BOOL WINAPI LocalmonEndDocPort(HANDLE hPort)
Definition: ports.c:494
struct _DOC_INFO_1W * PDOC_INFO_1W
BOOL WINAPI ImpersonatePrinterClient(_In_ HANDLE hToken)
static const WCHAR wszNonspooledPrefix[]
Definition: ports.c:11
PVOID WINAPI DllAllocSplMem(DWORD dwBytes)
Definition: memory.c:95
const uint16_t * PCWSTR
Definition: typedefs.h:55
BOOL WINAPI LocalmonReadPort(HANDLE hPort, PBYTE pBuffer, DWORD cbBuffer, PDWORD pcbRead)
Definition: ports.c:844
void WINAPI LeaveCriticalSection(LPCRITICAL_SECTION)
#define ERROR_INVALID_LEVEL
Definition: winerror.h:196
GLfloat GLfloat p
Definition: glext.h:8902
DWORD WriteTotalTimeoutConstant
Definition: winbase.h:691
static BOOL _SetTransmissionRetryTimeout(PLOCALMON_PORT pPort)
Definition: ports.c:441
BOOL WINAPI ReadFile(IN HANDLE hFile, IN LPVOID lpBuffer, IN DWORD nNumberOfBytesToRead, OUT LPDWORD lpNumberOfBytesRead OPTIONAL, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:123
WINBOOL WINAPI SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob, DWORD Command)
BYTE * PBYTE
Definition: pedump.c:66
DWORD cbLocalMonitor
Definition: main.c:11
size_t __cdecl wcslen(_In_z_ const wchar_t *_Str)
struct _PORT_INFO_1W PORT_INFO_1W
base of all file and directory entries
Definition: entries.h:82
_Check_return_ _CRTIMP int __cdecl _wcsicmp(_In_z_ const wchar_t *_Str1, _In_z_ const wchar_t *_Str2)
struct _LOCALMON_HANDLE * PLOCALMON_HANDLE
DWORD GetPortNameWithoutColon(PCWSTR pwszPortName, PWSTR *ppwszPortNameWithoutColon)
Definition: tools.c:142
struct _LOCALMON_PORT * PLOCALMON_PORT
#define ERROR_INSUFFICIENT_BUFFER
Definition: dderror.h:10