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);
}        

这一切都在正常条件下运作良好。但是我看到了两个不良行为的例子,按不良顺序排列:

  1. 如果我在一分钟左右没有关闭对话框,应用程序就会崩溃。这是因为线程正在收集垃圾吗?就 GC 可以告诉线程已完成而言,这有点道理?如果是这种情况,(我不知道是这样),只要对话框还在,我怎样才能让线程一直存在?

  2. 如果我立即使用“X”按钮 (WM_CLOSE) 关闭对话框,应用程序就会崩溃。我相信它在 windowproc 中崩溃了,但我无法在其中找到断点。我收到 AccessViolationException,异常显示“尝试读取或写入受保护的内存。这通常表明其他内存已损坏。” 这是一个比赛条件,但我不知道。仅供参考,一旦我处理了命令,我就一直在重置旧的 windowproc,但这更频繁地崩溃!

关于如何解决这些问题的任何想法?

4

2 回答 2

0

我可以提出两点观察......

  • 在您的DetectFileDialogProc中,您正在与wndnull 进行比较,这是一种IntPtr类型吗?如果是这样,那么检查比较应该是if (wnd > IntPtr.Zero){ .... }
  • 在您的WndProc中,您正在使用this变量,lock这是一件坏事……您应该做这样的事情,private readonly object objLock = new object();并在您的WndProc使用范围内lock (objLock){....}

看看是否能解决问题....

于 2010-03-23T23:49:44.687 回答
0

终于想出了一个解决方案,从不同的角度来攻克这个问题。我能够使用 SetWinEventHook 和选项 WINEVENT_OUTOFCONTEXT 在托管代码中设置系统范围的挂钩,该选项令人惊讶地具有以下属性:回调函数未映射到生成事件的进程的地址空间。我捕获事件 EVENT_SYSTEM_DIALOGSTART 以在创建对话框时接收通知,并在其销毁时接收通知 EVENT_SYSTEM_DIALOGEND。

于 2010-03-25T01:44:22.027 回答