窗口过程作为类内的虚函数。 [英] Window Procedures as Virtual Function inside classes.

查看:69
本文介绍了窗口过程作为类内的虚函数。的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关于

我正在尝试使用 MFC > Win32 API 。我打算将它包装到中。我的第一种方法没有成功。



整个代码

About
I am trying to make an alternative to the MFC, using the Win32 API. Which is I am going to wrap it into classes. My first approach was not succeeded.

The Whole Code

#include <Windows.h>
#include <tchar.h>

/** BASE WINDOW CLASS | AS A TEMPLATE **/
class CWnd
{
public:
	virtual void Init(HINSTANCE hInstance)	//Registers, Creates, Shows the Window
	{
		HWND         hwnd ;
		WNDCLASS     wndclass ;

		wndclass.style         = 0 ;
		wndclass.lpfnWndProc   = WndProc ;
		wndclass.cbClsExtra    = 0 ;
		wndclass.cbWndExtra    = 0 ;
		wndclass.hInstance     = hInstance ;
		wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
		wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
		wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
		wndclass.lpszMenuName  = NULL ;
		wndclass.lpszClassName = L"CWndMy32" ;

		RegisterClass (&wndclass);
     
		hwnd = CreateWindow (L"CWndMy32", TEXT ("The Hello Program"), 
                                      WS_OVERLAPPEDWINDOW,
			              CW_USEDEFAULT,CW_USEDEFAULT,
                                      CW_USEDEFAULT,CW_USEDEFAULT,
				     NULL,NULL,hInstance,
                                      (LPVOID)this) ; 
    //attach a pointer of the 
        //  class (Cwnd *) to the			                                            //window using the last parameter
                     // of CreateWindow

		ShowWindow (hwnd, SW_SHOWNORMAL) ;

		UpdateWindow (hwnd) ;
	}

protected:
	HINSTANCE hInst;
	HWND m_hwnd;
private:
	//Window Procedure for all of the windows (derived classes like the one below) created by this TEMPLATE CLASS
	static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
	{
		CWnd * _this = (CWnd *) GetWindowLong(hwnd, GWL_USERDATA);
		if(msg == WM_NCCREATE)
		{
			_this = new CWnd;
			_this->hInst = GetModuleHandle(0);
			_this->m_hwnd = hwnd;
			SetWindowLong(hwnd, GWL_USERDATA, (LONG) _this);
			return TRUE;
		}

		return _this->VirtualProc(hwnd, msg, wParam, lParam);
	}

	//Virtual Procedure Function to override in derived classes
	virtual LRESULT VirtualProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM        
        lParam)
	{
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}
};

/** A CLASS FOR USING THE TEMPLATE CLASS | WHICH IS CWnd **/
class CMyWnd : public CWnd
{
private:
	//This virtual function Overides the 'VirtualProc' function of the 'CWnd' Class
	LRESULT VirtualProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
	{
		if(msg == WM_LBUTTONDOWN)
			MessageBox(0, 0, 0, 0);	//To Test if it words, if so, an empty message box will appear when the window is clicked
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}
};

/** Windows Entry **/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nCmdShow)
{
	CMyWnd wnd;				//Object of the Template Used Class | CMyWnd : public CWnd
	wnd.Init(hInstance);

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

	return msg.wParam;
}





我做了什么

