Condividi tramite


Procedura: supportare l'interoperabilità COM mediante la visualizzazione di Windows Form in un thread condiviso

Aggiornamento: novembre 2007

È possibile risolvere i problemi di interoperabilità di Component Object Model (COM) visualizzando il form in un ciclo di messaggi .NET Framework, creato utilizzando il metodo Application.Run.

Per fare in modo che un Windows Form funzioni correttamente da un'applicazione client COM, è necessario eseguirlo in un ciclo di messaggi Windows Form. A questo scopo, adottare uno degli approcci seguenti:

Nel codice riportato di seguito viene illustrato come visualizzare Windows Form in un nuovo thread con un ciclo di messaggi condiviso.

In Visual Studio è disponibile un supporto completo per questa funzione.

Esempio

La visualizzazione di Windows Form in un thread condiviso è simile all'approccio illustrato in Procedura: supportare l'interoperabilità COM mediante la visualizzazione di ogni Windows Form nel relativo thread, con la differenza che, invece di visualizzare ciascun form nel relativo thread utilizzando il ciclo di messaggi, viene creato un ciclo di messaggi condiviso, eseguito in un solo nuovo thread nel componente .NET Framework.

Questo approccio rappresenta in modo più preciso il comportamento di un'applicazione Windows Form standard. Inoltre, semplifica la condivisione delle risorse tra più form, in quanto tutti i form vengono eseguiti nello stesso thread. La soluzione illustrata in Procedura: supportare l'interoperabilità COM mediante la visualizzazione di ogni Windows Form nel relativo thread consente la creazione di un nuovo thread per ciascun form. Tale soluzione richiede una quantità di codice aggiuntivo per la sincronizzazione del thread per condividere le risorse tra form diversi.

Poiché la visualizzazione di form in un thread condiviso è analoga al comportamento di un'applicazione Windows Form, con i Windows Form .NET Framework l'applicazione client viene chiusa quando il ciclo di messaggi .NET Framework termina. Questo comportamento si determina quando l'utente chiude il form progettato come form principale per la classe ApplicationContext. La classe ApplicationContext viene utilizzata per avviare il ciclo di messaggi.

Negli esempi di codice riportati di seguito, il form principale della classe ApplicationContext è impostato sul primo form aperto dall'applicazione client. Quando l'utente chiude l'istanza di tale form, il ciclo di messaggi .NET Framework viene chiuso e vengono chiusi tutti gli altri Windows Form.

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

Compilazione del codice

  • Compilare i tipi COMForm, Form1 e FormManager in un assembly denominato COMWinform.dll. Registrare l'assembly per l'interoperabilità COM utilizzando uno dei metodi descritti in Preparazione di un assembly per COM. A questo punto è possibile utilizzare l'assembly e il file della libreria dei tipi (TLB) corrispondente nelle applicazioni non gestite. Ad esempio, è possibile utilizzare la libreria dei tipi come riferimento in un progetto eseguibile di Visual Basic 6.0.

Vedere anche

Attività

Procedura: supportare l'interoperabilità COM visualizzando un Windows Form con il metodo ShowDialog

Procedura: supportare l'interoperabilità COM mediante la visualizzazione di ogni Windows Form nel relativo thread

Concetti

Esposizione di componenti .NET Framework a COM

Preparazione di un assembly per COM

Registrazione di assembly presso COM

Cenni preliminari su Windows Form e applicazioni non gestite