1

如果某些应用程序退出,我正在尝试使用它ThreadPool.UnsafeRegisterWaitForSingleObject来通知。它至少可以按我想要的方式工作,但是在我关闭主窗体后,它会抛出异常:

SEHException : 外部组件抛出异常

堆栈跟踪:

at Microsoft.Win32.SafeNativeMethods.CloseHandle(IntPtr handle)
at Microsoft.Win32.SafeHandles.SafeProcessHandle.ReleaseHandle()
at System.Runtime.InteropServices.SafeHandle.InternalFinalize()
at System.Runtime.InteropServices.SafeHandle.Dispose(Boolean disposing)
at System.Runtime.InteropServices.SafeHandle.Finalize()

这是代码:

Load += (s, e) => {
   var p = System.Diagnostics.Process.GetProcessById(8524).Handle;
   var wh = new ManualResetEvent(false);
   wh.SafeWaitHandle = new SafeWaitHandle(p, true);                
   var cl = ThreadPool.UnsafeRegisterWaitForSingleObject(
                                            wh, new WaitOrTimerCallback((o, b) => 
                {
                    MessageBox.Show("Exited!");
                }), null, Timeout.Infinite, true);
};

我什至不需要等待回调被调用,只需运行代码,然后关闭主窗体就会抛出异常。

有趣的是,如果使用OpenProcess本机函数来获取进程句柄而不是使用这样的Process类:

//ProcessAccessFlags.Synchronize = 0x00100000
var p = OpenProcess(ProcessAccessFlags.Synchronize, false, 8524);

然后它可以正常工作,没有任何例外,但我不确定在这种情况下是否最好尽可能地坚持托管包装器。另外我想了解为什么在使用Process类时会引发此异常。看起来Synchronize标志(记录中的必需标志)是使 usingOpenProcess和 wrapper 不同的原因Process。如果在这种情况下看起来Process无法替换OpenProcess,或者我在这里错过了什么?

其他信息:Visual Studio 2010,面向 .NET 4.0

谢谢你。

4

1 回答 1

2
wh.SafeWaitHandle = new SafeWaitHandle(p, true); 

这就是问题开始的地方。您现在有两个包裹同一个句柄的 SafeHandle。一个在 Process 对象中,另一个在 ManualResetEvent 对象中。不可避免地,其中之一将永远失败。你的代码总是会崩溃,在这种情况下它发生是因为 MRE 的终结器首先运行。相反,当 Process 终结器首先运行时,您有 50% 的几率。

第一种方法是不这样做。您已经有一个很好的事件可以执行此操作,请使用Process.Exited 事件。你甚至可以在正确的线程上运行它,这样 MessageBox 就不会消失在另一个窗口后面,使用它的 SynchronizingObject 属性。

另一种方法是通过调用 DuplicateHandle 来复制句柄。呜呜。或者只是解决您首先滥用此 ManualResetEvent 的原因。Process 类是在 .NET 1.0 中设计的,如果它的 Handle 属性是 SafeHandle,就不会出现这个问题。但是它的 IntPtr,他们无法再修复它了。通过从 SafeHandle 派生您自己的类来修复它,不要在 ReleaseHandle() 重载中做任何事情。

于 2015-09-12T13:43:30.913 回答