在窗口过程包装器中阅读访问违规

Read Access Violation in window procedure wrapper

本文关键字:读访问 窗口 过程 包装      更新时间:2023-10-16

我正在编写Windows API包装库。但是我正在遇到问题:每当我尝试在"包装"窗口过程中使用this指针(从静态版本中调用(时,就会丢弃读取访问违规行为。这是库接口

class widget {
protected:
    HWND hwnd;
public: // instinfo is class that represents WNDCLASS structure
    widget(instinfo ii, const WCHAR *text,
        DWORD styles = WS_OVERLAPPEDWINDOW,
        point ul = DEFUL,
        int cx = CW_USEDEFAULT,
        int cy = CW_USEDEFAULT,
        HWND parent = NULL,
        HMENU hm = NULL);
};
class olwindow : public widget {
    // static window proc: It registered by WNDCLASS structure
    static LRESULT CALLBACK baseWinProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);
    LRESULT CALLBACK winproc(UINT wm, WPARAM wp, LPARAM lp);
protected:
    virtual void LButtonDown(int, int) { } // an example event, is override by classes that inherit and wanna customize
public:
    friend class instinfo;
    olwindow(instinfo ii, const WCHAR *cap, point ul = DEFUL, 
        int cx = CW_USEDEFAULT, int cy = CW_USEDEFAULT);
};

相关功能是baseWinProcwinproc,我在下面显示:

LRESULT olwindow::baseWinProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
    olwindow *w;
    if (wm == WM_NCCREATE)
        SetWindowLong(hwnd, GWL_USERDATA, (LONG) ((CREATESTRUCT *) lp)->lpCreateParams);
    w = (olwindow *) GetWindowLong(hwnd, GWL_USERDATA);
    if (w)
        w->winproc(wm, wp, lp);
    return DefWindowProc(hwnd, wm, wp, lp);
}
LRESULT olwindow::winproc(UINT wm, WPARAM wp, LPARAM lp)
{
    switch (wm) {
        case WM_LBUTTONDOWN:
            // on line below is crash
            this->LButtonDown(GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    }
    return 0;
}

错误消息是

异常抛出:阅读访问违规。

this->是0x12a07e0。

这可能是什么意思?

编辑:这是widget类的构造函数,由olwindow

的构造函数调用
widget::widget(instinfo ii, const WCHAR * text, DWORD styles, point ul, 
         int cx, int cy, HWND parent, HMENU hm)
{
    this->hwnd = CreateWindow(ii.getClassName(), text, styles, 
         ul.x, ul.y, cx, cy, parent, hm, ii.getHinst(), this);
    if (this->hwnd == NULL)
        MessageBox(NULL, getErrMsg(), L"", 0);
    ShowWindow(this->hwnd, SW_SHOW);
}
olwindow::olwindow(instinfo ii, const WCHAR * cap, point ul, int cx, int cy) :
    widget(ii, cap, WS_OVERLAPPEDWINDOW, ul, cx, cy)
{ }

问题是由两个因素组合引起的:

  • 派生类型的建设顺序。
  • 事实,CreateWindow在返回之前将许多消息发送到窗口过程。从文档中:

    返回之前,CreateWindow将WM_Create消息发送到窗口过程。对于重叠,弹出和儿童窗口,CreateWindow将WM_Create,WM_GetMinMaxinfo和WM_NCcreate消息发送到窗口。

由于CreateWindow是在基类(widget(构造函数中执行的,因此当WM_NCCREATE消息处理程序运行时,尚未构造派生类'(olwindow(实例。施放指针通过CREATESTRUCT到派生类的类型是错误的。

要解决这个问题,您需要将指针归还其原始类型,即widget*。如果您需要访问窗口过程中的派生对象,则需要将窗口创建移至已完全构造的点。

作为替代方案,您可以继续从基类构造函数调用CreateWindow,但只有一旦完全构造就可以施放到派生对象,例如通过从派生类构造函数中存储一个布尔值。