我正在创建一个.NET组件,我想使用它来简化与支持COM Automation接口的应用程序的工作。该组件的功能之一是它将显示对话框以从用户那里收集信息。但是,在我需要使用的工作流程中,对话框会部分显示,然后冻结。
我已经创建了我使用Excel所做的简化版本。我实际上正在使用具有COM Automation接口的另一个应用程序,但是该问题是一个普遍的问题,可以使用大多数人应该使用的Excel进行重现。这是各个部分的简要说明。我正在用Visual Basic编写此代码,但假定C#也会发生相同的问题,尽管我尚未对此进行测试。
.NET类库项目“ MyCmd”
MyDef类
创建MyDef类的实例时,它将获取并保存当前活动的Excel工作表。它还侦听Excel Sheet对象的SelectionChange事件。作为对SelectionChange事件的响应,它将创建“ MyCmd”类的一个实例(如下所述),并触发自定义CommandCreated事件,并传递创建的MyCmd对象。
它还支持一个名为ForceEvent的公共方法,该方法与SelectionChange事件具有相同的作用。这用于使用不同的代码路径来测试功能。
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”),上面有几个按钮,但后面没有代码。我只是测试看看表单是否按预期显示。
全局模块
我还使用下面的代码连接到Excel并获取Excel Application对象。它按预期工作。
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 Form App,其中包含两个按钮和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
有人对问题的根源和解决方案有任何想法吗?我的猜测是这是某种线程问题,并且存在僵局,但我对该领域了解不足,甚至无法开始诊断或尝试其他事情。
答案 0 :(得分:1)
尝试继承处理StandardOleMarshalObject
中的COM事件的所有类:
public class Page {
private int pageNum;
public Page(int pageNum) {
this.pageNumber = pageNum;
}
public int getPageNum() {
return pageNum;
}
这将确保您在最初创建.NET对象的同一线程(大概是主STA线程)上接收事件回调。看看是否有帮助。
请注意,它仍可能会出现死锁或重新入库问题,这是COM编组中的典型问题,特别是如果您从Excel事件处理程序中调用另一个Excel API。理想情况下,您应该尽快从事件处理程序中返回,并且可能使用Public Class MyDef
Inherits System.Runtime.InteropServices.StandardOleMarshalObject
...
Public Class CommandFramework
Inherits System.Runtime.InteropServices.StandardOleMarshalObject
或SynchronizationContext.Current.Post()
之类的方法异步处理需要完成的任何事情。但是在异步情况下,您仍然应该考虑可能的可重入性,例如当您仍在处理上一个调用时再次调用事件处理程序时。
我有一些相关的问题: