为自己创作一个好的DirectX程序风格
一直以来,我都是模仿别人的程序,进行参数的修改,来达到自己的目的。但是久而久之,我发现这样不行,因为演示程序又很多很多缺陷,所以我不得不使用自己的程序,希望开创自己程序的风格。
那么,下面就是我通过学习windows程序而得出的心得。可能写得不好,有疑问的请指正。
在创建Windows程序的时候,我们必须要包含windows.h头文件,这是毋庸置疑的。
首先是消息函数。消息函数是一种典型的回调函数。回调函数就是一种自己并不显示地使用,但是需要别的函数将此函数的地址传给它,用别的函数来使用。调用函数就是通过回调函数指针来进行传值调用的。在我以前的文章中,调用的Display()函数就是一种回调函数。而消息函数就是给应用程序实例进行调用的。让我们看看回调函数吧。
- HRESULT CALLBACK MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
- {
- switch(msg)
- {
- case WM_DESTROY:
- PostQuitMessage(0);
- return 0;
- }
- return (long) DefWindowProc(hwnd, msg, wParam, lParam);
- }
其中HRESULT就是一个返回值类型,通过windows.h头文件一层又一层的typedef,我们找出了它的源泉:long。其实HRESULT就是long类型。而CALLBACK则是一个调用约定。它也是通过一层又一层的typedef语句进行规定的,它的原型其实是__stdcall。__stdcall又是什么?在百度百科中我们找到了答案:
1.参数从右向左压入堆栈
2.函数被调用者修改堆栈
3.函数名(在编译器这个层次)自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸
这样我们了解了什么是CALLBACK。总结一下,CALLBACK就是一个调用规定,它在汇编语言的层次规定了调用的方法。其实有很多的关键字:WINAPI、APIENTRY都是__stdcall的同义词。
扩展:c/c++还有一种调用方式,那就是__cdecl。这种方式和__stdcall有些不同,它是c/c++缺省的调用方式。详见http://baike.baidu.com/view/1280676.htm
再来看看,MsgProc()函数有这样几个参数:HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam。第一个是窗口的句柄,第二个是消息的结构体,第三个和第四个则是消息参数,其实这两个参数几乎完全相同。我们常常使用的是第一个参数wParam。函数体是对消息的处理。在一个switch语句结束后,我们就可以让操作系统来自动处理消息了。所以返回的是DefWindowProc(hwnd, msg, wParam, lParam)。
消息回调函数介绍完了,我们接下来可以介绍我们的主函数WinMain()了。大家或多或少地接触WinMain()函数,因为在MFC、ATL、WTL框架上都要使用WinMain()函数。WinMain()函数的作用就相当于控制台的main()函数,但是它的参数比main()函数要多。而且不支持重载,也就是说,我们可以使用重载的main( void ),但是不能使用WinMain( void )。使用WinMain()函数的时候,我们也必须声明函数的调用方式。使用WINAPI,或者APIENTRY就可以解决问题。但是这必须要定义相关的宏。如果你对WinMain()函数相当地了解了,那么你可以使用__stdcall来代替这些值。WinMain()函数是这样声明的:
- int WinMain(
- HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- LPSTR lpCmdLine,
- int nCmdShow);
第一个参数是当前程序的实例句柄,第二个参数是以前程序的实例句柄。通常为NULL。如果你限制程序一次只能打开一次,那么我们可以使用CreateMutex()函数来解决。这超出了我们的讨论范围。在这里略去。第三个参数是当前程序在命令行中的绝对路径。第四个参数是显示的方式,我们可以通过此值来确定程序的显示方式。通常要程序正常地显示,使用SW_SHOWNORMAL来标记nCmdShow即可。
然后我们在WinMain()函数中编写代码了。但无论你怎么编写,我们都必须满足这样一个流程:
1、设置Window Class结构并且注册它
2、设置窗口并且显示窗口
3、进入消息循环
4、解除窗口注册
首先设置windows class结构。我使用的是WNDCLASSEX结构,因为它提供了更多的功能。WNDCLASSEX结构如下所示:
- typedef struct {
- UINT cbSize;
- UINT style;
- WNDPROC lpfnWndProc;
- int cbClsExtra;
- int cbWndExtra;
- HINSTANCE hInstance;
- HICON hIcon;
- HCURSOR hCursor;
- HBRUSH hbrBackground;
- LPCTSTR lpszMenuName;
- LPCTSTR lpszClassName;
- HICON hIconSm;
- } WNDCLASSEX, *PWNDCLASSEX;
现在我来说明这些数据成员。
UINT cbSize;是该结构的大小(字节)
UINT style;是窗口的风格,有多种风格可以选择。参见MSDN。
WNDPROC lpfnWndProc;窗口消息循环函数的入口指针。
int cbClsExtra;需要附加在WNDCLASSEX结构体的字节数,通常设为零。
int cbWndExtra;为窗口实例附加的字节。通常是0。但是如果是对话框形式的,并且附加了资源,则需使用DLGWINDOWEXTRA。
HINSTANCE hInstance;为程序的实例句柄。
HICON hIcon;为图标的句柄,如果为NULL,程序则会在资源中寻找图标。
HCURSOR hCursor;为光标句柄,如果使用的是自定义的光标,则可以使用LoadCursor()函数来实现。
HBRUSH hbrBackground;为背景画板刷,暂时设为NULL,我还没有学会怎么使用。
LPCTSTR lpszMenuName;为菜单的指针。如果程序中有菜单,并且在资源中有菜单的编辑,那么可以不为空,但是如果不需要使用菜单,可以将其设为NULL。
LPCTSTR lpszClassName;为NDCLASSEX类的名字。
HICON hIconSm;16×16大小的小图标的句柄。
然后调用CreateWindow()函数。这个函数可以为窗口指定样式。它是这样定义的:
- HWND CreateWindow(
- LPCTSTR lpClassName,
- LPCTSTR lpWindowName,
- DWORD dwStyle,
- int x,
- int y,
- int nWidth,
- int nHeight,
- HWND hWndParent,
- HMENU hMenu,
- HINSTANCE hInstance,
- LPVOID lpParam
- );
下面我来说说这个参数的用法:
LPCTSTR lpClassName,为windows class(WNDCLASSEX) 类的名字;
LPCTSTR lpWindowName,为窗口的名字,它会显示在窗口上。
DWORD dwStyle,窗口的样式,通过各种样式的组合,我们可以得出不同的窗口来。因为有太多的样式了,所以有什么疑问,参照MSDN;
int x,窗口起始横坐标;
int y,窗口起始纵坐标;
int nWidth,窗口的宽;
int nHeight,窗口的高;
HWND hWndParent,这个是窗口的父窗口句柄;
HMENU hMenu,菜单的句柄。
HINSTANCE hInstance,程序实例句柄
LPVOID lpParam绘制客户区的参数
然后就调用ShowWindow()和UpdateWindow()函数来进行显示了。再进行消息循环,如果有什么消息传入的话,进入消息队列,否则进行窗口渲染。当然,我写的这个程序是非常简单的,以后我可能会使用这个程序的模板来写更多更多复杂的程序的。
下面就是我的代码,它是我在模仿多个windows程序的源代码后自己写的代码。
- /*---------------------------------------------------------------------------
- 蒋轶民制作 E-mail:jiangcaiyang123@163.com
- 文件名:MainFrame.cpp
- 作用:设计自己优化的程序结构
- ----------------------------------------------------------------------------*/
- #include<windows.h>
- // 定义的宏
- #define JCLASSNAME "优化的程序"
- #define JCAPTION "程序演示"
- #define WINDOW_WIDTH 320
- #define WINDOW_HEIGHT 320
- HRESULT CALLBACK MyAppProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
- {
- switch ( msg )
- {
- case WM_DESTROY:
- PostQuitMessage( 0 );
- return 0;
- }
- return (HRESULT)DefWindowProc( hWnd, msg, wParam, lParam );
- };
- int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmd, int show )
- {
- // 设置Window Class结构并且注册它
- WNDCLASSEX jWndCls = { sizeof( jWndCls ), CS_CLASSDC, MyAppProc, 0, 0, hInst,
- NULL, NULL, 0, NULL, JCLASSNAME, NULL };
- RegisterClassEx( &jWndCls );
- // 设置窗口并且显示窗口
- HWND hWnd = CreateWindow( JCLASSNAME, JCAPTION, WS_CAPTION | WS_SYSMENU, 20, 20,
- WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInst, BUTTON);
- if ( hWnd == NULL )
- return FALSE;
- ShowWindow( hWnd, SW_SHOWNORMAL );
- UpdateWindow( hWnd );
- // 进入消息循环
- MSG msg;
- ZeroMemory( &msg, sizeof( msg ) );
- while( msg.message != WM_QUIT )
- {
- if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
- {
- TranslateMessage( &msg );
- DispatchMessage( &msg );
- }
- else
- {
- // 什么也不做
- }
- }
- // 解除窗口注册
- UnregisterClass( JCLASSNAME, jWndCls.hInstance );
- return 0;
- }
我更多的DirectX程序,请看:
今天就写到这里了,如果有时间的话,我会写更多有关DirectX和Windows应用程序的!