mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 21:13:52 +00:00
62c4b828b4
More forwards to LocalSpl and LocalMon. At sometime will be merged together. Bug fixes. Printer Driver code is a wine hack. (WIP) Added information for shell tray icon notifications. Sync wine WinSpool driver tests. Unplugged from build.
1228 lines
37 KiB
C
1228 lines
37 KiB
C
/*
|
|
* PROJECT: ReactOS Local Port Monitor
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Functions related to ports
|
|
* COPYRIGHT: Copyright 2015-2017 Colin Finck (colin@reactos.org)
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
// Local Constants
|
|
static const WCHAR wszNonspooledPrefix[] = L"NONSPOOLED_";
|
|
static const DWORD cchNonspooledPrefix = _countof(wszNonspooledPrefix) - 1;
|
|
|
|
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
|
|
};
|
|
|
|
|
|
/**
|
|
* @name _GetNonspooledPortName
|
|
*
|
|
* Prepends "NONSPOOLED_" to a port name without colon.
|
|
*
|
|
* @param pwszPortNameWithoutColon
|
|
* Result of a previous GetPortNameWithoutColon call.
|
|
*
|
|
* @param ppwszNonspooledPortName
|
|
* Pointer to a buffer that will contain the NONSPOOLED port name.
|
|
* You have to free this buffer using DllFreeSplMem.
|
|
*
|
|
* @return
|
|
* ERROR_SUCCESS if the NONSPOOLED port name was successfully copied into the buffer.
|
|
* ERROR_NOT_ENOUGH_MEMORY if memory allocation failed.
|
|
*/
|
|
static __inline DWORD
|
|
_GetNonspooledPortName(PCWSTR pwszPortNameWithoutColon, PWSTR* ppwszNonspooledPortName)
|
|
{
|
|
DWORD cchPortNameWithoutColon;
|
|
|
|
cchPortNameWithoutColon = wcslen(pwszPortNameWithoutColon);
|
|
|
|
*ppwszNonspooledPortName = DllAllocSplMem((cchNonspooledPrefix + cchPortNameWithoutColon + 1) * sizeof(WCHAR));
|
|
if (!*ppwszNonspooledPortName)
|
|
{
|
|
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
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;
|
|
|
|
// If this is the end of the string, we have a legacy port.
|
|
p++;
|
|
return (*p == L'\0');
|
|
}
|
|
|
|
/**
|
|
* @name _ClosePortHandles
|
|
*
|
|
* Closes a port of any type if it's open.
|
|
* 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.
|
|
if (GetPortNameWithoutColon(pPort->pwszPortName, &pwszPortNameWithoutColon) == ERROR_SUCCESS)
|
|
{
|
|
if (_GetNonspooledPortName(pwszPortNameWithoutColon, &pwszNonspooledPortName) == ERROR_SUCCESS)
|
|
{
|
|
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.
|
|
* In particular, if the return value is FALSE and GetLastError returns ERROR_SUCCESS, no NONSPOOLED port is needed for this port.
|
|
*/
|
|
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.
|
|
dwErrorCode = GetPortNameWithoutColon(pPort->pwszPortName, &pwszPortNameWithoutColon);
|
|
if (dwErrorCode == ERROR_INVALID_PARAMETER)
|
|
{
|
|
// 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.
|
|
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.
|
|
ERR("Can't find a suitable mapping for the port \"%S\"!", pPort->pwszPortName);
|
|
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.
|
|
dwErrorCode = _GetNonspooledPortName(pwszPortNameWithoutColon, &pwszNonspooledPortName);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
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.
|
|
pPort->hFile = CreateFileW(pwszNonspooledFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
|
|
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;
|
|
|
|
for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
|
|
{
|
|
pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
|
|
|
|
if (wcscmp(pPort->pwszPortName, pwszPortName) == 0)
|
|
return pPort;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
_LocalmonGetPortLevel1(PLOCALMON_PORT pPort, PPORT_INFO_1W* ppPortInfo, PBYTE* ppPortInfoEnd, PDWORD pcbNeeded)
|
|
{
|
|
DWORD cbPortName;
|
|
PCWSTR pwszStrings[1];
|
|
|
|
// Calculate the string lengths.
|
|
if (!ppPortInfo)
|
|
{
|
|
cbPortName = (wcslen(pPort->pwszPortName) + 1) * sizeof(WCHAR);
|
|
|
|
*pcbNeeded += sizeof(PORT_INFO_1W) + cbPortName;
|
|
return;
|
|
}
|
|
|
|
// Set the pName field.
|
|
pwszStrings[0] = pPort->pwszPortName;
|
|
|
|
// Copy the structure and advance to the next one in the output buffer.
|
|
*ppPortInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPortInfo), dwPortInfo1Offsets, *ppPortInfoEnd);
|
|
(*ppPortInfo)++;
|
|
}
|
|
|
|
static void
|
|
_LocalmonGetPortLevel2(PLOCALMON_PORT pPort, PPORT_INFO_2W* ppPortInfo, PBYTE* ppPortInfoEnd, PDWORD pcbNeeded)
|
|
{
|
|
DWORD cbPortName;
|
|
PCWSTR pwszStrings[3];
|
|
|
|
// Calculate the string lengths.
|
|
if (!ppPortInfo)
|
|
{
|
|
cbPortName = (wcslen(pPort->pwszPortName) + 1) * sizeof(WCHAR);
|
|
|
|
*pcbNeeded += sizeof(PORT_INFO_2W) + cbPortName + cbLocalMonitor + cbLocalPort;
|
|
return;
|
|
}
|
|
|
|
// All local ports are writable and readable.
|
|
(*ppPortInfo)->fPortType = PORT_TYPE_WRITE | PORT_TYPE_READ;
|
|
(*ppPortInfo)->Reserved = 0;
|
|
|
|
// Set the pPortName field.
|
|
pwszStrings[0] = pPort->pwszPortName;
|
|
|
|
// Set the pMonitorName field.
|
|
pwszStrings[1] = (PWSTR)pwszLocalMonitor;
|
|
|
|
// Set the pDescription field.
|
|
pwszStrings[2] = (PWSTR)pwszLocalPort;
|
|
|
|
// Copy the structure and advance to the next one in the output buffer.
|
|
*ppPortInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPortInfo), dwPortInfo2Offsets, *ppPortInfoEnd);
|
|
(*ppPortInfo)++;
|
|
}
|
|
|
|
/**
|
|
* @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)
|
|
{
|
|
PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
|
|
|
|
TRACE("LocalmonClosePort(%p)\n", hPort);
|
|
|
|
// Sanity checks
|
|
if (!pPort)
|
|
{
|
|
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)
|
|
{
|
|
ClosePrinter(pPort->hPrinter);
|
|
pPort->hPrinter = NULL;
|
|
}
|
|
|
|
// Free virtual FILE: ports which were created in LocalmonOpenPort.
|
|
if (pPort->PortType == PortType_FILE)
|
|
{
|
|
EnterCriticalSection(&pPort->pLocalmon->Section);
|
|
RemoveEntryList(&pPort->Entry);
|
|
LeaveCriticalSection(&pPort->pLocalmon->Section);
|
|
DllFreeSplMem(pPort);
|
|
}
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
LocalmonEndDocPort(HANDLE hPort)
|
|
{
|
|
PLOCALMON_PORT pPort = (PLOCALMON_PORT)hPort;
|
|
|
|
TRACE("LocalmonEndDocPort(%p)\n", hPort);
|
|
|
|
// 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;
|
|
|
|
// A new document can now be started again.
|
|
pPort->bStartedDoc = FALSE;
|
|
}
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
LocalmonEnumPorts(HANDLE hMonitor, PWSTR pName, DWORD Level, PBYTE pPorts, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
|
|
{
|
|
DWORD dwErrorCode;
|
|
PBYTE pPortInfoEnd;
|
|
PLIST_ENTRY pEntry;
|
|
PLOCALMON_HANDLE pLocalmon = (PLOCALMON_HANDLE)hMonitor;
|
|
PLOCALMON_PORT pPort;
|
|
|
|
TRACE("LocalmonEnumPorts(%p, %S, %lu, %p, %lu, %p, %p)\n", hMonitor, pName, Level, pPorts, cbBuf, pcbNeeded, pcReturned);
|
|
|
|
// 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)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Begin counting.
|
|
*pcbNeeded = 0;
|
|
*pcReturned = 0;
|
|
|
|
EnterCriticalSection(&pLocalmon->Section);
|
|
|
|
// 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)++;
|
|
}
|
|
|
|
LeaveCriticalSection(&pLocalmon->Section);
|
|
dwErrorCode = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* @name LocalmonGetPrinterDataFromPort
|
|
*
|
|
* 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;
|
|
|
|
TRACE("LocalmonGetPrinterDataFromPort(%p, %lu, %p, %p, %lu, %p, %lu, %p)\n", hPort, ControlID, pValueName, lpInBuffer, cbInBuffer, lpOutBuffer, cbOutBuffer, lpcbReturned);
|
|
|
|
// 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;
|
|
|
|
TRACE("LocalmonOpenPort(%p, %S, %p)\n", hMonitor, pName, pHandle);
|
|
|
|
// Sanity checks
|
|
if (!pLocalmon || !pName || !pHandle)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
EnterCriticalSection(&pLocalmon->Section);
|
|
|
|
// 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));
|
|
pPort->pLocalmon = pLocalmon;
|
|
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)
|
|
{
|
|
LeaveCriticalSection(&pLocalmon->Section);
|
|
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.
|
|
if (_IsLegacyPort(pName, L"LPT"))
|
|
{
|
|
// 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
|
|
{
|
|
// This is no physical port, so don't keep its handle open.
|
|
_ClosePortHandles(pPort);
|
|
}
|
|
}
|
|
else if (GetLastError() != ERROR_SUCCESS)
|
|
{
|
|
LeaveCriticalSection(&pLocalmon->Section);
|
|
dwErrorCode = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else if (_IsLegacyPort(pName, L"COM"))
|
|
{
|
|
// COM ports can't be redirected over the network, so this is a physical one.
|
|
pPort->PortType = PortType_PhysicalCOM;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&pLocalmon->Section);
|
|
|
|
// 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;
|
|
|
|
TRACE("LocalmonSetPortTimeOuts(%p, %p, %lu)\n", hPort, lpCTO, Reserved);
|
|
|
|
// 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;
|
|
|
|
TRACE("LocalmonReadPort(%p, %p, %lu, %p)\n", hPort, pBuffer, cbBuffer, pcbRead);
|
|
|
|
// 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;
|
|
|
|
TRACE("LocalmonStartDocPort(%p, %S, %lu, %lu, %p)\n", hPort, pPrinterName, JobId, Level, pDocInfo);
|
|
|
|
// 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.
|
|
pPort->hFile = CreateFileW(pDocInfo1->pOutputFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
|
|
if (pPort->hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 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.
|
|
if (!_CreateNonspooledPort(pPort))
|
|
{
|
|
if (GetLastError() == ERROR_SUCCESS)
|
|
{
|
|
pPort->hFile = CreateFileW(pPort->pwszPortName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
|
|
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;
|
|
|
|
TRACE("LocalmonWritePort(%p, %p, %lu, %p)\n", hPort, pBuffer, cbBuf, pcbWritten);
|
|
|
|
// 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);
|
|
}
|
|
|
|
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);
|
|
}
|