如何prevent重入WPF事件处理过程中的ActiveX方法调用? [英] How to prevent re-entrancy of WPF event handler during ActiveX method call?

查看:231
本文介绍了如何prevent重入WPF事件处理过程中的ActiveX方法调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在呼吁一个ActiveX组件的方法从WPF和STA应用程序中。这个电话是后期绑定,通过执行:

We're calling methods on an ActiveX component from within a WPF and STA application. This calling is late-bound performed via:

res = ocx.GetType().InvokeMember(methodName, flags, null, ocx, args);

...其中OCX是检索与System.Windows.Forms.AxHost.GetOcx()方法中的ActiveX对象。

...where ocx is the ActiveX object retrieved with the System.Windows.Forms.AxHost.GetOcx() method.

该呼叫从一个WPF事件处理程序中进行的,说鼠标单击

This call is performed from within a WPF event handler, say 'mouse clicked'.

现在的问题。如果我们双击鼠标点击事件将触发,运行InvokeMember()。但是,的的这一呼吁,我们看到了鼠标点击事件被重新输入。因此,在同一个线程中,我们看到了事件处理程序两次调用堆栈。这是很意外,我们正在试图prevent的。我们怎样才能prevent这种情况的发生?

Now the problem. If we double-click the 'mouse clicked' event will trigger, running InvokeMember(). However, during this call, we see that the 'mouse clicked' event is re-entered. So in the same thread, we see the event handler twice on the call stack. This is very unexpected, and we're trying to prevent that. How can we prevent this from happening?

我们能想到的的为什么的它发生的唯一原因是:

The only reason we can think of why it happens is:

  • 在COM对象在另一个STA创建的,因此我们正在执行需要被编组交叉STA通话
  • 跨线程调用STA使用Windows消息发送RPC请求到COM组件
  • 跨线程STA呼叫使用Windows消息泵来接收RPC的答案
  • 在等待另一种类型的事件进来(如鼠标点击),这被处理的RPC答案被处理了。
  • 在此RPC答案被处理

东西:

  • 使用锁()在所有的事件处理程序。这个自锁(不工作)将锁定线程,在这种情况下,它是在同一线程重新进入事件处理程序。
  • 使用自定义锁像'布尔锁定= FALSE;如果(锁定!){锁定= TRUE;的InvokeMethod(); ...;锁定= FALSE; }。此工程部分:它扔掉的事件,而不是排队他们后,又需要大量的变化的所有的我们的事件处理程序,这是不是很好做​​
  • 使用Dispatcher.DisableProcessing从被处理停止(其它)消息。这并不能帮助:它抛出反正被处理,因为消息的异常
  • 创建一个新的线程,第二个调度和运行ocx.InvokeMehod()通过Dispatcher.Invoke()有它被另一个线程来处理。这给​​事件无法调用任何用户(从HRESULT异常:0x80040201)'。(是的,我们还订阅了ActiveX对象的COM事件)
  • 使用Dispatcher.PushFrame()来阻止事件的发生处理。这也将失败。
  • use lock() in all event handlers. This does not work since lock() will lock a thread, and in this case it is the same thread which re-enters the event handler.
  • use custom locking like 'bool locked = false; if (!locked) { locked = true; InvokeMethod(); ...; locked = false; }'. This works partially: it throws away the events instead of queuing them for later, and need an extensive change to all our event handlers, which is not nice to do.
  • use Dispatcher.DisableProcessing to stop (other) messages from being processed. This does not help: it throws an exception because of messages being processed anyways.
  • create a second dispatcher in a new thread, and run ocx.InvokeMehod() via Dispatcher.Invoke() to have it handled by another thread. This gives 'An event was unable to invoke any of the subscribers (Exception from HRESULT: 0x80040201)' (Yes, we're also subscribed to COM events of the ActiveX object).
  • use Dispatcher.PushFrame() to stop event handling from happening. This also fails.

这可能会奏效,但不知道如何实现这将是创建一个新的消息泵因为它可以被配置为暂时只处理RPC调用的WPF消息泵一个大胆的想法。这是沿http://jmorrill.hjtcentral.com/Home/tabid/428/EntryId/430/WPF-MediaKit-Updates.aspx ,但还是有些不同的,从这种情况。

A wild idea which might work, but don't know how to implement this would be creating a new message pump as the WPF message pump which can be configured to temporarily handle only RPC calls. This is along the lines of http://jmorrill.hjtcentral.com/Home/tabid/428/EntryId/430/WPF-MediaKit-Updates.aspx , but still somewhat different from this situation.

所以,问题归结为如何才能使的ActiveX调用同步像我们期望它已经成为取代异步?

So the question boils down to how can we make ActiveX call synchronously like we expected it already to be instead of async?

更新

为了更清楚所涉及的机制,不仅是鼠标事件,但更普遍的问题的新处理事件,而旧的正在执行',我再举一个例子用的堆栈跟踪

To make it more clear that the mechanism involved is not only about mouse-events, but the more generic problem of 'a new event is handled while the old is being executed', I'll give another example with a stack trace:

环境:我们有一个WPF网格上我们得到一个鼠标点击(Grid_MouseDown),这样我们就能把我们执行该方法的ActiveX对象CloseShelf。打开货架是需要时间的,所以我们订阅了该事件的EventShelfClosed,这在EventShelfClosed的事件处理程序将调用ListShelf'知道哪些杂志架系列都离开了。

Context: we've got a WPF Grid on which we get a mouseclick (Grid_MouseDown), we've got an ActiveX object on which we perform the method 'CloseShelf'. Opening the shelf will take time, so we are subscribed to the event 'EventShelfClosed', which in the event handler of EventShelfClosed will call 'ListShelf' to know which shelfs are left.

这是怎么管理的堆栈跟踪看起来像(汉斯要求非托管堆栈跟踪,但我不知道如何让一个):

This is how the managed stack trace looks like (Hans asked for an unmanaged stacktrace, but I don't know how to get one):

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]  

所发生的是在该方法'CloseShelf'将关闭的架子,但在这种情况下,CloseShelf',该事件'EventShelfClosed'被的调用CloseShelf期间发出和处理的是如此之快。现在CloseShelf会调用ListShelfs,但ListShelfs将失败,因为ActiveX组件被锁定返回null由CloseShelf电话这仍然是积极的。

What happens is the the method 'CloseShelf' will close the shelf, but in this case 'CloseShelf' is so fast that the event 'EventShelfClosed' is emitted and handled during the call to CloseShelf. Now CloseShelf will call ListShelfs, but ListShelfs will fail and return null because the ActiveX component is locked by the 'CloseShelf' call which is still active.

为什么这是一个问题吗? Becasue程序员不指望一个方法调用是异步。这创造了大量项目,这意味着现在auditting意外行为的所有来电之后我们袭来。

Why is this a problem? Becasue the programmer does not expect a method call to be async. This hit us after creating a large program, which now means auditting all calls for unexpected behaviour.

什么,我们看到在这种情况下?我们希望看到,CloseShelf的回报,而不在通话过程中处理其他事件。该方法应该是同步的,并且在主(非递归)消息循环期间处理的任何挂起事件

What would we like to see in this case? We would like to see that 'CloseShelf' returns without handling other events during the call. The method should be synchronous, and any pending events handled during the main (non-recursive) message loop.

有一个锁之类的布尔不会有所帮助,因为我们将在这里失踪的事件,它锁定了应用程序。

Having a 'lock' kind of boolean won't help here, since we would be missing events here, which locks up the application.

推荐答案

< tldr>试code以下的尝试3。有没有在电视上,当我坐下来写该< / tldr>

谢谢你的澄清!我看到有这里只有一个线程;而且,由于CloseShelf帧仍然是在栈上,它看起来像在COM调用实际上阻止。

Thanks for the clarification! I see that there is only one thread here; and also, since the CloseShelf frame is still on the stack, it looks like the COM call is actually blocking.

从堆栈跟踪,它看起来像COM对象调用的GetMessage或的PeekMessage API(或者如果它是VB6,的DoEvents或类似的),这将检查它的消息队列和处理消息 - 它是否会造成重的irregaurdless入特性。 AKA'抽消息队列' - 但如果它使用的PeekMessage如果没有消息时,它不会阻止,但仍然会执行的消息。在您的stacktrack这些电话可以隐于无形本地部分。堆栈跟踪实际证明,不知何故,COM调用回调到code!它看起来像你意识到这一点。如果这是一个有点云里雾里一些读者这里有几个链接:

From the stack trace, it looks like the com object is calling GetMessage or PeekMessage API (or if it's VB6, DoEvents or similar) which will check the message queue and PROCESS messages on it - irregaurdless of if it will cause re-entrancy. AKA 'pumping the message queue' - but if it uses peekmessage, it won't block if there are no messages, but will still execute the messages. In your stacktrack these calls could be hidden in the invisible native part. The stack trace actually proves that somehow, the COM call is calling back into your code! It looks like you are aware of this. If it's a bit foggy for some reader here are a couple links:

http://discuss.joelonsoftware.com/default.asp?joel。 3.456478.15
http://en.wikipedia.org/wiki/Event_loop

协同多任务处理(整个操作系统就像win3.0一个消息循环): 的http://en.wikipedia.org/wiki/Computer_multitasking#Cooperative_multitasking.2Ftime-sharing

Cooperative multitasking (one message loop for the whole OS like win3.0): http://en.wikipedia.org/wiki/Computer_multitasking#Cooperative_multitasking.2Ftime-sharing

该帖自愿割让的时间......实际上是通过这些调用来完成。它仍然发生的时间每个Windows应用程序的GUI线程上!

The quote 'voluntarily ceded time...' is actually done by these calls. And it STILL happens all the time on the GUI thread of every windows app!

由于实际调用在COM,如果你不能编辑它,你仍然有它周围code。这可能是仅此一方法(希望)。

Since the actual call is in the COM, if you can't edit it, you still have to code around it. It is probably just this one method (hopefully).

有时候,程序和组件检查目的的消息循环,到时候让事情作出回应,或重画屏幕。就像这张海报是试图做:

Sometimes programs and components check the message loop on purpose, to times to allow things to respond, or repaint the screen. Just like this poster is trying to do:

是否有类似的PeekMessage功能不处理消息?

关于该系统还可以处理内部事件该帖在这里buring你。

The quote about 'The system may also process internal events' is buring you here.

请注意,您说程序员没有想到一个方法调用是异步' - 它不是异步,或堆栈跟踪看起来不同。这是递归回到你的code。作为一个老Win3.1的程序员,这是我们处理与系统中的每一个应用程序!

note where you say 'the programmer does not expect a method call to be async' - it isn't async, or the stack trace would look different. it's 'recursing' back into your code. As an old Win3.1 programmer this was the hell we dealt with for EVERY app in the system!

我发现链接使用Google的检查消息队列中的PeekMessage,而想看看是否获取/的PeekMessage,等等,也可以从处理消息pvented $ P $。你可以从这个机制的文档见...

I found that link googling for 'check the message queue peekmessage' while trying to see if Get/Peekmessage, etc. could be prevented from processing messages. You can see from this documention...

http://msdn.microsoft.com/en-us/library/windows/desktop/ms644943(v=vs.85).aspx

...在发送类似PM_QS_INPUT | PM_QS_PAINT将prevent的问题。不幸的是,因为你没有调用它,你不能改变它!我没有看到任何方式来设置一个标志,从后来的呼叫控制后续消息的处理。

...that sending something like PM_QS_INPUT | PM_QS_PAINT would prevent the problem. Unfortunately, since you aren't calling it, you can't change it! And I didn't see any way to set a flag to control subsequent message processing from later calls.

如果一个读者仍然困惑(如果没有跳过此code)...举个简单的例子,做一个VB WinForms应用程序,使一个按钮,然后双击它,并粘贴此code在 - (我说,VB,因为application.doevents是调用这个讨厌的消息队列检查),最方便的方式:

If a reader is still confused (if not skip this code) ... For a simple example, make a VB Winforms App and make one button and double click it and paste this code in - (I say VB because application.doevents is the handiest way to invoke this nasty message queue check):

    For i As Integer = 0 To 20
        Text = i.ToString
        System.Threading.Thread.Sleep(100)
        Application.DoEvents()
    Next

现在单击按钮。请注意,您可以放大窗口和背景重绘 - 为的DoEvents允许这些事件发生通过检查消息队列(REM出的DoEvents,它会等待,直到计数完成;也可以尝试一下3倍,你会得到3在连续数)。

Now click the button. Note you can enlarge the window and the background repaints - as the doevents allows these events to happen by checking the message queue (REM out the doevents and it will 'wait' untill the count is done; also try clicking 3x and you will get 3 counts in a row).

现在......起脚。点击W / DOEVENTS不注释掉的按钮。点击按钮3次 - 在倒计时打断对方,然后恢复为previous之一结束。您可以暂停IDE,看到3点击调用堆栈。

NOW... the kicker. Click the button w/ Doevents NOT commented out. Click the button 3 times - the countdowns interrupt each other, then resume as the previous one finishes. You can Pause the IDE and see 3 click callstacks.

好吃!

我也看到你试过几件事情要被处理的停止消息或事件。如果code触发该EventShelfClosed获取调用的重入然而通过的PeekMessage或DOEVENTS造成的,可能不会影响它。

I also see that you tried a couple things to stop messages or events from being processed. If the code that fires the EventShelfClosed is getting called from the re-entrancy caused by a PeekMessage or 'Doevents', however, that may not effect it.

请注意这种做法有它的批评者: HTTP:/ /www.codinghorror.com/blog/2005/08/is-doevents-evil-revisited.html

Note this practice has it's detractors: http://www.codinghorror.com/blog/2005/08/is-doevents-evil-revisited.html

最好的办法是改变COM,因此它不会作出这样的检查的消息循环的任何API调用。

Best would be to change the COM so it doesn't make any API calls that are checking the message loop.

祝你好运!

另一种方式来改变这将是从事件控制EventShelfClosed移动,并称之为显式调用CloseShelf退出后(因为COM调用发生的真实情况)。不幸的是你的程序的建筑风格,可能不会允许没有这种弄脏pretty的车型主要变化和/或增加凝聚力和其他的东西。(不是时装模特提个醒: - )

Another way to change it would be to move from events controlling EventShelfClosed, and call it explicity after the call to CloseShelf is exited (since the com call is really happening). Unfortunately your program's architechture probably won't allow that without major changes and/or increased cohesion and other stuff that dirties pretty models (not fashion models mind you :-).

有一个不同的方法是做一个新的线程对象指向一个函数,使COM调用,然后启动它,然后加入,在希望类似的PeekMessage什么也不会找到一个消息泵上的新线程,因此没有干扰的东西。它看起来像一对夫妇,你试图参与这种类型的事情的。不幸的是如果COM偷窥消息,且有螺纹,KABOOM上没有消息泵。这可能会打击刚刚忽视的事情了吧。听起来这是发生了什么事。更进一步,如果COM依赖于只从GUI / messagepump线程访问其他项目,你是在跨线程调用的麻烦(这肯定会是这种情况如果COM与UI或任何UI对象进行交互)。

A different way would be to make a new thread object pointed to a function that makes the com invoke, then start it, then join it, in hopes that anything like PeekMessage would not find a message pump on the new thread and therefore not interfere with things. It looks like a couple of you attempts involved this type of thing. Unfortunately if the COM Peeks for messages, and there is no message pump on the thread, kaboom. It will probably blow up instead of just ignoring things. Sounds like that's what happened. Further more, if the COM relies on other items that should only be accessed from the GUI/messagepump thread, you're in trouble with cross-thread calls (which would certainly be the case if the COM interacts with the UI or any UI objects).

如果你不能阻止被选中,或prevent EventShelfClosed烧制而成,直到后来的消息队列,你没有选择,而是让EventShelfClosed被调用。但是你可以做的是导致它等待,然后摔倒在CloseShelf完成通。

If you can't stop the message queue from being checked, or prevent EventShelfClosed from firing until later, you have no choice but to let the EventShelfClosed get called. But what you can do is cause it to wait, and then fall thru when the CloseShelf is finished.

所以,你还必须有一个类级别的布尔字段设置/取消由CloseShelf将让EventShelfClosed知道它正在运行。

So you still have to have a class-level boolean field set/unset by CloseShelf so EventShelfClosed will know that it is running.

不幸的是只检查这一个while循环,即使是睡觉,会堵塞单线程你有,并冻结该应用程序。你可能只是尝试有EventShelfClosed再提高自身和退出功能,只要布尔设置;但由于停留的RaiseEvent的管理,请立即运行code和不检查该消息与一个计算器队列它将崩溃。如果你能弄清楚如何重新加注通过调用PostMessage的API(不SendMessage函数,运行它的时候了)EventShelfClosed - 这将继续把在GUI线程的消息队列多次的COM调用会使窗口检查。除非说COM等待队列为空的一些愚蠢的理由 - 另一个锁定。不推荐。

Unfortunately just checking this in a while loop, even with a sleep, will block the single thread you have, and freeze the app. You may just try to have EventShelfClosed re-raise itself and exit the function as long as the bool is set; but since RaiseEvent stays inside of managed, runs the code immediately, and does not check the message queue it will in crash with a stackoverflow. If you can figure out how to re-raise EventShelfClosed by calling the PostMessage API (not SendMessage, that runs it right away) - that will keep putting on the GUI thread's message queue as many times as the COM call will make windows check it. Unless said COM waits for the queue to be empty for some stupid reason - another lockup. Not recommending.

洙...你可以以火攻火。在这里,我采取另一条消息检查循环,让你的事件发生,而你的布尔清除等待。

Soo... You can fight fire with fire. Here I am implementing another message-checking loop to allow your events to happen while you wait for the bool to clear.

请注意,这是只打算在这一情况下解决这一问题。审核所有呼叫...这不是灵丹妙药。我的猜测是有没有。很凌乱,这是一个总的黑客攻击。

Note this is only going to fix this one problem in this one case. Auditing all calls ... this isn't a magic bullet. My guess is there is none. Very messy and this is a total hack.

这不是真正的尝试#3,它更像可能性#8。但是,我在我的老的回答中引用这一点,我懒得编辑。

It's not really attempt #3, it's more like possibility #8. But I referenced this in my old answer and am too lazy to edit that.

Boolean InCloseShelf
function CloseShelf(...)
    InCloseShelf=True;
    try
    {
         com call and all else
     }
     finally
         InCloseShelf=False

function EventShelfClosed(...
    while (InCloseShelf)
    {
         DoEvents
     }

现在当然没有在WPF中没有的DoEvents,它是在WinForms的'应用'。此博客已实施

Now of course there is no DoEvents in WPF, it's in winforms' 'application'. This blog has an implementation

http://dedjo.blogspot.com/ 2007/08 /如何到的DoEvents功能于wpf.html

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

未经检验的,当然!请注意,这是从修正的评论:

Untested, of course! Note this is from the correction in the comments:

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

或者,你总是可以参考WinForms和调用Application.DoEvents。

Or you could always reference WinForms and call Application.DoEvents.

我猜你已经知道这一点,但你是在一个糟糕的斑点现在。如果不这样做,祝你好运!如果你找到一个更好的办法,请更新帖子,因为,邪恶的黑客这样闹心,但现在你可以看到为什么人们说,有节制地检查消息队列!

I'm guessing you already know this, but you're in a bad spot right now. If this doesn't do it, Good luck! If you find a better way please update the post, because, well, evil hacks like this suck, but now you can see why people say to check the message queue sparingly!

这篇关于如何prevent重入WPF事件处理过程中的ActiveX方法调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