+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;
+
+ // 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)
+ {
+ ERR("HeapAlloc failed for wDocInfo1.pDatatype with last error %lu!\n", GetLastError());
+ 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)
+ {
+ ERR("HeapAlloc failed for wDocInfo1.pDocName with last error %lu!\n", GetLastError());
+ 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)
+ {
+ ERR("HeapAlloc failed for wDocInfo1.pOutputFile with last error %lu!\n", GetLastError());
+ 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;
+
+ // 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 with error %lu!\n", GetLastError());
+ 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;
+
+ // 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;
+
+ // 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);
+}
+