如何快速入门Windows编程

这篇博客主要介绍了Windows编程的基础,包括WinMain函数、窗口消息回调函数、窗口类的登记、主窗口的创建以及消息循环。作者强调了理解这些基本概念的重要性,并提供了一个简单的示例代码作为起点。
摘要由CSDN通过智能技术生成
摘要:不少朋友向我征询学习Windows界面编程的方法,每一次,我总毫不犹豫的回答:先去学Windows SDK吧。——是的,如果你要以最快的速度学习Windows编程,那么,你需要从Windows SDK开始。假如你选择MFC或者Delphi / C ++  Builder等等,虽然看似你快人一步,但是你仍然困惑着,总有一天,你还是得回头一点一滴了解Windows SDK,而此时,你已经走了不少冤枉路了。。。这其中的道理是很深刻的,本文作者以Joel的《抽象渗漏法则》解释这种现象,深得我心。

点击这里获得更多Windows界面开发相关的资料。要了解开源的界面库WinxGui,请访问WinxGui的官方网站

出处:从WinMain开始
作者:于无声处

本文应一个初学Windows程序设计的朋友而作。


目录

  1. 抽象渗漏法则(摘自Joel)
  2. 针对Windows GUI编程的封装
  3. 只用API函数创建GUI程序
  4. 完整的示例代码

一、抽象渗漏法则

根据Joel的抽象渗漏法则所有重大的抽象机制在某种程度上都是有漏洞的。Joel举过一个例子:

C ++ 字符串类型应该能让你假装字符串是个基本类型,它们尝试“字串很难处理”这个事实抽象掉,让它使用上象整型一样容易,几乎所有C ++ 字串类型都会重载加号运算符,才能把字串连接写成s  +   " bar " 。不过你知道吗?不管怎么努力,世上还是没有C ++ 字串类型能让你写成  " foo " + " bar " ,因为C ++ 里的字串常数一定是char  * ,绝对不会变成字串。这个抽象机制呈现一个程序语言本身不给补的漏洞。

当我想训练某人成为C
++ 程序员时,最好能完全不教char  * 和指针运算,直接去学STL字符串。问题是总有一天他们会写出  " foo "   +   " bar "  这样的代码,然后看到怪事出现,于是我就得停下来教他们有关char  * 的事情。他们也可能会试着调用某个需要OUT LPTSTR参数的Windows API,于是又得把char  * 、指针、Unicode、wchar_t以及tchar.h搞懂,才会知道如何调用。而这些全都是漏洞。

 

二、针对Windows GUI编程的封装
而针对Windows的GUI编程,有很多封装,如VCL、MFC、WTL等,凡此种种,都把WinMain、CreateWindow和 RegisterClassEx这些API与程序员隔离开来,对一个一开始就只接触这些类库的初学者来说,根本不知道原来一个Windows程序的入口点 其实是WinMain(事实上,一个Win32 EXE的入口点也并不是WinMain,而是编程语言的Runtime库,不过,这里把它抽象掉似乎更有益于理解)。

用API来搭建一个GUI程序是比较枯燥的,这种对于Windows GUI程序的枯燥搭建进行的抽象封装,它的所谓“某种程度上的漏洞”,也许就是使程序员根本不知道每个窗口都有一个窗口类(不是指OO语言里的Class),而每一个窗口类都有一个回调函数(Callback)来对不同的窗口消息进行不同的响应。

做为现在才接触Windows GUI编程的初学者,几乎都不了解一个Windows GUI程序是从WinMain开始的(前面说过,从WinMain开始也只是一个抽象而已,真实的情况并不是这样),那么如何仅仅使用Windows的API函数来创建一个GUI程序呢?

三、只用API搭建Windows GUI程序
1、WinMain()函数

首先,必须要声明一个WinMain()函数(为了简明起见,这里先不讨论_tWinMain这个宏,也不考虑Unicode的问题),它的原型在Windows.h中定义:

<span style="color:#000000;"><span style="color: rgb(51, 102, 255);">int</span> WINAPI WinMain(
    HINSTANCE <em>hInstance</em>,       <span style="color: rgb(51, 153, 102);">//程序当前实例的句柄,以后随时可以用GetModuleHandle(0)来获得</span>
    HINSTANCE <em>hPrevInstance</em>,   <span style="color: rgb(51, 153, 102);">//这个参数在Win32环境下总是0,已经废弃不用了</span>
    <span style="color: rgb(51, 102, 255);">char</span> * <em>lpCmdLine</em>,          </span><span style="color:#000000;"><span style="color: rgb(51, 153, 102);">//指向以/0结尾的命令行,不包括EXE本身的文件名,
                               //以后随时可以用GetCommandLine()来获取完整的命令行</span>
    <span style="color: rgb(51, 102, 255);">int</span> <em>nCmdShow</em>               <span style="color: rgb(51, 153, 102);">//指明应该以什么方式显示主窗口</span>
);</span>

