从WinMain开始

【保姆级教程】不到一元32位芯片 DIY迷你时钟 基于不到一元的CH32V003F4P9芯片制作的迷你多功能时钟。 阅读详情

本文应一个初学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程序可以找到它,让编程语言的运行时刻库在完成一些必要的初始化工作后,能够正确地调用它。所以,认为它就是程序的入口点,也是一种简单的“抽象法则”。

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

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

基本上所有的流程都如出一辙,所以完全可以设计出一个“Template模式”出来重用,让以后的程序直接从某个抽象基类继承,实现基类所需的虚方法就可以了,不过为了不偏离重心,还是用C语言的方式写出来:

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;
    }
}

如此简单,WinMain这个函数只有这么短,分别调用三个自定义函数就OK了。

       
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;
    }
} 


多模态特征融合方法总结 由于本人的研究是文本、语音和图像三者模态数据的融合,所以针对的是这三种模态的特征融合方法进行总结。 首先,多模态特征融合的方法大体分为三种 阅读详情

相关推荐

uni-app蓝牙MTU设置失效探因:从20字节限制到跨设备兼容性实战

本文深入探讨了uni-app蓝牙开发中MTU设置失效的常见问题,特别是20字节限制的成因及解决方案。通过分析设备兼容性、蓝牙协议栈特性和系统版本差异,提供了智能延迟策略、设备特征检测和渐进式MTU提升等实战方案,帮助开发者优化蓝牙数据传输效率。

weixin_29289821的博客 289

GetModuleHandle 函数示例

本例通过在一个DLL中实现一个辅助函数  DumpModule ,在辅助函数内调用  GetModuleHandle  函数,GetModuleHandleEx  函数及使用伪变量 __ImageBase,展示了三种方法得到可执行文件或 DLL 在进程虚拟地址空间的实例句柄。      DLL中 DumpModule 函数的实现: #define DLLWITHGETMODULE  exter

qq125151343的专栏 2574

Ubuntu双系统开机黑屏?3分钟搞定NVIDIA显卡驱动冲突(18.04/20.04实测有效)

本文详细解决Ubuntu双系统开机黑屏问题,特别是由NVIDIA显卡驱动冲突引起的故障。提供从临时进入系统到永久安装专有驱动的完整方案,适用于Ubuntu18.04/20.04用户,帮助快速恢复系统并优化显卡性能。

resnet7explorer的博客 310

WinMain(GetModuleHandle(0),NULL,NULL,0);

//--------------------------------------------------------------------------- #include #include #pragma hdrstop //--------------------------------------------------------------------------- #prag

autumn20080101的专栏 927

Tessy学习系列(三):单元测试——官方例程isValueInRange

注意:工程名称以及路劲不能包含空格和中文

不吃鱼的猫的博客 2348

[机器学习] 第二章 模型评估与选择 2.ROC:roc_curve原理及源代码详解

代码见:https://github.com/Carrie-Yi/machine_learning/tree/main/roc_curve

心宝的博客 1494

windows编程25-补充之GetModuleHandle函数获取进程、dll等的句柄

该函数被声明为: HMODULE GetModuleHandle(PCTSTR pszModule); 如果调用参数pszModule为NULL,则该函数返回当前调用进程的句柄。如果指定了一个以0结尾的字符串,那么函数返回该字串指定的可执行文件或者DLL的句柄。 ...

FairLikeSnow的博客 843

文章ACCEPT之后填写内容教程-Springer-Verlag即施普林格

1. 确认corresponding author通讯作者机构是否正确(最好细分到系,如搜不到系只填大学也可)这些信息因为在投稿的时候都填过,所以一般都没问题,可确认并继续。2. Co-Author: 这篇文章有没有其他作者,如果有就选yes,并确认投稿时所填的作者姓名和机构是否正确。这个也是如果前期都填对了就不用修改。3. Research funders: 问文章的研究有没有资助,没有就选no,有就选yes,并填写机构名称和grant/contact numbers。

ZhangJiqun&191cm&85kg 4786

GetModuleHandle

当一个文件被映射到调用进程的地址空间是,GetModuleHandle函数得到其中某一模块的句柄。  ??为在注释段避免争用,使用GetModuleHandle函数。??  HMODULE GetModuleHandle( LPCTSTR lpModuleName);    参数  lpModuleName    [in]用指针指向一个包含模块名以NULL字符结尾的串,模块是.dll或.exe文件

sanshao27的专栏 2059

32、社交媒体大数据中的情感分析与网络欺凌检测

本文探讨了社交媒体大数据在情感分析和网络欺凌检测中的应用。情感分析有助于应对自动化带来的社会变化、促进社会包容和推动环境保护。网络欺凌作为一种严重的在线危害,其检测依赖于数据预处理、特征提取与深度学习模型(如CNN、LSTM)进行分类。文章还总结了大数据在欺诈检测、医疗、制造、交通等领域的广泛应用,并分析了数据存储、处理速度、质量和技术工具等方面的挑战,提出了相应的应对策略。未来,随着技术进步,社交媒体大数据将在更多领域发挥关键作用。

