调用虚函数时崩溃

0 c++ windows crash debugging winapi

好的,这是一个非常奇怪的问题.我想首先说我不是c ++的初学者,我当然不是先进的.我在中间的某个地方.我正在尝试做的是创建Win32 API的C++ OOP包装器库(dll).这是我的图书馆的课程.我用Mingw使用命令编译它:

g++ -shared -o bin\win32oop.dll src\Application.cpp src\Form\Form.cpp -Wall
Run Code Online (Sandbox Code Playgroud)

SRC\Application.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_
Run Code Online (Sandbox Code Playgroud)

SRC\Application.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);
}
Run Code Online (Sandbox Code Playgroud)

SRC \表格\ Form.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_
Run Code Online (Sandbox Code Playgroud)

SRC \表格\ Form.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);
 }
}
Run Code Online (Sandbox Code Playgroud)

这是我正在测试库的程序:

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;
}
Run Code Online (Sandbox Code Playgroud)

好吧,现在问题.您是否在Form类中看到了虚拟Window Procedure?如果我从声明中删除虚拟,程序编译并运行正常.但是当我把它添加回来时,它会崩溃.臭名昭着的"不发送"对话框出现了.我不确定它什么时候崩溃,我会试着用MessageBox()来解决这个问题(大声笑,这是我学习如何用gdb调试的结果).我正在努力使它能够创建一个类如LoginForm并从Form派生并覆盖Window Procedure.我希望我能够很好地解释这个问题:D.这可能是编译器错误或我的愚蠢:P.无论如何,提前谢谢.

Mar*_*ork 7

问题出在这里:

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

     return true; 
}
Run Code Online (Sandbox Code Playgroud)

返回此方法时,将销毁表单对象.
因此,this调用Show()时存储的指针不再有效.

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

当你尝试执行调度时,它会被搞砸,因为它正在使用this指针来计算要调用的虚函数的地址.

它是虚拟的,而不是虚拟的原因是虚拟方法地址是在运行时计算的,而正常的方法地址是在编译时种植的.

在计算虚方法的地址时,this指针以某种方式被解引用(在这种情况下会导致UB),但由于对象已被破坏,该地址的数据已经被重新使用,因此您获得该函数的地址是一些随机的垃圾,并称这永远不会好.

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

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

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

        return true; 
    } 
}; 
Run Code Online (Sandbox Code Playgroud)