mirror of
https://github.com/reactos/reactos.git
synced 2024-10-05 00:43:21 +00:00
7bffb70353
WinSpool : Implement missing API. Sync/Port from wine. Setting it to fast track for needed testing of the later GDI code. Leaving Fix-me debug prints on. Local tree has WinSpool_winetest turned on. So no debug noise during normal ReactOS operation. SpoolSS : Reordered header types. Have more Spl function to be added while SpoolSV is being coded to forward to LocalSpl. Based on wine and old research from the GDI code.
1000 lines
32 KiB
C
1000 lines
32 KiB
C
/*
|
|
* PROJECT: ReactOS Spooler API
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Functions related to Printer Drivers
|
|
* COPYRIGHT: Copyright 2015-2017 Colin Finck (colin@reactos.org)
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
#include <marshalling/printerdrivers.h>
|
|
extern const WCHAR wszCurrentEnvironment[];
|
|
|
|
static int multi_sz_lenA(const char *str)
|
|
{
|
|
const char *ptr = str;
|
|
if(!str) return 0;
|
|
do
|
|
{
|
|
ptr += lstrlenA(ptr) + 1;
|
|
} while(*ptr);
|
|
|
|
return ptr - str + 1;
|
|
}
|
|
|
|
static int multi_sz_lenW(const WCHAR *str)
|
|
{
|
|
const WCHAR *ptr = str;
|
|
if (!str) return 0;
|
|
do
|
|
{
|
|
ptr += lstrlenW(ptr) + 1;
|
|
} while (*ptr);
|
|
|
|
return (ptr - str + 1);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
AddPrinterDriverA(PSTR pName, DWORD Level, PBYTE pDriverInfo)
|
|
{
|
|
TRACE("AddPrinterDriverA(%s, %lu, %p)\n", pName, Level, pDriverInfo);
|
|
return AddPrinterDriverExA(pName, Level, pDriverInfo, APD_COPY_NEW_FILES);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
AddPrinterDriverExA(PSTR pName, DWORD Level, PBYTE pDriverInfo, DWORD dwFileCopyFlags)
|
|
{
|
|
PDRIVER_INFO_8A pdiA;
|
|
DRIVER_INFO_8W diW;
|
|
LPWSTR nameW = NULL;
|
|
DWORD lenA;
|
|
DWORD len;
|
|
BOOL res = FALSE;
|
|
|
|
TRACE("AddPrinterDriverExA(%s, %d, %p, 0x%x)\n", debugstr_a(pName), Level, pDriverInfo, dwFileCopyFlags);
|
|
|
|
pdiA = (DRIVER_INFO_8A *) pDriverInfo;
|
|
ZeroMemory(&diW, sizeof(diW));
|
|
|
|
if (Level < 2 || Level == 5 || Level == 7 || Level > 8)
|
|
{
|
|
SetLastError(ERROR_INVALID_LEVEL);
|
|
return FALSE;
|
|
}
|
|
|
|
if (pdiA == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
/* convert servername to unicode */
|
|
if (pName)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pName, -1, NULL, 0);
|
|
nameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pName, -1, nameW, len);
|
|
}
|
|
|
|
/* common fields */
|
|
diW.cVersion = pdiA->cVersion;
|
|
|
|
if (pdiA->pName)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pName, -1, NULL, 0);
|
|
diW.pName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pName, -1, diW.pName, len);
|
|
}
|
|
|
|
if (pdiA->pEnvironment)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pEnvironment, -1, NULL, 0);
|
|
diW.pEnvironment = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pEnvironment, -1, diW.pEnvironment, len);
|
|
}
|
|
|
|
if (pdiA->pDriverPath)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pDriverPath, -1, NULL, 0);
|
|
diW.pDriverPath = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pDriverPath, -1, diW.pDriverPath, len);
|
|
}
|
|
|
|
if (pdiA->pDataFile)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pDataFile, -1, NULL, 0);
|
|
diW.pDataFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pDataFile, -1, diW.pDataFile, len);
|
|
}
|
|
|
|
if (pdiA->pConfigFile)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pConfigFile, -1, NULL, 0);
|
|
diW.pConfigFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pConfigFile, -1, diW.pConfigFile, len);
|
|
}
|
|
|
|
if ((Level > 2) && pdiA->pHelpFile)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pHelpFile, -1, NULL, 0);
|
|
diW.pHelpFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pHelpFile, -1, diW.pHelpFile, len);
|
|
}
|
|
|
|
if ((Level > 2) && pdiA->pDependentFiles)
|
|
{
|
|
lenA = multi_sz_lenA(pdiA->pDependentFiles);
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pDependentFiles, lenA, NULL, 0);
|
|
diW.pDependentFiles = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pDependentFiles, lenA, diW.pDependentFiles, len);
|
|
}
|
|
|
|
if ((Level > 2) && pdiA->pMonitorName)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pMonitorName, -1, NULL, 0);
|
|
diW.pMonitorName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pMonitorName, -1, diW.pMonitorName, len);
|
|
}
|
|
|
|
if ((Level > 2) && pdiA->pDefaultDataType)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pDefaultDataType, -1, NULL, 0);
|
|
diW.pDefaultDataType = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pDefaultDataType, -1, diW.pDefaultDataType, len);
|
|
}
|
|
|
|
if ((Level > 3) && pdiA->pszzPreviousNames)
|
|
{
|
|
lenA = multi_sz_lenA(pdiA->pszzPreviousNames);
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pszzPreviousNames, lenA, NULL, 0);
|
|
diW.pszzPreviousNames = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pszzPreviousNames, lenA, diW.pszzPreviousNames, len);
|
|
}
|
|
|
|
if (Level > 5)
|
|
{
|
|
diW.ftDriverDate = pdiA->ftDriverDate;
|
|
diW.dwlDriverVersion = pdiA->dwlDriverVersion;
|
|
}
|
|
|
|
if ((Level > 5) && pdiA->pszMfgName)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pszMfgName, -1, NULL, 0);
|
|
diW.pszMfgName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pszMfgName, -1, diW.pszMfgName, len);
|
|
}
|
|
|
|
if ((Level > 5) && pdiA->pszOEMUrl)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pszOEMUrl, -1, NULL, 0);
|
|
diW.pszOEMUrl = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pszOEMUrl, -1, diW.pszOEMUrl, len);
|
|
}
|
|
|
|
if ((Level > 5) && pdiA->pszHardwareID)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pszHardwareID, -1, NULL, 0);
|
|
diW.pszHardwareID = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pszHardwareID, -1, diW.pszHardwareID, len);
|
|
}
|
|
|
|
if ((Level > 5) && pdiA->pszProvider)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pszProvider, -1, NULL, 0);
|
|
diW.pszProvider = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pszProvider, -1, diW.pszProvider, len);
|
|
}
|
|
|
|
if ((Level > 7) && pdiA->pszPrintProcessor)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pszPrintProcessor, -1, NULL, 0);
|
|
diW.pszPrintProcessor = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pszPrintProcessor, -1, diW.pszPrintProcessor, len);
|
|
}
|
|
|
|
if ((Level > 7) && pdiA->pszVendorSetup)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pszVendorSetup, -1, NULL, 0);
|
|
diW.pszVendorSetup = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pszVendorSetup, -1, diW.pszVendorSetup, len);
|
|
}
|
|
|
|
if ((Level > 7) && pdiA->pszzColorProfiles)
|
|
{
|
|
lenA = multi_sz_lenA(pdiA->pszzColorProfiles);
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pszzColorProfiles, lenA, NULL, 0);
|
|
diW.pszzColorProfiles = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pszzColorProfiles, lenA, diW.pszzColorProfiles, len);
|
|
}
|
|
|
|
if ((Level > 7) && pdiA->pszInfPath)
|
|
{
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pszInfPath, -1, NULL, 0);
|
|
diW.pszInfPath = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pszInfPath, -1, diW.pszInfPath, len);
|
|
}
|
|
|
|
if ((Level > 7) && pdiA->pszzCoreDriverDependencies)
|
|
{
|
|
lenA = multi_sz_lenA(pdiA->pszzCoreDriverDependencies);
|
|
len = MultiByteToWideChar(CP_ACP, 0, pdiA->pszzCoreDriverDependencies, lenA, NULL, 0);
|
|
diW.pszzCoreDriverDependencies = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
MultiByteToWideChar(CP_ACP, 0, pdiA->pszzCoreDriverDependencies, lenA, diW.pszzCoreDriverDependencies, len);
|
|
}
|
|
|
|
if (Level > 7)
|
|
{
|
|
diW.dwPrinterDriverAttributes = pdiA->dwPrinterDriverAttributes;
|
|
diW.ftMinInboxDriverVerDate = pdiA->ftMinInboxDriverVerDate;
|
|
diW.dwlMinInboxDriverVerVersion = pdiA->dwlMinInboxDriverVerVersion;
|
|
}
|
|
|
|
res = AddPrinterDriverExW(nameW, Level, (LPBYTE) &diW, dwFileCopyFlags);
|
|
|
|
TRACE("got %u with %u\n", res, GetLastError());
|
|
HeapFree(GetProcessHeap(), 0, nameW);
|
|
HeapFree(GetProcessHeap(), 0, diW.pName);
|
|
HeapFree(GetProcessHeap(), 0, diW.pEnvironment);
|
|
HeapFree(GetProcessHeap(), 0, diW.pDriverPath);
|
|
HeapFree(GetProcessHeap(), 0, diW.pDataFile);
|
|
HeapFree(GetProcessHeap(), 0, diW.pConfigFile);
|
|
HeapFree(GetProcessHeap(), 0, diW.pHelpFile);
|
|
HeapFree(GetProcessHeap(), 0, diW.pDependentFiles);
|
|
HeapFree(GetProcessHeap(), 0, diW.pMonitorName);
|
|
HeapFree(GetProcessHeap(), 0, diW.pDefaultDataType);
|
|
HeapFree(GetProcessHeap(), 0, diW.pszzPreviousNames);
|
|
HeapFree(GetProcessHeap(), 0, diW.pszMfgName);
|
|
HeapFree(GetProcessHeap(), 0, diW.pszOEMUrl);
|
|
HeapFree(GetProcessHeap(), 0, diW.pszHardwareID);
|
|
HeapFree(GetProcessHeap(), 0, diW.pszProvider);
|
|
HeapFree(GetProcessHeap(), 0, diW.pszPrintProcessor);
|
|
HeapFree(GetProcessHeap(), 0, diW.pszVendorSetup);
|
|
HeapFree(GetProcessHeap(), 0, diW.pszzColorProfiles);
|
|
HeapFree(GetProcessHeap(), 0, diW.pszInfPath);
|
|
HeapFree(GetProcessHeap(), 0, diW.pszzCoreDriverDependencies);
|
|
|
|
TRACE("=> %u with %u\n", res, GetLastError());
|
|
return res;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
AddPrinterDriverExW(PWSTR pName, DWORD Level, PBYTE pDriverInfo, DWORD dwFileCopyFlags)
|
|
{
|
|
DWORD dwErrorCode;
|
|
WINSPOOL_DRIVER_INFO_8 * pdi = NULL;
|
|
WINSPOOL_DRIVER_CONTAINER pDriverContainer;
|
|
|
|
TRACE("AddPrinterDriverExW(%S, %lu, %p, %lu)\n", pName, Level, pDriverInfo, dwFileCopyFlags);
|
|
|
|
pDriverContainer.Level = Level;
|
|
|
|
switch (Level)
|
|
{
|
|
case 8:
|
|
{
|
|
PDRIVER_INFO_8W pdi8w = (PDRIVER_INFO_8W)pDriverInfo;
|
|
pdi = HeapAlloc(hProcessHeap, 0, sizeof(WINSPOOL_DRIVER_INFO_8));
|
|
|
|
pdi->pPrintProcessor = pdi8w->pszPrintProcessor;
|
|
pdi->pVendorSetup = pdi8w->pszVendorSetup;
|
|
|
|
pdi->pszzColorProfiles = pdi8w->pszzColorProfiles;
|
|
pdi->cchColorProfiles = 0;
|
|
if ( pdi8w->pszzColorProfiles && *pdi8w->pszzColorProfiles )
|
|
{
|
|
pdi->cchColorProfiles = multi_sz_lenW( pdi8w->pszzColorProfiles );
|
|
}
|
|
|
|
pdi->pInfPath = pdi8w->pszInfPath;
|
|
|
|
pdi->pszzCoreDriverDependencies = pdi8w->pszzCoreDriverDependencies;
|
|
pdi->cchCoreDependencies = 0;
|
|
if ( pdi8w->pszzCoreDriverDependencies && *pdi8w->pszzCoreDriverDependencies )
|
|
{
|
|
pdi->cchCoreDependencies = multi_sz_lenW( pdi8w->pszzCoreDriverDependencies );
|
|
}
|
|
|
|
pdi->ftMinInboxDriverVerDate = pdi8w->ftMinInboxDriverVerDate;
|
|
pdi->dwlMinInboxDriverVerVersion = pdi8w->dwlMinInboxDriverVerVersion;
|
|
}
|
|
case 6:
|
|
{
|
|
PDRIVER_INFO_6W pdi6w = (PDRIVER_INFO_6W)pDriverInfo;
|
|
if ( pdi == NULL ) pdi = HeapAlloc(hProcessHeap, 0, sizeof(WINSPOOL_DRIVER_INFO_6));
|
|
|
|
pdi->pMfgName = pdi6w->pszMfgName;
|
|
pdi->pOEMUrl = pdi6w->pszOEMUrl;
|
|
pdi->pHardwareID = pdi6w->pszHardwareID;
|
|
pdi->pProvider = pdi6w->pszProvider;
|
|
pdi->ftDriverDate = pdi6w->ftDriverDate;
|
|
pdi->dwlDriverVersion = pdi6w->dwlDriverVersion;
|
|
}
|
|
case 4:
|
|
{
|
|
|
|
PDRIVER_INFO_4W pdi4w = (PDRIVER_INFO_4W)pDriverInfo;
|
|
if ( pdi == NULL ) pdi = HeapAlloc(hProcessHeap, 0, sizeof(WINSPOOL_DRIVER_INFO_4));
|
|
|
|
pdi->pszzPreviousNames = pdi4w->pszzPreviousNames;
|
|
pdi->cchPreviousNames = 0;
|
|
if ( pdi4w->pDependentFiles && *pdi4w->pDependentFiles )
|
|
{
|
|
pdi->cchPreviousNames = multi_sz_lenW( pdi4w->pDependentFiles );
|
|
}
|
|
}
|
|
case 3:
|
|
{
|
|
PDRIVER_INFO_3W pdi3w = (PDRIVER_INFO_3W)pDriverInfo;
|
|
if ( pdi == NULL ) pdi = HeapAlloc(hProcessHeap, 0, sizeof(WINSPOOL_DRIVER_INFO_3));
|
|
|
|
pdi->pHelpFile = pdi3w->pHelpFile;
|
|
pdi->pDependentFiles = pdi3w->pDependentFiles;
|
|
pdi->pMonitorName = pdi3w->pMonitorName;
|
|
pdi->pDefaultDataType = pdi3w->pDefaultDataType;
|
|
|
|
pdi->pDependentFiles = pdi3w->pDependentFiles;
|
|
pdi->cchDependentFiles = 0;
|
|
if ( pdi3w->pDependentFiles && *pdi3w->pDependentFiles )
|
|
{
|
|
pdi->cchDependentFiles = multi_sz_lenW( pdi3w->pDependentFiles );
|
|
}
|
|
}
|
|
case 2:
|
|
{
|
|
PDRIVER_INFO_2W pdi2w = (PDRIVER_INFO_2W)pDriverInfo;
|
|
if ( pdi == NULL ) pdi = HeapAlloc(hProcessHeap, 0, sizeof(WINSPOOL_DRIVER_INFO_2));
|
|
|
|
pdi->pName = pdi2w->pName;
|
|
|
|
pdi->pEnvironment = pdi2w->pEnvironment;
|
|
if ( !pdi2w->pEnvironment || !*pdi2w->pEnvironment )
|
|
{
|
|
pdi2w->pEnvironment = (PWSTR)wszCurrentEnvironment;
|
|
}
|
|
|
|
pdi->pDriverPath = pdi2w->pDriverPath;
|
|
pdi->pDataFile = pdi2w->pDataFile;
|
|
pdi->pConfigFile = pdi2w->pConfigFile;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
SetLastError(ERROR_INVALID_LEVEL);
|
|
return FALSE;
|
|
}
|
|
|
|
pDriverContainer.DriverInfo.Level8 = pdi;
|
|
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcAddPrinterDriverEx( pName, &pDriverContainer, dwFileCopyFlags );
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcAddPrinterDriverEx failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
if ( pdi ) HeapFree( GetProcessHeap(), 0, pdi );
|
|
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
AddPrinterDriverW(PWSTR pName, DWORD Level, PBYTE pDriverInfo)
|
|
{
|
|
TRACE("AddPrinterDriverW(%S, %lu, %p)\n", pName, Level, pDriverInfo);
|
|
return AddPrinterDriverExW(pName, Level, pDriverInfo, APD_COPY_NEW_FILES);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
DeletePrinterDriverA(PSTR pName, PSTR pEnvironment, PSTR pDriverName)
|
|
{
|
|
TRACE("DeletePrinterDriverA(%s, %s, %s)\n", pName, pEnvironment, pDriverName);
|
|
return DeletePrinterDriverExA(pName, pEnvironment, pDriverName, 0, 0);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
DeletePrinterDriverExA(PSTR pName, PSTR pEnvironment, PSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
|
|
{
|
|
UNICODE_STRING NameW, EnvW, DriverW;
|
|
BOOL ret;
|
|
|
|
TRACE("DeletePrinterDriverExA(%s, %s, %s, %lu, %lu)\n", pName, pEnvironment, pDriverName, dwDeleteFlag, dwVersionFlag);
|
|
|
|
AsciiToUnicode(&NameW, pName);
|
|
AsciiToUnicode(&EnvW, pEnvironment);
|
|
AsciiToUnicode(&DriverW, pDriverName);
|
|
|
|
ret = DeletePrinterDriverExW(NameW.Buffer, EnvW.Buffer, DriverW.Buffer, dwDeleteFlag, dwVersionFlag);
|
|
|
|
RtlFreeUnicodeString(&DriverW);
|
|
RtlFreeUnicodeString(&EnvW);
|
|
RtlFreeUnicodeString(&NameW);
|
|
|
|
return ret;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
DeletePrinterDriverExW(PWSTR pName, PWSTR pEnvironment, PWSTR pDriverName, DWORD dwDeleteFlag, DWORD dwVersionFlag)
|
|
{
|
|
DWORD dwErrorCode;
|
|
|
|
TRACE("DeletePrinterDriverExW(%S, %S, %S, %lu, %lu)\n", pName, pEnvironment, pDriverName, dwDeleteFlag, dwVersionFlag);
|
|
|
|
if ( !pDriverName || !*pDriverName )
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if ( !pEnvironment || !*pEnvironment )
|
|
{
|
|
pEnvironment = (PWSTR)wszCurrentEnvironment;
|
|
}
|
|
|
|
// Do the RPC call.
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcDeletePrinterDriverEx(pName, pEnvironment, pDriverName, dwDeleteFlag, dwVersionFlag);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcDeletePrinterDriverEx failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
|
|
}
|
|
|
|
BOOL WINAPI
|
|
DeletePrinterDriverW(PWSTR pName, PWSTR pEnvironment, PWSTR pDriverName)
|
|
{
|
|
TRACE("DeletePrinterDriverW(%S, %S, %S)\n", pName, pEnvironment, pDriverName);
|
|
return DeletePrinterDriverExW(pName, pEnvironment, pDriverName, 0, 0);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
EnumPrinterDriversA(PSTR pName, PSTR pEnvironment, DWORD Level, PBYTE pDriverInfo, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
|
|
{
|
|
BOOL ret;
|
|
DWORD dwErrorCode, i;
|
|
UNICODE_STRING pNameW, pEnvironmentW;
|
|
PWSTR pwstrNameW, pwstrEnvironmentW;
|
|
PDRIVER_INFO_1W pdi1w = (PDRIVER_INFO_1W)pDriverInfo;
|
|
PDRIVER_INFO_8W pdi8w = (PDRIVER_INFO_8W)pDriverInfo;
|
|
|
|
TRACE("EnumPrinterDriversA(%s, %s, %lu, %p, %lu, %p, %p)\n", pName, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded, pcReturned);
|
|
|
|
pwstrNameW = AsciiToUnicode(&pNameW, pName);
|
|
pwstrEnvironmentW = AsciiToUnicode(&pEnvironmentW, pEnvironment);
|
|
|
|
ret = EnumPrinterDriversW( pwstrNameW, pwstrEnvironmentW, Level, pDriverInfo, cbBuf, pcbNeeded, pcReturned );
|
|
|
|
if (ret)
|
|
{
|
|
for ( i = 0; i < *pcReturned; i++ )
|
|
{
|
|
switch (Level)
|
|
{
|
|
case 1:
|
|
{
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi1w[i].pName);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
break;
|
|
}
|
|
case 8:
|
|
{
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pszPrintProcessor);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pszVendorSetup);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
dwErrorCode = UnicodeToAnsiZZInPlace(pdi8w[i].pszzColorProfiles);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pszInfPath);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
dwErrorCode = UnicodeToAnsiZZInPlace(pdi8w[i].pszzCoreDriverDependencies);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
case 6:
|
|
{
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pszMfgName);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pszOEMUrl);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pszHardwareID);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pszProvider);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
case 4:
|
|
{
|
|
dwErrorCode = UnicodeToAnsiZZInPlace(pdi8w[i].pszzPreviousNames);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
case 3:
|
|
{
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pHelpFile);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiZZInPlace(pdi8w[i].pDependentFiles);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pMonitorName);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pDefaultDataType);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
case 2:
|
|
case 5:
|
|
{
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pName);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pEnvironment);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pDriverPath);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pDataFile);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w[i].pConfigFile);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Cleanup:
|
|
RtlFreeUnicodeString(&pNameW);
|
|
RtlFreeUnicodeString(&pEnvironmentW);
|
|
|
|
return ret;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
EnumPrinterDriversW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pDriverInfo, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
|
|
{
|
|
DWORD dwErrorCode;
|
|
|
|
TRACE("EnumPrinterDriversW(%S, %S, %lu, %p, %lu, %p, %p)\n", pName, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded, pcReturned);
|
|
|
|
// Dismiss invalid levels already at this point.
|
|
if (Level > 8 || Level == 7 || Level < 1)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_LEVEL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !pEnvironment || !*pEnvironment )
|
|
{
|
|
pEnvironment = (PWSTR)wszCurrentEnvironment;
|
|
}
|
|
|
|
if (cbBuf && pDriverInfo)
|
|
ZeroMemory(pDriverInfo, cbBuf);
|
|
|
|
// Do the RPC call
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcEnumPrinterDrivers( pName, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded, pcReturned );
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcEnumPrinterDrivers failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
// Replace relative offset addresses in the output by absolute pointers.
|
|
ASSERT(Level <= 6 || Level == 8);
|
|
MarshallUpStructuresArray(cbBuf, pDriverInfo, *pcReturned, pPrinterDriverMarshalling[Level]->pInfo, pPrinterDriverMarshalling[Level]->cbStructureSize, TRUE);
|
|
}
|
|
|
|
Cleanup:
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
|
|
}
|
|
|
|
BOOL WINAPI
|
|
GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
|
|
{
|
|
DWORD dwErrorCode;
|
|
/*
|
|
* We are mapping multiple different pointers to the same pDriverInfo pointer here so that
|
|
* we can use the same incoming pointer for different Levels
|
|
*/
|
|
PDRIVER_INFO_1W pdi1w = (PDRIVER_INFO_1W)pDriverInfo;
|
|
PDRIVER_INFO_8W pdi8w = (PDRIVER_INFO_8W)pDriverInfo;
|
|
|
|
DWORD cch;
|
|
PWSTR pwszEnvironment = NULL;
|
|
|
|
TRACE("GetPrinterDriverA(%p, %s, %lu, %p, %lu, %p)\n", hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
|
|
|
|
// Check for invalid levels here for early error return. Should be 1-6 & 8.
|
|
if (Level < 1 || Level == 7 || Level > 8)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_LEVEL;
|
|
ERR("Invalid Level!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pEnvironment)
|
|
{
|
|
// Convert pEnvironment to a Unicode string pwszEnvironment.
|
|
cch = strlen(pEnvironment);
|
|
|
|
pwszEnvironment = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
|
|
if (!pwszEnvironment)
|
|
{
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
ERR("HeapAlloc failed!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, pEnvironment, -1, pwszEnvironment, cch + 1);
|
|
}
|
|
|
|
if (!GetPrinterDriverW(hPrinter, pwszEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded))
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Do Unicode to ANSI conversions for strings based on Level
|
|
switch (Level)
|
|
{
|
|
case 1:
|
|
{
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi1w->pName);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
break;
|
|
}
|
|
case 8:
|
|
{
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pszPrintProcessor);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pszVendorSetup);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
dwErrorCode = UnicodeToAnsiZZInPlace(pdi8w->pszzColorProfiles);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pszInfPath);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
dwErrorCode = UnicodeToAnsiZZInPlace(pdi8w->pszzCoreDriverDependencies);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
case 6:
|
|
{
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pszMfgName);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pszOEMUrl);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pszHardwareID);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pszProvider);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
case 4:
|
|
{
|
|
dwErrorCode = UnicodeToAnsiZZInPlace(pdi8w->pszzPreviousNames);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
case 3:
|
|
{
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pHelpFile);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiZZInPlace(pdi8w->pDependentFiles);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pMonitorName);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pDefaultDataType);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
case 2:
|
|
case 5:
|
|
{
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pName);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pEnvironment);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pDriverPath);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pDataFile);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = UnicodeToAnsiInPlace(pdi8w->pConfigFile);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
dwErrorCode = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
if (pwszEnvironment)
|
|
{
|
|
HeapFree(hProcessHeap, 0, pwszEnvironment);
|
|
}
|
|
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
|
|
{
|
|
DWORD dwErrorCode;
|
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
|
|
|
TRACE("GetPrinterDriverW(%p, %S, %lu, %p, %lu, %p)\n", hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
|
|
|
|
// Sanity checks.
|
|
if (!pHandle)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Dismiss invalid levels already at this point.
|
|
if (Level > 8 || Level == 7 || Level < 1)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_LEVEL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !pEnvironment || !*pEnvironment )
|
|
{
|
|
pEnvironment = (PWSTR)wszCurrentEnvironment;
|
|
}
|
|
|
|
if (cbBuf && pDriverInfo)
|
|
ZeroMemory(pDriverInfo, cbBuf);
|
|
|
|
// Do the RPC call
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcGetPrinterDriver(pHandle->hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcGetPrinterDriver failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
// Replace relative offset addresses in the output by absolute pointers.
|
|
ASSERT(Level <= 6 || Level == 8);
|
|
MarshallUpStructure(cbBuf, pDriverInfo, pPrinterDriverMarshalling[Level]->pInfo, pPrinterDriverMarshalling[Level]->cbStructureSize, TRUE);
|
|
}
|
|
|
|
Cleanup:
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
GetPrinterDriverDirectoryA(PSTR pName, PSTR pEnvironment, DWORD Level, PBYTE pDriverDirectory, DWORD cbBuf, PDWORD pcbNeeded)
|
|
{
|
|
UNICODE_STRING nameW, environmentW;
|
|
BOOL ret;
|
|
DWORD pcbNeededW;
|
|
INT len = cbBuf * sizeof(WCHAR)/sizeof(CHAR);
|
|
WCHAR *driverDirectoryW = NULL;
|
|
|
|
TRACE("GetPrinterDriverDirectoryA(%s, %s, %d, %p, %d, %p)\n", debugstr_a(pName), debugstr_a(pEnvironment), Level, pDriverDirectory, cbBuf, pcbNeeded);
|
|
|
|
if (len) driverDirectoryW = HeapAlloc( GetProcessHeap(), 0, len );
|
|
|
|
if (pName)
|
|
{
|
|
RtlCreateUnicodeStringFromAsciiz(&nameW, pName);
|
|
}
|
|
else
|
|
{
|
|
nameW.Buffer = NULL;
|
|
}
|
|
if (pEnvironment)
|
|
{
|
|
RtlCreateUnicodeStringFromAsciiz(&environmentW, pEnvironment);
|
|
}
|
|
else
|
|
{
|
|
environmentW.Buffer = NULL;
|
|
}
|
|
|
|
ret = GetPrinterDriverDirectoryW( nameW.Buffer, environmentW.Buffer, Level, (LPBYTE)driverDirectoryW, len, &pcbNeededW );
|
|
|
|
if (ret)
|
|
{
|
|
DWORD needed = WideCharToMultiByte( CP_ACP, 0, driverDirectoryW, -1, (LPSTR)pDriverDirectory, cbBuf, NULL, NULL);
|
|
|
|
if ( pcbNeeded )
|
|
*pcbNeeded = needed;
|
|
|
|
ret = needed <= cbBuf;
|
|
}
|
|
else
|
|
{
|
|
if (pcbNeeded) *pcbNeeded = pcbNeededW * sizeof(CHAR)/sizeof(WCHAR);
|
|
}
|
|
|
|
TRACE("required: 0x%x/%d\n", pcbNeeded ? *pcbNeeded : 0, pcbNeeded ? *pcbNeeded : 0);
|
|
|
|
HeapFree( GetProcessHeap(), 0, driverDirectoryW );
|
|
RtlFreeUnicodeString(&environmentW);
|
|
RtlFreeUnicodeString(&nameW);
|
|
|
|
return ret;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
GetPrinterDriverDirectoryW(PWSTR pName, PWSTR pEnvironment, DWORD Level, PBYTE pDriverDirectory, DWORD cbBuf, PDWORD pcbNeeded)
|
|
{
|
|
DWORD dwErrorCode;
|
|
|
|
TRACE("GetPrinterDriverDirectoryW(%S, %S, %lu, %p, %lu, %p)\n", pName, pEnvironment, Level, pDriverDirectory, cbBuf, pcbNeeded);
|
|
|
|
if ( !pEnvironment || !*pEnvironment )
|
|
{
|
|
pEnvironment = (PWSTR)wszCurrentEnvironment;
|
|
}
|
|
|
|
// Do the RPC call.
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcGetPrinterDriverDirectory(pName, pEnvironment, Level, pDriverDirectory, cbBuf, pcbNeeded);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcGetPrinterDriverDirectory failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|