Comment : prendre en charge l'interopérabilité COM en affichant les Windows Forms sur un thread partagé
Vous pouvez résoudre les problèmes d'interopérabilité COM (Component Object Model) en affichant votre formulaire sur une boucle de message .NET Framework, qui est créée en utilisant la méthode Application.Run.
Pour qu'un Windows Form fonctionne correctement à partir d'une application cliente COM, vous devez l'exécuter sur une boucle de message Windows Forms. Pour cela, utilisez l'une des approches suivantes :
Utilisez la méthode Form.ShowDialog pour afficher le Windows Form. Pour plus d'informations, consultez Comment : prendre en charge COM Interop en affichant un Windows Form avec la méthode ShowDialog.
Affichez chaque Windows Form sur un nouveau thread. Pour plus d'informations, consultez Comment : prendre en charge l'interopérabilité COM en affichant chaque Windows Form sur son propre thread.
Créez une boucle de message partagée sur un nouveau thread dans le composant .NET Framework.
L'exemple de code suivant montre comment afficher les Windows Forms sur un nouveau thread avec une boucle de message partagée.
Il existe une prise en charge étendue pour cette fonctionnalité dans Visual Studio.
Exemple
Afficher des Windows Forms sur un thread partagé est semblable à l'approche décrite dans Comment : prendre en charge l'interopérabilité COM en affichant chaque Windows Form sur son propre thread. Toutefois, au lieu d'afficher chaque formulaire sur son propre thread à l'aide de sa propre boucle de message, vous créez une boucle de message partagée qui s'exécute uniquement sur un nouveau thread dans le composant .NET Framework.
Cette approche représente plus exactement le comportement d'une application Windows Forms standard. Il vous simplifie également le partage des ressources entre plusieurs formulaires, car tous les formulaires s'exécutent sur le même thread. La solution dans Comment : prendre en charge l'interopérabilité COM en affichant chaque Windows Form sur son propre thread crée un nouveau thread pour chaque formulaire. Cette solution requiert un code de synchronisation de threads supplémentaire pour partager les ressources entre les différents formulaires.
Étant donné que l'affichage de formulaires sur un thread partagé ressemble plus au comportement d'une application Windows Forms, vous verrez qu'avec les Windows Forms .NET Framework l'application cliente se fermera à l'arrêt de la boucle de message .NET Framework. Ce comportement se produit lorsque l'utilisateur ferme le formulaire qui est désigné comme formulaire principal pour le ApplicationContext. ApplicationContext est utilisé pour démarrer la boucle de message.
Dans les exemples de code suivants, le formulaire principal de ApplicationContext a pour valeur le premier formulaire que l'application cliente ouvre. Par conséquent, lorsque l'utilisateur ferme cette instance de formulaire, la boucle de message .NET Framework prend fin et tous les autres Windows Forms ferment.
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
Compilation du code
- Compilez les types COMForm, Form1 et FormManager dans un assembly appelé COMWinform.dll. Enregistrez l'assembly pour COM Interop en utilisant l'une des méthodes décrites dans Empaquetage d'un assembly pour COM. Vous pouvez maintenant utiliser l'assembly et son fichier bibliothèque de types (.tlb) correspondant dans des applications non managées. Par exemple, vous pouvez utiliser la bibliothèque de types en tant que référence dans un projet exécutable Visual Basic 6.0.
Voir aussi
Tâches
Comment : prendre en charge COM Interop en affichant un Windows Form avec la méthode ShowDialog
Concepts
Exposition de composants .NET Framework à COM
Empaquetage d'un assembly pour COM
Inscription d'assemblys dans COM
Vue d'ensemble des applications Windows Forms et non managées