COM, ATL

ROT 와 모니커(Moniker) - 개념 및 델파이 코딩

디버그정 2008. 9. 10. 20:16

1. ROT가 뭘까요?


ROT는 Running Object Table의
약자입니다. 해석하자면 "실행중인 객체 테이블"쯤 되겠군요.
현재 컴퓨터 상에 실행중인 COM Object 들의 목록이 여기에 들어 있습니다.
MS워드를 실행시키면 MS워드가 실행중이라는 표시가 여기에 들어간다는
거죠. 하지만 모든 COM Object가 모두 기록 되어 있는것은 아닙니다.
여기에 기록하는건 OS가 아니라 COM Object 자신이기 때문이죠. COM
Object 스스로 자신이 실행되었다는 걸 테이블에다가 기록한 놈만 목록이
올라가 있는겁니다. 이건 Office 시리즈처럼 서로간의 의사소통을 위한
창구로 쓰이기도 하고, Windows Media Player 처럼 중복 실행을 방지하기
위한 목록으로도 사용됩니다.


또한 여기서 "모니커"라는
개념이 나옵니다. 이 모니커라는 것은 객체의 이름을 지정하는 또다른
표현입니다. 윈도우에서는 객체들을 구별하기 방법이 이미 존재하죠.
핸들이란 놈이 그 놈인데요, 이 핸들이란건 표현의 범위가 너무 좁습니다.
델파이의 TLabel이란 컴퍼넌트만 해도 핸들이 없죠. 그래서 핸들이란
놈은 시스템에서 표현되는 모든 객체들을 커버하지 못합니다. 이 모니커라는것은
OLE상에서 객체들의 구별을 위한 방법이며 매체의 역할을 하는 것입니다.
사실 이러한 설명만으로 이해가 쉽지 않겠지만 저 또한 이 모니커란
놈에대해 깊이있게 알지 못하니 어쩔수가 없겠네요. 자세한건 책이나
MSDN등을 참고하시고 일단 여기서는 ROT를 뒤지기 위한 수단 정도로
생각하시고 넘어가도록 하죠.


이 모니커란 것은 단지 이름일 뿐입니다.
테이블에 이름을 등록 했다면 다른 프로세스가 그 이름으로 등록객체를
액세스할 수 있어야 겠죠. 여기서 이름과 실제 객체를 묶어주는것을
바인딩(binding)이라고 하는데, 이 바인딩을 위해 IBindCtx라는 인터페이스가
또 등장합니다. 이건 뭐 그리 어려운것도 아니고 자세히 알 필요도 없을것
같고 하니 그냥 이렇게 써야하는구나 정도로 생각하도록 하죠.


2. 접근 방식


IRunningObjectTable 이라는 인터페이스가
있습니다.(ActiveX 유닛에 정의) 우리는 시스템으로부터 이 인터페이스를
얻어 오기만 하면 됩니다.


procedure TForm1.Button1Click(Sender: TObject);
var
  ROT : IRunningObjectTable;
  ENU : IEnumMoniker;
  MON : IMoniker;
  CTX : IBindCTX;
  DisplayName : PWideChar;
begin
  if FAILED(GetRunningObjectTable(0, ROT)) or
     FAILED(ROT.EnumRunning(ENU)) or
     FAILED(CreateBindCtx(0, CTX))
     then
    Exit;

  while SUCCEEDED(ENU.Next(1, MON, nil)) and Assigned(MON) do
     if SUCCEEDED(MON.GetDisplayName(CTX, MON, DisplayName)) then begin
        Memo1.Lines.Add(WideCharToString(DisplayName));
     end;
end;

initialization
  CoInitialize(nil);
finalization
  CoUnInitialize;


이건 버튼을 누르면 ROT를 뒤져서
현재 실행중인 객체들의 목록을 가져오는 예제 입니다.
GetRunningObjectTable
함수로 ROT의 인터페이스를 얻고 ENU에 모니커들의 목록을 얻은 다음
하나 하나 훑어 보는 방식입니다. 여러가지 시험결과 : 탐색기, IE,
Acrobat Reader는 ROT에 등록 되지 않습니다. 그리고 Office응용 프로그램,
Windows Media Player, Adobe PhotoShop 은 등록이 됩니다. 여기서는
일례로 윈도우즈 미디어 플레이어의 인터페이스를 뽑아내 보도록 하겠습니다.


procedure TForm1.Button2Click(Sender:TObject);
var
  ROT : IRunningObjectTable;
  ENU : IEnumMoniker;
  MON : IMoniker;
  CTX : IBindCTX;
  DisplayName : PWideChar;
  UNK : IUnknown;
begin
  if FAILED(GetRunningObjectTable(0, ROT)) or
     FAILED(ROT.EnumRunning(ENU)) or
     FAILED(CreateBindCtx(0, CTX))
     then
    Exit;


   while SUCCEEDED(ENU.Next(1, MON, nil)) and Assigned(MON) do
     if SUCCEEDED(MON.GetDisplayName(CTX, MON, DisplayName)) and
        (Pos('Microsoft Media Player', WideCharToString(DisplayName))>0) and
        SUCCEEDED(ROT.GetObject(MON, UNK))
        then
       ShowQueryInterface(UNK);

end;


