COM Interfaces

Started by
23 comments, last by kill 21 years, 9 months ago
Why did the designers of COM decide that all COM interfaces should support QueryInterface and be able to return all interfaces the component implements? Not only is it hard on the developer, it''s also completely unintuitive. Wouldn''t it be a lot more intuitive to have a simple IComponent interface that had a QueryInterface in it, and have QueryInterface return a pointer to IInterface that would be a dummy interface that all interfaces have to derive from? This way a component defines a QueryInterface that returns all interfaces component implements. Did I make any sense?
Advertisement
yes you did make sense.
single inheritance.
i think they wanted a simpler model for the version control. but your idea seems valid.

but then if they did that, someone would post on gamedev asking why not put QueryInterface in IUnknown ?.

so i really dont know.





Its my duty, to please that booty ! - John Shaft
It''s not really that unintuitive. When you implement a COM object, you usually just create a single C++ class which is derived from all the interfaces your object supports. That means you only ever have to implement one QueryInterface method. If you only support the one interface, then ATL can implement the QueryInterface method for you.

If I had my way, I''d have all of you shot!

codeka.com - Just click it.
It''s even simpler than your idea: There is this simple IComponent interface which contains only three methods:


  interface IUnknown {  virtual long AddRef() = 0;  virtual long Release() = 0;  virtual HRESULT QueryInterface() = 0;};  


Think of QueryInterface() like a cast in C. You can derive a class from 2 base classes (speaking in COM, implement 2 interfaces for an object), and then cast this class to any other of those interfaces (COM-side: another interface to the same object: just another set of methods implemented by the same object).

The new object returned by QueryInterface() also is IUnknown (so there isn''t even an additional IInterface), because any COMponent has to fully implement IUnknown (else you couldn''t return to IUnknown again to query for another interface)

-Markus-
If it was done that way, it would be impossible to get a pointer to the IComponent interface from any other interface. All interfaces would have to derive from the IComponent interface.

  interface IInterface{   virtual ULONG AddRef() = 0;   virtual ULONG Release() = 0;};interface IComponent : IInterface{   virtual HRESULT QueryInterface(REFIID iid, void** ppvObject) = 0;};interface IFoo : IInterface{   virtual HRESULT DoSomething() = 0;};interface IBar : IComponent{   virtual HRESULT DoSomethingElse() = 0;};  

How would we get a pointer to the IFoo interface from a pointer to the IInterface interface? It wouldn''t be possible, and thus all interfaces would have to derive from the IComponent interface as IBar does.
Arguing on the internet is like running in the Special Olympics: Even if you win, you're still retarded.[How To Ask Questions|STL Programmer's Guide|Bjarne FAQ|C++ FAQ Lite|C++ Reference|MSDN]
Personally, I agree with kill on the problems of COM. But the solution is a little different.

A good explanation for how it can be remedied (written by a guy who worked at microsoft around the time OLE was being invented) can be found here.

Here''s a quick quote:

quote:
The fatal design flaw of OLE is the requirement that one should be able to get from any interface to any other interface.


The solution, basically, is that you should be able to query the object for interfaces, not the interfaces themeselves for other interfaces. Which, of course, makes way more sense in the OO world of C++.

daerid@gmail.com
This is exactly what I meant. Sure if you get two interfaces from a component you can''t get a pointer from one interface to another, but usually you don''t need to. It''s not a very intuitive approach either. Think of it in terms of C++.


  class CMyClass : public CMyClass1, public CMyClass2;CMyClass MyClass;// now we can do this:CMyClass1 mc1 = (CMyClass1)MyClass;CMyClass2 mc2 = (CMyClass2)MyClass;// but we can''t expect the following to work:CMyClass2 _mc2 = (CMyClass2)mc1;CMyClass1 _mc1 = (CMyClass1)mc2;  
COM should really be implemented the same way that dynamic_cast<> works.


    // The base Interface classstruct IRoot{  virtual long AddRef() = 0;  virtual long Release() = 0;};// the root of the COM hierarchyclass CoObject{public:  virtual bool QueryInterface(const IID& iid,IRoot** pInterface);};// where IID is the 128 bit Interface Identifier  


Then you could just go ahead and query the OBJECT for the requested interface:


        // define a Position interfacestruct IPosition{  virtual long GetX() = 0;  virtual long GetY() = 0;};// define a Shape COM object that implements the IPosition interfaceclass CoShape : public CoObject, public IRoot, public IPosition{  long x;  long y;  long refcount;public:  long GetX() { return x; }  long GetY() { return y; }  long AddRef() { refcount++; }  long Release() { if(!(--refcount)) delete this; } // or whatever other cleanup is required    bool QueryInterface(const IID& iid,IRoot** pInterface)  {    if(iid == IID_IPosition)    {      *pInterface = this;      return true;    }    *pInterface = 0;    return false;  }};    


There may be some holes in that, but that seems (at least to me) to be a hell of a lot simpler than the current way of doing things.

[edited by - daerid on August 7, 2002 4:23:55 PM]
daerid@gmail.com
How many of you guys have actually created COM components before?

daerid, you code example is exactly how a COM component looks as it stands now, except you don''t inherit from two things, just IUnknown.

kill, you can cast between classes in the same hierarchy like in your example in C++. It does work.

The way COM is implemented make perfect sense, especially from the point of view of the actual object being implemented. All interfaces to the same object share the same QueryInterface method, so you only have to implement it once for all the interfaces your object supports.

I suggest you all read up on how COM actually works before trying to re-invent it.

If I had my way, I''d have all of you shot!

codeka.com - Just click it.
For quiet a while now I''ve been thinking about creating a better COM. Something that could be truly used by anyone on *any* operating system. Some things in COM are just rediculous and I feel that many of them could be done better.

Here''s my proposal.
1. Recognize the fact that there is a difference between IComponent and IInterface and that each interface should not know about all other interfaces.
2. Dump reference counting alltogether. Also, servers don''t need to implement "CanUnloadServer", COM library should have a simple hashtable with the components and dlls that host them with an internal reference count. The library can easily manage dlls by itself internally.
3. Ease the GUID requirements. I like to write libraries that use components internally, I shouldn''t have to register them. The way .NET does it with private and shared assemblies seems like a good idea.
4. Improve error handling. What were they thinking when they came up with HRESULTs??? A much simpler error handling system can be designed that will actually be more flexible.
5. Why don''t we create a factory component for a factory? And a factory for factory''s factory. Or better yet, let''s dump factories all together and let the user pass a simple value to the server dll that will interpret the value as it sees fit and initializes the component based on it. Should a custom factory component be necessary the designers will create one themselves.

These are some thoughts... I didn''t yet get out-of-proc servers, marshalling, automation and all the good stuff but I do have a bunch of ideas on improving that too.

Anyway if anyone would like to participate in an open source project to create better COM please post here.

This topic is closed to new replies.

Advertisement