声明,并且实现这个函数,让Linker程序可以找到它,让编程语言的运行时刻库在完成一些必要的初始化工作后,能够正确地调用它。所以,认为它就是程序的入口点,也是一种简单的“抽象法则”。

在这个入口点函数中,需要按顺序做下面几件事(如果是基于事先设计并存放在资源里的对话框的程序,稍有不同,以后再说):

  • 用RegisterClassEx函数登记一个独一无二的Class
  • 用CreateWindowEx函数创建一个主窗口
  • 进入一个”消息循环“,直到收到WM_QUIT消息
  • 从WinMain函数返回  

基本上所有的流程都如出一辙,所以完全可以设计出一个“Template模式”出来重用,让以后的程序直接从某个抽象基类继承,实现基类所需的虚方法就可以了,不过为了不偏离重心,还是用C语言的方式写出来:如此简单,WinMain这个函数只有这么短,分别调用三个自定义函数就OK了。

int  WINAPI WinMain(HINSTANCE, HINSTANCE, char   * int  cmdShow) {
    
if  (registerMyClass()  &&  createMyWindow(cmdShow)) {
        
return  messageLoop();
    } 
else  {
        std::ostringstream msg;
        msg 
<<   " 创建主窗口失败,错误代码: "   <<  GetLastError();
        MessageBoxA(
0 , msg.str().c_str(),  0 , MB_OK  |  MB_ICONSTOP);
        
return   0 ;
    }
}

 

2、窗口消息回调函数

简单地说,回调(Callback)函数就是一个按规定原型实现的一个函数,当别人来调用。比如说,每个窗口都有一个窗口类(用RegisterClassEx登记的Class,或者系统缺省已实现的Class),每个窗口类有一个回调函数,当窗口收到WIndows消息的时候,就会去调用这个回调函数,而这个回调函数的代码是程序员自己写的,用来根据实际情况处理不同的窗口消息。

LRESULT CALLBACK onMainWndMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    
switch  (msg) {
    
case  WM_DESTROY:
        PostQuitMessage(
0 );  // 如果是“窗口销毁”事件,则应该在消息队列中投递
        break ;               // 一个WM_QUIT消息,使GetMessage()返回FALSE
    default :
        
return  DefWindowProc(wnd, msg, wParam, lParam);
    }
    
return   0 ;
}
3、登记窗口类

在创建主窗口之前,一定要先用RegisterClassEx这个API函数登记一个类,类名必须是独一无二的,所以一般都用GUID字串来做类名。

bool  registerMyClass() {
    WNDCLASSEX  wce 
=  { 0 };
    wce.cbSize          
=  sizeof (wce);
    wce.style           
=  CS_VREDRAW  |  CS_HREDRAW;
    wce.lpfnWndProc     
=   & onMainWndMessage;   // 指明回调函数
    wce.hInstance        =  GetModuleHandle( 0 );
    wce.hIcon           
=  LoadIcon( 0 , MAKEINTRESOURCE(IDI_WINLOGO));
    wce.hCursor         
=  LoadCursor( 0 , MAKEINTRESOURCE(IDC_ARROW));
    wce.hbrBackground   
=  reinterpret_cast < HBRUSH > (COLOR_BTNFACE + 1 );
    wce.lpszClassName   
=  CLASS_NAME;  // 独一无二的类名
    wce.hIconSm          =  wce.hIcon;
    
return   0 != RegisterClassEx( & wce);
}

 

4、创建主窗口

