如果您查看CONTROL.CPP,将看到CONTROL :: CONTROLNATIVEWINDOW :: WndProc()。在这里,为了进行测试,我正在输出CONTROL名称。我有CONTROL的多个派生类,这些类重写了GetName()方法。但是,它打印的唯一派生类是WINDOW,即使它也应该输出TEXTBOX。

我无法确定的是,如果LRESULT CALLBACK InternalWinProc()中的reinterpret_cast错误地插入:

LRESULT CALLBACK InternalWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    NativeWindow* window = reinterpret_cast<NativeWindow*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    if (window) {
        Message msg = Message::Create(hWnd, message, IntPtr((void*)wParam), IntPtr((void*)lParam));
        window->WndProc(&msg);
    }

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


或者,线

window = new ControlNativeWindow(this);


在基类中CONTROL构造函数不能正确保存派生类型。

在调试器中运行并查看上面的行时,这将有一个vptr,将所有指针指向CONTROL :: ...方法,而不是各自的派生类。这使我认为您不能像我正在做的那样将派生类的this指针保存在基类中。

然后当我将其强制转换回InternalWndProc函数中的NativeWindow *时,它给出了错误的转换,因此当行

control->GetName();


在执行

Control::ControlNativeWindow::WndProc(Message * msg)


错误调用的方法

Window::GetName();




这是其余的代码:

控制

#ifndef CONTROL_H
#define CONTROL_H

#include "IntPtr.h"
#include "CreateParams.h"
#include "Point.h"
#include "Size.h"

#include "NativeWindow.h"

class Control
{
public:
    Control();

    ~Control();

    virtual void CreateControl();

    Control* GetParent() { return parent; }
    void SetParent(Control* parent) { this->parent = parent; }

    IntPtr GetHandle() { return window->GetHandle(); }

    Point GetLocation() { return Point(x, y); }
    void SetLocation(Point point);

    Size GetSize() { return Size(width, height); }
    void SetSize(Size size);

    std::string GetText();
    void SetText(std::string text);

    void Show();

    bool IsHandledCreated();

    class ControlNativeWindow : public NativeWindow
    {
    public:
        ControlNativeWindow(Control* control);
        virtual std::string GetName() { return "CONTROLNATIVEWindow\n"; }
    protected:
        virtual void WndProc(Message* msg);

    private:
        Control* control;
    };

    virtual std::string GetName() { return "Control\n"; }

protected:

    virtual void OnPaint();
    virtual void OnTextChanged();

    virtual void DefWndProc(Message* msg);
    virtual void WndProc(Message* msg);

    virtual void CreateHandle();

    virtual CreateParams GetCreateParams();

private:

    Control* parent;

    ControlNativeWindow* window;

    int x;
    int y;
    int width;
    int height;

    std::string text;

    bool visible;
};

#endif // CONTROL_H


控制面板

#include "Control.h"
#include "Utility.h"
#include <string>

#include "Message.h"

using namespace std;

Control::Control()
    : x(0), y(0),
    width(200), height(20), // fix this
    visible(false)
{
    window = new ControlNativeWindow(this);
}

Control::~Control()
{
}

void Control::CreateControl()
{
    CreateHandle();
}

void Control::SetLocation(Point point)
{
    x = point.X;
    y = point.Y;
}

void Control::SetSize(Size size)
{
    width = size.Width;
    height = size.Height;
}

std::string Control::GetText()
{
    if (IsHandledCreated())
    {
        HWND hWnd = (HWND)GetHandle().ToPointer();
        int len = GetWindowTextLength(hWnd);

        wchar_t* str = new wchar_t[len + 1]; // fix
        GetWindowText(hWnd, str, len);

        return string(WStringToAnsi(str));
    }

    return text;
}

void Control::SetText(string text)
{
    if (IsHandledCreated())
    {
        wstring str = AnsiToWString(text);
        SetWindowText((HWND)GetHandle().ToPointer(), str.c_str());
    }

    this->text = text;
}

