News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

PrintWindow API support

Started by loki_dre, June 13, 2008, 10:28:38 PM

Previous topic - Next topic

loki_dre

Is the PrintWindow function currently supported in MASM32?
http://msdn.microsoft.com/en-us/library/ms535695.aspx

Note: I had to install a newer version of the .NET Framework to get it running with C# code
http://www.codeproject.com/KB/cs/CapturingMinimizedWindow.aspx

I have included:
include \masm32\include\masm32rt.inc


evlncrn8

well its a windows api, so all you need to do is update the include file if it isn't already there
or use loadlibrary->GetProcAddress to use it...

PBrennick

Quote
Printing Text in Win32

Printing text in Win32 can be a little complicated, but isn't quite as hard as some sources would have you believe. Although if your text has different colors, fonts, and sizes embedded, it can be a little more complicated, generally, It's a pretty simple matter.

First, of course, you'll have to obtain a Printer DC. This can be done using the Print Dialog - described in detail in Common Dialog Box Library - or using GetPrinterDC, bypassing the user entirely. On return, the Print Dialog has placed the printer's DC into the hDC member. This DC will be appropriate to the printer picked by the user. On the other hand, GetPrinterDC returns the DC for the system default printer. If you use this, but still want the user to supply the number of pages and so forth, then you'll have to obtain that information using a dialog, yourself. The Print Dialog returns that info in the PRINTDLG structure. If you want to present the user with all of the options as pertains to the current document, then you'll have to obtain those specs, and pop them into the PRINTDLG structure before calling it up for the user. To have the dialog obtain the various handles needed without having to go through all the steps yourself, then include the PD_RETURNDEFAULT flag in the PRINTDLG structure, and the dialog will not display, but still gather the information. Either way, you'll then have to parse out and use any specifications the user presented. Once those details have been attended to, it's time to set up for printing.

First off, it's best now to determine things such as characters per line, lines per page, and the line spacing to use for proper presentation with all printers. I find it very effective to use GetTextMetrics on the Printer DC, to get the average height of characters of the current font. This I place into a variable for incrementing the vertical position for TextOut. Using this method, I find that my line count - how many lines per page - can be the same for any printer. If the user selected Legal paper, then you can adjust the line count appropriately (get that info from PRINTDLG if used.) As well, you can use that figure for checking to see if any line of text will fall below, or partly below, the last DC line, by subtracting the height of the text from the last line of the DC, and saving that number as a comparison variable. Then, before shooting TextOut, you can see if the Y value of the location is past that minimum value. This prevents clipping the bottom of a line. Believe it or not, I find this method to be totally satisfactory in almost every printing job I program.

Alternatively, you can use GetDeviceCaps for the printer DC, as well as for the display, then divide the display value into the printer value, to obtain a ratio-per-pixel value, which you can then use to set the incremental values.You may very well want to do this for graphics, to scale the output if need be. Next, you can then calculate the lines per page by dividing the result of GetTextMetrics into the overall resolution of the printer, which you can get using GetDeviceCaps and calling up the VERTRES value (you can also use the HORZRES value for line lengths). If line lengths are not important, then you can skip that rigamarole, but if they are, you can use the information from GetTextMetrics and GetDeviceCaps, do the division, and arrive at a "characters per line" figure. Naturally, this will be important if you will be sending wider-than-normal characters, or perhaps mixed sizes in a line. Otherwise, you can use the height of that character to assume that so many characters will fit on a line, and leave it at that.

Once these variables are set, you can calculate the number of pages, if this is important, by dividing the total number of lines - if known - to be printed, by the number of lines per page, adding one page for any remainder of the division. Generally, if you're printing from an edit control or listbox, or similar control, you'll be able to easily get the total number of lines. As well, if the user has selected a certain page number to start and/or end printing at, then a variable for each of these can be set and checked as printing progresses. For page numbering, I just increment a counter every time StartPage is hit, converting and placing the page number on the page. To "skip" pages until a desired one is reached, I just jump to a little proc that loads the strings into a "dead" buffer until the right number has elapsed, then return to the printing proc and pick up sending to a fresh page.

As far as the AbortProc is concerned, if you're not going to actually deal with a "real" proc, then this little gem will serve:

