/*------------------------------------------------------------- screensize.cpp -- Displays screen size in a message box (c) Seamanj, 2013/6/26 -------------------------------------------------------------*/ #include <windows.h> #include <tchar.h>//_vsntprintf在里面定义 #include <stdio.h> int CDECL MessageBoxPrintf( TCHAR* szCaption, TCHAR* szFormat, ... )//知识点1 { TCHAR szBuffer[1024]; va_list pArgList; va_start(pArgList, szFormat); _vsntprintf(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), szFormat, pArgList); va_end(pArgList); return MessageBox(NULL, szBuffer, szCaption, 0); } /*知识点1:函数调用规则 --------------------------------------------- 来自<<Windows环境下32位汇编语言程序设计>> P78 --------------------------------------------- 在参数入栈顺序上,C类型和StdCall类型是先把右边的参数先压入堆栈,而PASCAL类型是先把左边的参数压入堆栈. 在堆栈平衡上,C类型是在调用者在使用call指令完成后,自行用add esp,8指令把8个字节的参数空间清除,而PASCAL和 StdCall的调用者则不管这个事情,堆栈平衡的事情是由子程序用ret 8来实现的(ret 指令后面加一个操作数表示在ret 后把堆栈指针esp加上操作数 */ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)//知识点2 { int cxScreen, cyScreen; cxScreen = GetSystemMetrics(SM_CXSCREEN); cyScreen = GetSystemMetrics(SM_CYSCREEN); MessageBoxPrintf(TEXT("ScreenSize"), TEXT("The screen is %i pixels wide by %i pixels high."), cxScreen, cyScreen);//知识点3 return 0; } /*知识点2:模块与实例 --------------------------------------------- 来自<<Windows环境下32位汇编语言程序设计>> P98 --------------------------------------------- 一个模块代表的是一个运行中的exe文件或dll文件,用来代表这个文件中所有的代码和资源,磁盘上的文件不是模块,装入内存后运行时就叫做模块. 一个应用程序调用其他DLL中的API时,这些DLL文件被装入内存,就产生了不同的模块,为了区分地址空间中的不同模块,每个模块都有一个唯一的模块句 柄来标识. 由于很多API函数中都要用程序的模块句柄,以便利用程序中的各种资源,所以在程序的一开始就先取得模块句柄并存放到一个全局变量中可以省去 很多的麻烦,在Win32中,模块句柄在数值上等于程序在内存中装入的起始地址. 取模块句柄使用的API函数是GetModuleHandle,它的使用方法是: invoke GetModuleHandle,lpModuleName lpModuleName参数是一个指向含有模块名称字符串的指针,可以用这个函数取得程序地址空间中各个模块的句柄,例如,如果想得到User32.dll的句柄 以便使用其中包含的图标资源,那么可以如下使用: szUserDll db 'User32.dll',0 ... invoke GetModuleHandle,addr szUserDll .if eax mov hUserDllHandle.eax .endif ... 如果使用参数NULL调用GetModuleHandle,那么得到的是调用者本模块的句柄 invoke GetModuleHandle,NULL mov hInstance,eax 可以注意到,把返回的句柄放到了取名为hInstance的变量里而并不是放在hModule中,为什么是hInstance呢?Instance是"实例",它的概念来自于 Win16,Win16中不同运行程序的地址空间并非是完全隔离的,一个可执行文件运行后形成"模块",多次加载同一个可执行文件时,这个"模块"是公用的, 为了区分多次加载的"拷贝",就把每个"拷贝"叫做实例,每个实例均用不同的"实例句柄"(hInstance)值来标识它们. 但在Win32中,程序运行时是隔离的,每个实例都使用自己的私有的4GB空间,都认为自己是唯一的,不存在一个模块的多个实例的问题,实际上在 Win32中,实例句柄就是模块句柄,但很多API原型中用到模块句柄的时候使用的名称还是沿用hInstance,所以我们还是把变量名称取为hInstance. 在C语言的编程中,hInstance通过WinMain由系统传入,WinMain的原型是:WinMain(hInstance,hPrevInstance,lpzCmdParam,nCmdShow),程序不用 自己去获得hInstance,这个过程由C的初始化代码代劳了,但在Win32汇编中hInstance必须自己获取,如果不了解hModule就是hInstance的话,就无法 得知如何得到hInstance,因为并没有一个类似GetInstanceHandle之类的API函数. --------------------------- 来自<<WINDOWS程序设计>> P12 --------------------------- WinMain的第一个参数被称作"实例句柄".在Windows编程中,句柄仅是一个应用程序用来识别某些事情的数字.在这种情况下,该句柄唯一地标识该程序, 其他Windows函数需要用该句柄作为参数来调用该程序.在Windows的早期版本中,当同时地同一程序多次时,您便了该程序的"多个实例".同一应用程序的所 有实例共享代码和只读的内存(通常是菜单和对话框模板之类的资源).程序通过检查hPrevInstance参数就能够确定自身的其他实例是否正在运行.然后它 可以超过某种繁杂的工作并从前面的实例将某些数据移到自己的数据区域. 在32位的Windows版本中,该概念已被抛弃.WinMain的第二个参数总是NULL(定义为0). WinMain的第三个参数是用于运行程序的命令行.某些Windows应用程序使用这个参数以在程序启动时将文件加载到内存.WinMain的第四个参数指出程序 最初显示的方式,可以是正常地或者是最大化地充满整个窗口,或者是最小化显示在任务列表栏中. */ /*知识点3:BASIC PRINTF CONVERSIONS --------------------------------------- 来自<<THE C PROGRAMMING LANGUAGE>> P154 --------------------------------------- Character Argument type; Printed As d,i int; decimal number */