如何快速入门Windows编程

SAP 财务快速记账之--科目分配模型(Account assignment model ) SAP FICO 运维解决方案 阅读详情
摘要:不少朋友向我征询学习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中定义:

int WINAPI WinMain(
    HINSTANCE hInstance, //程序当前实例的句柄,以后随时可以用GetModuleHandle(0)来获得
    HINSTANCE hPrevInstance, //这个参数在Win32环境下总是0,已经废弃不用了
    char * lpCmdLine,
//指向以/0结尾的命令行,不包括EXE本身的文件名,
//以后随时可以用GetCommandLine()来获取完整的命令行

    int nCmdShow //指明应该以什么方式显示主窗口
);

声明,并且实现这个函数,让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 ;
    }
}
 
【网络与爬虫 61】移动端App爬虫终极指南:Android-iOS应用数据提取黑科技全解析 本文全面解析移动端App爬虫技术,涵盖Android和iOS两大平台的数据提取方法。从基础的网络抓包到高级的代码注入,从自动化测试框架到反调试技术绕过,为开发者提供移动端数据采集的完整解决方案和实战经验。 阅读详情

相关推荐

VBA学习笔记6:将多个工作表中满足条件的数据汇总到同一个工作表

VBA学习笔记6:将多个工作表中满足条件的数据汇总到同一个工作表

weixin_45355881的博客 4708

Windows编程

本文介绍Windows编程,是指Windows应用程序,不是系统编程

于高山之巅,方见大河奔涌;于群峰之上,更觉长风浩荡。 717

内存|内存的概念、内存的作用、内存的物理结构及内存使用

内存是硬件,是用于存放数据的硬件。程序执行前需要先放到内存中才能被CPU处理。内存是与CPU沟通的桥梁,计算机中所有程序的运行都要依靠内存,内存对计算机的影响非常大。本文简单介绍了内存的概念、作用、物理结构以及使用,内存远不止如此,后续文章会逐步深入讲解。

qq_59938293的博客 2万+

windows编程-网络编程

/ 1 定义和配置窗口对象//定义交互响应//回调//定义窗口代号//回调MyWinProc是我们的自定义函数,我们将这个函数回调到这个窗口对象中,将操作和窗口联系在一起(3)注册窗口类虽然我们定义了一个窗口,但是系统并不知道这个窗口的存在,所以接下来我们通过RegisterClass()函数来注册窗口类。

qwertyasdfghh的博客 3199

Windows 11最新WDK安装避坑指南:从Visual Studio配置到KMDF驱动开发

本文提供了在Windows 11系统上进行KMDF驱动开发的完整环境搭建与实战指南。针对最新WDK与Visual Studio的配置变化,详细解析了从独立安装WDK/SDK、创建首个KMDF驱动项目,到编译排错、部署调试的全流程,帮助开发者高效避坑,顺利开启Windows内核开发。

weixin_29302047的博客 529

视频编解码——视频编解码器工作原理

目录什么是视频编解码器为什么需要视屏编解码器视频编码器与数字视频容器时评编码器的历史 什么是视频编解码器 简单来说就是用于压缩或解压数字视频的软件或硬件 编码是信息从一种形式或格式转换为另一种形式的过程,解码是编码的反向。 适用于视频 编码是将视频、音频的原始格式的文件通过压缩技术转换成另一种格式(视频如h.264,h.265,m-jpeg等)。 解码是编码的反向过程。应用中,一般是将h.264,h.265格式数据解码成如YUV格式裸流交给显示器显示。 为什么需要视屏编解码器 需要在有限的带宽或存储空间

路人coder 1万+

window编程入门

第一个win32程序

学习 2156

Synopsys SCL许可证服务器搭建详解:从安装到生成.dat文件

