Author: cfinck Date: Sat Jun 27 12:36:45 2015 New Revision: 68288
URL: http://svn.reactos.org/svn/reactos?rev=68288&view=rev Log: [SPOOLSS, SPOOLSV, WINSPOOL] Connect AddJobW, EnumJobsW, GetJobW and SetJobW from winspool.drv over spoolsv.exe and spoolss.dll to the implemented functions in localspl.dll (including marshalling the involved structures down and up). I decided to not use my recently implemented MarshallDownStructure API here and rather do the pointer math in place as this is simpler and faster.
Modified: branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolss/jobs.c branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolss/spoolss.spec branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolsv/jobs.c branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolsv/rpcstubs.c branches/colins-printing-for-freedom/reactos/win32ss/printing/base/winspool/jobs.c branches/colins-printing-for-freedom/reactos/win32ss/printing/base/winspool/winspool.spec
Modified: branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolss/jobs.c URL: http://svn.reactos.org/svn/reactos/branches/colins-printing-for-freedom/reac... ============================================================================== --- branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolss/jobs.c [iso-8859-1] (original) +++ branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolss/jobs.c [iso-8859-1] Sat Jun 27 12:36:45 2015 @@ -8,13 +8,25 @@ #include "precomp.h"
BOOL WINAPI -AddJob(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded) +AddJobW(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded) { return LocalSplFuncs.fpAddJob(hPrinter, Level, pData, cbBuf, pcbNeeded); }
BOOL WINAPI -GetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded) +EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned) +{ + return LocalSplFuncs.fpEnumJobs(hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned); +} + +BOOL WINAPI +GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded) { return LocalSplFuncs.fpGetJob(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded); } + +BOOL WINAPI +SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command) +{ + return LocalSplFuncs.fpSetJob(hPrinter, JobId, Level, pJobInfo, Command); +}
Modified: branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolss/spoolss.spec URL: http://svn.reactos.org/svn/reactos/branches/colins-printing-for-freedom/reac... ============================================================================== --- branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolss/spoolss.spec [iso-8859-1] (original) +++ branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolss/spoolss.spec [iso-8859-1] Sat Jun 27 12:36:45 2015 @@ -59,7 +59,7 @@ @ stdcall EndDocPrinter(long) @ stdcall EndPagePrinter(long) @ stub EnumFormsW -@ stub EnumJobsW +@ stdcall EnumJobsW(long long long long ptr long ptr ptr) @ stub EnumMonitorsW @ stub EnumPerMachineConnectionsW @ stub EnumPortsW @@ -137,7 +137,7 @@ @ stub SendRecvBidiData @ stub SetAllocFailCount @ stub SetFormW -@ stub SetJobW +@ stdcall SetJobW(long long long ptr long) @ stub SetPortW @ stub SetPrinterDataExW @ stub SetPrinterDataW
Modified: branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolsv/jobs.c URL: http://svn.reactos.org/svn/reactos/branches/colins-printing-for-freedom/reac... ============================================================================== --- branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolsv/jobs.c [iso-8859-1] (original) +++ branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolsv/jobs.c [iso-8859-1] Sat Jun 27 12:36:45 2015 @@ -7,10 +7,53 @@
#include "precomp.h"
+static void +_MarshallDownJobInfo(PBYTE pJobInfo, DWORD Level) +{ + PJOB_INFO_1W pJobInfo1; + PJOB_INFO_2W pJobInfo2; + + // Replace absolute pointer addresses in the output by relative offsets. + if (Level == 1) + { + pJobInfo1 = (PJOB_INFO_1W)pJobInfo; + pJobInfo1->pDatatype = (PWSTR)((ULONG_PTR)pJobInfo1->pDatatype - (ULONG_PTR)pJobInfo1); + pJobInfo1->pDocument = (PWSTR)((ULONG_PTR)pJobInfo1->pDocument - (ULONG_PTR)pJobInfo1); + pJobInfo1->pMachineName = (PWSTR)((ULONG_PTR)pJobInfo1->pMachineName - (ULONG_PTR)pJobInfo1); + pJobInfo1->pPrinterName = (PWSTR)((ULONG_PTR)pJobInfo1->pPrinterName - (ULONG_PTR)pJobInfo1); + + if (pJobInfo1->pStatus) + pJobInfo1->pStatus = (PWSTR)((ULONG_PTR)pJobInfo1->pStatus - (ULONG_PTR)pJobInfo1); + + pJobInfo1->pUserName = (PWSTR)((ULONG_PTR)pJobInfo1->pUserName - (ULONG_PTR)pJobInfo1); + } + else if (Level == 2) + { + pJobInfo2 = (PJOB_INFO_2W)pJobInfo; + pJobInfo2->pDatatype = (PWSTR)((ULONG_PTR)pJobInfo2->pDatatype - (ULONG_PTR)pJobInfo2); + pJobInfo2->pDevMode = (PDEVMODEW)((ULONG_PTR)pJobInfo2->pDevMode - (ULONG_PTR)pJobInfo2); + pJobInfo2->pDocument = (PWSTR)((ULONG_PTR)pJobInfo2->pDocument - (ULONG_PTR)pJobInfo2); + pJobInfo2->pDriverName = (PWSTR)((ULONG_PTR)pJobInfo2->pDriverName - (ULONG_PTR)pJobInfo2); + pJobInfo2->pMachineName = (PWSTR)((ULONG_PTR)pJobInfo2->pMachineName - (ULONG_PTR)pJobInfo2); + pJobInfo2->pNotifyName = (PWSTR)((ULONG_PTR)pJobInfo2->pNotifyName - (ULONG_PTR)pJobInfo2); + pJobInfo2->pPrinterName = (PWSTR)((ULONG_PTR)pJobInfo2->pPrinterName - (ULONG_PTR)pJobInfo2); + pJobInfo2->pPrintProcessor = (PWSTR)((ULONG_PTR)pJobInfo2->pPrintProcessor - (ULONG_PTR)pJobInfo2); + + if (pJobInfo2->pParameters) + pJobInfo2->pParameters = (PWSTR)((ULONG_PTR)pJobInfo2->pParameters - (ULONG_PTR)pJobInfo2); + + if (pJobInfo2->pStatus) + pJobInfo2->pStatus = (PWSTR)((ULONG_PTR)pJobInfo2->pStatus - (ULONG_PTR)pJobInfo2); + + pJobInfo2->pUserName = (PWSTR)((ULONG_PTR)pJobInfo2->pUserName - (ULONG_PTR)pJobInfo2); + } +} + DWORD _RpcAddJob(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD Level, BYTE* pAddJob, DWORD cbBuf, DWORD* pcbNeeded) { DWORD dwErrorCode; + PADDJOB_INFO_1W pAddJobInfo1;
dwErrorCode = RpcImpersonateClient(NULL); if (dwErrorCode != ERROR_SUCCESS) @@ -21,6 +64,48 @@
AddJobW(hPrinter, Level, pAddJob, cbBuf, pcbNeeded); dwErrorCode = GetLastError(); + + if (dwErrorCode == ERROR_SUCCESS) + { + // Replace absolute pointer addresses in the output by relative offsets. + pAddJobInfo1 = (PADDJOB_INFO_1W)pAddJob; + pAddJobInfo1->Path = (PWSTR)((ULONG_PTR)pAddJobInfo1->Path - (ULONG_PTR)pAddJobInfo1); + } + + RpcRevertToSelf(); + return dwErrorCode; +} + +DWORD +_RpcEnumJobs(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, BYTE* pJob, DWORD cbBuf, DWORD* pcbNeeded, DWORD* pcReturned) +{ + DWORD dwErrorCode; + DWORD i; + PBYTE p = pJob; + + dwErrorCode = RpcImpersonateClient(NULL); + if (dwErrorCode != ERROR_SUCCESS) + { + ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode); + return dwErrorCode; + } + + EnumJobsW(hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned); + dwErrorCode = GetLastError(); + + if (dwErrorCode == ERROR_SUCCESS) + { + // Replace absolute pointer addresses in the output by relative offsets. + for (i = 0; i < *pcReturned; i++) + { + _MarshallDownJobInfo(p, Level); + + if (Level == 1) + p += sizeof(JOB_INFO_1W); + else if (Level == 2) + p += sizeof(JOB_INFO_2W); + } + }
RpcRevertToSelf(); return dwErrorCode; @@ -41,6 +126,12 @@ GetJobW(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded); dwErrorCode = GetLastError();
+ if (dwErrorCode == ERROR_SUCCESS) + { + // Replace absolute pointer addresses in the output by relative offsets. + _MarshallDownJobInfo(pJob, Level); + } + RpcRevertToSelf(); return dwErrorCode; } @@ -55,6 +146,19 @@ DWORD _RpcSetJob(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD JobId, WINSPOOL_JOB_CONTAINER* pJobContainer, DWORD Command) { - UNIMPLEMENTED; - return ERROR_INVALID_FUNCTION; + DWORD dwErrorCode; + + dwErrorCode = RpcImpersonateClient(NULL); + if (dwErrorCode != ERROR_SUCCESS) + { + ERR("RpcImpersonateClient failed with error %lu!\n", dwErrorCode); + return dwErrorCode; + } + + // pJobContainer->JobInfo is a union of pointers, so we can just convert any element to our BYTE pointer. + SetJobW(hPrinter, JobId, pJobContainer->Level, (PBYTE)pJobContainer->JobInfo.Level1, Command); + dwErrorCode = GetLastError(); + + RpcRevertToSelf(); + return dwErrorCode; }
Modified: branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolsv/rpcstubs.c URL: http://svn.reactos.org/svn/reactos/branches/colins-printing-for-freedom/reac... ============================================================================== --- branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolsv/rpcstubs.c [iso-8859-1] (original) +++ branches/colins-printing-for-freedom/reactos/win32ss/printing/base/spoolsv/rpcstubs.c [iso-8859-1] Sat Jun 27 12:36:45 2015 @@ -8,13 +8,6 @@ #include "precomp.h"
DWORD -_RpcEnumJobs(WINSPOOL_PRINTER_HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, BYTE *pJob, DWORD cbBuf, DWORD *pcbNeeded, DWORD *pcReturned) -{ - UNIMPLEMENTED; - return ERROR_INVALID_FUNCTION; -} - -DWORD _RpcAddPrinter(WINSPOOL_HANDLE pName, WINSPOOL_PRINTER_CONTAINER *pPrinterContainer, WINSPOOL_DEVMODE_CONTAINER *pDevModeContainer, WINSPOOL_SECURITY_CONTAINER *pSecurityContainer, WINSPOOL_PRINTER_HANDLE *pHandle) { UNIMPLEMENTED;
Modified: branches/colins-printing-for-freedom/reactos/win32ss/printing/base/winspool/jobs.c URL: http://svn.reactos.org/svn/reactos/branches/colins-printing-for-freedom/reac... ============================================================================== --- branches/colins-printing-for-freedom/reactos/win32ss/printing/base/winspool/jobs.c [iso-8859-1] (original) +++ branches/colins-printing-for-freedom/reactos/win32ss/printing/base/winspool/jobs.c [iso-8859-1] Sat Jun 27 12:36:45 2015 @@ -7,46 +7,191 @@
#include "precomp.h"
+static void +_MarshallUpJobInfo(PBYTE pJobInfo, DWORD Level) +{ + PJOB_INFO_1W pJobInfo1; + PJOB_INFO_2W pJobInfo2; + + // Replace absolute pointer addresses in the output by relative offsets. + if (Level == 1) + { + pJobInfo1 = (PJOB_INFO_1W)pJobInfo; + pJobInfo1->pDatatype = (PWSTR)((ULONG_PTR)pJobInfo1->pDatatype + (ULONG_PTR)pJobInfo1); + pJobInfo1->pDocument = (PWSTR)((ULONG_PTR)pJobInfo1->pDocument + (ULONG_PTR)pJobInfo1); + pJobInfo1->pMachineName = (PWSTR)((ULONG_PTR)pJobInfo1->pMachineName + (ULONG_PTR)pJobInfo1); + pJobInfo1->pPrinterName = (PWSTR)((ULONG_PTR)pJobInfo1->pPrinterName + (ULONG_PTR)pJobInfo1); + + if (pJobInfo1->pStatus) + pJobInfo1->pStatus = (PWSTR)((ULONG_PTR)pJobInfo1->pStatus + (ULONG_PTR)pJobInfo1); + + pJobInfo1->pUserName = (PWSTR)((ULONG_PTR)pJobInfo1->pUserName + (ULONG_PTR)pJobInfo1); + } + else if (Level == 2) + { + pJobInfo2 = (PJOB_INFO_2W)pJobInfo; + pJobInfo2->pDatatype = (PWSTR)((ULONG_PTR)pJobInfo2->pDatatype + (ULONG_PTR)pJobInfo2); + pJobInfo2->pDevMode = (PDEVMODEW)((ULONG_PTR)pJobInfo2->pDevMode + (ULONG_PTR)pJobInfo2); + pJobInfo2->pDocument = (PWSTR)((ULONG_PTR)pJobInfo2->pDocument + (ULONG_PTR)pJobInfo2); + pJobInfo2->pDriverName = (PWSTR)((ULONG_PTR)pJobInfo2->pDriverName + (ULONG_PTR)pJobInfo2); + pJobInfo2->pMachineName = (PWSTR)((ULONG_PTR)pJobInfo2->pMachineName + (ULONG_PTR)pJobInfo2); + pJobInfo2->pNotifyName = (PWSTR)((ULONG_PTR)pJobInfo2->pNotifyName + (ULONG_PTR)pJobInfo2); + pJobInfo2->pPrinterName = (PWSTR)((ULONG_PTR)pJobInfo2->pPrinterName + (ULONG_PTR)pJobInfo2); + pJobInfo2->pPrintProcessor = (PWSTR)((ULONG_PTR)pJobInfo2->pPrintProcessor + (ULONG_PTR)pJobInfo2); + + if (pJobInfo2->pParameters) + pJobInfo2->pParameters = (PWSTR)((ULONG_PTR)pJobInfo2->pParameters + (ULONG_PTR)pJobInfo2); + + if (pJobInfo2->pStatus) + pJobInfo2->pStatus = (PWSTR)((ULONG_PTR)pJobInfo2->pStatus + (ULONG_PTR)pJobInfo2); + + pJobInfo2->pUserName = (PWSTR)((ULONG_PTR)pJobInfo2->pUserName + (ULONG_PTR)pJobInfo2); + } +} + BOOL WINAPI -AddJob(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded) +AddJobA(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded) { - BOOL bReturnValue = FALSE; + UNIMPLEMENTED; + return FALSE; +} + +BOOL WINAPI +AddJobW(HANDLE hPrinter, DWORD Level, PBYTE pData, DWORD cbBuf, PDWORD pcbNeeded) +{ DWORD dwErrorCode; + PADDJOB_INFO_1W pAddJobInfo1;
// Do the RPC call RpcTryExcept { dwErrorCode = _RpcAddJob(hPrinter, Level, pData, cbBuf, pcbNeeded); - SetLastError(dwErrorCode); - bReturnValue = (dwErrorCode == ERROR_SUCCESS); } RpcExcept(EXCEPTION_EXECUTE_HANDLER) { - ERR("_RpcAddJob failed with exception code %lu!\n", RpcExceptionCode()); + dwErrorCode = RpcExceptionCode(); + ERR("_RpcAddJob failed with exception code %lu!\n", dwErrorCode); } RpcEndExcept;
- return bReturnValue; + if (dwErrorCode == ERROR_SUCCESS) + { + // Replace relative offset addresses in the output by absolute pointers. + pAddJobInfo1 = (PADDJOB_INFO_1W)pData; + pAddJobInfo1->Path = (PWSTR)((ULONG_PTR)pAddJobInfo1->Path + (ULONG_PTR)pAddJobInfo1); + } + + SetLastError(dwErrorCode); + return (dwErrorCode == ERROR_SUCCESS); }
BOOL WINAPI -GetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pJob, DWORD cbBuf, LPDWORD pcbNeeded) +EnumJobsA(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned) { - BOOL bReturnValue = FALSE; + UNIMPLEMENTED; + return FALSE; +} + +BOOL WINAPI +EnumJobsW(HANDLE hPrinter, DWORD FirstJob, DWORD NoJobs, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned) +{ + DWORD dwErrorCode; + DWORD i; + PBYTE p = pJob; + + // Do the RPC call + RpcTryExcept + { + dwErrorCode = _RpcEnumJobs(hPrinter, FirstJob, NoJobs, Level, pJob, cbBuf, pcbNeeded, pcReturned); + } + RpcExcept(EXCEPTION_EXECUTE_HANDLER) + { + dwErrorCode = RpcExceptionCode(); + ERR("_RpcEnumJobs failed with exception code %lu!\n", dwErrorCode); + } + RpcEndExcept; + + if (dwErrorCode == ERROR_SUCCESS) + { + // Replace relative offset addresses in the output by absolute pointers. + for (i = 0; i < *pcReturned; i++) + { + _MarshallUpJobInfo(p, Level); + + if (Level == 1) + p += sizeof(JOB_INFO_1W); + else if (Level == 2) + p += sizeof(JOB_INFO_2W); + } + } + + SetLastError(dwErrorCode); + return (dwErrorCode == ERROR_SUCCESS); +} + +BOOL WINAPI +GetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded) +{ + UNIMPLEMENTED; + return FALSE; +} + +BOOL WINAPI +GetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJob, DWORD cbBuf, PDWORD pcbNeeded) +{ DWORD dwErrorCode;
// Do the RPC call RpcTryExcept { dwErrorCode = _RpcGetJob(hPrinter, JobId, Level, pJob, cbBuf, pcbNeeded); - SetLastError(dwErrorCode); - bReturnValue = (dwErrorCode == ERROR_SUCCESS); } RpcExcept(EXCEPTION_EXECUTE_HANDLER) { - ERR("_RpcGetJob failed with exception code %lu!\n", RpcExceptionCode()); + dwErrorCode = RpcExceptionCode(); + ERR("_RpcGetJob failed with exception code %lu!\n", dwErrorCode); } RpcEndExcept;
- return bReturnValue; + if (dwErrorCode == ERROR_SUCCESS) + { + // Replace relative offset addresses in the output by absolute pointers. + _MarshallUpJobInfo(pJob, Level); + } + + SetLastError(dwErrorCode); + return (dwErrorCode == ERROR_SUCCESS); } + +BOOL WINAPI +SetJobA(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command) +{ + UNIMPLEMENTED; + return FALSE; +} + +BOOL WINAPI +SetJobW(HANDLE hPrinter, DWORD JobId, DWORD Level, PBYTE pJobInfo, DWORD Command) +{ + DWORD dwErrorCode; + WINSPOOL_JOB_CONTAINER JobContainer; + + // pJobContainer->JobInfo is a union of pointers, so we can just set any element to our BYTE pointer. + JobContainer.Level = Level; + JobContainer.JobInfo.Level1 = (WINSPOOL_JOB_INFO_1*)pJobInfo; + + // Do the RPC call + RpcTryExcept + { + dwErrorCode = _RpcSetJob(hPrinter, JobId, &JobContainer, Command); + } + RpcExcept(EXCEPTION_EXECUTE_HANDLER) + { + dwErrorCode = RpcExceptionCode(); + ERR("_RpcSetJob failed with exception code %lu!\n", dwErrorCode); + } + RpcEndExcept; + + SetLastError(dwErrorCode); + return (dwErrorCode == ERROR_SUCCESS); +}
Modified: branches/colins-printing-for-freedom/reactos/win32ss/printing/base/winspool/winspool.spec URL: http://svn.reactos.org/svn/reactos/branches/colins-printing-for-freedom/reac... ============================================================================== --- branches/colins-printing-for-freedom/reactos/win32ss/printing/base/winspool/winspool.spec [iso-8859-1] (original) +++ branches/colins-printing-for-freedom/reactos/win32ss/printing/base/winspool/winspool.spec [iso-8859-1] Sat Jun 27 12:36:45 2015 @@ -9,7 +9,7 @@ 108 stub AbortPrinter 109 stub AddFormA 110 stub AddFormW -111 stub AddJobA +111 stdcall AddJobA(long long ptr long ptr) 112 stdcall AddJobW(long long ptr long ptr) 113 stub AddMonitorA 114 stub AddMonitorW @@ -83,8 +83,8 @@ 182 stdcall EndPagePrinter(long) 183 stub EnumFormsA 184 stub EnumFormsW -185 stub EnumJobsA -186 stub EnumJobsW +185 stdcall EnumJobsA(long long long long ptr long ptr ptr) +186 stdcall EnumJobsW(long long long long ptr long ptr ptr) 187 stub EnumMonitorsA 188 stub EnumMonitorsW 189 stub EnumPortsA @@ -142,7 +142,7 @@ 241 stub FreePrinterNotifyInfo 242 stub GetFormA 243 stub GetFormW -244 stub GetJobA +244 stdcall GetJobA(long long long ptr long ptr) 245 stdcall GetJobW(long long long ptr long ptr) 246 stub GetPrintProcessorDirectoryA 247 stdcall GetPrintProcessorDirectoryW(wstr wstr long ptr long ptr) @@ -176,8 +176,8 @@ 275 stub SetAllocFailCount 276 stub SetFormA 277 stub SetFormW -278 stub SetJobA -279 stub SetJobW +278 stdcall SetJobA(long long long ptr long) +279 stdcall SetJobW(long long long ptr long) 280 stub SetPortA 281 stub SetPortW 282 stub SetPrinterA