development

[스크랩] .NET으로 ActiveX 만들기

toyship 2010. 8. 8. 19:46
반응형

.NET으로 ActiveX 만들기

 

.NET에서는 ActiveX를 대체할만한 SmartClient 기술이 있음에도 불구하고 ActiveX 만들기를 하는 이유는 다음과 같다.

기존 Unmanaged DLL을 사용하는 웹 컨트롤은 SmartClient 배포방식에 포함될수가 없다는 점 때문이다.
순수 Managed Code만으로 구성되어있을 경우 Smart 하게 배포가 가능한 모양이다.
이해가 안되는점은 Click Once에서 처럼 Manifest파일로서 묶어주면 별 차이 없지 않나 싶긴 하지만,
뭐 그나름대로 MS의 정책일테니... 그것까지 따지는것은 다음기회로 넘어가야겠다.

.NET에서는 Attribute들을 제공하여 Class자체에 여러 특성을 부여하곤 한다.
가끔 C++의 Template 를 보는듯한 느낌이 들때도 있다.
이 특성들을 몇개만 기술해주면 간단히 ActiveX를 만들 수 있다.
하지만 이 특성만으로는 쓸만한 ActiveX를 만들어내기에는 부족하며,
몇가지 작업을 더 해주어야 한다.

여기에서 설명할 ActiveX는 Flash와 같이 사용자 UI가 있는 ActiveX이다.

먼저 구성을 살펴보면
UserControl을 상속받은 클래스와
제공되는 메서드를 기술할 Interface와
ActiveX가 발생시키는 이벤트에 대한 Interface,
이벤트용 인터페이스 메서드유형별 delegate,
IObjectSafety 인터페이스가 필요하다.

일단 클래스 라이브러리 프로젝트를 하나 추가하고,
UserControl을 상속받은 '사용자 정의 컨트롤'을 하나 추가한다.

코드 편집화면으로 들어와서 메서드용 인터페이스와 이벤트용 인터페이스, 각 이벤트 메서드 형식에 해당하는 delegate를 기술한다.
자세한 내용은 제일 아래 샘플 코드를 참고한다.

그리고 IObjectSafety를 선언해야하는데, 이 내용은 코드샘플에 담아두었으니 그대로 복사해서 쓰면 된다.
아직까지도 왜 이걸 선언하면 안전하다는건지 모르겠다.
MS문서에 따르면 이걸 선언해줌으로서 이 ActiveX가 안전하다는 표시를 하는거라는데, 
악의적인 코드를 담고 이걸 선언하면 그만 아닌가?
이 얘긴 길어질듯 하니 원칙은 좀 접어두고 일단 선언하자 ㅡ.ㅡ;;;

지금부터는 필요한 어트리뷰트에 대한 설명이다.

[ComVisible(bool visible)]
사실 이 특성만 있으면 ActiveX다. 별다르게 해줄일이 없다.
그러나 ActiveX 자체가 가졌던 수많은 특성들을 더해주어야만 쓸만한 ActiveX가 될 수 있다.

[Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]
해당 클래스의 Guid값을 부여한다. C++로 개발했을때 와 달리 딱 요기 한군데만 지정해주면 된다.
각 클래스나 인터페이스들은 고유의 GUID를 가져야한다. 지정하지 않는다면, 알아서 생성해주기는 하지만,
결국 우리가 알고 있는 이름이 되어야, 다른데서 써먹을것 아닌가?
VS2005의 도구 -> Guid생성기를 활용하여 새로운 GUID를 생성후 명시적으로 지정해서 사용하자.

[InterfaceType(ComInterfaceType type)]
InterfaceIsDual, InterfaceIsIDispatch, InterfaceIsIUnknown 세가지 값을 지정할 수 있으며, 원하는 데로 골라쓰면 된다.
이 옵션에 대한 얘기들은 기존 COM관련 문서를 찾아 보라.
웹에서 사용하기 위한 ActiveX라면, InterfaceIsIDispatch를 쓰면 된다.

[ComSourceInterfaces(Type type)]
요게 재미있는 놈이다. 해당 타입에 인터페이스의 타입값을 부여하면
그 인터페이스의 메서드들이 이벤트 핸들러 처럼 동작하게 된다.

[DispId(int dispid)]
각 메서드, 프라퍼티, 이벤트 메서드에 대해서 부여하게 되며 서로 충돌이 있지 않으면 된다.
메서드 프라퍼티는 충돌이 없어야 하며 이벤트와 는 별도다
부여하지 않아도 무방하나, 난 일일이 관리해주는 편이다.

아래 샘플 코드는 직접 작성한 코드이며 참고용으로 활용해보자.


체크사항

* public partial class CActiveXControl : UserControl, IActiveXControl_Method, IObjectSafety
이 구문에서 꼭 UserControl다음에 IActiveXControl_Method가 와야 한다.
IObjectSafety나 기타 다른 Interface가 오면 안된다. 함수를 못찾게 되어버린다.
이유는 아마 InterfaceIsDispatch 때문인듯 싶다. 자세한 내용은 나도 잘 모르겠다.
이걸 InterfaceIsDual로 바꾸면 될듯한데, 바꿔보면 웹에서 구동할때
또 다른 오류가 발생하는것을 확인해서, 그냥 돌아와버렸다.

* 우리는 Managed Class를 ActiveX로 쓰고자 하는것이기에
어셈블리 등록을 해주어야 한다.
결국 COM이므로 이전에 C++로 작성하던것이랑 하는일은 매 한가지다.
등록이되어야 하는것이다.
다만 사용하는 명령이 다르다.

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe

이런 경로 쯤에 있으니 버전에 따라 찾아서 쓰면 되겠다.
간단한 사용법을 덧붙이자면,

Regasm.exe AssemblyPath [Option]

