Win32 C ++在类中创建窗口和过程

时间:2013-11-28 01:59:16

标签: c++ class winapi

前置/问题

我正在尝试制作一个相当简单的工具来帮助调试变量值。因为它是完全自我包含的类是我的目标。最终产品我可以使用类中的函数,如ShowThisValue(无论如何)。

我遇到的问题是,如果可能的话,我无法弄清楚在课堂上有这个程序。这是短版本,有问题。

-Code再次更新11/29 / 13- - 我现在把它放在自己的项目中。

[main.cpp中]

viewvars TEST; // global
TEST.CreateTestWindow(hThisInstance); // in WinMain() right before ShowWindow(hwnd, nFunsterStil);

[viewvars.h]整个更新

class viewvars {

private:
    HWND hWindow;            // the window, a pointer to
    LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);        
    static LRESULT CALLBACK ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

public:        
    viewvars(); // blank constructor
    int CreateTestWindow(HINSTANCE hInst);

};

// blank constructor
viewvars::viewvars() {}



// create the window
int viewvars::CreateTestWindow(HINSTANCE hInst) {

// variables
char thisClassName[] = "viewVars";     
MSG msg;       
WNDCLASS wincl;  


// check for class info and modify the info
if (!GetClassInfo(hInst, thisClassName, &wincl)) {
    wincl.style = 0;
    wincl.hInstance = hInst;
    wincl.lpszClassName = thisClassName;
    wincl.lpfnWndProc = &ThisWindowProc;
    wincl.cbClsExtra = 0;
    wincl.cbWndExtra = 0;
    wincl.hIcon = NULL;
    wincl.hCursor = NULL;
    wincl.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
    wincl.lpszMenuName = NULL;

    if (RegisterClass(&wincl) == 0) {
        MessageBox(NULL,"The window class failed to register.","Error",0); 
        return -1;
    }
}  

// create window
hWindow = CreateWindow(thisClassName, "Test", WS_POPUP | WS_CLIPCHILDREN, 10, 10, 200, 200, NULL, NULL, hInst, this);
if (hWindow == NULL) { 
    MessageBox(NULL,"Problem creating the window.","Error",0); 
    return -1; 
} 

// show window
ShowWindow(hWindow, TRUE);

// message loop
while (GetMessage(&msg, hWindow, 0, 0))
{
    TranslateMessage(&msg);  
    DispatchMessage(&msg); 
}    

// then quit window?
DestroyWindow(hWindow);
hWindow = NULL;

return msg.wParam;
}   


// window proc
LRESULT CALLBACK viewvars::ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {

MessageBox(NULL,"Has it gone this far?","Bench",0); 

// variable
viewvars *view;

// ????
if (message == WM_NCCREATE) {
    CREATESTRUCT *cs = (CREATESTRUCT*)lParam; 
    view = (viewvars*) cs->lpCreateParams;

    SetLastError(0);
    if (SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR) view) == 0) {
        if (GetLastError() != 0) {
            MessageBox(NULL,"There has been an error near here.","Error",0); 
            return FALSE;
        }
    }
}
else {
    view = (viewvars*) GetWindowLongPtr(hwnd, GWL_USERDATA);
}


if (view) return view->WindowProc(message, wParam, lParam);

MessageBox(NULL,"If shown, the above statement did not return, and the statement below did.","Error",0); 

return DefWindowProc(hwnd, message, wParam, lParam);
}


LRESULT viewvars::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) {
// you can access non-static members in here...
MessageBox(NULL,"Made it to window proc.","Error",0);

switch (message)
{
    case WM_PAINT: 
        return 0;
    break;
    case WM_DESTROY:
        PostQuitMessage(0);       
        return 0;
    break;
    default:
        MessageBox(NULL,"DefWindowProc Returned.","Error",0);
        return DefWindowProc(hWindow, message, wParam, lParam);
    break;
}
}

消息框按此顺序显示:

  • 它成功了吗?
  • 制作窗口过程
  • DefWindowProc返回
  • 它成功了吗? //重复?
  • 制作窗口过程
  • DefWindowProc返回
  • 问题创建窗口

感谢您的帮助。你知道问题出在哪里吗?enter image description here

5 个答案:

答案 0 :(得分:15)

使用非静态类方法作为窗口过程需要动态分配的thunk,这是一种高级技术,我不会在这里进入。

另一种方法是将类方法声明为static,然后它将作为窗口过程。当然,作为static方法,它不再能够在没有实例指针的情况下访问非静态类成员。要获取该指针,您可以让该类将this指针传递给lpParam的{​​{1}}参数,然后窗口过程可以从CreateWindow/Ex()消息中提取该指针,使用WM_NCCREATE将其存储在窗口中。之后,后续消息可以使用SetWindowLong/Ptr(GWL_USERDATA)检索该指针,从而能够访问该对象的非静态成员。例如:

GetWindowLong/Ptr(GWL_USERDATA)

class viewvars
{
private:
    HWND hWindow;
    LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);

    static LRESULT CALLBACK ThisWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
public:
    int CreateTestWindow(HINSTANCE hInst);
};

答案 1 :(得分:9)

主消息循环不能在你的类中,尤其不能在“CreateTestWindow”函数中,因为在你的线程收到WM_QUIT消息后,你不会从该函数返回GetMessage返回0。

