演练:在每个 Windows 窗体各自的线程上显示该 Windows 窗体自身以支持 COM Interop
更新:2007 年 11 月
可以通过在 .NET Framework 消息循环上显示窗体来解决 COM 互操作性问题,消息循环是使用 Application.Run 方法创建的。
若要使 Windows 窗体在 COM 客户端应用程序中正确工作,必须在 Windows 窗体消息循环上运行该窗体。为此,请执行下列方法之一:
使用 Form.ShowDialog 方法显示 Windows 窗体。有关更多信息,请参见 如何:通过使用 ShowDialog 方法显示 Windows 窗体来支持 COM Interop。
将每个 Windows 窗体显示在不同的线程上。
在 .NET Framework 组件的新线程上创建共享消息循环。有关更多信息,请参见 演练:通过在共享线程上显示 Windows 窗体支持 COM Interop。
下面的过程演示如何在单独的线程上显示 Windows 窗体。
若要将本主题中的代码作为一个单独的列表进行复制,请参见 如何:通过在每个 Windows 窗体各自的线程上显示该 Windows 窗体来支持 COM Interop。
过程
将窗体放置在一个单独线程上并调用 Application.Run 方法在该线程上启动 Windows 窗体消息泵。若要使用此方法,必须通过使用 Invoke 方法封送从非托管应用程序发出的对该窗体的任何调用。
此方法要求窗体的每个实例通过使用各自的消息循环在各自的线程上运行。一个线程上不能运行多个消息循环。因此,不能更改客户端应用程序的消息循环。但是,可以修改 .NET Framework 组件以启动一个使用其自己的消息循环的新线程。
在新线程上创建 Windows 窗体的每个实例
创建一个新的“类库”项目,并将其命名为 COMWinform。
删除默认的 Class1.vb 文件。
在“项目”菜单上单击“添加类”。
选择“COM 类”模板。
在“名称”框中,键入 COMForm.vb,然后单击“添加”。
将下面的代码语句粘贴在 COMForm 文件顶部类定义之前。
Imports System.Windows.Forms Imports System.Runtime.InteropServices
在 COMForm 类定义中,将下面的代码粘贴在构造函数定义之下。
Private WithEvents frmManager As FormManager Public Sub ShowForm1() ' Call the StartForm method by using a new instance ' of the Form1 class. StartForm(New Form1) End Sub Private Sub StartForm(ByVal frm As Form) ' This procedure is used to show all forms ' that the client application requests. When the first form ' is displayed, this code will create a new message ' loop that runs on a new thread. The new form will ' be treated as the main form. ' Later forms will be shown on the same message loop. If IsNothing(frmManager) Then frmManager = New FormManager(frm) Else frmManager.ShowForm(frm) End If End Sub Private Sub frmManager_MessageLoopExit() _ Handles frmManager.MessageLoopExit 'Release the reference to the frmManager object. frmManager = Nothing End Sub
在“项目”菜单中单击“添加类”并选择“类”模板。
在“名称”框中,键入 FormManager.vb,然后单击“添加”。
用下列代码替换 FormManager 文件的内容。
Imports System.Runtime.InteropServices Imports System.Threading Imports System.Windows.Forms <ComVisible(False)> _ Friend Class FormManager ' This class is used so that you can generically pass any ' form that you want to the delegate. Private WithEvents appContext As ApplicationContext Private Delegate Sub FormShowDelegate(ByVal form As Form) Event MessageLoopExit() Public Sub New(ByVal MainForm As Form) Dim t As Thread If IsNothing(appContext) Then appContext = New ApplicationContext(MainForm) t = New Thread(AddressOf StartMessageLoop) t.IsBackground = True t.SetApartmentState(ApartmentState.STA) t.Start() End If End Sub Private Sub StartMessageLoop() ' Call the Application.Run method to run the form on its own message loop. Application.Run(appContext) End Sub Public Sub ShowForm(ByVal form As Form) Dim formShow As FormShowDelegate ' Start the main form first. Otherwise, focus will stay on the ' calling form. appContext.MainForm.Activate() ' Create a new instance of the FormShowDelegate method, and ' then invoke the delegate off the MainForm object. formShow = New FormShowDelegate( _ AddressOf ShowFormOnMainForm_MessageLoop) appContext.MainForm.Invoke(formShow, New Object() {form}) End Sub Private Sub ShowFormOnMainForm_MessageLoop(ByVal form As Form) form.Show() End Sub Private Sub ac_ThreadExit( _ ByVal sender As Object, _ ByVal e As System.EventArgs) _ Handles appContext.ThreadExit appContext.MainForm.Dispose() appContext.MainForm = Nothing appContext.Dispose() appContext = Nothing RaiseEvent MessageLoopExit() End Sub End Class
在“项目”菜单中单击“添加 Windows 窗体”,然后单击“添加”。
双击“Button1”添加一个 Click 事件处理程序。
将下面的代码添加到 Click 事件处理程序中。
Private Sub Button1_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles Button1.Click MessageBox.Show("Clicked button") End Sub
生成解决方案。
此步骤还注册该项目以便在此计算机上进行 COM 互操作。
创建演示 COM 互操作 的可执行文件
启动 Microsoft Visual Basic 6.0。
创建新的标准 EXE 项目。
在“项目”菜单上,单击“引用”。
添加对生成 Visual Basic 2005 解决方案时生成的 COMWinform 类型库的引用。
- 或 -
如果在列表中看不到此项,请单击“浏览”手动查找该类型库 (.tlb) 文件。
将一个按钮添加到窗体中。
在“视图”菜单上单击“代码”,然后将下面的代码添加到窗体模块。
[Visual Basic]
Option Explicit
Private Sub Command1_Click()
Dim frm As COMWinform.COMForm
Set frm = New COMWinform.COMForm
frm.ShowForm1
End Sub
请参见
任务
如何:通过使用 ShowDialog 方法显示 Windows 窗体来支持 COM Interop
演练:通过在共享线程上显示 Windows 窗体支持 COM Interop