首页 文章

为什么这个简单的DLL在WM_ACTIVATEAPP到来之前崩溃了?

提问于
浏览
0

下面的最小DLL只使用Win32 API,它只是尝试创建一个MDI框架/客户端窗口和一个子窗口,并在DLL卸载时销毁框架窗口 . DLL在Windows XP上崩溃,但在USER32中执行INT x2B指令时例外 .

对于测试,DLL只是由调用LoadLibrary('badcode.dll')的单行应用程序调用 .

崩溃发生在DLL完成之前的最终"DestroyWindow(framewindowhandle)"内, after FrameWindowProc接收WM_ACTIVATE但 before 它接收WM_ACTIVEAPP .

DLL代码已经从更大的原始文件中删除,以尽可能地隔离bug . 尽管 not 破坏框架窗口也会使当前的崩溃消失,大约12年前,确定在NT上运行的Visual Basic等工具会崩溃,除非在卸载DLL之前调用"DestroyWindow(framewindowhandle)" . 然而,就在最近,如上所述,突然发现用于测试某些DLL入口点的新小程序在XP上崩溃 .

虽然用Delphi 6编写,但代码只依赖于vanilla Win32 API .

library badcode; // works if rewritten as a program instead of DLL

{$R *.RES} // removing this avoids crash

uses windows, messages; // only win32 calls are made

// 3 MDI window handles
var framewindowhandle, clientwindowhandle, childwindowhandle: hwnd;

function framewindowproc(windowhandle: hwnd; message: word; wparam, lparam: longint): longint; stdcall;
var ccs: tclientcreatestruct;
begin // frame window has received a message
if message = WM_CREATE then
  begin // create the client window
  ccs.hwindowmenu := 0; ccs.idfirstchild := 0;
  clientwindowhandle := createwindow('MDICLIENT', '', ws_child + ws_clipchildren + ws_visible, 10, 10, 50, 50, windowhandle, 0, hinstance, @ccs);
  result := 0; // we handled the message
  end
else // do default handling
  result := defframeproc(windowhandle, clientwindowhandle, message, wparam, lparam);
end;

function childwindowproc(windowhandle: hwnd; message: word; wparam, lparam: longint): longint; stdcall;
begin // child window has received a message, do default handling
result := defmdichildproc(windowhandle, message, wparam, lparam);
end;

procedure DLLHandler(reason: integer);
begin
if reason = DLL_PROCESS_DETACH then // unloading dll
  DestroyWindow(framewindowhandle); // causes the crash, never returns
end;

var wc: twndclass; mcs: tmdicreatestruct;

begin // DLL loading time
DLLProc := @DLLHandler; // so we can detect unload
wc.hinstance := hinstance;
wc.lpfnwndproc := @framewindowproc;
wc.style := 0; wc.cbclsextra := 0; wc.cbwndextra := 0;
wc.hicon := loadicon(0, IDI_ASTERISK);
wc.hcursor := loadcursor(0, IDC_ARROW);
wc.hbrbackground := 0;
wc.lpszmenuname := 'MENUBAR'; // changing to '' avoids the crash
wc.lpszclassname := 'BAD';
registerclass(wc); // register the frame window

wc.lpfnwndproc := @childwindowproc;
wc.lpszmenuname := '';
wc.lpszclassname := 'DATA';
registerclass(wc); // register the child window

framewindowhandle := createwindow('BAD', 'frame', WS_OVERLAPPEDWINDOW + WS_CLIPCHILDREN, 100, 100, 400, 600, 0, 0, hinstance, nil);

mcs.szclass := 'DATA'; mcs.sztitle := 'child'; mcs.howner := hinstance;
mcs.x := 50; mcs.y := 50; mcs.cx := 50; mcs.cy := 50; mcs.style := WS_MINIMIZE; // changing the style avoids the crash
childwindowhandle := sendmessage(clientwindowhandle, WM_MDICREATE, 0, longint(@mcs));
sendmessage(clientwindowhandle, WM_MDIRESTORE, childwindowhandle, 0); // skipping this avoids the crash
end.

1 回答

  • 0

    一些优秀的dependencywalker工具,我发现我的机器上的一些旧的扫描仪软件已经配置了USER32,以便在执行 any 程序时挂钩一个与OCR相关的DLL,并且该DLL正在进行一些有问题的调用,包括为某些程序加载两次原因 . 卸载扫描仪软件使崩溃消失,所有O / S DLL加载/卸载看起来更合理 . 不过,我将修改我的DLL,在附加/分离期间不执行任何操作,并包含用于启动/停止的新入口点 .

相关问题