Silverlight를 설치하려면 여기를 클릭합니다.*
Korea 대한민국변경|Microsoft 전체 사이트
MSDN
|개발자 센터
MSDN Home   MSDN Home
MSDN 홈 > MSDN Magazine > 2001년 기사 > .NET Interop: COM 기반 응용 프로그램과의 상호 작용을 위해 래퍼 사용

.NET Interop: COM 기반 응용 프로그램과의 상호 작용을 위해 래퍼 사용

David S. Platt
이 기사의 내용은 COM과 Visual Basic에 익숙한 사용자를 위한 것입니다.
난이도? ?? 1?? 2?? 3?
요약 클라이언트와 서버 양 측면에서 볼 때, Microsoft .NET 응용 프로그램을 개발하기 위해서는 .NET 응용 프로그램과 기존 COM 구성 요소 간의 상호 작용이 필수적으로 필요하게 될 것입니다. .NET Framework는 COM 개체를 위한 여러 래퍼를 사용함으로써 이러한 상호 작용을 가능하게 하고 있으며, 이럼으로써 속성 및 메서드를 .NET 구성 요소에 개방할 수가 있습니다. 이러한 래퍼들은 COM과 .NET을 쉽게 연결하는 데 중요한 역할을 합니다.
우선 래퍼에 대해 설명한 다음 .NET 구성 요소가 COM+ 트랜잭션에서 동작하는 다양한 방법에 대해 설명하겠습니다. COM과 .NET의 상호 운용성에 대한 내용 이외에도 ActiveX 컨테이너를 사용하여 .NET 컨트롤을 호스팅하는 방법과 .NET 컨테이너를 사용하여 ActiveX 컨트롤을 호스팅하는 방법에 대해서도 설명하겠습니다.

COM 시대가 끝났다는 표현은? 지나치게 과장된 표현입니다. Microsoft® .NET Framework와 관련하여, 언론은 아직 .NET이 출시되지 않았으며 앞으로도 이는 당분간 시간이 걸릴 것이라는 점을 잊고 있는 것 같습니다. .NET이 출시되면 40년 전 컬러 TV나 11년 전 Windows® 3.0 또는 현재의 HDTV와 같은 위상을 차지할 것입니다. .NET Framework는 기존 범용 프레임워크보다 훨씬 유용한 기능을 갖고 있지만, 전세계는 아직도 여러 표준과 관련하여 많은 투자를 하고 있는 실정입니다. Microsoft의 플랫폼은 지난 8년간 이 업계에서 영원한 존재인 COM에 의존해 왔습니다. 최초로 출시될 .NET 응용 프로그램에는 다른 .NET 응용 프로그램 없이 9개의 가질리언(gazillion) COM 응용 프로그램이 제공될 예정이며, 두 번째로 출시될 .NET 응용 프로그램에는 9개의 가질리언 COM 응용 프로그램과 1개의 .NET 응용 프로그램이 제공될 예정입니다. 처음 출시될 당시 컬러 TV 수상기가 흑백 방송을 수신할 필요가 있었고 초기 Windows 버전이 MS-DOS® 기반 프로그램을 실행할 필요가 있었지만 아무도 흑백 TV나 DOS 프로그램을 구입하지 않은 것과 마찬가지로, .NET은 COM과 완벽하게 상호 운용될 필요가 있습니다.?
Visual Studio® 6.0의 일부인 Visual J++®에서 COM에 대한 .NET 지원을 참고하면 디자인 개념의 기원과 최초의 테스트 상태를 알 수 있습니다. 이러한 COM 지원에 대해서는 제가 이미 3년 전에 COM의 본질: 프로그래머를 위한 워크북(The Essence of COM: A Programmers Workbook, 3rd Edition, Prentice Hall)이란 책에서 설명한 적이 있습니다.
이 기사에서 설명하는 예제 프로그램은 Visual Studio .NET Beta 2, build 9148에 포함되어 있습니다. 이 예제를 작성할 당시와 지금 이 기사를 읽는 때와는 시간적으로 차이가 있을 수 있기 때문에, 처음부터 응용 프로그램을 다시 작성해야 하더라도 놀라지는 마십시오.


맨 위로


.NET에서 COM 개체 사용

새로눈 .NET 코드는 기존 COM 코드와 상호 운용되어야 하기 때문에 이 경우에 대해 먼저 설명하겠습니다. .NET 클라이언트는 그림?1과 같이 런터임 호출 가능 래퍼(RCW)를 통해 COM 서버에 액세스합니다. RCW는 COM 개체를 래핑하고 이 개체와 .NET 공통 언어 런타임(CLR) 환경을 서로 연결해 주는 역할을 합니다. 이로 인해 .NET 클라이언트 측면에서는 COM 개체를 기본 .NET 개체인 것처럼 인식하며, COM 개체 측면에서는 .NET 클라이언트를 표준 COM 클라이언트인 것처럼 인식합니다.

그림 1 RCW를 통한 클라이언트 액세스

그림 1 RCW를 통한 클라이언트 액세스

