Gewusst wie: Unterstützen von COM-Interop durch Anzeigen von Windows Forms in einem freigegebenen Thread

Aktualisiert: November 2007

Sie können Probleme mit der COM (Component Object Model-)Interoperabilität beheben, indem Sie das Formular in einer .NET Framework-Meldungsschleife anzeigen, die mit der Application.Run-Methode erstellt wird.

Damit ein Windows Form über eine COM-Clientanwendung ordnungsgemäß funktioniert, müssen Sie es in einer Windows Forms-Meldungsschleife ausführen. Verwenden Sie hierfür einen der folgenden Ansätze:

Im folgenden Codebeispiel wird veranschaulicht, wie Sie Windows Forms in einem neuen Thread mit freigegebener Meldungsschleife anzeigen.

Für dieses Feature wird in Visual Studio umfassende Unterstützung angeboten.

Beispiel

Das Anzeigen von Windows Forms in einem freigegebenen Thread ist vergleichbar mit der Vorgehensweise unter Gewusst wie: Unterstützen von COM-Interop durch das Anzeigen einzelner Windows Forms in einem eigenen Thread. Anstatt jedoch jedes Formular unter Verwendung einer eigenen Meldungsschleife in einem eigenen Thread anzuzeigen, erstellen Sie eine freigegebene Meldungsschleife, die nur für einen neuen Thread in der .NET Framework-Komponente ausgeführt wird.

Diese Vorgehensweise ist dem Verhalten einer Windows Forms-Standardanwendung etwas ähnlicher. So können Sie auch Ressourcen zwischen mehreren Formularen einfacher freigeben, da alle Formulare im selben Thread ausgeführt werden. Durch die Projektmappe in Gewusst wie: Unterstützen von COM-Interop durch das Anzeigen einzelner Windows Forms in einem eigenen Thread wird ein neuer Thread für jedes Formular erstellt. Diese Projektmappe erfordert zusätzlichen Code zur Threadsynchronisierung, damit Ressourcen zwischen verschiedenen Formularen freigegeben werden können.

Da das Anzeigen von Formularen in einem freigegebenen Thread eher dem Verhalten einer Windows Forms-Anwendung ähnelt, werden Sie beobachten, dass die Clientanwendung bei Verwendung von .NET Framework Windows Forms geschlossen wird, wenn die .NET Framework-Meldungsschleife angehalten wird. Dieses Verhalten tritt auf, wenn der Benutzer das Formular schließt, das als Hauptformular für ApplicationContext definiert wurde. Die Meldungsschleife wird über ApplicationContext gestartet.

In den folgenden Codebeispielen wird das Hauptformular von ApplicationContext auf das erste Formular festgelegt, das von der Clientanwendung geöffnet wird. Wenn der Benutzer diese Formularinstanz schließt, wird die .NET Framework-Meldungsschleife folglich beendet, und alle anderen Windows Forms werden geschlossen.

Imports System.Windows.Forms
Imports System.Runtime.InteropServices

<ComClass(COMForm.ClassId, COMForm.InterfaceId, COMForm.EventsId)> _
Public Class COMForm

#Region "COM GUIDs"
    ' These  GUIDs provide the COM identity for this class 
    ' and its COM interfaces. If you change them, existing 
    ' clients will no longer be able to access the class.
    Public Const ClassId As String = "9322c6dd-2738-428b-ba89-414ce2ea1941"
    Public Const InterfaceId As String = "210f5b8e-296a-4e26-bd7b-cd2cffa43389"
    Public Const EventsId As String = "f25c0ebb-2a2e-42b5-bf20-4bb84989a7da"
#End Region

    ' A creatable COM class must have a Public Sub New() 
    ' with no parameters, otherwise, the class will not be 
    ' registered in the COM registry and cannot be created 
    ' via CreateObject.
    Public Sub New()
        MyBase.New()
    End Sub

    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

End Class
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
Imports System.Windows.Forms

Public Class Form1
    Inherits System.Windows.Forms.Form

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        MessageBox.Show("Clicked button")
    End Sub

    'Form overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing AndAlso components IsNot Nothing Then
            components.Dispose()
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        Me.TextBox1 = New System.Windows.Forms.TextBox
        Me.TextBox2 = New System.Windows.Forms.TextBox
        Me.TextBox3 = New System.Windows.Forms.TextBox
        Me.Button1 = New System.Windows.Forms.Button
        Me.SuspendLayout()
        '
        'TextBox1
        '
        Me.TextBox1.Location = New System.Drawing.Point(12, 12)
        Me.TextBox1.Name = "TextBox1"
        Me.TextBox1.Size = New System.Drawing.Size(100, 20)
        Me.TextBox1.TabIndex = 0
        '
        'TextBox2
        '
        Me.TextBox2.Location = New System.Drawing.Point(12, 38)
        Me.TextBox2.Name = "TextBox2"
        Me.TextBox2.Size = New System.Drawing.Size(100, 20)
        Me.TextBox2.TabIndex = 1
        '
        'TextBox3
        '
        Me.TextBox3.Location = New System.Drawing.Point(12, 64)
        Me.TextBox3.Name = "TextBox3"
        Me.TextBox3.Size = New System.Drawing.Size(100, 20)
        Me.TextBox3.TabIndex = 2
        '
        'Button1
        '
        Me.Button1.Location = New System.Drawing.Point(12, 90)
        Me.Button1.Name = "Button1"
        Me.Button1.Size = New System.Drawing.Size(75, 23)
        Me.Button1.TabIndex = 3
        Me.Button1.Text = "Button1"
        Me.Button1.UseVisualStyleBackColor = True
        '
        'Form1
        '
        Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
        Me.ClientSize = New System.Drawing.Size(130, 138)
        Me.Controls.Add(Me.Button1)
        Me.Controls.Add(Me.TextBox3)
        Me.Controls.Add(Me.TextBox2)
        Me.Controls.Add(Me.TextBox1)
        Me.Name = "Form1"
        Me.Text = "Form1"
        Me.ResumeLayout(False)
        Me.PerformLayout()

    End Sub
    Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
    Friend WithEvents TextBox2 As System.Windows.Forms.TextBox
    Friend WithEvents TextBox3 As System.Windows.Forms.TextBox
    Friend WithEvents Button1 As System.Windows.Forms.Button
End Class

Kompilieren des Codes

  • Kompilieren Sie die Typen COMForm, Form1 und FormManager in eine Assembly mit dem Namen COMWinform.dll. Registrieren Sie die Assembly für COM-Interop, indem Sie eine der unter Verpacken einer Assembly für COM beschriebenen Methoden verwenden. Sie können die Assembly jetzt mit der zugehörigen Typbibliothekdatei (.tlb) in nicht verwalteten Anwendungen einsetzen. Beispielsweise können Sie die Typbibliothek als Verweis in einem ausführbaren Visual Basic 6.0-Projekt verwenden.

Siehe auch

Aufgaben

Gewusst wie: Unterstützen von COM-Interop durch Anzeigen eines Windows Forms mit der ShowDialog-Methode

Gewusst wie: Unterstützen von COM-Interop durch das Anzeigen einzelner Windows Forms in einem eigenen Thread

Konzepte

Verfügbarmachen von .NET Framework-Komponenten in COM

Verpacken einer Assembly für COM

Registrieren von Assemblys mit COM

Übersicht über Windows Forms und nicht verwaltete Anwendungen