[PRINTING]
[reactos.git] / reactos / win32ss / printing / base / winspool / printers.c
index c447778..53b113f 100644 (file)
@@ -2,29 +2,46 @@
  * PROJECT:     ReactOS Spooler API
  * LICENSE:     GNU LGPL v2.1 or any later version as published by the Free Software Foundation
  * PURPOSE:     Functions related to Printers and printing
- * COPYRIGHT:   Copyright 2015 Colin Finck <colin@reactos.org>
+ * COPYRIGHT:   Copyright 2015-2017 Colin Finck <colin@reactos.org>
  */
 
 #include "precomp.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 void
-_MarshallUpPrinterInfo(PBYTE pPrinterInfo, DWORD Level)
+_MarshallUpPrinterInfo(PBYTE* ppPrinterInfo, DWORD Level)
 {
-    PPRINTER_INFO_1W pPrinterInfo1;
-    PPRINTER_INFO_2W pPrinterInfo2;
+    // Replace relative offset addresses in the output by absolute pointers and advance to the next structure.
+    if (Level == 0)
+    {
+        PPRINTER_INFO_STRESS pPrinterInfo0 = (PPRINTER_INFO_STRESS)(*ppPrinterInfo);
+
+        pPrinterInfo0->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo0->pPrinterName + (ULONG_PTR)pPrinterInfo0);
+
+        if (pPrinterInfo0->pServerName)
+            pPrinterInfo0->pServerName = (PWSTR)((ULONG_PTR)pPrinterInfo0->pServerName + (ULONG_PTR)pPrinterInfo0);
 
-    // Replace relative offset addresses in the output by absolute pointers.
-    if (Level == 1)
+        *ppPrinterInfo += sizeof(PRINTER_INFO_STRESS);
+    }
+    else if (Level == 1)
     {
-        pPrinterInfo1 = (PPRINTER_INFO_1W)pPrinterInfo;
+        PPRINTER_INFO_1W pPrinterInfo1 = (PPRINTER_INFO_1W)(*ppPrinterInfo);
 
         pPrinterInfo1->pName = (PWSTR)((ULONG_PTR)pPrinterInfo1->pName + (ULONG_PTR)pPrinterInfo1);
         pPrinterInfo1->pDescription = (PWSTR)((ULONG_PTR)pPrinterInfo1->pDescription + (ULONG_PTR)pPrinterInfo1);
         pPrinterInfo1->pComment = (PWSTR)((ULONG_PTR)pPrinterInfo1->pComment + (ULONG_PTR)pPrinterInfo1);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_1W);
     }
     else if (Level == 2)
     {
-        pPrinterInfo2 = (PPRINTER_INFO_2W)pPrinterInfo;
+        PPRINTER_INFO_2W pPrinterInfo2 = (PPRINTER_INFO_2W)(*ppPrinterInfo);
 
         pPrinterInfo2->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pPrinterName + (ULONG_PTR)pPrinterInfo2);
         pPrinterInfo2->pShareName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pShareName + (ULONG_PTR)pPrinterInfo2);
@@ -42,7 +59,66 @@ _MarshallUpPrinterInfo(PBYTE pPrinterInfo, DWORD Level)
             pPrinterInfo2->pServerName = (PWSTR)((ULONG_PTR)pPrinterInfo2->pServerName + (ULONG_PTR)pPrinterInfo2);
 
         if (pPrinterInfo2->pSecurityDescriptor)