/codebase : 이걸 설정해주어야 나중에 이 어셈블리의 파일경로를 읽어 올 수 있다.
/unregister : 등록을 취소한다.

이 두가지 정도의 옵션 정도만 기억하면 된다.
자세한건 한번 실행해보면 사용법이 주루룩 나오니 참고하면 되겠다.


* 만약 기존 DLL들을 사용한다면 DLL이 있는 위치에서 실행되어야 한다.
해당 DLL을 로딩하기전에 DLL이 있는 디렉토리로 실행경로을 옮겨주자.

이 코드는 현재 만들고 있는 ActiveX가 담긴 Assembly가 COM으로 등록되었을때,
해당 레지스트리에서 파일 경로를 읽어와서 실행 경로로 잡아주는 코드이다.
즉, 위에서 설명한 Regasm을 할때 /codebase옵션을 붙여주어야만 정상동작하는 코드다.

중요한건 마지막 줄이므로 경로는 필요한데로 적절히 쓰면 되겠다.

string sGUID = this.GetType().GUID.ToString("B");
string sRegPath = Path.Combine(Path.Combine(@"CLSID", sGUID), "InprocServer32");
RegistryKey regKey = Registry.ClassesRoot.OpenSubKey(sRegPath);
object oValue = regKey.GetValue("CodeBase");
Uri uriFilePath = new Uri(oValue.ToString(), UriKind.Absolute);
Environment.CurrentDirectory = Path.GetDirectoryName(uriFilePath.LocalPath);




이제 샘플코드를 보자.

IObjectSafety 선언하기

/// <summary>
/// See Internet SDK, IObjectSafety.
/// </summary>
[Flags]
public enum ObjectSafetyFlags : int
{
 /// <summary>
 /// Caller of interface may be untrusted
 /// </summary>
 INTERFACESAFE_FOR_UNTRUSTED_CALLER = 1,

 /// <summary>
 /// Data passed into interface may be untrusted
 /// </summary>
 INTERFACESAFE_FOR_UNTRUSTED_DATA = 2,

 /// <summary>
 /// Object knows to use IDispatchEx.
 /// </summary>
 INTERFACE_USES_DISPEX = 4,

 /// <summary>
 /// Objects knows to use IInternetHostSecurityManager.
 /// </summary>
 INTERFACE_USES_SECURITY_MANAGER = 8,

 /// <summary>
 /// Flags combination.
 /// </summary>
 SafeForScripting = INTERFACESAFE_FOR_UNTRUSTED_CALLER |
 INTERFACESAFE_FOR_UNTRUSTED_DATA
}

/// <summary>
/// See Internet SDK, IObjectSafety.
/// </summary>
[ComVisible(true)]
[ComImport!]
[Guid("CB5BDC81-93C1-11cf-8F20-00805F2CD064")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IObjectSafety
{
 void GetInterfaceSafetyOptions(ref Guid riid, out int supportedOptions, out int enabledOptions);
 void SetInterfaceSafetyOptions(ref Guid riid, int optionSetMask, int enabledOptions);
}


샘플 C# 코드

[ComVisible(false)]
public delegate void EventHandlerMyEvent(string sValue);
[Guid("5E603C7D-4B7C-412a-BAB4-063E5674F97A")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface IActiveXControl_Method
{
 [DispId(1)]
 void Method1(string sParam);
 [DispId(2)]
 string Property1
 {
  get;
  set;
 }
}
[Guid("93733F3F-2CDF-4979-ACEC-C53E98B10077")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface IActiveXControl_Event
{
 [DispId(1)]
 void OnEventFire(string sValue);
}
[Guid("0FFC1A66-39DA-41bf-BB7C-43539A27D87C")]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IActiveXControl_Event))]
public partial class CActiveXControl : UserControl, IActiveXControl_Method, IObjectSafety
{
 public event EventHandlerMyEvent OnEventFire;


 public CActiveXControl()
 {
  InitializeComponent();
 }

 #region IObjectSafety 멤버
 public void GetInterfaceSafetyOptions(ref Guid riid, out int supportedOptions, out int enabledOptions)
 {
  supportedOptions = enabledOptions = (int)ObjectSafetyFlags.SafeForScripting;
 }
 
 public void SetInterfaceSafetyOptions(ref Guid riid, int optionSetMask, int enabledOptions)
 {
 }
 #endregion

 #region IActiveXControl_Method 멤버
 public void Method1(string sParam)
 {
  string sMessage = sParam + " " + _sProperty1;
  MessageBox.Show(this, sMessage + " In .NET", "ActiveXControl");
  if (null != OnEventFire)
   OnEventFire(sMessage);
 }
 string _sProperty1;
 public string Property1
 {
  get { return _sProperty1; }
  set { _sProperty1 = value; }
 }
 #endregion
}


샘플 HTML 코드

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>ActiveXControl Test Page</title>
</head>
<body>
    <object id="ActiveXControl" classid="clsid:0FFC1A66-39DA-41bf-BB7C-43539A27D87C" width="100"
        height="100">
        <param name="Property1" value="World!!" />
    </object>
    <br />
    <script type="text/javascript">
    function ActiveXControl::OnEventFire (sValue) {
       alert!(sValue + " In Script");
    }
    </script>
    <div>
        <input type="button" value="Hello World" onclick="ActiveXControl.Method1('Hello');" /><br />
    </div>
</body>
</html>



이상 위의 샘플을 작성해서 돌려보면, 메서드 호출, 프라퍼티 바인딩, 이벤트 발생시키기 해당과정에서 파라메터 전달법등에 대해서 알 수 있을것이다.

 

출처 : http://silsol.egloos.com/3310351

 

출처 : Bandi34.Knowledge
글쓴이 : 반디34 원글보기
메모 :
반응형