'Return'에 해당되는 글 1건

  1. 2008/07/08 귀차니스트 Com 개체를 Retun in Com Programming (2)

  어떻게 하다보니 오늘 Com Dll을 만드는 일을 하게 되었습니다. 원인 인즉, .Net 1.1로 이미 배포되어있던 프로그램에 추가했던 Ftp 모듈이 약간 신뢰성이 안좋은 부분이 있어 그 부분을 수정하기 위해서 였습니다. 당시 %진행도가 필요한 Ftp 다운로드 모듈이 필요하여 웹상에 공개되어 있던 소스를 하나 받아 구성하긴 했는데, 그 마저도 바로 적용하지는 못하고 수정을 거쳐 적용을 했었습니다.
  그런데 이번에 부족한 부분이 없도록 수정을 해야 했는데, .Net 1.1에서 지원하지도 않는 것 소켓을 이용해서 통째로 짜기에는 너무 시간이 부족하더군요. 그래서 일단 WinAPI를 땡겨쓴 dll을 만들기로 했습니다. 제가 짧은 시간에 만든 것 보다야 MS에서 오래전에 공개된 API가 신뢰성이 높다고 판단하기 때문이었습니다.
  조금씩 만들어가다 보니 디자인이 영 별로더군요. 그래서 CFtpProgress 라는 클래스에서 Download, Upload 함수를 파일명과 함께 호출을 하면 CProgressObject라는 하나의 Com 개체를 리턴하는 쪽으로 수정하였습니다. 그런데 이게 또 아주 잘 알지 못하는 부분이다 보니 삽질을 하게 되더군요. 일단 삽질을 거쳐 수정을 하긴 했습니다. 방법이 대충 아래와 같더군요.( 테스트 설명 과정이다 보니 개체를 대충 작성 했습니다. )

TestDll.cpp (Language : cpp)
  1. // TestCom.idl : IDL source for TestCom
  2. //
  3. // This file will be processed by the MIDL tool to
  4. // produce the type library (TestCom.tlb) and marshalling code.
  5. import "oaidl.idl";
  6. import "ocidl.idl";
  7. [
  8.     object,
  9.     uuid(4F6057F5-8CED-464E-B0D7-6DC45F50B87B),
  10.     dual,
  11.     nonextensible,
  12.     helpstring("IMain Interface"),
  13.     pointer_default(unique)
  14. ]
  15. interface IMain : IDispatch{
  16.     [id(1), helpstring("method GetSub")] HRESULT GetSub(IDispatch** RetVal);
  17. };
  18. [
  19.     object,
  20.     uuid(E77D270C-26F8-4E31-A7CB-F3BCEDC153A2),
  21.     dual,
  22.     nonextensible,
  23.     helpstring("ISub Interface"),
  24.     pointer_default(unique)
  25. ]
  26. interface ISub : IDispatch{
  27.     [id(1), helpstring("method ShowMessageBox")] HRESULT ShowMessageBox(void);
  28. };
  29. [
  30.     uuid(EAB88B2E-5DD1-4EF0-9354-6C009D79C40E),
  31.     version(1.0),
  32.     helpstring("TestCom 1.0 Type Library")
  33. ]
  34. library TestComLib
  35. {
  36.     importlib("stdole2.tlb");
  37.     [
  38.         uuid(54146920-3926-44F5-9260-7F5A11276E6B),
  39.         helpstring("Main Class")
  40.     ]
  41.     coclass Main
  42.     {
  43.         [default] interface IMain;
  44.     };
  45.     [
  46.         uuid(0FA43AE1-6482-48BA-A096-937DFC03D057),
  47.         helpstring("Sub Class")
  48.     ]
  49.     coclass Sub
  50.     {
  51.         [default] interface ISub;
  52.     };
  53. };

  위와 같이 대충 IMain, ISub 인터페이스를 정의 해봤습니다. http://www.filewiki.net/tc/entry/ATL-Inherit-Implement-ATL으로-COM-라이브러리-작성시-상속-구현하는-방법 에 적어놓은 방법으로 Com Class를 생성 하시면 됩니다. 일단 그냥 Com Class 추가만 하다보니 IDispatch** RetVal로 정의 해놓은 부분이 있습니다. 이 부분이 수정 포인트라고 할 수 있겠군요.

Main.cpp (Language : cpp)
  1. STDMETHOD(GetSub)(ISub** RetVal);
Main.cpp (Language : cpp)
  1. STDMETHODIMP CMain::GetSub(ISub** RetVal)
  2. {
  3.     // TODO: Add your implementation code here
  4.     return S_OK;
  5. }

  일단 해당 Main 클래스를 정의한 C++ 소스에서 위와 같이 IDispatch를 ISub로 바꾸어 줍니다. 그리고 하나 더바꿔주어야 하는 부분이 있습니다. 바로 IDL 파일 부분입니다.

