从 C# 中的线程子类化窗口

发布于 2024-08-26 07:01:09 字数 2248 浏览 6 评论 0原文

我正在创建一个寻找窗口的线程。当它找到窗口时,它会覆盖其 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,但崩溃的频率更高!

关于如何解决这些问题有什么想法吗?

I'm creating a thread that looks for a window. When it finds the window, it overrides its windowproc, and handles WM_COMMAND and WM_CLOSE.

Here's the code that looks for the window and subclasses it:

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

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

This all works well under normal conditions. But I am seeing two instances of bad behavior in order of badness:

  1. If I do not close the dialog within a minute or so, the app crashes. Is this because the thread is getting garbage collected? This would kind of make sense, as far as GC can tell the thread is done? If this is the case, (and I don't know that it is), how can I make the thread stay around as long as the dialog is around?

  2. If I immediately close the dialog with the 'X' button (WM_CLOSE) the app crashes. I believe its crashing in the windowproc, but I can't get a breakpoint in there. I'm getting an AccessViolationException, The exception says "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." Its a race condition, but of what I don't know. FYI, I had been reseting the old windowproc once I processed the commands, but that was crashing even more often!

Any ideas on how I can solve these issues?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

眼眸印温柔 2024-09-02 07:01:09

我可以观察到两点......

  • 在您的 DetectFileDialogProc 中,您将 wnd 与 null 进行比较,这是一个 IntPtr 类型 yes ?如果是这样,比较检查应该是 if (wnd > IntPtr.Zero){ .... }
  • 在您的 WndProc 中,您正在使用 lock 的 this 变量这是一件坏事......你应该做这样的事情 private readonly object objLock = new object(); 和在您的 WndProc 中使用此 lock (objLock){....}

并查看是否可以解决问题...

Two points of observation that I can make....

  • In your DetectFileDialogProc, you are comparing wnd to null, that is an IntPtr type yes? if so, that check for the comparison should be if (wnd > IntPtr.Zero){ .... }
  • In your WndProc, you are using the this variable for the lock which is a bad thing to do...you should do something like this private readonly object objLock = new object(); and within your WndProc use this lock (objLock){....}

and see if that resolves the issue....

殊姿 2024-09-02 07:01:09

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

Finally came up with a solution, attacking the problem from a different angle. I was able to set a system-wide hook in managed code using SetWinEventHook, and the option WINEVENT_OUTOFCONTEXT, which amazingly has the property: The callback function is not mapped into the address space of the process that generates the event.
I trap the event EVENT_SYSTEM_DIALOGSTART to receive notifications whenever a dialog is created, and EVENT_SYSTEM_DIALOGEND when its destroyed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文