入职作业总结(3.0)Windows程序开发

版权声明:本文为博主原创文章,转载请注明来源 https://blog.csdn.net/fatever/article/details/79602672

看DirectX书的的时候,发现示例代码给出的部分与以往的主函数int main(int argc, char **argv)不同,以为只是给了个函数,主函数得自己写。查阅了资料后发现是孤陋寡闻,原来windows应用程序的入口函数定义本来就比较特别。

第一个windows程序

代码以及参考链接:Your First Windows Program,这里只是对学习该代码进行的记录笔记。

Windows中应用程序入口是这样的int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)。各个参数含义见 这里

代码中,只有LRESULT CALLBACK myWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)是由用户自己定义的,确定窗口外观,行为等属性的函数。

虽然LRESULT CALLBACK myWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)定义了大部分窗口的参数和逻辑,但主函数从未显示地调用过该函数。每当DispatchMessage()函数(在上述主函数循环中)被调用时,它会间接地导致Windows调用myWindowProc()函数。具体说明如下。

建立窗口

根据官方介绍,每个窗口(window)都与一个 窗口类(window class)相关联。必须注意的是,从C++的角度来说,每个 窗口类 并不是一个C++的类,而是一个被操作系统使用的数据结构。窗口类 是在运行时注册的,具体的注册过程如下,先填充一个WNDCLASS类:

    const wchar_t CLASS_NAME[]  = L"Sample Window Class";
    WNDCLASS wc = { };
    wc.lpfnWndProc   = WindowProc;   //函数指针,定义了应用窗口的行为
    wc.hInstance     = hInstance;
    wc.lpszClassName = CLASS_NAME;

lpfnWndProc是一个指向application-defined function 的指针,这个函数称为window procedure 或“window proc.”。
hInstance是一个应用实例的句柄,该句柄从WinMain(主函数)的参数中获得。
lpszClassName是一个区别窗口类的字符串。这个字符串局限于当前进程内,所以这个名字只需要在本进程内独一无二即可。但是,有一些名字,如”Button”被系统用来作为控制的类名,所以用户需要避开这些名字。
WNDCLASS还有其他的成员,但我们可以暂且忽略他们,或将他们置为0。

第二步,调用RegisterClass函数注册该窗口类

RegisterClass(&wc);

第三步,调用CreateWindowEx创建窗口并获得返回的句柄:

HWND hwnd = CreateWindowEx(
    0,                              // Optional window styles.
    CLASS_NAME,                     // Window class
    L"Learn to Program Windows",    // Window text
    WS_OVERLAPPEDWINDOW,            // Window style
    // Size and position
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    NULL,       // Parent window    
    NULL,       // Menu
    hInstance,  // Instance handle
    NULL        // Additional application data
);

第四步,调用ShowWindow显示窗口:

ShowWindow(hwnd, nCmdShow);

其中,nCmdShow参数可被用来最小化或最大化窗口。
至此,窗口建立过程已经完成。

消息传递

一个GUI程序需要应对用户行为(鼠标键盘等)和系统行为(如检测到外设,系统功耗模式切换等),windows采用一种消息传递机制来处理程序运行时接收到的这些行为。
消息是一个专用的数字用来指代特定的事件,如按下鼠标左键对应的消息#define WM_LBUTTONDOWN 0x0201。此外,一些消息还包含额外的数据,比如上述的WM_LBUTTONDOWN就包含x,y坐标。
传递消息的方式是由操作系统调用对应窗口注册的window procedure(前文提到)。
对于每一个创建窗口的线程,操作系统创建一个对应的队列,由它保管对应线程负责的所有窗口(可能不止一个)。这个队列不能被用户直接操作,但可以通过调用GetMessage函数来从中获取一个消息

GetMessage(&msg, NULL, 0, 0);

GetMessage函数移除队列顶的第一个消息。如果队列空,则该函数阻塞。如果需要在后台处理某些任务,则可以创建一个其他线程来处理。函数中第一个参数是MSG结构的地址。如果调用成功,该结构会包含检测到的行为以及对应窗口(可能同时有多个窗口)等数据。其他三个参数(一般置为0)给与用户滤出所需要消息的能力。
虽然MSG结构包含很多信息,但我们一般不直接访问它,而是调用以下两个函数:

TranslateMessage(&msg); 
DispatchMessage(&msg);

TranslateMessage主要处理键盘相关的输入,并将输入(键按下,升起)转换为对应字符。

DispatchMessage函数告诉操作系统调用消息对应窗口的 窗口处理函数(window procedure)。

一般来说,GetMessage都会返回一个非零值。当你想退出你的程序时,可以考虑调用PostQuitMessage函数:

PostQuitMessage(0);

该函数会将一个WM_QUIT消息放到消息队列中。这个信号是个特殊的信号,它会导致GetMessage函数返回0。这个信息时由该函数产生的,意味着(大部分情况下) 窗口处理函数 不可能收到一个这样的信号**,也就不需要在窗口处理函数中针对该信号进行特殊处理。

窗口处理函数

如前文所述,DispatchMessage函数会调用对应窗口的窗口处理函数,该窗口处理函数需要有以下形式(函数名可以不同):

    LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

参数:hwnd是所指窗口的句柄,uMsg是消息值,wParamlParam是消息的附带数据。
返回值:LRESULT是一个程序返回给Windows的整数返回值(翻译自MSDN文档,没完全懂,感觉是类似return 0中的0的作用)。

PS:CALLBACK的作用我找到这么一句话:

简单的说,我们调用别人的API叫call,调用的第三方api调用我们的函数叫回调(callback)

上述过程中,我们调用DispatchMessage来调用我们的WindowProc函数,所以叫回调函数

一种常见的做法是在WindowProc函数中利用switch来处理不同信号。对于不想特殊处理的信号,可以调用DefWindowProc执行系统默认的反馈操作。

PS:采用VS编译的时候,由于一开始选择的项目类型是console win32 project(命令行版)所以会失败。想要编译winMain项目需要做如下调整

  1. 菜单中选择 Project->Properties, 弹出Property Pages窗口
  2. 在左边栏中依次选择:Configuration Properties->C/C++->Preprocessor,然后在右边栏的Preprocessor Definitions对应的项中删除_CONSOLE, 添加_WINDOWS.
  3. 在左边栏中依次选择:Configuration Properties->Linker->System,然后在右边栏的SubSystem对应的项改为Windows(/SUBSYSTEM:WINDOWS)
展开阅读全文

没有更多推荐了,返回首页