유돌이

calendar

1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

Notice

2008. 12. 29. 23:47 델파이
이제 여기까지 종합적인 코드를 완성해 보겠습니다.

type 
  TBaseDShow = class(TObject)
   private 
   public  
     FilterGraph      : IGraphBuilder;   //필터그래프의 인터페이스
     MediaControl   : IMediaControl;  //필터그래프의 인터페이스 
     VideoWindow  : IVideoWindow;  //필터그래프의 인터페이스 
     constructor Create;
     destructor Destroy; override;      
     function CreateFilterGraph(var Graph: IGraphBuilder): Boolean;
   end; 

implementation

constructor TBaseDShow .Create;
begin
  inherited Create;
  CoInitialize(nil);        //COM을 초기화한다.
  CreateFilterGraph(FilterGraph);   //필터그래프를 생성한다.
  FilterGraph.QueryInterface(IID_IMediaControl, MediaControl);  //필터그래프의 또다른 인터페이스 얻어오기
  FilterGraph.QueryInterface(IID_IVideoWindow, VideoWindow); //필터그래프의 또다른 인터페이스 얻어오기
end;

 
destructor TBaseDShow .Destroy;
begin
  if Assigned(MediaControl) then MediaControl.Stop;         //비디오 랜더링을 중단한다.
  While Assigned(VideoWindow) do VideoWindow := nil;
  While Assigned(MediaControl)  do MediaControl := nil;
  While Assigned(FilterGraph)     do FilterGraph := nil;
  CoUninitialize;         //COM을 셧다운시킨다.
  inherited Destroy;
end; 

function TBaseDShow .CreateFilterGraph(var Graph: IGraphBuilder): Boolean;
var
  ID : Integer;
begin
  Result := False;
  if Failed(CoCreateInstance(CLSID_FilterGraph,
                              nil,
                              CLSCTX_INPROC_SERVER,
                              IID_IFilterGraph,
                              Graph))
  then Exit;
  Result := True;
end;
 
위의 코드를 보면 Destory부분에 약간의 예상외 로직이 보이실 것입니다. 우선 첫번째로 필터그래프를 소멸시키기 전에는 반드시 비디오 스트림의 흐름을 정지시켜야 할 것입니다. 그래서 MediaControl.Stop이라는 코드가 첫번째로 삽입된 것이고요, 두번째부터 인터페이스를 해제시키는 로직에 While문이 있다는 것에 조금 의아하셨을지도 모르겟습니다. 

DShow는 간혹가다가 원인 불명의 다운현상을 발생시킵니다. 이유는 여러가지가 있을 수 있겠습니다만, 주로 적절치 못한 구조의 필터나 혹은 WDM 장치 드라이버가 아닐까 생각합니다. 아무튼, 다운이 되는 경우는 주로 동영상을 플레이하거나 반대로 정지시키는 시점에서 발생한다는 것인데요, 상황에 따라서 완전히 다운되는 경우도 있고 아니면 잠시 대기상태에서 빠져나올 수도 있습니다. 
이때 완전히 다운되는 것은 막지 못하겠지만, 잠시 대기상태에서 빠져나오는 것은 위의 방식 처럼 While를 사용하여 해결할 수가 있다는 것이죠. 실제로 While문을 사용한 후부터 어플이 상당하게 안정화 되었음을 느낄 수 있었습니다.  밑져야 본전이니까 그냥 버릇처럼 사용하시면 될 것이라고 봅니다. 어차피 다운되면 다운 되는 것이니까요.  

  => While Assigned(FilterGraph)     do FilterGraph := nil;

[1]필터를 생성하는 함수.

이제 우리는 TBaseDShow Class에다가 다음 세가지 함수를 추가해 넣을 것입니다. 첫째는 필터를 생성하는 함수이고, 두번째는 필터의 핀을 찾아내는 함수이며, 마지막으로 각각의 다이렉트쇼 카테고리에서 음성이나 비디오 입력장치 등을 얻어 오는 함수입니다.

우선 필터를 생성하는 함수는 사실 필터그래프를 생성하는 함수와 90%이상 동일합니다. 왜냐하면 필터 자체가 하나의  COM Object이기 때문에 CoCreateInstance함수를 사용하여야 하기 때문입니다. 필터그래프를 생성했던 것과 동일하게, 만들고자 하는 함수 안에는 CoCreateInstance 함수 하나만이 달랑 위치해 있게 될 것입니다.
자, 그렇다면 굳이 이렇게 필터를 생성하는 함수와 필터그래프를 생성하는 함수를 별도로 구분하여 만들어 놓을 필요가 있을까하고 생각하실 것입니다. 두가지 모두 COM Object이기 때문에 COM Object를 생성하는 함수를 하나 만들어 두고, 필터그래프나 필터를 생성할때 동일하게 사용하면 되지 않을까하는 생각이 들기도 하실 것입니다. 결론은 그렇게 해도 됩니다. 다만 저는 이렇게 두가지 별도의 함수로 만들어 놓은 것이 실보다는 득이 더 많다고 생각한 것일 뿐입니다.

