This is the last one of this series of tutorials. Below I may post the example of writing a game with compilation. Still the basis, facing beginners. Here, the original author would like to thank: james of support, the right to provide free Chinese translation of this article.
- Translator TAOWEN2002
13.0 Windows in Windows
In this chapter, we will create a program with a window.
12.1 window
You may have guessed the reason why Windows called Windows. In Windows, there are two programs: GUI programs and console programs. The program of console mode looks like a DOS program that runs in a -DOS-DOS. Most of the programs you use are the GUI (Graphical User Interface) program, which has a graphical interface for interacting with the user. This is done by the creation window. Almost everything you see in Windows is a window. First, create a parent window, then a self-window (control) like editing box, static control (text tag - translator's note), buttons, etc.
13.2 Window Class
Every window has a name. You define your own classes for your parent window. For controls, you can use Windows standard class names (for example, "Edit", "Static", "Button")
13.3 Structure
The window class in your program is registered with the "RegisterClassex" function. (RegisterClassex is the expansion version of RegisterClass, the latter is not used) The statement of this function is:
Atom RegisterClassex (
Const Wndlcassex * lpwcx // has the address of the structure of class data
);
LPWCX: Points to the WNDCLASSEX structure. Before passing it to the function, you must fill in the structure with the appropriate class properties.
The unique parameter is a pointer to the structure. Let's take a look at some basic knowledge:
A structure is a collection of variables (data). It defines the Struct:
SomeStructure STRUCTDWORD1 DD? DWORD2 DD? SOME_WORD DW? ABYTE DB? ANOTHERBYTE DB? SomeStructure Ends
(The structure name is not necessarily capitalized)
You can use the question mark to define your variable in the unaptified Data section. Now you can create a structure according to the definition:
Initialized
InitializedStructure SomeStructure <100, 200, 10, 'A', 90H>
Uninitialized
UninitializedStructure SomeStructure <>
In the first example, a new structure is created (saving its OFFSET with the initialized structure), and each element of the structure is filled in with the initialization value. The second example only tells MASM to allocate memory as the structuren, and each data element is initialized. After creating a structure, you can use it in your code:
Mov Eax, INITIALIZEDSTRUCTURE.SOME_WORD; EAX is now 10 Inc uninitializedstructure.dword1; structure DWORD1 step
This is how the structure is in memory:
Memory address
content
Offset of initializedstructure
100 (DWORD, 4 BYTES)
Offset of initializedstructure 4
200 (DWORD, 4 BYTES) Offset of InitializedStructure 8
10 (Word, 2 Bytes)
Offset of InitializedStructure 10
65 or 'a' (1 byte)
Offset of InitializedStructure 11
90h (1 byte)
12.3 WNDCLASSEX
Now I have already learned enough structural knowledge, let us handle RegisterClassex. In the Win32 Programmer Reference, you can find the definition of the WNDCLASSEX structure.
typedef struct _WNDCLASSEX {// wc UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm;} WNDCLASSEX;
Explanation
CBSIZE
The size of the WndClassex structure. Used for Windows certification. You can get its size with SizeOf: Mov Wc.cbsize, SizeOf WndClassex
Style
Specify a style for class (if the window must have a scroll bar, plus the redraint flag. Wait)
lpfnwndproc
Pointing to the pointer to the Windows process (there are more content behind this chapter)
Cbclsextra
How much additional memory is made after the Windows class structure. Don't be important for us
CbWndextra
How much additional memory is assigned after the Windows instance. Not important to us
Hinstance
The strength handle of your program. You can get this handle with the getmoudlehandle function
Hicon
Window icon resource handle
Hcursor
Window cursor resource handle
HBRBACKGROUND
The brush handle for filling the background, or one of the standard brush type, such as Color_Window, Color_BTNFACE, Color_Background.
Lpsz Genename
Pointing a zero tail string of a specified menu class name
LPSZCLASSNAME
Pointing a zero tail string of a specified window class name
Hiconsm
A small icon handle associated with a window class
Create a folder named FirstWindow in your Win32 folder and create a new file called Window.asm in this folder, enter the content:
.486.model flat, stdcalloption casemap: noneincludelib /masm32/lib/kernel32.libincludelib /masm32/lib/user32.libincludelib /masm32/lib/gdi32.libinclude /masm32/include/windows.incinclude /masm32/include/kernel32.incinclude /masm32/include/user32.incinclude /masm32/include/gdi32.inc
Then create a .bat file called make.bat. Paste these text into:
@echo offml / c / coff window.asmlink / subsystem: windows window.objpause> NUL
From now on, in order to save space, only the small segment code is displayed. You can display all code here throughout
Translator Note: For the convenience, I put these back. 13.4 Registration class
Now we register in the process called WinMain. The initialization of the window during this process is completed.
Join these to your assembly file:
Winmain Proto Stdcall: DWORD,: DWORD,: DWORD.DATA?
Hinstance DD?
.code
Invoke GetModuleHandle, Nullmov Hinstance, EaxIvoke Winmain, Hinstance, Null, Null, SW_SHOWNORMAL
End Start
These codes get the module handle through getModuleHandle and put the module handle into the Hinstance variable. This handle is frequently used in the Windows API. Then it calls the WinMain process. This is not an API function, but a process we will define. The prototype is: Winmain Proto StdCall: DWORD,: DWORD,: DWORD,: DWORD, and thus a function with 4 parameters:
Now put these code in End Start:
Winmain Proc Hinst: DWORD, HPREVINST: DWORD, CMDLINE: DWORD, CMDSHOW: DWORD
Retwinmain ENDP
You don't need to use this WinMain process at all, but this is a very common way to intermire your program. Visual C automatically initializes the parameters of this function, but we must do it yourself. Don't manage HPREVINST and CMDLINE now. Concentrated attention on Hinst and cmdshow. HinST is an instance handle (= module handle), and cmdshow is the sign of how the window is displayed. (You can find more about the showWindows section in the API reference section)
Invoke Winmain, Hinstance, Null, Null, SW_SHOWNORMAL in front code call this function with the correct instance handle and display flag. Now we can write our initialization code in WinMain.
Winmain Proc Hinst: DWORD, HPREVINST: DWORD, CMDLINE: DWORD: WNDCLASSEXLOCAL HWND: DWORD
Retwinmain ENDP
This has two partial variables we will use during the process.
.DATA
ClassName DB "firstwindowclass", 0.code
WinMain proc hInst: DWORD, hPrevInst: DWORD, CmdLine: DWORD, CmdShow: DWORDLOCAL wc: WNDCLASSEXLOCAL hwnd: DWORD; now set all the structure members of the WNDCLASSEX structure wc: mov wc.cbSize, SIZEOF WNDCLASSEXmov wc.style, CS_HREDRAW or CS_VREDRAWmov wc.lpfnWndProc, OFFSET WndProcmov wc.cbClsExtra, NULLmov wc.cbWndExtra, NULLpush hInstpop wc.hInstancemov wc.hbrBackground, COLOR_WINDOWmov wc.lpszMenuName, NULLmov wc.lpszClassName, OFFSET ClassNameinvoke LoadIcon, NULL, IDI_APPLICATIONmov wc.hIcon, eaxmov wc.hIconSm, EaxInvoke Loadcursor, Null, IDC_ARROWMOV WC.HCURSOR, EAXINVOKE RegisterClassex, Addr Wcretwinmain Endp
Let's take a look at what happened:
Mov wc.cbsize, sizeof wndclassexmov wc.style, cs_hredraw or cs_vredrawmov wc.lpfnwndproc, offset wndprocmov wc.cbclsextra, nullmov wc.cbwndextra, null
The size of the structure is initialized (this is registerclassex requirements). Set the style of "CS_HREDRAW or CS_VREDRAW" and then set the offset of the window process. What you will know later is a window process, now you only need to remember that you need the address of the WNDPROC process. This address can be obtained through "OFFSET WndProc". Cb.clsextra and Cb.Wndextra We did not use their NULL.
Push hinstpop wc.hinstance
Wc.hinstance is set to WinMain's Hinst parameters. Why don't we use: Mov wc.hinstance, hinst? Because the MOV instruction is not allowed to move from an address to another. With PUSH / POP, the value is pressed into the stack and then pop it into the target.
Mov wc.hbrbackground, color_windowmov wc.lpszmenuname, nullmov wc.lpszclassname, Offset ClassName
The background color of the class is set to Color_Window, and the menu is not defined and the LPSZClassName is set to a class name string pointing to zero end: "firstWindowClass" it should be a unique name defined in your program.
Invoke Loadicon, Null, IDi_Applicationmov Wc.hicon, Eaxmov Wc. Hiconsm, EAX
The window needs an icon. But because we want a handle to point to the icon, we use LoadCon to load the icon and get the handle. Loadicon has two parameters: hinstance and lpiconname. Hinstance is a module handle that contains an executable file of an icon. LPiconName is a pointer to a string of the icon resource and the icon ID. If you use null to Hinstance, you can choose this one from some standard charts (this is because we have no icon resources here) HiconSM is a small icon, you can use the same handle. Invoke LoadCursor, Null, IDC_Arrowmov Wc.hcursor, EAX
The same is true for the cursor. NULL is Hinstance, and uses a standard cursor type: IDC_ARROW, standard Windows arrow type cursor.
Invoke RegisterClassex, Addr WC
Now, finally use registerclassex to register class, by a pointer to the WndClassex structure.
13.5 Creating a window
Now, you have already registered a class, you can use it to create a window:
HWND CREATEWINDOWEX
DWORD dwExStyle, // extended window styleLPCTSTR lpClassName, // pointer to registered class nameLPCTSTR lpWindowName, // pointer to window nameDWORD dwStyle, // window styleint x, // horizontal position of windowint y, // vertical position of windowint nWidth, / / window widthint nHeight, // window heightHWND hWndParent, // handle to parent or owner windowHMENU hMenu, // handle to menu, or child-window identifierHINSTANCE hInstance, // handle to application instanceLPVOID lpParam // pointer to window-creation data) ;
DWEXStyle and DWStyle are parameters for two decisive window styles.
LPClassName is a pointer to class names you registered.
LPWindowname is the name of your window (if any, this will become the title of your window)
X, Y, nwidth, nheight determines the location and size of your window
HMENU is the handle of the menu window (discussed later, now empty)
Hinstance is the handle of the program instance
LPPARARM is the extension value you can use in your program
.DATA
Appname "firstwindow", 0
.code
INVOKE CreateWindowEx, NULL, ADDR ClassName, ADDR AppName, / WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, / CW_USEDEFAULT, 400,300, NULL, NULL, / hInst, NULLmov hwnd, eaxinvoke ShowWindow, hwnd, SW_SHOWNORMALinvoke UpdateWindow, hwnd
(Note / seem to be in the same line when the assembler reads a line)
Our code will create a new window with our just registered class name. The title is "firstwindow" (program name, appname), the style is WS_OVERLAPPEDWINDOW, which is a window style that creates a title, system menu, a scalable border, and maximizing / minimizing button. CW_USERDEFAULT The location of X and Y will make Windows use the default location for the new window. The (initial) size of the window is 400 × 300 pixels. The return value of the function is the window handle, hwnd. It is stored in a local variable hwnd. Then the window is displayed with ShowWindow. UpdateWindow make sure the window is drawn.
13.6 message loop
The window can communicate through the message and your program and other window. Whenever, a message is sent to the specified window. Its window procedure is called. Each window has a message loop or message pump (PUMP). This is an endless check whether there is a loop for messages with your window. And if there is, pass the message to the DispatchMessage function. This function calls your window process. The message loop and window process are two completely different things! ! !
Winmain Proc Hinst: Hinstance, Hprevinst: Hinstance, Cmdline: lpstr, cmdshow: dwordlocal wc: wndclassexlocal hwnd: dwordlocal msg: msg; <<< New
........
. While Trueinvoke GetMessage, Addr MSG, NULL, 0, 0.Break .if (! EAX) Invoke TranslateMessage, Addr Msginvoke DispatchMessage, Addr Msg.Endw
This is what the message loop looks like. .While true,. Nendw will continue before the EAX is 0. If it receives a WM_QUIT message, getMessage returns 0, which will close the window thus to exit when getMessage returns 0. If this is not this (0), the message is passed to TranslateMessage (this function translates the button to messages) and the message is unpackaged by Windows with the DispatchMessage function. The message itself is in the component of a message loop in the MSG structure (Local MSG: MSG is joined the process, adding a local message structure called MSG) You can use this message in all your programs.
13.7 window process
The message will be sent to the window process. One window process looks like this:
WNDPROC Proto Stdcall: DWORD,: DWORD,: DWORD,: DWORD
.code
WndProc proc hWnd: DWORD, uMsg: DWORD, wParam: DWORD, lParam: DWORDmov eax, uMsg.IF eax == XXXX.ELSEIF eax == XXXX.ELSE invoke DefWindowProc, hWnd, uMsg, wParam, lParam.ENDIFretWndProc endp
Window process always has 4 parameters
HWnd contains window handles
UMSG message
The first parameter of the WPARAM message (defined by message)
LPARAM message second parameter (defined by message)
The message that the window does not processes should be passed to the DefWindowProc, which will handle it. An example of a window process:
WndProc proc hWnd: DWORD, uMsg: DWORD, wParam: DWORD, lParam: DWORDmov eax, uMsg.IF eax == WM_CREATE invoke MessageBox, NULL, ADDR AppName, ADDR AppName, NULL.ELSEIF eax == WM_DESTROY invoke PostQuitMessage, NULL.ELSE Invoke DefWindowProc, HWND, UMSG, WPARAM, LPARAM.ENDIFRETWNDPROC ENDP This code shows the program name when the window is initialized. Also note that I have added the processing of the WM_DESTROY message. This message is sent when the window will be turned off. The program should be reacted with PostquitMessage.
Take a look at the final code:
.486
.Model flat, stdcall
Option CaseMAP: NONE
INCLUDELIB /MASM32/LIB/kernel32.lib
INCLUDELIB /MASM32/LIB/USER32.LIB
INCLUDELIB /MASM32/LIB/GDI32.LIB
INCLUDE /MASM32/INCLUDE/Windows.inc
INCLUDE /MASM32/INCLUDE / WANEL32.INC
INCLUDE /MASM32/INCLUDE/USER32.INC
INCLUDE /MASM32/INCLUDE/gdi32.inc
Winmain Proto Stdcall: DWORD,: DWORD,: DWORD,: DWORD
WNDPROC Proto Stdcall: DWORD,: DWORD,: DWORD,: DWORD
.DATA?
Hinstance DD?
.DATA
ClassName DB "firstwindowclass", 0
Appname DB "firstwindow", 0
.code
Start:
Invoke getModuleHandle, NULL
Mov Hinstance, EAX
Invoke Winmain, Hinstance, Null, NULL, SW_SHOWNORMAL
Invoke EXITPROCESS, NULL
Winmain Proc Hinst: Hinstance, Hprevinst: Hinstance, Cmdline: lpstr, cmdshow: DWORD
Local WC: WNDCLASSEX
Local hwnd: DWORD
Local MSG: MSG
Mov wc.cbsize, sizeof wndclassex
Mov wc.style, CS_HREDRAW or CS_VREDRAW
Mov wc.lpfnwndproc, Offset WndProc
Mov wc.cbclsextra, NULL
Mov wc.cbwndextra, null
Push hinst
POP wc.hinstance
Mov wc.hbrbackground, Color_Window
Mov wc.lpszMenuname, NULL
MOV wc.lpszclassname, Offset ClassName
Invoke Loadicon, NULL, IDI_APPLICATION
Mov wc.hicon, EAX
Mov wc.hiconsm, EAX
Invoke loadcursor, null, IDC_ARROW
Mov wc.hcursor, EAX
Invoke RegisterClassex, Addr Wcinvoke CreateWindowex, Null, Addr ClassName, AddR Appname, /
WS_OVERLAPPEDWINDOW-WS_SIZEBOX-WS_MAXIMIMIMIZEBOX, CW_USEDEFAULT, /
CW_USEDEFAULT, 400, 300, NULL, NULL, /
Hinst, null
MOV HWND, EAX
Invoke ShowWindow, Hwnd, SW_SHOWNORMAL
Invoke UpdateWindow, HWnd
.While true
Invoke GetMessage, Addr MSG, NULL, 0, 0
.Break .if (! EAX)
Invoke TranslateMessage, Addr MSG
Invoke DispatchMessage, Addr MSG
.Endw
Mov Eax, Msg.wParam
RET
Winmain ENDP
WNDPROC PROC HWND: DWORD, UMSG: DWORD, WPARAM: DWORD, LPARAM: DWORD
MOV EAX, UMSG
.IF EAX == WM_CREATE
Invoke Messagebox, Null, Addr Appname, Addr Appname, Null
.ELSEIF EAX == WM_DESTROY
Invoke PostquitMessage, NULL
.Lse
Invoke DefWindowProc, Hwnd, UMSG, WPARAM, LPARAM
.Endif
RET
WNDPROC ENDP
End Start