=Getting and Setting the Default Printer= //[[user:JanetTerra]]// [[toc]] ---- ==Liberty BASIC's Native Printer Dialog== The //printerdialog// function brings up the standard Windows printer dialog box. From the Liberty BASIC helpfile >> PRINTERDIALOG >> Description >> This command opens the standard Windows Common Printer Dialog. If the user chooses a printer and accepts, the next print job will go to this printer. Accepting a printer also sets the global variables PrinterName$, PrintCollate and PrintCopies to reflect what the user chose for the Printer Name, Collate and Copies. If no printer is accepted, then PrinterName$ is set to an empty string. Unfortunately, Liberty BASIC doesn't interact well with Windows printer dialog. Despite what information PrinterName$ holds, all documents, whether text (//ldump//) or graphics (//vga//, //svga//, //xga//) will be sent to the default printer. A work-around is to set the desired printer as default before printing the document. This work-around is based upon contributions of Stefan Pendl. All of the code used in this article has been compiled from postings made by Stefan Pendl,[[user:stpendl|Stefan Pendl]], both at [[http://libertybasic.conforums.com|Liberty BASIC Conforums]] and at the [[http://groups.yahoo.com/group/libertybasic/|Official Liberty BASIC Support Group]]. The compiled snippets have been modified slightly for consistency with descriptive variable names, but otherwise remain intact. The first step is to identify (get) the current default printer. ==Get Default Printer== The information derived from the winspool.drv DLL will be placed in a struct. The struct must first be defined, here it's //pcchBuffer//, and the length of the struct element //value// set to _MAX_PATH. The call to the DLL is then made and the name of the default printer is placed into //pcchBuffer.value.struct//. Note that //byRef// is used so that the changes to //currentDefaultPrinter$// are made both locally and globally. The function itself returns a number, 0 for a failure, non-zero for success. [[code format="vbnet"]] GetDefaultPrinter = GetDefaultPrinter(currentDefaultPrinter$) if GetDefaultPrinter = 0 then print "Call failed" else print "DefaultPrinter = ";currentDefaultPrinter$ end if end function GetDefaultPrinter(byref currentDefaultPrinter$) ' Returns zero if call fails struct pcchBuffer, value as ulong currentDefaultPrinter$ = space$(_MAX_PATH) pcchBuffer.value.struct = _MAX_PATH open "winspool.drv" for dll as #winspool calldll #winspool, "GetDefaultPrinterA", _ currentDefaultPrinter$ as ptr, _ pcchBuffer as struct, _ GetDefaultPrinter as long close #winspool end function [[code]] Stefan's code has always been accompanied with an error catching routine. Should the call not work, Liberty BASIC can identify the cause of the failure. [[code format="vbnet"]] GetDefaultPrinter = GetDefaultPrinter(currentDefaultPrinter$) if GetDefaultPrinter = 0 then print "Call failed" else print "DefaultPrinter = ";currentDefaultPrinter$ end if end function GetDefaultPrinter(byref currentDefaultPrinter$) ' Returns zero if call fails struct pcchBuffer, value as ulong currentDefaultPrinter$ = space$(_MAX_PATH) pcchBuffer.value.struct = _MAX_PATH open "winspool.drv" for dll as #winspool calldll #winspool, "GetDefaultPrinterA", _ currentDefaultPrinter$ as ptr, _ pcchBuffer as struct, _ GetDefaultPrinter as long if GetDefaultPrinter = 0 then call DisplayError else currentDefaultPrinter$ = left$(currentDefaultPrinter$, pcchBuffer.value.struct - 1) end if close #winspool end function sub DisplayError ErrorCode = GetLastError() dwFlags = _FORMAT_MESSAGE_FROM_SYSTEM nSize = 1024 lpBuffer$ = space$(nSize); chr$(0) dwMessageID = ErrorCode calldll #kernel32, "FormatMessageA", _ dwFlags as ulong, _ lpSource as ulong, _ dwMessageID as ulong, _ dwLanguageID as ulong, _ lpBuffer$ as ptr, _ nSize as ulong, _ Arguments as ulong, _ result as ulong print "Error "; ErrorCode; ": "; left$(lpBuffer$, result) end sub function GetLastError() calldll #kernel32, "GetLastError", _ GetLastError as ulong end function [[code]] Storing the current default printer in a variable is important to allow that printer to be reassigned as default once the document has been printed. ==List All Printers== The next step is to get a list of all available printers. This requires a little more work and three more structs. Stefan's function loops around to obtain all the printer names, until there are no names left. The printer names are concatenated, deliminated with a semicolon, in the string variable PrinterInfo$. The loop ends when error #122 (The data area passed to a system call is too small) is encounered. Once again, //byRef// is used so that PrinterInfo$ remains the same locally and globally. Enumerating the printers requires allocating and searching blocks of memory, resulting in rather complex code. Also, listing printers available to the computer by a local (physical) connection requires different variables than listing printers available to the computer by a network or wireless connection. The EnumPrinters() function must be accessed twice, first for local printers then for network printers. The appropriate variables should be passed to the function each time. >> Flag to List Local Printers >> PRINTER.ENUM.LOCAL = hexdec("2") >> Flag to List Network Printers >> PRINTER.ENUM.CONNECTIONS = hexdec("4") The first pass stores the retrieved information in //LocalPrinterInfo$// and the second pass stores the retrieved information in //NetworkPrinterInfo$//. These two stringes are then concatenated with a semicolon to hold all printers in //PrinterInfo$//. Finally, an array is constructed to hold the individual printer names. [[code format="vbnet"]] ' Count and enumerate all printers ' Need to access function twice, first for local printers, second for network printers PRINTER.ENUM.LOCAL = hexdec("2") PRINTER.ENUM.CONNECTIONS = hexdec("4") nLocalPrinters = EnumPrinters(PrinterInfo$, PRINTER.ENUM.LOCAL) print "nLocalPrinters = ";nLocalPrinters LocalPrinterInfo$ = PrinterInfo$ print "LocalPrinterInfo$ = ";LocalPrinterInfo$ print nNetworkPrinters = EnumPrinters(PrinterInfo$, PRINTER.ENUM.CONNECTIONS) print "nNetworkPrinters = ";nNetworkPrinters NetworkPrinterInfo$ = PrinterInfo$ print "NetworkPrinterInfo$ = ";NetworkPrinterInfo$ print ' Add both to total and combine the 2 printer strings nPrinters = nLocalPrinters + nNetworkPrinters Print "nPrinters = ";nPrinters PrinterInfo$ = LocalPrinterInfo$;";";NetworkPrinterInfo$ print "PrinterInfo$ = ";PrinterInfo$ ' Place all printers in an array dim availablePrinters$(nPrinters) for i = 1 to nPrinters availablePrinters$(i) = word$(PrinterInfo$, i, ";") next i for i = 1 to nPrinters print i, availablePrinters$(i) next i end function EnumPrinters(byref PrinterInfo$, nFlags) ' Returns the number of printers found ' Fills the submitted variable with the printer names ' Separated by semicolons (;) open "winspool.drv" for dll as #winspool struct pcbNeeded, value as ulong struct pcReturned, value as ulong struct PrinterInfo4, _ pPrinterName$ as ptr, _ pServerName$ as ptr, _ Attributes as ulong PrinterInfo4Len = len(PrinterInfo4.struct) Level = 4 cbBuf = PrinterInfo4Len uFlags = _LMEM_MOVEABLE or _LMEM_ZEROINIT calldll #kernel32, "LocalAlloc", _ uFlags as uLong, _ cbBuf as uLong, _ hMem as uLong calldll #kernel32, "LocalLock", _ hMem as uLong, _ pBuffer as uLong [retryEnumPrinters] calldll #winspool, "EnumPrintersA", _ nFlags as ulong, _ PrinterName as ulong, _ Level as ulong, _ pBuffer as ulong, _ cbBuf as ulong, _ pcbNeeded as struct, _ pcReturned as struct, _ result as boolean if result = 0 then if GetLastError() = 122 then cbBuf = pcbNeeded.value.struct hOldMem = hMem calldll #kernel32, "LocalReAlloc", _ hOldMem as ulong, _ cbBuf as ulong, _ uFlags as ulong, _ hMem as ulong calldll #kernel32, "LocalLock", _ hMem as uLong, _ pBuffer as ulong goto [retryEnumPrinters] else call DisplayError end if else EnumPrinters = pcReturned.value.struct BufferPointer = pBuffer for count = 0 to EnumPrinters - 1 calldll #kernel32, "RtlMoveMemory", _ PrinterInfo4 as struct, _ BufferPointer as ulong, _ PrinterInfo4Len as ulong, _ result as void BufferPointer = BufferPointer + PrinterInfo4Len pointer = PrinterInfo4.pPrinterName$.struct PrinterInfo$ = winstring(pointer); ";"; PrinterInfo$ next count PrinterInfo$ = left$(PrinterInfo$, len(PrinterInfo$)-1) end if calldll #kernel32, "LocalFree", _ hMem as uLong, _ result as uLong close #winspool end function function GetLastError() calldll #kernel32, "GetLastError", _ GetLastError as ulong end function [[code]] The final step is to designate a different printer as default. ==Set Default Printer== The code to set a default printer is the simplest of all, just passing a valid printer name to the winspool.drv dll. Like the GetDefaultPrinter() function, the SetDefaultPrinter() function returns a 0 for failure, a non-zero for success. [[code format="vbnet"]] selectedDefaultPrinter$ = "My Inkjet Printer" ' Set new default printer SetDefaultPrinter = SetDefaultPrinter(selectedDefaultPrinter$) end function SetDefaultPrinter(selectedDefaultPrinter$) ' Returns zero if call fails open "winspool.drv" for dll as #winspool calldll #winspool, "SetDefaultPrinterA",_ selectedDefaultPrinter$ as ptr,_ SetDefaultPrinter as long close #winspool end function [[code]] This is the same code, but with Stefan's error trapping included. [[code format="vbnet"]] selectedDefaultPrinter$ = "My Inkjet Printer" ' Set new default printer SetDefaultPrinter = SetDefaultPrinter(selectedDefaultPrinter$) end function SetDefaultPrinter(selectedDefaultPrinter$) ' Returns zero if call fails open "winspool.drv" for dll as #winspool calldll #winspool, "SetDefaultPrinterA",_ selectedDefaultPrinter$ as ptr,_ SetDefaultPrinter as long close #winspool if SetDefaultPrinter = 0 then call DisplayError end function sub DisplayError ErrorCode = GetLastError() dwFlags = _FORMAT_MESSAGE_FROM_SYSTEM nSize = 1024 lpBuffer$ = space$(nSize); chr$(0) dwMessageID = ErrorCode calldll #kernel32, "FormatMessageA", _ dwFlags as ulong, _ lpSource as ulong, _ dwMessageID as ulong, _ dwLanguageID as ulong, _ lpBuffer$ as ptr, _ nSize as ulong, _ Arguments as ulong, _ result as ulong print "Error "; ErrorCode; ": "; left$(lpBuffer$, result) end sub function GetLastError() calldll #kernel32, "GetLastError", _ GetLastError as ulong end function [[code]] ==Getting, Listing, Setting the Default Printer (Mainwindow)== Using all three components, the programmer now has full control of getting, listing, and setting the default printer. [[code format="vbnet"]] ' Get the original default printer GetDefaultPrinter = GetDefaultPrinter(origDefaultPrinter$) print "origDefaultPrinter$ = ";origDefaultPrinter$ print ' Count and enumerate all printers ' Need to access function twice, first for local printers, second for network printers PRINTER.ENUM.LOCAL = hexdec("2") PRINTER.ENUM.CONNECTIONS = hexdec("4") nLocalPrinters = EnumPrinters(PrinterInfo$, PRINTER.ENUM.LOCAL) print "nLocalPrinters = ";nLocalPrinters LocalPrinterInfo$ = PrinterInfo$ print "LocalPrinterInfo$ = ";LocalPrinterInfo$ print nNetworkPrinters = EnumPrinters(PrinterInfo$, PRINTER.ENUM.CONNECTIONS) print "nNetworkPrinters = ";nNetworkPrinters NetworkPrinterInfo$ = PrinterInfo$ print "NetworkPrinterInfo$ = ";NetworkPrinterInfo$ print ' Add both to total and combine the 2 printer strings nPrinters = nLocalPrinters + nNetworkPrinters Print "nPrinters = ";nPrinters PrinterInfo$ = LocalPrinterInfo$;";";NetworkPrinterInfo$ print "PrinterInfo$ = ";PrinterInfo$ ' Place all printers in an array dim availablePrinters$(nPrinters) for i = 1 to nPrinters availablePrinters$(i) = word$(PrinterInfo$, i, ";") next i for i = 1 to nPrinters print i, availablePrinters$(i) next i print ' Select another default printer Input "Printer to set as default > ";selectedDefaultPrinter selectedDefaultPrinter$ = availablePrinters$(selectedDefaultPrinter) print ' Set new default printer SetDefaultPrinter = SetDefaultPrinter(selectedDefaultPrinter$) end function EnumPrinters(byref PrinterInfo$, nFlags) ' Returns the number of printers found ' Fills the submitted variable with the printer names ' Separated by semicolons (;) open "winspool.drv" for dll as #winspool struct pcbNeeded, value as ulong struct pcReturned, value as ulong struct PrinterInfo4, _ pPrinterName$ as ptr, _ pServerName$ as ptr, _ Attributes as ulong PrinterInfo4Len = len(PrinterInfo4.struct) Level = 4 cbBuf = PrinterInfo4Len uFlags = _LMEM_MOVEABLE or _LMEM_ZEROINIT calldll #kernel32, "LocalAlloc", _ uFlags as uLong, _ cbBuf as uLong, _ hMem as uLong calldll #kernel32, "LocalLock", _ hMem as uLong, _ pBuffer as uLong [retryEnumPrinters] calldll #winspool, "EnumPrintersA", _ nFlags as ulong, _ PrinterName as ulong, _ Level as ulong, _ pBuffer as ulong, _ cbBuf as ulong, _ pcbNeeded as struct, _ pcReturned as struct, _ result as boolean if result = 0 then if GetLastError() = 122 then cbBuf = pcbNeeded.value.struct hOldMem = hMem calldll #kernel32, "LocalReAlloc", _ hOldMem as ulong, _ cbBuf as ulong, _ uFlags as ulong, _ hMem as ulong calldll #kernel32, "LocalLock", _ hMem as uLong, _ pBuffer as ulong goto [retryEnumPrinters] else call DisplayError end if else EnumPrinters = pcReturned.value.struct BufferPointer = pBuffer for count = 0 to EnumPrinters - 1 calldll #kernel32, "RtlMoveMemory", _ PrinterInfo4 as struct, _ BufferPointer as ulong, _ PrinterInfo4Len as ulong, _ result as void BufferPointer = BufferPointer + PrinterInfo4Len pointer = PrinterInfo4.pPrinterName$.struct PrinterInfo$ = winstring(pointer); ";"; PrinterInfo$ next count PrinterInfo$ = left$(PrinterInfo$, len(PrinterInfo$)-1) end if calldll #kernel32, "LocalFree", _ hMem as uLong, _ result as uLong close #winspool end function function GetDefaultPrinter(byref currentDefaultPrinter$) ' Returns zero if call fails struct pcchBuffer, value as ulong currentDefaultPrinter$ = space$(_MAX_PATH) pcchBuffer.value.struct = _MAX_PATH open "winspool.drv" for dll as #winspool calldll #winspool, "GetDefaultPrinterA", _ currentDefaultPrinter$ as ptr, _ pcchBuffer as struct, _ GetDefaultPrinter as long close #winspool if GetDefaultPrinter = 0 then call DisplayError else currentDefaultPrinter$ = left$(currentDefaultPrinter$, pcchBuffer.value.struct - 1) end if end function function SetDefaultPrinter(selectedDefaultPrinter$) ' Returns zero if call fails print "selectedDefaultPrinter$ = ";selectedDefaultPrinter$ open "winspool.drv" for dll as #winspool calldll #winspool, "SetDefaultPrinterA",_ selectedDefaultPrinter$ as ptr,_ SetDefaultPrinter as long close #winspool Print "SetDefaultPrinter = ";SetDefaultPrinter if SetDefaultPrinter = 0 then call DisplayError end function sub DisplayError ErrorCode = GetLastError() dwFlags = _FORMAT_MESSAGE_FROM_SYSTEM nSize = 1024 lpBuffer$ = space$(nSize); chr$(0) dwMessageID = ErrorCode calldll #kernel32, "FormatMessageA", _ dwFlags as ulong, _ lpSource as ulong, _ dwMessageID as ulong, _ dwLanguageID as ulong, _ lpBuffer$ as ptr, _ nSize as ulong, _ Arguments as ulong, _ result as ulong print "Error "; ErrorCode; ": "; left$(lpBuffer$, result) end sub function GetLastError() calldll #kernel32, "GetLastError", _ GetLastError as ulong end function [[code]] ----