우선 첫째로, 필터그래프나 필터를 생성할 때마다 그 두가지를 분별해서 생각할 수 있게 해주며, 두번째는 사실 범용적인 COM Object를 생성하는 방식은 매개변수 하나를 더 설정해야 하는데요, 이것은 필터를 생성할 때마다 상당히 번거롭습니다. 필터가 한두개라면 모르지만 앞으로 10개 이상의 필터를 자유롭게 생성해야 하는데 동일한 매개 변수를 매번 설정하는게 참 번거롭기 때문입니다. 아무튼 이것은 코딩의 관점이니 구태여 저의 형식을 강요하지는 않겠습니다. 자, 그러면 아래의 완성된 로직을 보겠습니다.

function TBaseDShow.CreateFilter(const clsid: TGUID; var Filter: IBaseFilter): Boolean;
begin
  Result := False;
  if Failed(CoCreateInstance(clsid,
                                             NIL,
                                             CLSCTX_INPROC_SERVER,
                                             IID_IBaseFilter,
                                             Filter))
  then Exit;
  Result := True;
end;

위에서 보시면 아시겠지만, 필터그래프를 생성시키는 로직과 거의 비슷합니다. 설명은 드리지 않았지만 매개 변수중의 하나인 CLSCTX_INPROC_SERVER가 의미하는 것이 바로 In Process형의 COM Server임을 나타낸다는 것을 짐작하실 것입니다. 이것을 도움말 파일에서 찾아보면 다음과 같습니다. '이 파라미터는 개체가 동적 링크 라이브러리 (DLL)로서 처리 되어 애플리케이션의 처리의 일부로서 실행되는 것을 나타낸다.' 예상했던 것과 동일한 정의가 되어있음을 확인할 수가 있습니다.

자, 이제 함수의 매개변수중 두번째 IBaseFilter를 보아 주시기 바랍니다. 이 인터페이스는 모든 필터들이 반드시 상속 받아야할 인터페이스입니다. 그러나 반드시 있어야 할 인터페이스라고 해서 없으면 필터를 생성하지 못한다라는 것은 아닙니다. (실제로 확인은 해보지 않았지만 이것도 COM  Object의 일종이니 객체가 생성되긴 할 것이라고 봅니다.)

이 인터페이스가 없으면 필터그래프에 등록할 수가 없기 때문입니다. 앞서서 우리는 필터그래프가 어떠한 역할을 하는지를 말씀드렸습니다. 즉, 필터들을 필터그래프 자신에게 등록하고 그것들을 서로 연결시켜주는 서비스를 해준다고 하였지요. 이렇게 필터그래프에 등록할때에는 반드시 공통되는 인터페이스가 필요한데요, 이것이 바로 IBaseFilter라는 인터페이스입니다. 이 부분을 미리 살짝 보여드린다면 다음과 같은 코드가 될 것입니다.  

  1)  DongFilter : IBaseFilter;      //인터페이스형 변수선언. 
  2) CreateFilter(CLSID_DongFilter,DongFilter);     //필터를 생성한다. 
  3) FilterGraph.AddFilter(DongFilter ,'DongFilter Filter');    //필터그래프에 생성한 필터를 등록한다. 
 
위에서 보시듯이 순서대로 생각하시면 되겠습니다. 먼저 필터의 IBaseFilter 형 타입의 변수를 선언합니다. 그리고 두번째로 생성하면서 동시에 IBaseFilter 인터페이스를 얻어옵니다. 마지막으로 이  IBaseFilter형 타입의 변수를 필터그래프에 추가해 줍니다.(뒤쪽의 'DongFilter Filter'라는 것은 일종의 TPanel에서 Caption  정도라고 생각하시면 되겠습니다.)  여기까지...

[2]필터의 핀 인터페이스를 얻어오는 함수.

이제부터 조금 까다로운 필터의 핀 인터페이스를 얻어오는 함수에 대하여 설명해야 하겠습니다. 그런데 이 함수를 논하기전에 우선 필터의 핀이란 대체 무엇인지를 알아야 할 것입니다. 여러분은 전에 GraphEdit을 사용해서 필터를 비주얼하게 생성시켰던 것을 기억하실 것입니다. 이 필터들의 꽁지와 꽁지를 마우스로 잡아당겨서 화살표로 연결시켜주었는데요, 바로 이 꽁지부분을 필터의 '핀'이라고 지칭하는 것입니다. 사실 GraphEdit에서 보기에는 '핀'의 모습이 거대한 필터의 크기에 비하여 너무 보잘 것이 없습니다. 그래픽상으로는 그냥 조그마한 점에 불과한 것인데요, 실제 필터의 내부에서는 배보다 배꼽이 더 크게 구현되어 있습니다. 즉, 모든 로직의 대부분이 이 필터의 '핀'에 집중되어있다는 것입니다.

이것이 무슨 의미인지를 예를 들어서 보겠습니다. 우선 아래의 간단한 클래스를 보시겠습니다.

  TDong = class(TObject)
    Img : TImage;  //멤버객체(혹은 멤버필드)
   end;

위에서 보시면 아시겠지만 TDong이라는 클래스 안에 Img라는 TImage형 타입의 멤버객체가 또다시 존재하고 있습니다. 필터의 핀도 이와 거의 동일한 존재입니다. 즉, 필터가 하나의 COM Object라 한다면 핀은 그 COM Object 안에 포함된 또다른 형태의 멤버 COM Object라는(위에서 Img와 같은) 것입니다. 여기까지 이해가 되셨다면 왜 제가 처음에 배보다 배꼽이 더 크다고 비유하셨는지 아셨을 것입니다.
위의 TDong이라는 클래스에서 TDong이라는 객체는 사실 껍데기에 불과하고 달랑 TImag형 멤버객체인 Img 변수가 더 큰 비중을 차지하는 것처럼 보여집니다. 바로 필터의 '핀'이라는 것도 이와 같은 방식으로 필터 안에 놓여져 있기 때문에 그러한 비유를 한 것입니다. 필터에 있어서는 '핀' 이 전부라고 생각해셔도 무방할 정도입니다.  이것은 나중에 필터를 집적 만들어 보시면 아시게 될 것입니다.  일단은 이정도 이해 만을 가지고 다음으로 넘어가겠습니다.

자, 그렇다면 이제 문제의 핵심을 살펴봐야 겠습니다. 우리가 어떤 COM Object 에서 인터페이스를 뽑아내기란 식은죽 먹기였습니다. 바로 QueryInterface라는 메소드를 사용해서 가능했던 것이죠. 그러나 COM Object 안에 포함되어있는 또다른 멤버 COM Object의 인터페이스를 뽑아내는 것은 간단한 일이 아닙니다.
그래서 필터에서 그것을 가능하게 하기 위하여 메소드를 하나 제공해 줍니다. 바로 FindPin 이라는 메소드 입니다. 즉 Dong 이라는 IBaseFilter 인터페이스형 변수가 있다면, Dong.FindPin(Pin이름, 받아낼 '핀' 타입변수) 이런 형식으로 핀 인터페이스를 얻을 수가 있습니다. 그러나 저는 이 방식을 사용하지 않습니다. 이것에 대하여 부연설명을 해드려야 겠습니다.

핀의 인터페이스를 얻는 방식으로는 두가지 접근방식이 있습니다. 하나는 핀의 이름으로 검색하는 것이고, 또다른 하나는 핀의 위치로 검색하여 얻어내는 것입니다. 예를 들어 다음과 같은 필터가 있다고 합시다. 입력핀은 1 개이고 출력핀은 3개 정도입니다. ( 필터에서 핀은 수없이 많이 만들 수가 있음.)
 

                                       Dong.ax 필터.
                              ┌──────────┐ 
                              │                               ■  (출력핀1)
                              │                              │
              (입력핀1)  ■                                ■  (출력핀2)
                              │                              │ 
                              │                               ■  (출력핀3)
                              └──────────┘


위에서 만일 '입력핀1'을 얻고자 한다면(정확히 말씀드리자면 입력핀1이라는 COM Object의  IPin 인터페이스) 우리는 '입력'이라는 방향을 알고 있고 위에서 첫번째 순서라는 것을 알고 있기 때문에 이러한 조건을 가지고 원하는 핀을 얻을 수가 있습니다. 또한 '출력핀3'을 얻고자 한다면 '출력'이라는 핀의 방향을 알고 있고, 위에서 세번째에 있기 때문에 이러한 조건을 가지고 '출력핀3'을 얻을 수가 있는 것입니다.

정리하자면 핀을 검색하는 방식으로는 FindPin이라는 메소드를 사용하여 찾는 것과, 핀의 방향과 순서를 조건으로 에뉴무레이트(열거)하여 찾아내는 방식, 이렇게 두가지가 있다는 것입니다. 그런데 저는 주로 후자의 방식을 사용하는데요, 여기에는 이유가 있습니다. 

솔직히 말씀드리자면 FindPin 이라는 메소드가 실은 제대로 작동하지 않는 경우가 종종 있습니다. 여기에는 필터를 개발하는 사람들의 '소홀함'도 지적될 수가 있겠는데요, 아무튼 그 이유 여하를 떠나서 상당히 자주 잘못된 핀을 얻어올 수가 있다는 사실입니다.
제가 어디서 읽었는지는 가물가물 합니다만 그곳에서도 이렇게 핀의 이름으로 검색하지 말라고 경고한 것으로 기억납니다. 아무튼 저는 핀찾기에서 '조건'으로 에뮬레이트합니다. 그리고 남들도 이러한 방식을 선호하고 있다는 사실을 종종 확인할 수가 있습니다. 아주 시간이 없고, 그래서 미칠것 같은 스트레스에 휩싸여 있었을 적에 한번 '이름'으로 찾기를 시도한 적이 있었는데요, 결국 원하는 핀을 몇시간 동안이나 찾지 못해 헤메였던 기억이 생생 합니다. 
DShow는 이렇게 에뮬레이트 방식을 주로 사용합니다. 비단 핀찾기 뿐만이 아니라 나중에 카테고리를 뒤져야 할 때에도 에뮬레이트(열거) 방식을 사용할 것입니다.  자, 이제 핀을 찾는 함수를 다음과 같이 선보이겠습니다.  
      
     function TBaseDShow.FindPinOnFilter(const Filter: IBaseFilter;
                                                                 const PinDir: TPinDirection;
                                                                 var Pin: IPin): HRESULT;
     var
       IsConnected : Boolean;
       hr: DWORD;
       EnumPin: IEnumPins;
       ConnectedPin: IPin;
       PinDirection: TPinDirection;

     begin
       Result := S_False;
       if not Assigned(Filter) then exit;
       hr := Filter.EnumPins(EnumPin);
 
       if(SUCCEEDED(hr)) then
       begin

        while (S_OK = EnumPin.Next(1, Pin, nil)) do
         begin

           //핀이 연결되었는지 조사.
           hr := Pin.ConnectedTo(ConnectedPin);
           if hr = S_OK then
           begin
             IsConnected  := True;
             ConnectedPin := nil;
           end else IsConnected := False;
 
           //핀의 방향을 검사
           hr := Pin.QueryDirection(PinDirection);

           //매개변수의 핀방향과 동일하고 현재 연결된 상태가 아니라면 루프에서 탈출. 
           if (hr = S_OK) and (PinDirection = PinDir) and (not IsConnected) then break;

           pin := nil;
         end;

         Result := S_OK;
       end;
       EnumPin := nil;
     end;