void Control::Show()
{
    visible = true;
}

bool Control::IsHandledCreated()
{
    return window->GetHandle() == IntPtr::Zero;
}

void Control::OnPaint()
{
}

void Control::OnTextChanged()
{
}

void Control::DefWndProc(Message * msg)
{
    window->DefWndProc(msg);
}

void Control::WndProc(Message * msg)
{
    switch (msg->Msg)
    {
    case WM_PAINT:
        OnPaint();
        break;

    case WM_DESTROY:
        PostQuitMessage(0);

    default:
        DefWndProc(msg);
    };
}

void Control::CreateHandle()
{
    CreateParams cp = GetCreateParams();

    SetLocation(Point(cp.GetX(), cp.GetY()));

    window->CreateHandle(&cp);
}

CreateParams Control::GetCreateParams()
{
    CreateParams cp;
    cp.SetParent(parent->GetHandle());
    cp.SetCaption(text);
    return cp;
}

Control::ControlNativeWindow::ControlNativeWindow(Control* control)
{
    wstring ws = AnsiToWString(control->GetName());
    OutputDebugString(ws.c_str());
    this->control = static_cast<Control*>(control);
    ws = AnsiToWString(this->control->GetName());
    OutputDebugString(ws.c_str());
}

void Control::ControlNativeWindow::WndProc(Message * msg)
{
    // HERE IS THE ISSUE
    // IT IS OUTPUTTING WINDOW ALWAYS
    // HOWEVER, IT SHOULD OUTPUT THE CORRECT DERIVED CLASS
    wstring ws = AnsiToWString(control->GetName());
    OutputDebugString(ws.c_str());
    control->WndProc(msg);
}


本地窗口

#ifndef NATIVE_WINDOW_H
#define NATIVE_WINDOW_H

#include <string>
#include "IntPtr.h"

class CreateParams;
class Message;

class NativeWindow
{
public:
    NativeWindow();
    ~NativeWindow();

    virtual void CreateHandle(CreateParams* cp);

    IntPtr GetHandle() { return handle; }

    virtual void DefWndProc(Message* msg);
    virtual void WndProc(Message* msg);

    virtual std::string GetName() { return "NATIVEWindow\n"; }

private:
    IntPtr handle;

    class WindowClass
    {
    public:
        WindowClass(std::string className, int classStyle);

        std::string GetClsName() { return className; }
        int GetClassStyle() { return classStyle; }

    private:
        void registerClass();

        friend NativeWindow;
        NativeWindow* targetWindow;

        std::string className;
        int classStyle;
    };
};

#endif // NATIVE_WINDOW_H


本地窗口

#include "NativeWindow.h"
#include <Windows.h>
#include <string>
#include "CreateParams.h"
#include "Message.h"
#include "Utility.h"

using namespace std;

LRESULT CALLBACK InternalWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

NativeWindow::NativeWindow() : handle(IntPtr::Zero)
{
}

NativeWindow::~NativeWindow()
{
}

void NativeWindow::CreateHandle(CreateParams* cp)
{
    WindowClass windowClass(cp->GetClsName(), cp->GetClassStyle());

    wstring wideClassName = AnsiToWString(windowClass.GetClsName());
    wstring wideCaption = AnsiToWString(cp->GetCaption());

    HWND hWnd = CreateWindowEx(
        cp->GetExStyle(),
        wideClassName.c_str(),
        wideCaption.c_str(),
        cp->GetStyle(),
        cp->GetX(), cp->GetY(),
        cp->GetWidth(), cp->GetHeight(),
        (HWND)cp->GetParent().ToPointer(),
        nullptr,
        nullptr,
        nullptr
    );

    if (!hWnd)
    {
        MessageBox(nullptr, L"Call to CreateWindow Failed", L"FAIL", MB_OK);
        return;
    }

    handle = hWnd;
    windowClass.targetWindow = this;

    SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)this);
}

void NativeWindow::WndProc(Message* msg)
{
    DefWndProc(msg);
}

