1

ウィンドウを探すスレッドを作成しています。ウィンドウが見つかると、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. 1 分ほどでダイアログを閉じないと、アプリがクラッシュします。これは、スレッドがガベージ コレクションを取得しているためですか? スレッドが完了したことを GC が判断できる限り、これは理にかなっていますか? これが事実である場合 (そして、私はそうであるとは知りません)、ダイアログが存在する限りスレッドを存続させるにはどうすればよいですか?

  2. 「X」ボタン (WM_CLOSE) でダイアログをすぐに閉じると、アプリがクラッシュします。windowprocでクラッシュすると思いますが、そこにブレークポイントを取得できません。AccessViolationException が発生しました。この例外には、「保護されたメモリを読み書きしようとしました。これは、多くの場合、他のメモリが破損していることを示しています。」それは競合状態ですが、私にはわかりません。参考までに、コマンドを処理したら古いwindowprocをリセットしていましたが、それはさらに頻繁にクラッシュしていました!

これらの問題をどのように解決できるかについてのアイデアはありますか?

4

2 に答える 2

0

ついに解決策を思いつき、別の角度から問題を攻撃しました。SetWinEventHookと、驚くべきことに次のプロパティを持つオプションWINEVENT_OUTOFCONTEXTを使用して、マネージコードにシステム全体のフックを設定することができました。コールバック関数は、イベントを生成するプロセスのアドレス空間にマップされません。イベントEVENT_SYSTEM_DIALOGSTARTをトラップして、ダイアログが作成されるたびに通知を受信し、ダイアログが破棄されるとEVENT_SYSTEM_DIALOGENDを受信します。

于 2010-03-25T01:44:22.027 に答える
0

私ができる2つの観察ポイント....

  • あなたのでは、あなたはnullとDetectFileDialogProc比較しています、それはタイプですはい? もしそうなら、比較のためのそのチェックはwndIntPtrif (wnd > IntPtr.Zero){ .... }
  • で、変数をWndProc使用していますが、これは悪いことです...このようなことをし、これを使用する必要がありますthislockprivate readonly object objLock = new object();WndProclock (objLock){....}

問題が解決するかどうかを確認してください....

于 2010-03-23T23:49:44.687 に答える