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

时间:2012-12-11 17:32:36

标签: windows debugging dll mdi

下面的最小DLL(仅使用Win32 API)尝试除了创建MDI框架/客户端窗口和一个子窗口之外什么都不做,并在DLL卸载时销毁框架窗口。 DLL在Windows XP上崩溃,但在USER32中执行INT x2B指令时例外。

为了进行测试,只需调用LoadLibrary('badcode.dll')的单行应用程序调用DLL。

崩溃发生在DLL完成之前的最终“DestroyWindow(framewindowhandle)”中,之后,FrameWindowProc接收WM_ACTIVATE但之前接收到WM_ACTIVEAPP。

尽可能地从更大的原始文件中删除DLL代码以隔离错误。虽然破坏框架窗口也会使当前崩溃消失,大约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 :(得分:0)

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