The MASM Forum

General => The Workshop => Windows API => Topic started by: NoCforMe on March 30, 2025, 05:54:32 AM

Title: Creating menus dynamically
Post by: NoCforMe on March 30, 2025, 05:54:32 AM
Another in the series of how to create Win32 programs without using resource files or the resource editor.

The "standard" way to get a menu into a window in your Win32 program is to set it up in the resource editor, which is fine and dandy if you want to do things this way.

But what if a guy decides he wants to avoid the whole resource-editor thing? Well, it turns out to be really pretty simple to create menus "on the fly" within your code, requiring zero resources other than some text strings.

Here's the recipe, fleshed out in the attached code sample:

1. Create the menu IDs--the numbers that will be used in your WM_COMMAND handler to dispatch to your code when the user chooses items from the menu--and the text that will be shown in the menus.

2. Create the menus using CreateMenu() and CreatePopupMenu(). The former creates the top-level menu (meaning the whole enchilada), while the latter creates each "submenu", like File, Edit, etc. Here we're just creating a single submenu, File.

3. Create each submenu and each menu item using AppendMenu().

4. Finalize and activate the menu, attaching it to your window, using SetMenu().

It's really that simple. I use this all the time.

; Define the menu IDs:

$menuNewFile EQU 1000
$menuOpenFile EQU 1001
$menuSaveFile EQU 1002
$menuSaveFileAs EQU 1003
$menuViewExtraFile EQU 1004
$menuPrintSetup EQU 1005
$menuPrint EQU 1006
$menuConfigEdit EQU 1007
$menuToggleSaveIni EQU 1008
$menuExit EQU 1009

; and the item text:

MenuFileStr DB "File", 0
MenuNewStr DB "New", $tab, "Ctrl+N", 0
MenuOpenStr DB "Open ...", $tab, "Ctrl+O", 0
MenuSaveStr DB "Save ...", $tab, "Ctrl+S", 0
MenuSaveAsStr DB "Save as ...", 0
MenuViewExtraStr DB "View &Extra file ...", $tab, "Ctrl+E", 0
MenuPrintSetupStr DB "Print setup ...", 0
MenuPrintStr DB "Print ...", $tab, "Ctrl+P", 0
MenuConfigEditStr DB "Configuration editor ...", 0
MenuToggleSaveIniStr DB "Save program setup on exit", 0
MenuExitStr DB "Exit", 0

; Create the menu & submenus:

LOCAL menuHandle:HMENU, subMenu:HMENU

CALL CreateMenu
MOV menuHandle, EAX
CALL CreatePopupMenu
MOV subMenu, EAX

; Define the menu items:

INVOKE AppendMenu, menuHandle, MF_POPUP or MF_STRING, subMenu1, OFFSET MenuFileStr
INVOKE AppendMenu, subMenu, MF_STRING, $menuNewFile, OFFSET MenuNewStr
INVOKE AppendMenu, subMenu, MF_STRING, $menuOpenFile, OFFSET MenuOpenStr
INVOKE AppendMenu, subMenu, MF_STRING, $menuSaveFile, OFFSET MenuSaveStr
INVOKE AppendMenu, subMenu, MF_STRING, $menuSaveFileAs, OFFSET MenuSaveAsStr
INVOKE AppendMenu, subMenu, MF_SEPARATOR, 0, 0
INVOKE AppendMenu, subMenu, MF_STRING, $menuViewExtraFile, OFFSET MenuViewExtraStr
INVOKE AppendMenu, subMenu, MF_SEPARATOR, 0, 0
INVOKE AppendMenu, subMenu, MF_STRING, $menuPrintSetup, OFFSET MenuPrintSetupStr
INVOKE AppendMenu, subMenu, MF_STRING, $menuPrint, OFFSET MenuPrintStr
INVOKE AppendMenu, subMenu, MF_SEPARATOR, 0, 0
INVOKE AppendMenu, subMenu, MF_STRING, $menuConfigEdit, OFFSET MenuConfigEditStr
INVOKE AppendMenu, subMenu, MF_SEPARATOR, 0, 0
INVOKE AppendMenu, subMenu, MF_STRING or MF_CHECKED, $menuToggleSaveIni, OFFSET MenuToggleSaveIniStr
INVOKE AppendMenu, subMenu, MF_SEPARATOR, 0, 0
INVOKE AppendMenu, subMenu, MF_STRING, $menuExit, OFFSET MenuExitStr