.NET 클라이언트 개발자는 두 가지 방법 중 하나를 사용하여 RCW를 만듭니다. Visual Studio .NET을 사용하는 경우에는 프로젝트의 참조 섹션을 마우스 오른쪽 단추로 클릭한 다음 바로 가기 메뉴에서 참조 추가를 선택하면 됩니다. 그러면 시스템에 등록된 모든 COM 형식 라이브러리를 선택할 수 있는 대화 상자가 나타나며, 등록하지 않은 형식 라이브러리를 찾아서 선택할 수도 있습니다(그림?2 참조). 그런 다음 RCW를 만들려는 COM 개체를 선택하면 Visual Studio .NET에서 RCW를 만들어 프로젝트에 추가해 줍니다.


그림 2 RCW 생성

그림 2 RCW 생성

Visual Studio .NET을 사용하지 않는 경우 .NET SDK에는 명령줄 도구(동일한 작업을 수행하는 도구로 TlbImp.exe라고도 함)가 포함됩니다. 형식 라이브러리를 읽고 RCW 코드를 만드는 논리는 System.Runtime.InteropServices.TypeLibConverter라고 불리는 .NET 런타임 클래스 내에 존재합니다. Visual Studio .NET과 TlbImp.exe는 내부적으로 이 클래스를 사용합니다.


그림 3 .COM을 사용하는 NET 클라이언트

그림 3 COM을 사용하는 .NET

그림?3에서는 COM 개체 서버를 사용하는 .NET 클라이언트 프로그램을 예로 보여주고 있습니다. 본 기사 맨 위에 있는 링크를 따라 예제를 다운로드 하십시오. 이 예제에는 COM 서버, COM 클라이언트 및 .NET 클라이언트가 포함되어 있기 때문에 이 두 개를 비교해 볼 수 있습니다. 소스 코드는 그림?4에 표시되어 있습니다.
일단 RCW를 생성한 다음에는 짧은 이름을 사용하여 개체를 참조할 수 있도록 하기 위해, Imports 문을 사용해 RCW의 네임스페이스를 클라이언트 프로그램으로 가져오고 싶어할 것입니다. 이경우 새로운 연산자를 사용하면 RCW 개체를 쉽게 만들 수 있습니다. 일단 RCW 개체가 생성되면 RCW는 기본 COM 함수인 CoCreateInstance를 내부적으로 호출하고, 이 결과 RCW 개체에 의해 래핑되는 COM 개체가 만들어집니다. 그러면 .NET 클라이언트 프로그램은 RCW가 기본 .NET 개체인 것처럼 RCW에서 메서드를 호출하며, RCW는 각 호출을 COM 호출 규칙으로 변환합니다. 예를 들어 .NET 문자열은 COM이 필요로 하는 BSTR 문자열로 변환하여 해당 개체로 보냅니다. RCW는 COM 개체로부터 반환된 결과를 클라이언트로 반환하기 전에 기본 .NET 형식으로 변환합니다.
예제 COM 클라이언트 프로그램을 실행한 다음 단추를 클릭하면 제가 코드에 삽입해 놓은 대화 상자에 개체가 만들어지고 즉시 없어지는 것을 알 수 있을 것입니다. 예제로 제공한? .NET 클라이언트 프로그램을 실행한 다음 Get Time 단추를 클릭하면 개체가 만들어지지만 즉시 없어지지는 않습니다. 래퍼 개체가 시야에서 사라질 때 당연한 것으로 생각할 수 있지만 그렇지 않습니다. 개체 레퍼런스를 'nothing'으로 명시적으로 설정해 놓아도 마찬가지입니다. RCW가 범위를 벗어나서 프로그램에서 더 이상 액세스할 수가 없지만, 나중에 RCW의 가비지가 수집되거나 사라지기 전까지는 RCW가 래핑하는 COM 개체를 실제로 해제하지는 않습니다. 이것은 문제가 될 수 있습니다. 대부분의 COM 개체는 이러한 주기를 고려하지 않고 작성되기 때문에 클라이언트가 종료될 때 바로 해제되어야 하는 값비싼 리소스를 계속 사용하는 경우가 있을 수 있습니다.
이러한 문제는 두 가지 방법 중 하나로 해결할 수 있습니다. 첫 번째 방법은 System.GC.Collect 함수를 통해 고속 가비지 수집을 강제로 수행하는 것입니다. 이 함수를 호출하면 사용하고 있지 않은 모든 시스템 리소스와 범위 내에 존재하지 않는 RCW를 수집하여 다시 사용하게 됩니다. 이 방법의 단점은 전체 가비지 수집의 오버헤드가 높다는 것입니다. 마치 Julia Child가 전체 부엌을 청소하지 않고도 자신이 좋아하는 과도만을 닦아 낼 수 있는 것처럼, 하나의 개체를 버리기 위해 즉시 비용을 들일 필요는 없습니다. 다른 개체에 영향을 주지 않으면서 하나의 특정 COM 개체를 없애려면 System.Runtime.InteropServices.Marshal.ReleaseComObject 함수를 사용할 수 있습니다.
앞 단락에서 설명한 RCW 메커니즘을 위해서는 초기 바운드 개체가 필요합니다. 이것은 개발자가 개발 시점에서 래퍼 클래스를 구성하기 위해 형식 라이브러리가 제공하는 개체에 대해 근본적으로 이해하고 있어야 함을 의미합니다. 하지만 이것이 모든 디자인 시나리오에서 가능한 일은 아닙니다. 예를 들어, 클라이언트가 개체 및 메서드의 ProgID를 읽고 런타임 도중 이를 스크립트 코드로부터 읽는 경우, 스크립트 상황에서는 후기 바인딩이 필요합니다. 대부분의 COM 개체는 특별히 IDispatch 인터페이스를 지원하므로 이러한 형식의 후기 바운드 액세스를 사용할 수 있습니다. 이런 경우에는 RCW를 미리 만들 수 없습니다. 그렇다면 .NET은 이러한 시나리오를 어떻게 처리할 수 있을까요?


