COM : The Most Efficient Assassin.

     For once, Micro$oft comes pretty close to creating the VERY best in a certain category of programming tools available.  Too bad that category happens to be "Sources of HELL."  And the crown for high honors in that category goes to the creation called COM -- the Component Object Model.  Let me take you through a documentary on why COM is evil.
     We've all experienced the cycles of coding where everything is utter agony that we toil over for days without end and without sleep.  The ecstasy comes when it finally works.  Then the cycle begins anew.  COM is one way, by which, the differential in the cycle is highly exaggerated.  It's like coding while doing Marijuana, except it's legal in just about every country.  It's even allowable in Amish regions so that the evils created by the 'English' can be displayed so as to promote local Amish values.  Several island tribes have been known to use textbooks on COM to create rain.  Their rain gods are said to protect the island from evil and harm, and so the natives bring textbooks on COM to the island in order to have something from which to be protected.  These people endure great hardships as they have to repeatedly search for new COM textbooks or modify old ones to have new interfaces so that they can continue to fool the rain gods into thinking that the books are entirely different dangers.  The end user gods would not be fooled otherwise.
     So what is so terrible about this so-called Component Object Model?  COM is basically the result of years of work on the low-level OLE methodology.  There's no argument that COM manages to accomplish a few good things, but there is also no argument that it is done in a very broken way.  Of all the flaws and caveats with COM (and code-bloat being a major one), there are 2 BIG ones that stand out.

Number 1 --
     COM is dependent on reference counting...  and in a noticeably screwed up manner.  When you add a new Interface to an object, you increase its reference count.  This is automated through an 'AddRef()' call.  However, if you are done with a particular interface, you have to 'Release()' the interface yourself in order to decrement the reference count.  I'm not necessarily saying that we should automate reference releasing (although it's more than possible by just having a default destructor that executes a Release)...  You could just as well NOT automate the AddRef's.  Either way would be nice because it's symmetric.  Having an asymmetric automation is a surefire way to give rise to programming error.
     Just note that this only one edge of the reference counting blade.  A side effect here is that a COM object can increase its own reference count secretly.  So you can Release an interface, but it will still hang around.  And of course, the opposite is possible.  It's quite possible to Release an object one too many times and it will disappear for real.  If that happens, you'll get a lovely core dump the next time you reference the interface.
     Well, the obvious reply is that "Don't DO that.  Write better code." Ummmmmmmmm.... This is coming from Micro$oft, who believes that Moore's Law is something that was made to be squelched?  Yeah...  better code.  Practice what you preach, COM monkeys.

