Third lesson created a simple window
In this lesson we will write a Windows program, which will display a standard window at the desktop.
theory:
In the Windows program, you need to call a large number of standard Windows GUI functions when you write a graphical user interface. In fact, this is good for users and programmers. For users, it is the same set of standard windows, which is the same for these windows, so there is no need to re-learn when using different applications. For programmers, these GUI source code have been strictly tested by Microsoft, which can be used at any time. Of course, it is still difficult to specifically write a program for programmers. In order to create a window-based application, the specifications must be strictly observed. This is not difficult to make it, just use modular or object-oriented programming methods.
Below I will list a few steps to display a window at the desktop:
Get your application's handle (required); get the command line parameter (if you want to get parameters from the command line, optional); Register window classes (required, unless you use Windows predefined window classes, such as MessageBox or Dialog box; Generate a window (required); on the desktop display window (required, unless you do not want to display it immediately); refresh the window client area; enter the loop of unlimited acquisition window messages; if there is a message arrival, the window callback function responsible for the window; If the user turns off the window, the exit process is performed.
Compared to the programming of the single-use household, the program frame structure under Windows is quite complicated. But Windows and DOS are very different in the system architecture. Windows is a multitasking operating system, so there are multiple applications in the system to run together with each other. This requires Windows programmers to strictly abide by programming specification and develop a good programming style.
content:
Below is the source code of our simple window program. Before entering the complex details, I pointed out some points in the outline:
You should put all the constants and structures of all the procedures in the program in a header file, and this header file is included in the start of the source program. This will save you a lot of time, and you can also remove it again and again. At present, the most complete header file is written by hutch, you can download it in Hutch or my website. You can also define your own constants and structures, but it is best to put them in a separate header file with the includeLELIB instruction, contain your program to reference library files, such as: If your program wants to call "MessageBox", You should add the following line in the source file: includeLudelib user32.lib This statement tells that your program will use some introduction libraries. If you don't have to reference a library, just simply join the includeLib statement, don't worry about how the linker handles so many libraries, just use the link switch / libpath to indicate the path where the library is located in the link. When using the function prototype, constant, and structures in the header file, it is necessary to keep the definition in the header file, including uppercase. When the query function is defined, this will save you a lot of time; in compilation, use the Makefile file when the link is used, it is eliminated.
.386 .model flat, stdcall option casemap: none include /masm32/include/windows.inc include /masm32/include/user32.inc includelib /masm32/lib/user32.lib; calls to functions in user32.lib and kernel32.lib Include /masm32/include/kernel32.incinc includelib /masm32/lib/kernel32.libwinmain Proto: DWORD,: DWORD,: DWORD,: DWORD
.Data; Initialized Data ClassName DB "SimpleWinclass", 0; The name of our window Class Appname DB "Our First Window", 0; The Name of Our Window
??? .DATA; Uninitialized data hInstance HINSTANCE; Instance handle of our program CommandLine LPSTR .CODE; Here begins our code start: invoke GetModuleHandle, NULL; get the instance handle of our program; Under Win32, hmodule == hinstance mov hInstance. , eax mov hInstance, eax invoke GetCommandLine;. get the command line You do not have to call this function IF; your program does not process the command line mov CommandLine, eax invoke WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT.; Call the main function invoke EXITPROCESS, EAX; Quit Our Program. The exit code is returned in Eax from WinMain.
Winmain Proc Hinst: Hinstance, Hprevinst: Hinstance, Cmdline: lpstr, cmdshow: dword local wc: wndclassex; Create Local Variables On Stack Local MSG: MSG local hwnd: hwnd
mov wc.cbSize, SIZEOF WNDCLASSEX; fill values in members of wc mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra, NULL mov wc.cbWndExtra, NULL push hInstance pop wc.hInstance mov wc. hbrBackground, COLOR_WINDOW 1 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 wc; register our window class invoke CreateWindowEx, NULL, / aDDR ClassName, / aDDR AppName, / WS_OVERLAPPEDWINDOW, / CW_USEDEFAULT, / CW_USEDEFAULT, / CW_USEDEFAULT, / CW_USEDEFAULT, / NULL, / NULL, / hInst, / NULL mov hwnd, EAX invoke ShowWindow, hwnd, CmdShow; display our window on desktop invoke UpdateWindow, hwnd; refresh the client area.WHILE TRUE; Enter message loop invoke GetMessage, ADDR msg, NULL, 0,0 .BREAK .IF (eax!) invoke TranslateMessage, Addr Msg Invoke DispatchMess Beach
WndProc proc hWnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM .IF uMsg == WM_DESTROY; if the user closes our window invoke PostQuitMessage, NULL; quit our application .ELSE invoke DefWindowProc, hWnd, uMsg, wParam, lParam ; DEFAULT Message Processing Ret .endif Xor Eax, Eax Ret WndProc Endped Start
analysis:
Seeing a simple Windows program so many lines, do you have a bit trying to withdraw? But you must know that most of the code above is template. The template means that these code is almost all standard Windows programs. It is the same. When you write a Windows program, you can copy these code to copy it, of course, write these duplicate code is also very good. In fact, the code to be written is concentrated in WinMain. This is the same as some C compilers, there is no need to care for other miscellators, concentrate on the WinMain function. The only difference is that the C compiler requires your source code to have a function called WinMain. Otherwise, C cannot know which function and the relevant front-rear code link. Relative C, assembly language provides greater flexibility, it is not forcing a function called WinMain.
Below we are analyzing, you can do your idea, this is not a very easy way.
.386.model flat, stdcalloption casemap: noneWinMain proto: DWORD,: DWORD,: DWORD,: DWORDinclude /masm32/include/windows.incinclude /masm32/include/user32.incinclude /masm32/include/kernel32.incincludelib / masm32 / lib /user32.libincludelib /masm32/lib/kernel32.lib You can see the top three lines as "must" ..386 Tell MASN We want to use 80386 instructions. . Model flat, stdcall tells MASM we use the memory addressing mode, you can also join StdCall to tell MASM we use the parameter transfer agreement.
Next is the prototype of the function Winmain, because we will use the function later, so you must first declare. We must include a Window.inc file because there is a definition of a large number of constants and structures to be used. This file is a text file, you can open it with any text editor, Window.inc has not included all constants and structures Definition, but hutch and I have been continuously added new content. If you are temporarily can't find in Window.inc, you can also join.
Our program calls the function in User32.dll (such as: CreateWindowex, RegisterWindowClassex), and kernel32.dll (exitprocess), so you must link these two libraries. I will ask if you ask: What library does you need to link your program? The answer is: First check the function you want to call in what library, and then it contains. For example, if you want to call the function in GDI32.DLL, you have to include the gdi32.inc header file. Compared with MASM, TASM is much more simple, you will introduce a library, ie: import32.lib. .Codestart: Invoke getModuleHandle, Nullmov Hinstance, EaxInvoke getcommandlinemov Commandline, EaxInvoke Winmain, Hinstance, Null, CommandLine, SW_SHOWDEFAULTINVOKE EXITPROCESS, END START .DATA "Segment" contains all the code of your application, these code must be .code and end between. As for Label naming, just comply with the Windows specification and the only thing is nothing, it doesn't matter. The first statement of our program is to call getModuleHandle to find the handle of our application. Under Win32, the handle of the application and the handle of the module are the same. You can regard the instance handle as the ID number of your application. We call several functions to pass it as a parameter, so you can save a lot at the beginning and save it. Special note: The unique handle under Win32 is actually a linear address of your application in memory. The function of the function in Win32 is if there is a return value, which is passed through the EAX register. Other values can be returned by passing the incoming parameter addresses. When a Win32 function is called, the segment register and EBX, EDI, ESI and EBP registers are always saved, while the values in ECX and EDX are always unfained, and they cannot be applied. Special Note: After returning from the Windows API function, the values in EAX, ECX, and EDX are not necessarily the same. When the function returns, the return value is placed in EAX. If the function in your application is supplied to Windows, you must also respect this, that is, save the value of segment registers and EBX, ESP, ESI, EDI, and recover when the function is returned. If you don't have one, your app will crash soon. There are two functions that are provided to Windows calls from your program: Windows window procedures and callback functions. If your application does not handle the command line, you don't have to call getcommandline, just tell you how to do it if you want to call. The following is called WinMain. This function has 4 parameters: an instance handle of the application, the previous example handle of the application, how to display the command line parameter string pointer and window. Win32 does not have the concept of the previous example handle, so the second parameter is always 0. The reason why it retains it is for Win16 compatibility, in Win16, if HPREVINST is NULL, the function is the first time. Special attention: You don't have to declare a Winmain function, in this respect, you can do full, you don't even have one and WinMain equivalent. As long as you copy the code in WinMain to getcommandline, the features you implement are identical. Place the return code in Eax when WinMain returns. Then passes the return code to Windows via the EXITPROCESS function at the end of the application. Winmain Proc Inst: Hinstance, Hprevinst: Hinstance, Cmdline: lpstr, cmdshow: DWORD The above is the definition of WinMain. Pay attention to the parameters of Parameter: Type after the Proc Directive, which is passed by the caller to WinMain, and we reference the direct parameter name. As for the balance stack working in the stack and the decont, it is performed by the MASM to add relevant preamble and rear sequence instructions when compiling. Local WC: WNDCLASSEX LOCAL MSG: MSG local hwnd: hWnd local directive assigns memory space in the stack, all Local instructions must follow the proc. Local followed by the declared variable, its form is the variable name: variable type . For example, local wc: wndclassex is to tell MASM to allocate a memory space for the length of the Wndclassex structure in the stack in the stack, and then we do not need to consider the problem with the stack with this part variable. Compilation, this can't be said to be a gift. However, this requires such a local variable to release the stack space at the end of the function, and cannot be referenced outside the function outside the function. Another disadvantage is that you cannot initialize your local variables, you have to assign it later. . mov wc.cbSize, SIZEOF WNDCLASSEXmov wc.style, CS_HREDRAW or CS_VREDRAWmov wc.lpfnWndProc, OFFSET WndProcmov wc.cbClsExtra, NULLmov wc.cbWndExtra, NULLpush hInstancepop wc.hInstancemov wc.hbrBackground, COLOR_WINDOW 1 mov wc.lpszMenuName, NULLmov wc.lpszClassName , OFFSET ClassName invoke LoadIcon, NULL, IDI_APPLICATIONmov wc.hIcon, eaxmov wc.hIconSm, eaxinvoke LoadCursor, NULL, IDC_ARROWmov wc.hCursor, eax invoke RegisterClassEx, addr few lines conceptually very simple indeed w above. As long as a few lines of instructions are implemented. The main concept is Window Class, a window class is a standard for windows. This specification defines elements of several major windows, such as: icons, cursors, background colors, and functions responsible for handling the window. . You must have such a window class when you generate a window. If you want more than one type of window, the best way is to store this window class, which can save a lot of memory space. Maybe you won't feel too much today, but think that most of the PC is only 1M memory, it is necessary. If you want to define your own creation window, you must: Indicate the component elements of your window in a WindClass or WindowClassexe structure, then call RegisterClass or RegisterClassex, and then generate a window according to the window class. Different window classes must be defined for different features. Windows has several predefined window classes, such as buttons, edit boxes, etc. To generate this style, there is no need to define the window class in advance, as long as the package predefined class class name is called CREATEWINDOWEX. The most important member in Wndclassex is LPFNWndProc. The prefix lpfn indicates that the member is a long pointer to a function. Since the memory mode is a FLAT type in Win32, there is no difference in NEAR or FAR. Each window class must have a window process that when Windows sends a message belonging to a particular window to the window, the window class of the window is responsible for processing all messages such as keyboard messages or mouse messages. Since all window messages are handled almost intelligently, you can join the message processing process in it. Below I will explain every member of WndClassex ? WNDCLASSEX STRUCT DWORD cbSize DWORD style DWORD lpfnWndProc DWORD cbClsExtra DWORD cbWndExtra DWORD hInstance DWORD hIcon DWORD hCursor DWORD hbrBackground DWORD lpszMenuName DWORD lpszClassName DWORD hIconSm DWORD WNDCLASSEX ENDScbSize:??????????? WNDCLASSEX size. We can use SizeOf (WndClassex) to get accurate values. Style: From this window class, the window has a style. You can use the "OR" operator to put a few styles or go together. LpfnWndProc: Pointer for Window Processing Functions. Cbclsextra: Specifies the number of additional bytes immediately following the window structure. CBWndextra: Specifies the number of additional bytes that keep follows the window case. If an application registers a dialog box class in the resource, you must set this member into DLGWindowextra. Hinstance: The case handle of this module. Hicon: Icon handle. HCURSOR: The handle of the cursor. HbRBackground: handle of background painting brush. LPSZMENUNAME: Pointer to the menu. LPSZClassName: Pointer to the class name. HiconSM: Small icon associated with window classes. If the value is null. Then convert the icons in the HCURSOR to the size suitable small icon. invoke CreateWindowEx, NULL, / ADDR ClassName, / ADDR AppName, / WS_OVERLAPPEDWINDOW, / CW_USEDEFAULT, / CW_USEDEFAULT, / CW_USEDEFAULT, / CW_USEDEFAULT, / NULL, / NULL, / hInst, / NULL After registering the window class, we will call CREATEWINDOWEX to generate a practical window. Note that this function has 12 parameters. CreateWindowexa Proto DWEXStyle: DWORD, / LPCLASSNAME: DWORD, / LPWINDOWNAME: DWORD, / DWSTYLE: DWORD, / NWORD, / Y: DWORD, / NWIDTH: DWORD, / NHEIGHT: DWORD, / HWNDPART: DWORD, / HMENU: DWORD , / Hinstance: DWORD, / LPPARAM: DWORD Let's take a closer look at these parameters: DWEXStyle: Additional window style. This is a new parameter relative to the old CREATEWINDOW. You can use the new window style in 9x / NT. You can specify a general window style in Style, but some special window styles, such as the top window, must be specified in this parameter. If you don't want to specify any special style, set this parameter to NULL. LPClassName: (must). The address of the ASCIZ form of the window class name. Can be a class you customized or a predefined class name. As mentioned above, each application must have a window class. LPWindownAme: The address of the window name in the form of Asciiz. This name will be displayed on the title bar. If the parameter is blank, there is no in the title bar. DWStyle: Window style. Here you can specify the appearance of the window. You can specify that the parameter is zero, but then the window does not have the system menu, nor does it maximize and minimizes the button, and does not turn off the button, so you have to press Alt F4 to close it. The most popular window style is WS_OVERLAPPEDWINDOW. A window style is a bitmap mask so you can use "OR" to put your desired window style or get up. Like WS_OVERLAPPEDWindow is made by several of the most inconvenient and universal styles. X, Y: Specifies the screen coordinate position of the pixel unit in the upper left corner of the window. Default can be specified as cw_usedefault so Windows automatically specifies the most appropriate location for the window. NWIDTH, NHEIGHT: The window size in pixels. Default can be specified as CW_USEDEFAULT so that Windows automatically specifies the most appropriate size for the window. HWndParent: The handle of the parent window (if any). This parameter tells Windows This is who is a child window and his parent window. This is different from the MDI (multi-document structure), which is not limited to the client area of the parent window. He just used to tell the father and child relationship between the windows in order to destroy its sub-window in the parent window. In our case, this parameter is set to null because there is only one window. HMENU: Handle of the Windows menu. If only the system menu is used, this parameter is NULL. Looking back, look at the lpszMenuname parameters in the WndClassex structure, which also specifies a menu, which is a default menu, any window derived from the window, if you want to use another menu, you need to re-specify in this parameter. In fact, this parameter has a double significance: On the one hand, this parameter represents the menu handle when this is a custom window. On the other hand, if this is a predefined window, the parameter represents the ID number of the window. Windows is based on the LPClassName parameter to distinguish between custom windows or predefined windows. Hinstance: An instance handle of the application that generates the window. LPPARAM: (Optional) Pointer to the structural data type parameters of the window. The parameters of the ClientCreatestruct structure are transmitted when the window is generated in MDI. In general, this value is always zero, which means that there is no parameter to pass to the window. This value can be retrieved through the getWindowlong function. Mov hwnd, eaxinvoke showwindow, hwnd, cmdshowinvoke UpdateWindow, HWND call CREATEWINDOWEX successfully, window handle in EAX. We must save this value for later use. The window we just generated will not be displayed automatically, so you must call ShowWindow to display the window in the way we want. Next, you call UpdateWindow to update the client area. .While Trueinvoke GetMessage, Addr MSG, NULL, 0, 0.Break .if (! EAX) Invoke TranslateMessage, Addr Msg INVOKE DISPATCHMESSAGE, ADDR MSG.Endw At this time, our window has been displayed on the screen. But it can't receive messages from the outside world. So we must give it a related message. We are working through a message loop. Each module has only one message loop, and we constantly call GetMessage to get messages from Windows. GetMessage passed a MSG structure to Windows, then Windows fills the relevant message in this function, until Windows finds and fills the message, getMessage will return. System control may be transferred to other applications during this time. This constitutes a multitasking structure under Win16. If getMessage receives the WM_QUIT message, it will return false to end the loop and exit the application. The TranslateMessage function is a utility that accepts the original button message from the keyboard, then explains to WM_CHAR, put the WM_CHAR in the message queue, which contains the ASCII code of the button after explaining the "ASCII code", which is better than the original scan code. Much more. If your application does not process the button message, you may not call this function. DispatchMessage will send messages to functions responsible for the window process. Mov Eax, Msg.wParamretwinmain Endp If the message loop is over, the exit code is stored in the WPARAM in the MSG, you can pass it in the EAX register to Windows Current Windows does not use this end code, but we are best to comply with the Windows specification has been prevented. WNDPROC PROC HWND: HWND, UMSG: UINT, WPARAM: WPARAM, LPARAM: LPARAM It is our window handler. You can name this function casually. The first parameter hWnd is the handle of the window that receives the message. UMSG is the received message. Note that uMSG is not an MSG structure, in fact, just a number of dword types. Windows defines hundreds of messages, most of your applications will not process. When the message of this window occurs, Windows sends a correlation message to the window. Its window procedure handle these messages intelligently. WPARAM and LPARAM are just additional parameters to facilitate data related to this message. .IF uMSG == WM_DESTROYINVOKE POSTQUITMESSAGE, NULL.ELSE INVOKE DEFWINDOWPROC, HWND, UMSG, WPARAM, LPARAMRET.ENDIFXOR EAX, EAX RETWNDPROC ENDP