아까와 비슷하지만
여기서는 모니커의 목록중에서 DisplayName에
'Microsoft
Media Player'라는 글자가 포함된 모니커를 찾아서 ROT.GetObject 로
그 객체의 인터페이스를 뽑아옵니다. 맨 아래의 ShowQueryInterface는
제가 그냥 만든 함수인데 저기다가 인터페이스를 하나 집어 넣으면 모든
인터페이스들로 QueryInterface를 날려서 성공적으로 돌아오는 인터페이스만
보여주는 놈입니다. 같이 첨부하기에는 분량이 많아 생략했는데, 지난
IUnknown 인터페이스 강좌를 참고하시면 쉽게 만들수 있을것으로 생각됩니다.
어쨌든 그 결과는 이렇게 나옵니다.

IViewObject :: {0000010D-0000-0000-C000-000000000046}
IRunnableObject :: {00000126-0000-0000-C000-000000000046}
IViewObject2 :: {00000127-0000-0000-C000-000000000046}
IConnectionPointContainer :: {B196B284-BAB4-101A-B69C-00AA00341D07}
IUnknown :: {00000000-0000-0000-C000-000000000046}
IMarshal :: {00000003-0000-0000-C000-000000000046}
IPersistStorage :: {0000010a-0000-0000-C000-000000000046}
IPersistFile :: {0000010b-0000-0000-C000-000000000046}
IDataObject :: {0000010e-0000-0000-C000-000000000046}
IOleObject :: {00000112-0000-0000-C000-000000000046}
IOleInPlaceObject :: {00000113-0000-0000-C000-000000000046}
IOleWindow :: {00000114-0000-0000-C000-000000000046}
IOleInPlaceFrame :: {00000116-0000-0000-C000-000000000046}
IOleInPlaceActiveObject :: {00000117-0000-0000-C000-000000000046}
IOleClientSite :: {00000118-0000-0000-C000-000000000046}
IOleInPlaceSite :: {00000119-0000-0000-C000-000000000046}
IDispatch :: {00020400-0000-0000-C000-000000000046}
IActiveMovie :: {05589FA2-C356-11CE-BF01-00AA0055595A}
IPersistMoniker :: {79eac9c9-baf9-11ce-8c82-00aa004ba90b}
IQuickActivate :: {CF51ED10-62FE-11CF-BF86-00A0C9034836}
IPersistPropertyBag :: {37D84F60-42CB-11CE-8135-00AA004BB851}
IPersistStreamInit :: {7FD52380-4E07-101B-AE2D-08002B2EC713}
IOleInPlaceSiteEx :: {9C2CAD80-3424-11CF-B670-00AA004CD6D8}
IProvideClassInfo :: {B196B283-BAB4-101A-B69C-00AA00341D07}
IOleControl :: {B196B288-BAB4-101A-B69C-00AA00341D07}
IOleControlSite :: {B196B289-BAB4-101A-B69C-00AA00341D07}
ISpecifyPropertyPages :: {B196B28B-BAB4-101A-B69C-00AA00341D07}
IPerPropertyBrowsing :: {376BD3AA-3845-101B-84ED-08002B2EC713}
IProvideClassInfo2 :: {A6BC3AC0-DBAA-11CE-9DE3-00AA004BB851}
IActiveMovie2 :: {B6CD6554-E9CB-11D0-821F-00A0C91F9CA0}
IOleCommandTarget :: {B722BCCB-4E68-101B-A2BC-00AA00404770}
IObjectSafety :: {CB5BDC81-93C1-11CF-8F20-00805F2CD064}
IServiceProvider :: {6D5140C1-7436-11CE-8034-00AA006009FA}
IMediaPlayer2 :: {20D4F5E0-5475-11D2-9774-0000F80855E6}
IMediaPlayer :: {22D6F311-B0F6-11D0-94AB-0080C74C7E95}
IMediaPlayerDvd :: {746EB440-3835-11D2-9774-0000F80855E6}
IMediaBindStream :: {920F0DE3-91C5-11D2-828F-00C04FC99D4E}
INSPlay1 :: {265EC141-AE62-11D1-8500-00A0C91F9CA0}
INSPlay :: {E7C4BE80-7960-11D0-B727-00AA00B4E220}
INSOPlay :: {2179C5D1-EBFF-11CF-B6FD-00AA00B4E220}
IActiveMovie3 :: {265EC140-AE62-11D1-8500-00A0C91F9CA0}
ITIMEMediaPlayer :: {E6FAA0B2-69FE-11D2-B259-00A0C90D6111}
IClientSecurity :: {0000013D-0000-0000-C000-000000000046}
IMultiQI :: {00000020-0000-0000-C000-000000000046}
IProxyManager :: {00000008-0000-0000-C000-000000000046}


지 많죠.. 이 많은 인터페이스들을 모두 입맛대로 다룰수 있게 되는것입니다.
여기에 나온 인터페이스들의 대부분은 윈도우즈 미디어 플레이어 컴퍼넌트를
임포트하거나 NSPlay, ActiveMove 등을 임포트하면 사용할 수 있게 됩니다.


* 델파이에서 ROT.GetObject를 수행하면
가끔(이게 프로그래머 미치게 하는 단어죠) 해당 객체가 ROT에서 사라져
버리는 현상이 발생하는데, 정확한 원인을 알 수 없지만 ROT상에 객체를
등록할때 사용되는 옵션중에
ROTFLAGS_REGISTRATIONKEEPSALIVE가
세팅되지 않은 객체에 대해서 이런 일이 발생하는 것으로 추정됩니다.