从WinMain开始

保姆级教程:在紫光同创Titan2 FPGA上实现PCIe DMA,实测带宽接近理论极限 本文详细介绍了在紫光同创Titan2 FPGA上实现PCIe DMA性能调优的全过程,通过参数优化和架构设计,实测带宽达到6.91GB/s,接近理论极限。涵盖开发环境搭建、PCIe IP核定制、DMA引擎设计、驱动开发及性能调优等关键步骤,为FPGA开发者提供实用指南。 阅读详情

本文应一个初学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;
    }
} 


DC/DC开关电源学习笔记(十三)Buck-Boost升降压电路工作原理 Buck-Boost电路是一种能够实现输出电压高于或低于输入电压的DC-DC变换器,且输出电压极性与输入电压相反。它结合了Buck(降压)和Boost(升压)两种拓扑的特点,广泛应用于需要灵活电压调整的场合,如电池供电设备、LED驱动和工业控制系统。本文将从电路结构、工作原理、关键公式推导及实际应用等方面进行详细分析。 阅读详情

相关推荐

广工计组实验f2一位全加器

一位全加器实验

weixin_63692154的博客 2847

GetModuleHandle 函数示例

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

qq125151343的专栏 2574

mobaXterm连接vmWare虚拟机上的centOS7提示连接失败的解决方法

1.问题描述: mobaXterm安装后连接虚拟机vmWare上的centOS7发现连接不上,如下图所示: 2.解决方法 先是查了一下centOS的ip 开始错误的以为 virbr0: 后面的inet就是虚拟机的IP 实则不然(正确的ip是在ens33后面的inet中的ip,大佬们勿喷) 这个值需要配置一下的,具体配置过程如下: step1: 进入centOS下的/...

hss19214的博客 7890

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

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

autumn20080101的专栏 927

国科大学习资料--人工智能原理与算法-第六次作业解析(学长整理)

国科大学习资料–人工智能原理与算法-第六次作业解析(张文生老师主讲)(6.6、6.7、6.11) 6.6 说明如何通过使用辅助变量把诸如A+B=C这样的三元约束转变成三个二元约束。假设值域是有限的。(提示:考虑引进新变量表示变量对的值,引进约束如“X是Y变量对中的第一个元素”。)然后说明如何把多于三个变量的约束类似地转换为二元约束。说明如何通过改变变量的值域来消除一元约束。这就完整演示了任何CSP都可以转变为只含二元约束的CSP。 答:首先,我们引入一个新变量AB,假设A和B的范围均为N,AB的范围为N×N

无名何许人的博客 2870

信奥赛C++提高组csp-s之数论基础专题课:从同余到分数模运算1(知识地图:同余、裴蜀定理、扩展欧几里得、乘法逆元、分数模运算)

信奥赛C++提高组csp-s之数论基础专题课:从同余到分数模运算1(知识地图:同余、裴蜀定理、扩展欧几里得、乘法逆元、分数模运算)

王老师青少年编程 791

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

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

FairLikeSnow的博客 843

数据分析!Python爬虫爬取B站UP主数据并分析涨粉规律(附可视化图表)

爬取更多UP主:扩充列表,增加需要分析的UP主UID数量爬取更多作品:修改的参数,增加每个UP主的作品爬取页数新增分析维度:在类中添加方法,可分析发布时间(如定时发布对涨粉的影响)、内容标签(如游戏/知识区涨粉差异)优化可视化样式:调整方法中的图表类型、颜色、标题等,适配个人展示需求播放量与粉丝数强正相关:从相关性热力图可看出,总播放量与粉丝数的相关系数通常在0.8以上,说明曝光量是涨粉的核心基础作品数量存在“边际效应”

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1226

GetModuleHandle

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

sanshao27的专栏 2059

手把手教你学AutoSAR - 总目录

鉴于近期较多博友问及微信群,如下可申请好友验证,请备注“autosar”,否则可能添加不通过。验证通过后,拉入讨论群。PS:本人鉴于工作事项可能不能及时回复问题,可在交流群内部和其他博友之间相互沟通交流经验。欢迎来到桃源乐游的《AutoSAR配置与实践》,以下为学习大纲,蓝色字体点击可跳转。

nihaoljq2010的博客 7461

华为海思:大小海思的双轮驱动战略分析

大海思的定位清晰明确,即专注于为华为内部提供高性能、高集成度的芯片解决方案,以支持华为终端、服务器等业务的快速发展。小海思的产品线广泛,涵盖了广域物联、短距物联、手机穿戴、显示交互、智慧视觉、智慧媒体、MCU、模拟以及光模块等多个领域。通过提供多样化的芯片解决方案,小海思旨在满足外部客户的不同需求,拓展华为海思在芯片市场的份额和影响力。大海思的产品布局主要围绕华为的内部需求展开。在智能手机应用处理器方面,大海思的麒麟系列芯片已经成为华为手机的标志性产品,以其出色的性能和能效比赢得了市场的广泛认可。

dream it possible 2924

1,WinMain开始

ue

coder 179

qt项目中添加 jsoncpp 库

qt使用jsoncpp库

deroy 1350

网络游戏客户端从WinMain开始

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

醉翁蜗居 226

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

[API学习]开始入门WinMain

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

get it 466

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Windows程序设计(从WinMain开始)

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

gxj1680的专栏 1062

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

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、付费专栏及课程。

余额充值