; Bind the menu to its window:

INVOKE SetMenu, hWin, MenuHandle
Title: Re: Creating menus dynamically
Post by: zedd151 on March 30, 2025, 07:24:42 AM
Yabbut... that is a lot of code and data, just for the sake of not using a resource based menu. I have used similar methods too, but always fall back to using a resource menu. Especially if there are many sub menus and/or menu items.
There are some things that an .rc file handles more easily and efficiently than doing it in the source file. This being one of them, imho.
Title: Re: Creating menus dynamically
Post by: sinsi on March 30, 2025, 07:30:07 AM
I use CreatePopupMenu and InsertMenuItem, much the same thing.
QuoteTo add items to a menu, use the InsertMenuItem function. The older AppendMenu and InsertMenu functions are still supported, but InsertMenuItem should be used for new applications.

For a context menu I use this to handle WM_RBUTTONUP
mainpopup  proc hwnd:HWND,lparam:LPARAM ;parameters from WndProc
    local  pt:POINT
    movsx  eax,word ptr lparam
    movsx  edx,word ptr lparam+2
    mov    pt.x,eax
    mov    pt.y,edx
    invoke ClientToScreen,hwnd,addr pt
    invoke TrackPopupMenu,mainmenu,TPM_RIGHTBUTTON,pt.x,pt.y,0,hwnd,0
    ret
mainpopup  endp
Title: Re: Creating menus dynamically
Post by: NoCforMe on March 30, 2025, 07:35:20 AM
Interesting.
For my popup (right-click context) menus I use this:
MOV EAX, lParam
MOV EDX, EAX
AND EAX, 0FFFFH
MOV pt.x, EAX
SHR EDX, 16
MOV pt.y, EDX
INVOKE ClientToScreen, hWin, ADDR pt ;Where is our client in the "real world"?
INVOKE TrackPopupMenuEx, hMenu,
TPM_LEFTALIGN or TPM_TOPALIGN or TPM_RETURNCMD or TPM_RIGHTBUTTON or TPM_VERNEGANIMATION or TPM_VERTICAL,
pt.x, pt.y, hWin, NULL

I've always been a bit stumped by what, exactly, TPM_VERNEGANIMATION is (or how to pronounce it). But hey, it works ...

(BTW, your code to get x and y from lParam is better than mine; this was written before I started using MOVSX.)
Title: Re: Creating menus dynamically
Post by: sinsi on March 30, 2025, 07:48:16 AM
Quote from: NoCforMe on March 30, 2025, 07:35:20 AM(BTW, your code to get x and y from lParam is better than mine; this was written before I started using MOVSX.)
Quote from: MicrosoftSystems with multiple monitors can have negative x- and y- coordinates
Hence the need for MOVSX.
Title: Re: Creating menus dynamically
Post by: zedd151 on March 30, 2025, 07:49:09 AM
Quote from: NoCforMe on March 30, 2025, 07:35:20 AMI've always been a bit stumped by what, exactly, TPM_VERNEGANIMATION is (or how to pronounce it). But hey, it works ...
TrackPopupMenu (https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-trackpopupmenu)  :smiley:

Scroll down to see what TPM_VERNEGANIMATION does. "Animates menu from bottom to top". Probably works fine without it.

Vertical negative animation apparently.
Title: Re: Creating menus dynamically
Post by: sinsi on March 30, 2025, 08:47:50 AM
Quote from: zedd151 on March 30, 2025, 07:24:42 AMYabbut... that is a lot of code and data, just for the sake of not using a resource based menu. I have used similar methods too, but always fall back to using a resource menu. Especially if there are many sub menus and/or menu items.
There are some things that an .rc file handles more easily and efficiently than doing it in the source file. This being one of them, imho.
A bit off-topic but I don't like resources due to the fact that it is easy to change them (BeginUpdateResource etc.)
Anything from a practical joke (hah, this menu says "shit" lol) to nasty (infected images).
Title: Re: Creating menus dynamically
Post by: zedd151 on March 30, 2025, 08:55:56 AM
Quote from: sinsi on March 30, 2025, 08:47:50 AMA bit off-topic but I don't like resources due to the fact that it is easy to change them (BeginUpdateResource etc.)
Anything from a practical joke (hah, this menu says "shit" lol) to nasty (infected images).

