mirror of
https://github.com/reactos/reactos.git
synced 2024-10-22 05:46:19 +00:00
1306 lines
36 KiB
C
1306 lines
36 KiB
C
/*
|
|
* PROJECT: ReactOS Spooler API
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Functions related to Printers and printing
|
|
* COPYRIGHT: Copyright 2015-2018 Colin Finck (colin@reactos.org)
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
#include <marshalling/printers.h>
|
|
#include <marshalling/printerdrivers.h>
|
|
|
|
// Local Constants
|
|
|
|
/** And the award for the most confusingly named setting goes to "Device", for storing the default printer of the current user.
|
|
Ok, I admit that this has historical reasons. It's still not straightforward in any way though! */
|
|
static const WCHAR wszWindowsKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows";
|
|
static const WCHAR wszDeviceValue[] = L"Device";
|
|
|
|
static DWORD
|
|
_StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB_INFO_1W pAddJobInfo1)
|
|
{
|
|
DWORD cbNeeded;
|
|
DWORD dwErrorCode;
|
|
PJOB_INFO_1W pJobInfo1 = NULL;
|
|
|
|
// Create the spool file.
|
|
pHandle->hSPLFile = CreateFileW(pAddJobInfo1->Path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
|
|
if (pHandle->hSPLFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
ERR("CreateFileW failed for \"%S\" with error %lu!\n", pAddJobInfo1->Path, dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Get the size of the job information.
|
|
GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, NULL, 0, &cbNeeded);
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
ERR("GetJobW failed with error %lu!\n", dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Allocate enough memory for the returned job information.
|
|
pJobInfo1 = HeapAlloc(hProcessHeap, 0, cbNeeded);
|
|
if (!pJobInfo1)
|
|
{
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
ERR("HeapAlloc failed!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Get the job information.
|
|
if (!GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, cbNeeded, &cbNeeded))
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
ERR("GetJobW failed with error %lu!\n", dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Add our document information.
|
|
if (pDocInfo1->pDatatype)
|
|
pJobInfo1->pDatatype = pDocInfo1->pDatatype;
|
|
|
|
pJobInfo1->pDocument = pDocInfo1->pDocName;
|
|
|
|
// Set the new job information.
|
|
if (!SetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, 0))
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
ERR("SetJobW failed with error %lu!\n", dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// We were successful!
|
|
pHandle->dwJobID = pAddJobInfo1->JobId;
|
|
dwErrorCode = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
if (pJobInfo1)
|
|
HeapFree(hProcessHeap, 0, pJobInfo1);
|
|
|
|
return dwErrorCode;
|
|
}
|
|
|
|
static DWORD
|
|
_StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1)
|
|
{
|
|
DWORD dwErrorCode;
|
|
WINSPOOL_DOC_INFO_CONTAINER DocInfoContainer;
|
|
|
|
DocInfoContainer.Level = 1;
|
|
DocInfoContainer.DocInfo.pDocInfo1 = (WINSPOOL_DOC_INFO_1*)pDocInfo1;
|
|
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcStartDocPrinter(pHandle->hPrinter, &DocInfoContainer, &pHandle->dwJobID);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcStartDocPrinter failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
return dwErrorCode;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
AbortPrinter(HANDLE hPrinter)
|
|
{
|
|
TRACE("AbortPrinter(%p)\n", hPrinter);
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
HANDLE WINAPI
|
|
AddPrinterA(PSTR pName, DWORD Level, PBYTE pPrinter)
|
|
{
|
|
TRACE("AddPrinterA(%s, %lu, %p)\n", pName, Level, pPrinter);
|
|
UNIMPLEMENTED;
|
|
return NULL;
|
|
}
|
|
|
|
HANDLE WINAPI
|
|
AddPrinterW(PWSTR pName, DWORD Level, PBYTE pPrinter)
|
|
{
|
|
TRACE("AddPrinterW(%S, %lu, %p)\n", pName, Level, pPrinter);
|
|
UNIMPLEMENTED;
|
|
return NULL;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
ClosePrinter(HANDLE hPrinter)
|
|
{
|
|
DWORD dwErrorCode;
|
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
|
|
|
TRACE("ClosePrinter(%p)\n", hPrinter);
|
|
|
|
// Sanity checks.
|
|
if (!pHandle)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Do the RPC call.
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcClosePrinter(&pHandle->hPrinter);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcClosePrinter failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
// Close any open file handle.
|
|
if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle(pHandle->hSPLFile);
|
|
|
|
// Free the memory for the handle.
|
|
HeapFree(hProcessHeap, 0, pHandle);
|
|
|
|
Cleanup:
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
DeletePrinter(HANDLE hPrinter)
|
|
{
|
|
TRACE("DeletePrinter(%p)\n", hPrinter);
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
DeviceCapabilitiesA(LPCSTR pDevice, LPCSTR pPort, WORD fwCapability, LPSTR pOutput, const DEVMODEA* pDevMode)
|
|
{
|
|
TRACE("DeviceCapabilitiesA(%s, %s, %hu, %p, %p)\n", pDevice, pPort, fwCapability, pOutput, pDevMode);
|
|
UNIMPLEMENTED;
|
|
return 0;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
DeviceCapabilitiesW(LPCWSTR pDevice, LPCWSTR pPort, WORD fwCapability, LPWSTR pOutput, const DEVMODEW* pDevMode)
|
|
{
|
|
TRACE("DeviceCapabilitiesW(%S, %S, %hu, %p, %p)\n", pDevice, pPort, fwCapability, pOutput, pDevMode);
|
|
UNIMPLEMENTED;
|
|
return 0;
|
|
}
|
|
|
|
LONG WINAPI
|
|
DocumentPropertiesA(HWND hWnd, HANDLE hPrinter, LPSTR pDeviceName, PDEVMODEA pDevModeOutput, PDEVMODEA pDevModeInput, DWORD fMode)
|
|
{
|
|
TRACE("DocumentPropertiesA(%p, %p, %s, %p, %p, %lu)\n", hWnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode);
|
|
UNIMPLEMENTED;
|
|
return -1;
|
|
}
|
|
|
|
static PRINTER_INFO_9W * get_devmodeW(HANDLE hprn)
|
|
{
|
|
PRINTER_INFO_9W *pi9 = NULL;
|
|
DWORD needed = 0;
|
|
BOOL res;
|
|
|
|
res = GetPrinterW(hprn, 9, NULL, 0, &needed);
|
|
if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
|
|
{
|
|
pi9 = HeapAlloc(hProcessHeap, 0, needed);
|
|
res = GetPrinterW(hprn, 9, (LPBYTE)pi9, needed, &needed);
|
|
}
|
|
|
|
if (res)
|
|
return pi9;
|
|
|
|
ERR("GetPrinterW failed with %u\n", GetLastError());
|
|
HeapFree(hProcessHeap, 0, pi9);
|
|
return NULL;
|
|
}
|
|
|
|
LONG WINAPI
|
|
DocumentPropertiesW(HWND hWnd, HANDLE hPrinter, LPWSTR pDeviceName, PDEVMODEW pDevModeOutput, PDEVMODEW pDevModeInput, DWORD fMode)
|
|
{
|
|
HANDLE hUseHandle = NULL;
|
|
PRINTER_INFO_9W *pi9 = NULL;
|
|
LONG Result = -1, Length;
|
|
|
|
TRACE("DocumentPropertiesW(%p, %p, %S, %p, %p, %lu)\n", hWnd, hPrinter, pDeviceName, pDevModeOutput, pDevModeInput, fMode);
|
|
if (hPrinter)
|
|
{
|
|
hUseHandle = hPrinter;
|
|
}
|
|
else if (!OpenPrinterW(pDeviceName, &hUseHandle, NULL))
|
|
{
|
|
ERR("No handle, and no usable printer name passed in\n");
|
|
return -1;
|
|
}
|
|
|
|
pi9 = get_devmodeW(hUseHandle);
|
|
|
|
if (pi9)
|
|
{
|
|
Length = pi9->pDevMode->dmSize + pi9->pDevMode->dmDriverExtra;
|
|
// See wineps.drv PSDRV_ExtDeviceMode
|
|
if (fMode)
|
|
{
|
|
Result = 1; /* IDOK */
|
|
|
|
if (fMode & DM_IN_BUFFER)
|
|
{
|
|
FIXME("Merge pDevModeInput with pi9, write back to driver!\n");
|
|
// See wineps.drv PSDRV_MergeDevmodes
|
|
}
|
|
|
|
if (fMode & DM_IN_PROMPT)
|
|
{
|
|
FIXME("Show property sheet!\n");
|
|
Result = 2; /* IDCANCEL */
|
|
}
|
|
|
|
if (fMode & (DM_OUT_BUFFER | DM_OUT_DEFAULT))
|
|
{
|
|
if (pDevModeOutput)
|
|
{
|
|
memcpy(pDevModeOutput, pi9->pDevMode, pi9->pDevMode->dmSize + pi9->pDevMode->dmDriverExtra);
|
|
}
|
|
else
|
|
{
|
|
ERR("No pDevModeOutput\n");
|
|
Result = -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Result = Length;
|
|
}
|
|
|
|
HeapFree(hProcessHeap, 0, pi9);
|
|
}
|
|
|
|
if (hUseHandle && !hPrinter)
|
|
ClosePrinter(hUseHandle);
|
|
return Result;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
EndDocPrinter(HANDLE hPrinter)
|
|
{
|
|
DWORD dwErrorCode;
|
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
|
|
|
TRACE("EndDocPrinter(%p)\n", hPrinter);
|
|
|
|
// Sanity checks.
|
|
if (!pHandle)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
// For spooled jobs, the document is finished by calling _RpcScheduleJob.
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcScheduleJob(pHandle->hPrinter, pHandle->dwJobID);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcScheduleJob failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
// Close the spool file handle.
|
|
CloseHandle(pHandle->hSPLFile);
|
|
}
|
|
else
|
|
{
|
|
// In all other cases, just call _RpcEndDocPrinter.
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcEndDocPrinter(pHandle->hPrinter);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcEndDocPrinter failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
}
|
|
|
|
// A new document can now be started again.
|
|
pHandle->bStartedDoc = FALSE;
|
|
|
|
Cleanup:
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
EndPagePrinter(HANDLE hPrinter)
|
|
{
|
|
DWORD dwErrorCode;
|
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
|
|
|
TRACE("EndPagePrinter(%p)\n", hPrinter);
|
|
|
|
// Sanity checks.
|
|
if (!pHandle)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
// For spooled jobs, we don't need to do anything.
|
|
dwErrorCode = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// In all other cases, just call _RpcEndPagePrinter.
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcEndPagePrinter(pHandle->hPrinter);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcEndPagePrinter failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
}
|
|
|
|
Cleanup:
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
EnumPrintersA(DWORD Flags, PSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
|
|
{
|
|
TRACE("EnumPrintersA(%lu, %s, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
|
|
{
|
|
DWORD dwErrorCode;
|
|
|
|
TRACE("EnumPrintersW(%lu, %S, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
|
|
|
|
// Dismiss invalid levels already at this point.
|
|
if (Level == 3 || Level > 5)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_LEVEL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (cbBuf && pPrinterEnum)
|
|
ZeroMemory(pPrinterEnum, cbBuf);
|
|
|
|
// Do the RPC call
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcEnumPrinters(Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcEnumPrinters failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
// Replace relative offset addresses in the output by absolute pointers.
|
|
ASSERT(Level <= 9);
|
|
MarshallUpStructuresArray(cbBuf, pPrinterEnum, *pcReturned, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
|
|
}
|
|
|
|
Cleanup:
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
FlushPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten, DWORD cSleep)
|
|
{
|
|
TRACE("FlushPrinter(%p, %p, %lu, %p, %lu)\n", hPrinter, pBuf, cbBuf, pcWritten, cSleep);
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
|
|
{
|
|
DWORD dwErrorCode;
|
|
PWSTR pwszBuffer = NULL;
|
|
|
|
TRACE("GetDefaultPrinterA(%p, %p)\n", pszBuffer, pcchBuffer);
|
|
|
|
// Sanity check.
|
|
if (!pcchBuffer)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Check if an ANSI buffer was given and if so, allocate a Unicode buffer of the same size.
|
|
if (pszBuffer && *pcchBuffer)
|
|
{
|
|
pwszBuffer = HeapAlloc(hProcessHeap, 0, *pcchBuffer * sizeof(WCHAR));
|
|
if (!pwszBuffer)
|
|
{
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
ERR("HeapAlloc failed!\n");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (!GetDefaultPrinterW(pwszBuffer, pcchBuffer))
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
// We successfully got a string in pwszBuffer, so convert the Unicode string to ANSI.
|
|
WideCharToMultiByte(CP_ACP, 0, pwszBuffer, -1, pszBuffer, *pcchBuffer, NULL, NULL);
|
|
|
|
dwErrorCode = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
if (pwszBuffer)
|
|
HeapFree(hProcessHeap, 0, pwszBuffer);
|
|
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
|
|
{
|
|
DWORD cbNeeded;
|
|
DWORD cchInputBuffer;
|
|
DWORD dwErrorCode;
|
|
HKEY hWindowsKey = NULL;
|
|
PWSTR pwszDevice = NULL;
|
|
PWSTR pwszComma;
|
|
|
|
TRACE("GetDefaultPrinterW(%p, %p)\n", pszBuffer, pcchBuffer);
|
|
|
|
// Sanity check.
|
|
if (!pcchBuffer)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
cchInputBuffer = *pcchBuffer;
|
|
|
|
// Open the registry key where the default printer for the current user is stored.
|
|
dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_READ, &hWindowsKey);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Determine the size of the required buffer.
|
|
dwErrorCode = (DWORD)RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, NULL, &cbNeeded);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Allocate it.
|
|
pwszDevice = HeapAlloc(hProcessHeap, 0, cbNeeded);
|
|
if (!pwszDevice)
|
|
{
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
ERR("HeapAlloc failed!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Now get the actual value.
|
|
dwErrorCode = RegQueryValueExW(hWindowsKey, wszDeviceValue, NULL, NULL, (PBYTE)pwszDevice, &cbNeeded);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// We get a string "<Printer Name>,winspool,<Port>:".
|
|
// Extract the printer name from it.
|
|
pwszComma = wcschr(pwszDevice, L',');
|
|
if (!pwszComma)
|
|
{
|
|
ERR("Found no or invalid default printer: %S!\n", pwszDevice);
|
|
dwErrorCode = ERROR_INVALID_NAME;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Store the length of the Printer Name (including the terminating NUL character!) in *pcchBuffer.
|
|
*pcchBuffer = pwszComma - pwszDevice + 1;
|
|
|
|
// Check if the supplied buffer is large enough.
|
|
if (cchInputBuffer < *pcchBuffer)
|
|
{
|
|
dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Copy the default printer.
|
|
*pwszComma = 0;
|
|
CopyMemory(pszBuffer, pwszDevice, *pcchBuffer * sizeof(WCHAR));
|
|
|
|
dwErrorCode = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
if (hWindowsKey)
|
|
RegCloseKey(hWindowsKey);
|
|
|
|
if (pwszDevice)
|
|
HeapFree(hProcessHeap, 0, pwszDevice);
|
|
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
GetPrinterA(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
|
|
{
|
|
TRACE("GetPrinterA(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
|
|
if(pcbNeeded) *pcbNeeded = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
GetPrinterDriverA(HANDLE hPrinter, LPSTR pEnvironment, DWORD Level, LPBYTE pDriverInfo, DWORD cbBuf, LPDWORD pcbNeeded)
|
|
{
|
|
TRACE("GetPrinterDriverA(%p, %s, %lu, %p, %lu, %p)\n", hPrinter, pEnvironment, Level, pDriverInfo, cbBuf, pcbNeeded);
|
|
if(pcbNeeded) *pcbNeeded = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
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 < 1)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_LEVEL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
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("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
// Replace relative offset addresses in the output by absolute pointers.
|
|
ASSERT(Level <= 3);
|
|
MarshallUpStructure(cbBuf, pDriverInfo, pPrinterDriverMarshalling[Level]->pInfo, pPrinterDriverMarshalling[Level]->cbStructureSize, TRUE);
|
|
}
|
|
|
|
Cleanup:
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
|
|
{
|
|
DWORD dwErrorCode;
|
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
|
|
|
TRACE("GetPrinterW(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
|
|
|
|
// Sanity checks.
|
|
if (!pHandle)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Dismiss invalid levels already at this point.
|
|
if (Level > 9)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_LEVEL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (cbBuf && pPrinter)
|
|
ZeroMemory(pPrinter, cbBuf);
|
|
|
|
// Do the RPC call
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcGetPrinter(pHandle->hPrinter, Level, pPrinter, cbBuf, pcbNeeded);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcGetPrinter failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
// Replace relative offset addresses in the output by absolute pointers.
|
|
ASSERT(Level <= 9);
|
|
MarshallUpStructure(cbBuf, pPrinter, pPrinterInfoMarshalling[Level]->pInfo, pPrinterInfoMarshalling[Level]->cbStructureSize, TRUE);
|
|
}
|
|
|
|
Cleanup:
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefault)
|
|
{
|
|
BOOL bReturnValue = FALSE;
|
|
DWORD cch;
|
|
PWSTR pwszPrinterName = NULL;
|
|
PRINTER_DEFAULTSW wDefault = { 0 };
|
|
|
|
TRACE("OpenPrinterA(%s, %p, %p)\n", pPrinterName, phPrinter, pDefault);
|
|
|
|
if (pPrinterName)
|
|
{
|
|
// Convert pPrinterName to a Unicode string pwszPrinterName
|
|
cch = strlen(pPrinterName);
|
|
|
|
pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
|
|
if (!pwszPrinterName)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
ERR("HeapAlloc failed!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, pPrinterName, -1, pwszPrinterName, cch + 1);
|
|
}
|
|
|
|
if (pDefault)
|
|
{
|
|
wDefault.DesiredAccess = pDefault->DesiredAccess;
|
|
|
|
if (pDefault->pDatatype)
|
|
{
|
|
// Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
|
|
cch = strlen(pDefault->pDatatype);
|
|
|
|
wDefault.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
|
|
if (!wDefault.pDatatype)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
ERR("HeapAlloc failed!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, pDefault->pDatatype, -1, wDefault.pDatatype, cch + 1);
|
|
}
|
|
|
|
if (pDefault->pDevMode)
|
|
wDefault.pDevMode = GdiConvertToDevmodeW(pDefault->pDevMode);
|
|
}
|
|
|
|
bReturnValue = OpenPrinterW(pwszPrinterName, phPrinter, &wDefault);
|
|
|
|
Cleanup:
|
|
if (wDefault.pDatatype)
|
|
HeapFree(hProcessHeap, 0, wDefault.pDatatype);
|
|
|
|
if (wDefault.pDevMode)
|
|
HeapFree(hProcessHeap, 0, wDefault.pDevMode);
|
|
|
|
if (pwszPrinterName)
|
|
HeapFree(hProcessHeap, 0, pwszPrinterName);
|
|
|
|
return bReturnValue;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault)
|
|
{
|
|
DWORD dwErrorCode;
|
|
HANDLE hPrinter;
|
|
PSPOOLER_HANDLE pHandle;
|
|
PWSTR pDatatype = NULL;
|
|
WINSPOOL_DEVMODE_CONTAINER DevModeContainer = { 0 };
|
|
ACCESS_MASK AccessRequired = 0;
|
|
|
|
TRACE("OpenPrinterW(%S, %p, %p)\n", pPrinterName, phPrinter, pDefault);
|
|
|
|
// Sanity check
|
|
if (!phPrinter)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Prepare the additional parameters in the format required by _RpcOpenPrinter
|
|
if (pDefault)
|
|
{
|
|
pDatatype = pDefault->pDatatype;
|
|
DevModeContainer.cbBuf = sizeof(DEVMODEW);
|
|
DevModeContainer.pDevMode = (BYTE*)pDefault->pDevMode;
|
|
AccessRequired = pDefault->DesiredAccess;
|
|
}
|
|
|
|
// Do the RPC call
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, &DevModeContainer, AccessRequired);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcOpenPrinter failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
// Create a new SPOOLER_HANDLE structure.
|
|
pHandle = HeapAlloc(hProcessHeap, HEAP_ZERO_MEMORY, sizeof(SPOOLER_HANDLE));
|
|
if (!pHandle)
|
|
{
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
ERR("HeapAlloc failed!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
pHandle->hPrinter = hPrinter;
|
|
pHandle->hSPLFile = INVALID_HANDLE_VALUE;
|
|
|
|
// Return it as phPrinter.
|
|
*phPrinter = (HANDLE)pHandle;
|
|
}
|
|
|
|
Cleanup:
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
ReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead)
|
|
{
|
|
DWORD dwErrorCode;
|
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
|
|
|
TRACE("ReadPrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pNoBytesRead);
|
|
|
|
// Sanity checks.
|
|
if (!pHandle)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Do the RPC call
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcReadPrinter(pHandle->hPrinter, pBuf, cbBuf, pNoBytesRead);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcReadPrinter failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
Cleanup:
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
ResetPrinterA(HANDLE hPrinter, PPRINTER_DEFAULTSA pDefault)
|
|
{
|
|
TRACE("ResetPrinterA(%p, %p)\n", hPrinter, pDefault);
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
ResetPrinterW(HANDLE hPrinter, PPRINTER_DEFAULTSW pDefault)
|
|
{
|
|
TRACE("ResetPrinterW(%p, %p)\n", hPrinter, pDefault);
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
SetDefaultPrinterA(LPCSTR pszPrinter)
|
|
{
|
|
BOOL bReturnValue = FALSE;
|
|
DWORD cch;
|
|
PWSTR pwszPrinter = NULL;
|
|
|
|
TRACE("SetDefaultPrinterA(%s)\n", pszPrinter);
|
|
|
|
if (pszPrinter)
|
|
{
|
|
// Convert pszPrinter to a Unicode string pwszPrinter
|
|
cch = strlen(pszPrinter);
|
|
|
|
pwszPrinter = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
|
|
if (!pwszPrinter)
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
ERR("HeapAlloc failed!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, pszPrinter, -1, pwszPrinter, cch + 1);
|
|
}
|
|
|
|
bReturnValue = SetDefaultPrinterW(pwszPrinter);
|
|
|
|
Cleanup:
|
|
if (pwszPrinter)
|
|
HeapFree(hProcessHeap, 0, pwszPrinter);
|
|
|
|
return bReturnValue;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
SetDefaultPrinterW(LPCWSTR pszPrinter)
|
|
{
|
|
const WCHAR wszDevicesKey[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Devices";
|
|
|
|
DWORD cbDeviceValueData;
|
|
DWORD cbPrinterValueData = 0;
|
|
DWORD cchPrinter;
|
|
DWORD dwErrorCode;
|
|
HKEY hDevicesKey = NULL;
|
|
HKEY hWindowsKey = NULL;
|
|
PWSTR pwszDeviceValueData = NULL;
|
|
WCHAR wszPrinter[MAX_PRINTER_NAME + 1];
|
|
|
|
TRACE("SetDefaultPrinterW(%S)\n", pszPrinter);
|
|
|
|
// Open the Devices registry key.
|
|
dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszDevicesKey, 0, KEY_READ, &hDevicesKey);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Did the caller give us a printer to set as default?
|
|
if (pszPrinter && *pszPrinter)
|
|
{
|
|
// Check if the given printer exists and query the value data size.
|
|
dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, NULL, &cbPrinterValueData);
|
|
if (dwErrorCode == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_PRINTER_NAME;
|
|
goto Cleanup;
|
|
}
|
|
else if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
ERR("RegQueryValueExW failed with status %lu!\n", dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
cchPrinter = wcslen(pszPrinter);
|
|
}
|
|
else
|
|
{
|
|
// If there is already a default printer, we're done!
|
|
cchPrinter = _countof(wszPrinter);
|
|
if (GetDefaultPrinterW(wszPrinter, &cchPrinter))
|
|
{
|
|
dwErrorCode = ERROR_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Otherwise, get us the first printer from the "Devices" key to later set it as default and query the value data size.
|
|
cchPrinter = _countof(wszPrinter);
|
|
dwErrorCode = (DWORD)RegEnumValueW(hDevicesKey, 0, wszPrinter, &cchPrinter, NULL, NULL, NULL, &cbPrinterValueData);
|
|
if (dwErrorCode != ERROR_MORE_DATA)
|
|
goto Cleanup;
|
|
|
|
pszPrinter = wszPrinter;
|
|
}
|
|
|
|
// We now need to query the value data, which has the format "winspool,<Port>:"
|
|
// and make "<Printer Name>,winspool,<Port>:" out of it.
|
|
// Allocate a buffer large enough for the final data.
|
|
cbDeviceValueData = (cchPrinter + 1) * sizeof(WCHAR) + cbPrinterValueData;
|
|
pwszDeviceValueData = HeapAlloc(hProcessHeap, 0, cbDeviceValueData);
|
|
if (!pwszDeviceValueData)
|
|
{
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
ERR("HeapAlloc failed!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Copy the Printer Name and a comma into it.
|
|
CopyMemory(pwszDeviceValueData, pszPrinter, cchPrinter * sizeof(WCHAR));
|
|
pwszDeviceValueData[cchPrinter] = L',';
|
|
|
|
// Append the value data, which has the format "winspool,<Port>:"
|
|
dwErrorCode = (DWORD)RegQueryValueExW(hDevicesKey, pszPrinter, NULL, NULL, (PBYTE)&pwszDeviceValueData[cchPrinter + 1], &cbPrinterValueData);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
goto Cleanup;
|
|
|
|
// Open the Windows registry key.
|
|
dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_CURRENT_USER, wszWindowsKey, 0, KEY_SET_VALUE, &hWindowsKey);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Store our new default printer.
|
|
dwErrorCode = (DWORD)RegSetValueExW(hWindowsKey, wszDeviceValue, 0, REG_SZ, (PBYTE)pwszDeviceValueData, cbDeviceValueData);
|
|
if (dwErrorCode != ERROR_SUCCESS)
|
|
{
|
|
ERR("RegSetValueExW failed with status %lu!\n", dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
if (hDevicesKey)
|
|
RegCloseKey(hDevicesKey);
|
|
|
|
if (hWindowsKey)
|
|
RegCloseKey(hWindowsKey);
|
|
|
|
if (pwszDeviceValueData)
|
|
HeapFree(hProcessHeap, 0, pwszDeviceValueData);
|
|
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
SetPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
|
|
{
|
|
TRACE("SetPrinterA(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
SetPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD Command)
|
|
{
|
|
TRACE("SetPrinterW(%p, %lu, %p, %lu)\n", hPrinter, Level, pPrinter, Command);
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
StartDocPrinterA(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
|
|
{
|
|
DOC_INFO_1W wDocInfo1 = { 0 };
|
|
DWORD cch;
|
|
DWORD dwErrorCode;
|
|
DWORD dwReturnValue = 0;
|
|
PDOC_INFO_1A pDocInfo1 = (PDOC_INFO_1A)pDocInfo;
|
|
|
|
TRACE("StartDocPrinterA(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
|
|
|
|
// Only check the minimum required for accessing pDocInfo.
|
|
// Additional sanity checks are done in StartDocPrinterW.
|
|
if (!pDocInfo1)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (Level != 1)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_LEVEL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pDocInfo1->pDatatype)
|
|
{
|
|
// Convert pDocInfo1->pDatatype to a Unicode string wDocInfo1.pDatatype
|
|
cch = strlen(pDocInfo1->pDatatype);
|
|
|
|
wDocInfo1.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
|
|
if (!wDocInfo1.pDatatype)
|
|
{
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
ERR("HeapAlloc failed!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDatatype, -1, wDocInfo1.pDatatype, cch + 1);
|
|
}
|
|
|
|
if (pDocInfo1->pDocName)
|
|
{
|
|
// Convert pDocInfo1->pDocName to a Unicode string wDocInfo1.pDocName
|
|
cch = strlen(pDocInfo1->pDocName);
|
|
|
|
wDocInfo1.pDocName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
|
|
if (!wDocInfo1.pDocName)
|
|
{
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
ERR("HeapAlloc failed!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pDocName, -1, wDocInfo1.pDocName, cch + 1);
|
|
}
|
|
|
|
if (pDocInfo1->pOutputFile)
|
|
{
|
|
// Convert pDocInfo1->pOutputFile to a Unicode string wDocInfo1.pOutputFile
|
|
cch = strlen(pDocInfo1->pOutputFile);
|
|
|
|
wDocInfo1.pOutputFile = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
|
|
if (!wDocInfo1.pOutputFile)
|
|
{
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
ERR("HeapAlloc failed!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, pDocInfo1->pOutputFile, -1, wDocInfo1.pOutputFile, cch + 1);
|
|
}
|
|
|
|
dwReturnValue = StartDocPrinterW(hPrinter, Level, (PBYTE)&wDocInfo1);
|
|
dwErrorCode = GetLastError();
|
|
|
|
Cleanup:
|
|
if (wDocInfo1.pDatatype)
|
|
HeapFree(hProcessHeap, 0, wDocInfo1.pDatatype);
|
|
|
|
if (wDocInfo1.pDocName)
|
|
HeapFree(hProcessHeap, 0, wDocInfo1.pDocName);
|
|
|
|
if (wDocInfo1.pOutputFile)
|
|
HeapFree(hProcessHeap, 0, wDocInfo1.pOutputFile);
|
|
|
|
SetLastError(dwErrorCode);
|
|
return dwReturnValue;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
|
|
{
|
|
DWORD cbAddJobInfo1;
|
|
DWORD cbNeeded;
|
|
DWORD dwErrorCode;
|
|
DWORD dwReturnValue = 0;
|
|
PADDJOB_INFO_1W pAddJobInfo1 = NULL;
|
|
PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo;
|
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
|
|
|
TRACE("StartDocPrinterW(%p, %lu, %p)\n", hPrinter, Level, pDocInfo);
|
|
|
|
// Sanity checks.
|
|
if (!pHandle)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!pDocInfo1)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (Level != 1)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_LEVEL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pHandle->bStartedDoc)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_PRINTER_STATE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Check if we want to redirect output into a file.
|
|
if (pDocInfo1->pOutputFile)
|
|
{
|
|
// Do a StartDocPrinter RPC call in this case.
|
|
dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
|
|
}
|
|
else
|
|
{
|
|
// Allocate memory for the ADDJOB_INFO_1W structure and a path.
|
|
cbAddJobInfo1 = sizeof(ADDJOB_INFO_1W) + MAX_PATH * sizeof(WCHAR);
|
|
pAddJobInfo1 = HeapAlloc(hProcessHeap, 0, cbAddJobInfo1);
|
|
if (!pAddJobInfo1)
|
|
{
|
|
dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
|
|
ERR("HeapAlloc failed!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Try to add a new job.
|
|
// This only succeeds if the printer is set to do spooled printing.
|
|
if (AddJobW((HANDLE)pHandle, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
|
|
{
|
|
// Do spooled printing.
|
|
dwErrorCode = _StartDocPrinterSpooled(pHandle, pDocInfo1, pAddJobInfo1);
|
|
}
|
|
else if (GetLastError() == ERROR_INVALID_ACCESS)
|
|
{
|
|
// ERROR_INVALID_ACCESS is returned when the printer is set to do direct printing.
|
|
// In this case, we do a StartDocPrinter RPC call.
|
|
dwErrorCode = _StartDocPrinterWithRPC(pHandle, pDocInfo1);
|
|
}
|
|
else
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
ERR("AddJobW failed with error %lu!\n", dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (dwErrorCode == ERROR_SUCCESS)
|
|
{
|
|
pHandle->bStartedDoc = TRUE;
|
|
dwReturnValue = pHandle->dwJobID;
|
|
}
|
|
|
|
Cleanup:
|
|
if (pAddJobInfo1)
|
|
HeapFree(hProcessHeap, 0, pAddJobInfo1);
|
|
|
|
SetLastError(dwErrorCode);
|
|
return dwReturnValue;
|
|
}
|
|
|
|
BOOL WINAPI
|
|
StartPagePrinter(HANDLE hPrinter)
|
|
{
|
|
DWORD dwErrorCode;
|
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
|
|
|
TRACE("StartPagePrinter(%p)\n", hPrinter);
|
|
|
|
// Sanity checks.
|
|
if (!pHandle)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Do the RPC call
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcStartPagePrinter(pHandle->hPrinter);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcStartPagePrinter failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
|
|
Cleanup:
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
WritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten)
|
|
{
|
|
DWORD dwErrorCode;
|
|
PSPOOLER_HANDLE pHandle = (PSPOOLER_HANDLE)hPrinter;
|
|
|
|
TRACE("WritePrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pcWritten);
|
|
|
|
// Sanity checks.
|
|
if (!pHandle)
|
|
{
|
|
dwErrorCode = ERROR_INVALID_HANDLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!pHandle->bStartedDoc)
|
|
{
|
|
dwErrorCode = ERROR_SPL_NO_STARTDOC;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pHandle->hSPLFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
// Write to the spool file. This doesn't need an RPC request.
|
|
if (!WriteFile(pHandle->hSPLFile, pBuf, cbBuf, pcWritten, NULL))
|
|
{
|
|
dwErrorCode = GetLastError();
|
|
ERR("WriteFile failed with error %lu!\n", dwErrorCode);
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwErrorCode = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// TODO: This case (for direct printing or remote printing) has bad performance if multiple small-sized WritePrinter calls are performed.
|
|
// We may increase performance by writing into a buffer and only doing a single RPC call when the buffer is full.
|
|
|
|
// Do the RPC call
|
|
RpcTryExcept
|
|
{
|
|
dwErrorCode = _RpcWritePrinter(pHandle->hPrinter, pBuf, cbBuf, pcWritten);
|
|
}
|
|
RpcExcept(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
dwErrorCode = RpcExceptionCode();
|
|
ERR("_RpcWritePrinter failed with exception code %lu!\n", dwErrorCode);
|
|
}
|
|
RpcEndExcept;
|
|
}
|
|
|
|
Cleanup:
|
|
SetLastError(dwErrorCode);
|
|
return (dwErrorCode == ERROR_SUCCESS);
|
|
}
|
|
|
|
BOOL WINAPI
|
|
XcvDataW(HANDLE hXcv, PCWSTR pszDataName, PBYTE pInputData, DWORD cbInputData, PBYTE pOutputData, DWORD cbOutputData, PDWORD pcbOutputNeeded, PDWORD pdwStatus)
|
|
{
|
|
TRACE("XcvDataW(%p, %S, %p, %lu, %p, %lu, %p, %p)\n", hXcv, pszDataName, pInputData, cbInputData, pOutputData, cbOutputData, pcbOutputNeeded, pdwStatus);
|
|
return FALSE;
|
|
}
|