AbortProc proc  pdc:DWORD, error:DWORD
;---------------------------------------
        return  TRUE
;---------------------------------------
AbortProc endp

Yep, it's enough to satisfy Windows. However, if you're supplying an Abort window, then create a modeless dialog and point it's proc at this one. If the user presses the Abort button, set a variable - AbortDoc would be a good name, you think? - and check it each time before hitting StartPage. Actually, you could just as well use a modal dialog, but some consider that rude to tie up the machine until the whole thing's sent to the spooler. And, remember to destroy the dialog using DestroyWindow if it's modeless. In either case, use SetAbortProc to set up, and then, finally and at last, you can use StartDoc.

Once all of the calculations are done, then you can finally load a DOCINFO structure, and invoke StartDoc. The structure only need have it's size set and the lpszOutput member set to NULL (this member contains a null-terminated filename string if printing to a file). If you want to give the document a name (the one you'd see if you clicked up the Print Job box), put it's address into the pDocNamemember. Once done, invoke StartDoc and the race is on!

Here's where your first error checking can be done, if you want to do it, because StartDoc will return a value greater than zero if it's successful. This return value is also useful if you're wanting to set a priority for the print job, such as on a networked printer. You can, by the way, start the print job using only StartPage/EndPage, but the print job can end up interspersed with other jobs when printing.

Assuming success in StartDoc, you can then do any setup for the first page, and hit the inner loop, StartPage. Do any mapping mode changes and object selecting here, because you'll need to every time you hit StartPage. Again, success returns greater than zero. Once inside this tag, you stay there until that page is entirely sent to the printer DC. The functions within can include any of the GDI stuff, so long as you write to the page. Once done with that page - usually when incrementing a line counter and it hits your line number maximum - you catch EndPage. The DC is not actually sent to the Spooler until EndPage is sent, so you can tinker with the image in the DC all you like until sending that command. Again, an error check could be done here, although I fail to see any purpose for it other than to fall through to EndDoc.

Now, here's where you'll want to do your checking to see if you're printing another page, and if so, head back up to StartPage. If you have set a different mapping mode and selected objects into the printer DC, you have to do it again. In NT, it's not necessary, but it doesn't hurt to do it anyway, for compatibility.

If no more pages, just invoke EndDoc, delete the printer DC, and you're all done, folks.

Now for some nasty little details: generally speaking, any character you send to the printer will end up being printed - so this means no linefeeds, carriage returns, or any other such thing. Your proc, including any wordbreak proc, will have to parse them out as it goes. Depending on the type of doc you're printing, there can be quite a set of rules for printable characters. Retrieveing text from a list box or some such makes for no real problems, because you can get the text length of the item which does not include a terminator, and use that to shoot it to the printer DC.

As passed over above, you may also need to check a line for larger fonts, colors, and such before formatting it to send into the DC. If you saved off the horizontal resolution of the printer, then you can use GetTextExtentPoint32 to return the required size. Once you have picked up each section and subtracted it from the total available, you can then parse the line for a break, if need be, and then send that out. Changes to the colors and sizes can be made "as you go", so long as you calculated everything before sending it to the DC. To clarify a little, let's assume you got a horizontal resolution of 2700 for the printer, which is roughly 3-1/2 times that of the display. The text metrics for the selected font tell you the average character width, so you can divide that into the resolution and arrive at a characters per line range. If the line exceeds that, then you'll need to parse back to a line break below the value, and send just that out. This, of course, will also result in resetting your pointer to where you're picking up the text, so the next line won't skip the passed-over characters. Processing any formatting commands will depend on the individual file, of course, but in general, plain text is very simple. Once you're satisfied with the number of characters to be sent, then shoot them to the DC and get onto the next line.

