方法 : 独自のスレッドで各 Windows フォームを表示して COM 相互運用機能をサポートする
更新 : 2007 年 11 月
COM 相互運用機能に関する問題は、フォームを .NET Framework メッセージ ループに表示することで解決できます。このメッセージ ループは、Application.Run メソッドを使って作成します。
COM クライアント アプリケーションから Windows フォームを正しく動作させるには、フォームを Windows フォーム メッセージ ループで実行する必要があります。この場合、次のいずれかの方法を使用します。
Form.ShowDialog メソッドを使って、Windows フォームを表示する。詳細については、「方法 : ShowDialog メソッドで Windows フォームを表示して COM 相互運用機能をサポートする」を参照してください。
各 Windows フォームを別個のスレッドに表示する。
共有メッセージ ループを .NET Framework コンポーネントの新しいスレッドに作成する。詳細については、「方法 : 共有スレッド上に Windows フォームを表示することで COM 相互運用をサポートする」を参照してください。
Visual Studio では、この機能に対する広範なサポートが用意されています。
チュートリアル : Windows フォームを別個のスレッドに表示することによって COM 相互運用をサポートする
チュートリアル : Windows フォームを別個のスレッドに表示することによって COM 相互運用をサポートする
チュートリアル : Windows フォームを別個のスレッドに表示することによって COM 相互運用をサポートする
チュートリアル : Windows フォームを別個のスレッドに表示することによって COM 相互運用をサポートする
チュートリアル : Windows フォームを別個のスレッドに表示することによって COM 相互運用をサポートする
使用例
次のコード例は、独立したスレッドでフォームを表示し、Application.Run メソッドを呼び出して Windows フォームのメッセージ ポンプをそのスレッドで起動する方法を示しています。この方法を使用するには、Invoke メソッドを使用して、アンマネージ アプリケーションからフォームへのすべての呼び出しをマーシャリングする必要があります。
この方法では、フォームのインスタンスがそれぞれ独自のスレッドで、独自のメッセージ ループを使って実行されることが必要です。1 つのスレッドで複数のメッセージ ループを動作させることはできません。したがって、クライアント アプリケーションのメッセージ ループは変更できません。ただし、.NET Framework コンポーネントを変更して、その独自のメッセージ ループを使用する新しいスレッドを起動することは可能です。
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 = "1b49fe33-7c93-41ae-9dc7-8ac4d823286a"
Public Const InterfaceId As String = "11651e1f-6db0-4c9e-b644-dcb79e6de2f6"
Public Const EventsId As String = "7e61f977-b39d-47a6-8f34-f743c65ae3a3"
#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, 66)
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, 92)
Me.Button1.Name = "Button1"
Me.Button1.Size = New System.Drawing.Size(75, 23)
Me.Button1.TabIndex = 3
Me.Button1.Text = "Command"
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(132, 146)
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
コードのコンパイル方法
- COMForm、Form1、および FormManager の型を、COMWinform.dll という名前のアセンブリにコンパイルします。COM 用のアセンブリのパッケージ化 に解説されているメソッドの 1 つを使用して、アセンブリを COM 相互運用に登録します。これで、アセンブリとそれに対応する型ライブラリ (.tlb) ファイルをアンマネージ アプリケーションで使用できます。たとえば、Visual Basic 6.0 の実行可能プロジェクトで、型ライブラリを参照に使用することが可能です。
参照
処理手順
方法 : ShowDialog メソッドで Windows フォームを表示して COM 相互運用機能をサポートする
方法 : 共有スレッド上に Windows フォームを表示することで COM 相互運用をサポートする