The MASM Forum Archive 2004 to 2012

General Forums => The Campus => Topic started by: Ryan on May 10, 2012, 01:19:53 AM

Title: Changing font size in an EDIT control
Post by: Ryan on May 10, 2012, 01:19:53 AM
I'm new to MASM, but I have experience in high level languages (VB, C++).

If you haven't read my intro in the Soap Box, I am teaching myself Win32 programming in C++ and assembly.  A program that I thought would be interesting to write is one that solves Sudoku puzzles.  I have completed the C++ version, and I'm now working on the assembly version.

My program creates a 9x9 grid of EDIT boxes.  As numbers are entered in, the font size changes to fit the box appropriately via the EN_CHANGE notification code.  This works correctly in MASM.  In the process of troubleshooting an issue I had with assigning the control identifiers to the EDIT boxes, I populated the identifiers into their respective boxes.  I realized then that the font size wasn't being changed on every other box.  I don't know why this is.  See the OnCreate proc.  I do not have this problem in C++.  I have tried switching between using registers (very new to me) and local variables.  I have read the "Register Preservation Convention" help topic in MASM.  I have limited my use of the registers to eax, ecx, and edx.

I was having the same problem in my OnClearBtn proc when I was using bx.  I changed it to use a local variable, and it works correctly now.  I've tried to do the same thing with my OnCreate proc, but it still does not work.  I'm stumped as to what's causing it to work only half the time.

Is there a way to step through with MASM and watch variable/register values?

I am including a zip file with the full source code file and a screenshot of the window with inconsistent font sizes.

Thanks in advance!
Ryan


SetFont proc hEdit:HWND,iSize:dword
; From MSDN help on CreateFont function
; nHeight = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
; Works with CreateButton and OnClearBtn procs
; Fails on every other call from OnCreate
    invoke GetDC,hEdit
    invoke GetDeviceCaps,eax,LOGPIXELSY
    invoke MulDiv,iSize,eax,72
    neg eax
    invoke CreateFont,eax,0,0,0,0,0,0,0,0,0,0,0,0,0
    invoke SendMessage,hEdit,WM_SETFONT,eax,TRUE
    ret
SetFont endp

OnCreate proc hWnd:HWND
    ; Called from WM_CREATE msg
    local coords[9]:dword
    local txtID:word
    local hEdit:dword
   
    ; Code to initialize coords array removed for brevity

    ; ch register is the row counter, cl is the column
    ; cx is used for the control identifier
    ; txtID local variable is used to preserve cx around API calls
    mov ch,9
    rowloop:
        mov cl,9
        colloop:
            mov txtID,cx
            ; Array indexes need to be full 32-bit registers?
            ; Is there a better way?
            ; Copy ch and cl into eax and edx respectively
            xor eax,eax
            mov al,ch
            xor edx,edx
            mov dl,cl
            invoke CreateWindowEx,0, addr EditClass, 0,\
                WS_VISIBLE or WS_CHILD or WS_TABSTOP or WS_BORDER or ES_CENTER or\
                ES_AUTOHSCROLL or ES_AUTOVSCROLL or ES_NUMBER or ES_MULTILINE,\
                coords[edx*4-4], coords[eax*4-4], BOX_SIZE, BOX_SIZE, hWnd, txtID, 0, 0
            mov hEdit,eax ; hEdit stores the handle to the EDIT box
            invoke SetFont,hEdit,8 ; Fails on every other EDIT box.  No idea why.
            invoke SendMessage,hEdit,EM_LIMITTEXT,9,0
            invoke GetDlgCtrlID,hEdit ; Verify that the ID was set correctly
            invoke SetDlgItemInt,hWnd,eax,eax,TRUE ; Display it in the box
            mov cx,txtID
            dec cl
        jnz colloop
        dec ch
    jnz rowloop
    invoke CreateButton,hWnd,addr SolveButtonText,btnSolve,15
    invoke CreateButton,hWnd,addr ClearButtonText,btnClear,100
    ret
OnCreate endp

OnClearBtn proc hWnd:HWND
    local txtID:word

    ; ch register is the row counter, cl is the column
    ; cx is used for the control identifier
    ; txtID local variable is used to preserve cx around API calls
    ; I was using the bx register with the 'uses' clause in the proc header
    ; The SetFont proc (CreateFont) was working consistently on every other EDIT box
    ; Now works correctly every time with local variable :)
    mov ch,9
    clearRloop:
        mov cl,9
        clearCloop:
            mov txtID,cx
            invoke SetDlgItemText,hWnd,txtID,0
            invoke GetDlgItem,hWnd,txtID
            invoke SetFont,eax,14
            mov cx,txtID
            dec cl
        jnz clearCloop
        dec ch
    jnz clearRloop
    ret
OnClearBtn endp


