News:

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

Changing font size in an EDIT control

Started by Ryan, May 10, 2012, 01:19:53 AM

Previous topic - Next topic

Ryan

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



jj2007

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" ::)

Gunner

Changed one thing to get this result:


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

~Rob (Gunner)
- IE Zone Editor
- Gunners File Type Editor
http://www.gunnerinc.com

jj2007

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

Ryan

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?

Ryan

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!

Ryan

Yeah... in C++ I had to cast the identifier to an HMENU.

dedndave

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 ?

MichaelW

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.
eschew obfuscation

jj2007

Michael,

You don't think that could cause a resource leak, creating fonts many times without ever deleting them? Just curious...

MichaelW

Hi Jochen,

I didn't even consider that, because I would create the font only once.
eschew obfuscation

Ryan

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?

MichaelW

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.
eschew obfuscation

Ryan

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.

dedndave

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