📄 lion-tutorial03.htm
字号:
program to death!
<ul>
<b>.386</b> <br>
<b>.model flat,stdcall</b> <br>
<b>option casemap:none</b>
<p><b>WinMain proto :DWORD,:DWORD,:DWORD,:DWORD</b>
<p><b>include \masm32\include\windows.inc</b> <br>
<b>include \masm32\include\user32.inc</b> <br>
<b>include \masm32\include\kernel32.inc</b> <br>
<b>includelib \masm32\lib\user32.lib</b> <br>
<b>includelib \masm32\lib\kernel32.lib</b>
</ul>
The first three lines are "necessities". .386 tells MASM we intend to use 80386
instruction set in this program. .model flat,stdcall tells MASM that our program
uses flat memory addressing model. Also we will use stdcall parameter passing
convention as the default one in our program. <br>
Next is the function prototype for WinMain. Since we will call WinMain later,
we must define its function prototype first so that we will be able to invoke
it. <br>
We must include windows.inc at the beginning of the source code. It contains important
structures and constants that are used by our program. The include file , windows.inc,
is just a text file. You can open it with any text editor. Please note that windows.inc
does not contain all structures, and constants (yet). hutch and I are working
on it. You can add in new items if they are not in the file. <br>
Our program calls API functions that reside in user32.dll (CreateWindowEx, RegisterWindowClassEx,
for example) and kernel32.dll (ExitProcess), so we must link our program to those
two import libraries. The next question : how can I know which import library
should be linked to my program? The answer: You must know where the API functions
called by your program reside. For example, if you call an API function in gdi32.dll,
you must link with gdi32.lib. <br>
This is the approach of MASM. TASM 's way of import library linking is much more
simpler: just link to one and only one file: import32.lib.
<ul>
<b>.DATA</b> <br>
<b> ClassName db "SimpleWinClass",0</b> <br>
<b> AppName db "Our First Window",0</b>
<p><b>.DATA?</b> <br>
<b>hInstance HINSTANCE ?</b> <br>
<b>CommandLine LPSTR ?</b>
</ul>
Next are the "DATA" sections. <br>
In .DATA, we declare two zero-terminated strings(ASCIIZ strings): ClassName which
is the name of our window class and AppName which is the name of our window. Note
that the two variables are initialized. <br>
In .DATA?, two variables are declared: hInstance (instance handle of our program)
and CommandLine (command line of our program). The unfamiliar data types, HINSTANCE
and LPSTR, are really new names for DWORD. You can look them up in windows.inc.
Note that all variables in .DATA? section are not initialized, that is, they don't
have to hold any specific value on startup, but we want to reserve the space for
future use.
<ul>
<b>.CODE</b> <br>
<b> start:</b> <br>
<b> invoke GetModuleHandle, NULL</b> <br>
<b> mov hInstance,eax</b> <br>
<b> invoke GetCommandLine</b> <br>
<b> mov CommandLine,eax</b> <br>
<b> invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT</b>
<br>
<b> invoke ExitProcess,eax</b> <br>
<b> .....</b> <br>
<b>end start</b>
</ul>
.CODE contains all your instructions. Your codes must reside between <starting
label>: and end <starting label>. The name of the label is unimportant. You
can name it anything you like so long as it is unique and doesn't violate the
naming convention of MASM. <br>
Our first instruction is the call to GetModuleHandle to retrieve the instance
handle of our program. Under Win32, instance handle and module handle are one
and the same. You can think of instance handle as the ID of your program. It is
used as parameter to several API functions our program must call, so it's generally
a good idea to retrieve it at the beginning of our program. <br>
<b>Note: Actually under win32, instance handle is the linear address of your program
in memory.</b> <br>
Upon returning from a Win32 function, the function's return value, if any, can
be found in eax. All other values are returned through variables passed in the
function parameter list you defined for the call. <br>
A Win32 function that you call will nearly always preserve the segment registers
and the ebx, edi, esi and ebp registers. Conversely, ecx and edx are considered
scratch registers and are always undefined upon return from a Win32 function.
<br>
<b>Note: Don't expect the values of eax, ecx, edx to be preserved across API function
calls.</b> <br>
The bottom line is that: when calling an API function, expects return value in
eax. If any of your function will be called by Windows, you must also play by
the rule: preserve and restore the values of the segment registers, ebx, edi,
esi and ebp upon function return else your program will crash very shortly, this
includes your window procedure and windows callback functions. <br>
The GetCommandLine call is unnecessary if your program doesn't process a command
line. In this example, I show you how to call it in case you need it in your program.
<br>
Next is the WinMain call. Here it receives four parameters: the instance handle
of our program, the instance handle of the previous instance of our program, the
command line and window state at first appearance. Under Win32, there's NO previous
instance. Each program is alone in its address space, so the value of hPrevInst
is always 0. This is a leftover from the day of Win16 when all instances of a
program run in the same address space and an instance wants to know if it's the
first instance. Under win16, if hPrevInst is NULL, then this instance is the first
one. <br>
Note: You don't have to declare the function name as WinMain. In fact, you have
complete freedom in this regard. You don't have to use any WinMain-equivalent
function at all. You can paste the codes inside WinMain function next to GetCommandLine
and your program will still be able to function perfectly. <br>
Upon returning from WinMain, eax is filled with exit code. We pass that exit code
as the parameter to ExitProcess which terminates our application.
<p><b>WinMain proc Inst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD</b>
<p>The above line is the function declaration of WinMain. Note the parameter:type
pairs that follow PROC directive. They are parameters that WinMain receives
from the caller. You can refer to these parameters by name instead of by stack
manipulation. In addition, MASM will generate the prologue and epilogue codes
for the function. So we don't have to concern ourselves with stack frame on
function enter and exit.
<p><b> LOCAL wc:WNDCLASSEX</b> <br>
<b> LOCAL msg:MSG</b> <br>
<b> LOCAL hwnd:HWND</b>
<p>LOCAL directive allocates memory from the stack for local variables used in
the function. The bunch of LOCAL directives must be immediately below the PROC
directive. The LOCAL directive is immediately followed by <the name of local
variable>:<variable type>. So LOCAL wc:WNDCLASSEX tells MASM to allocate
memory from the stack the size of WNDCLASSEX structure for the variable named
wc. We can refer to wc in our codes without any difficulty involved in stack
manipulation. That's really a godsend, I think. The downside is that local
variables cannot be used outside the function they're created and will be automatically
destroyed when the function returns to the caller. Another drawback is that
you cannot initialize local variables automatically because they're just stack
memory allocated dynamically when the function is entered . You have to manually
assign them with desired values after LOCAL directives.
<p><b> mov wc.cbSize,SIZEOF WNDCLASSEX</b> <br>
<b> mov wc.style, CS_HREDRAW or CS_VREDRAW</b>
<br>
<b> mov wc.lpfnWndProc, OFFSET WndProc</b> <br>
<b> mov wc.cbClsExtra,NULL</b> <br>
<b> mov wc.cbWndExtra,NULL</b> <br>
<b> push hInstance</b> <br>
<b> pop wc.hInstance</b> <br>
<b> mov wc.hbrBackground,COLOR_WINDOW+1</b> <br>
<b> mov wc.lpszMenuName,NULL</b> <br>
<b> mov wc.lpszClassName,OFFSET ClassName</b>
<br>
<b> invoke LoadIcon,NULL,IDI_APPLICATION</b> <br>
<b> mov wc.hIcon,eax</b> <br>
<b> mov wc.hIconSm,eax</b> <br>
<b> invoke LoadCursor,NULL,IDC_ARROW</b> <br>
<b> mov wc.hCursor,eax</b> <br>
<b> invoke RegisterClassEx, addr wc</b>
<p>The inimidating lines above are really simple in concept. It just takes several
lines of instruction to accomplish. The concept behind all these lines is
<i>window class</i>. A window class is nothing more than a blueprint or specification
of a window. It defines several important characteristics of a window such as
its icon, its cursor, the function responsible for it, its color etc. You create
a window from a window class. This is some sort of object oriented concept.
If you want to create more than one window with the same characteristics, it
stands to reason to store all these characteristics in only one place and refer
to them when needed. This scheme will save lots of memory by avoiding duplication
of information. Remember, Windows is designed in the past when memory chips
are prohibitive and most computers have 1 MB of memory. Windows must be very
efficient in using the scarce memory resource. The point is: if you define your
own window, you must fill the desired characteristics of your window in a WNDCLASS
or WNDCLASSEX structure and call RegisterClass or RegisterClassEx before you're
able to create your window. You only have to register the window class once
for each window type you want to create a window from. <br>
Windows has several predefined Window classes, such as button and edit box.
For these windows (or controls), you don't have to register a window class,
just call CreateWindowEx with the predefined class name. <br>
The single most important member in the WNDCLASSEX is lpfnWndProc. lpfn stands
for long pointer to function. Under Win32, there's no "near" or "far" pointer,
just pointer because of the new FLAT memory model. But this is again a leftover
from the day of Win16. Each window class must be associated with a function
called window procedure. The window procedure is responsible for message handling
of all windows created from the associated window class. Windows will send messages
to the window procedure to notify it of important events concerning the windows
it 's responsible for,such as user keyboard or mouse input. It's up to the window
procedure to respond intelligently to each window message it receives. You will
spend most of your time writing event handlers in window procedure. <br>
I describe each member of WNDCLASSEX below:
<p><b>WNDCLASSEX STRUCT DWORD</b> <br>
<b> cbSize
DWORD ?</b> <br>
<b> style
DWORD ?</b> <br>
<b> lpfnWndProc DWORD
?</b> <br>
<b> cbClsExtra DWORD
?</b> <br>
<b> cbWndExtra DWORD
?</b> <br>
<b> hInstance DWORD
?</b> <br>
<b> hIcon
DWORD ?</b> <br>
<b> hCursor
DWORD ?</b> <br>
<b> hbrBackground DWORD
?</b> <br>
<b> lpszMenuName DWORD
?</b> <br>
<b> lpszClassName DWORD
?</b> <br>
<b> hIconSm
DWORD ?</b> <br>
<b>WNDCLASSEX ENDS</b>
<p><b>cbSize: </b>The size of WNDCLASSEX structure in bytes. We can use SIZEOF
operator to get the value. <br>
<b>style: </b>The style of windows created from this class. You can combine
several styles together using "or" operator. <br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -