我正在创建一个寻找窗口的线程。当它找到窗口时,它会覆盖它的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);
}
这一切在正常情况下都能很好地运作。但是我看到了两个不良行为的例子:
如果我在一分钟左右没有关闭对话框,应用程序崩溃了。这是因为线程正在收集垃圾吗?这有点意义,只要GC可以告诉线程完成了吗?如果是这种情况,(并且我不知道它是这样的话),只要对话框出现,我怎么能让线程保持不变?
如果我立即使用“X”按钮(WM_CLOSE)关闭对话框,则应用程序崩溃。我相信它在windowproc中崩溃了,但我无法在那里得到一个断点。我得到一个AccessViolationException,异常说“尝试读取或写入受保护的内存。这通常表明其他内存已损坏。”这是一个竞争条件,但我不知道。仅供参考,我处理过命令后,我一直在重新设置旧的windowproc,但这种情况更频繁地崩溃了!
关于如何解决这些问题的任何想法?
答案 0 :(得分:0)
我能做出两点观察......
DetectFileDialogProc
中,您将wnd
与null进行比较,即IntPtr
类型是吗?如果是这样,那么检查比较应该是if (wnd > IntPtr.Zero){ .... }
WndProc
中,您使用的this
变量用于lock
,这是一件坏事......你应该做这样的事情private readonly object objLock = new object();
和在WndProc
内使用此lock (objLock){....}
并查看是否能解决问题......
答案 1 :(得分:0)
最后提出了一个解决方案,从不同的角度解决问题。我能够使用SetWinEventHook和选项WINEVENT_OUTOFCONTEXT在托管代码中设置系统范围的钩子,它具有令人惊讶的属性:回调函数未映射到生成事件的进程的地址空间。 我捕获事件EVENT_SYSTEM_DIALOGSTART以在创建对话框时接收通知,并在EVENT_SYSTEM_DIALOGEND被销毁时接收。