Windows 编程基础

Windows 编程基础
2011年04月29日
  对于程序员和梦想成为程序员的人来说,几乎所有人都是从谭浩强的C语言教程开始的,而且都是dos下的C语言编程。但是,作为一个实用、做真正项目的程序员来说,Windows编程是真正需要的技术,但也是从学生转化为程序员过程中最大的门槛。
  在这篇文章中,我将带领大家,通过几个简单的程序代码开始,让读者开始走入Windows编程的世界。这篇文章也将承前启后,为后面我会继续介绍的一些比较深入的Windows编程技术做铺垫,让每个读者能有个连续性的学习过程,而不要有太抖的学习曲线。 Win32,通常是对32位Windows系统平台的简称,同时也指代从Windows XP开始的标准化Windows API体系,所以当听到win32的时候要意识到其所代表的内涵。
  正如DOS和控制台编程一样,Windows程序同样从一个Main函数开始,但是这个Main函数拥有以下的定义(摘自MSDN): int WINAPI WinMain( HINSTANCE hInstance, // 当前实例的句柄 HINSTANCE hPrevInstance, // 本exe前一个实例的句柄(已经被废弃) LPSTR lpCmdLine, // 类似argc,argv的命令行参数信息 int nCmdShow // 窗体创建后的可视化状态 );
  在控制台下,我们习惯性的用printf函数实现Hello World这个入门程序,实现这个字符串的打印,然而在Windows下我们没有控制台(当然可以在调试时用控制台输出必要的字符,printf函数同样有效)来做输入输出设备,我们需要用一个消息框来实现这样的现实。在Windows下,要想弹出一个消息框需要调用如下的函数: 于是,我们继续按照控制台下程序开发的思路,开始写下面的程序代码,实现一个传统的Hello World: 以上我和读者们一起走了一小步,但是对于即将成为Win32程序员的人来说却是一大步。做Windows编程,就要告别那些控制台下的东西,因为窗体时代和控制台时代是两个世界,两个完全不同的世界。在后面的文章中,我将带领大家尽量不用控制台函数,只用纯Windows的窗体版函数进行程序开发,进而让每一个读者都成为真正的Win32程序员。
  Windows程序开发,也就是一切以窗体为主来与计算机打交道,可能很多人说笨拙的只有控制台命令版本的MingW编译器只能进行控制台程序开发、编程学习,可是他们错了,MingW不仅仅能够进行窗体程序开发,还能开发Windows下的驱动程序。下面我们开始进行每一个Windows程序员都必经的一条路线,用最原始的手工方法创建一个基本窗体,其他的窗体都可以基于这个原型进行更改而得出。
  窗体开发,很多人潜意识中就会想到可视化窗体编辑器,就像玩Photoshop一样的"画"出自己想要的界面,然而这些可视化编辑器仅仅是基于代码平台为开发者们提供的一个便利工具,最终一切都还是要以代码的方式存在和运行。对于Windows来说,每个窗体都是窗体类的实例,当然这个窗体类和实例的概念与C++下相同的术语所表达的意思类似,含义却完全不同。在Windows操作系统下,窗体类是指这个窗体的类别,比如扁扁的标题栏的工具窗口、传统的窗口和不可修改尺寸大小的对话框窗口。
  要想创建一个窗体,首先需要定义一个WNDCLASS窗体类并注册到Windows系统内核,以表达清楚这个窗口所用于的功能和类别。WNDCLASS是一个struct,定义如下: typedef struct _WNDCLASS { UINT style; // 窗体样式 WNDPROC lpfnWndProc; // 回调函数 int cbClsExtra; // 额外空间大小 int cbWndExtra; // 额外空间大小 HINSTANCE hInstance; // 应用程序句柄 HICON hIcon; // 窗体图标 HCURSOR hCursor; // 鼠标指针 HBRUSH hbrBackground; // 窗体背景色 LPCTSTR lpszMenuName; // 窗体菜单 LPCTSTR lpszClassName; // 窗体类名称 } WNDCLASS, *PWNDCLASS;
  具体这个结构中的元素的含义,建议读者们去自己下一点点功夫读一下MSDN,用起来实际上都是很简单的,每一个元素的可选项在MSDN中也有详细的注明。定义好这个数据结构后,我们就需要将其注册到Windows系统中,使用到如下的函数: 当窗体类注册完成后,我们则需要以这个窗体类为模板,用CreateWindow函数创建它的一个实例。窗体默认刚创建出来的时候是隐藏的,但是我们可以通过ShowWindow函数或者CreateWindow函数的WS_VISIBLE参数来让其可见。创建窗体函数有如下的定义原型: HWND CreateWindow( LPCTSTR lpClassName, // 窗体类名 LPCTSTR lpWindowName, // 标题栏内容 DWORD dwStyle, // 创建样式 int x, // 横向位置 int y, // 垂直位置 int nWidth, // 宽度 int nHeight, // 高度 HWND hWndParent, // 父级窗口句柄或NULL HMENU hMenu, // 菜单句柄或子窗体识别符 HINSTANCE hInstance, // 应用程序实例句柄 LPVOID lpParam // 附加参数 );
  在创建完窗体后,如果我们在WinMain中直接return 0;的话,那么一切的工作都白做了,因为应用程序会像控制台程序一样直接退出,返回一个错误值为0,然后不会有任何窗体被创建出来(也许很多速度慢的电脑会看到窗体一闪而过)。那么我们如何让程序继续下去,像其他许许多多的Windows程序一样一直存在,直到我们把它关掉后才消失,程序才退出呢?
  回答这个问题,我们需要的是了解消息机制。在Windows下,每一个鼠标移动、每一次点击、每一个按键,都是以消息的形式发送给程序的。在没有消息的时候,应用程序会默认进入休眠,把处理器时间交给其他需要计算的程序,直到有消息抵达然后程序被重新唤醒,开始执行。获取消息的函数定义如下: 下面,我们需要一个循环,不断的等待和处理消息,这就是Windows下的消息泵,它拥有如下的编写方式: 在上面的代码中,实现了经典的消息泵,这个结构是所有Windows程序员都最熟悉的消息机制的核心,也是非常重要的区分Win32程序员和控制台程序员的根本技术。下面我们还需要让大家了解一下回调函数,回调函数实际上就是由上面的DispatchMessage函数所调用的,当我们调用它的时候,它会在内核中注册的窗体类中去寻找对应的回调函数的内存地址,然后调用它。细心的读者会发现这里存在问题,无论有多少个窗体类的实例,是不是每一个被创建的窗体的每一个消息都要由所有窗体共同的回调函数来处理呢?这样不就不能实现每个窗体实例的独立运作了吗?对的,的确是所有窗体公用一个回调函数处理消息事件,但是实现每个窗体的独立运作需要一些编程技巧,对hwnd进行识别和对应进行判断,对于这个问题我们会在以后的文章中给予解决。下面我们介绍回调函数的写法: 当我们写一个消息回调函数的时候,我们会抄袭一个类似上面定义的函数,定义为自己的名字,然后开始写一个switch case语法对uMsg消息类别进行判断,比如WM_CREATE意味着窗体刚被创建,WM_DESTROY意味着窗体即将被销毁等,下面是一个简单的例子: switch(uMsg) { case WM_CREATE: // 创建时做什么。。。 break; case WM_DESTROY: // 销毁时做什么。。。 break; } // 消息回收函数,如果你不知道如何处理,必须让windows回收 return DefWindowProc(hWnd, uMsg, wParam, lParam);
  下面我们来完成一个完整的Windows主函数,完成本节所涉及的内容吧。 #include // 在窗体中,比如我们设计一些特殊控件、图形显示、3D模块等等,都是通过修改这个回叫函数实现的 LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { // 窗口创建消息 case WM_CREATE: MessageBox(hwnd, "Hello World!", "From MSYS", 0); break; // 对话框重新绘制消息,每当窗口更新时产生 case WM_PAINT: { PAINTSTRUCT ps; HDC hdc; // 绘制设备句柄 // 开始绘制 hdc = BeginPaint(hwnd, &ps); // 用这个函数实现字符串的打印 TextOut(hdc, 10, 10, "hello world from Martin", 21); // 结束绘制 EndPaint(hwnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: // 窗口回叫函数中,不进行处理的消息,Windows需要回收,对话框不需要 return DefWindowProc(hwnd, uMsg, wParam, lParam); } // 这里永远也不会走到 return 0; } // 我们的Windows版本主函数 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, INT nShow) { // MessageBox(NULL, "Hello World!", "Hello", MB_OK); // 打印Hello World // 定义窗体类 WNDCLASS wc; wc.style = CS_VREDRAW | CS_HREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.lpszMenuName = NULL; wc.lpszClassName = "MyWndClass"; // 向内核注册我们新创建的窗体类 RegisterClass(&wc); // 用这个函数生成我们刚创建的窗体类,注意其第一个参数为我们的名字 HWND hwnd = CreateWindow("MyWndClass", "Hello from MSYS", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, 0); MSG msg; // 这是经典的Windows消息泵,在这里消息被不断取出然后进行翻译和分配 while(GetMessage(&msg, 0, 0, 0)) { // 翻译的主要工作是对不同的键盘扫描码进行转换 TranslateMessage(&msg); // 这只是隐含方式的对WndProc的调用,将消息发送过去 DispatchMessage(&msg); } return 0; }
  这里是这个例子的代码下载:hello
  普通窗体的创建介绍完了,这种窗体创建方式是Windows系统上所有窗体都使用的,无论是控件、自定义窗体还是对话框,只不过对话框的创建是由Windows自身给封装隐含了,但是最终都是用如上的方法创建的。用这个方法,我们还可以在窗体上绘制自己喜欢的图案,甚至根据鼠标事件的相应自己设计自己的按钮,其实只不过是图片的变更看起来像是东西被按下去而已,都只是一些最简单的魔法。写程序,最关键的是激发你自己的创意!

你可能感兴趣的:(操作系统,c/c++,photoshop)