You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Win32 API C++面向对象封装触发0xC0000005访问冲突问题排查

问题描述

正在学习Win32 API,同时学习C++并尝试对Win32 API做面向对象封装。实现了派生自BaseWindow基类的MainWindow类,项目包含可触发MessageBox的菜单,以及一个可创建DialogBox的菜单选项。

点击创建DialogBox的选项时程序崩溃,抛出的异常信息为:

LearningWin32.exe中0x00000001处抛出异常: 0xC0000005: 执行位置0x00000001时发生访问冲突。

该异常在BaseWindow.cpp的如下代码位置触发:

if (pBaseWindow)
    {
        OutputDebugString(L"BASE WINDOW pBASEWINDOW\n");

        pBaseWindow->m_hwnd = hwnd;
        lResult = pBaseWindow->HandleMessage(uMsg, wParam, lParam); // --> ExceptionThrown
    }

不确定问题是出在对消息流机制的理解上,还是C++基础掌握不足,或是两者都有,以下是目前的实现代码:


现有实现代码

Main.cpp

#ifndef UNICODE
#define UNICODE
#endif 

#include "MainWindow.h"


int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    MainWindow win(hInstance, L"MainWindow", nCmdShow);

    if (!win.Create(L"Notepad --", WS_OVERLAPPEDWINDOW, 0, 1024, 720))
    {
        return 0;
    }

    // 运行消息循环
    MSG msg = { };
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return 0;
}

BaseWindow.h

#pragma once

#include <Windows.h>


class BaseWindow
{

public:
    BaseWindow();

    ~BaseWindow();

    HWND Window() const;

    static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

protected:

    virtual PCWSTR  GetClassName() const = 0;
    virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;

    HWND m_hwnd;
};

BaseWindow.cpp

#include "BaseWindow.h"

BaseWindow::BaseWindow() : m_hwnd(NULL) { }

BaseWindow::~BaseWindow() { DestroyWindow(m_hwnd); }

HWND BaseWindow::Window() const { return m_hwnd; }

LRESULT CALLBACK BaseWindow::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    OutputDebugString(L"BASE WINDOW WindowProc\n");

    if (uMsg == WM_NCCREATE)
    {
        OutputDebugString(L"BASE WINDOW WindowProc -> WM_NCCREATE\n");

        CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
        BaseWindow* pBaseWindow = (BaseWindow*)pCreate->lpCreateParams;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pBaseWindow);
    }

    BaseWindow* pBaseWindow = reinterpret_cast<BaseWindow*>(GetWindowLongW(hwnd, GWL_USERDATA));

    LRESULT lResult;

    if (pBaseWindow)
    {
        OutputDebugString(L"BASE WINDOW pBASEWINDOW\n");

        pBaseWindow->m_hwnd = hwnd;
        lResult = pBaseWindow->HandleMessage(uMsg, wParam, lParam);
    }
    else
    {
        OutputDebugString(L"BASE WINDOW DefWindowProc\n");

        lResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return lResult;
}

MainWindow.h

#pragma once

#include "BaseWindow.h"


class MainWindow : public BaseWindow
{
private:
    HINSTANCE m_hInstance;
    LPCWSTR m_className;
    int m_nCmdShow;

public:
    MainWindow(HINSTANCE hInstance, LPCWSTR ClassName, int nCmdShow);

    //~MainWindow();

    BOOL Create(
        PCWSTR lpWindowName,
        DWORD dwStyle,
        DWORD dwExStyle = 0,
        int nWidth = CW_USEDEFAULT,
        int nHeight = CW_USEDEFAULT,
        int x = CW_USEDEFAULT,
        int y = CW_USEDEFAULT,
        HWND hWndParent = 0,
        HMENU hMenu = 0);

    virtual PCWSTR  GetClassName() const;

    virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
};

MainWindow.cpp

#include "MainWindow.h"
#include "resource/resource.h"

#include <string>

MainWindow::MainWindow(HINSTANCE hInstance, LPCWSTR ClassName, int nCmdShow)
    : m_hInstance(hInstance), m_className(ClassName), m_nCmdShow(nCmdShow)
{
    OutputDebugString(L"MAIN WINDOW CONSTRUCTOR\n");
}

PCWSTR  MainWindow::GetClassName() const { return m_className; }