Then again it's easy to change any strings with a hex editor, whether it is in resources or not. If a hacker/spammer/script kiddie is determined enough, not a lot can be done to stop them entirely, if that is the type of thing that you are talking about.
Title: Re: Creating menus dynamically
Post by: NoCforMe on March 30, 2025, 09:02:41 AM
Quote from: zedd151 on March 30, 2025, 07:49:09 AMScroll down to see what TPM_VERNEGANIMATION does. "Animates menu from bottom to top". Probably works fine without it.
Probably.

Heh; makes me wonder how much stuff like this is in my code that although it works OK, could do without it (or might actually be better done without).

Or conversely, how much of my code works OK but would be better if some small thing was added.
Title: Re: Creating menus dynamically
Post by: NoCforMe on March 30, 2025, 09:06:26 AM
Quote from: sinsi on March 30, 2025, 08:47:50 AMA bit off-topic but I don't like resources due to the fact that it is easy to change them (BeginUpdateResource etc.)
Anything from a practical joke (hah, this menu says "shit" lol) to nasty (infected images).
Well, here's another small advantage I see to not using resources in this case (menus):
If you use a resource editor, you need to duplicate the IDs attached to each menu item by copying them from your code to the resource file (or vice versa).
And if you change them in one place you have to change them in the other to keep them in sync.
I was going to say you could put them in an include file, but you can't, as the resource compiler only recognizes C header files, not assembly-language EQUs. (You can use include files in C/C++.)
With this method there's only one set of IDs and they're right in the source code.
Title: Re: Creating menus dynamically
Post by: Vortex on March 30, 2025, 08:49:46 PM
A menu editor can do also the job. The visual menu designer outputs BCX Basic code. It's easy to convert the BCX code to assembly :

' -------------------------------------------------------------------------
'        BCX Menu Creator Copyright 2001-2008 by Doyle Whisenant
'                      mekanixx@gmail.com                 
'
'    Paste this code into your program and call the function
'              to add the menu code to your program.         
'
'              Usage:  AddMenu(hParent)
' -------------------------------------------------------------------------

FUNCTION AddMenu(hwndOwner AS HWND) AS BOOL
CONST  ID_MENU0=9000
CONST  ID_MENU1=9001
CONST  ID_MENU3=9003

'--------------------------------------------------------------------------
GLOBAL MainMenu AS HMENU
  MainMenu=CreateMenu()
'--------------------------------------------------------------------------
GLOBAL FileMenu AS HMENU
  FileMenu=CreateMenu()
  InsertMenu(MainMenu,  0, MF_POPUP, FileMenu, "&File")
  AppendMenu(FileMenu, MF_STRING, ID_MENU1, "&Save")
  AppendMenu(FileMenu, MF_STRING, ID_MENU3, "&Load")
'--------------------------------------------------------------------------
  ' activate menu
  IF NOT SetMenu(hwndOwner, MainMenu) THEN
    FUNCTION=FALSE
  END IF
  FUNCTION=TRUE
END FUNCTION
Title: Re: Creating menus dynamically
Post by: jj2007 on March 31, 2025, 12:16:35 AM
Quote from: NoCforMe on March 30, 2025, 05:54:32 AMWell, it turns out to be really pretty simple to create menus "on the fly" within your code, requiring zero resources other than some text strings.

Quote from: jj2007 on December 03, 2014, 06:49:49 AMAttached a minimalistic Windows GUI application - no MasmBasic required, less than 100 lines and fully functional, with a menu and an edit control. Use it as a template together with the Iczelion tutorials.
Title: Re: Creating menus dynamically
Post by: jj2007 on March 31, 2025, 12:21:08 AM
GuiParas equ "A menu without resources, hooray!!!!", b LiteBlueGreen, icon Butterfly
GuiMenu equ @File, &Open, &Save, &Print, -, E&xit, @Edit, Undo, Copy, Paste
include \masm32\MasmBasic\Res\MbGui.asm
GuiEnd
Title: Re: Creating menus dynamically
Post by: NoCforMe on March 31, 2025, 08:16:58 AM
"Honey, did you see that?"
"No, what, dear?"
"That billboard back there. Didn't you see it?"
"No, I guess I missed it."
"Oh, it was just another one of those billboards for something called MasmBasic, whatever that is."
"Oh."
Title: Re: Creating menus dynamically
Post by: jj2007 on March 31, 2025, 08:56:13 AM
Quote from: jj2007 on March 31, 2025, 12:16:35 AMno MasmBasic required, less than 100 lines and fully functional, with a menu and an edit control
Title: Re: Creating menus dynamically
Post by: zedd151 on March 31, 2025, 09:14:54 AM
swiped from a masm32 sdk example, not mine  :cool:
fully funtional rich edit based editor no resources
it has the ancient richedit control