Figure 5 Late Binding

그림 5 후기 바인딩

.NET Framework는 대부분의 COM 개체가 지원하는 IDispatch 인터페이스에 대해 후기 바인딩을 지원합니다. 그림?5는 후기 바인딩 프로그램에 대한 예제이며 그림?6은 코드입니다. 정적 메서드인 Type.GetTypeFromProgID를 통해 개체의 ProgID를 기반으로 하는 .NET 시스템 형식을 만드십시오. ProgID 대신 이를 사용할 경우 정적 메서드인 Type.GetTypeFromCLSID(그림?6에는 없음)는 CLSID를 기반으로 하여 같은 역할을 합니다. 그런 다음 Activator.CreateInstance 메서드를 사용하여 COM 개체를 만들고 Type.InvokeMember 함수를 통해 메서드를 호출하여, 메서드의 매개 변수를 배열 형태로 전달합니다. 후기 바인딩의 경우 이 작업은 더 복잡하지만 어렵지 않게 수행할 수 있을 것입니다.


맨 위로


COM에서 .NET 개체 사용

반대로, 이미 COM과 통신하고 있는 클라이언트에서 .NET 개체를 사용하려는 경우를 가정해 봅시다. 이런 경우는 그 반대 상황보다 흔하지 않은 시나리오입니다. 왜냐하면 이 상황에서는 .NET 환경에서의 새로운 COM 개발을 미리 전제로 하기 때문입니다. 하지만 10개의 COM 개체를 사용하는 기존 COM 클라이언트에 .NET 개체로만 존재하는 기능 집합을 추가하려는 경우에 이러한 상황이 발생할 수 있습니다. 그림?7은 COM 호출 가능 래퍼(CCW)를 통해 이러한 상황을 지원하는 .NET Framework를 나타냅니다.
그림 7 COM 호출 가능 래퍼

그림 7 COM 호출 가능 래퍼

CCW는 .NET 개체를 래핑하고 이 개체와 CLR 환경을 연결해 주며 .NET 개체가 기본 .NET 개체인 것처럼 COM 클라이언트에 나타나도록 만듭니다. COM 호출이 가능한 래퍼와 함께 작동하도록 하려면 .NET 구성 요소의 어셈블리에 유효한 이름이 지정되어야 합니다. 그렇지 않으면 CLR 런타임에서는 이름을 명확하게 식별할 수 없게 됩니다. 이 이름은 표준 .NET 구성 요소의 위치 규칙을 따라야 하는데, 이는 이름이 글로벌 어셈블리 캐시(GAC)에 위치해야 하거나 또는 경우에 따라 클라이언트 응용 프로그램의 디렉터리 트리 내에 존재해야 함을 의미합니다. COM에서 만들려는 모든 .NET 클래스는 기본 생성자를 제공해야 하는데, 기본 생성자란 매개 변수가 필요하지 않은 생성자를 말합니다. COM 개체 생성 함수는 함수에서 만들어진 개체한테 매개 변수를 전달하는 방식을 알지 못하므로, 클래스에서 이러한 상황이 필요하지 않도록 만들어야 합니다. COM 클라이언트에 사용할 필요가 없는 생성자를 가지고 있는 한, .NET 클래스에서는 .NET 클라이언트에 사용하기 위해 매개 변수화된 생성자를 제한 없이 사용할 수 있습니다.
COM 클라이언트가 .NET 개체를 찾기 위해서는, 개체를 만들 때 클라이언트가 서버를 찾기 위해 COM에서 필요로 하는 레지스트리 항목을 만들어야 합니다. 이때 .NET SDK에 있는 RegAsm.exe라는 유틸리티 프로그램을 사용하시면 됩니다. 이 프로그램에서는 .NET 클래스에 있는 메타 데이터를 읽은 후 COM 클라이언트를 메타 데이터로 지정하는 레지스트리 항목을 만듭니다. 그림?8은 이 프로그램에서 만들어지는 레지스트리 항목입니다. 여기에 사용하는 COM 서버는 중개 DLL인 Mscoree.dll입니다. InProcServer32 키의 Class 값은 만들고 래핑하려는 .NET 클래스를 이 DLL에게 알려주며, Assembly 항목은 이 클래스를 찾을 .NET 어셈블리를 DLL에게 알려 줍니다.

