如何将WinAPI应用程序封装到C ++类中

时间:2010-06-14 12:11:40

标签: c++ winapi

有一个简单的WinAPI应用程序。它现在所做的就是:

  • 注册一个窗口类
  • 使用菜单注册托盘图标
  • 在注册表中创建一个值以自动启动
  • 最后,它使用互斥锁检查它是否是唯一的

由于我习惯于主要在C ++中编写代码,并且不允许使用MFC,因此我不得不以某种方式将其封装到C ++类中。到目前为止,我已经提出了这样的设计:

  • 有一个代表应用程序的类
  • 它保存所有wndclass,hinstance等变量,其中hinstance作为构造函数参数以及icmdshow和其他参数传递(参见WinMain原型)
  • 它具有注册窗口类,托盘图标,reigstry信息的功能
  • 它将消息循环封装在函数

在WinMain中,完成以下操作:

Application app(hInstance, szCmdLIne, iCmdShow);
return app.exec();

,构造函数执行以下操作:

registerClass();
registerTray();
registerAutostart();

到目前为止一切顺利。现在的问题是:如何创建窗口过程(必须是静态的,因为它是指向函数的c样式指针)并跟踪应用程序对象是什么,即保持指向应用程序的指针。 / p>

主要问题是:这通常是怎么做的?我太复杂了吗?将hInstance作为参数传递给Application构造函数是否可以? WndProc在哪里?

也许WndProc应该在课外,而Application指针应该是全局的?然后WndProc调用Application方法来响应各种事件。

还有一个可能的解决方案:使应用程序类成为单例。然后从WndProc获取该对象的句柄是微不足道的。

4 个答案:

答案 0 :(得分:4)

答案是SetWindowLongPtr。它允许您将void *与给定的hWnd相关联。然后,在WndProc中,您只需提取所述void *,强制转换并调用成员方法。问题解决方案。使用SetWindowLongPtr有一些起伏,你必须调用其他函数来查看效果或某些BS,并且Windows在CreateWindowEx返回之前发送消息,所以你必须为GetWindowLongPtr(hWnd,GWL_USERDATA)准备返回NULL。

这当然意味着对于给定的WindowProc,使用它的所有实例都必须具有公共接口,因为使用void *可以做的事情并不多。

而且,是的,可以将HINSTANCE传递给App构造函数。我见过的样本做了一些奇怪的事情来避免这种情况,但我自己从来没有让它成功。

编辑: 不要将Get / SetWindowLong与Get / SetWindowLongPtr混淆。 Get / SetWindowLong已弃用且不安全。

答案 1 :(得分:2)

  

你可以扩展这个类(我   用于答案)如你所愿,   取决于您想要的消息   处理

#pragma once 

#include <windows.h> 
#include <process.h> 
#include <iostream> 

using namespace std; 

static const char *g_AppName  = "Test"; 

class CMyWindow 
{ 
    HWND  _hWnd; 
    int _width; 
    int _height; 
public: 
    CMyWindow(const int width,const int height):_hWnd(NULL),_width(width),_height(height) 
    { 
        _beginthread( &CMyWindow::thread_entry, 0, this); 
    } 

    ~CMyWindow(void) 
    { 
        SendMessage(_hWnd, WM_CLOSE, NULL, NULL); 
    } 


private: 
    static void thread_entry(void * p_userdata) 
    { 
        CMyWindow * p_win = static_cast<CMyWindow*> (p_userdata); 
        p_win->create_window(); 
        p_win->message_loop(); 
    } 

    void create_window() 
    { 
        WNDCLASSEX wcex; 

        wcex.cbSize             = sizeof(WNDCLASSEX); 
        wcex.style              = CS_HREDRAW | CS_VREDRAW; 
        wcex.lpfnWndProc    = &CMyWindow::WindowProc; 
        wcex.cbClsExtra         = 0; 
        wcex.cbWndExtra         = 0; 
        wcex.hInstance          = GetModuleHandle(NULL); 
        wcex.hIcon              = LoadIcon(NULL, IDI_APPLICATION); 
        wcex.hCursor            = LoadCursor(NULL, IDC_ARROW); 
        wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1); 
        wcex.lpszMenuName   = NULL; 
        wcex.lpszClassName  = g_AppName; 
        wcex.hIconSm            = LoadIcon(NULL, IDI_APPLICATION); 

        RegisterClassEx(&wcex); 

        _hWnd = CreateWindow(g_AppName, g_AppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, GetModuleHandle(NULL), NULL); 

        ShowWindow(_hWnd, SW_SHOWDEFAULT); 
        UpdateWindow(_hWnd); 
    } 

    void message_loop() 
    { 
        MSG msg = {0}; 

        while (GetMessage(&msg, NULL, 0, 0)) 
        { 
            if(msg.message == WM_QUIT) 
            { 
                break; 
            } 

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

    static LRESULT WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
    { 
        switch(uMsg) 
        { 
        case WM_DESTROY: 
            PostQuitMessage(0); 
            return 0; 
        case WM_POWERBROADCAST: 
            { 
                //power management code here 
            } 

        } 

        return DefWindowProc(hWnd, uMsg, wParam, lParam); 
    } 
}; 
  

这是一个最小的引导程序:

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{

    CMyWindow t(640,480);

    Sleep(10000);

    return 0;
}

答案 2 :(得分:2)

不要按照提示使用Get/SetWindowLongPtr存储您的this指针,因为它是一个巨大的安全漏洞!您只需使用映射将HWND与指向类实例的指针相关联。您可以使用STL中的<map>类。

顺便说一下,你可以在那里找到关于这个主题的非常好的讨论:http://blogs.msdn.com/b/oldnewthing/archive/2005/04/22/410773.aspx

答案 3 :(得分:0)

我决定把它变成一个单例,因为它是主要的应用程序类,在程序中只有一个该类的实例没有问题。

现在我想问一个更相关的问题:假设我有一个偏好对话框。我创建对话框作为资源,然后给它一个过程,并在过程中,创建一个控制器对象。这是正确的方法吗?