Incrementing lines can be a little tricky, depending on what you're sending into the DC. As with any graphics environment, everything is done in terms of pixels or device units. For the display, of course, you deal with pixels; for a printer, it's dots. Basically - very basically - a printer has many more dots than the display, as discussed above. Every printer has a different resolution, it seems, making it necessary to provide some sort of proc to determine the line lengths, spacing, and so forth. For instance, I have two main printers - a Lexmark Z55, and an HP 712C. The Lexmark has a vertical resolution of over 3683 dots, while the HP has 3150. This makes for totally different ratios, as you can see. Of course, this also represents the printable area, which doesn't include margins, which you implement yourself. So, if you want to keep working in pixels, you simply have to obtain a conversion factor, set that up as the basic unit, then use that unit to adjust your pointer into the printer DC. For instance, once you obtain a multiplied value for the left margin and top margin, when going through the loop of StartPage, just set those values into your POINT structure, and that's where the first line goes. All adjustments to your pointer from then on will use the starting X factor for the beginning of a line, and make life far simpler for you.

There is a bit simpler way to format out and get disparate elements all formulated into a line of print and sent to the DC properly, which I have discovered to be the least-cumbersome method. First off, I designate a buffer large enough, at least, to hold all of the characters likely to fill a line in the printout. Next, I fill the buffer with spaces, then plaster the separate data items - such as columnar data with each item coming from a separate place - making sure no terminating zeroes are put in, except on the last item. Then I use invoke lstrlen, (buffer name) to determine the string length, then use TextOut to shoot it to the DC. If all of this data is shot using the same font (size and weight), then all of the stuff will fall right where you want it to.For dissimilar items, you'll have to keep track of actual pixel counts if you want to have that precision. DrawText prints right into a bitmap, so you may have to use the graphics method to print complex things, involving graphics elements as well as text.

As a little interesting (to me, of course, it was a headache) fact is that the pixel - to - display ratio is a funny thing. For instance, when I set a font to a height of 36 (lf.lfHeight) and sent it to my 200 DPI Lexmark printer, the text came out at just over 1/64 inch tall. Perfectly printed, I might add, but just a hair small for the recipient to read. To calculate relative sizes, I first use LOGFONT to set up a font and place zero in lf.lfHeight, to obtain a default size. This usually results in about a 12-point size. Then I retrieve the value of lfHeight, multiply it by how many itmes larger I want the current font, and plug that back in, use CreateFontIndirect again, then select the new font into the DC. Otherwise, you can use GetTextMetrics and retrieve the tmHeight member, which will give you the default size, as above. You can then use this to multiply (or divide) to achieve proportional sizes within your text. You can also directly manipulate the values in the TEXTMETRIC structure - but you had better be prepared for some mighty strange things when you don't get it just so.

You might not be shooting stuff from a listbox or somesuch, but instead need to arrange individual data items in a columnar format. Trying any other method of "pixelization" can be tricky and might not work right anyway, so for that, use TabbedTextOut. Since when shooting text to a DC, we don't get the convenience of using, say, a columnar listbox, we have to use another method to line everything up. The actual format of the command is

invoke  TabbedTextOut, hdc, start X pos, start Y pos, ADDR (buffer), length of string, # of elements in the tab array, ADDR of the tabs array, origin of tab start

The positional members are self-explanatory. Since we should probably be using (I do) a POINT structure for the starting X and Y positions (I name it cpos, so in data it's cpos POINT<>), we should have those numbers available. So, the first one would be cpos.x and the second would be cpos.y. Next, of course, is the address of the string (we'll cover that shortly), the length of the string (i usually use lstrlen to get that), and then we get to the tabs array.

The single most simple method I've managed to devise to set the tabs positions is to first retrieve the TEXTMETRICS tmAveCharWidth member, then multiply it by however many characters I want each tab position set to. So, if I have four columns, I first decide at what character position I want each column to start at, then retrieve and multiply the tmAveCharWidth member by those amounts, and place each into a tabs array. I have first initialized and gotten the TEXTMETRIC structure after creating the currently selected font. Here's what the tabs array looks like in .data and a sample of placing the tab positions at 0, 24, 48, and 72:

TheTabs dd  0, 0, 0, 0

mov     eax, tm.tmAveCharWidth
push    eax
mov     ecx, 24
mul     ecx
mov     TheTabs[4], eax
pop     eax
push    eax
mov     ecx, 48
mul     ecx
mov     TheTabs[8], eax
pop     eax
mov     ecx, 72
mul     ecx
mov     TheTabs[12], eax