以下是viewvars课程的简单实现。要点:

  • Window Proc是静态成员。
  • Window Proc和对象之间的链接是通过 使用GWLP_USERDATA。见SetWindowLongPtr
  • 如果窗口仍然存在,则DTOR类会销毁该窗口。 WM_DESTROY message将HWND成员设置为0。
  • 将OnMsgXXX方法添加到类中很简单:声明/定义然后 然后使用'this'指针从WindowProc中调用它们 存储在GWLP_USERDATA。

修改

  • 根据陈先生的建议,早先将HWND绑定到Object(在WM_NCCREATE中),以便在窗口创建期间将消息处理程序作为方法

我更改了创建样式,以显示窗口并能够移动它。

// VIEWVARS.H
class viewvars {

public:
    static viewvars* CreateTestWindow( HINSTANCE hInstance );
    viewvars() : m_hWnd( 0 ) {}
    ~viewvars();

private:
    static LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
    static const char * m_pszClassName;
    HWND m_hWnd;

};

// VIEWVARS.CPP
#include "viewvars.h"

const char * viewvars::m_pszClassName = "viewvars";

viewvars * viewvars::CreateTestWindow( HINSTANCE hInst ) {

    WNDCLASS wincl;
    if (!GetClassInfo(hInst, m_pszClassName, &wincl)) {
        wincl.style = 0;
        wincl.hInstance = hInst;
        wincl.lpszClassName = m_pszClassName;
        wincl.lpfnWndProc = WindowProc;
        wincl.cbClsExtra = 0;
        wincl.cbWndExtra = 0;
        wincl.hIcon = NULL;
        wincl.hCursor = NULL;
        wincl.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
        wincl.lpszMenuName = NULL;
        if (RegisterClass(&wincl) == 0) {
            MessageBox(NULL,"The window class failed to register.","Error",0);
            return 0;
        }
    }

    viewvars * pviewvars = new viewvars;
    HWND hWnd = CreateWindow( m_pszClassName, "Test", WS_VISIBLE | WS_OVERLAPPED, 50, 50, 200, 200, NULL, NULL, hInst, pviewvars );
    if ( hWnd == NULL ) {
        delete pviewvars;
        MessageBox(NULL,"Problem creating the window.","Error",0); 
        return 0; 
    }

    return pviewvars;

}

 LRESULT CALLBACK viewvars::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {

    switch ( uMsg ) {

        case WM_NCCREATE: {
            CREATESTRUCT * pcs = (CREATESTRUCT*)lParam;
            viewvars * pviewvars = (viewvars*)pcs->lpCreateParams;
            pviewvars->m_hWnd = hwnd;
            SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG)pcs->lpCreateParams );
            return TRUE;
        }

        case WM_DESTROY: {
            viewvars * pviewvars = (viewvars *)GetWindowLongPtr( hwnd, GWLP_USERDATA );
            if ( pviewvars ) pviewvars->m_hWnd = 0;
            break;
        }

        default:
            return DefWindowProc( hwnd, uMsg, wParam, lParam );

    }

    return 0;

}

viewvars::~viewvars() {
    if ( m_hWnd ) DestroyWindow( m_hWnd );
}

最后,一个“主要”样本,但要注意,这里没有办法结束这个过程。这应该由常规代码(另一个窗口)来处理。

// MAIN.CPP
#include <Windows.h>
#include "viewvars.h"

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
    viewvars * pviewvars = viewvars::CreateTestWindow( hInstance );
    if ( pviewvars == 0 ) return 0;

    BOOL bRet;
    MSG msg;
    while( (bRet = GetMessage( &msg, 0, 0, 0 )) != 0)
    { 
        if (bRet == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        }
    }

    delete pviewvars;

    return 0;

}

答案 2 :(得分:3)

不幸的是,使用实例方法作为WndProc的C风格回调函数将不起作用。至少没有任何直接的方式。

它不起作用的原因是实例方法需要传入this指针(指向实例)并且调用WndProc的代码无法正确设置。 Win32 API最初设计时考虑到了C,因此这是一个必须使用一些解决方法的领域。

解决此问题的一种方法是创建一个静态方法,用作窗口proc并将消息分派给类实例。必须注册类实例(读取添加到静态集合),以便静态方法知道将WndProc消息分派给实例。实例将在构造函数中使用静态调度程序注册自己,并在析构函数中删除它们自己。

当然,只有当您的WndProc处理程序需要调用其他实例成员函数或访问成员变量时,才需要所有注册和取消注册和调度开销。否则你可以让它静止,你已经完成了。

答案 3 :(得分:2)

在CreateWindow期间调用您的窗口过程。您将hWindow传递给DefWindowProc,但hWindow仅在CreateWindow返回后设置 - 因此您将DefWindowProc传递给垃圾窗口句柄。

我没有看到一个很好的方法来做到这一点。您可以通过将WindowProc更改为:

在窗口过程中设置hWindow
LRESULT WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

(添加了hwnd参数),将调用更改为:

return view->WindowProc(hwnd, message, wParam, lParam);

像这样创建窗口:

hWindow = NULL;
hWindow = CreateWindow(..., hInst, this);
if (hWindow == NULL)
    return -1;

(第一个赋值是确保hWindow被初始化;第二个是在调用窗口过程后CreateWindow失败的情况下),并在WindowProc的开头添加它:

if(!this->hWindow)
    this->hWindow = hwnd;

答案 4 :(得分:-2)

逐步调试调试器中的代码。当你到达

    MessageBox(NULL,"DefWindowProc Returned.","Error",0);
    return DefWindowProc(hWindow, message, wParam, lParam);

你会看到错误:hWindow是垃圾。您正在使用未初始化的变量。