From: Mojo on 25 Feb 2010 09:46 Hi All I've been shyed away from using the common dialog control for my 'open' and 'print' requirements due to bundling issues (I'm trying to do a portable app), resources, etc and although I found a great and straightforward API routine for the 'open' aspect (using the comdlg32.dll API call) there doesn't appear to be the same method for printing. All I want to do is bring up the print dialog so that they can choose their own printer. At the moment, I'm sending my print jobs straight to the default printer, but this isn't always the best way in a school environment. Any good tips without the need to use/register an ocx? Thanks
From: Dave O. on 25 Feb 2010 10:02 "Mojo" <please(a)dont.spam.com> wrote in message news:um3vclitKHA.4220(a)TK2MSFTNGP05.phx.gbl... > Hi All > > I've been shyed away from using the common dialog control for my 'open' > and > 'print' requirements due to bundling issues (I'm trying to do a portable > app), resources, etc and although I found a great and straightforward API > routine for the 'open' aspect (using the comdlg32.dll API call) there > doesn't appear to be the same method for printing. All I want to do is > bring up the print dialog so that they can choose their own printer. At > the > moment, I'm sending my print jobs straight to the default printer, but > this > isn't always the best way in a school environment. > > Any good tips without the need to use/register an ocx? > > Thanks > Hi, in the same way that you use the GetOpenFileName API to open a file you would use the ShowPrinter API to get the printer dialogue. Or if you just want to show a list of available printers then you can use the Printer object to iterate a list: I just tested it by filling a listbox although in real life a combo box would be more appropriate:: For i = 0 To Printers.Count - 1 List1.AddItem Printers(i).DeviceName Next Seems to work a treat, an obvious touch would be to have the default printer preselected. Regards Dave O.
From: Mike Williams on 25 Feb 2010 12:07 "Mojo" <please(a)dont.spam.com> wrote in message news:um3vclitKHA.4220(a)TK2MSFTNGP05.phx.gbl... > I've been shyed away from using the common dialog control for > my 'open' and 'print' requirements due to bundling issues . . and > although I found a great and straightforward API routine for the > 'open' aspect (using the comdlg32.dll API call) there doesn't > appear to be the same method for printing. All I want to do is > bring up the print dialog so that they can choose their own printer. The best way is to present your users with a standard Windows printer dialog using the PrintDialog API and instructing it to return a hDC for you. That method allows your users to properly select from all the available printer options in the dialog, including those options that are special to many printers and that would otherwise unavailable because they cannot be transferred to the VB Printer Object and also including those options that are not even known to the PrintDialog itself (other than the fact that it knows there is something in the Extras section that only the printer driver itself understands). However, the method involves doing all your printing to the returned hDC using the various GDI printing and drawing functions, and on the assumption that you are currently using the VB Printer Object for your printing and that you would prefer to continue to do so then that method would not be appropriate for you, although personally I would suggest it as being the very best method. Post again if you would like to go that route. Otherwise there are still plenty options available to you, although you should avoid at all costs any option that forces the system default printer to change to the printer your user has selected (some methods do that and they are to be avoided). One very simple method would be to create your own "home brewed" printer dialog using a Modal Form containing a ListBox and populating the ListBox by running through the built-in VB Printers Collection, allowing the user to select whichever printer he wants and then using Set Printer to st the VB printer to whatever he has chosen. There are of course many limitations to that approach, and it is non standard, but it is very easy to do and can be done in a dozen or so lines of code. Another alternative is to show a standard Windows printer dialog using the PrintDialog API, allowing the user to make his various selections and then transferring them to the VB Printer Object. That method does of course limit you to the settings that the VB Printer Object can actually "eat", but it is probably the second best option (after the hDC option I mentioned earlier). There is a Micro$oft DLL called vbprndlg.dll which wraps it all up nicely for you and which can be downloaded at http://support.microsoft.com/kb/322710 along with its various supporting files, although since you've said you want to avoid bundling supporting files then I imagine you won't want to use that DLL so you will probably be better off using the PrintDialog API through straight VB6 code. As they say in all the best cookery programs, here's one I prepared earlier ;-) It's actually a modification of some code that was on the MSDN website before they produced the vbprndlg.DLL, but I have modified it in various ways to overcome some of its limitations. For example I've added code to take account of long printer names, which can be very much longer than the CCHDEVICENAME limit of 32 characters that can be present in the DEVMODE dmDeviceName entry, especially when they are over networks but also sometimes even if they are not. I've also added some code to properly set the positioning of your printout so that it takes account of the printer's unrpintable margins. In fact the main reason I wrote all that "waffle" above is to alert you to the limitations of some of the other methods (although not to the limitations of the first method I suggested, because it doesn't really have any other than the added complexity of your printing code), and I did so in order that you would not be put off by the length of the code I am posting. It seems like a lot, but it really is well worth doing. Anyway, here is the code. To try it out just paste it into a VB Form containing one Command Button. Mike Option Explicit Private Declare Function GetDeviceCaps Lib "gdi32" _ (ByVal hdc As Long, ByVal nIndex As Long) As Long Private Declare Function PrintDialog Lib "comdlg32.dll" _ Alias "PrintDlgA" (pPrintdlg As PRINTDLG_TYPE) As Long Private Declare Sub CopyMemory Lib "kernel32" Alias _ "RtlMoveMemory" (hpvDest As Any, hpvSource As Any, _ ByVal cbCopy As Long) Private Declare Function GlobalLock Lib "kernel32" _ (ByVal hMem As Long) As Long Private Declare Function GlobalUnlock Lib "kernel32" _ (ByVal hMem As Long) As Long Private Declare Function GlobalAlloc Lib "kernel32" _ (ByVal wFlags As Long, ByVal dwBytes As Long) As Long Private Declare Function GlobalFree Lib "kernel32" _ (ByVal hMem As Long) As Long Private Declare Function SetBkMode Lib "gdi32" _ (ByVal hdc As Long, ByVal nBkMode As Long) As Long Private Const CCHDEVICENAME = 32 Private Const CCHFORMNAME = 32 Private Const GMEM_MOVEABLE = &H2 Private Const GMEM_ZEROINIT = &H40 Private Const DM_DUPLEX = &H1000& Private Const DM_ORIENTATION = &H1& Private Const PD_PRINTSETUP = &H40 Private Const PD_DISABLEPRINTTOFILE = &H80000 Private Const PHYSICALOFFSETX As Long = 112 Private Const PHYSICALOFFSETY As Long = 113 Private Const OPAQUE = 0 Private Const TRANSPARENT = 1 Private Type PRINTDLG_TYPE lStructSize As Long hwndOwner As Long hDevMode As Long hDevNames As Long hdc As Long flags As Long nFromPage As Integer nToPage As Integer nMinPage As Integer nMaxPage As Integer nCopies As Integer hInstance As Long lCustData As Long lpfnPrintHook As Long lpfnSetupHook As Long lpPrintTemplateName As String lpSetupTemplateName As String hPrintTemplate As Long hSetupTemplate As Long End Type Private Type DEVNAMES_TYPE wDriverOffset As Integer wDeviceOffset As Integer wOutputOffset As Integer wDefault As Integer extra As String * 200 End Type Private Type DEVMODE_TYPE dmDeviceName As String * CCHDEVICENAME dmSpecVersion As Integer dmDriverVersion As Integer dmSize As Integer dmDriverExtra As Integer dmFields As Long dmOrientation As Integer dmPaperSize As Integer dmPaperLength As Integer dmPaperWidth As Integer dmScale As Integer dmCopies As Integer dmDefaultSource As Integer dmPrintQuality As Integer dmColor As Integer dmDuplex As Integer dmYResolution As Integer dmTTOption As Integer dmCollate As Integer dmFormName As String * CCHFORMNAME dmUnusedPadding As Integer dmBitsPerPel As Integer dmPelsWidth As Long dmPelsHeight As Long dmDisplayFlags As Long dmDisplayFrequency As Long dmICMMethod As Long dmICMIntent As Long dmMediaType As Long dmDitherType As Long dmReserved1 As Long dmReserved2 As Long dmPanningWidth As Long dmPanningHeight As Long End Type Private Sub SetPrinterOrigin(x As Single, y As Single) With Printer .ScaleLeft = .ScaleX(GetDeviceCaps _ (.hdc, PHYSICALOFFSETX), _ vbPixels, .ScaleMode) - x .ScaleTop = .ScaleY(GetDeviceCaps _ (.hdc, PHYSICALOFFSETY), _ vbPixels, .ScaleMode) - y .CurrentX = 0 .CurrentY = 0 End With End Sub Private Function SelectPrinter(frmOwner As Form, Optional _ InitialPrinter As String, Optional _ PrintFlags As Long = PD_PRINTSETUP) _ As Boolean Dim LongPrinterName As String Dim PrintDlg As PRINTDLG_TYPE Dim DevMode As DEVMODE_TYPE Dim DevName As DEVNAMES_TYPE Dim lpDevMode As Long, lpDevName As Long Dim bReturn As Integer, OriginalPrinter As String Dim p1 As Printer, NewPrinterName As String PrintDlg.lStructSize = Len(PrintDlg) PrintDlg.hwndOwner = frmOwner.hWnd PrintDlg.flags = PrintFlags On Error Resume Next OriginalPrinter = Printer.DeviceName If Len(InitialPrinter) > 0 Then For Each p1 In Printers If InStr(1, p1.DeviceName, InitialPrinter, _ vbTextCompare) > 0 Then Set Printer = p1 Exit For End If Next End If DevMode.dmDeviceName = Printer.DeviceName DevMode.dmSize = Len(DevMode) DevMode.dmFields = DM_ORIENTATION DevMode.dmPaperWidth = Printer.Width DevMode.dmOrientation = Printer.Orientation DevMode.dmPaperSize = Printer.PaperSize On Error GoTo 0 PrintDlg.hDevMode = GlobalAlloc(GMEM_MOVEABLE Or _ GMEM_ZEROINIT, Len(DevMode)) lpDevMode = GlobalLock(PrintDlg.hDevMode) If lpDevMode > 0 Then CopyMemory ByVal lpDevMode, DevMode, Len(DevMode) bReturn = GlobalUnlock(PrintDlg.hDevMode) End If With DevName .wDriverOffset = 8 .wDeviceOffset = .wDriverOffset + 1 + Len _ (Printer.DriverName) .wOutputOffset = .wDeviceOffset + 1 + Len(Printer.Port) .wDefault = 0 End With With Printer DevName.extra = .DriverName & Chr(0) & _ .DeviceName & Chr(0) & .Port & Chr(0) End With PrintDlg.hDevNames = GlobalAlloc(GMEM_MOVEABLE Or _ GMEM_ZEROINIT, Len(DevName)) lpDevName = GlobalLock(PrintDlg.hDevNames) If lpDevName > 0 Then CopyMemory ByVal lpDevName, DevName, Len(DevName) bReturn = GlobalUnlock(lpDevName) End If If PrintDialog(PrintDlg) <> 0 Then ' ' Mike's amendment to handle long printer names CopyMemory DevName, ByVal lpDevName, Len(DevName) LongPrinterName = Mid$(DevName.extra, _ DevName.wDeviceOffset - DevName.wDriverOffset + 1) LongPrinterName = Left$(LongPrinterName, _ InStr(LongPrinterName, Chr$(0)) - 1) DoEvents ' allow dialog to remove itself from display Me.Refresh SelectPrinter = True lpDevName = GlobalLock(PrintDlg.hDevNames) CopyMemory DevName, ByVal lpDevName, 45 bReturn = GlobalUnlock(lpDevName) GlobalFree PrintDlg.hDevNames lpDevMode = GlobalLock(PrintDlg.hDevMode) CopyMemory DevMode, ByVal lpDevMode, Len(DevMode) bReturn = GlobalUnlock(PrintDlg.hDevMode) GlobalFree PrintDlg.hDevMode NewPrinterName = UCase$(Left(DevMode.dmDeviceName, _ InStr(DevMode.dmDeviceName, Chr$(0)) - 1)) ' Code now handles long printer names properly If Printer.DeviceName <> _ LongPrinterName Then For Each p1 In Printers If p1.DeviceName = _ LongPrinterName Then Set Printer = p1 End If Next End If On Error Resume Next ' Transfer settings from the Devmode structure to the ' VB printer object (this example just transfers some ' of them but you can of course use transfer more) Printer.Copies = DevMode.dmCopies Printer.Duplex = DevMode.dmDuplex Printer.Orientation = DevMode.dmOrientation Printer.PaperSize = DevMode.dmPaperSize Printer.PrintQuality = DevMode.dmPrintQuality Printer.ColorMode = DevMode.dmColor Printer.PaperBin = DevMode.dmDefaultSource SetBkMode Printer.hdc, TRANSPARENT ' On Error GoTo 0 Else SelectPrinter = False ' user cancelled For Each p1 In Printers If p1.DeviceName = OriginalPrinter Then Set Printer = p1 Exit For End If Next GlobalFree PrintDlg.hDevNames GlobalFree PrintDlg.hDevMode End If End Function Private Sub Command1_Click() ' Note: Specifying Printer.DeviceName in the following ' line will start the dialog off with the default ' printer initially selected in the selection box, but ' you can use any other string you wish. For example, ' using "Epson" will cause the dialog to start with ' the first printer it finds with the word "Epson" ' in its device name. If SelectPrinter(Me, Printer.DeviceName) Then Printer.TrackDefault = False Printer.ScaleMode = vbInches ' set origin to top left corner of physical page ' (otherwise it would be the top left corner of ' the printable area, which is not the same) SetPrinterOrigin 0, 0 Printer.CurrentX = 1: Printer.CurrentY = 1 Printer.Print "Hello World" Printer.EndDoc End If End Sub
From: Mike Williams on 25 Feb 2010 12:37 "Mojo" <please(a)dont.spam.com> wrote in message news:um3vclitKHA.4220(a)TK2MSFTNGP05.phx.gbl... > Any good tips without the need to use/register an ocx? One thing I forgot to mention in my previous response is that you should always use error trapping with your printing code, even if you are not doing anything that appears likely to cause an error. In fact I really should have done so in the code I just posted. It is even possible to get a error on the very first Printer.Print statement under certain circumstances, even if the user does actually have printers installed. So there's one tip . . . error trapping ;-) Mike
|
Pages: 1 Prev: Last MSDN applicable to VB6 Next: Open ports / Blocking ports |