위의 함수 내용에 대해서는 일일이 설명을 하지 않겠습니다. 기본적인 로직에 속하는 것이기 때문입니다. 하지만 여러분이 혹시 눈치채셨을지 모르겠지만 약간은 이상하다는 생각이 드실 것입니다. 그렇습니다. 위의 함수에서는 방향을  지시하는 매개변수는 있지만 위치를 지정하는 매개변수는 존재하지 않습니다. 

사실 위의 첫 모델에서는 위치를 지정하는 매개변수가 있었습니다. 하지만 점점 제 자신에 맞게 다듬다가 보니까 이렇게 단순화 된 것입니다. FindPinOnFilter함수는 C로 된것을 델파이로 포팅한 것입니다. 제 기억으로는 원래 더 복잡했던 것으로 기억합니다. 아무튼 여러분도 자신만의 FindPinOnFilter 함수를 개발하여 사용하실 날이 도래할 것입니다.

굳이 설명을 드리자면 위에서는 핀의 순서상에서 가장 첫번째로 '접속되지 않은' 핀을 리턴한다는 것입니다. 이 함수를 사용하여 실제로 핀(IPin 인터페이스)을 얻어내야 할 것입니다.  

출처 : 델마당  dong님의 글(dongsoft)
posted by 유돌이
2008. 12. 29. 23:45 델파이
우리는 전편에서 COM의 인터페이스에 대하여 알아보았습니다. 그러나 아직까지도 인터페이스에 대하여 감이 잘 오질 않으실 것입니다. 그렇지만 조금은 참고, 중요한 몇가지에 대하여 정리해 보겠습니다.

  1) COM 오브젝트를 생성하기 위해서는 CoCreateInstance라는 함수를 사용하는데 이 함수에는 세가지의 매개변수를 지정한다. 하나는 COM 오브젝트를 식별하기 위한 GUID인 CLSID 이며, 또다른 하나는 IID_로 시작하는 인터페이스 식별자이고, 나머지는 리턴되어 받아낼 실제 인터페이스형 타입의 변수이다. 

  2) CoCreateInstance 함수를 사용하여 COM 오브젝트를 생성하면서 우리는 첫번째 인터페이스를 뽑아낸다.

  3) 하나의 인터페이스가 또다른 인터페이스를 QueryInteface를 사용하여 국수 뽑듯이 뽑아낼 수가 있다.  

  4) 인터페이스를 뽑아내기 위해서는 반드시 두가지의 매개변수가 필요한데, 하나는 IID_로 시작되는 식별자이며 또다른 하나는 리턴되어 받아낼 실제 인터페이스형 타입의 변수다. (일반적으로 I로 시작되는...)  그런데 식별자와 인터페이스 타입의 변수에 사용된 GUID는 동일한 값이거나 혹은 상속관계에 있는 값이어야 한다. 여기까지...
 
[1]필터그래프의 인터페이스.

필터그래프 매니저 COM 오브젝트(이하 필터그래프) 에는 수많은 인터페이스가 존재합니다. 헬프파일을 찾아보시면 25개 정도의 인터페이스를 발견하실수가 있을 것입니다. 이 중에서 우리는 3가지의 인터페이스만을 사용할 것입니다.
만일 여러분이 나중에 좀더 복잡한 DShow 어플을 개발하고자 할때에, 이렇게 수많은 인터페이스 중에서 필요한 것을 골라서 선택하여 사용하시면 될 것입니다. 그러나 현재로는 3가지 아주 핵심적인 인터페이스만을 생각해보기로 하죠.

첫째로 DShow 어플에서 생성되어진 수많은 필터들을 연결해주거나 끊어 주거나 해야 합니다. GraphEdit에서 여러분은 직접 필터들을 마우스로 드래그하여 화살표를 꽁지에서 꽁지로 연결해 주었을 것입니다. 이러한 연결을 해주는 서비스 인터페이스가 필요합니다. 이 인터페이스가 바로 IFilterGraph라는 놈인데요, 우리는 여기서 상속받아서 기능이 좀더 많은 IGraphBuilder라는 인터페이스를 첫시발로 CoCreateInstance을 사용해 필터그래프 생성과 동시에 뽑아내었죠. 자, IGraphBuilder라는 인터페이스가 필요한 이유는? 필터들을 연결해 주기 위해서라고 일단 간단히 생각합시다.

두번째로 필요한 것은 필터를 연결한다음에 이것들을 실행시켜줄 인터페이스가 있어야 합니다. 즉,  Run하고, Stop하고, Pause하는 기능을 가진(이것을 다른말로 스트림을 제어하는) 인터페이스가 필요합니다. 이것을 IMediaControl 이라고 합니다. 이 인터페이스는 필수입니다. 이게 없으면 필터들 연결만 하고 끝이겠죠. 동영상이 Play되지 못한다는 말이죠.