\masm32\examples\exampl05\hlldemo\smalled\redit.asm

    include \masm32\include\masm32rt.inc


      WndProc          PROTO :DWORD,:DWORD,:DWORD,:DWORD
      TopXY            PROTO :DWORD,:DWORD
      RegisterWinClass PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
      MsgLoop          PROTO
      Main            PROTO
      Select_All      PROTO :DWORD
      ReEntryPoint    PROTO

      AutoScale MACRO swidth, sheight
        invoke GetPercent,sWid,swidth
        mov Wwd, eax
        invoke GetPercent,sHgt,sheight
        mov Wht, eax

        invoke TopXY,Wwd,sWid
        mov Wtx, eax

        invoke TopXY,Wht,sHgt
        mov Wty, eax
      ENDM

      DisplayWindow MACRO handl, ShowStyle
        invoke ShowWindow,handl, ShowStyle
        invoke UpdateWindow,handl
      ENDM

    .data?
      hInstance dd ?
      CommandLine dd ?
      hIcon dd ?
      hCursor dd ?
      sWid dd ?
      sHgt dd ?
      hWnd dd ?
      hEdit dd ?
      hMenu dd ?
      hfMnu dd ?
      heMnu dd ?

.code

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

ReEntryPoint proc    ;; <<<< This is the entry point

    ; ------------------
    ; set global values
    ; ------------------
      mov hInstance,  FUNC(GetModuleHandle, NULL)
      mov CommandLine, FUNC(GetCommandLine)
      mov hIcon,      FUNC(LoadIcon,NULL,IDI_APPLICATION)
      mov hCursor,    FUNC(LoadCursor,NULL,IDC_ARROW)
      mov sWid,        FUNC(GetSystemMetrics,SM_CXSCREEN)
      mov sHgt,        FUNC(GetSystemMetrics,SM_CYSCREEN)

      call Main

      invoke ExitProcess,eax

ReEntryPoint endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

Main proc

    LOCAL Wwd:DWORD,Wht:DWORD,Wtx:DWORD,Wty:DWORD

    STRING szClassName,"riched_Class"
    invoke RegisterWinClass,ADDR WndProc,ADDR szClassName,
                      hIcon,hCursor,NULL

    AutoScale 75, 70

    invoke CreateWindowEx,WS_EX_LEFT or WS_EX_ACCEPTFILES,
                          ADDR szClassName,
                          chr$("Untitled"),
                          WS_OVERLAPPEDWINDOW,
                          Wtx,Wty,Wwd,Wht,
                          NULL,NULL,
                          hInstance,NULL
    mov hWnd,eax

    mov hMenu, FUNC(CreateMenu)    ; main menu
    mov hfMnu, FUNC(CreateMenu)    ; file menu
    mov heMnu, FUNC(CreateMenu)    ; edit menu

  ; file menu

    invoke AppendMenu,hMenu,MF_POPUP,hfMnu,chr$("&File")
    invoke AppendMenu,hfMnu,MF_STRING,1000,chr$("&New",9,"Ctrl+N")
    invoke AppendMenu,hfMnu,MF_SEPARATOR,0,0
    invoke AppendMenu,hfMnu,MF_STRING,1001,chr$("&Open",9,"Ctrl+O")
    invoke AppendMenu,hfMnu,MF_SEPARATOR,0,0
    invoke AppendMenu,hfMnu,MF_STRING,1002,chr$("&Save",9,"Ctrl+S")
    invoke AppendMenu,hfMnu,MF_STRING,1003,chr$("Save &As")
    invoke AppendMenu,hfMnu,MF_SEPARATOR,0,0
    invoke AppendMenu,hfMnu,MF_STRING,1010,chr$("&Exit",9,"Alt+F4")

  ; edit menu

    invoke AppendMenu,hMenu,MF_POPUP,heMnu,chr$("&Edit")
    invoke AppendMenu,heMnu,MF_STRING,1100,chr$("&Undo",9,"Ctrl+Z")
    invoke AppendMenu,heMnu,MF_SEPARATOR,0,0
    invoke AppendMenu,heMnu,MF_STRING,1101,chr$("&Cut",9,"Ctrl+X")
    invoke AppendMenu,heMnu,MF_STRING,1102,chr$("C&opy",9,"Ctrl+C")
    invoke AppendMenu,heMnu,MF_STRING,1103,chr$("&Paste",9,"Ctrl+V")
    invoke AppendMenu,heMnu,MF_SEPARATOR,0,0
    invoke AppendMenu,heMnu,MF_STRING,1104,chr$("&Clear",9,"Del")
    invoke AppendMenu,heMnu,MF_SEPARATOR,0,0
    invoke AppendMenu,heMnu,MF_STRING,1105,chr$("&Copy All",9,"Ctrl+A")

    invoke SetMenu,hWnd,hMenu
    DisplayWindow hWnd,SW_SHOWNORMAL

    call MsgLoop
    ret

Main endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

RegisterWinClass proc lpWndProc:DWORD, lpClassName:DWORD,
                      Icon:DWORD, Cursor:DWORD, bColor:DWORD

    LOCAL wc:WNDCLASSEX

    mov wc.cbSize,        sizeof WNDCLASSEX
    mov wc.style,          CS_BYTEALIGNCLIENT or \
                          CS_BYTEALIGNWINDOW
    m2m wc.lpfnWndProc,    lpWndProc
    mov wc.cbClsExtra,    NULL
    mov wc.cbWndExtra,    NULL
    m2m wc.hInstance,      hInstance
    m2m wc.hbrBackground,  bColor
    mov wc.lpszMenuName,  NULL
    m2m wc.lpszClassName,  lpClassName
    m2m wc.hIcon,          Icon
    m2m wc.hCursor,        Cursor
    m2m wc.hIconSm,        Icon

    invoke RegisterClassEx, ADDR wc

    ret

RegisterWinClass endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

MsgLoop proc

    LOCAL rval  :DWORD
    LOCAL msg  :MSG

    StartLoop:
      invoke GetMessage,ADDR msg,NULL,0,0
      cmp eax, 0
      je ExitLoop

      Switch msg.message
      ; ------------------------------------
      ; menu hot key processing CTRL+Hotkey
      ; ------------------------------------
        Case WM_KEYDOWN
          mov rval, FUNC(GetAsyncKeyState,VK_CONTROL)
          cmp WORD PTR rval[2], 1111111111111111b
          jne @F
            Switch msg.wParam
              Case VK_A
                invoke Select_All,hEdit
                invoke SendMessage,hEdit,WM_COPY,0,0
                jmp StartLoop
              Case VK_C
                invoke SendMessage,hEdit,WM_COPY,0,0
                jmp StartLoop
              Case VK_N
                invoke SendMessage,hWnd,WM_COMMAND,1000,0
                jmp StartLoop
              Case VK_O
                invoke SendMessage,hWnd,WM_COMMAND,1001,0
                jmp StartLoop
              Case VK_S
                invoke SendMessage,hWnd,WM_COMMAND,1002,0
                jmp StartLoop
              Case VK_V
                invoke SendMessage,hEdit,EM_PASTESPECIAL,CF_TEXT,0
                jmp StartLoop
              Case VK_X
                invoke SendMessage,hEdit,WM_CUT,0,0
                jmp StartLoop
              Case VK_Z
                invoke SendMessage,hEdit,EM_UNDO,0,0
                jmp StartLoop
            Endsw
          @@:
      Endsw
      invoke TranslateMessage, ADDR msg
      invoke DispatchMessage,  ADDR msg
      jmp StartLoop
    ExitLoop:

    mov eax, msg.wParam
    ret

MsgLoop endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