GAC의 복잡한 디렉터리 구조 때문에 GAC에 있는 .NET 구성 요소를 등록하는 것은 어렵습니다. Explorer에는 사용자 지정 플러그인이 있어 GAC의 복잡성을 단순화할 수 있기 때문에, Explore에서는 복잡해 보이지 않지만 명령줄에서는 손대기 어려울 정도로 디렉터리가 중첩되어 있습니다. .NET 구성 요소를 표준 디렉터리에 같다 좋으면 아주 편리합니다. 여기서 regasm.exe를 실행한 다음 gacutil.exe를 사용하여 구성 요소를 GAC로 이동시킵니다. 등록한 다음 COM 구성 요소를 이동시키면 사용할 수 없게 될 수 있습니다. 그러나 .NET에서는 이런 경우가 없습니다. 사실, DLL Hell은 .NET 아키텍처가 해결하기 위해 특수하게 디자인되었던 문제 중의 하나입니다. 공유된 모든 구성 요소는 유효한 이름을 사용하여 이름이 충돌되지 않도록 지정된 상태에서 GAC에 저장됩니다. .NET 로더는 요청자의 디렉터리 트리에서 요청된 구성 요소를 검사한 다음 원하는 어셈블리를 찾을 수 없는 경우 GAC를 검사합니다.
COM 클라이언트는 .NET 개체가 기본 COM 개체인 것처럼 .NET 개체를 액세스합니다. 클라이언트가 CoCreateInstance를 호출하여 개체를 만들면 레지스트리는 등록된 서버인 Mscoree.dll로 요청을 보냅니다. 이 DLL은 요청된 CLSID를 검사하고 레지스트리를 읽은 다음, 만들려는 .NET 클래스와 해당 클래스가 포함된 어셈블리를 찾으며 해당 .NET 클래스를 기반으로 즉시 CCW을 수행합니다. 모든 DLL COM 서버는 이러한 목적으로 사용할 DllGetClassObject라는 글로벌 함수를 제공합니다. CCW는 기본 COM 형식을 자신의 .NET 형식으로 변환(예: BSTR을 .NET 문자열로 변환)한 다음 .NET 개체로 보냅니다. 또한 .NET의 결과와 오류를 COM으로 변환하기도 합니다. 이 기사에 대한 예제 코드에는 현재 시간을 제공하는 .NET 구성 요소와 현재 시간에 액세스하는 COM 클라이언트가 포함되어 있습니다.

.NET 개발자는 COM 클라이언트 상에서 일부 메서드, 인터페이스 및 클래스는 사용하고 다른 메서드, 인터페이스 및 클래스는 사용하지 않도록 얼마든지 원할 수 있습니다. 따라서 .NET에서는 System.Runtime.InteropServices.ComVisible라는 메타 데이터 속성을 제공합니다. 이 속성은 어셈블리, 클래스, 인터페이스, 개별 메서드 등에 사용될 수 있습니다. CCW는 런타임 도중 이 메타 데이터 특성을 읽은 다음 이 특성이 False로 설정된 요청에 대해 COM으로부터 오는 모든 액세스를 거부할 수 있습니다. 단 하위 계층에서 만들어진 설정은 상위 계층에서는 무시됩니다.

예를 들어, 인터페이스에서 ComVisible을 False로 설정하고 해당 인터페이스의 메서드 중 하나를 True를 설정하면, 클라이언트는 해당 인터페이스에서 해당 메서드를 호출할 수 있지만 다른 메서드에는 액세스할 수 없습니다. 예제 프로그램에서는 다음 코드에 나타난 것처럼 클래스에 대해 이 속성을 True로 설정했습니다. 따라서 COM에서는 이 항목을 볼 수 있습니다.

<System.Runtime.InteropServices.ComVisible(True)>
Public Class Class1
현재 CLR 기본 설정이 True이므로 이 특성이 False로 특별히 설정되어 있지 않으면 이 항목이 COM에 표시됩니다. 그러나, 어셈블리에 대한 Visual Studio .NET의 현재 기본 동작은 프로젝트의 AssemblyInfo.vb 파일에서 이 특성을 False로 설정하게 되어 있습니다. 이것은 사용자가 특별히 표시하도록 지정하지 않는 한 어셈블리 내에서 COM에 아무 것도 표시되지 않는다는 것을 의미합니다.?

개인적으로는 시스템 기본값을 완전히 반대되는 값으로 재정의하는 개발 도구의 관행에 전혀 동의하지는 않습니다. 사용자를 깜짝 놀라게 하는 것은 일반적으로 좋지 않은 것이므로 가능한 한 피해야 할 일입니다. 시스템 설명서를 참조하는 모든 프로그래머는 ComVisible의 기본값이 True임을 알고 있을 것입니다. 그러나 Visual Studio를 사용하여 간단한 구성 요소를 작성할 때 COM은 그 구성 요소를 찾을 수 없으며 프로그래머는 그 이유를 알 수 없을 것입니다. 기본값이 특별히 옳다거나 잘못되었다고 말하는 것은 아닙니다. 각각의 기본값에는 장점과 단점이 있으며 두 기본값이 모두 옳을 수 있다고 생각합니다. 그러나, 운영 체제와 기본적인 개발 환경에서 서로 다른 값을 사용하는 경우 제품이 출시되기 전에 둘 중의 한 기본값을 변경(둘 다 변경하면 결과가 동일)하지 않으면 Microsoft에 수많은 전화 문의가 쇄도할 것입니다.

 

오류 처리

COM과 .NET은 오류 처리에 있어 각기 고유의 내부적인 처리 방식을 가지고 있습니다. COM 서버 개발자는 자신의 메서드로부터 특별한 값을 반환하여 오류 상황을 나타내며 때로는 별도의 오류 정보 개체의 형태로 추가 정보를 스레드 로컬 저장소에 제공하기도 합니다. C++ 프로그래머는 자신의 코드를 통해 이러한 작업을 직접 수행합니다. Visual Basic®은 Err 개체와 이 개체의 Raise 메서드를 제공하여 이와 같은 메커니즘을 추상화하지만 결과적으로 동일한 작업을 수행합니다. COM 클라이언트는 오류 코드를 검사한 후 스레드 로컬 저장소에서 추가 오류 정보 개체를 찾아 오류의 현재 상태와 원인을 파악합니다.?

한편 .NET은 예외를 감지하고 전송함으로써 오류를 처리합니다. 이 방법은 Java 언어에서 사용하는 방법과 유사한 방법입니다. 오류를 표시하려는 서버 메서드는 오류를 설명하는 예외 개체를 만들어서 CLR로 전송합니다. 오류 통보를 받은 클라이언트 프로그래머는 Try...Catch 코드 블록을 사용하여 오류 처리기를 스택에 위치시킵니다. 서버가 오류를 전송하면 CLR은 이러한 오류 처리기를 찾고 오류 처리기를 찾은 경우에는 제어 권한을 오류 처리기로 넘깁니다.?

.NET이 COM과 통신하려는 경우 .NET과 COM은 각각 고유의 오류 처리 메서드를 기다립니다. 따라서 래퍼 계층(사용 방법에 따라 CCW 또는 RCW)에서는 고유의 서버 오류 형식을 정확하게 감지한 다음 처리할 방법을 인식하고 있는 자신의 클라이언트에 적합하도록 형식을 변환해야 합니다. .NET 클라이언트가 COM 서버를 호출하는 앞의 예제에서 COM 서버는 그림?9의 앞쪽에 표시된 코드를 사용하여 표준 COM 오류를 신호로 표시합니다. RCW는 이러한 오류를 탐지한 후 .NET 예외로 변환합니다. 이 예외는 클라이언트 코드에 의해 감지될 수 있습니다(그림?9 하단에 표시). COM 서버의 예외를 일반 System.Exception으로 취급하는 처리기를 보여드렸습니다. 원하시면 처리기가 System.Runtime.InteropServices.COMException 클래스만 감지하도록 만들어서 COM 기반의 예외와 다른 형식의 예외를 구분할 수 있습니다.
다른 방법으로, 그림?10의 위에 표시된 것처럼 예제 .NET 서버는 .NET 예외를 만들어 전송함으로써 오류를 신호로 표시합니다. 그림?10의 하단에 표시된 것처럼 Visual Basic으로 작성된 COM 클라이언트는 표준 On Error GoTo 메커니즘을 사용하여 오류를 처리합니다. 서버와 클라이언트는 일상적인 작업을 수행하며 래퍼는 상호 변환 작업을 수행합니다.

맨 위로


.NET 내에서의 트랜잭션

COM+와 MTS(Microsoft Transaction Services)에서는 자동 지원이 제공되었으므로 프로그래머는 트랜잭션에 관여하는 개체를 쉽게 만들 수 있었습니다. 프로그래머는 개체를 트랜잭션이 필요한 상태로 표시했습니다. 그러면 COM+는 개체가 활성화되었을 때 자동으로 개체를 만들었습니다. 개체는 데이터베이스를 변경하기 위해 SQL Server™와 같은 COM+ 리소스 관리자 프로그램을 사용했습니다. 이 프로그램은 트랜잭션 수행하기 위해 COM+ 방식을 지원했습니다. 그런 다음 개체는 결과가 만족스러운지 여부를 COM+에게 통보했습니다. 트랜잭션에 관여한 모든 개체가 정상이면 COM+는 트랜잭션을 커밋하여 변경 사항을 저장합니다. 비정상적인 개체가 있으면 COM+는 트랜잭션을 중단하고 모든 개체의 작동 결과를 무시한 다음 시스템의 상태를 원래의 값으로 롤백시킵니다.
기본 .NET 개체도 트랜잭션에 관여할 수 있습니다. 기존의 Microsoft 트랜잭션 처리 시스템은 COM을 기반으로 하기 때문에 .NET 개체는 자체의 상호 운용 기능을 사용하여 트랜잭션에 관여할 수 있습니다. 이에 대해서는 이 기사의 첫 번째 부분을 참조하십시오. 사용자는 .NET 개체를 COM 서버로 등록합니다. 그런 다음, COM+ Explorer를 사용하여 해당 구성 요소를 COM+ 응용 프로그램에 설치한 다음, 구성 요소가 기본 COM 구성 요소인 것처럼 트랜잭션 요구 사항을 정확하게 설정합니다. Regsvcs.exe라는 .NET SDK 명령줄 도구를 사용하여 등록을 수행하거나 단 한 번에 COM+ 응용 프로그램을 설치할 수도 있습니다.
?

또한 기본 COM 구성 요소는 트랜잭션 요구 사항을 자체의 형식 라이브러리에서 지정하므로 .NET 개체의 트랜잭션 요구 사항을 자체의 메타 데이터에 지정할 수 있습니다. 다음 코드는 트랜잭션의 필요성을 지정하는 속성이 포함된 .NET 개체를 Visual Basic로 작성한 예입니다.

Public Class <TransactionAttribute(TransactionOption.Required)>Class1
C#으로 개체를 작성하는 경우 각 괄호 대신 대괄호를 사용하는 것을 제외하고는 완전히 동일한 방법으로 작성할 수 있습니다. ASP .NET 페이지에서는 코드에 대한 트랜잭션 요구 사항을 표시하기 위해 속성을 추가합니다. 다음 코드를 참조하십시오.
<%@page Transaction="Required " %>
웹 서비스에서는 속성을 사용하여 개별 메서드를 표시함으로써 트랜잭션 요구 사항을 표시합니다. 다음 코드를 참조하십시오.
Public Function <WebMethod(), 
TransactionAttribute(TransactionOption.Required)> _
 HelloWorld() As String
트랜잭션에 관여하는 .NET 개체는 해당 트랜잭션의 결과에 영향을 미칩니다. 이를 위해서 두 가지 방법을 사용할 수 있습니다. COM+와 MTS에서 개체는 API 함수 CoGetObjectContext나 이름이 GetObjectContext인 래퍼를 호출하여 컨텍스트 개체를 가져온 후 이 컨텍스트 개체에 대해 메서드를 호출하여 개체의 트랜잭션 영향을 표시했습니다. .NET 개체는 시스템에서 제공되는 System.EnterpriseServices.ContextUtil 개체에서 개체의 컨텍스트를 찾습니다. 이 개체는 공통적으로 사용되는 SetAbort 및 SetComplete 메서드와 자주 사용되지 않는 EnableCommit 및 DisableCommit 메서드를 제공합니다. 이 메서드는 개체의 만족도 비트와 실행 여부 비트를 COM+와 완전히 동일하게 설정합니다. 컨텍스트에는 DeactivateOnReturn 및 MyTransactionVote 속성이 포함될 수 있는데 이 속성을 사용하여 위의 비트를 개별적으로 읽거나 설정할 수 있습니다. 다른 방법으로, AutoComplete라는 속성을 사용하여 .NET 개체를 표시하면 트랜잭션 호출을 자동으로 수행할 수 있습니다. 이렇게 하면 개체에서 발생하는 정상적인 반환은 자동으로 SetComplete를 호출하지만, 예외를 버려서 개체를 남겨 두면 SetAbort가 자동으로 호출됩니다.

맨 위로


ActiveX 컨테이너에서의 .NET 컨트롤

Windows 환경에서 대부분의 사용자 인터페이스 프로그래밍은 미리 패키지로 만들어진 컨트롤을 사용합니다. 사용자 인터페이스 요소, 메서드, 속성, 이벤트가 유용한 패키지에 포함된 재사용 가능 소프트웨어를 배포한다는 개념은 소프트웨어 시장에서 굉장한 성공을 거두고 있습니다. 이 패키지는 스마트 환경에서 신속한 개발을 수행하는 데 사용됩니다. 이러한 개념은 16비트 VBX 컨트롤에서 시작하여 Windows가 32비트로 전환될 때 ActiveX® 컨트롤로 전환되었습니다. 이 기사와 다른 기사의 각 페이지에는 차트 기록기와 심전계를 제거하기 위해서 스프레드시트에서 맞춤법 검사기에 이르기까지 모든 것을 제공하는 ActiveX 컨트롤 광고로 가득 차 있습니다. 이와 같은 광고는 호주의 유대 동물과 경쟁 관계에 있는 토끼가 성장하듯이 급속하게 성장하고 있습니다. .NET의 성공을 위해서는 이러한 시장이 지속적으로 성장해야 합니다.?

Windows Forms이라고 하는 .NET의 사용자 인터페이스 부문에서는 사용자 고유의 Windows Forms 컨트롤을 개발할 수 있도록 지원합니다. 이러한 Forms은 ActiveX 컨트롤과 개념적으로 동일한 작업을 수행하는 것으로 생각하면 됩니다. 그렇지만 이 Forms은 .NET용으로 제작되었다는 점이 다릅니다. 이 기사에서 자세하게 다루기에는 너무 방대한 내용이지만, 간단한 예제를 통해서 사용자 고유의 컨트롤을 쉽게 작성할 수 있는 방법을 보여드리겠습니다. 컨트롤을 호스팅하는 Windows Forms 클라이언트가 그림?11에 나타나 있으며 컨트롤의 코드는 그림?12에 나타나 있습니다.

그림 11 Windows Forms 클라이언트

그림 11 Windows Forms 클라이언트