세번째의 인터페이스는 사실 없어도 되는 것이지만, 좀더 세련된 DShow 어플을 만들기 위해서는 필수입니다. 일단 IMediaControl를 사용하여 DShow를 작동시켜 동영상을  Play시키면 디폴트로 별도의 랜더링 윈도우가 뜨면서 실행되는데요, 이게 영 볼품이 없습니다. 자신이 만든 어플의 메인 폼위에서 실행되었으면 하는데요, 이렇게 영상이 랜더링 되는 위치를 설정할 수 있게 해주는 인터페이스가 바로 IVideoWIndow라는 인터페이스입니다. 이 세가지는 거의 필수라고 보시면 되겠습니다.

자, 이제 이 나머지 인터페이스를 어떻게 뽑아낼까요? 우리는 처음에 필터그래프에서 IGraphBuilder라는 인터페이스를 FilterGraph라는 변수에 받아내었습니다. 요놈을 가지고 나머지 인터페이스를 뽑아내어 봅시다. 
일단 변수선언을 다음과 같이 합니다. 

    MediaControl: IMediaControl;
    VideoWindow: IVideoWindow;

그리고 다음과 같이 QueryInterface를 사용합니다.

  FilterGraph.QueryInterface(IID_IMediaControl, MediaControl);
  FilterGraph.QueryInterface(IID_IVideoWindow, VideoWindow);

자 이렇게 나머지 두개의 인터페이스를 얻을 수가 있었습니다. 그렇다면 여기까지 한가지 의문이 들 것입니다. 우리가 필터그래프 COM 오브젝트를  CoCreateInstance 함수를 사용하여 생성하였는데, 소멸(제거)는 어떻게 하는 것이지라고 말이죠. 자, 이것은 아주 중요한 이야기니까 별도의 설명을 해야하겠습니다. 

[2]COM의 소멸. 

자, 조금 헷갈리는 분야가 나왔습니다. COM  Server는 대체 무엇이고 COM Object는 또 무엇인가. 위의 제목이 COM의 소멸이라고 했는데 그렇다면 COM Server의 소멸을 의미하는 것인가, 아니면 COM Object 의 소멸을 의미하는 것인가. 기타 등등.  여러분은 이제 COM Server와 COM Object(혹은 객체)를 분별해야만 하는 시점이  다가온 것입니다.  

COM Server의 종류에는 Out of Process 방식과  In Process 방식의 두가지가 있습니다. 전자는 Exe형식의 완전한 독립된 응용어플로 생각하시면 되고요, 후자는 DLL 형태를 취하고 있습니다. 자, 형태론적인 관점에서 보면 두개의 차이가 명확해집니다. 그리고 내부적인 차이도 사실 무척이나 다릅니다. 여기에 대해서 잠깐 쉬었다 갈까요.

우리가 일반적으로 프로그램을 실행하면 하나의 프로세스가 생성되어서 윈도우즈 운영체제로 부터 연속된 메모리 주소를 부여받게 됩니다. 32비트 컴퓨터니까 2의 32승인 약 4G바이트의 메모리가 되겠죠. 그런데 여러분도 아시다시피 아무리 많은 응용프로그램을 실행시켜도 각각의 프로세스(혹은 프로그램)은 상대방의 메모리 주소를 전혀 침범하지 않고 잘만 작동합니다. 게다가 멀티 작업까지 가능하네요. 이게 대체 어떻게 된 것일까요.
하나의 프로세스가 실행되면서 윈도우즈 운영체제로부터 받는 메모리 주소는 사실 실제적인 물리주소가 아니라 가상 메모리의 주소를 받기 때문에 가능한 것입니다. 윈도우즈는 각각의 프로세스가 절대로 상대방의 물리적인 메모리 번지를 침범하지 못하도록 세심한 주의를 기울여 가상번지를 할당해 주고 있으므로 안심하고 멀티 태스킹 작업이 가능한 것입니다.

그런데 위의 사실을 역으로 생각하면 두개의 독립된 프로세스가 서로 상대방의 메모리를 침범하기란 운영체제가 허락하지 않는한 불가능한 일입니다. 그러나 이것을 가능하게 하는 두개의 방식이 있는데요, 하나는 공유메모리를 사용하는 것이고요, 또다른 하나는 바로 COM에서의 마샬링을 사용하는 것입니다. 저는 이제까지 공유메모리만 사용해오고 있었는데요, 예전에 한번 접근속도가 느린것 같아서 마샬링이란 것을 고려해 보았던 적이 있습니다.

아무튼, 결론은요 exe형(Out of Process) 타입의 COM Server는 내부적으로 마샬링과 같은 복잡한 과정을 거친다는 것과(왜냐하면 실제로 완전히 다른 프로그램의 메모리 영역에서 서비스를 받아야 하기 때문에),  Dll형(In Process) 타입의  COM Server는 그런일이 없기 때문에 상대적으로 간단한다는 것입니다. 이러한 것은 그냥 머릿속에 배경지식으로 활용하시길 바랍니다.

자, 이야기기 또 삼천포로 빠졌습니다. 여기까지 우리는 COM Server의 두가지 큰 형태를 알아보았습니다. 여기에 한 가지를 덧붙여 말씀드리자면 다행스럽게도 우리가 사용하는 DShow는 In Process타입의 DLL형태를 취하고 있다는 것입니다.

