From: Mike Williams on
"M Wade" <nowhere(a)columbus.rr.com> wrote in message
news:4C190FCC.8090905(a)columbus.rr.com...

> I am not very clear on using APIs and haven't the
> foggiest idea what the reference to DC is, but I
> am going to see if I can understand the code you
> supplied.

If you have the time to study the code then of course it is often a
rewarding thing to do, but if you don't have the time to do so, or if there
are other more important things for you to be getting on with, then that's
fine as well. Essentially the bulk of the code can be viewed as simply a
"black box" that performs a specific task for you.

You do really need to study the code in the Command Button Click Event
though, which is the part that shows you how to call the two routines in
order to allow the user to select the printer and to position your output
accurately on the page. Post again if you have any questions on that part,
or in fact on any other part of the code. By the way, I forgot to mentioned
that you should paste the code into a VB Form containing a Command Button
(although I'm sure you've already done that by now).

Mike




From: Shotgun Thom on
Marv:

The Microsoft DLL that is a replacement for the common dialog ocx that
Mike mentioned earlier is named vbprndlg.dll.

It can be found here: http://support.microsoft.com/kb/322710

It's a pretty solid wrapper for the API. It may be worth taking a
look at as well. It will return your duplex option.

Mike:

The biggest problem I've had with the vpprndlg.dll, or for that fact,
API code is for special printer options. I have a client that has a
Konica C350 by Minolta. It not only collates but can staple, punch 2
or 3 ring binder holes and makes you a cup of expresso while you
wait.. It's one of those we can do everything printers.

I have never been able to figure out how to set these esoteric options
other than right clicking on the printer icon and setting the printer
settings for a particular job that requires them and then resetting
those properties when finished. I know it can be done because even
simple Notepad can set these properties through the printers
properties setup for just the current print job without having to go
back and reset them. I just don't have a clue on how to do it in VB.

Tom

From: Mike Williams on
"Shotgun Thom" <tmoran4511(a)gmail.com> wrote in message
news:c9670b3a-076a-4d2b-8d6f-4b7dcdcabc24(a)z15g2000prh.googlegroups.com...

> Mike: The biggest problem I've had with the vpprndlg.dll,
> or for that fact, API code is for special printer options. I
> have a client that has a Konica C350 by Minolta. It not only
> collates but can staple, punch 2 or 3 ring binder holes and
> makes you a cup of expresso while you wait.. It's one of
> those we can do everything printers. I have never been able
> to figure out how to set these esoteric options other than right
> clicking on the printer icon and setting the printer settings for
> a particular job that requires them and then resetting those
> properties when finished.

When you use the MS vbprndlg DLL or any other equivalent method (API dialogs
or the CommonDialog control) in a way that transfers the user's settings
from the dialog to the VB Printer Object then you simply cannot get access
to all possible printer settings, especially those esoteric settings that I
mentioned myself in my response to the OP, the printiva "use metallic gold
ribbon", and the other esoteric settings you have mentioned, the "punch 3
ring binder holes and make a cup of expresso while you're waiting" stuff ;-)
That's partly because the VB Printer Object cannot "eat" such settings and
partly because Windows itself does not really know about such things (at
least not in detail in a standard Windows way). The details of such esoteric
settings are of course stored in Devmode Extras, but as far as Windows
itself is concerned some of that information is "just a bunch of numbers"
and many of them are known and understood in detail only by the printer
driver itself. You can however give your user access to all those settings,
but you cannot do so in a way that allows you to use the VB Printer object
for your printing, at least not without resorting to the kind of methods you
have mentioned, such as requiring the user to right click the printer and
change its default settings and then change them back again afterwards.

