简体   繁体   English

从 static function 调用虚拟 function

[英]Calling virtual function from static function

CASE案子

My base class "Control" subclasses WinAPI button control:我的基础 class “控制”子类 WinAPI 按钮控制:

hWndControl = CreateWindowEx
(
      0
    , L"BUTTON"
    , L"Button"
    , WS_VISIBLE | WS_CHILD | BS_OWNERDRAW | WS_EX_TRANSPARENT
    , wndRc.left
    , wndRc.top
    , wndRc.right
    , wndRc.bottom
    , hWndParent
    , 0
    , hInstance
    , 0
);

void* p_this{reinterpret_cast<void*>(this)}; // avoiding C-style cast    
SetWindowSubclass
(
      hWndControl
    , Control::ControlProc
    , 0
    , reinterpret_cast<DWORD_PTR>(p_this)
)

As far as I know this requires me to define the callback as static (which I do).据我所知,这需要我将回调定义为 static(我这样做)。 Here is the callback sample for reference:这是供参考的回调示例:

LRESULT CALLBACK Control::ControlProc
(
      HWND hWnd
    , UINT msg
    , WPARAM wParam
    , LPARAM lParam
    , UINT_PTR uIdSubclass
    , DWORD_PTR dwRefData
)
{
    //  RETRIEVE POINTER TO THIS CLASS OBJECT
    void* p_thisV{reinterpret_cast<void*>(dwRefData)}; // avoiding C-style cast
    Control* const p_this{reinterpret_cast<Control*>(p_thisV)};

    // PROCESS MESSAGES
    switch (msg)
    {
        //  DRAWING
        case MY_DRAWITEM: // custom message forwarding WM_DRAWITEM from main window
        {
            p_this->DrawControl();
        }
        break;

        ...
    }
    return DefSubclassProc(hWnd, msg, wParam, lParam);
}

As far as here everything works fine if I do the drawing in the callback function, or in member function defined in the base class referenced in callback.就这里而言,如果我在回调 function 或在回调中引用的基础 class 中定义的成员 function 中进行绘图,一切正常。

But I plan to inherit this base class for multiple different controls with different looks while using the same callback.但我计划在使用相同回调时为多个具有不同外观的不同控件继承此基础 class。 So I thought I would create virtual functions called in specific points of the callback, which I can override in derived class with custom behavior for each derived class like so:所以我想我会创建在回调的特定点调用的虚拟函数,我可以在派生的 class 中覆盖它,并为每个派生的 class 自定义行为,如下所示:

//  Base class header
class Control
{
    ...
    protected:
    virtual void DrawControl();
    ...
};

//  Derived class header
class CalendarItem : public Control
{
    ...
    protected:
    void DrawControl();
    ...
};

//  Derived class cpp
void CalendarItem::DrawControl()
{
    std::unique_ptr<DrawBg> drawBg = std::unique_ptr<DrawBg>(new DrawBg(Control::hWndControl));
    //  this is the actual drawing mechanism, works, not relevant
}

PROBLEM问题

I get exception in the callback function on line: p_this->DrawControl();我在在线回调 function 中遇到异常: p_this->DrawControl();

Exception text: p_this->**** was 0x75004D.异常文本:p_this->**** 为 0x75004D。

Can you please tell me how to fix this solution to work, or whether something like this is even possible?你能告诉我如何解决这个问题,或者这样的事情是否可能?

ControlProc() expects to receive a Control* pointer, not a CalendarItem* pointer, or any other derived class pointer. ControlProc()期望接收Control*指针,而不是CalendarItem*指针或任何其他派生的 class 指针。 So, when casting this , you need to convert it to a valid Control* pointer first , then cast that as-is to DWORD_PTR for SetWindowSubclass() , and then in ControlProc() you can cast the DWORD_PTR back to Control* directly.因此,在转换this时,您需要先将其转换为有效的Control*指针,然后将其转换为SetWindowSubclass()DWORD_PTR ,然后在ControlProc()中,您可以直接将DWORD_PTRControl* You don't need to, and should not, cast to an intermediate void* at all.您根本不需要也不应该强制转换为中间void*

Control* p_this = this; // implicit conversion, no explicit cast needed
SetWindowSubclass
(
    hWndControl,
    Control::ControlProc,
    0,
    reinterpret_cast<DWORD_PTR>(p_this)
);
LRESULT CALLBACK Control::ControlProc
(
    HWND hWnd,
    UINT msg,
    WPARAM wParam,
    LPARAM lParam,
    UINT_PTR uIdSubclass,
    DWORD_PTR dwRefData
)
{
    // RETRIEVE POINTER TO THIS CLASS OBJECT
    Control* const p_this = reinterpret_cast<Control*>(dwRefData);
    ...
    return DefSubclassProc(hWnd, msg, wParam, lParam);
}

The correct solution was hinted at by RbMm - this pointer is on stack, we need heap pointer so that it stays in memory when the callback function runs (so current running function is already finished). RbMm 暗示了正确的解决方案 - this指针在堆栈上,我们需要堆指针,以便在回调 function 运行时它留在 memory 中(因此当前运行的 ZC1C425268E68385D1AB5074C1 已经完成)。

The correct solution:正确的解决方案:

Creating derived class object:创建派生的 class object:

// provide base class pointer stored on heap
Derived* der = new Derived;
der->CreateInDerived(&(*der), ...); // "&(*der)" gets base class ptr from derived ptr

Derived class function:派生 class function:

void Derived::CreateInDerived(Base* ptr, ...)
{
    ptr->CreateInBase(ptr, ...);
}

Base class function:底座 class function:

void Base::CreateInBase(Base* ptr, ...)
{
    ...
    SetWindowSubclass
    (
          hWndControl
        , Control::ControlProc
        , 0
        , reinterpret_cast<DWORD_PTR>(ptr)
    )
    ...
}

Explanation:解释:

The problem was not at all in the clash of virtual and static function, but in life length of transferred pointer.问题根本不在于 virtual 和 static function 的冲突,而在于传输指针的寿命。 The pointer address was turned into a number in the function A, so that it can be given to callback function B through DWORD parameter.指针地址在function A中变成了一个数字,这样就可以通过DWORD参数给回调function B。

When function B attempted to retrieve the pointer address back from the number, pointer defined in function A was alrady out of scope (and probably overwritten, since function A finished and released the memory). When function B attempted to retrieve the pointer address back from the number, pointer defined in function A was alrady out of scope (and probably overwritten, since function A finished and released the memory).

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM