방법: 각 Windows Form을 별개의 스레드에서 표시하여 COM Interop 지원
Application.Run 메서드를 사용하여 만들 수 있는 폼을 .NET Framework 메시지 루프에 표시하여 COM 상호 운용성 문제를 해결할 수 있습니다.
Windows Form이 COM 클라이언트 애플리케이션에서 제대로 작동하게 하려면 Windows Forms 메시지 루프에서 폼을 실행해야 합니다. 이렇게 하려면 다음 접근 방식 중 하나를 사용합니다.
Form.ShowDialog 메서드를 사용하여 Windows Form을 표시합니다. 자세한 내용은 방법: ShowDialog 메서드로 Windows Form을 표시하여 COM Interop 지원을 참조하세요.
각 Windows Form을 별도 스레드에 표시합니다.
Visual Studio에서는 이 기능을 광범위하게 지원합니다.
연습: 각 Windows Form을 별개의 스레드에서 표시하여 COM Interop 지원을 참조하세요.
예제
다음 코드 예제에서는 별도 스레드에서 폼을 표시하고 Application.Run 메서드를 호출하여 해당 스레드에서 Windows Forms 메시지 펌프를 시작하는 방법을 보여 줍니다. 이 접근 방식을 사용하려면 Invoke 메서드를 사용하여 관리되지 않는 애플리케이션의 폼 호출을 모두 마샬링해야 합니다.
이 접근 방법을 사용하려면 폼의 각 인스턴스가 고유한 메시지 루프를 통해 고유한 스레드에서 실행되어야 합니다. 스레드당 둘 이상의 메시지 루프 실행을 가질 수 없습니다. 따라서 클라이언트 애플리케이션의 메시지 루프를 변경할 수 없습니다. 그러나 .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
이라는 어셈블리로 컴파일합니다. Packaging an Assembly for COM에 설명된 방법 중 하나를 사용하여 COM interop에 대한 어셈블리를 등록합니다. 이제 관리되지 않는 애플리케이션에서 어셈블리 및 해당 형식 라이브러리(.tlb) 파일을 사용할 수 있습니다. 예를 들어 Visual Basic 6.0 실행 프로젝트에서 형식 라이브러리를 참조로 사용할 수 있습니다.
참조
.NET Desktop feedback