请问这是图片出处!谁有原哲学视频原出处!

完善句子的作者、出处、完整全攵或修改错误的作者、出处、内容请

上一篇中我给各位说了一般人认為C++中较为难的东西——指针其实对于C++,难点当然不局限在指针这玩意儿上还有一些有趣的概念,如模板类、虚基类、纯虚函数等这些都是概念性的东西,几乎每一本C++书上都会介绍而平时我们除了会接触到纯虚函数外,其他的不多用纯虚函数,你可以认为与C#中的抽潒方法或接口中的方法类似即只定义,不实现好处就是多态,发何处理由派生类来决定。

在开始吹牛之前我先推荐一套哲学视频原出处教程,孙鑫老师的C++教程共20课,我不是帮他老人家打广告而是因为孙老师讲的课是我听过的最好的课,我都看过4次了我完全可鉯用他的哲学视频原出处教程来复习C++的。

好了F话说完了,下面我就扯一下编写一个Win32应用程序的大致流程不管你的程序有多么复杂,多麼变态其基本思路和流程是不变的。这就好比你写书法的时候特别是写楷书,我不管你用的是欧体、颜体还是柳体,你都得遵守“詠字八法”基本规则

那么,我们要编写一个Win32应用程序要经过哪几个步骤呢?

你不妨想一想你有一家工厂是生产女性服装的,如果你偠生产一批新式服装(例如某种冬装)你会有哪些流程?

首先如果我们确定要做这么一款服式,我们要请设计师来把服装设计好然後打版,打版就是生成基本样本以后工人就按照这个样本做就行了。

其次注册产品,向上级主管申报登记后就转入车间或下游加工企业开工。

再次为了展示你的新产品的特色,你要举办一场服装表演

接着、持续更新,发现产品存在的问题不断改进修正。

我们开發Win32应用程序也是遵守这样的规范不过,我想现在很少人用Win32在实际开发中毕竟它的开发效率是相当地低下,所以曾被某些人误认为只適用于开发木马程序。其实也不一定的,不要太邪恶了

MFC对Win API函数的封装,后来出现了托管C++你可以用于写WinForm程序,这样可以提高开发效率

如果你有足够的时间,如果你还在学习编程如果你是刚进入大学的年轻有为者,你不用急因为你会有更多的时间磨炼,你应当考虑哆学一点C类语言C++的学习你会发现你能学到很多其他语言中学不到的知识,特别是接触到不少原理性的东西能加深你对编程哲学的认知。

我们在学习标准C++的时候都知道每个应用程序运行时都会先进入入口点函数main,而当从main函数跳出时程序就结束了在Windows编程里面,也是一样嘚只是我们的入口点函数不叫main,叫WinMain这个函数不同于main,我们不能乱来它的定义必须与声明保持一致。

我建议各位安装VS的时候都顺便哽新帮助文档到本地硬盘,这样我们可以方便查找有一点要注意,目前DestTop Develop的文档基本上是英文的做好心理准备。

WinMain函数怎么写呢不用记嘚,到MSDN文档一搜直接复制就行了。


这个函数带了一个CALLBACK说明它是一个回调函数,那么这个CALLBACK是啥呢我们先不管,我们先动写一个Windows让大镓有一个更直观的认识。

1、启动你的开发工具版本任意。

2、从菜单栏中依次【文件】【新建】【项目】在新建项目窗口中,选择Win32-Win32应用程序

2、点击确定后,会弹出一个向导单击【下一步】。项目类型选择Windows应用程序附加选项选择空项目,我们要自己编写实现代码

3、單击完成,项目创建成功打开【解决方案资源管理器】,在“源文件”文件夹上右击从菜单中找到【添加】【新建项】,注意是源攵件,不要搞到头文件去了

在新建项窗口中选C++代码文件,.cpp后缀的不要选错了,选成头文件不然无法编译,因为头文件是不参与编译嘚文件名随便。

包含中的委托我想这样就好理解了,委托只声明了方法的参数和返回值并没有具体处理代码。

WinMain是由系统调用的而WinMainΦ的代码如何写,那操作系统就不管了就好像我告诉你明天有聚会,一起去爬山反正我是通知你了,至于去不去那是你决定了

接下來看看入口点函数的参数。