void NativeWindow::DefWndProc(Message * msg)
{
    DefWindowProc((HWND)msg->HWnd.ToPointer(), (LRESULT)msg->Result.ToPointer(), (WPARAM)msg->WParam.ToPointer(), (LPARAM)msg->LParam.ToPointer());
}

NativeWindow::WindowClass::WindowClass(std::string className, int classStyle)
{
    this->className = className;
    this->classStyle = classStyle;
    registerClass();
}

void NativeWindow::WindowClass::registerClass()
{
    WNDCLASSEX wndclass;
    wndclass.cbSize = sizeof(WNDCLASSEX);
    wndclass.style = classStyle;
    wndclass.lpfnWndProc = InternalWndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = nullptr;
    wndclass.hIcon = LoadIcon(nullptr, MAKEINTRESOURCE(IDI_APPLICATION));
    wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wndclass.lpszMenuName = nullptr;
    wstring ws = AnsiToWString(className);
    wndclass.lpszClassName = ws.c_str();
    wndclass.hIconSm = LoadIcon(nullptr, MAKEINTRESOURCE(IDI_APPLICATION));

    WNDCLASSEX wcex;
    wcex.lpszClassName = ws.c_str();

    bool found = GetClassInfoEx(nullptr, ws.c_str(), &wcex);
    if (found)
        return;

    if (!RegisterClassEx(&wndclass))
    {
        DWORD dw = GetLastError();

        if (dw == ERROR_CLASS_ALREADY_EXISTS)
        {
            MessageBox(nullptr, L"Class already exists", L"SUCCESS", MB_OK);
        }
        else
        {
            MessageBox(nullptr, L"Call to RegisterClassEx Failed", L"FAIL", MB_OK);
        }
    }
}

LRESULT CALLBACK InternalWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    NativeWindow* window = reinterpret_cast<NativeWindow*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
    if (window) {
        Message msg = Message::Create(hWnd, message, IntPtr((void*)wParam), IntPtr((void*)lParam));
        window->WndProc(&msg);
    }

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


窗口

#ifndef WINDOW_H
#define WINDOW_H

#include <Windows.h>
#include "Control.h"
#include "ControlCollection.h"

class Window : public Control
{
public:
    Window();
    ~Window();

    void Show();

    virtual std::string GetName() { return "Window\n"; }

protected:
    virtual CreateParams GetCreateParams();

private:
    ControlCollection controls;
};

#endif // WINDOW_H


文本框

#ifndef TEXTBOX_H
#define TEXTBOX_H

#include "Control.h"

class TextBox : public Control
{
public:
    TextBox();
    ~TextBox();

    virtual std::string GetName() { return "TextBox\n"; }

protected:

    virtual void WndProc(Message* msg);

    virtual void OnTextChanged();

    virtual CreateParams GetCreateParams();

private:
    void reflectCommand(Message* msg);
};

#endif // TEXTBOX_H

最佳答案

直到基类的构造函数完成后,C ++对象才成为派生类的成员。简而言之,直到控件构造函数退出后,该对象才是文本框类型。

考虑带有vtable的对象:

class A { public: A() x() { doit(); } virtual void doit(); private: int x; }


和派生类:

class B { public: virtual void doit(); private: std::string myname; }


进入A的构造函数主体时,B对象如下所示:

+--------------------+
| A vtable           | // A vtable
+--------------------+
| int x              | // A's member (0 from initializer list)
+--------------------+
| std::string myname | // B's member (uninit)
+--------------------+


请注意,如果执行B :: doit(),它将访问未初始化的myname。

B的构造函数将把vtable指针重新分配给B vtable并运行myname的构造函数,但这是在我们已经执行了A构造函数主体之后。 Java执行此操作的方式与其他方法不同,因为反射要求对象在运行时不要更改类型。

因此,调用对象的虚拟方法之一还没有引用派生类型的重写。

通常,对象具有某种init方法,构造后要求用户使用该方法以允许派生的类参与初始化。

07-28 01:05
查看更多