WndProc proc hWin  :DWORD,
            uMsg  :DWORD,
            wParam :DWORD,
            lParam :DWORD

    LOCAL fname  :DWORD
    LOCAL patn  :DWORD
    LOCAL Rct    :RECT
    LOCAL buffer[MAX_PATH]:BYTE

    Switch uMsg
      Case WM_COMMAND
      ;======== menu commands ========
        Switch wParam
          Case 1000
            invoke SetWindowText,hEdit,0
            fn SetWindowText,hWin,"Untitled"

          Case 1001
            sas patn, "All files",0,"*.*",0
            mov fname, OpenFileDlg(hWin,hInstance,"Open File",patn)
            cmp BYTE PTR [eax], 0
            jne @F
            return 0
            @@:
            invoke Read_File_In,hEdit,fname
            invoke SetWindowText,hWin,fname

          Case 1002
            invoke GetWindowText,hWin,ADDR buffer,MAX_PATH
            fn szCmp,ADDR buffer,"Untitled"
            test eax, eax
            jnz Save_As
            invoke Write_To_Disk,hEdit,ADDR buffer

          Case 1003
            Save_As:
            sas patn, "All files",0,"*.*",0
            mov fname, SaveFileDlg(hWin,hInstance,"Save File As ...",patn)
            cmp BYTE PTR [eax], 0
            jne @F
            return 0
            @@:
            invoke Write_To_Disk,hEdit,fname
            invoke SetWindowText,hWin,fname

          Case 1010
            invoke SendMessage,hWin,WM_SYSCOMMAND,SC_CLOSE,NULL

        ;====== edit menu commands ======
          Case 1100
            invoke SendMessage,hEdit,EM_UNDO,0,0
          Case 1101
            invoke SendMessage,hEdit,WM_CUT,0,0
          Case 1102
            invoke SendMessage,hEdit,WM_COPY,0,0
          Case 1103
            invoke SendMessage,hEdit,EM_PASTESPECIAL,CF_TEXT,0
          Case 1104
            invoke SendMessage,hEdit,WM_CLEAR,0,0
          Case 1105
            invoke Select_All,hEdit
            invoke SendMessage,hEdit,WM_COPY,0,0

        Endsw
    ;====== end menu commands ======

      Case WM_SETFOCUS
        invoke SetFocus,hEdit

      Case WM_DROPFILES
        invoke Read_File_In,hEdit,DropFileName(wParam)

      Case WM_CREATE
        fn LoadLibrary,"RICHED32.DLL"
        mov hEdit, FUNC(RichEd1,0,0,100,100,hWin,hInstance,555,0)
        invoke SendMessage,hEdit,WM_SETFONT,FUNC(GetStockObject,ANSI_FIXED_FONT),0
        invoke SendMessage,hEdit,EM_EXLIMITTEXT,0,500000000
        invoke SendMessage,hEdit,EM_SETOPTIONS,ECOOP_XOR,ECO_SELECTIONBAR

      Case WM_SIZE
        invoke GetClientRect,hWin,ADDR Rct
        invoke MoveWindow,hEdit,0,0,Rct.right,Rct.bottom,TRUE

      Case WM_CLOSE

      Case WM_DESTROY
        invoke PostQuitMessage,NULL
        return 0

    Endsw

    invoke DefWindowProc,hWin,uMsg,wParam,lParam

    ret

WndProc endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

TopXY proc wDim:DWORD, sDim:DWORD

    shr sDim, 1      ; divide screen dimension by 2
    shr wDim, 1      ; divide window dimension by 2
    mov eax, wDim    ; copy window dimension into eax
    sub sDim, eax    ; sub half win dimension from half screen dimension

    return sDim

TopXY endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

Select_All Proc Edit:DWORD

    LOCAL tl :DWORD
    LOCAL Cr :CHARRANGE

    mov Cr.cpMin,0
    invoke GetWindowTextLength,Edit
    inc eax
    mov Cr.cpMax, eax
    invoke SendMessage,Edit,EM_EXSETSEL,0,ADDR Cr

    ret

Select_All endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

end ReEntryPoint
Title: Re: Creating menus dynamically
Post by: NoCforMe on March 31, 2025, 09:52:41 AM
It was the post after that I was responding to.
In any case, it was, it was a, it was just a joke, son ...
[cartoon reference for those old enough]