flat assembler
Message board for the users of flat assembler.

Index > Windows > PAGESETUPDLG

Thread Post new topic Reply to topic

Joined: 28 Mar 2006
Posts: 68
Hello, this time i have a problem accessing strings located inside a structure.

First of all: i have a Richedit-Control inside my dialog, plus there is a "Print"-Button. If the user presses this button, i call the PageSetupDlg-Dialog (which should be used instead of the PrintDlg, as stated in the WIN32API.HLP). The PageSetupDlg returns a value different than zero, which means that the user pressed OK. So if this is the case, i then want to create a DeviceContext using the Data (Drivername, Devicename) the user has selected (This is returned by PageSetupDlg using the PAGESETUPDLG-structure).

But when trying to create the DeviceContext, i run into problems. I just can't figure out how to access the strings for Drivername and Devicename located at PAGESETUPDLG.hDevNames.

hDevNames is a handle to a global memory object that contains a DEVNAMES structure, and this DEVNAMES-structure itself contains word-variables which point to null-terminated strings.

I would be glad if anyone could help how i can access these strings.
Post 22 May 2006, 12:57
View user's profile Send private message Reply with quote

Joined: 26 Aug 2004
Posts: 274
Location: North Central Mississippi

Everything I have learned about printing RichEdit docs came from Chib777 and his utilities XXContorls. Here is a link to the MASM32 site where he annouced the most recent version:


and a link to his site:


Attached is a program segment in FASM that I use in some commercial apps, ugly, but it works. In this case I use RTF files in RichEdit controls. Usage:

PrintRTF proc pFileName:DWORD, bLandOrPor:DWORD, PrintViewPrompt:DWORD, NumCopies:DWORD

provide a pointer to a filename
binary for Landscape or Portrait layout
binary for Print or View, not currently used
DWORD for number of copies

Most of the work is done formatting the individual pages. Unfortunately, this routines automatically prints to the default printer, and bypasses the printer dialog and page setup dialog. It uses hardcoded pages margins.

Hope this can help!


Filename: PrintRTF.asm
Filesize: 22.65 KB
Downloaded: 49 Time(s)

Some Assembly Required
It's a good day to code!
U.S.Constitution; Bill of Rights; Amendment 1:
... the right of the people peaceably to assemble, ...
The code is dark, and full of errors!
Post 22 May 2006, 22:22
View user's profile Send private message Reply with quote

Joined: 28 Mar 2006
Posts: 68
Yes, interesting, thank you. But i just found a tutorial about printing in windows. It uses the PrintSetupDlg and i think i'll give it a try, hehe.
Here's the link: http://www.catch22.net/tuts/printing.asp
Post 23 May 2006, 08:23
View user's profile Send private message Reply with quote

Joined: 28 Mar 2006
Posts: 68
Hello! Some time has passed and i still haven't finished the "print-from-richedit-control" procedure. It's not yet possible to set the margins, but anyway it may be helpful for some (and it uses the PAGESETUPDLG instead of the PRINTDLG). The handle to the window is passed to the procedure, and the Richedit-Control in passed window has to have the synonym ID_RTF.