Of course, the tabs array could have as many members as might be used anywhere in our program, but in this case, we would still only use the first four, and tell TabbedTextOut that there's only four. This way, you can reuse the same tab array everywhere in the program.

So, we now have the four TheTabs members set (the first one remains at zero in this example). Next, make absolutely certain to shut off updating of the current position using invoke SetTextAlign,hdc, TA_NOUPDATECP, or else the results will be maybe funny, but certainly not what you intended! Next, you're going to have to format the string from the four data items all into one string, with Tab characters between items, to get this to work. I set up a buffer large enough for the whole finished string, then use lstrcat to place each data item in, place a 9 (tab character), then the next item, and so forth, until all items are placed in the buffer. Then, it's a simple matter - being sure that you've adjusted the X and Y positions for the start of the string properly - to get the string's length, then shoot it using TabbedTextOut like this:

invoke  lstrlen, ADDR TheString
invoke  TabbedTextOut, hdc, cpos.x, cpos.y, ADDR TheString, eax, 4, ADDR TheTabs, cpos.x

This places the string, with tabs expanded, from the starting point cpos.x, running to the right from there. Using this method, the final member - the origin to use for the tabs - is also the X starting point. Works every time. Naturally, if you have any doubt about the particular item(s) fitting into the space available, you may very well have to use GetTextExtentPoint32 to get the total width of the string, then compare it to the HORZRES member of GetDeviceCaps. This little struggle will, of course, depend on the type of stuff you're sending to the printer DC.

So, to try to put it all in a nutshell for you, here are the steps you can take to print text. The required steps are marked with an asterisk; the others can be used if you need them.

1. Obtain the printer DC*
2. Get the DC's horizontal and vertical resolutions
3. Set mapping mode to MM_TEXT, set color(s)
4. Establish a font and select it into the DC
5. Get the DC's TEXTMETRICs and set line spacing accordingly*
6. Use text metrics and horizontal resolution to set maximum characters per line
7. Use text metrics and vertical resolution to set maximum lines per page
8. Initialize a DOCINFO structure*
9. invoke SetAbortProc*
10. invoke StartDoc*
11. invoke StartPage*
12. Reset mapping mode, reselect objects, colors, etc.*
13. Set desired text to DC*
14. invoke EndPage*
15. If more pages, loop back up to (11)
16. invoke EndDoc*
17. Delete printer DC*

For some items, such as number 5, you might have to calculate those things "on the fly", because of the text involved. After resetting the font to the new size, use GetTextMetrics, then set that height into your line spacing. After finishing that line and resetting the new lower-on-the-page starting point, reset the line spacing to whatever font size is being used in that line, and after finishing that line, reset it again to any new font size. You just have to be sure that you reset to the next line down before using a smaller line spacing, because the next line could print right through the middle of the bigger font above (if that is the case). Otherwise, these steps outline the necessary ones in order to print text.

Example Code:


.data
TAB            dd  3
FontTah        db  'Tahoma',0
DatePict       db  "MM'-'dd'-'yyyy",0
TimePict       db  "hh':'mm':'ss tt",0
szPerR         db  ')',0

FontPrint      LOGFONT <-10,0,0,0,600,FALSE,FALSE,FALSE,0,0,0,0,0,'Courier New'>

.data?
InchesOn       dd  ?
PageSize       dd  ?
PrtHeading     dd  ?
PrtColor       dd  ?
PH             dd  ?

.code

;===================================================
; Convert to pixels PROCEDURE
;===================================================
ConvToPix PROC PixelsPerIn:DWORD, VarSize

         mov     eax, PixelsPerIn
      .if InchesOn
           imul     eax, 10

      .else
           imul     eax, 1000
            xor     edx, edx
            mov     ecx, 254
            div     ecx
      .endif
         mov     ecx, eax
         mov     eax, VarSize
        imul     eax, ecx
         xor     edx, edx
         mov     ecx, 10000
         div     ecx
         ret

ConvToPix ENDP

