演练:通过在共享线程上显示 Windows 窗体支持 COM Interop

更新:2007 年 11 月

可以通过在 .NET Framework 消息循环上显示窗体来解决 COM 互操作性问题,消息循环是使用 Application.Run 方法创建的。

若要使 Windows 窗体在 COM 客户端应用程序中正确工作,必须在 Windows 窗体消息循环上运行该窗体。为此,请执行下列方法之一:

下面的过程演示如何使用共享消息循环在新线程上显示 Windows 窗体。

若要将本主题中的代码作为一个单独的清单进行复制,请参见 如何:通过在共享线程上显示 Windows 窗体来支持 COM Interop

过程

此方法类似 演练:在每个 Windows 窗体各自的线程上显示该 Windows 窗体自身以支持 COM Interop 中演示的方法。但是,此方法不是使用每个窗体各自的消息循环在各自的线程上显示窗体自身,而是创建一个仅在 .NET Framework 组件中的新线程上运行的共享消息循环。

此方法更加准确地表示标准 Windows 窗体应用程序的行为。此方法还使在多个窗体间共享资源更加容易,因为所有窗体运行在同一线程上。演练:在每个 Windows 窗体各自的线程上显示该 Windows 窗体自身以支持 COM Interop 中的解决方案为每个窗体创建一个新线程。该解决方案需要额外线程同步代码以在不同窗体间共享资源。

因为此方法更类似于 Windows 窗体应用程序的行为,所以您将看到客户端应用程序打开的 .NET Framework Windows 窗体将在 .NET Framework 消息循环停止时关闭。当用户关闭指定为 ApplicationContext 的主窗体的窗体时会发生此行为。ApplicationContext 用于开始消息循环。

在下面的代码示例中,ApplicationContext 的主窗体设置为客户端应用程序打开的第一个窗体。因此,当用户关闭该窗体实例时,.NET Framework 消息循环退出,并且所有其他 Windows 窗体将关闭。

在新线程上创建一个共享消息循环供所有窗体使用

  1. 创建一个新的“类库”项目,并将其命名为 COMWinform。

  2. 删除默认的 Class1.vb 文件。

  3. 在“项目”菜单上单击“添加类”。

  4. 选择“COM 类”模板。

  5. 在“名称”框中,键入 COMForm.vb,然后单击“添加”。

  6. 将下面的代码语句粘贴在 COMForm 文件顶部类定义之前。

    Imports System.Windows.Forms
    Imports System.Runtime.InteropServices
    
  7. 在 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
    
  8. 在“项目”菜单中单击“添加类”并选择“类”模板。

  9. 在“名称”框中,键入 FormManager.vb,然后单击“添加”。

  10. 用下列代码替换 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
    
  11. 在“项目”菜单中单击“添加 Windows 窗体”,然后单击“添加”。

  12. 将一些 TextBox 控件和一个 Button 控件添加到窗体中。

  13. 双击“Button1”添加一个 Click 事件处理程序。

  14. 将下面的代码添加到 Click 事件处理程序中。

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        MessageBox.Show("Clicked button")
    End Sub
    
  15. 生成解决方案。

    此步骤还注册该项目以便在此计算机上进行 COM 互操作。

创建演示 COM 互操作 的可执行文件

  1. 启动 Microsoft Visual Basic 6.0。

  2. 创建新的标准 EXE 项目。

  3. 在“项目”菜单上,单击“引用”。

  4. 添加对生成 Visual Basic 2005 解决方案时生成的 COMWinform 类型库的引用。

    - 或 -

    如果在列表中看不到此项,请单击“浏览”手动查找该类型库 (.tlb) 文件。

  5. 将一个按钮添加到窗体中。

  6. 在“视图”菜单上单击“代码”,然后将下面的代码添加到窗体模块。

Option Explicit

Private Sub Command1_Click()
    Dim frm As COMWinform.COMForm
    Set frm = New COMWinform.COMForm
    frm.ShowForm1
End Sub
  1. 在“文件”菜单上单击“生成 EXE”来编译此项目。

  2. 运行已编译的 Visual Basic 6.0 可执行文件。

  3. 单击此按钮以显示先前创建的类库中的 Windows 窗体。

  4. 将焦点设置到此 Windows 窗体中的一个 TextBox 控件上,然后按 Tab 键在控件之间切换。

    注意 Tab 键将焦点从一个控件移动到另一个控件。还请注意,按 Enter 键后将引发按钮的 Click 事件。

请参见

任务

如何:通过使用 ShowDialog 方法显示 Windows 窗体来支持 COM Interop

演练:在每个 Windows 窗体各自的线程上显示该 Windows 窗体自身以支持 COM Interop

概念

向 COM 公开 .NET Framework 组件

将 COM 的程序集打包

向 COM 注册程序集

Windows 窗体和非托管应用程序概述