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); // これをスキップするとクラッシュを回避できます 終わり。