;===================================================
; Print document PROCEDURE
;===================================================
Print PROC uses ebx esi
LOCAL    tm:TEXTMETRIC
LOCAL    lf:LOGFONT
LOCAL    psd:PAGESETUPDLG
LOCAL    pd:PRINTDLG
LOCAL    doci:DOCINFO
LOCAL    pt:POINT
LOCAL    sz:SIZEL
LOCAL    tr:TEXTRANGE
LOCAL    rect:RECT
LOCAL    hREdit:DWORD, hFontHead, hFontPrt, PtrX, PtrY, PaperX, PaperY, mL, mT, mR, mB, CurLine
LOCAL    LinesLeft:DWORD, TabWth, hRgn, PageNum, OffSetX, OffSetY, AvgCharWth, MaxLine, hDC
LOCAL    szBuff0[256]:BYTE, szBuff1[256]:BYTE, TimeBuff[32]:BYTE
LOCAL    PrtBuff[1024*2]:BYTE, PrtWrap[1024*2]:BYTE

      INVOKE     GetFocus
         mov     hREdit, eax

;---------- [Zero out the structures] ----------
      INVOKE     RtlZeroMemory, addr psd, sizeof psd
      INVOKE     RtlZeroMemory, addr pd, sizeof pd
      INVOKE     RtlZeroMemory, addr doci, sizeof doci
      INVOKE     RtlZeroMemory, addr lf, sizeof lf

