2015-05-06 12:47:20 +00:00
|
|
|
/*
|
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-05-06 12:47:20 +00:00
|
|
|
* PURPOSE: Main functions
|
2017-09-29 17:18:19 +00:00
|
|
|
* COPYRIGHT: Copyright 2015 Colin Finck (colin@reactos.org)
|
2015-05-06 12:47:20 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
|
2015-07-06 12:41:06 +00:00
|
|
|
// Global Variables
|
|
|
|
DWORD cbLocalMonitor;
|
|
|
|
DWORD cbLocalPort;
|
|
|
|
PCWSTR pwszLocalMonitor;
|
|
|
|
PCWSTR pwszLocalPort;
|
|
|
|
|
|
|
|
// Local Constants
|
|
|
|
static MONITOR2 _MonitorFunctions = {
|
|
|
|
sizeof(MONITOR2), // cbSize
|
|
|
|
LocalmonEnumPorts, // pfnEnumPorts
|
|
|
|
LocalmonOpenPort, // pfnOpenPort
|
|
|
|
NULL, // pfnOpenPortEx
|
|
|
|
LocalmonStartDocPort, // pfnStartDocPort
|
|
|
|
LocalmonWritePort, // pfnWritePort
|
|
|
|
LocalmonReadPort, // pfnReadPort
|
|
|
|
LocalmonEndDocPort, // pfnEndDocPort
|
|
|
|
LocalmonClosePort, // pfnClosePort
|
2020-08-26 22:12:20 +00:00
|
|
|
LocalmonAddPort, // pfnAddPort moved to localui.dll since w2k, but~
|
|
|
|
LocalmonAddPortEx, // pfnAddPortEx
|
|
|
|
LocalmonConfigurePort, // pfnConfigurePort moved to localui.dll since w2k, but~
|
|
|
|
LocalmonDeletePort, // pfnDeletePort moved to localui.dll since w2k, but~
|
2015-07-06 12:41:06 +00:00
|
|
|
LocalmonGetPrinterDataFromPort, // pfnGetPrinterDataFromPort
|
|
|
|
LocalmonSetPortTimeOuts, // pfnSetPortTimeOuts
|
|
|
|
LocalmonXcvOpenPort, // pfnXcvOpenPort
|
|
|
|
LocalmonXcvDataPort, // pfnXcvDataPort
|
|
|
|
LocalmonXcvClosePort, // pfnXcvClosePort
|
|
|
|
LocalmonShutdown, // pfnShutdown
|
|
|
|
NULL, // pfnSendRecvBidiDataFromPort
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-07-15 18:15:33 +00:00
|
|
|
/**
|
|
|
|
* @name _IsNEPort
|
|
|
|
*
|
|
|
|
* Checks if the given port name is a virtual Ne port.
|
|
|
|
* A virtual Ne port may appear in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports and can have the formats
|
|
|
|
* Ne00:, Ne01:, Ne-02:, Ne456:
|
|
|
|
* This check is extra picky to not cause false positives (like file name ports starting with "Ne").
|
|
|
|
*
|
|
|
|
* @param pwszPortName
|
|
|
|
* The port name to check.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* TRUE if this is definitely a virtual Ne port, FALSE if not.
|
|
|
|
*/
|
|
|
|
static __inline BOOL
|
|
|
|
_IsNEPort(PCWSTR pwszPortName)
|
|
|
|
{
|
|
|
|
PCWSTR p = pwszPortName;
|
|
|
|
|
|
|
|
// First character needs to be 'N' (uppercase or lowercase)
|
|
|
|
if (*p != L'N' && *p != L'n')
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
// Next character needs to be 'E' (uppercase or lowercase)
|
|
|
|
p++;
|
|
|
|
if (*p != L'E' && *p != L'e')
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
// An optional hyphen may follow now.
|
|
|
|
p++;
|
|
|
|
if (*p == L'-')
|
|
|
|
p++;
|
|
|
|
|
|
|
|
// Now an arbitrary number of digits may follow.
|
|
|
|
while (*p >= L'0' && *p <= L'9')
|
|
|
|
p++;
|
|
|
|
|
|
|
|
// Finally, the virtual Ne port must be terminated by a colon.
|
|
|
|
if (*p != ':')
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
// If this is the end of the string, we have a virtual Ne port.
|
|
|
|
p++;
|
|
|
|
return (*p == L'\0');
|
|
|
|
}
|
|
|
|
|
2015-07-06 12:41:06 +00:00
|
|
|
static void
|
|
|
|
_LoadResources(HINSTANCE hinstDLL)
|
|
|
|
{
|
|
|
|
LoadStringW(hinstDLL, IDS_LOCAL_MONITOR, (PWSTR)&pwszLocalMonitor, 0);
|
|
|
|
cbLocalMonitor = (wcslen(pwszLocalMonitor) + 1) * sizeof(WCHAR);
|
|
|
|
|
|
|
|
LoadStringW(hinstDLL, IDS_LOCAL_PORT, (PWSTR)&pwszLocalPort, 0);
|
|
|
|
cbLocalPort = (wcslen(pwszLocalPort) + 1) * sizeof(WCHAR);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL WINAPI
|
|
|
|
DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
|
|
|
{
|
|
|
|
switch (fdwReason)
|
|
|
|
{
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
DisableThreadLibraryCalls(hinstDLL);
|
|
|
|
_LoadResources(hinstDLL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WINAPI
|
|
|
|
LocalmonShutdown(HANDLE hMonitor)
|
|
|
|
{
|
|
|
|
PLOCALMON_HANDLE pLocalmon;
|
|
|
|
PLOCALMON_PORT pPort;
|
2015-07-07 10:30:30 +00:00
|
|
|
PLOCALMON_XCV pXcv;
|
2020-08-26 22:12:20 +00:00
|
|
|
PLIST_ENTRY pEntry;
|
2015-07-07 10:30:30 +00:00
|
|
|
|
|
|
|
TRACE("LocalmonShutdown(%p)\n", hMonitor);
|
2015-07-06 12:41:06 +00:00
|
|
|
|
|
|
|
pLocalmon = (PLOCALMON_HANDLE)hMonitor;
|
|
|
|
|
2020-08-26 22:12:20 +00:00
|
|
|
if ( pLocalmon->Sig != SIGLCMMON )
|
|
|
|
{
|
|
|
|
ERR("LocalmonShutdown : Invalid Monitor Handle\n",hMonitor);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-06 12:41:06 +00:00
|
|
|
// Close all virtual file ports.
|
2020-08-26 22:12:20 +00:00
|
|
|
if (!IsListEmpty(&pLocalmon->FilePorts))
|
2015-07-06 12:41:06 +00:00
|
|
|
{
|
2020-08-26 22:12:20 +00:00
|
|
|
for (pEntry = pLocalmon->FilePorts.Flink; pEntry != &pLocalmon->FilePorts; pEntry = pEntry->Flink)
|
|
|
|
{
|
|
|
|
pPort = CONTAINING_RECORD(&pLocalmon->FilePorts.Flink, LOCALMON_PORT, Entry);
|
|
|
|
LocalmonClosePort((HANDLE)pPort);
|
|
|
|
}
|
2015-07-06 12:41:06 +00:00
|
|
|
}
|
|
|
|
|
2015-07-07 10:30:30 +00:00
|
|
|
// Do the same for the open Xcv ports.
|
2020-08-26 22:12:20 +00:00
|
|
|
if (!IsListEmpty(&pLocalmon->XcvHandles))
|
2015-07-06 12:41:06 +00:00
|
|
|
{
|
2020-08-26 22:12:20 +00:00
|
|
|
for (pEntry = pLocalmon->XcvHandles.Flink; pEntry != &pLocalmon->XcvHandles; pEntry = pEntry->Flink)
|
|
|
|
{
|
|
|
|
pXcv = CONTAINING_RECORD(pEntry, LOCALMON_XCV, Entry);
|
|
|
|
LocalmonXcvClosePort((HANDLE)pXcv);
|
|
|
|
}
|
2015-07-07 10:30:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now close all registry ports, remove them from the list and free their memory.
|
2020-08-26 22:12:20 +00:00
|
|
|
if (!IsListEmpty(&pLocalmon->RegistryPorts))
|
2015-07-07 10:30:30 +00:00
|
|
|
{
|
2020-08-26 22:12:20 +00:00
|
|
|
for (pEntry = pLocalmon->RegistryPorts.Flink; pEntry != &pLocalmon->RegistryPorts; pEntry = pEntry->Flink)
|
|
|
|
{
|
|
|
|
pPort = CONTAINING_RECORD(pEntry, LOCALMON_PORT, Entry);
|
|
|
|
if ( LocalmonClosePort((HANDLE)pPort) ) continue;
|
|
|
|
RemoveEntryList(&pPort->Entry);
|
|
|
|
DllFreeSplMem(pPort);
|
|
|
|
}
|
2015-07-06 12:41:06 +00:00
|
|
|
}
|
|
|
|
|
2015-07-07 10:30:30 +00:00
|
|
|
// Finally clean the LOCALMON_HANDLE structure itself.
|
|
|
|
DeleteCriticalSection(&pLocalmon->Section);
|
2015-07-06 12:41:06 +00:00
|
|
|
DllFreeSplMem(pLocalmon);
|
|
|
|
}
|
|
|
|
|
|
|
|
PMONITOR2 WINAPI
|
2015-05-06 12:47:20 +00:00
|
|
|
InitializePrintMonitor2(PMONITORINIT pMonitorInit, PHANDLE phMonitor)
|
|
|
|
{
|
2015-07-06 12:41:06 +00:00
|
|
|
DWORD cchMaxPortName;
|
|
|
|
DWORD cchPortName;
|
|
|
|
DWORD dwErrorCode;
|
|
|
|
DWORD dwPortCount;
|
|
|
|
DWORD i;
|
|
|
|
HKEY hKey;
|
|
|
|
PMONITOR2 pReturnValue = NULL;
|
|
|
|
PLOCALMON_HANDLE pLocalmon;
|
|
|
|
PLOCALMON_PORT pPort = NULL;
|
|
|
|
|
2015-07-07 10:30:30 +00:00
|
|
|
TRACE("InitializePrintMonitor2(%p, %p)\n", pMonitorInit, phMonitor);
|
|
|
|
|
2015-07-06 12:41:06 +00:00
|
|
|
// Create a new LOCALMON_HANDLE structure.
|
|
|
|
pLocalmon = DllAllocSplMem(sizeof(LOCALMON_HANDLE));
|
2020-08-26 22:12:20 +00:00
|
|
|
pLocalmon->Sig = SIGLCMMON;
|
2015-07-07 10:30:30 +00:00
|
|
|
InitializeCriticalSection(&pLocalmon->Section);
|
2015-07-06 12:41:06 +00:00
|
|
|
InitializeListHead(&pLocalmon->FilePorts);
|
2015-07-07 10:30:30 +00:00
|
|
|
InitializeListHead(&pLocalmon->RegistryPorts);
|
|
|
|
InitializeListHead(&pLocalmon->XcvHandles);
|
2015-07-06 12:41:06 +00:00
|
|
|
|
|
|
|
// The Local Spooler Port Monitor doesn't need to care about the given registry key and functions.
|
|
|
|
// Instead it uses a well-known registry key for getting its information about local ports. Open this one.
|
|
|
|
dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Ports", 0, KEY_READ, &hKey);
|
|
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the number of ports and the length of the largest port name.
|
|
|
|
dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &dwPortCount, &cchMaxPortName, NULL, NULL, NULL);
|
|
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Loop through all ports.
|
|
|
|
for (i = 0; i < dwPortCount; i++)
|
|
|
|
{
|
|
|
|
// Allocate memory for a new LOCALMON_PORT structure and its name.
|
|
|
|
pPort = DllAllocSplMem(sizeof(LOCALMON_PORT) + (cchMaxPortName + 1) * sizeof(WCHAR));
|
|
|
|
if (!pPort)
|
|
|
|
{
|
|
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
|
2020-08-26 22:12:20 +00:00
|
|
|
pPort->Sig = SIGLCMPORT;
|
2015-07-07 10:30:30 +00:00
|
|
|
pPort->pLocalmon = pLocalmon;
|
2015-07-06 12:41:06 +00:00
|
|
|
pPort->hFile = INVALID_HANDLE_VALUE;
|
2020-08-26 22:12:20 +00:00
|
|
|
pPort->pwszPortName = (PWSTR)(pPort+1);
|
2015-07-06 12:41:06 +00:00
|
|
|
|
|
|
|
// Get the port name.
|
|
|
|
cchPortName = cchMaxPortName + 1;
|
|
|
|
dwErrorCode = (DWORD)RegEnumValueW(hKey, i, pPort->pwszPortName, &cchPortName, NULL, NULL, NULL, NULL);
|
|
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
ERR("RegEnumValueW failed with status %lu!\n", dwErrorCode);
|
|
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
|
2015-07-15 18:15:33 +00:00
|
|
|
// pwszPortName can be one of the following to be valid for this Port Monitor:
|
|
|
|
// COMx: - Physical COM port
|
|
|
|
// LPTx: - Physical LPT port (or redirected one using "net use LPT1 ...")
|
|
|
|
// FILE: - Opens a prompt that asks for an output filename
|
|
|
|
// C:\bla.txt - Redirection into the file "C:\bla.txt"
|
|
|
|
// \\COMPUTERNAME\PrinterName - Redirection to a shared network printer installed as a local port
|
|
|
|
//
|
|
|
|
// We can't detect valid and invalid ones by the name, so we can only exclude empty ports and the virtual "Ne00:", "Ne01:", ... ports.
|
|
|
|
// Skip the invalid ones here.
|
|
|
|
if (!cchPortName || _IsNEPort(pPort->pwszPortName))
|
2015-07-06 12:41:06 +00:00
|
|
|
{
|
|
|
|
DllFreeSplMem(pPort);
|
2017-02-19 10:31:37 +00:00
|
|
|
pPort = NULL;
|
2015-07-06 12:41:06 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add it to the list.
|
2015-07-07 10:30:30 +00:00
|
|
|
InsertTailList(&pLocalmon->RegistryPorts, &pPort->Entry);
|
2020-08-26 22:12:20 +00:00
|
|
|
TRACE("InitializePrintMonitor2 Port : %s \n",debugstr_w(pPort->pwszPortName));
|
2015-07-06 12:41:06 +00:00
|
|
|
|
|
|
|
// Don't let the cleanup routine free this.
|
|
|
|
pPort = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return our handle and the Print Monitor functions.
|
|
|
|
*phMonitor = (HANDLE)pLocalmon;
|
|
|
|
pReturnValue = &_MonitorFunctions;
|
|
|
|
dwErrorCode = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
Cleanup:
|
|
|
|
if (pPort)
|
|
|
|
DllFreeSplMem(pPort);
|
|
|
|
|
|
|
|
SetLastError(dwErrorCode);
|
|
|
|
return pReturnValue;
|
2015-05-06 12:47:20 +00:00
|
|
|
}
|