(略,直接看完整代码

5、消息循环

消息循环很简单,仅当GetMessage这个API函数返回FALSE时,才退出循环。而GetMessage()仅当处理到消息队列中的WM_QUIT消息时才会返回FALSE。 

int  messageLoop() {
    MSG msg;
    
while  (GetMessage( & msg,  0 0 0 )) {
        TranslateMessage(
& msg);
        DispatchMessage(
& msg);
    }
    
return  static_cast <int > (msg.wParam);
}

 

四、完整的示例代码
#include  < sstream >
#include 
< windows.h >

// 独一无二的类名,一般用GUID字串,以免与其他程序的类名重复
static  const  char   *  CLASS_NAME  =   " {198CEAB2-AD78-4ed3-B099-247639080CB0} " ;

/* ***********************************************************************
    回调函数,当主窗口收到任何Windows消息时被调用
***********************************************************************
*/
LRESULT CALLBACK onMainWndMessage(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    
switch  (msg) {
    
case  WM_DESTROY:
        PostQuitMessage(
0 );  // 如果是“窗口销毁”事件,则应该在消息队列中投递
        break ;               // 一个WM_QUIT消息,使GetMessage()返回FALSE
    default :
        
return  DefWindowProc(wnd, msg, wParam, lParam);
    }
    
return   0 ;
}

/* ***********************************************************************
    登记自己的窗口类
***********************************************************************
*/
bool  registerMyClass() {
    WNDCLASSEX  wce 
=  { 0 };
    wce.cbSize          
=  sizeof (wce);
    wce.style           
=  CS_VREDRAW  |  CS_HREDRAW;
    wce.lpfnWndProc     
=   & onMainWndMessage;   // 指明回调函数
    wce.hInstance        =  GetModuleHandle( 0 );
    wce.hIcon           
=  LoadIcon( 0 , MAKEINTRESOURCE(IDI_WINLOGO));
    wce.hCursor         
=  LoadCursor( 0 , MAKEINTRESOURCE(IDC_ARROW));
    wce.hbrBackground   
=  reinterpret_cast < HBRUSH > (COLOR_BTNFACE + 1 );
    wce.lpszClassName   
=  CLASS_NAME;  // 独一无二的类名
    wce.hIconSm          =  wce.hIcon;
    
return   0 != RegisterClassEx( & wce);
}

/* ***********************************************************************
    创建并显示主窗口
***********************************************************************
*/
bool  createMyWindow(int  cmdShow) {
    HWND mainWnd 
=  CreateWindowEx( 0 , CLASS_NAME,  " Demo " , WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        
0 0 , GetModuleHandle( 0 ),  0 );
    
if  ( 0 != mainWnd) {
        ShowWindow(mainWnd, cmdShow);
        UpdateWindow(mainWnd);
        
return  true ;
    } 
else  {
        
return  false ;
    }
}

/* ***********************************************************************
    消息循环
***********************************************************************
*/
int  messageLoop() {
    MSG msg;
    
while  (GetMessage( & msg,  0 0 0 )) {
        TranslateMessage(
& msg);
        DispatchMessage(
& msg);
    }
    
return  static_cast <int > (msg.wParam);
}

/* ***********************************************************************
    WinMain,程序入口
***********************************************************************
*/
int  WINAPI WinMain(HINSTANCE, HINSTANCE, char   * int  cmdShow) {
    
if  (registerMyClass()  &&  createMyWindow(cmdShow)) {
        
return  messageLoop();
    } 
else  {
        std::ostringstream msg;
        msg 
<<   " 创建主窗口失败,错误代码: "   <<  GetLastError();
        MessageBoxA(
0 , msg.str().c_str(),  0 , MB_OK  |  MB_ICONSTOP);
        
return   0 ;
    }
}
Forger 推荐的学习windows 编程的所有书籍都在这儿: windows 编程 windows MFC入门到精通 windows 高级编程 windows 网络编程 Programming Windows Programming Windows with MFC Programming Applications for Windows Network Programming for Microsoft Windows Programming Windows by Charles Petzold. The book to get on Win32 API. If you want to write programs using just the API (which is what this tutorial covers), you need this book. 如果你想只用windows API编程,这本书是入门经典,你需要这本书。 Programming Windows with MFC by Jeff Prosise. If you want to venture into MFC (AFTER becoming fully accustomed to using the Win32 API), this is the book for you. If you don't like MFC but intend on getting a job doing windows developement, get this anyway, it's better to know than not. 如果你想用MFC这本书是你想要的 Programming Applications for Windows by Jeffrey Richter. Not for newbies, if you want to be up on managing processes and threads, dlls, windows memory management, exception handling, and hooking into the system, then this is the book for you. 如果你想了解操作系统内幕,这本书是经典,书中包含了进程,线程,动态库,windows内存管理,异常处理。。。 Visual C++ Windows Shell Programming by Dino Esposito. For anyone interested in the visual and user-friendly aspects of windows, this book covers writing extentions to the windows shell, working efficiently with files and drag and drop, customizing the taskbar and windows explorer, and numerous other tricks. Well worthwhile for anyone writing GUI apps in windows. Network Programming for Microsoft Windows Up to date information on network programming, including NetBIOS, mailslots and pipes, and of course the ever important windows sockets, complete with winsock2 and raw sockets. Also contains specific information on the various windows platforms including 2000 and CE.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值