반응형

QueryInterface의 이해

 저번 장에서 우리는 IUnknown의 세가지 함수중에 두가지를 살펴보았다. 이번엔 약속한대로 QueryInterface라는 놈을 본다.


이 블로그의 포스트에 있는 COM의 장점을 본 사람이거나 아니면 COM을 조금이라도 이해하는 사람들은 COM이 무지막지한 Encapsulation을 보장하며, 이를 위한 구현 방법으로  COM이 채택한 방식은 Interface-Based의 프로그래밍 개념이고, 더 나아가서는 이것이 Virtual 함수 형태로 구현되어 있다는 것을 기억할 것이다. Virtual 함수라는 놈은 C++에서는 내부적으로 vTable과 vPtr을 통해 함수의 포인터를 얻어와 그 구현이 이루어진다는 것도 알고 있을것이다. (무지 미안하구 건방져보이지만, 이부분이 이해 안돼면, 다른 책에서 가상함수 부분과 인터페이스 프로그래밍 부분을 공부하고 오셔야 합니다. 이 글은 COM에 대한설명이고, 기본적인 개념은 이해하고 있으신 분을 대상으로 합니다.- 사실 모르면 그냥 넘어가두 돼긴 합니다.)


 뭐 어쨌던간에.... COM을 사용하고자 하는 외부에서 보기에는  COM 객체의 인터페이스만이 보이기 땜에.... 그리고 그 인터페이스를 통해서만 인터페이스에 딸려있는 함수들(곧 서비스들)을 사용할수 있기 땜다는 겁니다.


그러니까 COM객체를 사용하고자 하는 분은 일단 COM객체를 부른담에.... 인터페이스를 얻고, 그 인터페이스에 딸린 함수를  호출해야 하는 것입니다.


예를 들어보지요.

DirectX를 보면 이런걸 알수 있습니다.(DirectX를 모르셔도 이해할수 있게 설명하도록 노력하지요.) 당신이 하고 싶은 일은 화면의 해상도를 640*480으로 바꾸는 일입니다.

DirectX안에 DirectDraw안에 SetCooperativeLevel(맞나 기억이 가물)라는 함수가 있습니다. 당신은 이 함수를 부르기 위해서 일단 DirectDraw인터페이스를 얻어와야 합니다. 그리고는 그 인터페이스를 이용해서 그 함수를 호출해야 하지요. 음 괜히 돌아왔지만 결론은 그 함수를 쓰려면 그 인터페이스를 얻어와야 한다는 겁니다.


여기서 당신은 의문이 들겁니다. 왜 이렇게 해야할까? 그냥 그 함수 부르면 될텐데....

물론 장점이 많기 때문입니다. 장점을 안다는 건 중요하죠. 그래야 이걸 쓸테니까요... DirectX는 무지 많은 버전이 있습니다. SetCooperativeLevel이란 함수도 버전마다 있겠지요.

당신이 어떤 인터페이스를 얻어오느냐에 따라 원하는 함수 호출이 가능해집니다. 일반 DLL같은 경우에는 버전에 따라 각기 다른 DLL을 사용하거나 배포해야 하지만, COM은 DirectX9를 깔면 밑의 버전은 안깔아두 돼죠.... 뭐 너무나 당연하다고 생각할지 모르지만, 개발자라면 특히 설치본을 만들어본 개발자라면 이게 당연한게 아니라는 걸알겁니다.


대충이런 식이지요. IDirectDraw7인터페이스를 얻어서 SetCooperativeLevel를 호출하는 것과

IDirectDraw6인터페이스를 얻어서 호출하는 것은 다르다는 겁니다. 지금 설명한건 단지 한가지의 장점이고요. 이것말고도 많은 장점이 있습니다. 예전에 설명했던 COM의 장점에 내용이 있으니까 다시 보세요.


이제 요점이자 정리입니다. QueryInterface는 인터페이스를 얻기 위해 씁니다.

HRESULT QueryInterface(      

    REFIID riid,    LPVOID *ppvObj );

앞에는 얻고자 하는 인터페이스의 IID가 들어가고, 뒤에는 얻은 인터페이스의 가상포인터를 받아옵니다. 주의하세요. LPVOID의 포인터형입니다. 다시 말하면 LPVOID *는 void**와 같다는 거지요... COM은 이중포인터입니다. 고민하시지요. 왜 COM은 이중포인터를 쓰는걸까? 사실 포인터 하나두 C++에 익숙하지 않은 사람은 머리 빠지는데.....


COM의 인터페이스 포인터 자체가 포인터이기때문에 Call by reference를 이루려면 이중포인터가 되어야 하는겁니다. 음 말이 어려운가요.... 우리가 얻고자 하는 놈은 포인터인데요.. 그 포인터를 함수에 집어넣어서 어떤 값을 갖도록 하려면 그놈의 포인터를 넣어야 하는거죠. 포인터의 포인터가 돼는겁니다. 그래서 이중포인터.... 잘 생각해보시면 이해가 돼실겁니다. 이해안가시면...... 뭐 글을 남겨주시던가 하시면 저두 어찌 설명해야하나 다시 고민해 보지요^^


그리고, 인터페이스를 얻는다는것은 그 COM객체를 참조하는 포인터가 하나더 생겼다는 의미가 돼고, 그건 COM입장에서는 자신을 참조하는 놈이 하나 더 늘었다는 의미가 돼기때문에 COM객체는 AddRef함수를 Query Interface안에서 호출합니다. COM을 사용하는 경우  AddRef를 사용하지 않았는데 Release를 사용하는 경우가 많은데요. 그게 이런경우지요.

 

 

여담으로. DIrectDraw의 경우에 QueryInterface를 사용하지 않고, DirectDrawCreate함수였던가를 사용하는데요.... 이건 DirectDraw를 사용하는 사람들을 위한 편의로 MS에서 예외적으로 DirectDrawCreate라는 함수를 만든겁니다. 원래는 QueryInterface를 사용하는것이고요. 당연히 QueryInterface로도 DirectDrawCreate기능을 할 수 있습니다.(QueryInterface만으로는 안돼고요 CReateInstance라는 함수가 또 있습니다.)

반응형

+ Recent posts