c++ - 调用虚函数时崩溃

标签 c++ windows debugging winapi crash

好吧,这是一个非常奇怪的问题。首先,我想说我不是 C++ 的初学者,当然也不是高级的。我在中间的某个地方。我想要做的是制作 Win32 API 的 C++ OOP 包装器库 (dll)。这是我的图书馆的类(class)。我使用命令用 Mingw 编译了它:

g++ -shared -o bin\win32oop.dll src\Application.cpp src\Form\Form.cpp -Wall

源\应用程序.h:

#ifndef WOOP_APPLICATION_H_
#define WOOP_APPLICATION_H_

namespace Woop
{
 class Application
 {
 public:
  bool Init(void);
  virtual bool OnInit(void);
 };
}

#endif // WOOP_APPLICATION_H_

源\应用程序.cpp

#include <windows.h>
#include "Application.h"
#include "Form\Form.h"

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

namespace Woop
{
 bool Application::Init(void)
 {
  WNDCLASSEX wc;

  wc.cbSize        = sizeof(WNDCLASSEX);
  wc.style         = 0;
  wc.lpfnWndProc   = WndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = GetModuleHandle(NULL);
  wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName  = NULL;
  wc.lpszClassName = "woop";
  wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

  if(RegisterClassEx(&wc) == 0)
  {
   MessageBox(NULL, "Window Registration Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
   return false;
  }

  this->OnInit();

  return true;
 }

 bool Application::OnInit(void)
 {
  return true;
 }
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    Woop::Form *wnd = 0;

    if (uMsg == WM_NCCREATE) 
 {   
        SetWindowLong (hwnd, GWL_USERDATA, long((LPCREATESTRUCT(lParam))->lpCreateParams));
    }

 wnd = (Woop::Form *)(GetWindowLong (hwnd, GWL_USERDATA));

    if (wnd) return wnd->WndProc(hwnd, uMsg, wParam, lParam);

    return ::DefWindowProc (hwnd, uMsg, wParam, lParam);
}

源\窗体\窗体.h

#ifndef WOOP_FORM_FORM_H_
#define WOOP_FORM_FORM_H_

namespace Woop
{
 class Form
 {
 public:
  bool Show(void);
  virtual LRESULT WndProc(HWND, UINT, WPARAM, LPARAM);
 protected:
  HWND _handle;
 };
}

#endif // WOOP_FORM_FORM_H_

源\窗体\窗体.cpp

#include <windows.h>
#include "Form.h"

namespace Woop
{
 bool Form::Show(void)
 {
  _handle = CreateWindowEx(WS_EX_CLIENTEDGE, "woop", "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, GetModuleHandle(NULL), this);

  if(_handle == NULL)
  {
   MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
   return false;
  }

  ShowWindow(_handle, SW_SHOWNORMAL);

  return true;
 }

 LRESULT Form::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
  switch(uMsg)
  {
   case WM_DESTROY:
    PostQuitMessage(0);
   break;            
  }
  return DefWindowProc(hwnd, uMsg, wParam, lParam);
 }
}

这是我用来测试库的程序:

class SampleApp : public Woop::Application
{
 bool OnInit(void)
        {
         Form form;
         form.Show();

         return true;
        }
};

INT APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, INT)
{
 SampleApp application;
 if(application.Init() == false) return 0;

 MSG Msg;
 while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }

 return 0;
}

好了,现在是问题。你在 Form 类中看到那个虚拟窗口过程了吗?如果我从声明中删除虚拟,程序编译并运行良好。但是当我把它加回去时,它崩溃了。出现臭名昭著的“不要发送”对话框。我不确定它什么时候崩溃,我会尝试使用 MessageBox() 来解决这个问题(哈哈,这是我没有学习如何使用 gdb 进行调试的结果)。我试图做到这一点,以便我可以创建一个类,例如 LoginForm 并从 Form 派生并覆盖 Window Procedure。我希望我足够好地解释了这个问题:D。这可能是编译器错误或我的愚蠢 :P。无论如何,提前致谢。

最佳答案

问题出在这里:

bool OnInit(void) 
{ 
     Form form; 
     form.Show(); 

     return true; 
}

当该方法返回时,表单对象被销毁。
因此,您在调用 Show() 时存储的 this 指针不再有效。

  _handle = CreateWindowEx(WS_EX_CLIENTEDGE, "woop", "", WS_OVERLAPPEDWINDOW,
                           CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL,
                           GetModuleHandle(NULL), 
  /* Here ----> */         this
                          ); 

当您尝试执行分派(dispatch)时,它真的搞砸了,因为它使用 this 指针来计算要调用的虚函数的地址。

它使用 virtual 而不是当你拿走 virtual 时崩溃的原因是虚拟方法地址是在运行时计算的,而普通方法地址是在编译时植入的。

当计算虚方法的地址时,this 指针以某种方式取消引用(在本例中导致 UB),但由于对象已被销毁,该地址处的数据可能已被删除重新使用,因此您获得的函数地址是一些随机垃圾,调用它永远不会好。

一个简单的解决方案是使表单成为应用程序对象的一部分。
因此它的生命周期与应用程序相同:

class SampleApp : public Woop::Application 
{ 
    Form form;

    bool OnInit(void) 
    { 
        form.Show(); 

        return true; 
    } 
}; 

关于c++ - 调用虚函数时崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3374277/

相关文章:

c++ - 如何从 QInputDialog 获取文本(正则表达式)

windows - Vagrant:运行 Vagrant ssh 时如何在 Windows 命令窗口中粘贴文本

c# - GetSystemTimeZones 缺少 GMT 标准时间

wpf - 将 WPF 项目作为 WinForm 解决方案中的进程进行调试

visual-studio - 用户账号不匹配如何远程调试?

python-3.x - 在使用 ipdb ResourceWarning 进行 unitest 时调试 python 代码

c++ - mmap问题,分配大量内存

c++ - 这是一个实用且性能足够好的着色器,可以在移动设备上进行模糊处理吗?

c++ - 类中类的大小

windows - 在 Powershell 中删除 file1 中存在于 file2 中的行