News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Windows printing APIs: Ugh, yuck!

Started by NoCforMe, September 26, 2022, 08:19:46 AM

Previous topic - Next topic

NoCforMe

God damn! This code that I just got working is one of the ugliest pieces of shit I've ever had the displeasure of writing.

All I wanted to do was to print a page in landscape mode, automatically, without the user having to select that in the print dialog. Sounds simple, right? How hard could that be?

Well, after copping code from two different examples on MSDN and trying this and trying that, check out what I came up with. In English, leaving out a lot of the details:

1. Call PrintDlgEx(). (This is the easy part, minimal setup.)
2. Call OpenPrinter(), using the printer name from PRINTDLGEX--> DEVNAMES.
3. Call DocumentProperties() THE FIRST TIME with the printer handle (from the call above) and the name. This call only returns how much memory we need to allocate for the next step.
4. Allocate memory (for DEVMODE) using GlobalAlloc() with the # returned above. (The memory must be moveable.)
5. Call DocumentProperties() THE SECOND TIME to get default printer settings.
6. Call GetPrinter(), THE FIRST TIME, again just to get the amount of memory needed (for PRINTER_INFO_2).
7. Call GlobalAlloc() again for that structure.
8. Call GetPrinter() THE SECOND TIME  to do ... I lost track of just what it does, BUT IT DOES SOMETHING VERY IMPORTANT ...
9. Call DocumentProperties() THE THIRD TIME to do ... something necessary ...
10. Call SetPrinter() to (finally?) set those settings.
11. Call ClosePrinter(), because apparently we're done with that part of the mess.

At this point, I call StartDoc() to start the normal print process. Think we're done? Hell no:

12. Call ResetDC() (with the modified DEVMODE) to ... finally, finally set that crap we need ...

Now we can call StartPage(), select stuff into the DC, draw into the DC, call EndPage(), EndDoc(), DeleteDC(), and of course GlobalFree() on those allocations.

That ResetDC()? That wasn't even in the MSDN example; I had to find that in a forum (one of Microsoft's).

Oh, and guess what: that's not the end of it. This code leaves the printer in landscape mode, so I've got to save the original mode and then reset it. I wonder how many calls that'll take ...

Hey, guys and gals up there in Redmond, do you think you could maybe make this process a little more complicated?

Here's my code, if anyone cares:


PrintSomething PROC
LOCAL dmPtr:DWORD, printerHandle:HANDLE, dmHandle:HANDLE, piHandle:HANDLE, printerDevName:DWORD
LOCAL pdex:PRINTDLGEX, dm:DEVMODE, docInfo:DOCINFO, gpRect:RECT, pi2:PRINTER_INFO_2
LOCAL memSizeNeeded:DWORD


; Set up print dialog stuff:
PUSH EDI
LEA EDI, pdex
MOV ECX, SIZEOF PRINTDLGEX
XOR AL, AL
REP STOSB
POP EDI

MOV pdex.lStructSize, SIZEOF PRINTDLGEX
MOV EAX, DialogHandle
MOV pdex.hwndOwner, EAX
MOV pdex.Flags, PD_RETURNDC OR PD_NOPAGENUMS
MOV pdex.nCopies, 1
MOV pdex.nStartPage, START_PAGE_GENERAL

INVOKE PrintDlgEx, ADDR pdex

; Check result of dialog:
CMP EAX, S_OK
JE @F
exit88: XOR EAX, EAX
RET

@@: CMP pdex.dwResultAction, PD_RESULT_PRINT
JNE exit88

; Open the printer, using name from DEVNAMES:
MOV EDX, pdex.hDevNames
MOV EDX, [EDX] ;Dereference.
MOV ECX, EDX
MOVZX EAX, [EDX].DEVNAMES.wDeviceOffset
ADD EAX, ECX
MOV printerDevName, EAX
INVOKE OpenPrinter, printerDevName, ADDR printerHandle, NULL