-            pPrinterInfo2->pSecurityDescriptor = (PWSTR)((ULONG_PTR)pPrinterInfo2->pSecurityDescriptor + (ULONG_PTR)pPrinterInfo2);
+            pPrinterInfo2->pSecurityDescriptor = (PSECURITY_DESCRIPTOR)((ULONG_PTR)pPrinterInfo2->pSecurityDescriptor + (ULONG_PTR)pPrinterInfo2);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_2W);
+    }
+    else if (Level == 3)
+    {
+        PPRINTER_INFO_3 pPrinterInfo3 = (PPRINTER_INFO_3)(*ppPrinterInfo);
+
+        pPrinterInfo3->pSecurityDescriptor = (PSECURITY_DESCRIPTOR)((ULONG_PTR)pPrinterInfo3->pSecurityDescriptor + (ULONG_PTR)pPrinterInfo3);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_3);
+    }
+    else if (Level == 4)
+    {
+        PPRINTER_INFO_4W pPrinterInfo4 = (PPRINTER_INFO_4W)(*ppPrinterInfo);
+
+        pPrinterInfo4->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo4->pPrinterName + (ULONG_PTR)pPrinterInfo4);
+
+        if (pPrinterInfo4->pServerName)
+            pPrinterInfo4->pServerName = (PWSTR)((ULONG_PTR)pPrinterInfo4->pServerName + (ULONG_PTR)pPrinterInfo4);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_4W);
+    }
+    else if (Level == 5)
+    {
+        PPRINTER_INFO_5W pPrinterInfo5 = (PPRINTER_INFO_5W)(*ppPrinterInfo);
+
+        pPrinterInfo5->pPrinterName = (PWSTR)((ULONG_PTR)pPrinterInfo5->pPrinterName + (ULONG_PTR)pPrinterInfo5);
+        pPrinterInfo5->pPortName = (PWSTR)((ULONG_PTR)pPrinterInfo5->pPortName + (ULONG_PTR)pPrinterInfo5);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_5W);
+    }
+    else if (Level == 6)
+    {
+        *ppPrinterInfo += sizeof(PRINTER_INFO_6);
+    }
+    else if (Level == 7)
+    {
+        PPRINTER_INFO_7W pPrinterInfo7 = (PPRINTER_INFO_7W)(*ppPrinterInfo);
+
+        if (pPrinterInfo7->pszObjectGUID)
+            pPrinterInfo7->pszObjectGUID = (PWSTR)((ULONG_PTR)pPrinterInfo7->pszObjectGUID + (ULONG_PTR)pPrinterInfo7);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_7W);
+    }
+    else if (Level == 8)
+    {
+        PPRINTER_INFO_8W pPrinterInfo8 = (PPRINTER_INFO_8W)(*ppPrinterInfo);
+
+        pPrinterInfo8->pDevMode = (PDEVMODEW)((ULONG_PTR)pPrinterInfo8->pDevMode + (ULONG_PTR)pPrinterInfo8);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_8W);
+    }
+    else if (Level == 9)
+    {
+        PPRINTER_INFO_9W pPrinterInfo9 = (PPRINTER_INFO_9W)(*ppPrinterInfo);
+
+        pPrinterInfo9->pDevMode = (PDEVMODEW)((ULONG_PTR)pPrinterInfo9->pDevMode + (ULONG_PTR)pPrinterInfo9);
+
+        *ppPrinterInfo += sizeof(PRINTER_INFO_9W);
     }
 }
 
@@ -63,7 +139,7 @@ _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB
     }
 
     // Get the size of the job information.
-    GetJobW(pHandle->hPrinter, pAddJobInfo1->JobId, 1, NULL, 0, &cbNeeded);
+    GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, NULL, 0, &cbNeeded);
     if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
     {
         dwErrorCode = GetLastError();
@@ -81,7 +157,7 @@ _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB
     }
 
     // Get the job information.
