Compartilhar via


Como: Oferecer suporte a interoperabilidade COM exibindo Windows Forms em um thread compartilhado

Você pode resolver problemas de Interoperabilidade do Component Object Model (COM) exibindo o Formulário do Windows em um loop de mensagem .NET Framework, que é criado usando o método Application.Run.

Para fazer um Formuláriodo do Windows funcionar corretamente de um aplicativo cliente COM, você deve executá-lo em um loop de mensagem dos Formulários do Windows.Para fazer isso, use uma das seguintes abordagens:

O código exemplo a seguir demonstra como exibir Formulários do Windows em um novo thread com um loop de mensagem compartilhado.

Há suporte abrangente para este recurso no Visual Studio.

Exemplo

Exibir Formulários do Windows em um thread compartilhado é semelhante à abordagem mostrada em Como: Suporte interoperabilidade COM, exibindo cada Windows formulário em seu próprio thread.No entanto, em vez de exibir cada formulário em seu próprio thread usando seu próprio loop de mensagens, você cria um loop de mensagens compartilhado que é executado em apenas um novo thread no componente .NET Framework.

Essa abordagem representa com mais precisão o comportamento de um aplicativo padrão de Formulários do Windows.Ele também torna mais fácil para você compartilhar recursos entre vários formulários, porque todos os formulários são executados no mesmo segmento.A solução Como: Suporte interoperabilidade COM, exibindo cada Windows formulário em seu próprio thread cria um novo thread para cada formulário.Essa solução requer código de sincronização de threads adicional para compartilhar recursos entre diferentes formulários.

Como exibir formulários em um thread compartilhado é mais semelhante ao comportamento de um aplicativo de Formulários do Windows, você verá que com Formulários do Windows .NET Framework o aplicativo cliente será fechado quando o loop de mensagem .NET Framework para.Esse comportamento ocorre quando o usuário fecha o formulário designado como o formulário principal para o ApplicationContext.O ApplicationContext é usado para iniciar o loop de mensagens.

Nos códigos exemplo a seguir, o formulário principal do ApplicationContext é definido como o primeiro formulário que o aplicativo cliente abre.Portanto, quando o usuário fechar esta instância do formulário, o loop de mensagens .NET Framework termina e todas os outros Formulários do Windows serão fechados.

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

Compilando o código

  • Compile os tipos COMForm, Form1 e FormManager em um conjunto de módulos (assemblys) denominado COMWinform.dll.Registre o conjunto de módulos (assemblys) para interoperabilidade COM usando um dos métodos descritos em Empacotando um assembly para COM.Agora você pode usar o conjunto (assembly) e o arquivo de biblioteca de tipos (.tlb) correspondente em aplicativos não gerenciados.Por exemplo, você pode usar a biblioteca de tipos como uma referência em um projeto executável Visual Basic 6.0.

Consulte também

Tarefas

Como: Suporte interoperabilidade COM, exibindo um formulário do Windows com o método ShowDialog

Como: Suporte interoperabilidade COM, exibindo cada Windows formulário em seu próprio thread

Conceitos

Expor o .NET Framework Components to COM

Empacotando um assembly para COM

Registrar assemblies com

Visão Geral de Aplicativos de Formulários do Windows e Aplicativos Não Gerenciados