cc789的博客 91

Xilinx高级调试方法--远程调试

本文主要介绍Xilinx的一些高级调试方法,以及如何使用Xilinx的相关IP。

斌赋的博客 3309

1,WinMain开始

ue

coder 179

2026年算法工程师常见面试简答题

本文摘要:文章系统梳理了机器学习与深度学习中的30个核心概念,涵盖模型优化、评估指标、经典算法和大模型应用等关键知识点。主要内容包括:1)过拟合与欠拟合的成因及解决方法;2)偏差-方差权衡原理;3)数据划分与交叉验证策略;4)分类/回归任务评估指标选择;5)逻辑回归、决策树、SVM等传统算法解析;6)梯度下降优化与正则化技术;7)CNN、RNN、Transformer架构对比;8)Embedding表示与RAG技术。特别强调面试高频考点如XGBoost改进、注意力机制原理等,并给出实际工程中的解决方案建议,

不务正业的猿 350

网络游戏客户端从WinMain开始

    网络游戏客户端从WinMain开始      各位看官,您是否看得懂以下的源码吗?如果是,恭喜您,您已经具有WINDOW网络游戏开发的一个基本基础条件,也就是您已经Window应用程序运行最基本最原始的原理。如果没有看懂,请继续听我的解释。     记得哦,所有的window应用程序都是从WinMain开始哦(当然,游戏也不例外,它也是一种应用程序哦)。WinMain函数以及Win...

醉翁蜗居 226

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

[API学习]开始入门WinMain

WinMain作为程序的入口函数相当于C语言的main函数,其定义如下:WinMain(               HINSTANCE hInstance,//运行本次程序的句柄               HINSTANCE hPrevInstance, //运行程序之前的句柄        LPSTR lpCmdLine, //运行时的参数        int nShowCmd   )//

get it 466

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Windows程序设计(从WinMain开始)

 目录抽象渗漏法则(摘自Joel) 针对Windows GUI编程的封装 只用API函数创建GUI程序 WinMain函数 登记自己的窗口类 创建主窗口 消息循环 完整的示例代码 一、抽象渗漏法则根据Joel的抽象渗漏法则,所有重大的抽象机制在某种程度上都是有漏洞的。Joel举过一个例子: C++字符串类型应该能让你假装字

gxj1680的专栏 1062

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

WinMain程序,从这里开始

#include <Windows.h> LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rect; switch(message) { case WM_CREATE: PlaySound(TEXT("hellowin.wav"),NULL,SND_FILENAME | SND_ASYNC); retu

单于的学习历程 167

【windows GUI 编程】从WinMain开始

为什么80%的码农都做不了架构师?>>> ...

weixin_33816946的博客 141

A003 MFC从零开始【分析MFC的WinMain入口】

(都可以重写实现自己的功能)

喜欢唱,rap,篮球的程序员 115

从WinMain开始(CE下创建窗口)

从WinMain开始发 布 时 间 : 2008-11-19    来 源 : 来自网络    作 者 : 尹海波   浏 览 : 930 目录抽象渗漏法则(摘自Joel)针对Windows GUI编程的封装只用API函数创建GUI程序WinMain函数登记自己的窗口类创建主窗口消息循环完整的示例代码一、抽象渗漏法则根据Joel的抽象渗漏法则,所有重大的抽象机制在某种程度上都是有漏洞的。Joel举过一个例子: C 字符串类型应该能让你假装字符串是个基本类型,它们尝试“字串很难处理”这个事实抽象掉,让

661

03 WinMain函数

简介 WinMain函数是Windows程序的入口函数。相当于C语言程序的main函数,当 WinMain结束或返回时, Windows程序结束。 函数原型 int CALLBACK WinMain( _In_ HINSTANCE hInstance, //应用程序实例句柄 _In_opt_ HINSTANCE hPrevInstance, //父程序的实例 _In_ LPSTR lpCmdLine, //命令行参数 _In_ int nShowCmd //窗口显示方式 ); //Win..

Write the Code. 860

VC++——应用程序执行机制之WinMain函数

小编要和大家分享的是WinMain函数,在DOS下,程序的执行是从main函数开始的。在Windows下,对应的函数是WinMain。但是,如果浏览Hello程序的所有的方法和全局函数,是找不到WinMain函数的。MFC考虑到典型的Windows程序需要的大部分初始化工作都是标准化的,因此把WinMain函数隐藏在应用程序的框架中,编译时会自动将该函数链接到可执行文件中。程序员可以重写WinMa

河北正定欢迎您-少占鱼 1327
上一篇: 美官员称,朝鲜放了一个大炮仗
下一篇: Delphi和C++的语法区别
SeaWave
博客等级 码龄27年 117粉丝 126原创
评论 21
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值