0

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

为了测试,DLL 由调用 LoadLibrary('badcode.dll') 的单行应用程序简单地调用。

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

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

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

库错误代码;// 如果重写为程序而不是 DLL 则有效

{$R *.RES} // 删除它可以避免崩溃

使用窗口、消息;// 只进行 win32 调用

// 3 个 MDI 窗口句柄
var framewindowhandle, clientwindowhandle, childwindowhandle: hwnd;

函数 framewindowproc(windowhandle: hwnd; 消息: word; wparam, lparam: longint): longint; 标准调用;
var ccs: tclientcreatestruct;
begin // 框架窗口已收到消息
如果消息 = WM_CREATE 那么
  begin // 创建客户端窗口
  ccs.hwindowmenu := 0; ccs.idfirstchild := 0;
  clientwindowhandle := createwindow('MDICLIENT', '', ws_child + ws_clipchildren + ws_visible, 10, 10, 50, 50, windowhandle, 0, hinstance, @ccs);
  结果:= 0;// 我们处理了消息
  结尾
else // 做默认处理
  结果:= defframeproc(窗口句柄,客户端窗口句柄,消息,wparam,lparam);
结尾;

函数 childwindowproc(windowhandle: hwnd; 消息: word; wparam, lparam: longint): longint; 标准调用;
begin // 子窗口收到消息,做默认处理
结果:= defmdichildproc(窗口句柄,消息,wparam,lparam);
结尾;

程序 DLLHandler(原因:整数);
开始
if reason = DLL_PROCESS_DETACH then // 卸载 dll
  销毁窗口(框架窗口句柄);// 导致崩溃,永不返回
结尾;

var wc: twndclass; mcs:tmdicreatestruct;

begin // DLL 加载时间
DLLProc := @DLLHandler; // 所以我们可以检测卸载
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.hbr背景:= 0;
wc.lpszmenuname := '菜单栏'; // 更改为 '' 避免崩溃
wc.lpszclassname := 'BAD';
注册类(厕所);//注册框架窗口

wc.lpfnwndproc := @childwindowproc;
wc.lpszmenuname := '';
wc.lpszclassname := '数据';
注册类(厕所);//注册子窗口

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

mcs.szclass := '数据'; mcs.sztitle := '孩子'; mcs.howner := 障碍;
mcs.x := 50; mcs.y := 50; mcs.cx := 50; mcs.cy := 50; mcs.style := WS_MINIMIZE; // 改变样式避免崩溃
childwindowhandle := sendmessage(clientwindowhandle, WM_MDICREATE, 0, longint(@mcs));
发送消息(客户端窗口句柄,WM_MDIRESTORE,子窗口句柄,0);// 跳过这个可以避免崩溃
结尾。
4

1 回答 1

0

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

于 2012-12-16T02:34:18.220 回答