-    if (!GetJobW(pHandle->hPrinter, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, cbNeeded, &cbNeeded))
+    if (!GetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, cbNeeded, &cbNeeded))
     {
         dwErrorCode = GetLastError();
         ERR("GetJobW failed with error %lu!\n", dwErrorCode);
@@ -89,11 +165,13 @@ _StartDocPrinterSpooled(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1, PADDJOB
     }
 
     // Add our document information.
-    pJobInfo1->pDatatype = pDocInfo1->pDatatype;
+    if (pDocInfo1->pDatatype)
+        pJobInfo1->pDatatype = pDocInfo1->pDatatype;
+
     pJobInfo1->pDocument = pDocInfo1->pDocName;
 
     // Set the new job information.
-    if (!SetJobW(pHandle->hPrinter, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, 0))
+    if (!SetJobW((HANDLE)pHandle, pAddJobInfo1->JobId, 1, (PBYTE)pJobInfo1, 0))
     {
         dwErrorCode = GetLastError();
         ERR("SetJobW failed with error %lu!\n", dwErrorCode);
@@ -134,6 +212,13 @@ _StartDocPrinterWithRPC(PSPOOLER_HANDLE pHandle, PDOC_INFO_1W pDocInfo1)
     return dwErrorCode;
 }
 
+HANDLE WINAPI
+AddPrinterW(PWSTR pName, DWORD Level, PBYTE pPrinter)
+{
+    UNIMPLEMENTED;
+    return NULL;
+}
+
 BOOL WINAPI
 ClosePrinter(HANDLE hPrinter)
 {
@@ -297,8 +382,16 @@ BOOL WINAPI
 EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
 {
     DWORD dwErrorCode;
-    DWORD i;
-    PBYTE p = pPrinterEnum;
+
+    // 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
@@ -314,18 +407,14 @@ EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cb
 
     if (dwErrorCode == ERROR_SUCCESS)
     {
-        // Replace relative offset addresses in the output by absolute pointers.
-        for (i = 0; i < *pcReturned; i++)
-        {
-            _MarshallUpPrinterInfo(p, Level);
+        DWORD i;
+        PBYTE p = pPrinterEnum;
 
-            if (Level == 1)
-                p += sizeof(PRINTER_INFO_1W);
-            else if (Level == 2)
-                p += sizeof(PRINTER_INFO_2W);
-        }
+        for (i = 0; i < *pcReturned; i++)
+            _MarshallUpPrinterInfo(&p, Level);
     }
 
+Cleanup:
     SetLastError(dwErrorCode);
     return (dwErrorCode == ERROR_SUCCESS);
 }
@@ -333,13 +422,131 @@ EnumPrintersW(DWORD Flags, PWSTR Name, DWORD Level, PBYTE pPrinterEnum, DWORD cb
 BOOL WINAPI
 GetDefaultPrinterA(LPSTR pszBuffer, LPDWORD pcchBuffer)
 {
-    return FALSE;
+    DWORD dwErrorCode;
+    PWSTR pwszBuffer = NULL;
+
+    // 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 = GetLastError();
+            ERR("HeapAlloc failed with error %lu!\n", dwErrorCode);
+            goto Cleanup;
+        }
+    }
+
+    if (!GetDefaultPrinterW(pwszBuffer, pcchBuffer))
+    {
+        dwErrorCode = GetLastError();
+        goto Cleanup;
+    }
+
+    dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+    if (pwszBuffer)
+        HeapFree(hProcessHeap, 0, pwszBuffer);
+
+    SetLastError(dwErrorCode);
+    return (dwErrorCode == ERROR_SUCCESS);
 }
 
 BOOL WINAPI
 GetDefaultPrinterW(LPWSTR pszBuffer, LPDWORD pcchBuffer)
 {
-    return FALSE;
+    DWORD cbNeeded;
+    DWORD cchInputBuffer;
+    DWORD dwErrorCode;
+    HKEY hWindowsKey = NULL;
+    PWSTR pwszDevice = NULL;
+    PWSTR pwszComma;
+
+    // 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 = GetLastError();
+        ERR("HeapAlloc failed with error %lu!\n", dwErrorCode);
+        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
@@ -363,31 +570,62 @@ GetPrinterDriverW(HANDLE hPrinter, LPWSTR pEnvironment, DWORD Level, LPBYTE pDri
 BOOL WINAPI
 GetPrinterW(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
 {
-    return FALSE;
+    DWORD dwErrorCode;
+
+    // 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(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)
+    {
+        PBYTE p = pPrinter;
+        _MarshallUpPrinterInfo(&p, Level);
+    }
+
+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;
-    PWSTR pwszDatatype = NULL;
     PRINTER_DEFAULTSW wDefault = { 0 };
-    size_t StringLength;
 
     if (pPrinterName)
     {
         // Convert pPrinterName to a Unicode string pwszPrinterName
-        StringLength = strlen(pPrinterName) + 1;
+        cch = strlen(pPrinterName);
 
-        pwszPrinterName = HeapAlloc(hProcessHeap, 0, StringLength * sizeof(WCHAR));
+        pwszPrinterName = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
         if (!pwszPrinterName)
         {
             ERR("HeapAlloc failed for pwszPrinterName with last error %lu!\n", GetLastError());
             goto Cleanup;
         }
 
-        MultiByteToWideChar(CP_ACP, 0, pPrinterName, -1, pwszPrinterName, StringLength);
+        MultiByteToWideChar(CP_ACP, 0, pPrinterName, -1, pwszPrinterName, cch + 1);
     }
 
     if (pDefault)
@@ -396,18 +634,17 @@ OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefaul
 
         if (pDefault->pDatatype)
         {
-            // Convert pDefault->pDatatype to a Unicode string pwszDatatype that later becomes wDefault.pDatatype
-            StringLength = strlen(pDefault->pDatatype) + 1;
+            // Convert pDefault->pDatatype to a Unicode string wDefault.pDatatype
+            cch = strlen(pDefault->pDatatype);
 
-            pwszDatatype = HeapAlloc(hProcessHeap, 0, StringLength * sizeof(WCHAR));
-            if (!pwszDatatype)
+            wDefault.pDatatype = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+            if (!wDefault.pDatatype)
             {
-                ERR("HeapAlloc failed for pwszDatatype with last error %lu!\n", GetLastError());
+                ERR("HeapAlloc failed for wDefault.pDatatype with last error %lu!\n", GetLastError());
                 goto Cleanup;
             }
 
-            MultiByteToWideChar(CP_ACP, 0, pDefault->pDatatype, -1, pwszDatatype, StringLength);
-            wDefault.pDatatype = pwszDatatype;
+            MultiByteToWideChar(CP_ACP, 0, pDefault->pDatatype, -1, wDefault.pDatatype, cch + 1);
         }
 
         if (pDefault->pDevMode)
@@ -417,15 +654,15 @@ OpenPrinterA(LPSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSA pDefaul
     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);
 
-    if (pwszDatatype)
-        HeapFree(hProcessHeap, 0, pwszDatatype);
-
     return bReturnValue;
 }
 
@@ -436,8 +673,7 @@ OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefau
     HANDLE hPrinter;
     PSPOOLER_HANDLE pHandle;
     PWSTR pDatatype = NULL;
-    WINSPOOL_DEVMODE_CONTAINER DevModeContainer;
-    WINSPOOL_DEVMODE_CONTAINER* pDevModeContainer = NULL;
+    WINSPOOL_DEVMODE_CONTAINER DevModeContainer = { 0 };
     ACCESS_MASK AccessRequired = 0;
 
     // Prepare the additional parameters in the format required by _RpcOpenPrinter
@@ -446,14 +682,13 @@ OpenPrinterW(LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefau
         pDatatype = pDefault->pDatatype;
         DevModeContainer.cbBuf = sizeof(DEVMODEW);
         DevModeContainer.pDevMode = (BYTE*)pDefault->pDevMode;
-        pDevModeContainer = &DevModeContainer;
         AccessRequired = pDefault->DesiredAccess;
     }
 
     // Do the RPC call
     RpcTryExcept
     {
-        dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, pDevModeContainer, AccessRequired);
+        dwErrorCode = _RpcOpenPrinter(pPrinterName, &hPrinter, pDatatype, &DevModeContainer, AccessRequired);
     }
     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
     {
@@ -515,12 +750,253 @@ Cleanup:
     return (dwErrorCode == ERROR_SUCCESS);
 }
 
+BOOL WINAPI
+ResetPrinterW(HANDLE hPrinter, PPRINTER_DEFAULTSW pDefault)
+{
+    UNIMPLEMENTED;
+    return FALSE;
+}
+
+BOOL WINAPI
+SetDefaultPrinterA(LPCSTR pszPrinter)
+{
+    BOOL bReturnValue = FALSE;
+    DWORD cch;
+    PWSTR pwszPrinter = NULL;
+
+    if (pszPrinter)
+    {
+        // Convert pszPrinter to a Unicode string pwszPrinter
+        cch = strlen(pszPrinter);
+
+        pwszPrinter = HeapAlloc(hProcessHeap, 0, (cch + 1) * sizeof(WCHAR));
+        if (!pwszPrinter)
+        {
+            ERR("HeapAlloc failed for pwszPrinter with last error %lu!\n", GetLastError());
+            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];
+
+    // 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)
+        {
+            // The caller gave us an invalid printer name, return with ERROR_FILE_NOT_FOUND.
+            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 = GetLastError();
+        ERR("HeapAlloc failed with error %lu\n", dwErrorCode);
+        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
+SetPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pPrinter, DWORD 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;
+
+    // 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;
@@ -570,7 +1046,7 @@ StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
 
         // Try to add a new job.
         // This only succeeds if the printer is set to do spooled printing.
-        if (AddJobW(pHandle->hPrinter, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
+        if (AddJobW((HANDLE)pHandle, 1, (PBYTE)pAddJobInfo1, cbAddJobInfo1, &cbNeeded))
         {
             // Do spooled printing.
             dwErrorCode = _StartDocPrinterSpooled(pHandle, pDocInfo1, pAddJobInfo1);
@@ -590,14 +1066,17 @@ StartDocPrinterW(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo)
     }
 
     if (dwErrorCode == ERROR_SUCCESS)
+    {
         pHandle->bStartedDoc = TRUE;
+        dwReturnValue = pHandle->dwJobID;
+    }
 
 Cleanup:
     if (pAddJobInfo1)
         HeapFree(hProcessHeap, 0, pAddJobInfo1);
 
     SetLastError(dwErrorCode);
-    return pHandle->dwJobID;
+    return dwReturnValue;
 }
 
 BOOL WINAPI