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; }
解决方案
错误原因
- 类的非静态成员函数默认携带隐含的
this指针参数,无法匹配Win32 API要求的回调函数签名,会导致调用约定不匹配、参数错位。 - 原代码调用
DialogBox时直接执行了AboutDlgProc函数,把返回的BOOL值强制转换为函数指针,指向的是非法内存地址,直接触发访问冲突。
修改步骤
- 修改
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);
- 修改
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
相关产品推荐
相关产品推荐