기준 클래스인 System.Windows.Forms.UserControl에 있는 클래스로부터 Windows Forms 컨트롤을 만들 수 있습니다. Visual Studio .NET에서 Windows Control Library 프로젝트를 만들면 이 과정이 자동으로 수행됩니다. MFC 기준 클래스인 COleControl로부터 ActiveX 컨트롤을 작성했던 C++ 개발자는 이와 같은 접근 방식에 익숙할 것입니다. 이 기준 클래스에는 배경색과 같은 간단한 속성에서부터 컨테이너와의 복잡한 통신에 이르기까지 모든 Windows Forms 컨트롤에 공통되는 기능이 포함되어 있습니다. 사용자 컨트롤은 동작을 변경하려는 Windows Forms 이벤트 핸들러를 재정의합니다. 이 경우에는 OnPaint 알림을 재정의하려고 합니다. 여기에서 저자는 컨트롤의 직사각형을 컨트롤의 배경색으로 칠하고 현재 시간을 컨트롤의 기본 글꼴을 사용하여 직사각형 위에 그립니다. 이것이 이 컨트롤을 작성하는데 필요한 모든 작업입니다. 마우스 오른쪽 단추로 도구 상자를 클릭하여 Visual Studio 도구 상자에 컨트롤을 넣은 다음 바로 가기 메뉴에서 도구 상자 사용자 지정을 선택하고 컨트롤을 선택합니다(그림?13 참조).

그림 13 Windows 도구 상자에 .NET 컨트롤 배치

그림 13 Windows 도구 상자에서 .NET 컨트롤 배치

하지만 Windows Forms 컨트롤을 작성한 경우 .NET 응용 프로그램에서만 이 컨트롤을 사용할 수 있다는 것을 의미합니까? 현재까지는 .NET 응용 프로그램이 많지 않습니다. 따라서 개발비를 투자하기 전에 좀 더 기다려야 되지 않습니까? 여러분이 이 질문에 "아니오"라고 대답할 수 있도록 돕기 위해 UserControl 기준 클래스에는 Visual Basic 6.0과 같은 ActiveX 컨트롤에 액세스할 수 있는 모든 기능이 포함되어 있습니다. 이를 통해서 .NET 컨트롤 개발자는 설치된 다양한 기반을 활용하여 상당한 매출을 올릴 수 있습니다.
.NET 컨트롤이 ActiveX 호스트에 액세스할 수 있도록 만들기 위해서는 상대적으로 작은 크기의 코드를 작성해야 합니다. ActiveX 컨트롤은 표준 COM 서버가 만들지 않는 몇 개의 레지스트리 항목을 만들기 때문에, 이러한 기능을 사용자의 .NET 컨트롤에 추가해야 합니다. CLR에는 미리 제작된 함수들이 포함되어 있는데 이 함수를 사용하면 항목들을 만들고 제거할 수 있습니다. 이 항목의 이름은 Control.ActiveXRegister 및 Control.ActiveXUnregister가 됩니다. 등록 프로세스 중 두 개의 외부 함수를 호출하도록 .NET COM 등록 유틸리티에게 알려 주는 속성이 표시된 컨트롤 클래스에서 두 개의 외부 함수를 제공해야 합니다. 이 함수는 ActiveXRegister 및 ActiveXUnregister에 권한을 넘겨야 합니다(그림?14 참조). 이것이 오늘 작성해야 할 유일한 특수 코드이며 앞으로 상위 버전에서 기준 클래스에 사용될 수도 있습니다.
본 기사의 앞에서 설명한 것처럼, 일단 이 작업이 수행된 후에는 .NET 컨트롤을 ActiveX 컨트롤처럼 사용하려면 COM 서버가 되기를 원하는 임의의 다른 .NET 클래스로 .NET 컨트롤을 등록하면 됩니다. True로 설정된 ComVisible 속성을 사용하여 .NET 컨트롤을 만들어야 COM 클라이언트에서 이 컨트롤을 볼 수 있는지를 알 수 있습니다. 클라이언트 컴퓨터에 CLR이 설치되어 있어야 합니다. 형식 라이브러리를 생성한 다음 RegAsm.exe 유틸리티를 사용하여 등록합니다. 그런 다음, 클라이언트 응용 프로그램(여기서는 Visual Basic)이 볼 수 있는 적절한 위치에 사용자의 컨트롤 DLL을 위치시켜야 합니다. 대개는 GAC에 위치시키지만 경우에 따라서는 다른 이진 파일이 존재하는 VB98 디렉터리에 위치시킬 수도 있습니다. 그림?15에 나타난 것처럼 Visual Basic 컨트롤 선택 대화 상자에서 .NET 컨트롤이 옵션으로 제공될 것입니다.


그림 15 Visual Basic 6.0에서의 .NET 컨트롤

그림 15 Visual Basic 6.0에서의 .NET 컨트롤

맨 위로


.NET 컨테이너의 ActiveX 컨트롤

