0

Win32 API のみを使用する以下の最小限の DLL は、MDI フレーム/クライアント ウィンドウと 1 つの子ウィンドウを作成し、DLL のアンロード時にフレーム ウィンドウを破棄するだけです。Windows XP で DLL がクラッシュし、USER32 で INT x2B 命令を実行すると例外が発生します。

テストのために、DLL は、LoadLibrary('badcode.dll') を呼び出す 1 行のアプリケーションによって単純に呼び出されます。

クラッシュは、FrameWindowProc が WM_ACTIVATE を受け取った後、 WM_ACTIVEAPP を受け取るに、DLL が終了する直前の最後の「DestroyWindow(framewindowhandle)」内で発生します。

DLL コードは、バグを分離するために、はるかに大きなオリジナルから可能な限り削除されています。フレーム ウィンドウを破棄しないことでも現在のクラッシュは解消されますが、約 12 年前、NT で実行されている Visual Basic などのツールは、DLL がアンロードされる前に「DestroyWindow(framewindowhandle)」が呼び出されない限りクラッシュすると判断されました。しかし、つい最近、いくつかの DLL エントリポイントをテストするために作成された新しい小さなプログラムが、前述のように XP で突然クラッシュすることが判明しました。

Delphi 6 で書かれていますが、コードはバニラの Win32 API のみに依存しています。

ライブラリの悪いコード; // DLL ではなくプログラムとして書き直せば動く

{$R *.RES} // これを削除するとクラッシュを回避できます

ウィンドウ、メッセージを使用します。// win32 呼び出しのみが行われます

// 3 つの MDI ウィンドウ ハンドル
var framewindowhandle、clientwindowhandle、childwindowhandle: hwnd;

function framewindowproc(windowhandle: hwnd; message: word; wparam, lparam: longint): longint; stdcall;
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(windowhandle、clientwindowhandle、メッセージ、wparam、lparam);
終わり;

function childwindowproc(windowhandle: hwnd; message: word; wparam, lparam: longint): longint; stdcall;
begin // 子ウィンドウがメッセージを受信しました。デフォルトの処理を行います
結果:= defmdichildproc(ウィンドウハンドル、メッセージ、wparam、lparam);
終わり;

プロシージャ DLLHandler(理由: 整数);
始める
if reason = DLL_PROCESS_DETACH then // dll をアンロード中
  DestroyWindow(framewindowhandle); // クラッシュを引き起こし、戻りません
終わり;

var wc: twndclass; mcs: tmdicreatestruct;

begin // DLL の読み込み時間
DLLProc := @DLLHandler; // アンロードを検出できるように
wc.hinstance := hinstance;
wc.lpfnwndproc := @framewindowproc;
wc.スタイル:= 0; wc.cbclsextra := 0; wc.cbwndextra := 0;
wc.hicon := loadicon(0, IDI_ASTERISK);
wc.hcursor := loadcursor(0, IDC_ARROW);
wc.hbrbackground:= 0;
wc.lpszmenuname := 'メニューバー'; // '' に変更すると、クラッシュを回避できます
wc.lpszclassname:= 'BAD';
registerclass(トイレ); // フレーム ウィンドウを登録する

wc.lpfnwndproc := @childwindowproc;
wc.lpszmenuname:= '';
wc.lpszclassname := 'データ';
registerclass(トイレ); // 子ウィンドウを登録する

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

mcs.szclass := 'データ'; mcs.sztitle := '子'; mcs.hower := ヒンスタンス;
mcs.x := 50; mcs.y := 50; mcs.cx := 50; mcs.cy:= 50; mcs.style := WS_MINIMIZE; // スタイルを変更するとクラッシュを回避できます
childwindowhandle := sendmessage(clientwindowhandle, WM_MDICREATE, 0, longint(@mcs));
sendmessage(clientwindowhandle, WM_MDIRESTORE, childwindowhandle, 0); // これをスキップするとクラッシュを回避できます
終わり。
4

1 に答える 1

0

優れたdependencywalkerツールを使用して、私のマシン上の古いスキャナーソフトウェアが、プログラムの実行時にOCR関連のDLLをフックするようにUSER32を構成しており、そのDLLが、いくつかの2回のロードを含む、疑わしい呼び出しを行っていることを発見しました。理由。スキャナーソフトウェアをアンインストールすると、クラッシュがなくなり、すべてのO /SDLLのロード/アンロードがはるかに合理的に見えます。それでも、アタッチ/デタッチ中に何もしないようにDLLを変更し、開始/停止するための新しいエントリポイントを含めます。

于 2012-12-16T02:34:18.220 に答える