Title: Re: Changing font size in an EDIT control
Post by: jj2007 on May 10, 2012, 02:08:16 AM
Hi Ryan,
Have a look at the attached executable, and especially at the result (eax) of the WM_SETFONT call.
What exactly is the problem? The font size looks ok to me. The odd thing is that the SendMessage WM_SETFONT returns zero every now and then, but then, according to MSDN, it does "not return a value" ::)
Title: Re: Changing font size in an EDIT control
Post by: Gunner on May 10, 2012, 02:29:46 AM
Changed one thing to get this result:
(http://www.gunnerinc.com/images/sudoku.png)

Is this what you are after?  The parameters of most API calls are DWORD, if it expects a WORD size, it will tell you.
Change txtID to a DWORD and use ecx instead of cx

Title: Re: Changing font size in an EDIT control
Post by: jj2007 on May 10, 2012, 02:34:52 AM
Yep, Gunner got it :U

Still, check if you cannot create 4 or 5 fonts once, instead of recreating them every time.
Great start for a first day project, Ryan :U
Title: Re: Changing font size in an EDIT control
Post by: Ryan on May 10, 2012, 02:41:04 AM
Thank you Gunner!

It's ironic because the control identifier is a 16-bit word in wParam in the Windows procedure.  It didn't have anything to do with setting the font size, but the CreateWindowEx function.  DOH!

Any idea why it worked on half, but not the other?
Title: Re: Changing font size in an EDIT control
Post by: Ryan on May 10, 2012, 02:51:35 AM
Thanks for the compliment jj, but I've been working on it on and off for a couple weeks.

For creating the fonts, do you mean to have them defined in the .data section so I don't have to create them each time, just reference the global instance?  That sounds like a good idea!  Thanks!
Title: Re: Changing font size in an EDIT control
Post by: Ryan on May 10, 2012, 02:56:25 AM
Yeah... in C++ I had to cast the identifier to an HMENU.
Title: Re: Changing font size in an EDIT control
Post by: dedndave on May 10, 2012, 03:15:35 AM
you can declare them in the uninitialized data section (.DATA?) as type HFONT
        .DATA?

hFont1  HFONT ?
hFont2  HFONT ?
hFont3  HFONT ?
hFont4  HFONT ?
hFont5  HFONT ?


notice that MASM isn't as picky about types as C is   :P
you could declare them as DWORD's and it would make little difference
        .DATA?

hFont1  dd ?
hFont2  dd ?
hFont3  dd ?
hFont4  dd ?
hFont5  dd ?
Title: Re: Changing font size in an EDIT control
Post by: MichaelW on May 10, 2012, 08:34:28 AM
I was able to correct the font size problem by replacing the SetFont procedure with this:

SetFont proc hEdit:HWND,iSize:dword
    invoke GetDC, 0
    invoke GetDeviceCaps, eax, LOGPIXELSY
    mul   iSize
    xor   edx, edx
    mov   ecx, 72
    div   ecx
    invoke CreateFont,eax,0,NULL,NULL,FW_NORMAL,0,NULL,NULL,
                      DEFAULT_CHARSET,OUT_TT_PRECIS,CLIP_DEFAULT_PRECIS,
                      PROOF_QUALITY,DEFAULT_PITCH or FF_DONTCARE,
                      NULL
    ret
SetFont endp

And no other changes.
Title: Re: Changing font size in an EDIT control
Post by: jj2007 on May 10, 2012, 10:25:27 AM
Michael,

You don't think that could cause a resource leak, creating fonts many times without ever deleting them? Just curious...
Title: Re: Changing font size in an EDIT control
Post by: MichaelW on May 10, 2012, 10:45:39 AM
Hi Jochen,

I didn't even consider that, because I would create the font only once.
Title: Re: Changing font size in an EDIT control
Post by: Ryan on May 10, 2012, 11:26:57 AM
Thanks Michael.

I see you expanded the MulDiv function into assembly.  I'm curious about one thing: The example I copied from the MSDN help negated the result before passing it to CreateFont.  Is that completely unnecessary?
Title: Re: Changing font size in an EDIT control
Post by: MichaelW on May 10, 2012, 12:14:28 PM
Per the CreateFont documentation, the function interprets the font height as:

> 0 The font mapper transforms this value into device units and matches it against the cell height of the available fonts.
0   The font mapper uses a default height value when it searches for a match.
< 0 The font mapper transforms this value into device units and matches its absolute value against the character height of


So the height is matched against cell height or matched against character height. I think that could make a difference under some circumstances, but for my test code there was no apparent difference. For a character in a fixed-size control matching against the cell height seems like a better choice to me.
Title: Re: Changing font size in an EDIT control
Post by: Ryan on May 10, 2012, 01:01:18 PM
Please forgive my ignorance, but what does cell height mean in this context, and more to the point, how does it affect my font size that I am using?  I assume I will have to adjust my numbers since the scaling is different.  I tried to research it on my own, but Google searches are leading to many topics regarding Excel.
Title: Re: Changing font size in an EDIT control
Post by: dedndave on May 10, 2012, 01:02:56 PM
also...
the user has selected a set of fonts to use for display
you can use SystemParametersInfo to fill a LOGFONT structure with the selected font
well, acutally, the NONCLIENTMETRICS structure has 5 LOGFONT structures   :P
the lfMessageFont member is probably the one you want
       LOCAL   ncm:NONCLIENTMETRICS

       mov     ecx,sizeof NONCLIENTMETRICS
       mov     ncm.cbSize,ecx
       INVOKE  SystemParametersInfo,SPI_GETNONCLIENTMETRICS,ecx,addr ncm,0

once you have the LOGFONT filled in, you can set the point size as Jochen suggested, then use CreateFontIndirect
       mov     ncm.lfMessageFont.lfHeight,8
       INVOKE  CreteFontIndirect,addr ncm.lfMessageFont
       mov     hFont8,eax

now you have a handle to a font that the user selected, but the size that you chose
when you are done using the font, delete the handle
       INVOKE  DeleteObject,hFont8
Title: Re: Changing font size in an EDIT control
Post by: dedndave on May 10, 2012, 01:15:50 PM
you can use GetDeviceCaps to get the "pixels per logical inch" setting
then use a little formula to convert between point size and height
normally, the LOGPIXELSY value is 96 dpi - but don't count on it
        LOCAL   hdcDesktop  :HDC
        LOCAL   uLogPixelsY :UINT

        INVOKE  GetDC,HWND_DESKTOP
        mov     hdcDesktop,eax
        INVOKE  GetDeviceCaps,eax,LOGPIXELSY
        mov     uLogPixelsY,eax
        INVOKE  ReleaseDC,HWND_DESKTOP,hdcDesktop


once you have the "pixels per logical inch" value...
lfHeight = -(PointSize * uLogPixelsY) / 72

i find it easier to stick a constant into the lfHeight member, like 8   :P
if i want it larger, i stick a larger number in there - lol
in the end, what you are after is a font that will allow your text to fit in a box
the other approach is to use GetTextExtentPoint32 to measure the font and make your boxes fit the text
Title: Re: Changing font size in an EDIT control
Post by: Ryan on May 11, 2012, 11:34:25 PM
Per dedndave and jj's suggestion, I declared global variables in the data? section to hold the font handles that I use in the program, instead of creating a new font for each change.


InitFont proc fontSize:dword
    local hdcDesktop:HDC
   
    invoke GetDC,0
    mov hdcDesktop,eax
    invoke GetDeviceCaps, eax, LOGPIXELSY
    invoke MulDiv,fontSize,eax,72
    neg eax
    invoke CreateFont,eax,0,0,0,0,0,0,0,0,0,0,0,0,0
    invoke ReleaseDC,0,hdcDesktop
    ret
InitFont endp


This gets executed once at the beginning for each font size I use.  I took into account dedndave's last example of invoking ReleaseDC at the end of the proc.  Releasing the DC appears to void my fonts that I create.  If I comment out the invoking of ReleaseDC, things work as expected.

How crucial is it that I release the DC?
Title: Re: Changing font size in an EDIT control
Post by: dedndave on May 12, 2012, 12:02:23 AM
hmmmm....
something is amiss   :P
the only reason you need the DC is for GetDeviceCaps
releasing it should have no affect on the font handles

but - try this, anyways
InitFont proc fontSize:dword
    local hdcDesktop:HDC
   
    invoke GetDC,0
    mov hdcDesktop,eax
    invoke GetDeviceCaps, eax, LOGPIXELSY
    push eax
    invoke ReleaseDC,0,hdcDesktop
    pop eax
    invoke MulDiv,fontSize,eax,72
    neg eax
    invoke CreateFont,eax,0,0,0,0,0,0,0,0,0,0,0,0,0
    ret
InitFont endp


EDIT: i see what was happening
the font handle you returned in EAX was being destroyed by the ReleaseDC call   :P
API calls may (and usually do) trash EAX, ECX, and EDX

so - this would also work
InitFont proc fontSize:dword
    local hdcDesktop:HDC
   
    invoke GetDC,0
    mov hdcDesktop,eax
    invoke GetDeviceCaps, eax, LOGPIXELSY
    invoke MulDiv,fontSize,eax,72
    neg eax
    invoke CreateFont,eax,0,0,0,0,0,0,0,0,0,0,0,0,0
    push eax
    invoke ReleaseDC,0,hdcDesktop
    pop eax
    ret
InitFont endp
Title: Re: Changing font size in an EDIT control
Post by: Ryan on May 12, 2012, 12:08:02 AM
Ah.  That makes sense.  Thank you!