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