In order to get at all that stuff in a way that does not require you to mess
about with right clicking or changing default settings, you need to present
a printer dialog to the user in the normal way but you need to ask the
dialog to return a DC for you. That returned DC will contain all of the
things that the user has selected in the printer dialog, the printer itself
and all the user's settings, including all those esoteric settings, and you
can then draw and print stuff to that DC in much the same way as you would
draw and print things to any DC. This of course means that you cannot use
the VB Printer Object and you must instead use the Windows StartDoc, EndDoc
etc methods to create the document and use the standard TextOut and Drawtext
and StretchBlt etc methods for drawing text and lines and boxes and images,
etc. You can use whichever method you personally prefer for showing the
dialog. Here is an example in which I have used the standard CommonDialog
Control. As is the case with just about everything I write, it is not (and
never will be) a "finished product" because I am just a hobbyist and I
usually take things only as far as I need to in order to convince myself
that I can do something. It should give you a good start though.

Paste the following code into a VB Form containing a CommonDialog Control, a
Command Button and an Image Control. For test purposes set the Image
Control's Stretch property to True (so that you can see the whole image and
know that it prints properly) and set its Picture property to a picture that
exists on your own machine. The example prints a couple of rectangles and a
picture and some text, using two pages. Since it is just test code it makes
no attempt to maintain the original aspect ratio of the picture when
printing it, but you can of course easily add code to do that.

Mike

