📜 ⬆️ ⬇️

Using macros in MASM using the example of creating a window

Back in 2001, I spent a lot of time studying assembler under Win32. Then, after long torment with writing the same code a hundred times over, I undertook to write for myself a small library of macros. As a result, it was possible to seriously alleviate the fate of myself and reduce the need to repeat huge code towels, if necessary, to write a simple program with one window.

Recently I stumbled upon those projects and decided to lay out some of them, who could come in handy ...


')
The composition of the project

So let's get started. The project attached below has the following structure.

\ MacrosDirectory with macros used in the application
Macros.IncHere are the main macros that are needed when writing any Win32 program. Here are macros to allocate memory, facilitate the inclusion of files, macros to define data, and so on.
Window.MacMacros that make window creation easier
Status.MacMacros for creating and using status lines
Menu.MacMacros for creating and using menus
Quake.BmpThe image that will be downloaded and displayed in the program window
Scull.IcoIcon image (just a shard)
Rsrc.rcResource Definition File
Window.AsmThe main program file
Window.ExeCompiled program
WndExample.AsmThis file contains the source code for processing messages going to the “Example” window of our program.

When you start Window.Exe, the displayed window will look like this:

image

Simplest program, no window


include macros\macros.inc @Start @Uses kernel32 .code WinMain Proc invoke ExitProcess, 0 WinMain Endp End WinMain 


The first line here is the inclusion of the main macros, followed by the Start macro, which creates the beginning of the program and substitutes information about the memory model, the processor used, and so on. Next comes the Uses macro; it includes the necessary library in the program. In this case, we will use kernel32.dll as it contains the function we use to complete the ExitProcess process.

Next comes the code block specified with .code , which contains the main program procedure. In fact, the procedure itself can be called whatever you like and the name WinMain I gave it just from the bald. The main thing is that at the end of the file there should be a line End {Name_of_function_ of entry point}

This program does not carry any functional load, so after launching it will not do anything - just complete its work. Now the source code of the program listed in the archive:
 include macros\macros.inc IDC_MAINSTATUS Equ 1 IDC_MENUEXIT Equ 10 @Start @Uses gdi32, user32, comctl32, kernel32 .xlist include macros\Menu.mac include macros\Window.mac include macros\Status.mac .list .data? hIcon Dd ? hBrush Dd ? hCursor Dd ? hImage Dd ? hInstance Dd ? @DefineMenu Menu @DefineStatus Example @DefineWindow Example .code ; Main program cycle WinMain Proc mov hInstance, @Result(GetModuleHandle, NULL) mov hIcon, @Result(LoadIcon, hInstance, 100) mov hCursor, @Result(LoadCursor,NULL,IDC_ARROW) mov hBrush, @Result(GetSysColorBrush, COLOR_APPWORKSPACE) @CreateWindow Example, hInstance, NULL,'Example_wnd', \ WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_VISIBLE, \ WS_EX_APPWINDOW, 'Example', \ hIcon, hBrush, hCursor, NULL @SetWndSize Example, 700, 600 @MoveWnd Example, 100, 100 @CreateMenu Menu @AppendMenu Menu, 'Exit', IDC_MENUEXIT @AttachMenu Example, Menu @CreateStatus Example, Example, IDC_MAINSTATUS @SetStatusParts Example, 2,300,-1,0,0,0,0,0,0,0,0 @SetStatusText Example, 'Example program window...', 0, 0 @SetStatusText Example, 'The CHEMI$T Copyright(C)2001', 0, 1 @ProcessMsgs Example, FALSE @DestroyMenu Menu @DestroyWindow Example invoke ExitProcess, 0 WinMain Endp End WinMain 


Now I will explain in stages what is happening in this source. First, the implementation macros include the menu, status lines and window functionality. They are framed by special commands of the macro assembler .xlist (disabling listing) and .list (listing inclusion) this was done only to ensure that if the listing is issued by the macroassembler, there is no code from these files / for only extra code towels / Next comes the description of the block of uninitialized data .data? , variables in this block are not initialized, the system simply allocates memory without resetting it. Such variables without initialization are fraught with use, for anything can be in memory. It also allocates space for variables that, in the first lines of the WinMain method, accept the values ​​of the loaded resources and the instance of the application itself.
The @DefineMenu, @DefineStatus and @DefineWindow macros initialize the variables in which the parameters of the objects / menu, the status of the line and the window respectively /
And after all the initializations comes the most interesting.
Four first lines
  mov hInstance, @Result(GetModuleHandle, NULL) mov hIcon, @Result(LoadIcon, hInstance, 100) mov hCursor, @Result(LoadCursor,NULL,IDC_ARROW) mov hBrush, @Result(GetSysColorBrush, COLOR_APPWORKSPACE) 