注意我们平时看到很多如HANDLE,HINSTANCEHBRUSH,WPARAMLPARAM,HICONHWND等一大串数据类型,也许我们会说怎么Windows开发有那么多数据类型。其實你错了人总是被眼睛所看到的东西欺骗,Win API 中根本没有什么新的数据类型全都是标准C++中的类型,说白了这些东西全是数字来的。如果你不信自己可以研究一下。

它定义这些名字只是方便使用罢了,比如下面这样:

第一个变量指的是窗口的句柄第二个指的是一个圖标的句柄,第三个是当前应用程序的实例句柄你看看,如果我们所有的句柄都是int我们就无法判断那些类型是专门用来表示光标资源,不知道哪些类型是专用来表示位图的句柄了但是,如果我们这样:

这样就很直观我一看这名就知道是Brush Handlers,哦我就明白它是专门用来管理内存中的画刷资源的,看这就很明了,所以通常这些新定义的类型或者宏,都是取有意义的名字比如消息,它也是一个数字洳果我说115代表叫你去滚,但光是一个115谁知道你什么意思但是,如果我们为它定义一个宏:

WinMain的第一个参数是当前应用程序的实例句柄第②个参数是前一个实例,比如我把kill.exe运行了两个实例进程列表中会有两个kill.exe,这时候第一次运行的实例号假设为0001就传递第一个参数hInstance,第二佽运行的假设实例号为0002就传给了hPrevInstance参数。

lpCmdLine参数从名字上就猜到了就是命令行参数,那LPSTR是啥呢它其实就是一个字符串,你可以跟入定义僦知道了它其实就是char*,指向char的指针记得我上一篇文章中说的指针有创建数组的功能吗?对其实这里传入的命令行参数应该是char[ ],这就昰我在第一篇文章中要说指针的原因

这里告诉大家一个技巧,我们怎么知道哪些参数是指针类型呢因为不是所有参数都有 * 标识。技巧還是在命名上以后,只要我们看到P开头的或者LP开头的,都是指针类型

最后一个参数nCmdShow是主窗口的显示方式。它定义了以下宏

0


这个参數是操作系统传入的,我们无法修改它那么,应用程序在运行时是如何决定这个参数的呢?看看这个不用我介绍了吧,你一定很熟悉


我们写了WinMain,但我们还要在WinMain前面预先定义一个WindowProc函数C++与C#,Java这些语言不同你只需记住,C++编译器的解析是从左到右从上到下的,如果某函数要放到代码后面来实现但在此之前要使用,那么你必须先声明一下不然编译时会找不到。这里因为我们通常会把WindowProc实现放在WinMain之后泹是在WinMain中设计窗口类时要用到它的指针,这时候我们必须在WinMain之前声明WindowProc。

同样地WindowProc的定义我们不用记,到MSDN直接抄就行了

// 必须要进行前导聲明


前导声明与后面实现的函数的签名必须一致,编译才会认为它们是同一个函数在WindowProc中返回DefWindowProc是把我们不感兴趣或者没有处理的消息交回給操作系统来处理。也许你会问函数的名字一定要叫WindowProc吗?当然不是了你可以改为其他名字,如MyProc但前提是返回值和参数的类型以及个數必须一致。

这个函数带了CALLBACK说明不是我们调用的,也是由操作系统调用的我们在这个函数里面对需要处理的消息进行响应。至于为什么可以改函数的名字而系统为什么能找到这个函数呢,后面你就知道了

设计窗口类,其实就是设计我们程序的主窗口如有没有标题欄,背景什么颜色有没有边框,可不可以调整大小等要设计窗口类,我们用到一个结构——

通常情况下我们用WNDCLASS就可以了,当然还有┅个WNDCLASSEX的扩展结构在API里面,凡是看到EX结尾的都是扩展的意思比如CreateWindowEx就是CreateWindow的扩展函数。

第一个成员是窗口的类样式注意,不要和窗口样式(WS_xxxxx)混淆了这里指的是这个窗口类的特征,不是窗口的外观特征这两个style是不一样的。

它的值可以参考MSDN通常我们只需要两个就可以了——CS_HREDRAW | CS_VREDRAW,从名字就看出来了就是同时具备水平重画和垂直重画。因为当我们的窗口显示的时候被其他窗口挡住后重新显示,或者大小调整后窗口都要发生绘制,就像我们在纸上涂鸦一样每次窗口的变化都会“粉刷”一遍,并发送WM_PAINT消息

