reactos/win32ss/printing/monitors/localmon/tools.c
James Tabor 62c4b828b4 [Printing] Update and Add Functions
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.
2020-08-26 17:12:20 -05:00

261 lines
7.4 KiB
C

/*
* PROJECT: ReactOS Local Port Monitor
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
* PURPOSE: Various support functions shared by multiple files
* COPYRIGHT: Copyright 2015 Colin Finck (colin@reactos.org)
*/
#include "precomp.h"
/**
* @name DoesPortExist
*
* Checks all Port Monitors installed on the local system to find out if a given port already exists.
*
* @param pwszPortName
* The port name to check.
*
* @return
* TRUE if a port with that name already exists on the local system.
* If the return value is FALSE, either the port doesn't exist or an error occurred.
* Use GetLastError in this case to check the error case.
*/
BOOL
DoesPortExist(PCWSTR pwszPortName)
{
BOOL bReturnValue = FALSE;
DWORD cbNeeded;
DWORD dwErrorCode;
DWORD dwReturned;
DWORD i;
PPORT_INFO_1W p;
PPORT_INFO_1W pPortInfo1 = NULL;
// Determine the required buffer size.
EnumPortsW(NULL, 1, NULL, 0, &cbNeeded, &dwReturned);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
dwErrorCode = GetLastError();
ERR("EnumPortsW failed with error %lu!\n", dwErrorCode);
goto Cleanup;
}
// Allocate a buffer large enough.
pPortInfo1 = DllAllocSplMem(cbNeeded);
if (!pPortInfo1)
{
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
goto Cleanup;
}
// Now get the actual port information.
if (!EnumPortsW(NULL, 1, (PBYTE)pPortInfo1, cbNeeded, &cbNeeded, &dwReturned))
{
dwErrorCode = GetLastError();
ERR("EnumPortsW failed with error %lu!\n", dwErrorCode);
goto Cleanup;
}
// We were successful! Loop through all returned ports.
dwErrorCode = ERROR_SUCCESS;
p = pPortInfo1;
for (i = 0; i < dwReturned; i++)
{
// Check if this existing port matches our queried one.
if (wcsicmp(p->pName, pwszPortName) == 0)
{
bReturnValue = TRUE;
goto Cleanup;
}
p++;
}
Cleanup:
if (pPortInfo1)
DllFreeSplMem(pPortInfo1);
SetLastError(dwErrorCode);
return bReturnValue;
}
DWORD
GetLPTTransmissionRetryTimeout(VOID)
{
DWORD cbBuffer;
DWORD dwReturnValue = 90; // Use 90 seconds as default if we fail to read from registry.
HKEY hKey;
LSTATUS lStatus;
// Six digits is the most you can enter in Windows' LocalUI.dll.
// Larger values make it crash, so introduce a limit here.
WCHAR wszBuffer[6 + 1];
// Open the key where our value is stored.
lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows", 0, KEY_READ, &hKey);
if (lStatus != ERROR_SUCCESS)
{
ERR("RegOpenKeyExW failed with status %ld!\n", lStatus);
goto Cleanup;
}
// Query the value.
cbBuffer = sizeof(wszBuffer);
lStatus = RegQueryValueExW(hKey, L"TransmissionRetryTimeout", NULL, NULL, (PBYTE)wszBuffer, &cbBuffer);
if (lStatus != ERROR_SUCCESS)
{
ERR("RegQueryValueExW failed with status %ld!\n", lStatus);
goto Cleanup;
}
// Return it converted to a DWORD.
dwReturnValue = wcstoul(wszBuffer, NULL, 10);
Cleanup:
if (hKey)
RegCloseKey(hKey);
return dwReturnValue;
}
/**
* @name GetPortNameWithoutColon
*
* Most of the time, we operate on port names with a trailing colon. But some functions require the name without the trailing colon.
* This function checks if the port has a trailing colon and if so, it returns the port name without the colon.
*
* @param pwszPortName
* The port name with colon
*
* @param ppwszPortNameWithoutColon
* Pointer to a PWSTR that will contain the port name without colon.
* You have to free this buffer using DllFreeSplMem.
*
* @return
* ERROR_SUCCESS if the port name without colon was successfully copied into the buffer.
* ERROR_INVALID_PARAMETER if this port name has no trailing colon.
* ERROR_NOT_ENOUGH_MEMORY if memory allocation failed.
*/
DWORD
GetPortNameWithoutColon(PCWSTR pwszPortName, PWSTR* ppwszPortNameWithoutColon)
{
DWORD cchPortNameWithoutColon;
// Compute the string length of pwszPortNameWithoutColon.
cchPortNameWithoutColon = wcslen(pwszPortName) - 1;
// Check if pwszPortName really has a colon as the last character.
if (pwszPortName[cchPortNameWithoutColon] != L':')
return ERROR_INVALID_PARAMETER;
// Allocate the output buffer.
*ppwszPortNameWithoutColon = DllAllocSplMem((cchPortNameWithoutColon + 1) * sizeof(WCHAR));
if (!*ppwszPortNameWithoutColon)
{
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
return ERROR_NOT_ENOUGH_MEMORY;
}
// Copy the port name without colon into the buffer.
// The buffer is already zero-initialized, so no additional null-termination is necessary.
CopyMemory(*ppwszPortNameWithoutColon, pwszPortName, cchPortNameWithoutColon * sizeof(WCHAR));
return ERROR_SUCCESS;
}
/**
* @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');
}
DWORD
GetTypeFromName(LPCWSTR name)
{
HANDLE hfile;
if (!wcsncmp(name, L"LPT", ARRAYSIZE(L"LPT") - 1) )
return PORT_IS_LPT;
if (!wcsncmp(name, L"COM", ARRAYSIZE(L"COM") - 1) )
return PORT_IS_COM;
if (!lstrcmpW(name, L"FILE:") )
return PORT_IS_FILE;
// if (name[0] == '/')
// return PORT_IS_UNIXNAME;
// if (name[0] == '|')
// return PORT_IS_PIPE;
if ( _IsNEPort( name ) )
return PORT_IS_VNET;
if (!wcsncmp(name, L"XPS", ARRAYSIZE(L"XPS") - 1))
return PORT_IS_XPS;
/* Must be a file or a directory. Does the file exist ? */
hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
FIXME("%p for OPEN_EXISTING on %s\n", hfile, debugstr_w(name));
if (hfile == INVALID_HANDLE_VALUE)
{
/* Can we create the file? */
hfile = CreateFileW(name, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
FIXME("%p for OPEN_ALWAYS\n", hfile);
}
if (hfile != INVALID_HANDLE_VALUE)
{
CloseHandle(hfile); FIXME("PORT_IS_FILENAME %d\n",PORT_IS_FILENAME);
return PORT_IS_FILENAME;
}
FIXME("PORT_IS_UNKNOWN %d\n",PORT_IS_UNKNOWN);
/* We can't use the name. use GetLastError() for the reason */
return PORT_IS_UNKNOWN;
}