.NET에서 ActiveX 컨트롤을 사용할 수 없으면 상당수의 데스크톱 개발자들은 .NET을 사용하여 클라이언트 응용 프로그램을 작성할 수 없습니다. 그러면 ActiveX 컨트롤 개발자들은 자신들의 컨트롤을 변환할 수 있을 정도로 방대한 규모의 시장을 갖지 못할 것이며 전체 개념이 빛을 보지 못할 수도 있습니다. 따라서 .NET Windows Forms 개발자는 ActiveX 컨트롤 호스팅을 위한 지원을 포함시키도록 현명하게 결정해야 합니다.
Windows Forms 응용 프로그램은 기본적으로 ActiveX 컨트롤을 사용하는 방법을 모르고 있습니다. 다만 고유의 기본 .NET 아키텍처로 작성된 컨트롤만을 이해합니다. Windows Forms 응용 프로그램이 ActiveX 컨트롤을 호스팅할 수 있도록 하려면 ActiveX 컨트롤이 포함될 래퍼 클래스를 만들고, 래퍼 클래스의 COM 기반 월드 뷰와 컨테이너의 .NET 월드 뷰를 서로 연결하고, 래퍼 클래스가 마치 기본 Windows Forms 컨트롤인 것처럼 래퍼 클래스를 Windows Forms에 표시해야 합니다. 본 기사의 앞에서 설명한 것처럼, 사용자는 기본적으로 런타임 호출 가능 래퍼를 가지고 있어야 합니다. 이 래퍼는 ActiveX 컨트롤에서 제공되는 모든 COM 인터페이스를 사용하며, 또한 ActiveX 컨트롤이 호스트로부터 필요로 하는 COM 인터페이스를 제공합니다. 이 아키텍처는 그림?16에 나타나 있습니다. 작업량이 많다고 생각하십니까? 그렇습니다. 하지만 걱정하지 않으셔도 됩니다. CLR에서 제공되는 System.Windows.Forms.AxHost 클래스가 사용자를 위해 모든 작업을 수행해 줍니다.

그림 16 Windows Forms

그림 16 Windows Forms

Windows Forms 응용 프로그램에서 호스팅할 ActiveX 컨트롤의 각 클래스에 대해 별도의 래퍼 클래스를 AxHost로부터 만들어야 합니다. 이 클래스에는 ActiveX 컨트롤을 만드는데 사용되는 클래스 ID나 프로그램 ID가 포함되어 있으며, 내부 ActiveX 컨트롤의 속성, 메서드, 이벤트를 기본 .NET 형식으로 제공할 것입니다. ActiveX 컨트롤을 Visual C++® 또는 Visual J++로 가져온 경험이 있는 사용자라면 이 내용들에 익숙할 것입니다. .NET SDK와 함께 제공되는 AxImp.exe라는 명령줄 유틸리티를 사용하여 이 래퍼 클래스를 만들 수 있습니다. Visual Studio .NET을 사용하는 경우, 마우스 오른쪽 단추로 도구 상자를 클릭한 다음 바로 가기 메뉴에서 도구 상자 사용자 지정을 클릭하면 그림?17과 같은 대화 상자가 표시됩니다. 이 대화 상자에는 현재 Microsoft에서 COM 컨트롤이라고 부르는 목록이 나타납니다. "ActiveX"라는 이름은 더 이상 사용하지 않습니다.

그림 17 ActiveX 컨트롤에 대한 참조를 가져오는 Visual Studio .NET

그림 17 ActiveX 컨트롤에 대한 참조를 가져오는 Visual Studio .NET

이 목록에서 컨트롤을 선택하면 Visual Studio .NET은 AxImp.exe를 내부적으로 실행한 다음 사용자를 대신하여 이 래퍼를 생성합니다. 이 래퍼는 프로젝트의 일부분으로써 별도의 DLL로 만들어집니다. 소스 코드를 직접 볼 수는 없지만 소스 코드의 메서드 및 속성은 개체 브라우저에서 볼 수 있습니다. 그림?18을 참조하십시오. 새 컨트롤이 도구 상자에 나타납니다. 사용자는 친숙한 방식으로 이 도구 상자를 사용할 수 있습니다.

그림 19 Web Form 내부의 웹 브라우저 컨트롤

그림 19 Web Form 내부의 웹 브라우저 컨트롤

Microsoft 웹 브라우저 ActiveX 컨트롤을 사용하는 예제 Windows Forms 프로그램을 작성했습니다.그림?19는 MSDN Magazine 웹 페이지를 나타내는 화면입니다. 앞에서 설명한 것처럼 ActiveX 컨트롤을 Visual Studio .NET으로 가져왔습니다. 그런 다음 ActiveX 컨트롤을 Windows 폼에 배치시키고 다음과 같은 코드를 작성했습니다.

Protected Sub Command1Click(ByVal eventSender As System.Object, _
    ByVal eventArgs As System.EventArgs) Handles Command1.Click _  
        WebBrowser1.Navigate((Text1.Text))
End Sub
사용자가 Fetch 단추를 클릭하면 래퍼 클래스의 Navigate 메서드를 호출하여 사용자가 입력한 URL을 전달합니다. 래퍼 클래스는 이 호출을 COM 호출로 변환한 다음 래핑된 ActiveX 컨트롤로 보냅니다.

맨 위로


결론

Microsoft .NET가 시장에서 성공하려면 COM과 상호 운용될 필요가 있습니다. .NET 아키텍처는 .NET과 COM를 연결해 주는 래퍼 클래스를 통해서 클라이언트와 서버에게 상호 운용성을 제공합니다. .NET 개체는 이와 같은 상호 운용 성능을 사용하여 COM+ 트랜잭션에 관여할 수 있습니다. COM과 .NET의 상호 운용 성능을 사용하여 기존의 ActiveX 컨테이너는 .NET 컨트롤을 호스팅할 수 있으며 .NET Windows Forms 응용 프로그램은 ActiveX 컨트롤을 호스팅할 수 있습니다.

? 최종수정일: 2003년 1월 6일

Top of Page Top of Page


Microsoft