만일 우리가 Dong.Exe라는 COM Server(Out of Process)을 개발하였다면, Dong.Exe자체를 하나의 COM Server 라 하며, 그 안에는 여러가지 종류의  COM Obect가 존재할 수 있을 것입니다. 또한 마찬가지로 Dong.dll라는 COM Server(In Process)을 개발하였다면 Dong.dll 자체를 하나의 COM Server라 할 것이고, 그 안에는 수많은 COM Object가 존재할 수 있을 것입니다. 자, 이제 COM Server와 COM Object에 대한 어느정도의 분별이 생기셨을 것입니다.

조금더 예를 든다면, 여러분은 나중에 간단한 형태의 필터를 개발하게 될 것입니다. 이것은 물론 In Porcess 형태의 일종의 COM Server입니다. 만일 여러분이 비디오 스트림의 상하를 거꾸로 변경하는 Convert.dll 필터를 개발하였다면, 바로 이 Convert.dll이 하나의 COM Server이자 동시에 달랑 하나뿐인 COM Object를 가지고 있을 뿐인 것이죠.

현재 DirectShow에는 기본 필터가 수십여가지 있습니다. 이 필터 하나하나가 각각 별도의  In Process COM Server 의 DLL 형태로 존재할까요? 그렇지가 않습니다. DirectShow는 하나의 Dll 형태의 Server에 많은 형태의 (필터그래프 매니저를 비롯한 다양한 종류의 필터) COM Object를 담아 두고 있습니다. 이 DLL 파일의 이름이 바로 Quartz.dll입니다. Quartz.dll이라는 In Process COM Server에는 수많은 DShow용 COM Object가 포함되어 있습니다.

처음에 DShow를 공부할때, 필터를 하나의 ax확장자를 가진 파일 단위로만 생각해서 헷갈렸던 기억이 납니다. 왜냐하면 DirectShow에서 제공하는 기본필터들을 찾아 보려고 CLSID로 레지스트리를 일일이 뒤졌는데 모두다 Quartz.dll이라고 설정되어 있었기 때문입니다. 필터는 하나의 COM Server가 아니라 하나의 COM Object입니다. 따라서 어떤 하나의 Dll 형 서버에 수많은 필터가(혹은 필터그래프 매니저와 같은 다른 COM Object가...) 포함되어 있을 수가 있는 것입니다.

여러분이 만일 CoCreateInstance로 COM Object를 생성시켰다면 COM 라이브러리는(여러분은 CoInitialize를 사용하여 이미 COM 라이브러리를 초기화 하셨습니다.) 매개변수 중 하나인 CLSID를 파악하여 레지스트리에서 해당 CLSID의  COM Object가 포함된 COM Server(하드 디스크 어딘가에 저장된 dll)을 로딩할 것입니다. 그런데 이미 로딩된 COM Server에 있는 어떤 COM Obejct를 또다시 CoCreateInstance를 사용하여 인스턴스화(객체화)하였다면 어떨까요? COM 라이브러리가 이미 로딩되어 있는 Dll을 다시 로딩하지는 않을 것입니다.

이제 우리는 In Process COM Server가 어떤 형식으로 로딩되는 것인지 아셨을 것입니다. 그렇다면 이제 COM Server가 어떤 방식으로 메모리에서 언로딩(소멸)되는지 알아보도록 하겠습니다.  


    Dong.DLL  In Process Server
    ┌──────────┐
    │                              │
    │  A COM Object    ─┼───▶ CoCreateInstance로 인스턴스화 ( Dong.dll이 로딩된다.)
    │  B COM Object       │
    │  C COM Object    ─┼───▶ CoCreateInstance로 인스턴스화 ( Dong.dll이 이미 로딩되어 있다.)
    │           .                  │
    │           .                  │
    └──────────┘ 

위와같이 Dong.dll이라는 이름의 COM Server가 있다고 생각해 봅니다. 우리는  CoCreateInstance를 사용하여 A와 C라는 COM Object를 위의 순서대로 인스턴스화 하였습니다. 이제 이 상태에서 A와 C라는 COM Object가 소멸되면 COM 라이브러리는 Dong.dll을 자동으로 메모리에서 언로딩할 것입니다. 즉,다시말해서 하나의 COM Server에서 사용되어졌던 모든 COM Object가 소멸되면 해당 COM Server 는 자동으로 언로딩된다는 것입니다. (실제로 하나의 COM Server를 메모리에 로딩하거나 언로딩할때 실행되는 로직이 별도로 있습니다. 그래서 로딩과 언로딩이라는 직설적인 표현보다는 생성과 소멸이라는 말이 약간은 더 어울리는 것입니다. 하지만 여기까지는 일단 자동으로 로딩되고 언로딩된다고 이해하여도 상관은 없을 것입니다.)

자... 그렇다면 이제 어떻게 해서 COM Object를 소멸시킬 수가 있을지를 생각해 봅시다. 우리가 CoCreateInstance를 사용하여 필터그래프라는 COM Object를 생성하면서 최초로 IGraphBuilder라는 인터페이스를 얻어왔던 것을 기억하실 것입니다. 그리고 이 IGraphBuilder라는 인터페이스 형 타입의 변수에서 QueryInterface를 사용하여 나머지 주요 한 두개의 인터페이스인 IMediaControl과 IVideoWindow를 얻을 수가 있었습니다. 만약, 이 상태에서 세개의 인터페이스를 모두 제거한다면 필터그래프라는 COM Object는 자동 소멸하게 될 것입니다. 즉, 이것을 정리한다면 A라는 어떤 COM Object를 생성하여 그 안에 포함된 다수의 인터페이스 중에 몇개의 인터페이스를 얻어와 사용하다가, 그 얻어왔던 인터페이스를 모두 제거한다면 A라는 COM Object는 더이상 외부에 노출된 인터페이스가 없으므로 자동 소멸한다는 것입니다. 

