2015-07-06 12:41:06 +00:00
/*
* PROJECT : ReactOS Local Port Monitor
2017-09-29 17:18:19 +00:00
* LICENSE : GPL - 2.0 + ( https : //spdx.org/licenses/GPL-2.0+)
2015-07-06 12:41:06 +00:00
* PURPOSE : Functions related to ports
2017-09-29 17:18:19 +00:00
* COPYRIGHT : Copyright 2015 - 2017 Colin Finck ( colin @ reactos . org )
2015-07-06 12:41:06 +00:00
*/
# include "precomp.h"
// Local Constants
static const WCHAR wszNonspooledPrefix [ ] = L " NONSPOOLED_ " ;
static const DWORD cchNonspooledPrefix = _countof ( wszNonspooledPrefix ) - 1 ;
2017-07-05 15:29:13 +00:00
static DWORD dwPortInfo1Offsets [ ] = {
FIELD_OFFSET ( PORT_INFO_1W , pName ) ,
MAXDWORD
} ;
static DWORD dwPortInfo2Offsets [ ] = {
FIELD_OFFSET ( PORT_INFO_2W , pPortName ) ,
FIELD_OFFSET ( PORT_INFO_2W , pMonitorName ) ,
FIELD_OFFSET ( PORT_INFO_2W , pDescription ) ,
MAXDWORD
} ;
2015-07-06 12:41:06 +00:00
/**
* @ name _GetNonspooledPortName
*
* Prepends " NONSPOOLED_ " to a port name without colon .
*
* @ param pwszPortNameWithoutColon
* Result of a previous GetPortNameWithoutColon call .
*
2015-07-15 18:15:33 +00:00
* @ param ppwszNonspooledPortName
* Pointer to a buffer that will contain the NONSPOOLED port name .
* You have to free this buffer using DllFreeSplMem .
*
2015-07-06 12:41:06 +00:00
* @ return
2015-07-15 18:15:33 +00:00
* ERROR_SUCCESS if the NONSPOOLED port name was successfully copied into the buffer .
* ERROR_NOT_ENOUGH_MEMORY if memory allocation failed .
2015-07-06 12:41:06 +00:00
*/
2015-07-15 18:15:33 +00:00
static __inline DWORD
_GetNonspooledPortName ( PCWSTR pwszPortNameWithoutColon , PWSTR * ppwszNonspooledPortName )
2015-07-06 12:41:06 +00:00
{
DWORD cchPortNameWithoutColon ;
cchPortNameWithoutColon = wcslen ( pwszPortNameWithoutColon ) ;
2015-07-15 18:15:33 +00:00
* ppwszNonspooledPortName = DllAllocSplMem ( ( cchNonspooledPrefix + cchPortNameWithoutColon + 1 ) * sizeof ( WCHAR ) ) ;
if ( ! * ppwszNonspooledPortName )
2015-07-06 12:41:06 +00:00
{
ERR ( " DllAllocSplMem failed with error %lu! \n " , GetLastError ( ) ) ;
2015-07-15 18:15:33 +00:00
return ERROR_NOT_ENOUGH_MEMORY ;
2015-07-06 12:41:06 +00:00
}
2015-07-15 18:15:33 +00:00
CopyMemory ( * ppwszNonspooledPortName , wszNonspooledPrefix , cchNonspooledPrefix * sizeof ( WCHAR ) ) ;
CopyMemory ( & ( * ppwszNonspooledPortName ) [ cchNonspooledPrefix ] , pwszPortNameWithoutColon , ( cchPortNameWithoutColon + 1 ) * sizeof ( WCHAR ) ) ;
return ERROR_SUCCESS ;
}
/**
* @ name _IsLegacyPort
*
* Checks if the given port name is a legacy port ( COM or LPT ) .
* This check is extra picky to not cause false positives ( like file name ports starting with " COM " or " LPT " ) .
*
* @ param pwszPortName
* The port name to check .
*
* @ param pwszPortType
* L " COM " or L " LPT "
*
* @ return
* TRUE if this is definitely the asked legacy port , FALSE if not .
*/
static __inline BOOL
_IsLegacyPort ( PCWSTR pwszPortName , PCWSTR pwszPortType )
{
const DWORD cchPortType = 3 ;
PCWSTR p = pwszPortName ;
// The port name must begin with pwszPortType.
if ( _wcsnicmp ( p , pwszPortType , cchPortType ) ! = 0 )
return FALSE ;
p + = cchPortType ;
// Now an arbitrary number of digits may follow.
while ( * p > = L ' 0 ' & & * p < = L ' 9 ' )
p + + ;
// Finally, the legacy port must be terminated by a colon.
if ( * p ! = ' : ' )
return FALSE ;
2015-07-06 12:41:06 +00:00
2015-07-15 18:15:33 +00:00
// If this is the end of the string, we have a legacy port.
p + + ;
return ( * p = = L ' \0 ' ) ;
2015-07-06 12:41:06 +00:00
}
/**
* @ name _ClosePortHandles
*
2015-07-07 10:30:30 +00:00
* Closes a port of any type if it ' s open .
2015-07-06 12:41:06 +00:00
* Removes any saved mapping or existing definition of a NONSPOOLED device mapping .
*
* @ param pPort
* The port you want to close .
*/
static void
_ClosePortHandles ( PLOCALMON_PORT pPort )
{
PWSTR pwszNonspooledPortName ;
PWSTR pwszPortNameWithoutColon ;
// A port is already fully closed if the file handle is invalid.
if ( pPort - > hFile = = INVALID_HANDLE_VALUE )
return ;
// Close the file handle.
CloseHandle ( pPort - > hFile ) ;
pPort - > hFile = INVALID_HANDLE_VALUE ;
// A NONSPOOLED port was only created if pwszMapping contains the current port mapping.
if ( ! pPort - > pwszMapping )
return ;
// Free the information about the current mapping.
DllFreeSplStr ( pPort - > pwszMapping ) ;
pPort - > pwszMapping = NULL ;
// Finally get the required strings and remove the DOS device definition for the NONSPOOLED port.
2015-07-15 18:15:33 +00:00
if ( GetPortNameWithoutColon ( pPort - > pwszPortName , & pwszPortNameWithoutColon ) = = ERROR_SUCCESS )
2015-07-06 12:41:06 +00:00
{
2015-07-15 18:15:33 +00:00
if ( _GetNonspooledPortName ( pwszPortNameWithoutColon , & pwszNonspooledPortName ) = = ERROR_SUCCESS )
2015-07-06 12:41:06 +00:00
{
DefineDosDeviceW ( DDD_REMOVE_DEFINITION , pwszNonspooledPortName , NULL ) ;
DllFreeSplMem ( pwszNonspooledPortName ) ;
}
DllFreeSplMem ( pwszPortNameWithoutColon ) ;
}
}
/**
* @ name _CreateNonspooledPort
*
* Queries the system - wide device definition of the given port .
* If such a definition exists , it ' s a legacy port remapped to a named pipe by the spooler .
* 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 ) .
*
* @ param pPort
* Pointer to the LOCALMON_PORT structure of the desired port .
*
* @ return
* TRUE if a NONSPOOLED port was successfully created , FALSE otherwise .
* A more specific error code can be obtained through GetLastError .
2015-07-15 18:15:33 +00:00
* In particular , if the return value is FALSE and GetLastError returns ERROR_SUCCESS , no NONSPOOLED port is needed for this port .
2015-07-06 12:41:06 +00:00
*/
static BOOL
_CreateNonspooledPort ( PLOCALMON_PORT pPort )
{
const WCHAR wszLocalSlashes [ ] = L " \\ \\ . \\ " ;
const DWORD cchLocalSlashes = _countof ( wszLocalSlashes ) - 1 ;
const WCHAR wszSpoolerNamedPipe [ ] = L " \\ Device \\ NamedPipe \\ Spooler \\ " ;
const DWORD cchSpoolerNamedPipe = _countof ( wszSpoolerNamedPipe ) - 1 ;
BOOL bReturnValue = FALSE ;
DWORD cchPortNameWithoutColon ;
DWORD dwErrorCode ;
HANDLE hToken = NULL ;
PWSTR p ;
PWSTR pwszDeviceMappings = NULL ;
PWSTR pwszNonspooledFileName = NULL ;
PWSTR pwszNonspooledPortName = NULL ;
PWSTR pwszPipeName = NULL ;
PWSTR pwszPortNameWithoutColon = NULL ;
// We need the port name without the trailing colon.
2015-07-15 18:15:33 +00:00
dwErrorCode = GetPortNameWithoutColon ( pPort - > pwszPortName , & pwszPortNameWithoutColon ) ;
if ( dwErrorCode = = ERROR_INVALID_PARAMETER )
2015-07-06 12:41:06 +00:00
{
2015-07-15 18:15:33 +00:00
// This port has no trailing colon, so we also need no NONSPOOLED mapping for it.
dwErrorCode = ERROR_SUCCESS ;
goto Cleanup ;
}
else if ( dwErrorCode ! = ERROR_SUCCESS )
{
// Another unexpected failure.
2015-07-06 12:41:06 +00:00
goto Cleanup ;
}
cchPortNameWithoutColon = wcslen ( pwszPortNameWithoutColon ) ;
// The spooler has usually remapped the legacy port to a named pipe of the format in wszSpoolerNamedPipe.
// Construct the device name of this pipe.
pwszPipeName = DllAllocSplMem ( ( cchSpoolerNamedPipe + cchPortNameWithoutColon + 1 ) * sizeof ( WCHAR ) ) ;
if ( ! pwszPipeName )
{
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY ;
ERR ( " DllAllocSplMem failed with error %lu! \n " , GetLastError ( ) ) ;
goto Cleanup ;
}
CopyMemory ( pwszPipeName , wszSpoolerNamedPipe , cchSpoolerNamedPipe * sizeof ( WCHAR ) ) ;
CopyMemory ( & pwszPipeName [ cchSpoolerNamedPipe ] , pwszPortNameWithoutColon , ( cchPortNameWithoutColon + 1 ) * sizeof ( WCHAR ) ) ;
// 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.
// Examples show that a value of MAX_PATH * sizeof(WCHAR) is usually taken here, so we have no other option either.
pwszDeviceMappings = DllAllocSplMem ( MAX_PATH * sizeof ( WCHAR ) ) ;
if ( ! pwszDeviceMappings )
{
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY ;
ERR ( " DllAllocSplMem failed with error %lu! \n " , GetLastError ( ) ) ;
goto Cleanup ;
}
// Switch to the SYSTEM context, because we're only interested in creating NONSPOOLED ports for system-wide ports.
// User-local ports (like _some_ redirected networked ones) aren't remapped by the spooler and can be opened directly.
hToken = RevertToPrinterSelf ( ) ;
if ( ! hToken )
{
dwErrorCode = GetLastError ( ) ;
ERR ( " RevertToPrinterSelf failed with error %lu! \n " , dwErrorCode ) ;
goto Cleanup ;
}
// 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.
if ( ! QueryDosDeviceW ( pwszPortNameWithoutColon , pwszDeviceMappings , MAX_PATH ) )
{
// No system-wide port exists, so we also need no NONSPOOLED mapping.
dwErrorCode = ERROR_SUCCESS ;
goto Cleanup ;
}
// Check if this port has already been opened by _CreateNonspooledPort previously.
if ( pPort - > pwszMapping )
{
// In this case, we just need to do something if the mapping has changed.
// Therefore, check if the stored mapping equals the current mapping.
if ( wcscmp ( pPort - > pwszMapping , pwszDeviceMappings ) = = 0 )
{
// We don't need to do anything in this case.
dwErrorCode = ERROR_SUCCESS ;
goto Cleanup ;
}
else
{
// Close the open file handle and free the memory for pwszMapping before remapping.
CloseHandle ( pPort - > hFile ) ;
pPort - > hFile = INVALID_HANDLE_VALUE ;
DllFreeSplStr ( pPort - > pwszMapping ) ;
pPort - > pwszMapping = NULL ;
}
}
// The port is usually mapped to the named pipe and this is how we received our data for printing.
// What we now need for accessing the actual port is the most recent mapping different from the named pipe.
p = pwszDeviceMappings ;
for ( ; ; )
{
if ( ! * p )
{
// We reached the end of the list without finding a mapping.
2022-05-02 12:26:10 +00:00
ERR ( " Can't find a suitable mapping for the port \" %S \" ! \n " , pPort - > pwszPortName ) ;
2015-07-06 12:41:06 +00:00
goto Cleanup ;
}
if ( _wcsicmp ( p , pwszPipeName ) ! = 0 )
break ;
// Advance to the next mapping in the list.
p + = wcslen ( p ) + 1 ;
}
// We now want to create a DOS device "NONSPOOLED_<PortName>" to this mapping, so that we're able to open it through CreateFileW.
2015-07-15 18:15:33 +00:00
dwErrorCode = _GetNonspooledPortName ( pwszPortNameWithoutColon , & pwszNonspooledPortName ) ;
if ( dwErrorCode ! = ERROR_SUCCESS )
2015-07-06 12:41:06 +00:00
goto Cleanup ;
// Delete a possibly existing NONSPOOLED device before creating the new one, so we don't stack up device definitions.
DefineDosDeviceW ( DDD_REMOVE_DEFINITION , pwszNonspooledPortName , NULL ) ;
if ( ! DefineDosDeviceW ( DDD_RAW_TARGET_PATH , pwszNonspooledPortName , p ) )
{
dwErrorCode = GetLastError ( ) ;
ERR ( " DefineDosDeviceW failed with error %lu! \n " , dwErrorCode ) ;
goto Cleanup ;
}
// This is all we needed to do in SYSTEM context.
ImpersonatePrinterClient ( hToken ) ;
hToken = NULL ;
// Construct the file name to our created device for CreateFileW.
pwszNonspooledFileName = DllAllocSplMem ( ( cchLocalSlashes + cchNonspooledPrefix + cchPortNameWithoutColon + 1 ) * sizeof ( WCHAR ) ) ;
if ( ! pwszNonspooledFileName )
{
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY ;
ERR ( " DllAllocSplMem failed with error %lu! \n " , GetLastError ( ) ) ;
goto Cleanup ;
}
CopyMemory ( pwszNonspooledFileName , wszLocalSlashes , cchLocalSlashes * sizeof ( WCHAR ) ) ;
CopyMemory ( & pwszNonspooledFileName [ cchLocalSlashes ] , wszNonspooledPrefix , cchNonspooledPrefix * sizeof ( WCHAR ) ) ;
CopyMemory ( & pwszNonspooledFileName [ cchLocalSlashes + cchNonspooledPrefix ] , pwszPortNameWithoutColon , ( cchPortNameWithoutColon + 1 ) * sizeof ( WCHAR ) ) ;
// Finally open it for reading and writing.
2015-07-15 18:45:37 +00:00
pPort - > hFile = CreateFileW ( pwszNonspooledFileName , GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ , NULL , CREATE_ALWAYS , 0 , NULL ) ;
2015-07-06 12:41:06 +00:00
if ( pPort - > hFile = = INVALID_HANDLE_VALUE )
{
dwErrorCode = GetLastError ( ) ;
ERR ( " CreateFileW failed with error %lu! \n " , dwErrorCode ) ;
goto Cleanup ;
}
// Store the current mapping of the port, so that we can check if it has changed.
pPort - > pwszMapping = AllocSplStr ( pwszDeviceMappings ) ;
if ( ! pPort - > pwszMapping )
{
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY ;
goto Cleanup ;
}
bReturnValue = TRUE ;
dwErrorCode = ERROR_SUCCESS ;
Cleanup :
if ( hToken )
ImpersonatePrinterClient ( hToken ) ;
if ( pwszDeviceMappings )
DllFreeSplMem ( pwszDeviceMappings ) ;
if ( pwszNonspooledFileName )
DllFreeSplMem ( pwszNonspooledFileName ) ;
if ( pwszNonspooledPortName )
DllFreeSplMem ( pwszNonspooledPortName ) ;
if ( pwszPipeName )
DllFreeSplMem ( pwszPipeName ) ;
if ( pwszPortNameWithoutColon )
DllFreeSplMem ( pwszPortNameWithoutColon ) ;
SetLastError ( dwErrorCode ) ;
return bReturnValue ;
}
static PLOCALMON_PORT
_FindPort ( PLOCALMON_HANDLE pLocalmon , PCWSTR pwszPortName )
{
PLIST_ENTRY pEntry ;
PLOCALMON_PORT pPort ;
2015-07-07 10:30:30 +00:00
for ( pEntry = pLocalmon - > RegistryPorts . Flink ; pEntry ! = & pLocalmon - > RegistryPorts ; pEntry = pEntry - > Flink )
2015-07-06 12:41:06 +00:00
{
pPort = CONTAINING_RECORD ( pEntry , LOCALMON_PORT , Entry ) ;
if ( wcscmp ( pPort - > pwszPortName , pwszPortName ) = = 0 )
return pPort ;
}
return NULL ;
}
2017-07-05 15:29:13 +00:00
static void
_LocalmonGetPortLevel1 ( PLOCALMON_PORT pPort , PPORT_INFO_1W * ppPortInfo , PBYTE * ppPortInfoEnd , PDWORD pcbNeeded )
2015-07-06 12:41:06 +00:00
{
DWORD cbPortName ;
2019-01-06 11:18:40 +00:00
PCWSTR pwszStrings [ 1 ] ;
2015-07-06 12:41:06 +00:00
2017-07-05 15:29:13 +00:00
// Calculate the string lengths.
if ( ! ppPortInfo )
2015-07-06 12:41:06 +00:00
{
cbPortName = ( wcslen ( pPort - > pwszPortName ) + 1 ) * sizeof ( WCHAR ) ;
2017-07-05 15:29:13 +00:00
* pcbNeeded + = sizeof ( PORT_INFO_1W ) + cbPortName ;
return ;
2015-07-06 12:41:06 +00:00
}
2017-07-05 15:29:13 +00:00
// Set the pName field.
pwszStrings [ 0 ] = pPort - > pwszPortName ;
2015-07-06 12:41:06 +00:00
2017-07-05 15:29:13 +00:00
// Copy the structure and advance to the next one in the output buffer.
* ppPortInfoEnd = PackStrings ( pwszStrings , ( PBYTE ) ( * ppPortInfo ) , dwPortInfo1Offsets , * ppPortInfoEnd ) ;
( * ppPortInfo ) + + ;
2015-07-06 12:41:06 +00:00
}
2017-07-05 15:29:13 +00:00
static void
_LocalmonGetPortLevel2 ( PLOCALMON_PORT pPort , PPORT_INFO_2W * ppPortInfo , PBYTE * ppPortInfoEnd , PDWORD pcbNeeded )
2015-07-06 12:41:06 +00:00
{
DWORD cbPortName ;
2019-01-06 11:18:40 +00:00
PCWSTR pwszStrings [ 3 ] ;
2015-07-06 12:41:06 +00:00
2017-07-05 15:29:13 +00:00
// Calculate the string lengths.
if ( ! ppPortInfo )
2015-07-06 12:41:06 +00:00
{
cbPortName = ( wcslen ( pPort - > pwszPortName ) + 1 ) * sizeof ( WCHAR ) ;
2017-07-05 15:29:13 +00:00
* pcbNeeded + = sizeof ( PORT_INFO_2W ) + cbPortName + cbLocalMonitor + cbLocalPort ;
return ;
2015-07-06 12:41:06 +00:00
}
2017-07-05 15:29:13 +00:00
// All local ports are writable and readable.
( * ppPortInfo ) - > fPortType = PORT_TYPE_WRITE | PORT_TYPE_READ ;
( * ppPortInfo ) - > Reserved = 0 ;
2015-07-06 12:41:06 +00:00
2017-07-05 15:29:13 +00:00
// Set the pPortName field.
pwszStrings [ 0 ] = pPort - > pwszPortName ;
2015-07-06 12:41:06 +00:00
2017-07-05 15:29:13 +00:00
// Set the pMonitorName field.
pwszStrings [ 1 ] = ( PWSTR ) pwszLocalMonitor ;
2015-07-06 12:41:06 +00:00
2017-07-05 15:29:13 +00:00
// Set the pDescription field.
pwszStrings [ 2 ] = ( PWSTR ) pwszLocalPort ;
2015-07-06 12:41:06 +00:00
2017-07-05 15:29:13 +00:00
// Copy the structure and advance to the next one in the output buffer.
* ppPortInfoEnd = PackStrings ( pwszStrings , ( PBYTE ) ( * ppPortInfo ) , dwPortInfo2Offsets , * ppPortInfoEnd ) ;
( * ppPortInfo ) + + ;
2015-07-06 12:41:06 +00:00
}
/**
* @ name _SetTransmissionRetryTimeout
*
* Checks if the given port is a physical one and sets the transmission retry timeout in this case using the value from registry .
*
* @ param pPort
* The port to operate on .
*
* @ return
* TRUE if the given port is a physical one , FALSE otherwise .
*/
static BOOL
_SetTransmissionRetryTimeout ( PLOCALMON_PORT pPort )
{
COMMTIMEOUTS CommTimeouts ;
// Get the timeout from the port.
if ( ! GetCommTimeouts ( pPort - > hFile , & CommTimeouts ) )
return FALSE ;
// Set the timeout using the value from registry.
CommTimeouts . WriteTotalTimeoutConstant = GetLPTTransmissionRetryTimeout ( ) * 1000 ;
SetCommTimeouts ( pPort - > hFile , & CommTimeouts ) ;
return TRUE ;
}
BOOL WINAPI
LocalmonClosePort ( HANDLE hPort )
{
2015-07-07 10:30:30 +00:00
PLOCALMON_PORT pPort = ( PLOCALMON_PORT ) hPort ;
TRACE ( " LocalmonClosePort(%p) \n " , hPort ) ;
2015-07-06 12:41:06 +00:00
// Sanity checks
2015-07-07 10:30:30 +00:00
if ( ! pPort )
2015-07-06 12:41:06 +00:00
{
SetLastError ( ERROR_INVALID_PARAMETER ) ;
return FALSE ;
}
// Close the file handle, free memory for pwszMapping and delete any NONSPOOLED port.
_ClosePortHandles ( pPort ) ;
// Close any open printer handle.
if ( pPort - > hPrinter )
2015-07-07 10:30:30 +00:00
{
2015-07-06 12:41:06 +00:00
ClosePrinter ( pPort - > hPrinter ) ;
2015-07-07 10:30:30 +00:00
pPort - > hPrinter = NULL ;
}
2015-07-06 12:41:06 +00:00
2015-07-15 18:15:33 +00:00
// Free virtual FILE: ports which were created in LocalmonOpenPort.
2015-07-07 10:30:30 +00:00
if ( pPort - > PortType = = PortType_FILE )
{
EnterCriticalSection ( & pPort - > pLocalmon - > Section ) ;
RemoveEntryList ( & pPort - > Entry ) ;
LeaveCriticalSection ( & pPort - > pLocalmon - > Section ) ;
2017-02-19 10:31:37 +00:00
DllFreeSplMem ( pPort ) ;
2015-07-07 10:30:30 +00:00
}
2015-07-06 12:41:06 +00:00
SetLastError ( ERROR_SUCCESS ) ;
return TRUE ;
}
BOOL WINAPI
LocalmonEndDocPort ( HANDLE hPort )
{
PLOCALMON_PORT pPort = ( PLOCALMON_PORT ) hPort ;
2015-07-07 10:30:30 +00:00
TRACE ( " LocalmonEndDocPort(%p) \n " , hPort ) ;
2015-07-06 12:41:06 +00:00
// Sanity checks
if ( ! pPort )
{
SetLastError ( ERROR_INVALID_PARAMETER ) ;
return FALSE ;
}
// Ending a document requires starting it first :-P
if ( pPort - > bStartedDoc )
{
// Close all ports opened in StartDocPort.
// That is, all but physical LPT ports (opened in OpenPort).
if ( pPort - > PortType ! = PortType_PhysicalLPT )
_ClosePortHandles ( pPort ) ;
// Report our progress.
SetJobW ( pPort - > hPrinter , pPort - > dwJobID , 0 , NULL , JOB_CONTROL_SENT_TO_PRINTER ) ;
// We're done with the printer.
ClosePrinter ( pPort - > hPrinter ) ;
pPort - > hPrinter = NULL ;
2015-07-15 18:45:37 +00:00
// A new document can now be started again.
pPort - > bStartedDoc = FALSE ;
2015-07-06 12:41:06 +00:00
}
SetLastError ( ERROR_SUCCESS ) ;
return TRUE ;
}
BOOL WINAPI
LocalmonEnumPorts ( HANDLE hMonitor , PWSTR pName , DWORD Level , PBYTE pPorts , DWORD cbBuf , PDWORD pcbNeeded , PDWORD pcReturned )
{
DWORD dwErrorCode ;
2017-07-05 15:29:13 +00:00
PBYTE pPortInfoEnd ;
PLIST_ENTRY pEntry ;
2015-07-07 10:30:30 +00:00
PLOCALMON_HANDLE pLocalmon = ( PLOCALMON_HANDLE ) hMonitor ;
2017-07-05 15:29:13 +00:00
PLOCALMON_PORT pPort ;
2015-07-07 10:30:30 +00:00
TRACE ( " LocalmonEnumPorts(%p, %S, %lu, %p, %lu, %p, %p) \n " , hMonitor , pName , Level , pPorts , cbBuf , pcbNeeded , pcReturned ) ;
2015-07-06 12:41:06 +00:00
2017-07-05 15:29:13 +00:00
// Windows Server 2003's Local Port Monitor does absolutely no sanity checks here, not even for the Level parameter.
// As we implement a more modern MONITOR2-based Port Monitor, check at least our hMonitor.
if ( ! pLocalmon )
2015-07-06 12:41:06 +00:00
{
2017-07-05 15:29:13 +00:00
dwErrorCode = ERROR_INVALID_HANDLE ;
2015-07-06 12:41:06 +00:00
goto Cleanup ;
}
// Begin counting.
* pcbNeeded = 0 ;
* pcReturned = 0 ;
2015-07-07 10:30:30 +00:00
EnterCriticalSection ( & pLocalmon - > Section ) ;
2017-07-05 15:29:13 +00:00
// Count the required buffer size and the number of ports.
for ( pEntry = pLocalmon - > RegistryPorts . Flink ; pEntry ! = & pLocalmon - > RegistryPorts ; pEntry = pEntry - > Flink )
{
pPort = CONTAINING_RECORD ( pEntry , LOCALMON_PORT , Entry ) ;
if ( Level = = 1 )
_LocalmonGetPortLevel1 ( pPort , NULL , NULL , pcbNeeded ) ;
else if ( Level = = 2 )
_LocalmonGetPortLevel2 ( pPort , NULL , NULL , pcbNeeded ) ;
}
// Check if the supplied buffer is large enough.
if ( cbBuf < * pcbNeeded )
{
LeaveCriticalSection ( & pLocalmon - > Section ) ;
dwErrorCode = ERROR_INSUFFICIENT_BUFFER ;
goto Cleanup ;
}
// Copy over the Port information.
pPortInfoEnd = & pPorts [ * pcbNeeded ] ;
for ( pEntry = pLocalmon - > RegistryPorts . Flink ; pEntry ! = & pLocalmon - > RegistryPorts ; pEntry = pEntry - > Flink )
{
pPort = CONTAINING_RECORD ( pEntry , LOCALMON_PORT , Entry ) ;
if ( Level = = 1 )
_LocalmonGetPortLevel1 ( pPort , ( PPORT_INFO_1W * ) & pPorts , & pPortInfoEnd , NULL ) ;
else if ( Level = = 2 )
_LocalmonGetPortLevel2 ( pPort , ( PPORT_INFO_2W * ) & pPorts , & pPortInfoEnd , NULL ) ;
( * pcReturned ) + + ;
}
2015-07-07 10:30:30 +00:00
LeaveCriticalSection ( & pLocalmon - > Section ) ;
2017-07-05 15:29:13 +00:00
dwErrorCode = ERROR_SUCCESS ;
2015-07-06 12:41:06 +00:00
Cleanup :
SetLastError ( dwErrorCode ) ;
return ( dwErrorCode = = ERROR_SUCCESS ) ;
}
/*
2015-07-07 10:30:30 +00:00
* @ name LocalmonGetPrinterDataFromPort
2015-07-06 12:41:06 +00:00
*
* Performs a DeviceIoControl call for the given port .
*
* @ param hPort
* The port to operate on .
*
* @ param ControlID
* The dwIoControlCode passed to DeviceIoControl . Must not be zero !
*
* @ param pValueName
* This parameter is ignored .
*
* @ param lpInBuffer
* The lpInBuffer passed to DeviceIoControl .
*
* @ param cbInBuffer
* The nInBufferSize passed to DeviceIoControl .
*
* @ param lpOutBuffer
* The lpOutBuffer passed to DeviceIoControl .
*
* @ param cbOutBuffer
* The nOutBufferSize passed to DeviceIoControl .
*
* @ param lpcbReturned
* The lpBytesReturned passed to DeviceIoControl . Must not be zero !
*
* @ return
* TRUE if the DeviceIoControl call was successful , FALSE otherwise .
* A more specific error code can be obtained through GetLastError .
*/
BOOL WINAPI
LocalmonGetPrinterDataFromPort ( HANDLE hPort , DWORD ControlID , PWSTR pValueName , PWSTR lpInBuffer , DWORD cbInBuffer , PWSTR lpOutBuffer , DWORD cbOutBuffer , PDWORD lpcbReturned )
{
BOOL bOpenedPort = FALSE ;
DWORD dwErrorCode ;
PLOCALMON_PORT pPort = ( PLOCALMON_PORT ) hPort ;
2015-07-07 10:30:30 +00:00
TRACE ( " LocalmonGetPrinterDataFromPort(%p, %lu, %p, %p, %lu, %p, %lu, %p) \n " , hPort , ControlID , pValueName , lpInBuffer , cbInBuffer , lpOutBuffer , cbOutBuffer , lpcbReturned ) ;
2015-07-06 12:41:06 +00:00
// Sanity checks
if ( ! pPort | | ! ControlID | | ! lpcbReturned )
{
dwErrorCode = ERROR_INVALID_PARAMETER ;
goto Cleanup ;
}
// If this is a serial port, a temporary file handle may be opened.
if ( pPort - > PortType = = PortType_PhysicalCOM )
{
if ( _CreateNonspooledPort ( pPort ) )
{
bOpenedPort = TRUE ;
}
else if ( GetLastError ( ) ! = ERROR_SUCCESS )
{
dwErrorCode = GetLastError ( ) ;
goto Cleanup ;
}
}
else if ( pPort - > hFile = = INVALID_HANDLE_VALUE )
{
// All other port types need to be opened already.
dwErrorCode = ERROR_INVALID_PARAMETER ;
goto Cleanup ;
}
// Pass the parameters to DeviceIoControl.
if ( ! DeviceIoControl ( pPort - > hFile , ControlID , lpInBuffer , cbInBuffer , lpOutBuffer , cbOutBuffer , lpcbReturned , NULL ) )
{
dwErrorCode = GetLastError ( ) ;
ERR ( " DeviceIoControl failed with error %lu! \n " , dwErrorCode ) ;
goto Cleanup ;
}
dwErrorCode = ERROR_SUCCESS ;
Cleanup :
if ( bOpenedPort )
_ClosePortHandles ( pPort ) ;
SetLastError ( dwErrorCode ) ;
return ( dwErrorCode = = ERROR_SUCCESS ) ;
}
BOOL WINAPI
LocalmonOpenPort ( HANDLE hMonitor , PWSTR pName , PHANDLE pHandle )
{
DWORD dwErrorCode ;
PLOCALMON_HANDLE pLocalmon = ( PLOCALMON_HANDLE ) hMonitor ;
PLOCALMON_PORT pPort ;
2015-07-07 10:30:30 +00:00
TRACE ( " LocalmonOpenPort(%p, %S, %p) \n " , hMonitor , pName , pHandle ) ;
2015-07-06 12:41:06 +00:00
// Sanity checks
if ( ! pLocalmon | | ! pName | | ! pHandle )
{
dwErrorCode = ERROR_INVALID_PARAMETER ;
goto Cleanup ;
}
2017-07-05 15:29:13 +00:00
EnterCriticalSection ( & pLocalmon - > Section ) ;
2015-07-06 12:41:06 +00:00
// Check if this is a FILE: port.
if ( _wcsicmp ( pName , L " FILE: " ) = = 0 )
{
// For FILE:, we create a virtual port for each request.
pPort = DllAllocSplMem ( sizeof ( LOCALMON_PORT ) ) ;
2015-07-07 10:30:30 +00:00
pPort - > pLocalmon = pLocalmon ;
2015-07-06 12:41:06 +00:00
pPort - > PortType = PortType_FILE ;
pPort - > hFile = INVALID_HANDLE_VALUE ;
// Add it to the list of file ports.
InsertTailList ( & pLocalmon - > FilePorts , & pPort - > Entry ) ;
}
else
{
// Check if the port name is valid.
pPort = _FindPort ( pLocalmon , pName ) ;
if ( ! pPort )
{
2015-07-07 10:30:30 +00:00
LeaveCriticalSection ( & pLocalmon - > Section ) ;
2015-07-06 12:41:06 +00:00
dwErrorCode = ERROR_UNKNOWN_PORT ;
goto Cleanup ;
}
// Even if this API is called OpenPort, port file handles aren't always opened here :-P
// Windows only does this for physical LPT ports here to enable bidirectional communication with the printer outside of jobs (using ReadPort and WritePort).
// The others are only opened per job in StartDocPort.
2015-07-15 18:15:33 +00:00
if ( _IsLegacyPort ( pName , L " LPT " ) )
2015-07-06 12:41:06 +00:00
{
// Try to create a NONSPOOLED port and open it.
if ( _CreateNonspooledPort ( pPort ) )
{
// Set the transmission retry timeout for the ReadPort and WritePort calls.
// This also checks if this port is a physical one.
if ( _SetTransmissionRetryTimeout ( pPort ) )
{
// This is definitely a physical LPT port!
pPort - > PortType = PortType_PhysicalLPT ;
}
else
{
2015-07-07 10:30:30 +00:00
// This is no physical port, so don't keep its handle open.
2015-07-06 12:41:06 +00:00
_ClosePortHandles ( pPort ) ;
}
}
else if ( GetLastError ( ) ! = ERROR_SUCCESS )
{
2015-07-07 10:30:30 +00:00
LeaveCriticalSection ( & pLocalmon - > Section ) ;
2015-07-06 12:41:06 +00:00
dwErrorCode = GetLastError ( ) ;
goto Cleanup ;
}
}
2015-07-15 18:15:33 +00:00
else if ( _IsLegacyPort ( pName , L " COM " ) )
2015-07-06 12:41:06 +00:00
{
2015-07-15 18:15:33 +00:00
// COM ports can't be redirected over the network, so this is a physical one.
2015-07-06 12:41:06 +00:00
pPort - > PortType = PortType_PhysicalCOM ;
}
}
2015-07-07 10:30:30 +00:00
LeaveCriticalSection ( & pLocalmon - > Section ) ;
2015-07-06 12:41:06 +00:00
// Return our fetched LOCALMON_PORT structure in the handle.
* pHandle = ( PHANDLE ) pPort ;
dwErrorCode = ERROR_SUCCESS ;
Cleanup :
SetLastError ( dwErrorCode ) ;
return ( dwErrorCode = = ERROR_SUCCESS ) ;
}
/*
* @ name LocalmonSetPortTimeOuts
*
* Performs a SetCommTimeouts call for the given port .
*
* @ param hPort
* The port to operate on .
*
* @ param lpCTO
* Pointer to a COMMTIMEOUTS structure that is passed to SetCommTimeouts .
*
* @ param Reserved
* Reserved parameter , must be 0.
*
* @ return
* TRUE if the SetCommTimeouts call was successful , FALSE otherwise .
* A more specific error code can be obtained through GetLastError .
*/
BOOL WINAPI
LocalmonSetPortTimeOuts ( HANDLE hPort , LPCOMMTIMEOUTS lpCTO , DWORD Reserved )
{
BOOL bOpenedPort = FALSE ;
DWORD dwErrorCode ;
PLOCALMON_PORT pPort = ( PLOCALMON_PORT ) hPort ;
2015-07-07 10:30:30 +00:00
TRACE ( " LocalmonSetPortTimeOuts(%p, %p, %lu) \n " , hPort , lpCTO , Reserved ) ;
2015-07-06 12:41:06 +00:00
// Sanity checks
if ( ! pPort | | ! lpCTO )
{
dwErrorCode = ERROR_INVALID_PARAMETER ;
goto Cleanup ;
}
// If this is a serial port, a temporary file handle may be opened.
if ( pPort - > PortType = = PortType_PhysicalCOM )
{
if ( _CreateNonspooledPort ( pPort ) )
{
bOpenedPort = TRUE ;
}
else if ( GetLastError ( ) ! = ERROR_SUCCESS )
{
dwErrorCode = GetLastError ( ) ;
goto Cleanup ;
}
}
else if ( pPort - > hFile = = INVALID_HANDLE_VALUE )
{
// All other port types need to be opened already.
dwErrorCode = ERROR_INVALID_PARAMETER ;
goto Cleanup ;
}
// Finally pass the parameters to SetCommTimeouts.
if ( ! SetCommTimeouts ( pPort - > hFile , lpCTO ) )
{
dwErrorCode = GetLastError ( ) ;
ERR ( " SetCommTimeouts failed with error %lu! \n " , dwErrorCode ) ;
goto Cleanup ;
}
dwErrorCode = ERROR_SUCCESS ;
Cleanup :
if ( bOpenedPort )
_ClosePortHandles ( pPort ) ;
SetLastError ( dwErrorCode ) ;
return ( dwErrorCode = = ERROR_SUCCESS ) ;
}
BOOL WINAPI
LocalmonReadPort ( HANDLE hPort , PBYTE pBuffer , DWORD cbBuffer , PDWORD pcbRead )
{
BOOL bOpenedPort = FALSE ;
DWORD dwErrorCode ;
PLOCALMON_PORT pPort = ( PLOCALMON_PORT ) hPort ;
2015-07-07 10:30:30 +00:00
TRACE ( " LocalmonReadPort(%p, %p, %lu, %p) \n " , hPort , pBuffer , cbBuffer , pcbRead ) ;
2015-07-06 12:41:06 +00:00
// Sanity checks
if ( ! pPort | | ( cbBuffer & & ! pBuffer ) | | ! pcbRead )
{
dwErrorCode = ERROR_INVALID_PARAMETER ;
goto Cleanup ;
}
// Reading is only supported for physical ports.
if ( pPort - > PortType ! = PortType_PhysicalCOM & & pPort - > PortType ! = PortType_PhysicalLPT )
{
dwErrorCode = ERROR_INVALID_HANDLE ;
goto Cleanup ;
}
// If this is a serial port, a temporary file handle may be opened.
if ( pPort - > PortType = = PortType_PhysicalCOM )
{
if ( _CreateNonspooledPort ( pPort ) )
{
bOpenedPort = TRUE ;
}
else if ( GetLastError ( ) ! = ERROR_SUCCESS )
{
dwErrorCode = GetLastError ( ) ;
goto Cleanup ;
}
}
// Pass the parameters to ReadFile.
if ( ! ReadFile ( pPort - > hFile , pBuffer , cbBuffer , pcbRead , NULL ) )
{
dwErrorCode = GetLastError ( ) ;
ERR ( " ReadFile failed with error %lu! \n " , dwErrorCode ) ;
goto Cleanup ;
}
Cleanup :
if ( bOpenedPort )
_ClosePortHandles ( pPort ) ;
SetLastError ( dwErrorCode ) ;
return ( dwErrorCode = = ERROR_SUCCESS ) ;
}
BOOL WINAPI
LocalmonStartDocPort ( HANDLE hPort , PWSTR pPrinterName , DWORD JobId , DWORD Level , PBYTE pDocInfo )
{
DWORD dwErrorCode ;
PDOC_INFO_1W pDocInfo1 = ( PDOC_INFO_1W ) pDocInfo ; // DOC_INFO_1W is the least common denominator for both DOC_INFO levels.
PLOCALMON_PORT pPort = ( PLOCALMON_PORT ) hPort ;
2015-07-07 10:30:30 +00:00
TRACE ( " LocalmonStartDocPort(%p, %S, %lu, %lu, %p) \n " , hPort , pPrinterName , JobId , Level , pDocInfo ) ;
2015-07-06 12:41:06 +00:00
// Sanity checks
if ( ! pPort | | ! pPrinterName | | ( pPort - > PortType = = PortType_FILE & & ( ! pDocInfo1 | | ! pDocInfo1 - > pOutputFile | | ! * pDocInfo1 - > pOutputFile ) ) )
{
dwErrorCode = ERROR_INVALID_PARAMETER ;
goto Cleanup ;
}
if ( Level > 2 )
{
dwErrorCode = ERROR_INVALID_LEVEL ;
goto Cleanup ;
}
// Calling StartDocPort multiple times isn't considered a failure, but we don't need to do anything then.
if ( pPort - > bStartedDoc )
{
dwErrorCode = ERROR_SUCCESS ;
goto Cleanup ;
}
// Open a handle to the given printer for later reporting our progress using SetJobW.
if ( ! OpenPrinterW ( pPrinterName , & pPort - > hPrinter , NULL ) )
{
dwErrorCode = GetLastError ( ) ;
ERR ( " OpenPrinterW failed with error %lu! \n " , dwErrorCode ) ;
goto Cleanup ;
}
// We need our Job ID for SetJobW as well.
pPort - > dwJobID = JobId ;
// Check the port type.
if ( pPort - > PortType = = PortType_PhysicalLPT )
{
// Update the NONSPOOLED mapping if the port mapping has changed since our OpenPort call.
if ( ! _CreateNonspooledPort ( pPort ) & & GetLastError ( ) ! = ERROR_SUCCESS )
{
dwErrorCode = GetLastError ( ) ;
goto Cleanup ;
}
// Update the transmission retry timeout as well.
_SetTransmissionRetryTimeout ( pPort ) ;
}
else if ( pPort - > PortType = = PortType_FILE )
{
// This is a FILE: port. Open the output file given in the Document Info.
2015-07-15 18:45:37 +00:00
pPort - > hFile = CreateFileW ( pDocInfo1 - > pOutputFile , GENERIC_WRITE , FILE_SHARE_READ | FILE_SHARE_WRITE , NULL , CREATE_ALWAYS , 0 , NULL ) ;
2015-07-06 12:41:06 +00:00
if ( pPort - > hFile = = INVALID_HANDLE_VALUE )
{
dwErrorCode = GetLastError ( ) ;
goto Cleanup ;
}
}
else
{
2015-07-15 18:15:33 +00:00
// This can be:
// - a physical COM port
// - a non-physical LPT port (e.g. with "net use LPT1 ...")
// - any other port (e.g. a file or a shared printer installed as a local port)
//
// For all these cases, we try to create a NONSPOOLED port per job.
// If _CreateNonspooledPort reports that no NONSPOOLED port is necessary, we can just open the port name.
2015-07-06 12:41:06 +00:00
if ( ! _CreateNonspooledPort ( pPort ) )
{
if ( GetLastError ( ) = = ERROR_SUCCESS )
{
2015-07-15 18:45:37 +00:00
pPort - > hFile = CreateFileW ( pPort - > pwszPortName , GENERIC_WRITE , FILE_SHARE_READ , NULL , CREATE_ALWAYS , 0 , NULL ) ;
2015-07-06 12:41:06 +00:00
if ( pPort - > hFile = = INVALID_HANDLE_VALUE )
{
dwErrorCode = GetLastError ( ) ;
goto Cleanup ;
}
}
else
{
dwErrorCode = GetLastError ( ) ;
goto Cleanup ;
}
}
}
// We were successful!
dwErrorCode = ERROR_SUCCESS ;
pPort - > bStartedDoc = TRUE ;
Cleanup :
SetLastError ( dwErrorCode ) ;
return ( dwErrorCode = = ERROR_SUCCESS ) ;
}
BOOL WINAPI
LocalmonWritePort ( HANDLE hPort , PBYTE pBuffer , DWORD cbBuf , PDWORD pcbWritten )
{
BOOL bOpenedPort = FALSE ;
DWORD dwErrorCode ;
PLOCALMON_PORT pPort = ( PLOCALMON_PORT ) hPort ;
2015-07-07 10:30:30 +00:00
TRACE ( " LocalmonWritePort(%p, %p, %lu, %p) \n " , hPort , pBuffer , cbBuf , pcbWritten ) ;
2015-07-06 12:41:06 +00:00
// Sanity checks
if ( ! pPort | | ( cbBuf & & ! pBuffer ) | | ! pcbWritten )
{
dwErrorCode = ERROR_INVALID_PARAMETER ;
goto Cleanup ;
}
// If this is a serial port, a temporary file handle may be opened.
if ( pPort - > PortType = = PortType_PhysicalCOM )
{
if ( _CreateNonspooledPort ( pPort ) )
{
bOpenedPort = TRUE ;
}
else if ( GetLastError ( ) ! = ERROR_SUCCESS )
{
dwErrorCode = GetLastError ( ) ;
goto Cleanup ;
}
}
else if ( pPort - > hFile = = INVALID_HANDLE_VALUE )
{
// All other port types need to be opened already.
dwErrorCode = ERROR_INVALID_PARAMETER ;
goto Cleanup ;
}
// Pass the parameters to WriteFile.
if ( ! WriteFile ( pPort - > hFile , pBuffer , cbBuf , pcbWritten , NULL ) )
{
dwErrorCode = GetLastError ( ) ;
ERR ( " WriteFile failed with error %lu! \n " , dwErrorCode ) ;
goto Cleanup ;
}
// If something was written down, we consider that a success, otherwise it's a timeout.
if ( * pcbWritten )
dwErrorCode = ERROR_SUCCESS ;
else
dwErrorCode = ERROR_TIMEOUT ;
Cleanup :
if ( bOpenedPort )
_ClosePortHandles ( pPort ) ;
SetLastError ( dwErrorCode ) ;
return ( dwErrorCode = = ERROR_SUCCESS ) ;
}
2020-08-26 22:12:20 +00:00
BOOL WINAPI
LocalmonAddPortEx ( HANDLE hMonitor , LPWSTR pName , DWORD Level , LPBYTE lpBuffer , LPWSTR lpMonitorName )
{
PLOCALMON_HANDLE pLocalmon = ( PLOCALMON_HANDLE ) hMonitor ;
PLOCALMON_PORT pPort ;
HKEY hKey ;
DWORD dwErrorCode , cbPortName ;
PORT_INFO_1W * pi = ( PORT_INFO_1W * ) lpBuffer ;
FIXME ( " LocalmonAddPortEx(%p, %lu, %p, %s) => %s \n " , hMonitor , Level , lpBuffer , debugstr_w ( lpMonitorName ) , debugstr_w ( pi ? pi - > pName : NULL ) ) ;
// Sanity checks
if ( ! pLocalmon )
{
dwErrorCode = ERROR_INVALID_PARAMETER ;
goto Cleanup ;
}
if ( ( lpMonitorName = = NULL ) | |
( lstrcmpiW ( lpMonitorName , L " Local Port " ) ! = 0 ) | |
( pi = = NULL ) | |
( pi - > pName = = NULL ) | |
( pi - > pName [ 0 ] = = ' \0 ' ) )
{
ERR ( " Fail Monitor Port Name \n " ) ;
SetLastError ( ERROR_INVALID_PARAMETER ) ;
return FALSE ;
}
if ( Level ! = 1 )
{
SetLastError ( ERROR_INVALID_LEVEL ) ;
return FALSE ;
}
dwErrorCode = RegOpenKeyW ( HKEY_LOCAL_MACHINE , L " Software \\ Microsoft \\ Windows NT \\ CurrentVersion \\ Ports " , & hKey ) ;
if ( dwErrorCode = = ERROR_SUCCESS )
{
if ( DoesPortExist ( pi - > pName ) )
{
RegCloseKey ( hKey ) ;
FIXME ( " Port Exist => FALSE with %u \n " , ERROR_INVALID_PARAMETER ) ;
SetLastError ( ERROR_INVALID_PARAMETER ) ;
return FALSE ;
}
cbPortName = ( wcslen ( pi - > pName ) + 1 ) * sizeof ( WCHAR ) ;
// Create a new LOCALMON_PORT structure for it.
pPort = DllAllocSplMem ( sizeof ( LOCALMON_PORT ) + cbPortName ) ;
if ( ! pPort )
{
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY ;
RegCloseKey ( hKey ) ;
goto Cleanup ;
}
pPort - > Sig = SIGLCMPORT ;
pPort - > hFile = INVALID_HANDLE_VALUE ;
pPort - > pLocalmon = pLocalmon ;
pPort - > pwszPortName = wcscpy ( ( PWSTR ) ( pPort + 1 ) , pi - > pName ) ;
// Insert it into the Registry list.
InsertTailList ( & pLocalmon - > RegistryPorts , & pPort - > Entry ) ;
dwErrorCode = RegSetValueExW ( hKey , pi - > pName , 0 , REG_SZ , ( const BYTE * ) L " " , sizeof ( L " " ) ) ;
RegCloseKey ( hKey ) ;
}
Cleanup :
if ( dwErrorCode ! = ERROR_SUCCESS ) SetLastError ( ERROR_INVALID_PARAMETER ) ;
FIXME ( " LocalmonAddPortEx => %u with %u \n " , ( dwErrorCode = = ERROR_SUCCESS ) , GetLastError ( ) ) ;
return ( dwErrorCode = = ERROR_SUCCESS ) ;
}
// Fallback Throw Back code....
//
// This is pre-w2k support, seems to be moved into LocalUI.
//
//
BOOL WINAPI
LocalmonAddPort ( HANDLE hMonitor , LPWSTR pName , HWND hWnd , LPWSTR pMonitorName )
{
DWORD res , cbPortName ;
HKEY hroot ;
PLOCALMON_HANDLE pLocalmon = ( PLOCALMON_HANDLE ) hMonitor ;
PLOCALMON_PORT pPort ;
WCHAR PortName [ MAX_PATH ] = { 0 } ; // Need to use a Dialog to get name.
FIXME ( " LocalmonAddPort : %s \n " , debugstr_w ( ( LPWSTR ) pMonitorName ) ) ;
res = RegOpenKeyW ( HKEY_LOCAL_MACHINE , L " Software \\ Microsoft \\ Windows NT \\ CurrentVersion \\ Ports " , & hroot ) ;
if ( res = = ERROR_SUCCESS )
{
if ( DoesPortExist ( PortName ) )
{
RegCloseKey ( hroot ) ;
FIXME ( " => %u \n " , ERROR_ALREADY_EXISTS ) ;
res = ERROR_ALREADY_EXISTS ;
goto Cleanup ;
}
cbPortName = ( wcslen ( PortName ) + 1 ) * sizeof ( WCHAR ) ;
// Create a new LOCALMON_PORT structure for it.
pPort = DllAllocSplMem ( sizeof ( LOCALMON_PORT ) + cbPortName ) ;
if ( ! pPort )
{
res = ERROR_NOT_ENOUGH_MEMORY ;
RegCloseKey ( hroot ) ;
goto Cleanup ;
}
pPort - > Sig = SIGLCMPORT ;
pPort - > hFile = INVALID_HANDLE_VALUE ;
pPort - > pLocalmon = pLocalmon ;
pPort - > pwszPortName = wcscpy ( ( PWSTR ) ( pPort + 1 ) , PortName ) ;
// Insert it into the Registry list.
InsertTailList ( & pLocalmon - > RegistryPorts , & pPort - > Entry ) ;
res = RegSetValueExW ( hroot , PortName , 0 , REG_SZ , ( const BYTE * ) L " " , sizeof ( L " " ) ) ;
RegCloseKey ( hroot ) ;
}
FIXME ( " => %u \n " , res ) ;
Cleanup :
SetLastError ( res ) ;
return ( res = = ERROR_SUCCESS ) ;
}
BOOL WINAPI
LocalmonConfigurePort ( HANDLE hMonitor , LPWSTR pName , HWND hWnd , LPWSTR pPortName )
{
//// See ConfigurePortUI
FIXME ( " LocalmonConfigurePort : %s \n " , debugstr_w ( pPortName ) ) ;
return FALSE ;
}
BOOL WINAPI
LocalmonDeletePort ( HANDLE hMonitor , LPWSTR pName , HWND hWnd , LPWSTR pPortName )
{
DWORD res ;
HKEY hroot ;
PLOCALMON_HANDLE pLocalmon = ( PLOCALMON_HANDLE ) hMonitor ;
PLOCALMON_PORT pPort ;
FIXME ( " LocalmonDeletePort : %s \n " , debugstr_w ( pPortName ) ) ;
res = RegOpenKeyW ( HKEY_LOCAL_MACHINE , L " Software \\ Microsoft \\ Windows NT \\ CurrentVersion \\ Ports " , & hroot ) ;
if ( res = = ERROR_SUCCESS )
{
res = RegDeleteValueW ( hroot , pPortName ) ;
RegCloseKey ( hroot ) ;
pPort = _FindPort ( pLocalmon , pPortName ) ;
if ( pPort )
{
EnterCriticalSection ( & pPort - > pLocalmon - > Section ) ;
RemoveEntryList ( & pPort - > Entry ) ;
LeaveCriticalSection ( & pPort - > pLocalmon - > Section ) ;
DllFreeSplMem ( pPort ) ;
}
FIXME ( " => %u with %u \n " , res , GetLastError ( ) ) ;
}
SetLastError ( res ) ;
return ( res = = ERROR_SUCCESS ) ;
}