逐步解說:在自己的執行緒上顯示每個 Windows Form 以支援 COM Interop

您可以在 .NET Framework 訊息迴圈上顯示表單,藉此解決 COM 的互通性問題,此訊息迴圈則是使用 Application.Run 方法所建立。

若要使 Windows Form 能從 COM 用戶端應用程式正確運作,您就必須在 Windows Form 訊息迴圈中執行 Windows Form。 若要這麼做,請使用下列其中一種方法:

下列程序示範如何在不同的執行緒上顯示 Windows Form。

若要將此主題中的程式碼複製為一份清單,請參閱 HOW TO:在自己的執行緒上顯示每個 Windows Form 以支援 COM Interop

程序

將表單放在不同的執行緒上然後呼叫 Application.Run 方法,以便在該執行緒上啟動 Windows Forms 的訊息。 若要使用這個方法,您必須從 Unmanaged 應用程式使用 Invoke 方法封送處理對表單的任何呼叫。

這個方式需要表單的每個執行個體使用自己的訊息迴圈在自己的執行緒上執行。 您不能讓每個執行緒上有超過一個以上的訊息迴圈。 因此,您不能變更用戶端應用程式的訊息迴圈。 然而,您可以修改 .NET Framework 元件以啟動使用專屬訊息迴圈的新執行緒。

若要在新執行緒上建立 Windows Form 的每個執行個體

  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 Form],然後按一下 [加入]。

  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 Interop 的專案。

若要建立示範 COM Interop 的可執行檔

  1. 啟動 Microsoft Visual Basic 6.0。

  2. 建立新的標準 EXE 專案。

  3. 在 [專案] 功能表上,按一下 [參考]。

  4. 將參考加入當建置 Visual Basic 2005 方案時產生的 COMWinform 型別程式庫。

    -或-

    如果在清單中看不到這個型別程式庫,請手動按一下 [瀏覽] 以尋找型別程式庫 (.tlb) 檔案。

  5. 在表單中加入一個按鈕。

  6. 在 [檢視] 功能表上按一下 [程式碼],然後將下列程式碼加入表單模組中。

[Visual Basic]

Option Explicit

Private Sub Command1_Click()
    Dim frm As COMWinform.COMForm
    Set frm = New COMWinform.COMForm
    frm.ShowForm1
End Sub
  1. 按一下 [檔案] 功能表上的 [建立執行檔],編譯專案。

  2. 執行已編譯的 Visual Basic 6.0 可執行檔。

  3. 按一下按鈕,從您稍早建立的類別庫顯示 Windows Form。

  4. 將焦點設定在 Windows Form 上的其中一個 TextBox 控制項,然後按 TAB 鍵在各個控制項之間移動。

    請注意,TAB 鍵會在控制項之間移動焦點。 此外,請注意,當您按 ENTER 鍵時會引發按鈕的 Click 事件。

請參閱

工作

HOW TO:顯示 Windows Form 和 ShowDialog 方法以支援 COM Interop

概念

將 .NET Framework 元件公開給 COM

封裝 COM 的組件

向 COM 註冊組件

Windows Form 和 Unmanaged 應用程式概觀