打赏

C\C++ Windows程序设计[2]:创建一个窗口

上篇文章讲了如何编写一个弹出对话框的程序,这篇文章就来讲讲如何在C\C++下创建一个窗口。

来简单介绍下 Windows窗口程序 的创建过程:
首先我们需要注册一个窗口类,可以使用RegisterClass函数,接着使用CreateWindow创建一个窗口。下一步需要让窗口在屏幕上面显示出来,可以使用ShowWindow来完成,接着是更新窗口,注意一定不能漏掉这个步骤!这个阔以使用UpdateWindow来完成。接着就是建立消息循环队列啦,如果没有这个,程序就不能响应Windows发来的信息啦。


来康康代码:
#include <windows.h>

LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;        //声明用来处理消息的函数

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )
{
    static TCHAR szAppName[] = TEXT("MyWindow") ;
    HWND hwnd ;
    MSG msg ;
    WNDCLASS wndclass ;        //声明一个窗口类对象

    //以下为窗口类对象wndclass的属性
    wndclass.style = CS_HREDRAW | CS_VREDRAW ;                         //窗口样式
    wndclass.lpszClassName = szAppName ;                               //窗口类名
    wndclass.lpszMenuName = NULL ;                                     //窗口菜单:无
    wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH) ;    //窗口背景颜色
    wndclass.lpfnWndProc = WndProc ;                                   //窗口处理函数
    wndclass.cbWndExtra = 0 ;                                          //窗口实例扩展:无
    wndclass.cbClsExtra = 0 ;                                          //窗口类扩展:无
    wndclass.hInstance = hInstance ;                                   //窗口实例句柄
    wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION ) ;               //窗口最小化图标:使用缺省图标
    wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ) ;                 //窗口采用箭头光标

    if( !RegisterClass( &wndclass ) )
    {    //注册窗口类, 如果注册失败弹出错误提示
        MessageBox( NULL, TEXT("窗口注册失败"), TEXT("错误"), MB_OK | MB_ICONERROR ) ;
        return 0 ;
    }

    hwnd = CreateWindow(               //创建窗口
        szAppName,                     //窗口类名
        TEXT("我的第一个Windows窗口"),   //窗口标题
        WS_OVERLAPPEDWINDOW,           //窗口的风格
        CW_USEDEFAULT,                 //窗口初始显示位置x:使用缺省值
        CW_USEDEFAULT,                 //窗口初始显示位置y:使用缺省值
        CW_USEDEFAULT,                 //窗口的宽度:使用缺省值
        CW_USEDEFAULT,                 //窗口的高度:使用缺省值
        NULL,                          //父窗口:无
        NULL,                          //子菜单:无
        hInstance,                     //该窗口应用程序的实例句柄 
        NULL                       
    ) ;

    ShowWindow( hwnd, iCmdShow ) ;        //显示窗口
    UpdateWindow( hwnd ) ;                //更新窗口

    while( GetMessage( &msg, NULL, 0, 0 ) )        //从消息队列中获取消息
    {
        TranslateMessage( &msg ) ;                 //将虚拟键消息转换为字符消息
        DispatchMessage( &msg ) ;                  //分发到回调函数(过程函数)
    }
    return msg.wParam ;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    HDC hdc ;                //设备环境句柄
    PAINTSTRUCT ps ;         //绘制结构
    RECT rect;               //矩形结构

    switch( message )        //处理得到的消息
    {
        case WM_PAINT:           //处理窗口区域无效时发来的消息
            hdc = BeginPaint( hwnd, &ps ) ;
            GetClientRect( hwnd, &rect ) ;
            DrawText( hdc, TEXT("Hello World"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER ) ;  //文字
            EndPaint( hwnd, &ps ) ;
            return 0 ;
        case WM_DESTROY:         //处理窗口关闭时的消息
            MessageBox( hwnd, TEXT("关闭程序!"), TEXT("结束"), MB_OK | MB_ICONINFORMATION ) ;
            PostQuitMessage( 0 ) ;
            return 0;
    }
    return DefWindowProc( hwnd, message, wParam, lParam ) ;        //DefWindowProc处理我们自定义的消息处理函数没有处理到的消息
}


编译(方式同上篇文章),你会看到个Windows窗口,啊恭喜你!你已经编译出了你人生第一个Windows窗口程序(滑稽)。
由上面的代码可知Windows窗口的代码框架:
#include <windows.h>

LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;        //声明用来处理消息的函数

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow ) //Windows窗口程序的入口函数
{
    //创建窗口、消息循环等
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ){
    //处理消息
    
    return DefWindowProc( hwnd, message, wParam, lParam ) ;
}

观察上面的代码,你会发现,哎,main不见了,取而代之的是WinMain,还有一个WndProc!是的你没看错,Windows窗口程序不再使用main作为入口函数了,而是使用WinMain来作为主函数。而且WinMain的参数也变了: ```cpp int WINAPI WinMain( //Windows窗口程序入口函数 HINSTANCE hInstance, //实例句柄 HINSTANCE hPrevInstance, //无用参数,总是为NULL(空,即“0”) PSTR szCmdLine, //传入的命令 int iCmdShow //窗口显示类型 ) ```
再仔细观察,你会发现,WinMain旁边还有个WINAPI,这是什么意思呢?其实,WINAPI是个在windef.h里面定义的标识符: ```cpp #define WINAPI __stdcall ``` 这条语句规定了一种函数调用约定,表明如何生成在堆栈中放置函数调用参数的机器代码,绝大部分 Windows 函数调用都定义成 WINAPI。

Windows窗口程序工作原理


运行刚才编译的程序,你会发现不管怎么拖动、缩放来改变 程序 的大小,那段Hello World的文字永远是在屏幕正中央,这是怎么做到的呢?这就涉及到Windows窗口程序的工作原理了。 其实,用户调整窗口大小所产生的琐碎代码都是 Windows 实现的而并非应用程序,那么应用程序如何知道自己的大小以及被改变了?每当用户进行拖动窗口或者点击窗口之类的操作时,Windows都会向程序发送一条消息,这就是上面代码中的WndProc函数的作用。每当Windows向应用程序发送一条消息,都会触发WndProc函数,现在来康康WndProc的参数:
LRESULT CALLBACK WndProc(  //处理Windows发送来的消息
    HWND hwnd,             //窗口句柄(后面会讲到)
    UINT message,          //Windows发来的消息
    WPARAM wParam,         //传入的参数,暂时不需要理解
    LPARAM lParam          //传入的参数,暂时不需要理解
)

显然,WndProc是对message这个参数进行处理。


现在来一个个解析调用到的Windows API:

函数 意义
LoadIcon 加载图标
LoadCursor 加载鼠标光标
GetStockObject 获取图形对象
RegisterClass 注册一个窗口类
MessageBox 弹出对话框
CreateWindow 创建一个窗口
ShowWindow 显示窗口
UpdateWindow 更新窗口
GetMessage 从消息队列中获取消息
TranslateMessage 转换消息
DispatchMessage 将消息分发到回调函数处理
BeginDraw 开始绘图
GetClientRect 获取窗口大小
DrawText 绘制文字
EndPaint 结束绘制
PostQuitMessage 插入"退出"消息到消息队列
DefWindowProc 执行默认的消息处理



posted @ 2020-03-16 09:27  _Return  阅读(...)  评论(...编辑  收藏