;---------- [Fill the page setup structure
         mov     psd.lStructSize, sizeof psd
        push     hWnd
         pop     psd.hwndOwner
        push     hInst
         pop     psd.hInstance
         mov     psd.rtMargin.left, 360
         mov     psd.rtMargin.top, 360
         mov     psd.rtMargin.right, 360
         mov     psd.rtMargin.bottom, 516
         mov     psd.ptPaperSize.x, 8500
         mov     psd.ptPaperSize.y, 11000

      INVOKE     GetDC, hREdit
         mov     hDC, eax

;---------- [Get page size
      INVOKE     lstrcpy, addr lf.lfFaceName, addr FontPrint.lfFaceName
      INVOKE     GetDeviceCaps, hDC, LOGPIXELSY
         mov     ecx, FontPrint.lfHeight
         neg     ecx
         mul     ecx
         xor     edx, edx
         mov     ecx, 72
         div     ecx
         neg     eax
         mov     lf.lfHeight, eax
         mov     eax, FontPrint.lfWeight
         mov     lf.lfWeight, eax
      INVOKE     CreateFontIndirect, addr lf
         mov     hFontPrt, eax

      INVOKE     SelectObject, hDC, hFontPrt
      INVOKE     GetDeviceCaps, hDC, LOGPIXELSY
        imul     eax, 10
        push     eax
      INVOKE     GetTextMetrics, hDC, addr tm
         pop     eax
         xor     edx, edx
         mov     ecx, tm.tmHeight
;         add     ecx, tm.tmInternalLeading
;         add     ecx, tm.tmExternalLeading
         div     ecx
      .if edx
              inc     eax
      .endif
         mov     PageSize, eax
      INVOKE     ReleaseDC, hREdit, hDC
      INVOKE     DeleteObject, hFontPrt

      INVOKE     GetUserDefaultLCID
         mov     edx, eax
      INVOKE     GetLocaleInfo, edx, LOCALE_IMEASURE, addr szBuff0, sizeof szBuff0
         mov     InchesOn, 1
      .if byte ptr szBuff0 != '1'
            mov     InchesOn, 0
      .endif

      INVOKE     SendMessage, hREdit, EM_EXGETSEL, 0, addr cr
         mov     pd.lStructSize,  sizeof pd
       MOVmd     pd.hwndOwner, hWnd
       MOVmd     pd.hInstance, hInst
         mov     eax, cr.cpMin
         mov     ecx, hREdit
      .if eax != cr.cpMax || F1SelectOn && ecx == hWndTemp
         INVOKE     SendMessage, hREdit, EM_GETLINECOUNT, 0, 0
            mov     ecx, PageSize
            xor     edx, edx
            div     ecx
         .if edx
               inc     eax
         .endif
            mov     pd.nMinPage, 1
            mov     pd.nMaxPage, ax
            mov     pd.nFromPage, 1
            mov     pd.nToPage, ax
            mov     eax, PD_RETURNDC or PD_SELECTION

      .else
         INVOKE     SendMessage, hREdit, EM_GETLINECOUNT, 0, 0
            mov     ecx, PageSize
            xor     edx, edx
            div     ecx
         .if edx
               inc     eax
         .endif
            mov     pd.nMinPage, 1
            mov     pd.nMaxPage, ax
            mov     pd.nFromPage, 1
            mov     pd.nToPage, ax
            mov     eax, PD_RETURNDC or PD_NOSELECTION or PD_ALLPAGES
      .endif
         mov     pd.Flags, eax
      INVOKE     PrintDlg, addr pd
      .if eax
         .if PH != 1
            INVOKE     GetDateFormat, NULL, NULL, NULL, offset DatePict, addr TimeBuff, 12
               mov     dword ptr TimeBuff[10], '    '
            INVOKE     GetTimeFormat, NULL, TIME_FORCE24HOURFORMAT, NULL, offset TimePict, addr TimeBuff[12], 12
               mov     dword ptr szBuff0, 00202020h
            INVOKE     lstrcat, addr TimeBuff, addr szBuff0
         .endif

         INVOKE     GetDeviceCaps, pd.hDC, PHYSICALOFFSETX ; = device units left/right margin
            mov     OffSetX, eax
         INVOKE     GetDeviceCaps, pd.hDC, PHYSICALOFFSETY ; = device units top/bottom margin
            mov     OffSetY, eax
         INVOKE     GetDeviceCaps, pd.hDC, LOGPIXELSX
            mov     ebx, eax
         INVOKE     ConvToPix, ebx, psd.ptPaperSize.x
            mov     PaperX, eax
         INVOKE     ConvToPix, ebx, psd.rtMargin.left
            mov     mL, eax
         INVOKE     ConvToPix, ebx, psd.rtMargin.right
            mov     mR, eax
         INVOKE     GetDeviceCaps, pd.hDC, LOGPIXELSY
            mov     ebx, eax
         INVOKE     ConvToPix, ebx, psd.ptPaperSize.y
            mov     PaperY, eax
         INVOKE     ConvToPix, ebx, psd.rtMargin.top
            mov     mT, eax
         INVOKE     ConvToPix, ebx, psd.rtMargin.bottom
            mov     mB, eax
            mov     eax, OffSetX
            mov     ecx, OffSetY
            sub     mL, eax
            sub     mT, ecx
            add     mR, eax
            add     mB, ecx
         INVOKE     lstrcpy, addr lf.lfFaceName, addr FontPrint.lfFaceName
         INVOKE     GetDeviceCaps, pd.hDC, LOGPIXELSY
            mov     ecx, FontPrint.lfHeight
            neg     ecx
            mul     ecx
            xor     edx, edx
            mov     ecx, 72
            div     ecx
            neg     eax
            mov     lf.lfHeight, eax
            mov     eax, FontPrint.lfWeight
            mov     lf.lfWeight, eax
         INVOKE     CreateFontIndirect, addr lf
            mov     hFontPrt, eax

         INVOKE     lstrcpy, addr lf.lfFaceName, addr FontTah
            mov     lf.lfHeight, 52
            mov     lf.lfWeight, 600
         INVOKE     CreateFontIndirect, addr lf
            mov     hFontHead, eax

            mov     doci.cbSize, sizeof doci
            mov     doci.lpszDocName, offset AppName
            mov     eax, pd.Flags
            and     eax, PD_PRINTTOFILE
         .if eax
               mov     eax, 'ELIF'
               mov     dword ptr szBuff0, eax
               mov     eax, ':'
               mov     dword ptr szBuff0+4, eax
               lea     eax, szBuff0
               mov     doci.lpszOutput, eax
         .else
               mov     doci.lpszOutput, NULL
         .endif
            mov     doci.lpszDatatype, NULL
            mov     doci.fwType, NULL
         INVOKE     StartDoc, pd.hDC, addr doci
            mov     ecx, hREdit
            mov     eax, pd.Flags
            and     eax, PD_SELECTION
xor     eax, eax
         .if eax
            INVOKE     SendMessage, hREdit, EM_EXLINEFROMCHAR, 0, cr.cpMin
               mov     CurLine, eax
               mov     ecx, PageSize
               xor     edx, edx
               div     ecx
               mov     PageNum, 0 ;eax
            INVOKE     SendMessage, hREdit, EM_EXLINEFROMCHAR, 0, cr.cpMax
               sub     eax, CurLine
            .if sdword ptr eax < 0 || eax == 0
                  mov     eax, 1
            .endif
               mov     LinesLeft, eax
               mov     pd.nToPage, -1
         .else
             movzx     eax, pd.nFromPage
               dec     eax
               mov     PageNum, eax
              imul     eax, PageSize
               mov     CurLine, eax
            INVOKE     SendMessage, hREdit, EM_GETLINECOUNT, 0, 0
                or     eax, eax
                je     AllDone
               mov     LinesLeft, eax
         .endif

            mov     eax, mL
            mov     rect.left, eax
            mov     eax, PaperX
            sub     eax, mR
            mov     rect.right, eax
            mov     eax, mT
            mov     rect.top, eax
            mov     eax, PaperY
            sub     eax, mB
            mov     rect.bottom, eax
         INVOKE     CreateRectRgn, rect.left, rect.top, rect.right, rect.bottom
            mov     hRgn, eax

NextPage:
            inc     PageNum
            mov     eax, PageNum
         .if ax > pd.nToPage
               jmp     AllDone
         .endif
         INVOKE     StartPage, pd.hDC
            mov     eax, mT
            mov     PtrY, eax
         INVOKE     SelectObject, pd.hDC, hFontPrt
         INVOKE     SelectObject, pd.hDC, hRgn

;---------- [Get average character width and max line
            mov     eax, 'W i.'
            mov     dword ptr szBuff0, eax
         INVOKE     GetTextExtentPoint32, pd.hDC, addr szBuff0, 4, addr pt
            mov     eax, pt.x
            shr     eax, 2
            mov     AvgCharWth, eax
            xor     edx, edx
            mov     eax, rect.right
            mov     ecx, AvgCharWth
            div     ecx
            dec     eax
            mov     MaxLine, eax

;---------- [Get tab width
            mov     eax, 'WWWW'
            mov     dword ptr szBuff0, eax
         INVOKE     GetTextExtentPoint32, pd.hDC, addr szBuff0, 4, addr pt
            mov     eax, pt.x
            shr     eax, 2
           imul     eax, TAB
            mov     TabWth, eax
           push     pt.y

mov     PrtHeading, 1
mov     PrtColor, 1
;---------- [Page number
         .if PrtHeading
            INVOKE     SelectObject, pd.hDC, hFontHead
               mov     eax, 'gaP('
               mov     dword ptr szBuff0, eax
               mov     word ptr szBuff0[4], ' e'
            INVOKE     BaseAscii, PageNum, addr szBuff0[6], 0, 10, 0, 1, 1
            INVOKE     lstrcat, addr szBuff0, addr szPerR
            .if PH != 1
               INVOKE     lstrcpy, addr PrtBuff, addr TimeBuff
               INVOKE     lstrcat, addr PrtBuff, addr szBuff0

            .else
               INVOKE     lstrcpy, addr PrtBuff, addr szBuff0
            .endif
            INVOKE     lstrlen, addr FileName
               mov     ebx, eax
               mov     eax, mL
               mov     PtrX, eax
            .if PrtColor
               INVOKE     SetTextColor, pd.hDC, 00ff0000h
            .endif
            INVOKE     TextOut, pd.hDC, PtrX, PtrY, addr FileName, ebx
            INVOKE     lstrlen, addr PrtBuff
               mov     ebx, eax
            INVOKE     GetTextExtentPoint32, pd.hDC, addr PrtBuff, ebx, addr pt
               mov     eax, PaperX
               sub     eax, mR
               sub     eax, pt.x
               mov     PtrX, eax
              push     pt.x
            .if PrtColor
               INVOKE     SetTextColor, pd.hDC, 00ffff00h
            .endif
            INVOKE     TextOut, pd.hDC, PtrX, PtrY, addr TimeBuff, ebx

            INVOKE     lstrlen, addr szBuff0
               mov     ebx, eax
            INVOKE     GetTextExtentPoint32, pd.hDC, addr szBuff0, ebx, addr pt
               pop     ecx
               mov     eax, pt.x
               add     PtrX, ecx
               sub     PtrX, eax
            .if PrtColor
               INVOKE     SetTextColor, pd.hDC, 00000000h
            .endif
            INVOKE     TextOut, pd.hDC, PtrX, PtrY, addr szBuff0, ebx

               mov     eax, pt.y
               add     PtrY, eax
               shr     eax, 1
               add     PtrY, eax
            INVOKE     SelectObject, pd.hDC, hFontPrt
         .endif
            pop     pt.y

NextLine:
            mov     eax, PtrY
            add     eax, pt.y
            cmp     eax, rect.bottom
            jnb     EndOfPage
            mov     eax, mL
            mov     PtrX, eax
         INVOKE     SendMessage, hREdit, EM_LINEINDEX, CurLine, 0
            mov     tr.chrg.cpMin, eax
         INVOKE     SendMessage, hREdit, EM_LINELENGTH, eax, 0
            add     eax, tr.chrg.cpMin
            mov     tr.chrg.cpMax, eax
            inc     CurLine
            lea     eax, PrtBuff
            mov     tr.lpstrText, eax
         INVOKE     SendMessage, hREdit, EM_GETTEXTRANGE, 0, addr tr
         .if LinesLeft == 1 && byte ptr PrtBuff == 0dh
               jmp     EndOfLine
         .endif
             or     eax, eax
             je     EndOfLine
         .if PrtColor
            INVOKE     SetTextColor, pd.hDC, 00008000h ;PrnColors[16]
         .endif

LoopLine:
            xor     esi, esi
         INVOKE     lstrlen, addr PrtBuff
            mov     ebx, eax
         .if ebx >= MaxLine
               mov     ebx, MaxLine
            INVOKE     lstrcpy, addr PrtWrap, addr PrtBuff[ebx]
               mov     esi, 1
         .endif
         INVOKE     TabbedTextOut, pd.hDC, PtrX, PtrY, addr PrtBuff, ebx, 1, addr TabWth, PtrX
         .if PrtColor
            INVOKE     lstrcpyn, addr szBuff0, addr PrtBuff, 9
            INVOKE     SetTextColor, pd.hDC, 000000ffh
            INVOKE     TabbedTextOut, pd.hDC, PtrX, PtrY, addr szBuff0, 8, 1, addr TabWth, PtrX
            INVOKE     lstrcpyn, addr szBuff0, addr PrtBuff+9, 40
            INVOKE     SetTextColor, pd.hDC, 00000000h
               mov     ecx, 9
              imul     ecx, AvgCharWth
               add     ecx, PtrX
            INVOKE     TabbedTextOut, pd.hDC, ecx, PtrY, addr szBuff0, 39, 1, addr TabWth, PtrX
         .endif

EndOfLine:
               mov     eax, pt.y
               add     PtrY, eax
         .if esi
            INVOKE     lstrcpy, addr PrtBuff, addr PrtWrap
               jmp     LoopLine
         .endif
            dec     LinesLeft
             je     EndOfPage
            jmp     NextLine

EndOfPage:
         INVOKE     EndPage, pd.hDC
         .if LinesLeft
               jmp     NextPage
         .endif

AllDone:
         INVOKE     EndDoc, pd.hDC
         INVOKE     DeleteDC, pd.hDC
         INVOKE     DeleteObject, hFontHead
         INVOKE     DeleteObject, hFontPrt
         INVOKE     DeleteObject, hRgn
      .endif
         ret
Print ENDP


Paul
The GeneSys Project is available from:
The Repository or My crappy website

loki_dre

Thanks,

I used:
.data
        _PrintWindow db "PrintWindow",0
        _user32 db "user32",0
.code
invoke GetModuleHandle,offset _user32
invoke GetProcAddress,eax,offset _PrintWindow
push 0
push memDC
push hwnd
call eax

hutch--

In the current beta version 10 you have this prototype in user32.inc.


PrintWindow PROTO :DWORD,:DWORD,:DWORD
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php