이제 결론에 다다랗습니다. 그렇다면 얻어온 인터페이스는 어떻게 소멸시키는가? 이것은 너무 간단해서 싱거울 정도입니다. 얻어온 인터페이스 형 타입의 변수에 nil값을 주면 됩니다. 여기까지를 한번 종합해서 정리하겠습니다.  

  1) COM Server는 Exe나 DLL과 같이 하나의 파일 단위를 의미한다. 

  2) 하나의 COM Server 안에는 수많은 COM Object가 존재할 수 있다. 

  3) COM Server는 최초로 그 안에 포함된 COM Obejct가 CoCreateInstance로 생성될 때 COM 라이브러리에 의해(여러분은 처음에 CoInitialize라는 함수로 COM 라이브러리를 초기화하셨습니다.) 자동으로 로딩(생성)되며, 반대로 생성되었던 COM Server안의 모든 COM Object가 소멸되면 자동으로 언로딩(소멸)된다.

  4) COM Object는 그 안에 포함되어 있던 수많은 인터페이스 중에서 외부로 노출되어 졌던 모든 인터페이스가 해제되면 자동으로 소멸한다. 

  5) 인터페이스를 해제하기 위해서는 얻어온 해당 인터페이스형 타입의 변수에 nil을 대압하면 된다.  


  ****************************************** 중요. ********************************************* 
  COM에 대하여 위와같은 해설은 수박 겉핥기 식이라고 할 수가 있습니다. 별도의 책을 사서 한번쯤 휘둘러 읽어보실 필요가 있습니다. 그 시간이 정 안된다면, 김용성님의 Visual C++ 6완벽가이드(일명 눈깔책)을 구입하여 그 안에 정리된 COM 부분을 익히시는 것이 상당한 도움이 될 것이라고 생각합니다.  
  *********************************************************************************************

출처 : 델마당  dong님의 글(dongsoft)


 

posted by 유돌이
2008. 12. 24. 18:04 델파이

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure ListFiles(D,Name,SearchName : String);
  end;

var
  Form1: TForm1;

implementation
{$R *.DFM}

procedure TForm1.ListFiles(D, Name, SearchName: String);
var
  SR: TSearchRec;
begin
  if D[Length(D)] <> '' then
    D := D + '';

  if FindFirst(D+Name, faAnyFile, SR) = 0 then
    repeat
      if (SR.Attr <> faDirectory) and (SR.Name[1] <> '.') then
        if AnsiUpperCase(SR.Name) = AnsiUpperCase(SearchName) then
          ListBox1.Items.Add(D+SR.Name); {파일을 찾으면 label1.Caption에 디렉토리를 표시}
    Until (FindNext(SR)<>0);
  FindClose(SR);

  if FindFirst(D+'*.*', faDirectory, SR) = 0 then
  begin
    repeat
      if ((Sr.Attr and faDirectory) = faDirectory) and
         (SR.Name[1]<>'.')
      then
        ListFiles(D+SR.Name+'', Name, SearchName); // 재귀적 호출을 한다
    until (FindNext(SR) <> 0);
  end;
  FindClose(SR);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  // c: 부터 하위 디렉토리에서 delphi32.exe 파일을 찾는다
  ListFiles('c:','*.*','project1.exe');
end;

end.

 

 

출처 : http://www.howto.pe.kr/


'델파이' 카테고리의 다른 글

