Code:
' file: init_prtdlg.bas ' ---------------------------------------------------- ' Initializing the PrtDlg() for a named printer ' Includes demo of enumerating available printers. ' ---------------------------------------------------- ' Author: Michael Mattias Racine WI 9.21.04 ' COMPILER: Pb/Win 7.02 ' Use and distribution: Placed in public domain by author 9/21/04 ' --------------------------------------------------------------- #COMPILE EXE #REGISTER NONE #DEBUG ERROR ON #DIM ALL %I_AM_USING_DDOC_PRINT_AND_PREVIEW = 0 ' if true, uses ddoc fuctions, else does the printer enum '=====[Windows API Header Files] ============================ ' If you don't need all of the functionality supported by these APIs ' (and who does?), you can selectively turn off various modules by putting ' the following constants in your program BEFORE you #include "win32api.inc": ' ' %NOGDI = 1 ' no GDI (Graphics Device Interface) functions ' %NOMMIDS = 1 ' no Multimedia ID definitions %NOMMIDS = 1 #INCLUDE "WIN32API.INC" ' DATE: May 9 2002. GDI required for COMDLG32.INC ' PrtDlg() functions and UDTs: #INCLUDE "COMDLG32.INC" ' Program-level equates %PRINTER_NAME_SIZE = 128 %ID_CB = 101 ' combobox with list of installed printers %ID_CALL_PRTDLG = 102 ' button to call the dialog %ID_SEL_PRINTER = 103 ' label to show what user selected regardless of how box was initialized %NODEFAULT_ITEMDATA = 0 ' at which index the [No Default] entry is located ' ---------------------------------------------------------------------------------- ' This function requires the ddoc Print And Preview Library. 'If you ain't got that you can use any other method desired to load the list ' ----------------------------------------------------------------------------------- #IF %I_AM_USING_DDOC_PRINT_AND_PREVIEW ' ddoc headers #INCLUDE "ddoc_p32.inc" FUNCTION LoadInstalledPrinters (szP() AS ASCIIZ * %PRINTER_NAME_SIZE) AS LONG LOCAL nPrinter AS LONG, I AS LONG nPRinter = DpPrinterCount () REDIM szP(nPrinter) FOR I = 1 TO nPrinter ' load printers 1-n, printer 0 = "no Default" szP (I) = dpgetPrinter (I) NEXT END FUNCTION #ELSE ' i.e., NOT using ddoc Print and Preview. We have to do this the long way... FUNCTION LoadInstalledPrinters (szP() AS ASCIIZ * %PRINTER_NAME_SIZE ) AS LONG LOCAL pInfo5() AS PRINTER_INFO_5,_ dwNeeded AS LONG,_ dwReturned AS LONG,_ pszName AS ASCIIZ PTR,_ pBuffer AS DWORD,_ dwEnumFlags AS DWORD,_ Z AS LONG,_ Level AS LONG, _ sBuffer AS STRING Level = 5& dwEnumFlags = %PRINTER_ENUM_LOCAL OR %PRINTER_ENUM_CONNECTIONS ' << return all printers for this machine pszName = %NULL ' we do not send a name string since we want all ' --------------------------------------------------------------------------------------------------------- ' Get the size of the buffer needed for printer enumeration.. ' This call will fail on insufficient buffer, but that's what we want it do... ' Note that dwNeeded is not just for the PRINTER_INFO_5 structures, it includes the space needed for the ' printer names pointed to by the pPrinterName members of the PRINTER_INFO_5 structures returned. ' You cannot size anything other than the buffer size required based on the value of dwNeeded; ' you have to use dwReturned when the enum succeeds to get the number of printers. ' --------------------------------------------------------------------------------------------------------- Z = EnumPrinters (dwEnumFlags, BYVAL pszName, Level, BYVAL %NULL, 0, dwNeeded, dwReturned) ' create needed buffer sBuffer = STRING$(dwNeeded, 0) ' set buffer pointer, repeat enumeration pBuffer = STRPTR (sBuffer) IF EnumPrinters (dwEnumFlags, BYVAL pszName, Level, BYVAL pBuffer, dwNeeded, dwNeeded, dwReturned) THEN REDIM szP (dwReturned) ' resize passed array to handle the correct number of printers. REDIM PINFO5(dwReturned) AT pBuffer ' set up to read the PRINTER_INFO5 structures at head of buffer FOR Z = 1 TO dwReturned ' szP(Z) = pInfo5(Z-1).@pPrinterName ' put names returned in passed array NEXT ELSE MSGBOX "Enum Printers Failed", %MB_ICONHAND,"Oops!" END IF END FUNCTION #ENDIF ' if using ddoc P & P ' -------------------------------------------------------------------' ' Call the prtDlg() Common Dialog, initializing for a named printer ' if one was passed ' -------------------------------------------------------------------' FUNCTION CallPrtDlg (BYVAL hWnd AS LONG, OPTIONAL szPrinterName AS ASCIIZ) AS STRING LOCAL GotPrinterName AS LONG, iRet AS LONG LOCAL pDevNames AS DEVNAMES PTR, hDevNames AS LONG LOCAL pDevMode AS DEVMODE PTR, hDevMode AS LONG LOCAL psz AS ASCIIZ PTR, pDest AS DWORD LOCAL PD AS PRINTDLGAPI LOCAL szUsePrinterName AS ASCIIZ * %PRINTER_NAME_SIZE ' ==[End DIM, start code]==== GotPrinterName = ISTRUE(VARPTR(szPRinterName)) ' was the optional parameter passed? ' Initialize the PRINTDLGAPI structure, include a named printer if it was passed ' Although we only *need* to allocate the extra space if/when we get a printername, it costs nothing ' to allow for the space at all times hDevNames = GlobalAlloc (%GMEM_MOVEABLE OR %GMEM_ZEROINIT, SIZEOF(@pDevNames) + %PRINTER_NAME_SIZE) IF hDevNames = 0 THEN MSGBOX "GlobalAlloc of Devnames failed!" EXIT FUNCTION END IF hDevMode = GlobalAlloc (%GMEM_MOVEABLE OR %GMEM_ZEROINIT, SIZEOF(@pDevMode)) IF hDevMode = 0 THEN MSGBOX "GlobalAlloc of DevMode failed!" EXIT FUNCTION END IF ' -------------------------------------------- ' SET COMMON ELEMENTS OF PRINTDLGAPI STRUCTURE ' -------------------------------------------- PD.lStructSize = SIZEOF(PD) PD.hWndOwner = hWnd PD.hDevMode = hDevMode PD.hDevNames = hDevNames PD.FLAGS = %PD_NOPAGENUMS OR %PD_USEDEVMODECOPIESANDCOLLATE ' any flags you may want can go here ' --------------------------------------------------------------------------------------------- ' INITIALIZE THE DIALOG BOX'S DEVICE NAME FOR NAMED PRINTER IF WE GOT ONE, or NULL IF WE DIDN'T ' --------------------------------------------------------------------------------------------- pDevNames = GlobalLock(PD.hDevNames) IF ISTRUE GotPrinterName THEN @pDevnames.wDeviceOffset = SIZEOF (@pDevNames) ' name of printer to use immediately follows Devnames pDest = pDevnames + SIZEOF(@pDevNames) ' point to area following DEVNAMES structure CopyMemory pDest, VARPTR(szPrinterName), lstrlen(szPrinterName) ' copy printer name ELSE @pDevNames.wDeviceOffset = %NULL END IF GlobalUnlock PD.hDevNames ' ------------------------------ ' CALL THE PRINT DIALOG ' ------------------------------ iRet = PrintDlg(PD) IF ISTRUE iRet THEN ' user Clicked OK (to something!) ' Get the name of the selected printer.. IF PD.hDevNames <> 0 THEN pDevNames = GlobalLock(PD.hDevNames) psz = pDevnames psz = psz + @pDevnames.wDeviceOffset ' add returned offset szUsePrinterName = @psz ' <<< user-friendly printer name, e.g., "HP Desk Jet 870" GlobalUnlock PD.hDevnames ELSE MSGBOX "Can't get DevNames Handle in Print Routine", %MB_ICONERROR, "Print Subsystem Error" szUsePrinterName = "ERROR" END IF ELSE ' we did not get a printerName szUsePRinterName = "USER CANCELED WITHOUT SELECTING" END IF 'Free the globals we alloc'ed GlobalFree PD.hDevNames GlobalFree PD.hDevMode FUNCTION = szUsePrinterName ' return selected name (or error message for this demo) END FUNCTION ' What is this? I now *MUST* use the "CALLBACK FUNCTION" syntax , and *may not* use params? ' There's a behavior change from 6x for sure. (and maybe from 7.0?) (not documented in 7.02 help file as change) CALLBACK FUNCTION DlgProc () LOCAL hWnd AS LONG, uMsg AS LONG, wParam AS LONG, lParam AS LONG LOCAL szPrinters() AS ASCIIZ * %PRINTER_NAME_SIZE, Z AS LONG, iIndex AS LONG LOCAL szSelPrinter AS ASCIIZ * %PRINTER_NAME_SIZE, iSel AS LONG LOCAL szText AS ASCIIZ * %MAX_PATH ' set wndProc variables, since code was already writen before the "CALLBACK FUNCTION" thing was known... hWnd = CBHNDL uMsg = CBMSG wParam = CBWPARAM lParam = CBLPARAM SELECT CASE AS LONG uMSG CASE %WM_INITDIALOG ' load the "Use default" entry and the list of installed printers to combobox REDIM szPrinters(0) ' list loader will resize CALL LoadInstalledPrinters (szPrinters ()) 'load printers 1-n szPrinters (0) = "[No Default]" ' set element zero FOR Z = 0 TO UBOUND (szPrinters,1) iIndex = SendDlgItemMessage (hWnd, %ID_CB, %CB_ADDSTRING, 0, VARPTR(szPrinters(Z))) ' set item data to row number so we can tell if a default printer is wanted SendDlgItemMessage hWnd, %ID_CB, %CB_SETITEMDATA, iIndex, Z NEXT ' pre-select the first entry in list SendDlgITemMessage hWnd, %ID_CB, %CB_SETCURSEL, 0, 0 CASE %WM_COMMAND SELECT CASE LOWRD(wParam) CASE %ID_CALL_PRTDLG ' erase selection from last call szSelPrinter = "" SetDlgItemText hWnd, %ID_SEL_PRINTER, szSelPrinter ' find currently selected printer iSel = SendDlgITemMessage (hWnd, %ID_CB, %CB_GETCURSEL, 0, 0) IF iSel >= 0 THEN ' something is selected IF SendDlgItemMessage (hWnd, %ID_CB, %CB_GETITEMDATA,iSel,0) = %NODEFAULT_ITEMDATA THEN ' call the initializer without a printer name szSelPrinter = CallPrtDlg (hwnd) ELSE SendDlgItemMessage hWnd, %ID_CB, %CB_GETLBTEXT, iSel, BYVAL VARPTR (szText) ' get printer name ' and call the initializer WITH a named printer szSelPrinter = CallPrtDlg (hwnd, szText) END IF ' show the last results SetDlgItemText hWnd, %ID_SEL_PRINTER, szSelPrinter ELSE MSGBOX "No Printer Selected!" END IF END SELECT ' of control Id END SELECT END FUNCTION FUNCTION PBMAIN() AS LONG LOCAL hDLG AS LONG, cbStyle AS DWORD cbStyle = %WS_VISIBLE OR %WS_CHILD OR %CBS_DROPDOWNLIST DIALOG NEW 0, "Initialized PrtDlg() Demo", 0, 0, 260, 170, _ %DS_CENTER OR %WS_CAPTION OR %WS_SYSMENU OR %WS_VISIBLE , 0 TO hDlg ' Abort if the dialog could not be created IF hDlg = 0 THEN EXIT FUNCTION ' Error occurred ' add our controls CONTROL ADD LABEL , hDlg, -1, "Printer for which to initialize", 2, 12, 150, 14 CONTROL ADD COMBOBOX , hDlg, %ID_CB, ,2, 30, 200, 100, cbStyle CONTROL ADD BUTTON , hDlg, %ID_CALL_PRTDLG, "Call PrtDlg", 110, 120, 40,14 CONTROL ADD LABEL , hDlg, -1, "Selected Printer", 2, 150, 60, 14 CONTROL ADD LABEL , hDlg, %ID_SEL_PRINTER, "" , 80, 150, 160, 14 DIALOG SHOW MODAL hDlg, CALL DlgProc() ' must use 'CALLBACK FUNCTION' procedure header. END FUNCTION ' /// END OF FILE INIT_PRTDLG.BAS ///
------------------
Michael Mattias
Tal Systems Inc.
Racine WI USA
mailto:[email protected][email protected]</A>
www.talsystems.com