proc PrintCurRE,hwnd:DWORD
        push    ebx

        ; ======================================================================
        ; The following partial program will demonstrate how to print the 
        ; content of a RichEdit-Control to paper.
        ; This is achieved through this steps:
        ;   1) The user choses the wanted printer-device, margins, etc
        ;      (PrintSetupDlg)
        ;   2) Creation of a device context
        ;   3) Formatting the text in the RichEdit-Control for the created
        ;      device context.
        ;   4) Setting up the print-job (StartDoc)
        ;   5) Print page per page
        ;   ?) Cleaning up: deletion of the device context, unlocking of locked
        ;      memory objects.

        ; ----------------------------------------------------------------------
        ; Let the user chose the wanted printer-device and other configurations
        ; for the print-job (f.i. margins, paper-size). A deactivated option is
        ; the page-layout (portrait or landscape, default is portrait).
        ; This is done by calling the PageSetupDlg which comes with the
        ; CommonDialogs.dll (comdlg32.dll).
        ; As parameter for the PageSetupDlg a PAGESETUPDLG-structure is used,
        ; which configures which options in the PageSetupDlg are made available
        ; for the user. Also, this structure allows to set a handle to a dialog
        ; box template which should be used instead of the standard
        ; PageSetupDlg-dialog box.
        mov     [pagup.lStructSize],        \   ; Specify the size of the 
                sizeof.PAGESETUPDLG             ; structure.
        mov     eax,[hwnd]                      ; Set the owner-dialog of
        mov     [pagup.hwndOwner],eax           ; the PageSetupDlg.
        ; Initialize recommended margins and minimum margins (set to 10 mm on 
        ; each side):
        mov     [pagup.rtMargin+RECT.left],1000
        mov     [pagup.rtMargin+RECT.top],1000
        mov     [pagup.rtMargin+RECT.right],1000
        mov     [pagup.rtMargin+RECT.bottom],1000
        mov     [pagup.rtMinMargin+RECT.left],1000
        mov     [pagup.rtMinMargin+RECT.top],1000
        mov     [pagup.rtMinMargin+RECT.right],1000
        mov     [pagup.rtMinMargin+RECT.bottom],1000
        ; Setting behaviourial flags for the PageSetupDlg. Here, PSD_NOWARNING
        ; is used to oppress the system from displaying a warning message if
        ; there is no default printer. 
        ; PSD_INHUNDREDTHSOFMILLIMETERS indicates that all sizes
        ; are to be set in hundredths of millimeters. PSD_MARGINS and
        ; PSD_MINMARGINS tells the PageSetupDlg to use the rtMargin and
        ; rtMinMargin members to setup the margins:
        mov     [pagup.Flags],PSD_NOWARNING+\
        ; After the PAGESETUPDLG-structure has been initialized, the
        ; PrintSetupDlg is called, which will return 0 in case the user cancels
        ; the process.
        invoke  PageSetupDlg,pagup              ; Show the PageSetupDialogs.
        cmp     eax,0                           ; If user cancels,
        je      printcurre_fin                  ; end procedure.

        ; ----------------------------------------------------------------------
        ; After the user has chosen the wanted options, a device context has to
        ; be created, which outputs to a printer-device. A device context is a
        ; structure that defines a set of graphic objects and the graphic modes
        ; that affect output. These graphic objects could be a pen for line
        ; drawing, a brush for painting/filling, a bitmap, a palette of colours
        ; or a region for clipping, which is for this program the most important
        ; object, since the output is clipped page by page.
        ; To create a device context, the function CreateDC has to be called
        ; with the following parameters:
        ;   LPCTSTR lpszDriver, // pointer to string specifying driver name 
        ;   LPCTSTR lpszDevice, // pointer to string specifying device name  
        ;   LPCTSTR lpszOutput, // do not use; set to NULL 
        ;   CONST DEVMODE *lpInitData   // pointer to optional printer data 
        ; Since the PageSetupDlg has saved options selected by the user back to
        ; the PAGESETUPDLG-structure, these parameters can get obtained easily.
        ; As a member of PAGESETUPDLG, hDevNames points to a DEVNAMES structure
        ; which in turn points to a null-terminated string that contains the
        ; filename of the device driver.
        ; Another member of PAGESETUPDLG is hDevMode, and as the name might let
        ; guess, this member points to a DEVMODE structure which points to a
        ; string containing the device name.