BOOL MainWindow::Create(
    PCWSTR lpWindowName,
    DWORD dwStyle,
    DWORD dwExStyle,
    int nWidth,
    int nHeight,
    int x,
    int y,
    HWND hWndParent,
    HMENU hMenu)
{
    OutputDebugString(L"MAIN WINDOW CREATE\n");

    WNDCLASS wc = { 0 };

    wc.lpfnWndProc = BaseWindow::WindowProc;
    wc.hInstance = m_hInstance;
    wc.lpszClassName = GetClassName();
    wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
    wc.hIcon = LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_ICON1));

    if (RegisterClass(&wc))
    {
        OutputDebugString(L"MAIN WINDOW CREATE CLASS REGISTERED\n");
    }
    else
    {
        OutputDebugString(L"MAIN WINDOW CREATE CLASS UNABLE TO REGISTER\n");
    }

    m_hwnd = CreateWindowEx(
        dwExStyle,
        GetClassName(),
        lpWindowName,
        dwStyle,
        x, y,
        nWidth, nHeight,
        hWndParent,
        hMenu,
        m_hInstance,
        this);

    if (!m_hwnd)
    {
        OutputDebugString(L"--------- Window Creation Failed ---------\n");
        ExitProcess(0);
    }

    // 窗口居中
    RECT rc;
    GetWindowRect(m_hwnd, &rc);
    int xPos = (GetSystemMetrics(SM_CXSCREEN) - rc.right) / 2;
    int yPos = (GetSystemMetrics(SM_CYSCREEN) - rc.bottom) / 2;

    SetWindowPos(m_hwnd, HWND_TOP, xPos, yPos, 0, 0, SWP_NOZORDER | SWP_NOSIZE);

    ShowWindow(m_hwnd, m_nCmdShow);
    UpdateWindow(m_hwnd);

    return (m_hwnd ? TRUE : FALSE);
}

LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    OutputDebugString(L"MAIN WINDOW HANDLEMESSAGE\n");

    switch (uMsg)
    {
    case WM_COMMAND: // 菜单选项处理
    {
        switch (LOWORD(wParam))
        {
        case ID_FILE_NEW:
            MessageBox(m_hwnd, L"New file", L"Menu option New", MB_OK | MB_ICONASTERISK);
            break;

        case ID_FILE_OPEN:
            MessageBox(m_hwnd, L"Menu option Open", L"Open", MB_OK | MB_ICONASTERISK);
            break;

        case ID_FILE_EXIT:
        {
            int msgBoxID = MessageBox(m_hwnd, L"Are you sure you want to exit?", L"Exit", MB_OKCANCEL | MB_ICONHAND);
            if (msgBoxID == IDOK) PostMessage(m_hwnd, WM_CLOSE, 0, 0);
        }
        break;

        case ID_HELP_SHOWDIALOGBOX:
        {
            OutputDebugString(L"MAIN WINDOW SHOWDIALOGBOX\n");

            int ret = DialogBox(
                m_hInstance,
                MAKEINTRESOURCE(IDD_DIALOG1),
                m_hwnd,
                (DLGPROC)AboutDlgProc(m_hwnd, uMsg, wParam, lParam));

            if (ret == IDOK) {
                MessageBox(m_hwnd, L"Dialog exited with IDOK.", L"Notice",
                    MB_OK | MB_ICONINFORMATION);
            }
            else if (ret == IDCANCEL) {
                MessageBox(m_hwnd, L"Dialog exited with IDCANCEL.", L"Notice",
                    MB_OK | MB_ICONINFORMATION);
            }
            else if (ret == -1) {
                MessageBox(m_hwnd, L"Dialog failed!", L"Error",
                    MB_OK | MB_ICONINFORMATION);
            }
        }
        break;
        }
        break;
    }

    case WM_CLOSE:
        OutputDebugString(L"MAIN WINDOW HANDLEMESSAGE -> WM_CLOSE\n");
        DestroyWindow(m_hwnd);
        return 0;

    case WM_DESTROY:
        OutputDebugString(L"MAIN WINDOW HANDLEMESSAGE -> WM_DESTROY\n");
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(m_hwnd, &ps);
        FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
        EndPaint(m_hwnd, &ps);
    }
    return 0;

    default:
        OutputDebugString(L"MAIN WINDOW HANDLEMESSAGE -> DefWindowProc\n");
        return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
    }
    return TRUE;
}

BOOL CALLBACK MainWindow::AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
    OutputDebugString(L"MAIN WINDOW AboutDlgProc\n");

    switch (Message)
    {
    case WM_INITDIALOG:
        // 对话框显示前的初始化操作
        return TRUE;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDOK:
            EndDialog(hwnd, IDOK);
            break;
        case IDCANCEL:
            EndDialog(hwnd, IDCANCEL);
            break;
        }
        break;

    default:
        return FALSE;
    }
    return TRUE;
}

解决方案

错误原因

  1. 类的非静态成员函数默认携带隐含的this指针参数,无法匹配Win32 API要求的回调函数签名,会导致调用约定不匹配、参数错位。
  2. 原代码调用DialogBox时直接执行了AboutDlgProc函数,把返回的BOOL值强制转换为函数指针,指向的是非法内存地址,直接触发访问冲突。

修改步骤

  1. 修改MainWindow.h中的AboutDlgProc声明,添加static修饰,去除隐含的this指针:
    修改前:
BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);

修改后:

static BOOL CALLBACK AboutDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam);
  1. 修改MainWindow.cpp中DialogBox的调用逻辑,直接传入回调函数的指针,而非函数执行的返回值:
    修改前:
int ret = DialogBox(
                m_hInstance,
                MAKEINTRESOURCE(IDD_DIALOG1),
                m_hwnd,
                (DLGPROC)AboutDlgProc(m_hwnd, uMsg, wParam, lParam));

修改后:

int ret = DialogBox(
                m_hInstance,
                MAKEINTRESOURCE(IDD_DIALOG1),
                m_hwnd,
                (DLGPROC)AboutDlgProc);

内容的提问来源于stack exchange,提问作者Ander-RL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 12:39:04