; Find out how much memory we need to allocate for DEVMODE:
INVOKE DocumentProperties, NULL, printerHandle, printerDevName, NULL, NULL, 0

; Allocate moveable memory for DEVMODE:
INVOKE GlobalAlloc, GMEM_MOVEABLE, EAX
MOV dmHandle, EAX

; Get default DEVMODE info:
INVOKE DocumentProperties, NULL, printerHandle, printerDevName, dmHandle, NULL, DM_OUT_BUFFER

; INVOKE DocumentProperties, DialogHandle, printerHandle, printerDevName,
; dmHandle, dmHandle, DM_IN_BUFFER or DM_OUT_BUFFER

INVOKE GetPrinter, printerHandle, 2, 0, 0, ADDR memSizeNeeded
INVOKE GlobalAlloc, GMEM_MOVEABLE, memSizeNeeded
MOV piHandle, EAX
MOV EDX, EAX
MOV [EDX].PRINTER_INFO_2.pSecurityDescriptor, NULL

INVOKE GetPrinter, printerHandle, 2, piHandle, memSizeNeeded, ADDR memSizeNeeded

MOV EDX, piHandle
MOV EDX, [EDX].PRINTER_INFO_2.pDevMode

MOV [EDX].DEVMODE.dmOrientation, DMORIENT_LANDSCAPE
OR [EDX].DEVMODE.dmFields, DM_ORIENTATION

INVOKE DocumentProperties, NULL, printerHandle, printerDevName,
EDX, EDX, DM_IN_BUFFER or DM_OUT_BUFFER

INVOKE SetPrinter, printerHandle, 2, piHandle, 0

INVOKE ClosePrinter, printerHandle

; Try to print something:

; Set up DOCINFO structure:
MOV docInfo.cbSize, SIZEOF DOCINFO
MOV EAX, OFFSET DocName
MOV docInfo.lpszDocName, EAX
MOV docInfo.lpszOutput, NULL
MOV docInfo.lpszDatatype, NULL
MOV docInfo.fwType, 0
INVOKE StartDoc, pdex.hDC, ADDR docInfo

MOV EDX, piHandle
INVOKE ResetDC, pdex.hDC, [EDX].PRINTER_INFO_2.pDevMode

INVOKE StartPage, pdex.hDC

INVOKE GetStockObject, BLACK_PEN
INVOKE SelectObject, pdex.hDC, EAX

MOV gpRect.top, 1200
MOV gpRect.bottom, 1600
MOV gpRect.left, 1200
MOV gpRect.right, 2000

INVOKE DrawText, pdex.hDC, OFFSET TestText, -1, ADDR gpRect, DT_LEFT or DT_TOP or DT_SINGLELINE
INVOKE EndPage, pdex.hDC
INVOKE EndDoc, pdex.hDC
INVOKE DeleteDC, pdex.hDC

INVOKE GlobalFree, dmHandle
INVOKE GlobalFree, piHandle

RET

PrintSomething ENDP
Assembly language programming should be fun. That's why I do it.

hutch--

David,

Its been a long time since I have written anything for paper printers but it can be done using a rich edit control.

NoCforMe

No, this is just for "freehand" printing, images and text. (Project is a CD jewel-case printer. Remember those? Like the old CD Stomper, which I still have. Prints nice labels, tray cards and covers for CDs.)

Yes, RichEdit printing is its own can of worms (mostly accomplished through the magic of EM_FORMATRANGE), but it's nowhere near as complicated as this.
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: NoCforMe on September 26, 2022, 09:14:09 AM
No, this is just for "freehand" printing, images and text.

Compliments that you got it running :thumbsup:

NoCforMe

Just a note that it turned out that I didn't even need to set landscape mode, so this stuff will go into the archives.

I don't recommend trying to use this code; somehow it set landscape mode as the default for the printer, and that didn't go away until I restarted the computah. Unintended side effect.
Assembly language programming should be fun. That's why I do it.