OleVariant 형을 스트링으로 변환하는 방법  (0) 2008.12.29
쿠키 읽고/쓰기(GetCookie, SetCookie)  (0) 2008.12.29
실행파일 삭제  (0) 2008.12.24
키보드 이벤트[keybd_event  (0) 2008.12.24
레지스트리 값 읽고 쓰기  (0) 2008.12.24
posted by 유돌이
2008. 12. 24. 18:03 델파이

//자기 자신을 삭제하는 로직//

 

procedure DeleteMe;
var
BatchFile:TextFile;
BatchFileName:String;
ProcessInfo:TProcessInformation;
StartUpInfo:TStartupInfo;
begin
//
BatchFileName:=ExtractFilePath(application.exename)+'$$336699.bat';

AssignFile(BatchFile, BatchFileName);
Rewrite(BatchFile);

Writeln(BatchFile, ':try');
Writeln(BatchFile, 'del "' + application.exename + '"');
Writeln(BatchFile, 'if exist "' + application.exename + '"' + ' goto try');
Writeln(BatchFile, 'del "' + BatchFileName + '"');
CloseFile(BatchFile);

FillChar(StartUpInfo, SizeOf(StartUpInfo), $00);
StartUpInfo.dwFlags := STARTF_USESHOWWINDOW;
StartUpInfo.wShowWindow := SW_HIDE;

if CreateProcess(nil, PChar(BatchFileName), nil, nil,False, IDLE_PRIORITY_CLASS,
nil, nil, StartUpInfo, ProcessInfo) then
begin
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
end;
//Close;
end;


'델파이' 카테고리의 다른 글

쿠키 읽고/쓰기(GetCookie, SetCookie)  (0) 2008.12.29
파일 찾기  (0) 2008.12.24
키보드 이벤트[keybd_event  (0) 2008.12.24
레지스트리 값 읽고 쓰기  (0) 2008.12.24
웹에 있는 파일 다운로드 받기(UrlDownloadToFile)  (0) 2008.12.24
posted by 유돌이
2008. 12. 24. 18:02 델파이
VOID keybd_event(

      BYTE  bVK,       //가상 키코드

      BYTE  bScan,    //하드웨어 스캔 코드

      BYTE  dwFlags,  //동작 지정 플래그 ULONG_PTR

      DWORD dwExtraInfo   //추가정보

)

 

 [사용 예 ]

 - F5 키 누른 효과 

  keybd_event(VK_F5, 0, 0, 0);
  keybd_event(VK_F5, 0, KEYEVENTF_KEYUP, 0);

 

 

참고 : 가상키  값, 하드웨어 스캔코드  정보 있는 곳

http://www.codeproject.com/KB/system/keyboard.aspx


'델파이' 카테고리의 다른 글

파일 찾기  (0) 2008.12.24
실행파일 삭제  (0) 2008.12.24
레지스트리 값 읽고 쓰기  (0) 2008.12.24
웹에 있는 파일 다운로드 받기(UrlDownloadToFile)  (0) 2008.12.24
TWebBrowser 에서 텍스트 형태 바꾸기  (0) 2008.12.24
posted by 유돌이
2008. 12. 24. 18:01 델파이

[선언부]

var
  Reg: TRegistry;
  RegVal: String;

 

[초기화]

  Reg := TRegistry.Create;
   Reg.RootKey := HKEY_LOCAL_MACHINE;  
   Reg.OpenKey(REG_CLSID, True);

 

[값 읽기]

  RegVa := Reg.ReadString('REG_READ_NAME');

 

[값 쓰기]

  Reg.WriteString('REG_WRITE_NAME', '1');

 

[메모리 해제]

  Reg.Free;


'델파이' 카테고리의 다른 글

실행파일 삭제  (0) 2008.12.24
키보드 이벤트[keybd_event  (0) 2008.12.24
웹에 있는 파일 다운로드 받기(UrlDownloadToFile)  (0) 2008.12.24
TWebBrowser 에서 텍스트 형태 바꾸기  (0) 2008.12.24
델파이 자료형~!  (0) 2008.12.24
posted by 유돌이
2008. 12. 24. 17:59 델파이

//[함수 구현 부분]

 

function DownloadFile(SourceFile, DestFile: string): Boolean;
begin
  try
  Result := UrlDownloadToFile(nil, PChar(SourceFile), PChar(DestFile), 0, nil) = 0;
  except
  Result := False;
  end;
end;

// [함수 호출 부분~]

 

// 다운로드할 URL 경로
SourceFile = 'http://.../SB_DB.txt';
// 저장할 파일의 경로
DestFile = 'c:\SB_DB.txt';

if DownloadFile(SourceFile, DestFile) then begin
    ShowMessage('Download succesful!');
    ShellExecute(Application.Handle, PChar('open'), PChar(DestFile),
    PChar(''), nil, SW_NORMAL)
  end else begin
    ShowMessage('Error while downloading ' + SourceFile)
  end;

'델파이' 카테고리의 다른 글

키보드 이벤트[keybd_event  (0) 2008.12.24
레지스트리 값 읽고 쓰기  (0) 2008.12.24
TWebBrowser 에서 텍스트 형태 바꾸기  (0) 2008.12.24
델파이 자료형~!  (0) 2008.12.24
문자열 관련 함수  (0) 2008.12.24
posted by 유돌이
2008. 12. 24. 17:33 델파이

String.Compare :: 문자를 비교

 

String.Lower  :: 문자를 소문자로 변환


String.Mid :: 경로 지정

'델파이' 카테고리의 다른 글

TWebBrowser 에서 텍스트 형태 바꾸기  (0) 2008.12.24
델파이 자료형~!  (0) 2008.12.24
델파이에서 브라우저 띄우기  (0) 2008.12.24
델파이에서 dll 등록하기  (0) 2008.12.24
델파이 폼 최소화 시키는 법  (0) 2008.12.24
posted by 유돌이
2008. 12. 24. 17:32 델파이

델파이 프로그램에서 브라우저를 띄우실려면

 

우선 툴파레트에서 'internet' 영역에 있는 'TwebBrowser'를 선택하셔서

브라우저가 나타날 위치에 드레그하시고...

 

procedure TForm2.FormCreate(Sender: TObject);

 

부분에 아래와 같이 추가  하시면 됩니다.

 

=> WebBrowser.Navigate('http://blog.naver.com/cysnim12');


'델파이' 카테고리의 다른 글

델파이 자료형~!  (0) 2008.12.24
문자열 관련 함수  (0) 2008.12.24
델파이에서 dll 등록하기  (0) 2008.12.24
델파이 폼 최소화 시키는 법  (0) 2008.12.24
델파이 기본 함수  (0) 2008.12.24
posted by 유돌이
2008. 12. 24. 14:33 델파이
WinExec('Regsvr32 /s "파일명"',0);

'델파이' 카테고리의 다른 글

델파이 자료형~!  (0) 2008.12.24
문자열 관련 함수  (0) 2008.12.24
델파이에서 브라우저 띄우기  (0) 2008.12.24
델파이 폼 최소화 시키는 법  (0) 2008.12.24
델파이 기본 함수  (0) 2008.12.24
posted by 유돌이
prev 1 next