1.创建了一个 Base Class Called'' CWnd ''(我还称之为 TEMPLATE CLASS

2.在 CWnd :: Init ;注册,创建并显示窗口。

(在CreateWindow调用中,我附加了一个指针 CWnd

窗口,因此 CWnd 的每个派生类都有不同的数据,

但是相同的静态窗口程序)

3. CWnd :: WndProc 静态窗口程序。

4. CWnd :: VirtualProc 虚拟窗口过程,它被覆盖在派生

班。

5.使用 TEMPLATE CLASS 创建一个派生类,称为'' CMyWnd ''。

6. CMyWnd :: VirtualProc 是覆盖的功能CWnd :: VirtualProc



问题

代码可以编译而没有错误,窗口创建得很好。但问题是, CWnd :: VirtualProc 虚拟函数未正确覆盖

CMyWnd :: VirtualProc



任何帮助?请?



What I Did
1. Created a Base Class Called ''CWnd'' (also I call it the TEMPLATE CLASS)
2. In CWnd::Init ;Registered, Created and Showed the window.
(In the CreateWindow call, I Attached a pointer of the CWnd to
the window, So that every derived class of CWnd will have different data,
but a same static window procedure )
3. CWnd::WndProc is the Static window procedure.
4. CWnd::VirtualProc is the Virtual window procedure which is overridden in derived
classes.
5. Created a Derived class using TEMPLATE CLASS, called ''CMyWnd''.
6. CMyWnd::VirtualProc is the function that overrode CWnd::VirtualProc.

The Problem
Code can be compiled with no errors, and the window is created nicely. But the problem is that, CWnd::VirtualProc virtual function is not correctly overrode with
CMyWnd::VirtualProc .

Any Help ?? Please ?

推荐答案

看起来您的WM_NCCREATE处理程序正在创建父类的新实例,该实例用于所有后续消息。
It looks like your WM_NCCREATE handler is creating a new instance of the parent class, which is used for all subsequent messages.


您的问题是静态CWnd :: WndProc()中的以下行:

Your problem is the following line in your static CWnd::WndProc():
_this = new CWnd;



请参阅下面的更正代码...还有其他一些小问题。

为了避免这样的错误, CWnd类摘要。这种方式直接创建CWnd会导致编译错误。你可以这样做,例如为它创建一个具有受保护可见性的空构造函数!



我觉得奇怪的是你的虚函数在基类中是私有的,你应该把它至少保护。在派生类中,使用virtual标记重写方法是一个很好的做法,即使它不是必需的,因为它对于阅读代码的人来说是一个很好的文档,而visual C ++有一个非常好的覆盖关键字,你可以在之后编写函数声明(就地,或'const''关键字之后)如果函数实际上没有覆盖基类方法,它会导致编译失败(例如,因为有人在更改基类方法签名期间重构...)。所以msvc中的override关键字也是测试问题的好方法!



关于你的代码的一些其他注释:

通过WM_NCCREATE挂钩是非常好的,尽管我很少看到它,这是挂钩的正确方法。在你的实现中,WM_NCCREATE没有传递给虚拟wndproc,但这是一个实现细节,类中很少需要WM_NCCREATE,通常WM_CREATE就足够了。如果你考虑使用64位,那么你应该使用SetWindowLongPtr和GWLP_USERDATA而不是SetWindowLong + GWL_USERDATA。虚拟wndproc通常在窗口消息上包含一个大开关,我通常喜欢这样写:


See the corrected code below... And there are some other minor issues too.
To avoid such bugs make the CWnd class abstract. This way creating CWnd directly causes compile error. You can do this for example by creating an empty constructor for it with protected visibility!

Something that seems strange to me is that your virtual function is private in the base class, you should put it to at least protected. In the derived class its a good practice to mark the overridden method with virtual even if its not required because its a good documentation for people who read the code, and visual C++ has a very nice ''override'' keyword that you can write after the function declaration (in place, or after the ''const'' keyword) and it causes the compilation to fail if the function doesn''t actually override a base class method (for example because someone has changed the base class method signature during refactor...). So the override keyword in msvc is a nice way to test for your problem as well!

A few other notes regarding your code:
The hooking via WM_NCCREATE is very nice, it is the correct way to do the hooking despite the fact that I see it rarely. In your implementation WM_NCCREATE isn''t passed to the virtual wndproc but that is an implementation detail, WM_NCCREATE is rarely needed inside the class, usually WM_CREATE is enough. If you consider going to 64 bit then you should use SetWindowLongPtr with GWLP_USERDATA instead of SetWindowLong+GWL_USERDATA. The Virtual wndproc usually contains a big switch on the window message and I usually like writing it this way:

//-------- In the derived class:
    struct SWndMsg
    {
        HWND hwnd;     // hwnd is optional because the m_hwnd member is always available
        UINT msg;
        WPARAM wParam;
        LPARAM lParam;
    }

    static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
        if(msg == WM_NCCREATE)
        {
            CWnd * _this = (CWnd*)((CREATESTRUCT*)lParam)->lpCreateParams; // !!!!!!!!!!! your bug
            _this->hInst = GetModuleHandle(0);
            _this->m_hwnd = hwnd;
            SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)_this);
        }

        CWnd * _this = (CWnd *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
        SWndMsg m;
        m.hwnd = hwnd;
        m.msg = msg;
        m.wParam = wParam;
        m.lParam = lParam;
        return _this->VirtualProc(m);
    }

    // A method in the base class that you can use with the SWndMsg struct
    // instead of DefWindowProc()
    LRESULT Default(SWndMsg& msg)
    {
        return DefWindowProc(msg.hwnd, msg.msg, msg.wParam, msg.lParam);
    }

//-------- In the derived class:
    // Here comes the 'override' keyword...
    virtual LRESULT VirtualProc(SWndMsg& msg) override
    {
        switch (msg)
        {
        case WM_LBUTTONDOWN:
            // Its easier to split the code into multiple functions if you
            // pass just 1 parameter instead of 4 and it looks nicer...
            return OnWMLButtonDown(msg);
        case WM_COMMAND:
            return OnWMCommand(msg);
        ...
        default:
            return Default(msg);
        }
    }

    void OnWMLButtonDown(SWndMsg& msg)
    {
        if (blahblah)
        {
            ...
        }
        else
        {
            // The Default() method can come handy in a lot of message specific methods...
            return Default(msg);
        }
    }



代码可能包含错误,因为我在浏览器中写了它并没有编译它!


The code might contain bugs as I wrote it in the browser and havent compiled it!


请参阅下面的教程!不错的一个!

http://www.infernodevelopment.com/cn -win32-api-simple-gui-wrapper [ ^ ]
Just take the Tutorial below ! Nice One !
http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper[^]


这篇关于窗口过程作为类内的虚函数。的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