/*++ * * WOW v1.0 * * Copyright (c) 1991, Microsoft Corporation * * WSPOOL.C * WOW32 printer spooler support routines * * These routines help a Win 3.0 task to use the print spooler apis. These * apis were exposed by DDK in Win 3.1. * * History: * Created 1-July-1993 by Chandan Chauhan (ChandanC) * --*/ #include "precomp.h" #pragma hdrstop #include extern WORD gUser16hInstance; VOID WOWSpoolerThread(WOWSPOOL *lpwowSpool); WORD gprn16 = 0x100; // Global spooler job # (can be anything > 0) MODNAME(wspool.c); LPDEVMODE GetDefaultDevMode32(LPSTR szDriver) { LONG cbDevMode; LPDEVMODE lpDevMode = NULL; if (szDriver != NULL) { if (!(spoolerapis[WOW_EXTDEVICEMODE].lpfn)) { if (!LoadLibraryAndGetProcAddresses(L"WINSPOOL.DRV", spoolerapis, WOW_SPOOLERAPI_COUNT)) { goto LeaveGetDefaultDevMode32; } } if ((cbDevMode = (*spoolerapis[WOW_EXTDEVICEMODE].lpfn)(NULL, NULL, NULL, szDriver, NULL, NULL, NULL, 0)) > 0) { if ((lpDevMode = (LPDEVMODE) malloc_w(cbDevMode)) != NULL) { if ((*spoolerapis[WOW_EXTDEVICEMODE].lpfn)(NULL, NULL, lpDevMode, szDriver, NULL, NULL, NULL, DM_COPY) != IDOK) { free_w(lpDevMode); lpDevMode = NULL; } } } LeaveGetDefaultDevMode32: if (!lpDevMode) { LOGDEBUG(0,("WOW::GetDefaultDevMode32: Unable to get default DevMode\n")); } } return(lpDevMode); } ULONG FASTCALL WG32OpenJob (PVDMFRAME pFrame) { INT len; PSZ psz1 = NULL; PSZ psz2 = NULL; PSZ pszDriver = NULL; ULONG ul=0; DOC_INFO_1 DocInfo1; HANDLE hnd; register POPENJOB16 parg16; PRINTER_DEFAULTS PrinterDefault; PPRINTER_DEFAULTS pPrinterDefault = NULL; GETARGPTR(pFrame, sizeof(OPENJOB16), parg16); // save off the 16-bit params now since this could callback into a 16-bit // fax driver & cause 16-bit memory to move. if(parg16->f1) { if(psz1 = malloc_w_strcpy_vp16to32(parg16->f1, FALSE, 0)) { len = strlen(psz1)+1; len = max(len, 40); pszDriver = malloc_w(len); } } if(parg16->f2) { psz2 = malloc_w_strcpy_vp16to32(parg16->f2, FALSE, 0); } FREEARGPTR(parg16); // all 16-bit pointers are now invalid!! // this implies that psz1 may also be bad if(!pszDriver) { goto exitpath; } if (!(spoolerapis[WOW_OpenPrinterA].lpfn)) { if (!LoadLibraryAndGetProcAddresses(L"WINSPOOL.DRV", spoolerapis, WOW_SPOOLERAPI_COUNT)) { goto exitpath; } } if (GetDriverName(psz1, pszDriver, len)) { if((PrinterDefault.pDevMode = GetDefaultDevMode32(pszDriver)) != NULL) { PrinterDefault.pDatatype = NULL; PrinterDefault.DesiredAccess = 0; pPrinterDefault = &PrinterDefault; if ((*spoolerapis[WOW_OpenPrinterA].lpfn) (pszDriver, &hnd, pPrinterDefault)) { DocInfo1.pDocName = psz2; DocInfo1.pOutputFile = psz1; DocInfo1.pDatatype = NULL; if (ul = (*spoolerapis[WOW_StartDocPrinterA].lpfn) (hnd, 1, (LPBYTE)&DocInfo1)) { ul = GetPrn16(hnd); } else { ul = GetLastError(); } } else { ul = GetLastError(); } } } LOGDEBUG(0,("WOW::WG32OpenJob: ul = %x\n", ul)); if (pPrinterDefault) { free_w(PrinterDefault.pDevMode); } exitpath: if(psz1) { free_w(psz1); } if(psz2) { free_w(psz2); } if(pszDriver) { free_w(pszDriver); } RETURN(ul); } ULONG FASTCALL WG32StartSpoolPage (PVDMFRAME pFrame) { ULONG ul=0; register PSTARTSPOOLPAGE16 parg16; GETARGPTR(pFrame, sizeof(STARTSPOOLPAGE16), parg16); if (!(ul = (*spoolerapis[WOW_StartPagePrinter].lpfn) (Prn32(parg16->f1)))) { ul = GetLastError(); } LOGDEBUG(0,("WOW::WG32StartSpoolPage: ul = %x\n", ul)); FREEARGPTR(parg16); RETURN(ul); } ULONG FASTCALL WG32EndSpoolPage (PVDMFRAME pFrame) { ULONG ul=0; register PENDSPOOLPAGE16 parg16; GETARGPTR(pFrame, sizeof(ENDSPOOLPAGE16), parg16); if (!(ul = (*spoolerapis[WOW_EndPagePrinter].lpfn) (Prn32(parg16->f1)))) { ul = GetLastError(); } LOGDEBUG(0,("WOW::WG32EndSpoolPage: ul = %x\n", ul)); FREEARGPTR(parg16); RETURN(ul); } ULONG FASTCALL WG32CloseJob (PVDMFRAME pFrame) { ULONG ul=0; register PCLOSEJOB16 parg16; GETARGPTR(pFrame, sizeof(CLOSEJOB16), parg16); if (!(ul = (*spoolerapis[WOW_EndDocPrinter].lpfn) (Prn32(parg16->f1)))) { ul = GetLastError(); } if (!(ul = (*spoolerapis[WOW_ClosePrinter].lpfn) (Prn32(parg16->f1)))) { ul = GetLastError(); } if (ul) { FreePrn(parg16->f1); } LOGDEBUG(0,("WOW::WG32CloseJob: ul = %x\n", ul)); FREEARGPTR(parg16); RETURN(ul); } ULONG FASTCALL WG32WriteSpool (PVDMFRAME pFrame) { DWORD dwWritten; ULONG ul=0; register PWRITESPOOL16 parg16; LPVOID pBuf; GETARGPTR(pFrame, sizeof(WRITESPOOL16), parg16); GETMISCPTR (parg16->f2, pBuf); if (ul = (*spoolerapis[WOW_WritePrinter].lpfn) (Prn32(parg16->f1), pBuf, FETCHWORD(parg16->f3), &dwWritten)) { ul = FETCHWORD(parg16->f3); } else { ul = GetLastError(); } LOGDEBUG(0,("WOW::WG32WriteSpool: ul = %x\n", ul)); FREEMISCPTR(pBuf); FREEARGPTR(parg16); RETURN(ul); } ULONG FASTCALL WG32DeleteJob (PVDMFRAME pFrame) { ULONG ul = 0; register PDELETEJOB16 parg16; GETARGPTR(pFrame, sizeof(DELETEJOB16), parg16); if (!(ul = (*spoolerapis[WOW_DeletePrinter].lpfn) (Prn32(parg16->f1)))) { ul = GetLastError(); } LOGDEBUG(0,("WOW::WG32DeleteJob: ul = %x\n", ul)); FREEARGPTR(parg16); RETURN(ul); } ULONG FASTCALL WG32SpoolFile (PVDMFRAME pFrame) { INT len; PSZ psz2 = NULL; PSZ psz3 = NULL; PSZ psz4 = NULL; PSZ pszDriver = NULL; LONG ul = -1; // SP_ERROR HANDLE hFile = NULL; HANDLE hPrinter = NULL; HANDLE hThread = NULL; WOWSPOOL *lpwowSpool = NULL; DOC_INFO_1 DocInfo1; DWORD dwUnused; register PSPOOLFILE16 parg16; GETARGPTR(pFrame, sizeof(SPOOLFILE16), parg16); // save off the 16-bit params now since this could callback into a 16-bit // fax driver & cause 16-bit memory to move. // ignore psz1 (printer name) // get the port name and the associated driver name if(parg16->f2) { if(!(psz2 = malloc_w_strcpy_vp16to32(parg16->f2, FALSE, 0))) { goto exitpath; } len = strlen(psz2)+1; len = max(len, 40); if(!(pszDriver = malloc_w(len))) { goto exitpath; } if(!GetDriverName(psz2, pszDriver, len)) { goto exitpath; } } // get the Job Title if(parg16->f3) { if(!(psz3 = malloc_w_strcpy_vp16to32(parg16->f3, FALSE, 0))) { goto exitpath; } } // get the file name if(parg16->f4) { if(!(psz4 = malloc_w_strcpy_vp16to32(parg16->f4, FALSE, 0))) { goto exitpath; } } FREEARGPTR(parg16); // all 16-bit pointers are now invalid!! // all fields of this struct are initially zero if(!(lpwowSpool = (WOWSPOOL *)malloc_w_zero(sizeof(WOWSPOOL)))) { goto exitpath; } if(!(spoolerapis[WOW_OpenPrinterA].lpfn)) { if(!LoadLibraryAndGetProcAddresses(L"WINSPOOL.DRV", spoolerapis, WOW_SPOOLERAPI_COUNT)) { goto exitpath; } } // open the specified file if((hFile = DPM_CreateFile(psz4, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL)) == INVALID_HANDLE_VALUE) { goto exitpath; } // create the WOWSpoolerThread to handle the "spooling" if(!(hThread = CreateThread(NULL, 16384, (LPTHREAD_START_ROUTINE)WOWSpoolerThread, lpwowSpool, CREATE_SUSPENDED, (LPDWORD)&dwUnused))) { goto exitpath; } // open the printer if((*spoolerapis[WOW_OpenPrinterA].lpfn)(pszDriver, &hPrinter, NULL)) { DocInfo1.pDocName = psz3; DocInfo1.pOutputFile = NULL; DocInfo1.pDatatype = "RAW"; // start a doc if(!(*spoolerapis[WOW_StartDocPrinterA].lpfn)(hPrinter, 1, (LPBYTE)&DocInfo1)) { goto ClosePrinter; } // start a page if((*spoolerapis[WOW_StartPagePrinter].lpfn)(hPrinter)) { // tell the WOWSpoolerThread that it's OK to do its thing lpwowSpool->fOK = TRUE; lpwowSpool->hFile = hFile; lpwowSpool->hPrinter = hPrinter; lpwowSpool->prn16 = gprn16; // tell the app that everything is hunky dory ul = (LONG)gprn16++; // make sure this doesn't go negative (-> an error ret to the app) if(gprn16 & 0x8000) { gprn16 = 0x100; } } // error path else { (*spoolerapis[WOW_EndDocPrinter].lpfn) (hPrinter); ClosePrinter: // note: hPrinter is freed by WOW_ClosePrinter (*spoolerapis[WOW_ClosePrinter].lpfn) (hPrinter); } } exitpath: LOGDEBUG(2,("WOW::WG32SpoolFile: ul = %x\n", ul)); if(psz2) { free_w(psz2); } if(psz3) { free_w(psz3); } if(psz4) { free_w(psz4); } if(pszDriver) { free_w(pszDriver); } // give the spooler thread a kick start then close the thread handle // (note: the thread will still be active) if(hThread) { ResumeThread(hThread); CloseHandle(hThread); } // clean up if there was an error -- otherwise the thread will clean up if(ul == -1) { if(hFile) { DPM_CloseHandle(hFile); } if(lpwowSpool) { free_w(lpwowSpool); } // note: hPrinter is freed by WOW_ClosePrinter } return((ULONG)ul); } #define SPOOL_BUFF_SIZE 4096 VOID WOWSpoolerThread(WOWSPOOL *lpwowSpool) { DWORD dwBytes; DWORD dwWritten; BYTE buf[SPOOL_BUFF_SIZE]; // this thread will only do something if fOK is TRUE if(lpwowSpool && lpwowSpool->fOK) { do { // this is a sequential read if(DPM_ReadFile(lpwowSpool->hFile, buf, SPOOL_BUFF_SIZE, &dwBytes, NULL)) { // if dwBytes==0 --> EOF if(dwBytes) { // if(!(*spoolerapis[WOW_WritePrinter].lpfn)(lpwowSpool->hPrinter, buf, dwBytes, &dwWritten)) { LOGDEBUG(0,("WOW::WOWSpoolerThread:WritePrinter ERROR!\n")); break; } else if(dwBytes != dwWritten) { LOGDEBUG(0,("WOW::WOWSpoolerThread:WritePrinter error!\n")); break; } } } } while (dwBytes == SPOOL_BUFF_SIZE); // shut down the print job (*spoolerapis[WOW_EndPagePrinter].lpfn) (lpwowSpool->hPrinter); (*spoolerapis[WOW_EndDocPrinter].lpfn) (lpwowSpool->hPrinter); // note: hPrinter is freed by WOW_ClosePrinter (*spoolerapis[WOW_ClosePrinter].lpfn) (lpwowSpool->hPrinter); // clean up if(lpwowSpool->hFile) { DPM_CloseHandle(lpwowSpool->hFile); } if(lpwowSpool) { free_w(lpwowSpool); } } // end if ExitThread(0); } WORD GetPrn16(HANDLE h32) { HANDLE hnd; VPVOID vp; LPBYTE lpMem16; hnd = LocalAlloc16(LMEM_MOVEABLE, sizeof(HANDLE), (HANDLE) gUser16hInstance); vp = LocalLock16(hnd); if (vp) { GETMISCPTR (vp, lpMem16); if (lpMem16) { *((PDWORD16)lpMem16) = (DWORD) h32; FREEMISCPTR(lpMem16); LocalUnlock16(hnd); } } else { LOGDEBUG (0, ("WOW::GETPRN16: Can't allocate a 16 bit handle\n")); } return (LOWORD(hnd)); } HANDLE Prn32(WORD h16) { VPVOID vp; HANDLE h32 = NULL; LPBYTE lpMem16; vp = LocalLock16 ((HANDLE) MAKELONG(h16, gUser16hInstance)); if (vp) { GETMISCPTR (vp, lpMem16); if (lpMem16) { h32 = (HANDLE) *((PDWORD16)lpMem16); FREEMISCPTR(lpMem16); } LocalUnlock16 ((HANDLE) MAKELONG(h16, gUser16hInstance)); } return (h32); } VOID FreePrn (WORD h16) { LocalFree16 ((HANDLE) MAKELONG(h16, gUser16hInstance)); } BOOL GetDriverName (char *psz, char *pszDriver, int cbDriver) { CHAR szAllDevices[1024]; CHAR *szNextDevice; CHAR szPrinter[64]; CHAR *szOutput; UINT len; if(!psz || (*psz == '\0')) { return FALSE; } len = strlen(psz); szAllDevices[0]='\0'; GetProfileString ("devices", NULL, "", szAllDevices, sizeof(szAllDevices)); szNextDevice = szAllDevices; LOGDEBUG(6,("WOW::GetDriverName: szAllDevices = %s\n", szAllDevices)); // strings from win.ini will be of the form "PS Printer=PSCRIPT,LPT1:" while (*szNextDevice) { szPrinter[0]='\0'; GetProfileString ("devices", szNextDevice, "", szPrinter, sizeof(szPrinter)); if (*szPrinter) { if (szOutput = WOW32_strchr (szPrinter, ',')) { szOutput++; while (*szOutput == ' ') { szOutput++; } if (!WOW32_stricmp(psz, szOutput)) { break; // found it! } // some apps pass "LPT1" without the ':' -- account for that // if the app passed "LPT1" and ... if (psz[len-1] != ':') { // ...strlen(szOutput) == 5 && szOutput[4] == ':' ... if((strlen(szOutput) == len+1) && (szOutput[len] == ':')) { // ...clobber the ':' char ... szOutput[len] = '\0'; // ...and see if the strings match now if (!WOW32_stricmp(psz, szOutput)) { break; // found it! } } } } } if (szNextDevice = WOW32_strchr (szNextDevice, '\0')) { szNextDevice++; } else { szNextDevice = ""; break; } } if (*szNextDevice) { LOGDEBUG(0,("WOW::GetDriverName: szNextDevice = %s\n", szNextDevice)); if (lstrcpyn (pszDriver, szNextDevice, cbDriver)) { pszDriver[cbDriver-1] = '\0'; return TRUE; } } // else they may have specified a network printer eg. "\\msprint44\corpk" // in which case we'll assume it's all right (since it will fail once the // WOW functions that call into this will fail when they call into the // driver with a bogus driver name) if(psz[0] == '\\' && psz[1] == '\\') { strncpy(pszDriver, psz, cbDriver); pszDriver[cbDriver-1] = '\0'; return TRUE; } return FALSE; }