Win32窗口之初级封装

封装一个Win32窗口类,还真不是那么容易的事。我打算分成几个等级的封装,后面的会解决一些前面留下的问题。接下来就看看最初级最粗糙的封装。

新建一个Window类,声明如下:

 1 class Window
 2 {
 3 public:
 4     Window(char* szClassName, char* szWindowTitle);
 5 
 6     void show() const;
 7 
 8 protected:
 9     virtual void draw() const;
10 
11     virtual HRESULT winProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
12 
13 private:
14     static  HRESULT CALLBACK WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
15 
16     bool createWin32Window(char* szClassName, char* szWindowTitle);
17 
18 protected:
19     HWND mhWnd;
20 
21     bool mbExit;
22 };

 

构造函数有两个参数分别代表窗口类名字和显示在标题栏上的文字,构造函数调用了createWin32Window并将两个参数传进去,createWin32Window定义如下:

void Window::createWin32Window(char* szClassName, char* szWindowTitle)
{
    WNDCLASS wc;
    wc.lpfnWndProc = (WNDPROC)&Window::WinProc;
    wc.lpszClassName = szClassName;
    //......

    RegisterClass(&wc); // 注册窗口类

    mhWnd = CreateWindowEx(0, szClassName, szWindowTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, wc.hInstance, NULL);// 创建窗口
}

这里我没有做任何异常检查,一般情况下我们应该检查RegisterClass和CreateWindowEx的返回值,看看是否正确如期望中的一样。特别是在构造函数或者会通过构造函数调用的函数中,请不要添加一些可能会执行不成功的代码或者抛出异常。这里也没有添加一些函数来设定窗口的显示位置和显示样式,都是使用默认。窗口创建成功后,可以通过调用show显示出来,show函数定义如下:

void Window::show() const
{
    ShowWindow(mhWnd, SW_SHOWNORMAL);

    UpdateWindow(mhWnd);

    MSG msg;
    while (!mbExit)
    {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            draw();
        }
    }

    DestroyWindow(mhWnd);
}

这个函数首先通过API显示出窗口,然后进入消息循环。PeekMessage会查看消息队列中是否有消息没有处理,如果有,移除该消息并返回true,接下来分派消息,然后由系统调用Window类的静态成员函数WinProc(注意首字母大写)进行消息响应,这部分会在后面介绍。如果消息队列中没有消息,PeekMessage会立即返回false,并调用draw虚函数。这种情况在一些游戏程序中很常见,我们希望当应用程序空闲(没有待处理的消息)的时候去更新下一帧,而不是傻等。那非游戏程序一般是这样的方式:

while(GetMessage(&msg), NULL, NULL, NULL)
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

注意,这种方式show窗口会造成只要窗口没有关闭,show后面的代码不会执行,这也是一个很要命的缺点,我们将在以后的版本中解决这个问题。下面来看看由系统调用的回调函数WinProc,由于WinProc的签名不能随便更改,因此它不能作为类的非静态成员函数,因为非静态成员函数隐含了this指针作为参数,只能是非成员函数或者静态成员函数。到底是设计为非成员函数好呢还是静态成员函数好呢?

HRESULT CALLBACK Window::WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    return window.winProc(hWnd, msg, wParam, lParam);
}

在这里,WinProc内部调用了成员函数winProc(注意首字母小写),而winProc是protected访问级别的,不想让客户端调用它。如果WinProc是非成员函数的话,还必须将它作为类的friend才行,而我要告诉大家,friend这东西能不用就不要用,破坏封装性,虽然生活中多几个friend总是好的。而将它设为类的静态成员函数的话,则可以免掉这个问题。大家有没有注意到WinProc中有一个window对象,其实它是一个全局对象,定义在其他文件(main.cpp)中,并在此文件中加上一句extern Window window;告诉编译器这个window对象是全局对象,定义在其他文件中。所以这相对于上面那个确定,这个更致命。
最后看看成员函数winProc

HRESULT Window::winProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_CLOSE: mbExit = true; break;
        case WM_PAINT: draw(); break;
        // more to come
        default: return DefWindowProc(hWnd, msg, wParam, lParam);
    }

    return 0;
}

你可以在这里处理任何消息,也可以在子类中去重写它处理其他消息。比较好的做法是,针对每一个需要处理的消息添加并调用一个虚函数,让子类去重写这个虚函数。比如添加一个响应鼠标移动的 virtual void mouseMove(int x, int y)然后再switch分支中增加一个case WM_MOUSEMOVE:mouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));break;即可。
现在这个窗口类只是被很粗糙地封装完成了,它能完成的就只能是显示一个窗口,点击关闭按钮退出程序。听起来好像一个空白的窗口算是已经完成了,但是还早,它有很多不足。

1. 创建窗口的时候,虽然第一个参数ClassName是客户端穿进去的,如果创建两个窗口并且这个参数相同的话,会创建失败,因为Windows不允许一个类名被注册两次。加上这个参数是为以后做铺垫。

2. 显示窗口后,show函数后面的代码将不会执行,直到窗口被关闭。

3. 必须定义一个全局的window对象,且名称固定(因为在WinProc中需要能访问到这个对象),从这点看来,这个封装基本一无是处。

4. 定义多个对象(szClassName参数前提要不相同)的话,其他将无法响应消息。

差不多就这些吧,但是每个都很要命,等下一篇来改进吧。

 

 

转载于:https://www.cnblogs.com/jianqifeihong/archive/2012/09/11/2680899.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用C++代码封装win32操作类, 与MFC相似,对于学习SDK与C++是巨好的参考 Tutorials Menu of tutorials Tutorial 1: The Simplest Window Tutorial 2: Using Classes and Inheritance Tutorial 3: Using Messages to Create a Scribble Window Tutorial 4: Repainting the Window Tutorial 5: Wrapping a Frame around our Scribble Window Tutorial 6: Customising Window Creation Tutorial 7: Customising the Toolbar Tutorial 8: Loading and Saving Files Tutorial 9: Printing Tutorial 10: Finishing Touches Tutorial 1: The Simplest Window The following code uses Win32++ to create a window. This is all the code you need (in combination with Win32++) to create and display a simple window. Note that in order to add the Win32++ code to our program, we use an #include statement as shown below. #include "../Win32++/Wincore.h" INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPTSTR, int) { //Start Win32++ CWinApp MyApp; //Create a CWnd object CWnd MyWindow; //Create (and display) the window MyWindow.Create(); //Run the application return MyApp.Run(); } This program has four key steps: Start Win32++. We do this here by creating a CWinApp object called MyApp. Create a CWnd object called MyWindow. Create a default window by calling the Create function. Start the message loop, by calling the Run function. If you compile and run this program, you'll find that the application doesn't end when the window is closed. This is behaviour is normal. An illustration of how to use messages to control the windows behaviour (including closing the application) will be left until tutorial 3.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值