Initialize the application variables / instance, icon, cursor, brush to draw the window /. It uses a nice Result macro. This macro makes the specified API call with the passed parameters and returns the contents of the EAX register, which is used to return the results of the function. If this macro were not available, then each line would be split into a similar code:
  invoke GetModuleHandle, NULL mov hInstance, eax 

Macros for creating and working windows should be called sequentially, @CreateWindow - creates a window, @SetWndSize - sets the window size, @MoveWnd moves the window to the required coordinates on the screen, @ProcessMsgs fulfills the main message processing cycle going to your window, @DestroyWindow - deletes window. When you create a window, you need to create a file with event handlers for the window. In the above project, this is the WndExample.Asm file. This name is set so that the event handler file is automatically included by the mask Wnd <Window Name> .Asm
I didn’t finish macros for creating menus and for creating a status line, I did it only to the functionality I needed.
Menu macros:
@CreateMenu {Name_menu}
Creating a menu with the desired name

@AppendMenu {Name_name}, {Heading_Menu}, {Message_code}
Add a menu item with the desired title. By clicking on this menu item in the message queue will get the message code.

@AttachMenu {Window Name}, {Menu Name}
Adding a menu to the specified window.

Macros for working with the status line / ComCtl32 required /
@CreateStatus {Name_Status_String}, {Name_Window}, {ID_Status-line}
Creating row status at specified window

@SetStatusParts {Name_Status_String}, {Qty_parts}, {Width_parts}, {}, {}, {}, {} / Up to ten parts, the latter indicates the size = -1, i.e. stretch /
The division into several parts, this macro could be refined, but as it seems I did not do it then

@SetStatusText {Name_Status_String}, {Text}, {Style / I don’t remember why /}, {Part_Status_String}
Setting the status to the desired part of the status line


Event Handler File

This file contains the source text of the main window procedure in which user-defined handlers are registered and in which menu event handlers should also be registered. Each window event handler looks like this:
@WndHandlerStart {Window_Name}, {Handler_Name}

mov eax, TRUE
@WndHandlerEnd {WindowName}, {Handler_Name}

The main procedure in the above project is at the end of the file and looks like this:
 @WndProcedureBegin Example, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT ; Menu handlers @WndMenuHandler IDC_MENUEXIT, Exit ; Sample user handler @WndUserHandler Example, WM_SIZING @WndProcedureEnd Example 

Here a handler is assigned to the menu item to which the message ID is assigned IDC_MENUEXIT handler named Exit. Also, a custom WM_SIZING message handler is registered. A custom event handler must have the name of the message it is processing. All events that are pre-recorded in the window can be viewed in the Window.Mac file, in the @WndProcedureBegin macro. The list of these events is as follows: Close, Paint, Help, Activate, Deactivate, SysCommand, Show, Hide, Create, Destroy, KeyDown, KeyUp, Resize, DblClick, MouseUp, MouseDown, WheelDown, WheelUp. Examples of these handlers are included in the project source and you can click F1 in the window and spin the mouse wheel.

In principle, everything related to these events can be viewed in MSDN and source codes, there is nothing complicated in this description and I will not include it.


Compile the program


Compiling requires the masm32 package (can be found here ) After installation, it is advisable to put the path to the masm32 \ bin directory in the Path environment variable and edit the masm32 / bin / build.bat file by correcting the ml compiler call and linker to add the paths of libraries and include files and not I had to constantly write the path data in the code.

So in the ML.Exe call you need to add another parameter / IF: \ masm32 \ include - instead of F: \ masm32 you need to specify the path where you installed the masm32 package. And in two calls to the Link.exe linker, you need to add the path to the libraries using the / LIBPATH parameter: F: \ masm32 \ lib. Again, replace the path with one that matches yours.

Next, in the directory with the project we give two commands: bres ( bres.bat compiles the resource file rsrc.rc in the current directory) and after it the build window ( build.bat - compiles and links the project).

Projects shifted to GitHub

Source: https://habr.com/ru/post/107272/


All Articles