本文详细解析了Synopsys SCL许可证服务器的完整搭建流程,从系统环境准备、SCL安装到生成.dat文件。重点阐述了如何正确获取HOST ID、配置License文件以及调试常见故障,为数字芯片设计工程师提供了一套从零到一的实战避坑指南,确保EDA工具链稳定运行。

weixin_29306011的博客 613

windows编程介绍并推荐入门书籍(15本)

7. Windows Driver Frameworks:Windows Driver Frameworks(WDF)是一组用于开发Windows驱动程序的框架,包括Kernel-Mode Driver Framework(KMDF)和User-Mode Driver Framework(UMDF)两种,它们提供了简化驱动程序开发过程的工具和模型。2. .NET Framework:.NET Framework是微软的开发平台,其中包含一组库和工具,可用于开发Windows应用程序、Web应用程序和服务。

记录 IT 领域经验与见解的博客 5998

faster lio 回环 加入GTSAM优化的记录

对Faster lio 加入回环、gtsam优化,在室外建图遇到的问题和过程

Nico_jion的博客 2601

从零到一:基于Blazor和Elsa Workflows的工作流系统开发实战

本文详细介绍了如何利用Blazor和Elsa Workflows构建高效的工作流系统,从技术选型到生产环境部署,涵盖动态工作流建模、Blazor前端集成及性能优化等核心内容。通过实战案例,展示如何实现订单处理系统的自动化与灵活性,提升企业流程效率。

weixin_26875109的博客 429

(C/C++)Windows编程

Windows编程的核心都在这里,带给你不同的体验

MOON_YZM的博客 3319

41、矩形和圆柱形开槽微带贴片天线设计及性能分析

本文提出了一种适用于ISM频段(2.4 - 2.48 GHz)的微带圆形贴片天线,专为生物医学植入和可穿戴设备设计。通过在贴片上蚀刻矩形槽并去除圆柱形槽,实现了小型化与性能优化。采用Rogers 3010基板材料,结合缺陷接地结构(DGS)技术,有效改善了带宽、回波损耗和极化纯度。仿真结果显示,天线在弯曲状态下仍保持良好性能,回波损耗达-13 dB,VSWR为1.6,方向性提升至5.38 dB。与以往设计相比,本次设计在回波损耗和谐振频率稳定性方面表现优异,具备良好的生物相容性和应用潜力。未来将从开槽结构、

github5actions的博客 259

Windows游戏编程快速入门方法

                        Windows游戏编程快速入门方法                         Easideao(简单思路)  序言:     从2001年到2005年,在不知不觉中我已经渡过了4年的职业游戏开发生涯。在这4年里经常会有些网友向我询问编程的入门有没有捷径。每一次我都不知怎么回答才算合适,我也一直想表达一下我的思路和想法

chinagdh的专栏 2079

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Windows下C语言网络编程快速入门

         C语言的学习,一般的方式是,先学C,然后是C++,最好还要有汇编语言和微机原理基础,然后才是Visual C++。这样的方式,对学习者来说,要花费很多时间和耐力。而在学校教学中,也没有时间深入学习Windows编程的实用技术了。  其实,具有了C语言基础后,再有一些基本的C++类的概念,就可以直接学习Windows C编程了。  一、走近Windows C语言  很多语言都把显示

DX 4617

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

怎么样快速入门windows内核编程

<br />2010-10-17 00:56<br />    实际上我的windows内核编程和恶意代码分析的经历只有3个月,还不知道今后能不能继续用得上。我很幸运地接受到一个博士后老师指导,他原本是想让我帮他实现一个系统来着,计划开发周期为8个月,而我由于保不了研,不得不只做出一个原型,就被迫找工作。<br />    其实他也没手把手教我,只是交给了一些资料,教给我做研究并独立解决问题的方法,让我规避了很多陷阱,下面列举一些最有用的资料:<br />1、入门的话,当属codeproject上的Driv

天道酬勤 4668

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

上一篇: DDOS终极加速列车算法
下一篇: Windows精解:窗口类释疑
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值