; ??????????????????????????????????????????????????????????????????????????????
; Before these can be used, they have to get locked
; ??????????????????????????????????????????????????????????????????????????????
        invoke  GlobalLock,[pagup.hDevNames]    ; Lock the DEVNAMES-struct and
        mov     [hdena],eax                     ; save the returned handle.
        invoke  GlobalLock,[pagup.hDevMode]     ; Lock the DEVMODE-struct and
        mov     [hdemo],eax                     ; save the returned handle.
        invoke  CreateDC,[hdena],[hdemo],   \   ; Create the device context,
                NULL,[hdemo]                    ; and, in case
        cmp     eax,NULL                        ; it returns NULL,
        je      fcrecdc                         ; output a error-message and end
                                                ; the process.
        mov     [hDC],eax                       ; Otherwise save the handle to
                                                ; the device context.

        ; ----------------------------------------------------------------------
        ; Is the device context created successfully, the next step is to set up
        ; the richedit-control. This is done by sending the EM_FORMATRANGE
        ; message to the richedit-control. This message uses the FORMATRANGE
        ; structure as parameter, which contains infos about the output device,
        ; and the area to render. So the first step is to initialize this
        ; structure:
        mov     eax,[hDC]
        ; Setting the devices: hdc is the handle to the device context where the
        ; output is actually send to. hdcTarget contains the handle to the
        ; device context to format for. So the hdc and hdcTarget are usually the
        ; same handles, but would differ in case of print-previews (in this case
        ; hdc would have the handle of the window in which the output is viewed,
        ; whilst hdcTarget is the device context of the printer.
        mov     [fora.hdc],eax                  ; Device for rendering
        mov     [fora.hdcTarget],eax            ; Target-device
        ; The rcPage-member of FORMATRANGE specifies the entire area of the
        ; rendering device, this is the size of the paper. Units are in TWIPS.
        mov     [fora.rcPage+RECT.left],0       ; Sheet begins at
        mov     [fora.rcPage+RECT.top],0        ; 0|0.
        stdcall huMMToTWIPS,                \   ; Convert the width of the
                [pagup.ptPaperSize+POINT.x]     ; sheet-size to TWIPS
        mov     eax,[istwips]                   ; and save resulting
        mov     [fora.rcPage+RECT.right],eax    ; width.
        stdcall huMMToTWIPS,                \   ; Convert the height of the
                [pagup.ptPaperSize+POINT.y]     ; sheet-size to TWIPS
        mov     eax,[istwips]                   ; and save the resulting
        mov     [fora.rcPage+RECT.bottom],eax   ; height.
        ; The rc-member of FORMATRANGE specifies the area to render to, units
        ; are in TWIPS. Here is the place where the margins get calculated
        ; and subtracted from the sheet-size (rc-member) to get the area to
        ; render to.
        ; **********************************************************************
        ; *** The following calculations were done guided by 
        ; *** http://www.catch22.net/tuts/printing.asp
        ; Since most printers cannot print to the edge of a piece of paper,
        ; there will always be a small border around the outside of this
        ; printable area which cannot be printed to. To get the printable area
        ; of a printing device, the procedure GetDeviceCaps is used.
        ; A sheet of paper:
        ; @@@@@@@@@@@@@@@@
        ; @**************@
        ; @*............*@
        ; @*............*@
        ; @*............*@      @ = Printer margin
        ; @*............*@      * = User's margin
        ; @*............*@      . = User's page area
        ; @*............*@
        ; @*............*@
        ; @*............*@
        ; @*............*@
        ; @**************@
        ; @@@@@@@@@@@@@@@@
mov     [fora.rc+RECT.left],0
mov     [fora.rc+RECT.top],0
mov     eax,dword[fora.rcPage+RECT.right]
mov     [fora.rc+RECT.right],eax
mov     eax,dword[fora.rcPage+RECT.bottom]
mov     [fora.rc+RECT.bottom],eax

        ; As last member the range of the text to format is set. Here, if cpMin
        ; is set to 0 and cpMax to -1, all the text is printed.
        mov     [fora.chrg+CHARRANGE.cpMin],0
        mov     [fora.chrg+CHARRANGE.cpMax],-1
        ; Before being able to send a message to the RichEdit-Control, the
        ; handler to this control must get obtained:
        invoke  GetDlgItem,[hwnd],ID_RTF
        mov     [hPRTE],eax

        ; ----------------------------------------------------------------------
        ; To set up the print-job, the StartDoc procedure has to be called,
        ; which, as parameter, takes a DOCINFO-structure.
        mov     [dofo.cbSize],sizeof.DOCINFO    ; Specify the size.
        mov     [dofo.lpszDocName],docnam       ; Documentname
        ; Points to a null-terminated string that specifies the name of an 
        ; output file. If this pointer is NULL, the output will be sent to the 
        ; device identified by the device context handle that was passed to the 
        ; StartDoc function:
        mov     [dofo.lpszOutput],NULL
        ; The last two members are ignored in Version above Win95:
        mov     [dofo.lpszDatatype],NULL
        mov     [dofo.fwType],NULL
        ; After initializing the DOCINFO structure, the StartDoc procedure can
        ; be called, which returns a value above zero, if successfull. This
        ; value is the print job identifier for the document:
        invoke  StartDoc,[hDC],dofo

        ; ----------------------------------------------------------------------
        ; Now print page per page. This loop was copied from the example of
        ; "Using Built-In Printing Features from a Rich Edit Control" found in
        ; the WIN32API.HLP.
        ; As first step the textlength is requested. The example mentioned above
        ; uses the WM_GETTEXTLENGTH message for this task, but it seems that the
        ; result of this message is larger than the actual textlength, when the
        ; Richedit-Control uses diverse text-formatings like tables,
        ; text-styles, and so forth. For further infos the WM_GETTEXTLENGTH
        ; entry in the WIN32API.HLP provides some infos.
        ; So instead of using WM_GETTEXTLENGTH to find out the actual
        ; textlength, first all text in the Richedit-control is selected:
        mov     dword[change.cpMin],0
        mov     dword[change.cpMax],-1
        invoke  SendMessage,[hPRTE],EM_EXSETSEL,0,change
        ; After selecting all the text, a request is send to get the cpMin and
        ; cpMax position of the selection:
        invoke  SendMessage,[hPRTE],EM_EXGETSEL,0,change
        mov     eax,[change.cpMax]              ; Save the textlength. It will 
        mov     [txtlen],eax                    ; be used to calculate if the
                                                ; last printed char is too the 
                                                ; last one in the richedit-ctrl.
    @@: invoke  StartPage,[hDC]                 ; Start a page
        ; Print as much text as can fit on a page. The return value is the
        ; index of the first character on the next page. Using TRUE for the
        ; wParam parameter causes the text to be printed.
        ; While the EM_FORMATRANGE message (with FALSE as wparam!) causes the
        ; RichEdit Control to format it's content for the given device-context
        ; (in fora), the EM_DISPLAYBAND message is the cause the contents gets
        ; printed:
        invoke  SendMessage,[hPRTE],        \   ; Returns the index of the last
                EM_FORMATRANGE,FALSE,fora       ; character that fits in the
        mov     [laca],eax                      ; range + 1.
        invoke  SendMessage,[hPRTE],        \   ; Output the formatted contents
                EM_DISPLAYBAND,0,fora.rc        ; to the device context (fora).
        ; End a page:
        invoke  EndPage,[hDC]
        ; Is there more text to print? This can be found out by comparing the
        ; index of the last printed character with the textlen.
        mov     eax,[laca]                      ; If the last character
        cmp     eax,[txtlen]                    ; has been printed,
        je      @f                              ; end the loop
        ; Before being sure that another page has to be printed, another check
        ; is run: this time laca+1 is compared with the txtlen, and, if they are
        ; equal, the loop will also get ended.
        ; This check seems odd, but if, f.i. the Richedit control would be
        ; empty, EM_FORMATRANGE would return 0 while the txtlen would be set
        ; to 1. Since 0 is not equal to 1, the program would generate another
        ; page and another and so forth. Too, i have achieved the same effect by
        ; trying to print the delivered "prasland.rtf" file in landscape format.
        ; Then, EM_FORMATRANGE would always return 1225 while the txtlen is set
        ; to 1226, which, again, would result in endless printing-loop.
        inc     eax                             ; last character + 1
        cmp     eax,[txtlen]
        je      @f
        ; Otherwise, set the cpMin member of the FORMATRANGE structure to the
        ; last formatted character (which is actually the last formated
        ; character + 1) as new start position for the next page. The cpMax
        ; member is set to -1 again:
        mov     eax,[laca]
        mov     [fora.chrg+CHARRANGE.cpMin],eax
        mov     [fora.chrg+CHARRANGE.cpMax],-1
        jmp     @b                              ; Jump to the start of the loop
    @@: ; The printing process is nearly finished.
        ; When printing is complete, the EM_FORMATRANGE message must be sent 
        ; to the control with wParam = 0 and lParam = NULL to free the 
        ; information cache by the control:
        invoke  SendMessage,[hPRTE],EM_FORMATRANGE,0,NULL
        ; End the document:
        invoke  EndDoc,[hDC]

        ; ----------------------------------------------------------------------
        ; After the printing has been done, the device context has to be 
        ; deleted:
        invoke  DeleteDC,[hDC]
        ; Plus all locked memory objects have to get unlocked:
        invoke  GlobalUnlock,[hdena]            ; Unlock DEVNAMES
        invoke  GlobalUnlock,[hdemo]            ; Unlock DEVMODE

        pop     ebx
        stdcall TInfoBox,[ebp+8],fdc
        jmp     ulop

;== Converts a digit given in hundredths of millimeters to it's equivalent in 
;== TWIPS, where 25.4 mm = 1 inch = 1440 TWIPS.
;== The calculation is done by converting the millimeters to inches through
;== a division by 2540 (25.4 mm * 100 to get hundredths of millimeters). The
;== resulting inches then get multiplied by 1440 to get the TWIPS.
;== The procedure receives the digit to convert as parameter and writes the
;== resulting TWIPS into the istwips dword variable.
proc huMMToTWIPS,humm:DWORD
        push    ebx
        ; ----------------------------------------------------------------------
        ; This part converts the millimeters to inches by a division through
        ; 254 instead of the 2540 stated in the main comment. By this, the
        ; result will be more precise, but it forces us to set the multiplicator
        ; to 144.
        mov     edx,0                           ; Clear remainder
        mov     eax,[humm]                      ; Set humm as dividend
        mov     ebx,254                         ; Set divisor
        div     ebx                             ; Calculate
        mov     ebx,eax                         ; Save the resulting inches

        ; ----------------------------------------------------------------------
        ; Multiply the inches by 144 to get the TWIPS:
        mov     eax,144                         ; Set multiplicator
        mul     ebx
        mov     [istwips],eax                   ; Save the resulting twips
        pop     ebx

I'm no assembler-professional, i know! Wink
Post 23 Jun 2006, 09:21
View user's profile Send private message Reply with quote
Tomasz Grysztar

Joined: 16 Jun 2003
Posts: 7725
Location: Kraków, Poland
Tomasz Grysztar
I think you'd get better precision by first doing MUL and then the DIV instead of the opposite order. Also since your function already returns the value in EAX, there's no need to store it in global variable.
It could look like:
proc huMMToTWIPS,humm:DWORD
        mov     eax,[humm]
        mov     ecx,1440
        mul     ecx             ; EAX*1440 stored in EDX:EAX
        mov     ecx,2540
        div     ecx             ; EDX:EAX divided by 2540 -> result in EAX
; this not really necessary:
;        mov     [istwips],eax
Post 23 Jun 2006, 09:59
View user's profile Send private message Visit poster's website Reply with quote

Joined: 28 Mar 2006
Posts: 68
Thanks! Half the instructions i used, hehe
Post 23 Jun 2006, 10:05
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  

< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum

Copyright © 1999-2020, Tomasz Grysztar. Also on YouTube, Twitter.

Website powered by rwasa.