Option Explicit
Private Declare Function StartDoc Lib "gdi32" Alias _
"StartDocA" (ByVal hdc As Long, lpdi As DOCINFO) As Long
Private Declare Function EndDoc Lib "gdi32" _
(ByVal hdc As Long) As Long
Private Declare Function StartPage Lib "gdi32" _
(ByVal hdc As Long) As Long
Private Declare Function EndPage Lib "gdi32" _
(ByVal hdc As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" _
(ByVal hdc As Long) As Long
Private Declare Function CreateFontIndirect Lib "gdi32" _
Alias "CreateFontIndirectA" (lpLogFont As LOGFONT) As Long
Private Declare Function SelectObject Lib "gdi32" _
(ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function CreatePen Lib "gdi32" _
(ByVal nPenStyle As Long, ByVal nWidth As Long, _
ByVal crColor As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" _
(ByVal hObject As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" _
(ByVal hdc As Long) As Long
Private Declare Function StretchBlt Lib "gdi32" _
(ByVal hdc As Long, _
ByVal x As Long, ByVal y As Long, _
ByVal nWidth As Long, ByVal nHeight As Long, _
ByVal hSrcDC As Long, ByVal xSrc As Long, _
ByVal ySrc As Long, _
ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, _
ByVal dwRop As Long) As Long
Private Declare Function SetStretchBltMode Lib "gdi32" _
(ByVal hdc As Long, ByVal nStretchMode As Long) As Long
Private Declare Function TextOut Lib "gdi32" Alias _
"TextOutA" (ByVal hdc As Long, ByVal x As Long, ByVal _
y As Long, ByVal lpString As String, ByVal nCount _
As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" _
(ByVal hdc As Long, ByVal nindex As Long) As Long
Private Declare Function SetBkMode Lib "gdi32" _
(ByVal hdc As Long, ByVal nBkMode As Long) As Long
Private Declare Function Rectangle Lib "gdi32" _
(ByVal hdc As Long, ByVal x1 As Long, ByVal y1 As Long, _
ByVal x2 As Long, ByVal y2 As Long) As Long
Private Const LF_FACESIZE As Long = 32&
Private Const LOGPIXELSX As Long = 88&
Private Const LOGPIXELSY As Long = 90&
Private Const PHYSICALOFFSETX As Long = 112&
Private Const PHYSICALOFFSETY As Long = 113&
Private Const PHYSICALWIDTH As Long = 110&
Private Const PHYSICALHEIGHT As Long = 111&
Private Const POINTSPERINCH As Long = 72&
Private Const NORMAL As Long = 400&
Private Const BOLD As Long = 700&
Private Const TRANSPARENT As Long = 1&
Private Const OPAQUE As Long = 2&
Private Const STRETCH_ANDSCANS As Long = 1&
Private Const STRETCH_ORSCANS As Long = 2&
Private Const STRETCH_DELETESCANS As Long = 3&
Private Const STRETCH_HALFTONE As Long = 4&
Private Const PS_SOLID As Long = 0&
Private Type DOCINFO
cbSize As Long
lpszDocName As String
lpszOutput As String
lpszDatatype As String
fwType As Long
End Type
Private Type LOGFONT
lfHeight As Long
lfWidth As Long
lfEscapement As Long
lfOrientation As Long
lfWeight As Long
lfItalic As Byte
lfUnderline As Byte
lfStrikeOut As Byte
lfCharSet As Byte
lfOutPrecision As Byte
lfClipPrecision As Byte
lfQuality As Byte
lfPitchAndFamily As Byte
lfFaceName As String * LF_FACESIZE
End Type
Private Type PrinterInfo
Handle As Long
PixPerInchX As Long
PixPerInchY As Long
OffsetX As Long
OffsetY As Long
PageWidthInches As Single
PageHeightInches As Single
End Type
Private MyPrinter As PrinterInfo
Private UserCancelled As Boolean
Private LF1 As LOGFONT
Private oldFont As Long, oldPen As Long
Private myFont1 As Long, myFont2 As Long, myFont3 As Long
Private myPen1 As Long, myPen2 As Long

Private Sub GetMyPrinter()
UserCancelled = False
CommonDialog1.PrinterDefault = False
CommonDialog1.CancelError = True
' The following cdlPDReturnDC flag is essential, but
' you can combine it with other flags if you wish
CommonDialog1.Flags = cdlPDReturnDC
On Error GoTo dlgError
CommonDialog1.ShowPrinter
On Error GoTo 0
MyPrinter.Handle = CommonDialog1.hdc
MyPrinter.PixPerInchX = GetDeviceCaps _
(MyPrinter.Handle, LOGPIXELSX)
MyPrinter.PixPerInchY = GetDeviceCaps _
(MyPrinter.Handle, LOGPIXELSY)
MyPrinter.OffsetX = GetDeviceCaps _
(MyPrinter.Handle, PHYSICALOFFSETX)
MyPrinter.OffsetY = GetDeviceCaps _
(MyPrinter.Handle, PHYSICALOFFSETY)
MyPrinter.PageWidthInches = CSng(GetDeviceCaps _
(MyPrinter.Handle, PHYSICALWIDTH)) / _
MyPrinter.PixPerInchX
MyPrinter.PageHeightInches = CSng(GetDeviceCaps _
(MyPrinter.Handle, PHYSICALHEIGHT)) / _
MyPrinter.PixPerInchY
' Set up a few fonts that can be easily used
' throughout the printing code (this is just the
' way I personally prefer to do it rather than
' create and destroy a font each time I use one)
With LF1
.lfFaceName = "Times New Roman" + Chr$(0)
' 12 point standard
.lfEscapement = 0
.lfHeight = 12 * (-MyPrinter.PixPerInchY / POINTSPERINCH)
.lfWeight = NORMAL
End With
myFont1 = CreateFontIndirect(LF1)
With LF1
.lfFaceName = "Times New Roman" + Chr$(0)
' 16 point standard
.lfEscapement = 0
.lfHeight = 16 * (-MyPrinter.PixPerInchY / POINTSPERINCH)
.lfWeight = NORMAL
End With
myFont2 = CreateFontIndirect(LF1)
With LF1
.lfFaceName = "Times New Roman" + Chr$(0)
' 12 point rotated by 90 degrees
.lfEscapement = 900
.lfHeight = 12 * (-MyPrinter.PixPerInchY / POINTSPERINCH)
.lfWeight = NORMAL
End With
myFont3 = CreateFontIndirect(LF1)
' store the old font handle and set the initial font to myFont1
oldFont = SelectObject(MyPrinter.Handle, myFont1)
' set up a couple of black pens (0.01 inches and 0.025 inches)
myPen1 = CreatePen(PS_SOLID, MyPrinter.PixPerInchX * 0.01, vbBlack)
myPen2 = CreatePen(PS_SOLID, MyPrinter.PixPerInchX * 0.025, vbBlack)
' store the old pen handle and set the initial pen to myPen1
oldPen = SelectObject(MyPrinter.Handle, myPen1)
SetBkMode MyPrinter.Handle, TRANSPARENT ' FontTransparent
' Set the stretch mode to the desired mode
SetStretchBltMode MyPrinter.Handle, STRETCH_DELETESCANS
'SetStretchBltMode MyPrinter.Handle, STRETCH_HALFTONE
Exit Sub
dlgError:
UserCancelled = True
End Sub

Private Sub TextPrint(s1 As String, x As Single, y As Single)
Dim xpos As Long, ypos As Long
xpos = x * MyPrinter.PixPerInchX - MyPrinter.OffsetX
ypos = y * MyPrinter.PixPerInchY - MyPrinter.OffsetY
TextOut MyPrinter.Handle, xpos, ypos, s1, Len(s1)
End Sub

Private Sub RectPrint(x1 As Single, y1 As Single, _
x2 As Single, y2 As Single)
Rectangle MyPrinter.Handle, _
x1 * MyPrinter.PixPerInchX - MyPrinter.OffsetX, _
y1 * MyPrinter.PixPerInchY - MyPrinter.OffsetY, _
x2 * MyPrinter.PixPerInchX - MyPrinter.OffsetX, _
y2 * MyPrinter.PixPerInchY - MyPrinter.OffsetY
End Sub

Private Function PrintImage(p1 As StdPicture, _
x As Single, y As Single, wide As Single, _
high As Single) As Boolean
Dim hOldBitmap As Long, hMemoryDC As Long
hMemoryDC = CreateCompatibleDC(Me.hdc)
hOldBitmap = SelectObject(hMemoryDC, p1.Handle)
' Note: I use StretchBlt here because it is what
' I normally use for the printing of various
' images, but you could probably use the
' Render method instead if you wish
StretchBlt MyPrinter.Handle, _
x * MyPrinter.PixPerInchX - MyPrinter.OffsetX, _
y * MyPrinter.PixPerInchY - MyPrinter.OffsetY, _
wide * MyPrinter.PixPerInchX, _
high * MyPrinter.PixPerInchY, _
hMemoryDC, _
0, 0, _
Me.ScaleX(p1.Width, vbHimetric, vbPixels), _
Me.ScaleY(p1.Height, vbHimetric, vbPixels), _
vbSrcCopy
hOldBitmap = SelectObject(hMemoryDC, hOldBitmap)
DeleteDC hMemoryDC
End Function

Private Sub Command1_Click()
Dim lret As Long, s1 As String, docinf As DOCINFO
' Display a printer dialog and set up a printer DC
' in accordance with the user's selection and set
' up a few fonts and a couple of pens
GetMyPrinter
' Exit if the user pressed Cancel in the dialog
If UserCancelled Then
Exit Sub
End If
' start a document
docinf.cbSize = 20 ' Size of DOCINFO structure
lret = StartDoc(MyPrinter.Handle, docinf) 'Start document
If lret < 0 Then
MsgBox "Failed to start document"
Exit Sub
End If
lret = StartPage(MyPrinter.Handle) 'Start a new page
If lret < 0 Then
MsgBox "Failed to start page!"
EndDoc MyPrinter.Handle
Exit Sub
End If
' print a rectangle using myPen1 with an exact one
' inch all round border
SelectObject MyPrinter.Handle, myPen1
RectPrint 1, 1, MyPrinter.PageWidthInches - 1, _
MyPrinter.PageHeightInches - 1
' print a rectangle just inside it using the thicker myPen2
SelectObject MyPrinter.Handle, myPen2
RectPrint 1.1, 1.1, MyPrinter.PageWidthInches - 1.1, _
MyPrinter.PageHeightInches - 1.1
' print an image at location 1.2,1.2 at size 3 x 2 inches
PrintImage Image1.Picture, 1.2, 1.2, 3, 2
' print some text using myFont1
SelectObject MyPrinter.Handle, myFont1
s1 = "This is some text on page ONE "
TextPrint s1, 1.2, 1.2 ' print some text (positions in inches)
' print some more text using myFont2
SelectObject MyPrinter.Handle, myFont2
s1 = "This should be in myFont2, which we have set to 16 points"
TextPrint s1, 1.2, 2
' print some m ore text using myFont3
SelectObject MyPrinter.Handle, myFont3
s1 = "And this should be 12 points rotated at 90 degrees."
TextPrint s1, 1.2, 7
lret = EndPage(MyPrinter.Handle) 'End the page
lret = StartPage(MyPrinter.Handle) ' start a new page
' now change back to myFont1
SelectObject MyPrinter.Handle, myFont1
s1 = "And this is on page Two"
TextPrint s1, 1, 1
lret = EndPage(MyPrinter.Handle) 'End the page
lret = EndDoc(MyPrinter.Handle) 'End the document
' Reset font back to original and delete font we created
SelectObject MyPrinter.Handle, oldFont
DeleteObject myFont1 'Delete the fonts
DeleteObject myFont2
DeleteObject myFont3
SelectObject MyPrinter.Handle, oldPen
DeleteObject myPen1 ' Delete the pens
DeleteObject myPen2
' Delete the printer DC
DeleteDC MyPrinter.Handle
End Sub





From: Shotgun Thom on
Thanks, Mike. I see what you're doing. This will be helpful in
future projects and I thank you.

The problem with the Konica client is that we are using the TxText
control, which has two internal print routines. A print page method
and a print doc method. When calling these methods we actually do
pass the Printer.hDC to the control. However, it seems to have no
impact on the settings of those special print options. Still have to
change the defaults directly in the printer and then set it back
afterwards for it to take effect. I really don't like doing it that
way.

BTW, one danger of doing it this way is duplicate settings. You'll
love this. The Konica allows you to select the number of copies to
print directly in it's setup program. He needed 44 copies and set the
copies option to 44. After clicking OK he noticed the copies number
in the command dialog box said 1 so he changed it to 44. Hit print...
and you guessed it.... he got 44 sets of 44 copies. Cleaned out the
paper bins before he realized it. :)

Thanks, again, Mike!

Tom

From: Mike Williams on
"Shotgun Thom" <tmoran4511(a)gmail.com> wrote in message
news:8ddc34e9-af22-43f5-85cb-13feac34ffcb(a)x27g2000prf.googlegroups.com...

> Thanks, Mike. I see what you're doing. This will be
> helpful in future projects and I thank you.

You're welcome.

> The problem with the Konica client is that we are using the
> TxText control, which has two internal print routines. A
> print page method and a print doc method. When calling
> these methods we actually do pass the Printer.hDC to the
> control. However, it seems to have no impact on the settings
> of those special print options. Still have to change the defaults
> directly in the printer and then set it back afterwards for it to
> take effect. I really don't like doing it that way.

Well it won't succeed if you are passing the VB Printer Object's hDC (as is
implied in your statement, "we pass the Printer.hDC") because the VB Printer
Object has not been set up to deal with (and actually cannot be set up to
deal with) those special print options. I've never heard of the TxText
control and therefore have no knowledge of how it fits into your program,
but you will have much more chance of success if you use the code I posted
to show the printer dialog to the user in such a way that it returns a DC
from the dialog (exactly as it does in the code I posted) and if you pass
/the returned hDC/ to the TxText control.

> BTW, one danger of doing it this way is duplicate settings. You'll
> love this. The Konica allows you to select the number of copies
> to print directly in it's setup program. He needed 44 copies and
> set the copies option to 44. After clicking OK he noticed the copies
> number in the command dialog box said 1 so he changed it to 44.
> Hit print...and you guessed it.... he got 44 sets of 44 copies.
> Cleaned out the paper bins before he realized it. :)

That's a good one! I don't know how the Konica deals with such things, nor
from your brief explanation do I know the full details of how this actually
occurred (perhaps the TxText Control you are using and the Konica itself
ended up /both/ being set for 44 copies, and I'm sure there is a way of
making sure it does not happen like that again) but it is interesting
nonetheless. Maybe under such circumstances you could hang the surplus 1892
pages on the back of the toilet door (or do they perhaps call them something
other than toilets on your side of the big pond?). At least they'd be of
some use there ;-) You'd need to alter your code so that it does /not/ do
duplex printing though. I think for such a use at least one side of the page
should have no ink on it ;-)

> Thanks, again, Mike!

You're welcome.

Mike