mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 06:15:26 +00:00
476 lines
17 KiB
C
476 lines
17 KiB
C
/*
|
|
* PROJECT: ReactOS Local Spooler
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Functions related to Printer Configuration Data
|
|
* COPYRIGHT: Copyright 2017 Colin Finck (colin@reactos.org)
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
DWORD WINAPI
|
|
LocalGetPrinterData(HANDLE hPrinter, PWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
|
|
{
|
|
TRACE("LocalGetPrinterData(%p, %S, %p, %p, %lu, %p)\n", hPrinter, pValueName, pType, pData, nSize, pcbNeeded);
|
|
|
|
// The ReactOS Printing Stack forwards all GetPrinterData calls to GetPrinterDataEx as soon as possible.
|
|
// This function may only be called if localspl.dll is used together with Windows Printing Stack components.
|
|
WARN("This function should never be called!\n");
|
|
return LocalGetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, pType, pData, nSize, pcbNeeded);
|
|
}
|
|
|
|
static DWORD
|
|
_MakePrinterSubKey(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PWSTR* ppwszSubKey)
|
|
{
|
|
const WCHAR wszBackslash[] = L"\\";
|
|
|
|
size_t cbSubKey;
|
|
PWSTR p;
|
|
|
|
// Sanity check
|
|
if (!pKeyName || !*pKeyName)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
// Allocate a buffer for the subkey "PrinterName\KeyName".
|
|
cbSubKey = (wcslen(pPrinterHandle->pPrinter->pwszPrinterName) + 1 + wcslen(pKeyName) + 1) * sizeof(WCHAR);
|
|
*ppwszSubKey = DllAllocSplMem(cbSubKey);
|
|
if (!*ppwszSubKey)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
// Concatenate the subkey.
|
|
p = *ppwszSubKey;
|
|
StringCbCopyExW(p, cbSubKey, pPrinterHandle->pPrinter->pwszPrinterName, &p, &cbSubKey, 0);
|
|
StringCbCopyExW(p, cbSubKey, wszBackslash, &p, &cbSubKey, 0);
|
|
StringCbCopyExW(p, cbSubKey, pKeyName, &p, &cbSubKey, 0);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
static DWORD
|
|
_LocalGetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
|
|
{
|
|
DWORD dwErrorCode;
|
|
HKEY hKey = NULL;
|
|
PWSTR pwszSubKey = NULL;
|
|
|
|
dwErrorCode = _MakePrinterSubKey(pPrinterHandle, pKeyName, &pwszSubKey);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
goto Cleanup;
|
|
|
|
// Open the subkey.
|
|
dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, pwszSubKey, 0, KEY_READ, &hKey);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
ERR("RegOpenKeyExW failed for \"%S\" with error %lu!\n", pwszSubKey, dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Query the desired value.
|
|
*pcbNeeded = nSize;
|
|
dwErrorCode = (DWORD)RegQueryValueExW(hKey, pValueName, NULL, pType, pData, pcbNeeded);
|
|
|
|
Cleanup:
|
|
if (hKey)
|
|
RegCloseKey(hKey);
|
|
|
|
if (pwszSubKey)
|
|
DllFreeSplMem(pwszSubKey);
|
|
|
|
return dwErrorCode;
|
|
}
|
|
|
|
static DWORD
|
|
_LocalGetPrintServerHandleData(PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
|
|
{
|
|
DWORD dwErrorCode;
|
|
|
|
if (wcsicmp(pValueName, SPLREG_DEFAULT_SPOOL_DIRECTORY) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_BEEP_ENABLED) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_ALLOW_USER_MANAGEFORMS) == 0)
|
|
{
|
|
*pcbNeeded = nSize;
|
|
return (DWORD)RegQueryValueExW(hPrintersKey, pValueName, NULL, pType, pData, pcbNeeded);
|
|
}
|
|
else if (wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY_DEFAULT) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY_DEFAULT) == 0)
|
|
{
|
|
// Store a DWORD value as REG_NONE.
|
|
*pType = REG_NONE;
|
|
*pcbNeeded = sizeof(DWORD);
|
|
if (nSize < *pcbNeeded)
|
|
return ERROR_MORE_DATA;
|
|
|
|
// Apparently, these values don't serve a purpose anymore.
|
|
*((PDWORD)pData) = 0;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else if (wcsicmp(pValueName, SPLREG_NET_POPUP) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_RETRY_POPUP) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_NET_POPUP_TO_COMPUTER) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_EVENT_LOG) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ERROR) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ENABLED) == 0)
|
|
{
|
|
HKEY hKey;
|
|
|
|
dwErrorCode = (DWORD)RegOpenKeyExW(hPrintKey, L"Providers", 0, KEY_READ, &hKey);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
ERR("RegOpenKeyExW failed for \"Providers\" with error %lu!\n", dwErrorCode);
|
|
return dwErrorCode;
|
|
}
|
|
|
|
*pcbNeeded = nSize;
|
|
dwErrorCode = (DWORD)RegQueryValueExW(hKey, pValueName, NULL, pType, pData, pcbNeeded);
|
|
RegCloseKey(hKey);
|
|
return dwErrorCode;
|
|
}
|
|
else if (wcsicmp(pValueName, SPLREG_MAJOR_VERSION) == 0)
|
|
{
|
|
// Store a DWORD value as REG_NONE.
|
|
*pType = REG_NONE;
|
|
*pcbNeeded = sizeof(DWORD);
|
|
if (nSize < *pcbNeeded)
|
|
return ERROR_MORE_DATA;
|
|
|
|
// Apparently, these values don't serve a purpose anymore.
|
|
*((PDWORD)pData) = dwSpoolerMajorVersion;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else if (wcsicmp(pValueName, SPLREG_MINOR_VERSION) == 0)
|
|
{
|
|
// Store a DWORD value as REG_NONE.
|
|
*pType = REG_NONE;
|
|
*pcbNeeded = sizeof(DWORD);
|
|
if (nSize < *pcbNeeded)
|
|
return ERROR_MORE_DATA;
|
|
|
|
// Apparently, these values don't serve a purpose anymore.
|
|
*((PDWORD)pData) = dwSpoolerMinorVersion;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else if (wcsicmp(pValueName, SPLREG_ARCHITECTURE) == 0)
|
|
{
|
|
// Store a string as REG_NONE with the length of the environment name string.
|
|
*pType = REG_NONE;
|
|
*pcbNeeded = cbCurrentEnvironment;
|
|
if (nSize < *pcbNeeded)
|
|
return ERROR_MORE_DATA;
|
|
|
|
// Copy the environment name as the output value for SPLREG_ARCHITECTURE.
|
|
CopyMemory(pData, wszCurrentEnvironment, cbCurrentEnvironment);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else if (wcsicmp(pValueName, SPLREG_OS_VERSION) == 0)
|
|
{
|
|
POSVERSIONINFOW pInfo = (POSVERSIONINFOW)pData;
|
|
|
|
// Store the OSVERSIONINFOW structure as REG_NONE.
|
|
*pType = REG_NONE;
|
|
*pcbNeeded = sizeof(OSVERSIONINFOW);
|
|
if (nSize < *pcbNeeded)
|
|
return ERROR_MORE_DATA;
|
|
|
|
// Return OS version information.
|
|
pInfo->dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
|
|
GetVersionExW(pInfo);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else if (wcsicmp(pValueName, SPLREG_OS_VERSIONEX) == 0)
|
|
{
|
|
POSVERSIONINFOEXW pInfo = (POSVERSIONINFOEXW)pData;
|
|
|
|
// Store the OSVERSIONINFOEXW structure as REG_NONE.
|
|
*pType = REG_NONE;
|
|
*pcbNeeded = sizeof(OSVERSIONINFOEXW);
|
|
if (nSize < *pcbNeeded)
|
|
return ERROR_MORE_DATA;
|
|
|
|
// Return extended OS version information.
|
|
pInfo->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
|
|
GetVersionExW((POSVERSIONINFOW)pInfo);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else if (wcsicmp(pValueName, SPLREG_DS_PRESENT) == 0)
|
|
{
|
|
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pInfo;
|
|
|
|
// We want to store a REG_DWORD value.
|
|
*pType = REG_DWORD;
|
|
*pcbNeeded = sizeof(DWORD);
|
|
if (nSize < *pcbNeeded)
|
|
return ERROR_MORE_DATA;
|
|
|
|
// Get information about the domain membership of this computer.
|
|
dwErrorCode = DsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE*)&pInfo);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
ERR("DsRoleGetPrimaryDomainInformation failed with error %lu!\n", dwErrorCode);
|
|
return dwErrorCode;
|
|
}
|
|
|
|
// Return whether this computer is a workstation or server inside a domain.
|
|
*((PDWORD)pData) = (pInfo->MachineRole == DsRole_RoleMemberWorkstation || pInfo->MachineRole == DsRole_RoleMemberServer);
|
|
DsRoleFreeMemory(pInfo);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else if (wcsicmp(pValueName, SPLREG_DS_PRESENT_FOR_USER) == 0)
|
|
{
|
|
DWORD cch;
|
|
PWSTR p;
|
|
WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
|
|
WCHAR wszUserSam[UNLEN + 1];
|
|
|
|
// We want to store a REG_DWORD value.
|
|
*pType = REG_DWORD;
|
|
*pcbNeeded = sizeof(DWORD);
|
|
if (nSize < *pcbNeeded)
|
|
return ERROR_MORE_DATA;
|
|
|
|
// Get the local Computer Name.
|
|
cch = MAX_COMPUTERNAME_LENGTH + 1;
|
|
if (!GetComputerNameW(wszComputerName, &cch))
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
|
|
return dwErrorCode;
|
|
}
|
|
|
|
// Get the User Name in the SAM format.
|
|
// This could either be:
|
|
// COMPUTERNAME\User
|
|
// DOMAINNAME\User
|
|
cch = UNLEN + 1;
|
|
if (!GetUserNameExW(NameSamCompatible, wszUserSam, &cch))
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
ERR("GetUserNameExW failed with error %lu!\n", dwErrorCode);
|
|
return dwErrorCode;
|
|
}
|
|
|
|
// Terminate the SAM-formatted User Name at the backslash.
|
|
p = wcschr(wszUserSam, L'\\');
|
|
*p = 0;
|
|
|
|
// Compare it with the Computer Name.
|
|
// If they differ, this User is part of a domain.
|
|
*((PDWORD)pData) = (wcscmp(wszUserSam, wszComputerName) != 0);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else if (wcsicmp(pValueName, SPLREG_REMOTE_FAX) == 0)
|
|
{
|
|
// Store a DWORD value as REG_NONE.
|
|
*pType = REG_NONE;
|
|
*pcbNeeded = sizeof(DWORD);
|
|
if (nSize < *pcbNeeded)
|
|
return ERROR_MORE_DATA;
|
|
|
|
// TODO: We don't support any fax service yet, but let's return the same value as Windows Server 2003 here.
|
|
*((PDWORD)pData) = 1;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else if (wcsicmp(pValueName, SPLREG_DNS_MACHINE_NAME) == 0)
|
|
{
|
|
DWORD cchDnsName = 0;
|
|
|
|
// Get the length of the fully-qualified computer DNS name.
|
|
GetComputerNameExW(ComputerNameDnsFullyQualified, NULL, &cchDnsName);
|
|
dwErrorCode = GetLastError();
|
|
if (dwErrorCode != ERROR_MORE_DATA)
|
|
{
|
|
ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode);
|
|
return dwErrorCode;
|
|
}
|
|
|
|
// Check if our supplied buffer is large enough.
|
|
*pType = REG_SZ;
|
|
*pcbNeeded = cchDnsName * sizeof(WCHAR);
|
|
if (nSize < *pcbNeeded)
|
|
return ERROR_MORE_DATA;
|
|
|
|
// Get the actual DNS name.
|
|
if (!GetComputerNameExW(ComputerNameDnsFullyQualified, (PWSTR)pData, &cchDnsName))
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode);
|
|
return dwErrorCode;
|
|
}
|
|
|
|
// Lowercase the output just like Windows does.
|
|
_wcslwr((PWSTR)pData);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// For all other, unknown settings, we just return ERROR_INVALID_PARAMETER.
|
|
// That also includes SPLREG_WEBSHAREMGMT, which is supported in Windows Server 2003 according to the documentation,
|
|
// but is actually not!
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
DWORD WINAPI
|
|
LocalGetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, PDWORD pType, PBYTE pData, DWORD nSize, PDWORD pcbNeeded)
|
|
{
|
|
BYTE Temp;
|
|
DWORD dwErrorCode;
|
|
DWORD dwTemp;
|
|
PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
|
|
|
|
TRACE("LocalGetPrinterDataEx(%p, %S, %S, %p, %p, %lu, %p)\n", hPrinter, pKeyName, pValueName, pType, pData, nSize, pcbNeeded);
|
|
|
|
// Even if GetPrinterDataExW in winspool ensures that the RPC function is never called without a valid pointer for pType,
|
|
// it's officially optional. Windows' fpGetPrinterDataEx also works with NULL for pType!
|
|
// Ensure here that it is always set to simplify the code later.
|
|
if (!pType)
|
|
pType = &dwTemp;
|
|
|
|
// pData is later fed to RegQueryValueExW in many cases. When calling it with zero buffer size, RegQueryValueExW returns a
|
|
// different error code based on whether pData is NULL or something else.
|
|
// Ensure here that ERROR_MORE_DATA is always returned.
|
|
if (!pData)
|
|
pData = &Temp;
|
|
|
|
if (!pHandle)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
}
|
|
else if (!pcbNeeded)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_PARAMETER;
|
|
}
|
|
else if (pHandle->HandleType == HandleType_Printer)
|
|
{
|
|
dwErrorCode = _LocalGetPrinterHandleData(pHandle->pSpecificHandle, pKeyName, pValueName, pType, pData, nSize, pcbNeeded);
|
|
}
|
|
else if (pHandle->HandleType == HandleType_PrintServer)
|
|
{
|
|
dwErrorCode = _LocalGetPrintServerHandleData(pValueName, pType, pData, nSize, pcbNeeded);
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
SetLastError(dwErrorCode);
|
|
return dwErrorCode;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
LocalSetPrinterData(HANDLE hPrinter, PWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
|
|
{
|
|
TRACE("LocalSetPrinterData(%p, %S, %lu, %p, %lu)\n", hPrinter, pValueName, Type, pData, cbData);
|
|
|
|
// The ReactOS Printing Stack forwards all SetPrinterData calls to SetPrinterDataEx as soon as possible.
|
|
// This function may only be called if localspl.dll is used together with Windows Printing Stack components.
|
|
WARN("This function should never be called!\n");
|
|
return LocalSetPrinterDataEx(hPrinter, L"PrinterDriverData", pValueName, Type, pData, cbData);
|
|
}
|
|
|
|
static DWORD
|
|
_LocalSetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle, PCWSTR pKeyName, PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
|
|
{
|
|
DWORD dwErrorCode;
|
|
HKEY hKey = NULL;
|
|
PWSTR pwszSubKey = NULL;
|
|
|
|
dwErrorCode = _MakePrinterSubKey(pPrinterHandle, pKeyName, &pwszSubKey);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
goto Cleanup;
|
|
|
|
// Open the subkey.
|
|
dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, pwszSubKey, 0, KEY_SET_VALUE, &hKey);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
ERR("RegOpenKeyExW failed for \"%S\" with error %lu!\n", pwszSubKey, dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Set the value.
|
|
dwErrorCode = (DWORD)RegSetValueExW(hKey, pValueName, 0, Type, pData, cbData);
|
|
|
|
Cleanup:
|
|
if (hKey)
|
|
RegCloseKey(hKey);
|
|
|
|
if (pwszSubKey)
|
|
DllFreeSplMem(pwszSubKey);
|
|
|
|
return dwErrorCode;
|
|
}
|
|
|
|
static DWORD
|
|
_LocalSetPrintServerHandleData(PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
|
|
{
|
|
DWORD dwErrorCode;
|
|
|
|
if (wcsicmp(pValueName, SPLREG_DEFAULT_SPOOL_DIRECTORY) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_PORT_THREAD_PRIORITY) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_SCHEDULER_THREAD_PRIORITY) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_BEEP_ENABLED) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_ALLOW_USER_MANAGEFORMS) == 0)
|
|
{
|
|
return (DWORD)RegSetValueExW(hPrintersKey, pValueName, 0, Type, pData, cbData);
|
|
}
|
|
else if (wcsicmp(pValueName, SPLREG_NET_POPUP) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_RETRY_POPUP) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_NET_POPUP_TO_COMPUTER) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_EVENT_LOG) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ERROR) == 0 ||
|
|
wcsicmp(pValueName, SPLREG_RESTART_JOB_ON_POOL_ENABLED) == 0 ||
|
|
wcsicmp(pValueName, L"NoRemotePrinterDrivers") == 0)
|
|
{
|
|
HKEY hKey;
|
|
|
|
dwErrorCode = (DWORD)RegOpenKeyExW(hPrintKey, L"Providers", 0, KEY_SET_VALUE, &hKey);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
ERR("RegOpenKeyExW failed for \"Providers\" with error %lu!\n", dwErrorCode);
|
|
return dwErrorCode;
|
|
}
|
|
|
|
dwErrorCode = (DWORD)RegSetValueExW(hKey, pValueName, 0, Type, pData, cbData);
|
|
RegCloseKey(hKey);
|
|
return dwErrorCode;
|
|
}
|
|
else if (wcsicmp(pValueName, SPLREG_WEBSHAREMGMT) == 0)
|
|
{
|
|
WARN("Attempting to set WebShareMgmt, which is based on IIS and therefore not supported. Returning fake success!\n");
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// For all other, unknown settings, we just return ERROR_INVALID_PARAMETER.
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
DWORD WINAPI
|
|
LocalSetPrinterDataEx(HANDLE hPrinter, PCWSTR pKeyName, PCWSTR pValueName, DWORD Type, PBYTE pData, DWORD cbData)
|
|
{
|
|
DWORD dwErrorCode;
|
|
PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
|
|
|
|
TRACE("LocalSetPrinterDataEx(%p, %S, %S, %lu, %p, %lu)\n", hPrinter, pKeyName, pValueName, Type, pData, cbData);
|
|
|
|
if (!pHandle)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
}
|
|
else if (pHandle->HandleType == HandleType_Printer)
|
|
{
|
|
dwErrorCode = _LocalSetPrinterHandleData(pHandle->pSpecificHandle, pKeyName, pValueName, Type, pData, cbData);
|
|
}
|
|
else if (pHandle->HandleType == HandleType_PrintServer)
|
|
{
|
|
dwErrorCode = _LocalSetPrintServerHandleData(pValueName, Type, pData, cbData);
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
SetLastError(dwErrorCode);
|
|
return dwErrorCode;
|
|
}
|