如何快速入门Windows编程

转载 2006年11月09日 00:13:00
摘要:不少朋友向我征询学习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中定义:


声明,并且实现这个函数,让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, 000)) {
        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,
        
00, GetModuleHandle(0), 0);
    
if (0!=mainWnd) {
        ShowWindow(mainWnd, cmdShow);
        UpdateWindow(mainWnd);
        
return true;
    } 
else {
        
return false;
    }
}

/************************************************************************
    消息循环
***********************************************************************
*/
int messageLoop() {
    MSG msg;
    
while (GetMessage(&msg, 000)) {
        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;
    }
}

C++实现第一个Windows程序设计Hello World

C++实现第一个Windows程序设计目录结构: C实现第一个Windows程序设计 控制台的Hello World Windows版的Hello World WinMain函数的参数 WinMain...
  • qq_35445306
  • qq_35445306
  • 2017-08-15 00:21:13
  • 1552

windows程序设计一 最简单的windows程序

这里所指的windows程序是传统的win32应用程序,使用windows API编写.首先,先来看一个简单的例子:#include int WINAPI WinMain(_In_ HINSTANCE...
  • swindy1
  • swindy1
  • 2017-06-11 22:30:53
  • 2031

为什么要学习Windows程序设计?

每天我们都在用各种工具编写各种程序,这些程序都是运行在Windows操作系统上的,不都是Windows程序吗?为什么还要学习Windows程序设计?的确我们都在编写着Windows程序,这些程序可能是...
  • sunylat
  • sunylat
  • 2016-12-21 10:11:20
  • 1624

Windows程序设计的第一个实例

之前一直学习的是控制台程序设计,后来接触MFC程序设计,中间忽略了Windows程序设计的学习,所以在开始接触MFC的时候非常迷茫,不知道MFC框架下的各个函数的意义,不明白其内部的原理及机制,只大概...
  • Alex123980
  • Alex123980
  • 2016-07-28 10:38:31
  • 1234

Windows编程初步

Windows编程初步  目前来说Windows已经家喻户晓,它的历史是Microsoft历史上最辉煌的一页.人们现在已经基本上使用Windows操作系统,因此在编程方面我们有必要从DOS环境编程向W...
  • likaier
  • likaier
  • 2005-03-29 10:19:00
  • 2204

windows编程经典书籍

本人是刚刚开始学习windows编程的,感觉看雪学院的大牛很NB.想找一些书籍来看学习学习,可是不知道看哪些书好.驱动,对菜鸟们来说真是一个很深奥的话题,所以 ,我找来了这篇文章供大家分享,以后大家发...
  • dadalan
  • dadalan
  • 2009-02-18 13:30:00
  • 57724

如何学习Windows编程

有很多初入门的小朋友跟我说,他们想学“底层”的技术,因为觉得技术含量高,所以想学Windows C++开发,看着调用一个个的参数超级多,调用起来超级麻烦的Windows API,觉着很酷。于是我念头一...
  • oldmanzhao
  • oldmanzhao
  • 2013-07-20 01:57:11
  • 26124

Windows图像编程

 Windows图像编程图形设备接口(GDI,Graphics Device Interface)的主要目标之一是支持在输出设备(如视频显示器、打印机和绘图仪)上的与设备无关的图形。 GDI通过将应用...
  • teshorse
  • teshorse
  • 2006-11-26 03:01:00
  • 3680

学好Windows编程要看的书籍

本文是接着前面的学好C++要阅读的书籍而写的,如果想了解C++的学习的话请先看学好C++要阅读的书籍:http://blog.csdn.net/a809146548/article/details/4...
  • swartz_lubel
  • swartz_lubel
  • 2017-03-10 18:24:15
  • 3377

Windows程序设计 中文版+英文版+全书源代码 高清完整CHM版

  • 2008年07月14日 18:51
  • 18.62MB
  • 下载
收藏助手
不良信息举报
您举报文章:如何快速入门Windows编程
举报原因:
原因补充:

(最多只允许输入30个字)