C#のスレッドからウィンドウをサブクラス化する
-
22-09-2019 - |
質問
ウィンドウを探すスレッドを作成しています。ウィンドウが見つかると、WindowProcをオーバーライドし、WM_CommandとWM_Closeを処理します。
これがウィンドウを探してサブクラスを探しているコードです。
public void DetectFileDialogProc()
{
Window fileDialog = null;
// try to find the dialog twice, with a delay of 500 ms each time
for (int attempts = 0; fileDialog == null && attempts < 2; attempts++)
{
// FindDialogs enumerates all windows of class #32770 via an EnumWindowProc
foreach (Window wnd in FindDialogs(500))
{
IntPtr parent = NativeMethods.User32.GetParent(wnd.Handle);
if (parent != IntPtr.Zero)
{
// we're looking for a dialog whose parent is a dialog as well
Window parentWindow = new Window(parent);
if (parentWindow.ClassName == NativeMethods.SystemWindowClasses.Dialog)
{
fileDialog = wnd;
break;
}
}
}
}
// if we found the dialog
if (fileDialog != null)
{
OldWinProc = NativeMethods.User32.GetWindowLong(fileDialog.Handle, NativeMethods.GWL_WNDPROC);
NativeMethods.User32.SetWindowLong(fileDialog.Handle, NativeMethods.GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(new WindowProc(WndProc)).ToInt32());
}
}
とWindowProc:
public IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
lock (this)
{
if (!handled)
{
if (msg == NativeMethods.WM_COMMAND || msg == NativeMethods.WM_CLOSE)
{
// adding to a list. i never access the window via the hwnd from this list, i just treat it as a number
_addDescriptor(hWnd);
handled = true;
}
}
}
return NativeMethods.User32.CallWindowProc(OldWinProc, hWnd, msg, wParam, lParam);
}
これはすべて、通常の条件下でうまく機能します。しかし、私は悪の順に悪い行動の2つの例を見ています:
1分ほど以内にダイアログを閉じないと、アプリがクラッシュします。これは、スレッドがゴミを収集しているからですか? GCがスレッドが完了していると判断できる限り、これは理にかなっていますか?この場合(そして、私はそれがそうであるかどうかはわかりません)、ダイアログが周りにある限り、どうすればスレッドを維持することができますか?
「x」ボタン(wm_close)で直ちにダイアログを閉じると、アプリがクラッシュします。 WindowProcでクラッシュしていると思いますが、そこにブレークポイントを取得することはできません。私はAccessViolationExceptionを取得しています。例外には、「保護されたメモリの読み取りまたは書き込みを試みました。これは、他のメモリが破損していることを示すことが多い」と書かれています。その人種状態ですが、私が知らないことについて。参考までに、コマンドを処理すると古いWindowprocをリセットしていましたが、それはさらに頻繁にクラッシュしていました!
これらの問題をどのように解決できるかについてのアイデアはありますか?
解決 2
最後に、別の角度から問題を攻撃し、解決策を思いつきました。 SetwineVenthookを使用して管理コードにシステム全体のフックを設定することができました。WineVent_outofContextを使用して、驚くほどプロパティを備えています。コールバック関数は、イベントを生成するプロセスのアドレス空間にマッピングされません。イベントevent_system_dialogstartをトラップして、ダイアログが作成されるたびに通知を受信し、event_system_dialogendが破壊されたときにevent_system_dialogendをトラップします。
他のヒント
私が作ることができる2つの観察点...
- あなたの中で
DetectFileDialogProc
, 、あなたは比較していますwnd
nullに、それはですIntPtr
はいと入力しますか?もしそうなら、その比較のチェックはif (wnd > IntPtr.Zero){ .... }
- あなたの中で
WndProc
, 、あなたはを使用していますthis
の変数lock
これは悪いことです...あなたはこのようなことをするべきですprivate readonly object objLock = new object();
そしてあなたの中にWndProc
これを使ってlock (objLock){....}
そして、それが問題を解決するかどうかを確認してください。