也称“SDK编程”,即调用 Windows 提供的API接口(C),完成窗口程序的设计,API接口可以理解为一些C的函数和数据类型(结构体、枚举)。
难点1:
突然冒出很多“新”的数据类型,DWORD 、LPSTR 、LRESULT等等
难点2:
句柄的概念,应用程序实例句柄、窗口句柄、画刷光标句柄、文件句柄等等
难点3:
消息队列、消息处理与程序流程。
先写一个最简单的SDK窗口程序(VS2012平台):
步骤1:
创建工程,选择《Win32项目》(不是控制台项目)
步骤2:
一步一步点下去,在《应用程序设置》页面,把 空项目 勾上,空白项目压力最小。
步骤3:
添加一个CPP文件,编写代码:
代码如下:
#include
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
case WM_CLOSE:
::PostQuitMessage(0);
break;
default:
return ::DefWindowProc(hwnd,message,wParam,lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
WNDCLASS wc;
wc.cbClsExtra=0;
wc.cbWndExtra=0;
// wc.hbrBackground=NULL;
wc.hbrBackground=(HBRUSH)::GetStockObject(GRAY_BRUSH);
// wc.hCursor=NULL;
wc.hCursor=::LoadCursor(NULL,IDC_CROSS);
wc.hIcon=NULL;
wc.hInstance=hInstance;
wc.lpfnWndProc=WndProc;
wc.lpszClassName="Test";
wc.lpszMenuName=NULL;
wc.style=CS_VREDRAW | CS_HREDRAW;
::RegisterClass(&wc);
HWND hwnd = ::CreateWindow("Test","First Windows Appliation",WS_OVERLAPPEDWINDOW,0,0,800,600,NULL,NULL,hInstance,NULL);
::ShowWindow(hwnd,nCmdShow);
::UpdateWindow(hwnd);
MSG msg;
while(::GetMessage(&msg,NULL,NULL,NULL))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
return 0;
}
1、WinMain 是程序的入口,由操作系统调用,返回 int 值供操作系统了解程序运行的结果(是否异常),这与控制台的main 函数类似;
2、函数调用的规则:函数调用的时候,需要把参数入栈,调用完毕也需要回收栈的空间,多个参数则有从右到左或者相反的方向。__stdcall 和 __cdec 都是从右到左,其中__cdec是C和C++程序的缺省调用方式,特点是 调用函数 负责栈的管理。__stdcall 则是由被调函数完成栈的管理(回收)。
3、WINAPI 、CALLBACK 都是__stdcall
其实这点区别不重要,关键是 回调函数 ,普通的函数调用方默认等待被调函数的返回,有些情况下肯定是不行的,比如消息处理函数:如果“消息处理” 不能 “并行” 操作,那么可能消息堵塞!
所以,有些函数应该设计成类似 “线程” 的非等待类型函数。这时候主调函数回收栈的方式就不能接收了,因为主调函数不等待被调函数结束!
然而,上面的都不重要!指定函数为WINAPI 基本上不会出错
4、四个参数,两个句柄 (HINSTANCE),句柄其实就是一个32位的无符号整数,Windows管理文件、内存、GDI(画笔、画刷等待绘图工具)、图标、光标、菜单等待,各种各样的资源。为了管理方便,给每个资源进行编号(就像身份证)就可以理解了,但是。。。一个无符号整数是很难理解的,所以用typedef 给 unsigned int 搞几个别名吧!这样就好理解!
Windows下出现的各种 “新” 数据类型中,很多都是这样的操作!
5、第一个参数,操作系统分配给应用程序的句柄(身份ID),后面属于该程序的窗口等等,都可以利用它来表明身份,第二个参数已经被弃用(程序运行多个副本),第三个参数是命令行,运行程序的同时可以加入命令行参数(类似ping ******),第四个参数就是窗口的显示方式,最大化、隐藏、最小化等等
6、WinMain的基本流程是,定义一个描述窗口特征的结构体(WNDCLASS),填写结构体的分量值(其中最重要的填入窗口的消息处理函数),注册这个窗口类(此类并非面向对象的类),根据注册的窗口类名称创建窗口、显示窗口,然后进入一个类似“死”循环的消息处理。最后在获取到主窗口销毁消息后退出程序。
7、窗口函数:LRESULT也是一个32位无符号整数,返回消息处理结果。四个参数,第一个是窗口句柄,注册窗口类绑定了窗口函数,注册的窗口类是可以创建多个窗口的,所以消息是发给哪个窗口的,必须区分。第二个参数是消息的类型,枚举类型(其实也是int);第三和第四个参数是附加信息,比如鼠标点击消息需要附加鼠标位置、按键信息等等。
窗口函数肯定是一个switch 结构,对不同消息进行不同入口处理,这里其实是窗口程序设计的重点
8、WinMain消息循环中,GetMessage的第二个参数必须设置成NULL (所有窗口),否则程序无法通过关闭窗口进行退出。(窗口关闭,销毁窗口,回收窗口句柄,只捕获该窗口消息则无法获取后续的WM_QUIT消息)。