lpfnWndProc参数就是用来设置你用哪个WindowProc来处悝消息,前面我说过我们只要不更改回调函数的返回值和参数的类型和顺序,就可以随意设置函数的名字那为什么系统可以找到我们鼡的回调函数呢,对的就是通过lpfnWndProc传进去的,它是一个函数指针也就是它里面保存的是我们定义的WindowProc的入口地址,使用很简单我们只需偠把函数的名字传给它就可以了。

hbrBackground是窗口的背景色你也可以不设置,但在处理WM_PAINT消息时必须绘制窗口背景也可以直接用系统定义的颜色,MSDN为我们列出这些值大家不用记,直接到MSDN拿来用就行了这些都比较好理解,看名字就知道了

lpszMenuName指的是菜单的ID,没有菜单就NULLlpszClassName就是我们偠向系统注册的类名,字符不能与系统已存在的类名冲突,如“BUTTON”类

所以,在WinMain中设计窗口类

窗口类设计完成后,不要忘了向系统注冊这样系统才能知道有这个窗口类的存在。向操作系统注册窗口类使用RegisterClass函数,它的参数就是一个指向WNDCLASS结构体的指针所以我们传递的時候,要加上&符号

窗口类注册完成后,就应该创建窗口然后显示窗口,调用CreateWindow创建窗口如果成功,会返回一个窗口的句柄我们对这個窗口的操作都要用到这个句柄。什么是句柄呢其实它就是一串数字,只是一个标识而已内存中会存在各种资源,如图标、文本等為了可以有效标识这些资源,每一个资源都有其唯一的标识符这样,通过查找标识符就可以知道某个资源存在于内存中哪一块地址中,就好比你出身的时候长辈都要为你取个名字,你说名字用来干吗名字就是用来标识你的,不然你见到A叫小明,遇到B又叫小明那誰知道哪个才是小明啊?就好像你上大学去报到号会为你分配一个可以在本校学生中唯一标识你的学号,所有学生的学号都是不同的這样,只要通过索引学号就可以找到你的资料。

CreateWindow函数返回一个HWND类型它就是窗口类的句柄。

L"我的应用程序", //窗口标题文字 38, //窗口相对于父级嘚X坐标 20, //窗口相对于父级的Y坐标


窗外观的样式都是WS_打头的是Window Style的缩写,这个我就不说了MSDN上全有了。

窗口创建后就要显示它,就像我们的產品做了要向客户展示。显示窗口调用ShowWindow函数

既然要显示窗口了,那么ShowWindow的第一个参数就是刚才创建的窗口的句柄第二个参数控制窗口洳何显示,你可以从SW_XXXX中选一个也可以用WinMain传进来的参数,还记得WinMain的最后一个参数吗

为什么更新窗口这一步可有可无呢?因为只要程序在運行着只要不是最小化,只要窗口是可见的那么,我们的应用程序会不断接收到WM_PAINT通知这里先不说,后面你会明白的好了,更新窗ロ当然是调用UpdateWindow函数。

Windows操作系统是基于消息控制机制的用户与系统之间的交互,程序与系统之间的交互都是通过发送和接收消息来完荿的。就好像军队一样命令一旦传达,就要执行当然,我们的应用程序和军队不一样我们收到指令不一要执行,我们是可以选择性哋执行

我们知道,代码是不断往前执行的像我们刚才写的WinMain函数一样,如果你现在运行程序你会发现什么都没有,是不是程序不能运荇呢不是,其实程序是运行了只是它马上结束了,只要程序执行跳出了WinMain的右大括号程序就会结束了。那么要如何让程序不结束了,可能大家注意到我们在C程序中可以用一个getchar()函数来等到用户输入这样程序就人停在那里,直到用户输入内容但我们的窗口应用不能这樣做,因为用户有可能进行其他操作如最小化窗口,移动窗口改变窗口大小,或者点击窗口上的按钮等因此,我们不能简地弄一个getchar茬那里这样就无法响应用户的其他操作了。

可以让程序留在某处不结束的另一个方法就是使用循环而且是死循环,这样程序才会永久哋停在某个地方但这个死循环必须具有跳出的条件,不然你的程序会永久执行直达停电或者把电脑砸了。

这样消息循环就出现了只偠有与用户交互,系统人不断地向应用程序发送消息通知因为这些消息是不定时不断发送的,必须有一个绶冲区来存放就好像你去银荇办理手续要排队一样,我们从最前端取出一条一条消息处理后面新发送的消息会一直在排队,直到把所有消息处理完这就是消息队列

要取出一条消息调用GetMessage函数。函数会传入一个MSG结构体的指针当收到消息,会填充MSG结构体中的成员变量这样我们就知道我们的应用程序收到什么消息了,直到GetMessage函数取不到消息条件不成立,循环跳出这时应用程序就退出。MSG的定义如下:

hwnd不用说了就是窗口句柄,哪個窗口的句柄还记得WindowProc回调函数吗?你把这个函数交给了谁来处理hwnd就是谁的句柄,比如我们上面的代码我们是把WindowProc赋给了新注册的窗口類,并创建了主窗口返回一个表示主窗口的句柄,所以这里MSG中的hwnd指的就是我们的主窗口。

message就是我们接收到的消息看到,它是一个数芓无符号整型,所以我们操作的所有消息都是数字来的wParam和lParam是消息的附加参数,其实也是数值来的通常,lParam指示消息的处理结果不同消息的结果(返回值)不同,具体可参阅MSDN

有了一个整型的值来表示消息,我们为什么还需要附加参数呢你不妨想一下,如果接收一条WM_LBUTTONDOWN消息即鼠标左键按下时发送的通知消息,那么我们不仅知道左键按下这件事,我们更感趣的是鼠标在屏幕上的哪个坐标处按下左键,按了几下这时候,你公凭一条WM_LBUTTONDOWN消息是无法传递这么多消息的可能我们需要把按下左键时的坐标放入wParam参数中;最典型的就是WM_COMMAND消息,因為只要你使用菜单点击按钮都会发送这样一条消息,那么我怎么知道用户点了哪个按钮呢如果窗口中只有一个按钮,那好办用户肯萣单击了它,但是如果窗口上有10个按钮呢?而每一个按钮被单击都会发送WM_COMMAND消息你能知道用户点击了哪个按钮吗?所以我们要把用户點击了的那个按钮的句柄存到lParam参数中,这样一来我们就可以判断出用户到底点击了哪个按钮了。

这个函数在定义时带了一个WINAPI现在,按照前面我说的方法你应该猜到,它就是一个宏而真实的值是__stdcall,前文中说过了

第一个参数是以LP开头,还记得吗我说过的,你应该想箌它就是 MSG* 一个指向MSG结构的指针。第二个参数是句柄通常我们用NULL,因为我们会捕捉整个应用程序的消息后面两个参数是用来过滤消息嘚,指定哪个范围内的消息我接收在此范围之外的消息我拒收,如果不过滤就全设为0.返回值就不说了,自己看

TranslateMessage是用于转换按键信息嘚,因为键盘按下和弹起会发送WM_KEYDOWN和WM_KEYUP消息但如果我们只想知道用户输了哪些字符,这个函数可以把这些消息转换为WM_CHAR消息它表示的就是键盤按下的那个键的字符,如“A”这样我们处理起来就更方便了。

DispatchMessage函数是必须调用的它的功能就相当于一根传送带,每收到一条消息DispatchMessage函数负责把消息传到WindowProc让我们的代码来处理,如果不调用这个函数我们定义的WindowProc就永远接收不到消息,你就不能做消息响应了你的程序就呮能从运行就开始死掉了,没有响应

其实现在我们的应用程序是可以运行了,因为在WindowProc中我们调用了DefWindowProc函数,消息我们不作任何处理又紦控制权路由回到操作系统来默认处理,所以整个过程中,我们现在的消息循环是成立的只不过我们不做任何响应罢了。

好的现在峩把完整的代码贴一下,方便你把前面我们说的内容串联起来

// 必须要进行前导声明 L"我的应用程序", //窗口标题文字 38, //窗口相对于父级的X坐标 20, //窗ロ相对于父级的Y坐标


所有代码看上去貌似很正常,也遵守了流程设计窗口类,注册窗口类创建窗口,显示窗口更新窗口,消息循环是吧,这段代码看上去毫无破绽运行应该没问题吧。好如果你如此自信,那就试试吧

哈哈,结果会让很多人失望很多初学者就昰这样,一切看起来好像正常于是有人开始骂VC是垃圾,是编译器有bug也有人开始想放弃了,妈的这么难,不学了人啊,总是这样咾指责别人的问题,从不在自己身上找问题是真的VC的bug吗?

我前面说了这段代码貌似很正常,呵呵你看到问题在哪吗?给你两分钟来找错我提示一下,这个程序没有运行是因为主窗口根本就没有创建因为我在代码里面做了判断,如果窗口顺柄hwnd为NULL就退出,现在程序┅运行就退出了明显是窗口创建失败。

好了不用找了,很多人找不出来尤其是许多初学者,不少人找了一遍又一遍都说没有错误,至少代码提示没说有错编译运行也没报错,所以不少人自信地说代码没错。

其实你是对的代码确实没有错,而问题就出在WNDCLASS结构上认真看一下MSDN上有关RegisterClass函数说明中的一句话,这句话很多人没注意到但它很关键。

现在你明白了吧还不清楚?没关系看看我把代码这樣改一下你就知道了。

现在你运行一下,你一定能看到窗口

但现在你对窗口无法进行操作,因为后续的代码还没完成

为什么现在又鈳以了呢?MSDN那句话的意思就是说我们在注册窗口类之前必须填充WNDCLASS结构体何为填充,就是要为结构的所有成员赋值就算不需要你也要为咜赋一个NULL或0,因为结构在创建时没有对成员进行初始化这就导致变量无法正确的分配内存,最后注册失败

那么,如果一个结构体成员佷多而我只需要用到其中三个,其他的也要初始化是不是很麻烦,是的除了为每个成员赋值,还有一种较简单的方法就是在声明變量时给它赋一对大括号,里面放置结构体的应该分配内存的大小如:

这样一来,我们也发现窗口也可以成功创建。

我们还可以更简單直接把sizeof也去掉,在声明变量时直接赋一对空的大括号就行了,就如这样

这样写更简单,窗口类同样可以正常注册大括号代表的昰代码块,这样结构体有了一个初值,因此它会按照结构体的大小分配了相应的内存

为什么会这样呢?这里涉及到一个关于结构体的┅个很有趣的赋值方式我们先放下我们这个例子,下面我写一个简单的例子你就明白了。

在本例中我们定义了一个表示矩形的结构體 RECT ,它有四个成员分别横坐标,纵坐标宽度,高度但是,我们在声明和赋值中我们只用了一对大括号,把每个成员的值按照定義的顺序依次写到大括号中,即{ 0, 0, 20, 30 }x的值为0,y的值为0width为20,height的值为30

也就是说,我们可以通过这种简单的方法向结构变量赋值注意值的顺序要和成员变量定义的顺序相同。

现在回到我们的Windows程序来,我们明白了这种赋值方式对于 WNDCLASS wc = {  } 就不难理解了,这样虽然大括号里面是空的其实它已经把变量初始化了,都赋了默认值这样一来,就可以正确分配内存了

通常情况下,当我们的主窗口关闭后应用程序应该退出(木马程序除外),但是我们刚才运行后发现,为什么我的窗口关了但程序不退出呢?前面我说了要退出程序,就要先跳出消息循环和关闭哪个窗口无关。因此我们要解决两个问题:

1、如果跳出消息循环;

2、什么时候退出程序。

其实两个问题是可以合并到一起解决

首先要知道,当窗口被关闭为窗口所分配的内存会被销毁,同时我们会收到一条WM_DESTROY消息,因而我们只要在收到这条消息时调鼡PostQuitMessage函数,这个函数提交一条WM_QUIT消息而在消息循环中,WM_QUIT消息使GetMessage函数返回0这样一来,GetMessage返回FALSE就可以跳出消息循环了,这样应用程序就可以退絀了


我们会收到很多消息,所以用switch判断一下是不是WM_DESTROY消息如果是,退出应用程序

好了,这样我们一个完整的Windows应用程序就做好了。


下媔是完整的代码清单

// 必须要进行前导声明 L"我的应用程序", //窗口标题文字 38, //窗口相对于父级的X坐标 20, //窗口相对于父级的Y坐标

完善句子的作者、出处、完整全攵或修改错误的作者、出处、内容请

完善句子的作者、出处、完整全攵或修改错误的作者、出处、内容请

我要回帖

更多关于 哲学视频原出处 的文章

 

随机推荐