C# 使用.NET类库显示表单以响应COM事件会冻结表单
我正在创建一个.NET组件,用于简化与支持COM自动化接口的应用程序的工作。该组件的一个特性是,它将显示对话框以从用户处收集信息。但是,在我需要使用的工作流中,对话框部分显示,然后冻结 我用Excel创建了一个简化版。实际上,我正在使用另一个带有COM自动化接口的应用程序,但这个问题是一个一般性问题,可以使用Excel重现,这是大多数人应该有的。下面是对各种作品的简要描述。我用VisualBasic写这篇文章,但假设C会出现同样的问题,尽管我还没有测试过 .NET类库项目“MyCmd” 类别MyDef 创建MyDef类的实例时,它将获取并保存当前活动的Excel工作表。它还侦听Excel工作表对象的SelectionChange事件。作为对SelectionChange事件的响应,它创建一个“MyCmd”类的实例(如下所述),并触发一个自定义CommandCreated事件,传递创建的MyCmd对象 它还支持一个名为ForceEvent的公共方法,该方法的作用与SelectionChange事件相同。这用于使用不同的代码路径测试功能C# 使用.NET类库显示表单以响应COM事件会冻结表单,c#,.net,vb.net,com,C#,.net,Vb.net,Com,我正在创建一个.NET组件,用于简化与支持COM自动化接口的应用程序的工作。该组件的一个特性是,它将显示对话框以从用户处收集信息。但是,在我需要使用的工作流中,对话框部分显示,然后冻结 我用Excel创建了一个简化版。实际上,我正在使用另一个带有COM自动化接口的应用程序,但这个问题是一个一般性问题,可以使用Excel重现,这是大多数人应该有的。下面是对各种作品的简要描述。我用VisualBasic写这篇文章,但假设C会出现同样的问题,尽管我还没有测试过 .NET类库项目“MyCmd” 类别My
Imports Microsoft.Office.Interop.Excel
Public Class MyDef
Private WithEvents _sheet As Microsoft.Office.Interop.Excel.Worksheet
Public Event CommandCreated(ByVal command As MyCmd)
Public Sub New()
_sheet = g_app.ActiveSheet
End Sub
Public Sub ForceEvent()
Dim cmd As New MyCmd()
RaiseEvent CommandCreated(cmd)
End Sub
Private Sub _sheet_SelectionChange(Target As Range) Handles _sheet.SelectionChange
Me.ForceEvent()
End Sub
End Class
类MyCmd
包含单个方法的类,该方法创建并显示对话框的新实例
Public Class MyCmd
Private _form As Form = Nothing
Public Sub ShowDialog()
If _form Is Nothing Then
_form = New CommandDialog
End If
_form.Visible = True
End Sub
End Class
我正在测试的对话框是一个表单(名为“CommandDialog”),有几个按钮,后面没有代码。我只是测试表单是否按预期显示
Public Module Globals
Private _app As Microsoft.Office.Interop.Excel.Application = Nothing
Public Function g_app() As Object
If Not _app Is Nothing Then
Return _app
Else
Try
_app = GetObject(, "Excel.Application")
Catch ex As Exception
MsgBox("Excel must be running")
Return Nothing
End Try
Return _app
End If
End Function
End Module
全局模块
我还使用下面的代码连接到Excel并获取Excel应用程序对象。它按预期工作
Public Module Globals
Private _app As Microsoft.Office.Interop.Excel.Application = Nothing
Public Function g_app() As Object
If Not _app Is Nothing Then
Return _app
Else
Try
_app = GetObject(, "Excel.Application")
Catch ex As Exception
MsgBox("Excel must be running")
Return Nothing
End Try
Return _app
End If
End Function
End Module
.NET类库项目“MyCmd”
我还有另一个名为“CommandFramework”的项目,它使用MyCmd类库。第二个项目是一个Windows窗体应用程序,它包含两个按钮和一个事件处理程序,用于MyDef支持的CommandCreated事件。整个代码如下
单击第一个按钮时,它将创建MyDef类的实例,由于WithEvent声明,该类还将为CommandCreated事件设置事件处理程序。单击第二个按钮调用ForceEvent方法,该方法将触发CommandCreated事件并显示对话框。我还可以单击活动Excel工作表中的一个单元格,这会导致触发SelectionChange事件并导致调用ForceEvent方法。但是,在这种情况下,对话框仅部分显示并被冻结。除了单击第二个按钮或接收Excel SelectionChange事件的触发器外,代码流是相同的。第二种情况导致冻结
Public Class CommandFramework
Private WithEvents _cmdDef As MyCommand.MyDef = Nothing
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
_cmdDef = New MyCommand.MyDef
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
_cmdDef.ForceEvent()
End Sub
Private Sub cmdDef_CommandCreated(command As MyCommand.MyCmd) Handles _cmdDef.CommandCreated
command.ShowDialog()
End Sub
End Class
有人知道问题的原因是什么,解决方案是什么吗?我猜这是某种线程问题,出现了死锁,但我对这方面的了解还不够,甚至无法开始诊断或尝试其他方法。尝试继承所有处理COM事件的类: 这将确保在最初创建.NET对象的同一线程(可能是主STA线程)上接收事件回调。看看这是否有帮助 注意,它仍然可能容易出现死锁或重入问题,这是COM编组的典型问题,特别是当您从Excel事件处理程序调用另一个Excel API时。理想情况下,您应该尽快从事件处理程序返回,并可能异步处理需要执行的任何操作,例如
SynchronizationContext.Current.Post()
或wait Task.Yield()
。但是,对于异步场景,您仍然应该关心可能的可重入性,例如,当您仍在处理上一次调用时,事件处理程序再次被调用
我有一些相关的问答: