C# 如何防止在ActiveX方法调用期间WPF事件处理程序重新进入?
我们正在从WPF和STA应用程序中调用ActiveX组件上的方法。此调用通过以下方式执行:C# 如何防止在ActiveX方法调用期间WPF事件处理程序重新进入?,c#,.net,wpf,com,activex,C#,.net,Wpf,Com,Activex,我们正在从WPF和STA应用程序中调用ActiveX组件上的方法。此调用通过以下方式执行: res = ocx.GetType().InvokeMember(methodName, flags, null, ocx, args); …其中ocx是使用System.Windows.Forms.AxHost.GetOcx()方法检索的ActiveX对象 此调用在WPF事件处理程序中执行,例如“鼠标单击” 现在问题来了。如果我们双击“鼠标点击”事件将触发,运行InvokeMember()。但是,在此
res = ocx.GetType().InvokeMember(methodName, flags, null, ocx, args);
…其中ocx是使用System.Windows.Forms.AxHost.GetOcx()方法检索的ActiveX对象
此调用在WPF事件处理程序中执行,例如“鼠标单击”
现在问题来了。如果我们双击“鼠标点击”事件将触发,运行InvokeMember()。但是,在此调用过程中,我们看到“鼠标单击”事件被重新输入。因此,在同一线程中,我们在调用堆栈上看到两次事件处理程序。这是非常出乎意料的,我们正在努力防止这种情况发生。我们怎样才能防止这种情况发生
我们能想到为什么会发生这种情况的唯一原因是:
- COM对象是在另一个STA中创建的,因此我们正在执行需要编组的跨STA调用
- 跨线程STA调用使用Windows消息向COM组件发送RPC请求
- 跨线程STA调用使用Windows消息泵接收RPC应答
- 在等待过程中,会出现另一种类型的事件(如“鼠标单击”),并且在处理RPC应答之前处理该事件
- 这个RPC答案得到处理
- 在所有事件处理程序中使用lock()。这不起作用,因为lock()将锁定线程,在本例中,是同一个线程重新进入事件处理程序
- 使用自定义锁定,如“bool locked=false”;如果(!locked){locked=true;InvokeMethod();…;locked=false;}'。这在一定程度上是可行的:它会丢弃事件,而不是将它们排队等待以后,并且需要对所有事件处理程序进行大量更改,这是不好的
- 使用Dispatcher.DisableProcessing停止处理(其他)消息。这并没有帮助:它会抛出一个异常,因为消息无论如何都会被处理
- 在新线程中创建第二个调度程序,并通过dispatcher.Invoke()运行ocx.invokeMhod(),让另一个线程处理它。这给出了“一个事件无法调用任何订阅服务器(HRESULT的异常:0x80040201)”(是的,我们还订阅了ActiveX对象的COM事件)
- 使用Dispatcher.PushFrame()停止事件处理。这也失败了
请看我的下一个答案,再尝试几次#3是最丑的,但应该可以工作。如果你已经有了异步行为,我会试试Jeffrey Richter的
PowerThreading
库。它有AsyncEnumerator
来简化异步编程。它还具有锁定原语,可以帮助您实现场景。据我所知,这个原语不同于常规的监视器
类,因为它不允许在同一个线程中重新输入代码,所以它可能会对您有所帮助。不幸的是,我还没有尝试过那个原始版本,所以无法添加太多内容
这里有一篇关于这个原语的文章:我在过去处理过类似的问题,尽管不是来自WPF 在win32应用程序中,推荐的方法是使用IMessageFilter::MessagePending-这可以配置为
MyAxWrapper.dll!MyAxWrapper.MyAxWrapper.InvokeMember(string methodName, System.Reflection.BindingFlags flags, object[] args, int refArgIdx) Line 53 C#
MyAxWrapper.dll!MyAxWrapper.LoggingMyAxWrapper.InvokeMember(string methodName, System.Reflection.BindingFlags flags, object[] args, int refArgIdx) Line 151 + 0x14 bytes C#
MyAxWrapper.dll!MyAxWrapper.MyAxWrapper.InvokeMethod(string methodName, object[] args) Line 92 + 0x18 bytes C#
MyAxWrapper.dll!MyAxWrapper.MyAxAppWrapper.ListShelfs(string CanvasPageId) Line 300 + 0x42 bytes C#
PACS.dll!PACS.MyAxDatabase.GetShelfIdsOn(string canvasPageId) Line 223 + 0xf bytes C#
MyAxCanvas.dll!MyAxCanvas.MyAxCanvasPlugin.UpdateTimeLineSelection(string canvasPageId) Line 123 + 0x10 bytes C#
MyAxCanvas.dll!MyAxCanvas.MyAxCanvasPlugin.EventShelfClosed(string canvasPageId, string shelfId) Line 180 + 0xb bytes C#
MyAxWrapper.dll!MyAxWrapper.MyAxAppWrapper.FireEvent(string eventName, object[] args) Line 21 + 0x73 bytes C#
MyAxWrapper.dll!MyAxWrapper.MyAxEventForwarder.EventShelfClosed(string CanvasPageID, string ShelfID) Line 177 + 0x58 bytes C#
[Native to Managed Transition]
[Native to Managed Transition]
MyAxWrapper.dll!MyAxWrapper.MyAxWrapper.InvokeMember(string methodName, System.Reflection.BindingFlags flags, object[] args, int refArgIdx) Line 75 + 0x2b bytes C#
MyAxWrapper.dll!MyAxWrapper.LoggingMyAxWrapper.InvokeMember(string methodName, System.Reflection.BindingFlags flags, object[] args, int refArgIdx) Line 151 + 0x14 bytes C#
MyAxWrapper.dll!MyAxWrapper.MyAxWrapper.InvokeMethod(string methodName, object[] args) Line 92 + 0x18 bytes C#
MyAxWrapper.dll!MyAxWrapper.MyAxAppWrapper.CloseShelf(string a) Line 218 + 0x42 bytes C#
MyAxCanvas.dll!MyAxCanvas.MyAxCanvasPlugin.EventCanvasPageCreated.AnonymousMethod__0(DataModel.Item exam) Line 110 + 0x1d bytes C#
ItemPresenter.dll!ItemPresenter.ItemPresenter.OnItemClicked(DataModel.Item study) Line 36 + 0x14 bytes C#
ItemPresenter.dll!ItemPresenter.ItemPresenter.ItemPresenterPerYearControls_Click(object sender, System.Windows.RoutedEventArgs e) Line 215 + 0x1e bytes C#
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) + 0x78 bytes
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) + 0x1ae bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) + 0x79 bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs e) + 0x17 bytes
ItemPresenter.dll!ItemPresenter.ItemPresenterControl.Grid_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) Line 47 + 0x29 bytes C#
PresentationCore.dll!System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(System.Delegate genericHandler, object genericTarget) + 0x31 bytes
PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target) + 0x29 bytes
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) + 0x3e bytes
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) + 0x1ae bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) + 0x79 bytes
PresentationCore.dll!System.Windows.UIElement.RaiseTrustedEvent(System.Windows.RoutedEventArgs args) + 0x41 bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs args, bool trusted) + 0x2c bytes
PresentationCore.dll!System.Windows.Input.InputManager.ProcessStagingArea() + 0x1ff bytes
PresentationCore.dll!System.Windows.Input.InputManager.ProcessInput(System.Windows.Input.InputEventArgs input) + 0x45 bytes
PresentationCore.dll!System.Windows.Input.InputProviderSite.ReportInput(System.Windows.Input.InputReport inputReport) + 0x62 bytes
PresentationCore.dll!System.Windows.Interop.HwndMouseInputProvider.ReportInput(System.IntPtr hwnd, System.Windows.Input.InputMode mode, int timestamp, System.Windows.Input.RawMouseActions actions, int x, int y, int wheel) + 0x263 bytes
PresentationCore.dll!System.Windows.Interop.HwndMouseInputProvider.FilterMessage(System.IntPtr hwnd, MS.Internal.Interop.WindowMessage msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) + 0x46d bytes
PresentationCore.dll!System.Windows.Interop.HwndSource.InputFilterMessage(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) + 0x75 bytes
WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) + 0xbe bytes
WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) + 0x7d bytes
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) + 0x53 bytes
WindowsBase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source, System.Delegate method, object args, int numArgs, System.Delegate catchHandler) + 0x42 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.InvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs) + 0xb4 bytes
WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam) + 0x104 bytes
[Native to Managed Transition]
[Managed to Native Transition]
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame) + 0xc1 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) + 0x49 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.Run() + 0x4c bytes
PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore) + 0x17 bytes
PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) + 0x6f bytes
PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window window) + 0x26 bytes
PresentationFramework.dll!System.Windows.Application.Run() + 0x1b bytes
MyAxCanvasStandalone.exe!MyAxCanvasStandalone.App.Main(string[] args) Line 37 + 0xa bytes C#
[Native to Managed Transition]
[Managed to Native Transition]
mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) + 0x6d bytes
Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() + 0x2a bytes
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state) + 0x63 bytes
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool ignoreSyncCtx) + 0xb0 bytes
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x2c bytes
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x44 bytes
[Native to Managed Transition]
For i As Integer = 0 To 20
Text = i.ToString
System.Threading.Thread.Sleep(100)
Application.DoEvents()
Next
Boolean InCloseShelf
function CloseShelf(...)
InCloseShelf=True;
try
{
com call and all else
}
finally
InCloseShelf=False
function EventShelfClosed(...
while (InCloseShelf)
{
DoEvents
}
void DoEvents(){
DispatcherFrame f = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
(SendOrPostCallback)delegate(object arg) {
DispatcherFrame fr = arg as DispatcherFrame;
fr.Continue=True;
}, f);
Dispatcher.PushFrame(frame);
}
static void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame(true);
Dispatcher.CurrentDispatcher.BeginInvoke
(
DispatcherPriority.Background,
(SendOrPostCallback) delegate(object arg)
{
var f = arg as DispatcherFrame;
f.Continue = false;
},
frame
);
Dispatcher.PushFrame(frame);
}
.... OnClick(...)
{
if(SelectionChanged!=null)
SelectionChanged(...)
}
.... OnClick(...)
{
Dispatcher.BeginInvoke(delegate(){
if(SelectionChanged!=null)
SelectionChanged(...)
});
}