Number 2 --
     COM encourages aliasing.  As in, it encourages the practice of giving one item multiple names.  Basically, in COM, you create Interfaces to existing objects.  Once you've done that, you've aliased the object.  The purpose of creating more Interfaces for this object is to allow new functionality for the object without doing anything to the old interfaces.  It sounds like a good idea except for a few things.  One, it causes code-bloat, but apparently that's harmless.  Two, you've aliased the object and can potentially get confused about reference counts...  we have to assume people are stupid, after all -- Micro$oft has done that so far, and look at their success.  The other problem is that it's fairly common (especially in DirectX) to break old interfaces when putting in new ones.  COM does say that you can recode the guts of an interface all you want as long as the functionality does not change.  That's an utter joke.  There's only two real ways to do that --  Either you bugfix the guts, or you change the performance of the guts.  Neither of which is advocated.  If you're not doing either of those, then it's an utterly cruel joke.  You simply can't touch your old interface.
     The other problem with this philosophy, though, is the example that you can bugfix/update existing functionalities of old interfaces, as well as adding new functionalities.  By the rule of COM, we cannot put the updates of the old functionalities into the old interfaces even if nothing changes in parameter space and such.  The only thing we can do is create an entirely new interface.  Moreover, if we add new functionalities that are completely unrelated to old ones, we still have to create a new interface.  This is not just code-bloat, but unjustifiable code-bloat.  The problem with Micro$oft's attitude to code-bloat is that they don't have ANY reasons...  only excuses.  There are no exceptions, btw.
     Example of Code-Bloat?  DirectDraw2 and Direct3DRM2 objects.  Instead of just adding features through a new interface, they actually hold implementations of EVERYTHING that existed in their predecessors DirectDraw and Direct3DRM.  Very few behaviour changes were in there; a lot was cut & paste.  WHY??!??!  That goes against the very idea of COM even by itself.

     Well...  time for an example...
     Take the DirectX 5 Direct3D Ramp renderer, which for some historical reason is called a "Device."  Someone got a brilliant idea that it would be faster if the ramping was calculated only once for the one time that the texture was bound.  Problem is that sometimes, people animate textures or apply multi-texturing (lightmapping, for instance).  Shimatta!!!  The end result now is that your animated textures in everything from DirectX 5 through 7 (not sure about 8) will fill in black for all colors in animation frames that were not in the first frame. Which means that pre-DX5 Ramp modes don't work and there is no workaround.
     Ideally, there should have been a Ramp2 interface for the new ramping mode, but that didn't happen.  And this is a perfect example of how Micro$oft's big statement about COM is a major fallacy.  A position paper from M$ says that "COM solves the software versioning problem." Perhaps if every software engineer were perfect in this world, it would do just that.  But we cannot say that COM produces no problems.  For everything that it solves, new problems arise. That's a general rule in programming, I know...  but again, that's not a reason.  It's an excuse.  The old approach of providing new .dlls and putting new version filenames worked far better and created less interference between the old and new.
     This creates another question.  What constitutes a broken interface?  COM works on interface contracting, so what do you do when a contract is broken.  Do you fix the broken interface, or do you leave it there, still broken, and create a new one that ISN'T broken? Do you do what the standard answer to this question says or do you exercise some power and  actually decide to do the opposite?
     The huge problem lies in Micro$oft's attempts at answering that.  Whenever some COM util has a broken interface, they quote the infinite authority of compatibility.  They claim that something shouldn't be fixed because it risks compatibility, but the fact is that they just don't want to fix it.  Other times, they're rather happy to break something because it is apparently an all new version and so compatibility is non-existent, and they can simply ship a broken product and tell you to go to their website to download bugfixes.  "While you're downloading, buy all our other products or we will charge them to your credit card for you. Have a nice day."  The icing on the cake is that they even fail to properly document across their own versions of their own SDK's.  Isn't this EXACTLY the sort of thing that COM was supposed to prevent?
     There ARE fixes to the aforementioned problems.  For instance, you can have your own variables that keep track of reference counts for you.  But this is a C++ style fix (smart references).  COM is supposed to be language and OS neutral, so this sort of a fix is rather defiant.  And we also have to remember that anything from M$ that says "OS neutral" is a boldfaced lie.  This is not a value judgment -- M$ would simply be cutting away at their own bottom line by making something cross-platform.  Besides which, if COM was such a great solution, why on Earth do we have to put bandages on it?
     You also have one more issue to deal with the fact that not everything is a COM interface.  In particular...  DRIVERS...  Sometimes, they like to masquerade as COM interfaces, but this duct tape approach is not very convincing.  The thing is that drivers change all the time.  Why aren't there new COM interfaces for every new release of a driver?  Why is it that drivers don't balloon to infinity, which is apparently an okay thing because...  because...  Moore's Law!  SO THERE! Got a problem wit' dat?!??  Well, NP-HARD, too!!
     Don't get me completely wrong.  The idea of reusable interfaces is good.  It's just that we need a better and less broken solution than COM.  If a way could be found to get around the aliasing issue, that would clear up a good deal of the related problems.  You can't say that something solves everything when it creates problems for less-than-perfect programmers.  When you get right down to it...  we're all less than perfect.

Well, it boils down to this -- you rid the world of COM...
                            or you rid the world of people.

Hmmmmmmm...  damn...  that made it tougher.  I kinda like both ideas.


View the Soapbox archives.
Back to my home page.