Main.idl (Language : cpp)
  1. [
  2.     object,
  3.     uuid(E77D270C-26F8-4E31-A7CB-F3BCEDC153A2),
  4.     dual,
  5.     nonextensible,
  6.     helpstring("ISub Interface"),
  7.     pointer_default(unique)
  8. ]
  9. interface ISub : IDispatch{
  10.     [id(1), helpstring("method ShowMessageBox")] HRESULT ShowMessageBox(void);
  11. };
  12. [
  13.     object,
  14.     uuid(4F6057F5-8CED-464E-B0D7-6DC45F50B87B),
  15.     dual,
  16.     nonextensible,
  17.     helpstring("IMain Interface"),
  18.     pointer_default(unique)
  19. ]
  20. interface IMain : IDispatch{
  21.     [id(1), helpstring("method GetSub")] HRESULT GetSub([out,retval]ISub** RetVal);
  22. };

  IMain, ISub의 순서를 제대로 바꿔주어야 합니다. 아래에 있는 정의를 위에서 판단하지는 못하겠죠? 일단 ISub를 위로 순서를 수정한 다음, IMain을 아래로 바꾸어 주고, IDispatch역시 ISub로 바꾸어 줍니다. 그리고 마지막 남은 소스 부분에서 정의 해야 합니다.

Main.cpp (Language : cpp)
  1. STDMETHODIMP CMain::GetSub(ISub** RetVal)
  2. {
  3.     // TODO: Add your implementation code here
  4.     CComQIPtr< ISub > ComPtr;
  5.     ComPtr.CoCreateInstance( CLSID_Sub );
  6.     ComPtr.QueryInterface( RetVal );
  7.     return S_OK;
  8. }

  바로 위와같이 해당 기능을 구현합니다. CComQIPtr SmartPointer 클래스를 통하여 ISub 인터페이스에 대한 관리를 쉽게 처리하기로 했습니다. 그냥 그저 ISub 자체만으로도 처리는 가능합니다. 하지만 여기서 다루지는 않겠습니다. 그리고 CoCreateInstance( CLSID_Sub );를 통한 SmartPointer에 실질적인 Com 개체를 생성합니다.
  그런 다음 바로 중요한 것이 QueryInterface인데 Com개체가 Addref, Release와 같은 함수를 통하여 Reference Counting을 수행한다는 것을 알고 계신분들이 계실겁니다. 그래서 그냥 그러 *RetVal = ComPtr;과 같은 대입식으로 Com개체를 리턴한다면 해당 ComPtr이 Scope를 벗어났을때 해당 Com개체는 메모리상에서 삭제되게 됩니다.
  그래서 QueryInterface기본 구현에서 Addref를 호출해주므로 이를 통한 Interface를 얻으면서 대입합니다. 그런 다음 Sub에서는 ShowMessageBox같은 함수를 만들어 테스트를 해보도록 하겠습니다.

Sub.cpp (Language : cpp)
  1. STDMETHODIMP CSub::ShowMessageBox(void)
  2. {
  3.     // TODO: Add your implementation code here
  4.     MessageBox(NULL, L"aaa", L"", MB_OK);
  5.     return S_OK;
  6. }
Test.cs (Language : csharp)
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. namespace WindowsFormsApplication5
  10. {
  11.     public partial class Form1 : Form
  12.     {
  13.         public Form1()
  14.         {
  15.             InitializeComponent();
  16.         }
  17.         private void button1_Click(object sender, EventArgs e)
  18.         {
  19.             TestComLib.IMain MainObj = new TestComLib.MainClass();
  20.             TestComLib.ISub SubObj = MainObj.GetSub();
  21.             SubObj.ShowMessageBox();
  22.         }
  23.     }
  24. }

  이렇게 테스트를 해보시길 바랍니다. 이렇게 하면 ShowMessageBox에서 처리한대로 MessageBox 함수를 통한 메시지박스가 표시됩니다. 어떻게 생각하면 약간 복잡할 수도 있지만 의외로 간단할 수 있습니다. 그럼 이를 이용한 좋은 라이브러리를 만들어 보세요^^.



크리에이티브 커먼즈 라이센스
Creative Commons License
2008/07/08 22:21 2008/07/08 22:21
TAG , ,

댓글을 달아 주세요

  1. kkamagui 2008/07/09 07:53  댓글주소  수정/삭제  댓글쓰기

    와우 멋진데 >ㅁ<;)-b

    COM 소스는 언제봐도 적응이 안된다야 ㅜ_ㅜ

    드라이버 소스가 더 쉬운거 같오... ㅜ_ㅜ)-b

    • 귀차니스트 2008/07/09 21:48  댓글주소  수정/삭제

      헐 ㅋㅋ 형 드라이버가 더 어려운거잖아요 ㅠㅠ
      이건 드라이버에 비하면 ㅠㅠ