Why COM Stinks, Since You Asked

Posted 21 Sep 2003 at 19:11 UTC by mrorganic Share This

pphaneuf asks, I deliver.

This is not an essay or a reasoned discourse so much as a list of aggravations and headaches I've had over the years with COM. I've come to believe that COM not only stinks, but has wasted more programmer time to less good purpose than any other API or technology I can think of. My dislike for this API knows no bounds.

1. COM was not a designed technology. That is, it was never thought out completely beforehand. Microsoft never intended COM to be as prevalent and deeply-embedded in Windows as it is today; it was intended to replace the horrible and unreliable DDE. It was needed to allow Microsoft's new Office suite interoperate more gracefully. Microsoft's developers, being the good dogmatists they are, adopted the then-cutting-edge concept of component software. That is, rather than having many monolithic pieces of software all doing different things, you'd simply write a bunch of objects which would expose interfaces you could plug into your own applications.

But COM was really only thought out as a way to link a few applications together: a spreadsheet (Excel), a word processor (Word), and e-mail (MAPI). It didn't then (and still doesn't) operate very gracefully with database applications, even Microsoft's own Access product.

Over time, Microsoft moved more and more low-level system work into COM objects. It was modern! It was high-tech! It made software more modular! But it also made software dependencies ferociously complex, and horrendously difficult to debug. You can't really flowchart a modern application's information flow if it is a COM application -- you can't tell what the I/O is doing at a given moment because you can't get a good picture of how many programs are holding instances to the COM object.

And then there's the worst and most glaring defect of COM: a bug in a low-level component means that all other programs that depend on that component will be buggy too. And if the component is a vedor-written one, it cannot be fixed except by the vendor.

Finally, because Microsoft kept the DLL (dynamically linked library) format for libraries and just shoehorned COM objects into them, we still have to deal with "DLL hell" -- one system DLL can overwrite another. In COM-land this problem is even worse, since the COM interfaces of two DLLs may look exactly the same, but may have different implementations depending on the DLL installed.

2. The Registry. A modern Windows system has to support literally thousands of discrete COM interfaces in order to function. How are all these interfaces to be tracked and managed, especially in networked environments? Some genius at Microsoft came up with the idea of a central, heirarchical registry of settings.

Now, this in itself isn't a bad idea. Where Microsoft went wrong was in making it the central repository for the entire operating system. Furthermore, they designed it as a binary database rather than plain-text. In practice, this means two things: if the Registry gets corrupted (a fairly common occurrence for a developer) it can keep your machine from even booting up, and it means you can't just use a text-editor to fix it. If you hose the Registry badly enough, a re-install is your only recourse. This is an unconscionable design decision, and in my view the very worst thing about COM.

Contrast this with most Unix systems, where system configuration files are kept separate from application configuration files, and nearly all configuration files are plain-text. Further, a faulty configuration file here or there will not prohibit the system from booting up (it may fail to work correctly, but it will usually boot). (The only exception to this that I can think of is to mess up a lilo.conf or grub.conf file.)

3. Security. Oh, we don't need to bring up the latest Blaster worm to belabor this point. All we have to do is look at the dependencies for any substantial COM program, and try to figure out where any possible buffer-overrun or elevated-privilege situation might come up. It's hopeless. COM is pervasive in Windows, and this means that Windows is not securable even in principle. There's just no way to figure out all possible program pathways or all possible input/output combinations. In the name of making all applications "interoperable", Microsoft has chosen to make everything equally insecure.

A worse problem is that, in order for many users to be able to install ActiveX plugins (a fancy name for -- you guessed it -- COM components), they have to have elevated system privileges (usually Administrator). So if you open up an infected e-mail in Outlook, it (being a good COM citizen) will automatically invoke a helper program to decode the content of said message, and all of a sudden...you're boned. Sure, you can argue that users should be smarter than to open binary attachments from strangers -- but should Microsoft really make it that easy for someone to shoot themselves in the foot? Or to make it that easy for a virus/worm/trojan to propagate that easily or quickly among networked machines?

The only way to make a Windows machine secure is to turn the power off.

4. COM is a programmer's nightmare. Anyone who has had to wrestle with the rat's nest of ATL (ActiveX Template Library, or the C++ interface to COM) knows what I'm talking about. It is impossible to write clean COM code. In fact, reading COM code isn't like reading C++ code at all -- it's a maze of casts, macros, compiler-specific keywords, and ide-generated templates that give no clear idea as to the code's underlying structure or purpose.

Another peeve I have is that ATL/COM pollutes the global namespace -- many things get #define'd and typedef'd in the myriad header files, and few of them are adequately documented. Worse, if you happen to use a #define of your own that conflicts with one that COM or ATL defines, things can break in obscure and very hard-to-debug ways. (If you are lucky, the compile will fail; if you are unlucky, the program will compile but then fail at run-time with infuriatingly weird behavior that can take ages to figure out.)

In my darker moments, I wonder if the engineers at Microsoft secretly hate their jobs, and want to make sure that we hate ours, too.

CONCLUSION

Oh, I could go on and on about how and why COM is a horrible thing. The best thing you can say about it is that it (kind of) works. But even this is faint praise -- there are other approaches that work just as well with far less pain. They could have adopted the Unix method of piping one program's output into another's input, or they could have adopted a systemwide scripting language like Apple's AppleScript. (The did eventually do this -- with Scripting Host -- but it came too late and was too limited to do much good.)

The worst part of this whole sorry saga is that we're stuck with COM. Windows these days is mostly made up of the kernel plus a big grab-bag of COM-based APIs. Even the venerable ODBC -- the most decent API Microsoft ever came up with -- has been deprecated in favor of OLE DB (which is slower and less secure, but hey!, it's based on COM). Even the vaunted .NET stuff relies on COM under the covers. It makes the programmer's job (somewhat) less vexing, but it does nothing for the security side of things. And there's always the Registry to put the gray in your hair.

So what's a programmer to do? My advice: just do your best, and use COM sparingly, and only if you absolutely need to have it. Even in these debased times, it's possible to write COM-free software on Windows.


Well said., posted 21 Sep 2003 at 21:02 UTC by DeepNorth » (Journeyer)

I have been programming for a long time. It seems to me that the people who come up with these types of things are clever journeymen that overestimate both their own skill and the skill of others. As I get older, KISS becomes more and more attractive.

<rant orientation="orthogonal">I am very unhappy with software creators introducing new versions of software which they say I must pay for when what I asked for is a bug-free version of the software I already own.</rant>

Not the reasons I would pick....., posted 22 Sep 2003 at 12:22 UTC by listen » (Journeyer)

First lets establish what we mean by COM - the binary standard based around the IUnknown interface.

COM is a horrific hack around the type system of C++. Because there wasn't ( and still isn't) an ABI for C++ on Win32/i386, COM was the way round it. Lets cast everything to void, and simulate rtti, exceptions, virtual inheritance, and all the features that make C++ actually useful!

My top reasons to hate COM:

1. The binary compatibility mantra. "NEVER break bincompat, EVERYTHING will break!!!!" is the standard battle cry of the COM programmer. What this effectively means is that you can only make very limited changes to any COM interface, or you will cause an amazing amount of pain for yourself.

2. Lets make everything a component! Even the tiniest classes are made into components with never changing interfaces by the COM zealot.

3. These two things lead to the true evil of COM - refactoring is nigh on impossible. You can't change interfaces or move functionality, without ultrapain. So the crappy interfaces that some muppet thought up in 1995, when they had no idea what it was for, are still kicking about now, and are being abused by overloading arguments and packing everyhting into variants or strings or DOMDocuments...

4. VB. COM was crippled by the fact that VB had to be able to be a full COM citizen. As a language, its a big pile of bollocks. That could fill an article. VB is the language that tried to convince people that interface inheritance is "good enough" for OO.

5. VBScript, MS's half hearted attempt at a Javascript competitor and system scripting. This is VBScript castrated. What do you end up with when you castrate a big pile of bollocks?

6. COM+ / MTS. This was a pathetic attempt to pretend that COM was a serious system for backend "enterprise" systems. The most pathetic part is that a huge number of huge companies actually believed it. The registry hell is mitigated, in that registration is per component group or "COM+ Application". Security is better in that these groups can be instantiated in a separate process runiing as a different user. But COM+ is a debugging hell. Don't get me started on COM+ Events and the extreme shabbiness of MSMQ....

7. The C++ shambles. Have you ever read any C++ written by a COM head without wanting to poke out your eyes? Just look how many different string types we can convert between. Thats far better than using char*, you silly C children. Microsofts tools and APIs are what make people think that C++ is hard. The sad thing is that two of the three biggest C++ OSS projects seem to like to emulate this sickness - both Mozilla and OOo have slightly less sick versions of COM. Only KDE can actually bear to have fully capable C++ classes in dynamically loaded libraries.

To mitigate, however, .Net ( I can't bring myself to capitalise that) is a great improvement. It is not based on COM, but it can interface with it. A lot of the APIs are com wrappers. However, it does keep the sickening .exe and .dll extensions, which make my stomach turn, especially when I see them on unix. You listening, mono monkey boys?

The saddest thing about COM is that maybe it all could have been avoided on Windows, and we could have had real non-suckass-CORBA components on Unix if C++ had had a fully specified ABI in the early 90s. In unix, we happily wrap every API in every language. I can't help feeling that in some ways ( but not in a practical day to day tear your hair out way) that is even worse than the COM monstrosity. I don't know if CORBA or Mono or SWIG or the Hub or DBUS or UNO or SOAP or a C++ ABI can save us (well I know CORBA won't), but we have to do something, right?

.Net won't save them from making the same mistakes, posted 24 Sep 2003 at 10:09 UTC by murrayc » (Master)

Yes, the problems with COM are mostly with the awful COM APIS that Microsoft developed. Also, the various awful COM language bindings (and the inconsistent, incomplete level of support for various COM features in the various bindings) but then, so many of the COM APIs are tied to only one language binding (what is the point of a COM API that can only be used from Visual Basic or MVSC++).

They are starting again (well, lots of their critical APIs are still COM-only) with .Net, but Is there any reason to believe that Microsoft won't fuck up their .Net APIS just as they fucked up their COM APIs. The problem here isn't theory or basic technology - the problem is simple corporate incompetence caused by market monopoly.

Protocols, not components, posted 24 Sep 2003 at 12:49 UTC by Omnifarious » (Journeyer)

Components are OK when you actually link libraries into your application. That's what they're for. But, as soon as your application is split up into pieces that run in different places on your network, the whole component and API concept is broken.

Function call APIs inherently introduce tight state coupling between the using component and the used component. The using component makes several calls to the used component and assumes (because that's how it works when the components are in the same address space) that it is the only thing making calls to the used component, and that the used component is in a particular state at a particular time.

This state coupling is very costly when two wildly separate entities either implement a component that conforms to a particular API, or when someone completely different implements the used component and the using component.

If you want to have wildly disparate entities implement their own pieces of the system, or if those entities live more than a few microseconds away on a network, you shouldn't develop the links between system components as APIs. They should be developed as protocols. The focus should be on the structure and form of the data transmitted so both sides can agree on it, not the actions that one side or the other will take on the data.

Focusing on the actions leads to systems that have synchronous characteristics that are hard to overcome when the speed of light proves to be a constant. It also leads to an imperative style of program design that doesn't leave enough wiggle room to implementors. A request recieved over a network isn't a command that should lead to a particular, well-described sequence of actions on the part of the reciever. It is more like a message that one expects the reciever to interpret in a particular way.

I talk a little more about this, possibly a little less coherently in my "Why CORBA and other forms of RPC are bad" paper that I wrote in early 1999.

I always wondered about that, posted 24 Sep 2003 at 17:05 UTC by sej » (Master)

Microsofts tools and APIs are what make people think that C++ is hard.

I always suspected that people confused C++ with what Microsoft was doing with the language. Not that things were that much better on the Unix side, as an ever changing compiler and support library (and standard) made it constant work to keep legacy C++ code compiling.

Building And Linking A Library Shouldn't Be Hard As COM, posted 24 Sep 2003 at 17:42 UTC by nymia » (Master)

Without COM, coding on DOS or Windows would probably be a nightmare, though. Linking will mostly be static with some combination of overlays. Perhaps some of the people here have done it on Clipper and Borland Turbo compilers. When MS-DOS introduced DLLs, they saved the day and they were really great. Using a DLL totally changed a lot of things around, though. Overlays became a thing of the past and most libs are loaded only once in memory. Life back then was great!

Thanks to the Big Monster now known as COM/DCOM, coding has become bigger and bulkier. There is definitely a lot of bagagge sitting on top of that simple mechanism.

First, a point by point analysis of this article, posted 26 Sep 2003 at 21:17 UTC by pphaneuf » (Journeyer)

I will post my replies to this article in a few steps, since there is so much content and things to say.

Right from the start, on point #1, signs of underlying confusion arise. COM wasn't designed to replace DDE, it received the inter-process communication a bit after its origin. It was designed as a solution to the "DLL hell" problems. Whether it did a good job or not, you can judge for yourself, but I would point out that most DLL problems that I ever had were caused by non-COM DLLs. COM generally did a good job of keeping things in line.

How can you say that COM doesn't operate very gracefully with database applications? COM is roughly at the same level as the dynamic linker on Unix, this is like saying that database applications don't work very well with shared libraries! They're so incredibly orthogonal, I can't even go there.

A bug in a low level component will be buggy too. There's no need for COM to see this in action. A bug in the kernel or in glibc (and God knows there are!) will screw you just as bad. And what is this about "if this is a vendor-written [component], it cannot be fixed except by the vendor" thing? That's not a problem of COM, that's a problem of not having the sources. In fact, if you had the sources to the component, you could very easily fix the bug and install your component in such a way that requests for the broken component gets redirected to your version of the component, but if you install a new version of the component, it will be able to be used in parallel (think "sonames", which Windows DLL doesn't have, so COM provides an equivalent capability).

In point #2, he talks about the registry, how it is a bad idea to put everything in it. The original COM hackers at Microsoft designed the registry for COM only, it is when others saw it that they decided to jump into it and use it for everything, with the disaster that we all know. There was a registry in Windows 3.1 (if you had COM installed on your box), and it contained only COM information.

Point #3 is all about DCOM. I despise DCOM and most transparent RPC mechanisms personally, but none of this can be blamed on COM. ActiveX is dumb, no mistake about that, but it's nothing else than an easy way to run unknown code on your machine. If I put up an ELF binary on a web page and somehow Mozilla would run it on your box, it wouldn't ELF being stupid, it would be Mozilla for running it.

Point #4, I can't really say anything about. When I was designing XPLC, one of the first thing I did was buy and read Essential COM, by Don Box. As I was reading through that book, I became more and more convinced of a number of things. First, this is the best design for a component system I had seen. Second, it is the crappiest, ugliest and most painful component system I had seen. I didn't even need to write code using it to see how painful using it would be. That was pretty much one of the driving rationale for XPLC, that it should be easy to use. Then I read further into the book and it started degenerating into apartments (which solve a problem, but I'd rather avoid the problem altogether), so-called security and other things.

Okay, so they had some bad ideas, that's no reason to throw out the good ones. But you won't catch me writing COM code. By the way, I did eventually write a COM component and an application. I nearly went insane in the process, partly because I wouldn't use any of the wizards, I deliberately exposed myself to the raw pain of COM itself.

In the conclusion, it is said that instead of COM, they could have used the Unix method of piping one program's output into another's input. Well, no, COM is there for the same reason that we have shared libraries and Perl/Python/Ruby/Tcl modules. While a pipe-based component that would do what OpenSSL's libcrypto does might be totally possibly, I'm not 100% certain I would like to use it very much.

Each tool has it place, but using a screwdriver handle to drive a nail or hitting yourself on the fingers with a hammer are stupid ideas, it's not the fault of the tools, it's the fault of whoever has the idea.

So, that's for the article.

Next, the comments..., posted 26 Sep 2003 at 23:08 UTC by pphaneuf » (Journeyer)

Let's start with listen's comment.

COM isn't specifically about hacking around C++'s type system, but more about hacking around any language's type system, so that they can all be used from one another. In Unix, we have C as a global standard, talk about losing the features from C++! And on top of it, we have to write all these glue libraries for scripting languages...

Never breaking binary compatibility doesn't sound like a bad thing to me, considering the number of time I've been screwed by this. C++ libraries are most often a complete binary compatibility circus, forcing me to have a gazillion copies of libraries around or statically linking them. This is not progress!

In fact, you are not allowed to make any changes to a COM interface after it has been released. If you add a method to an interface without changing the IID of the interface, an application linking with the new version of the component will crash if it is run on a machine with the older version of the component. That's what COM was invented for, avoiding those breakage. You could say that this is what sonames are for, but changing the soname is not keeping the binary compatibility, it is all about breaking it completely (if you change the soname, you can't use programs linked with the old library at all)!

IID are very much like sonames, but at a finer grain. If you do something safe, like adding a method at the end of an interface, change the IID but keep returning the interface when asked for the old IID. Now, new code will be able to use the feature, old code will work with the new component and new code will safely catch the case where you only have the older component installed (it will not acknowledge the new IID). This is all legal and "by the book" COM, safe and easy.

To do it really safely, they recommend making the new version of the interface derive from the old, that makes the compiler do all the work of being sure the old interface is still perfectly compatible. Now, that's your pick, the way I explained works correctly if you know what you're doing and deriving the "new" interface let the compiler do the work for you (since I'm lazy, I like leaving the work to the compiler, deriving a class and adding a method isn't very difficult).

This actually makes refactoring easier, believe it or not! It allows you to refactor things either without breaking compatibility (if the changes are small) or without crashing everything (if the changes are larger). Look at DirectX for a not-so-good example: you have a program that works with DirectX 3, and it is binary compatible with DirectX 9, which is completely different. If you have Qt 3 installed, does your Qt 2 programs work with it (with the same library)? Your Qt 1 programs?

Of course, this is all pay as you go, like it ought to be. If you're like the Qt developers and don't care very much about breaking binary compatibility 100% between major releases (which is kind of fair, when you think of it), you can do the same with COM and install parallel components of each versions, it will all work.

Visual Basic is bollocks. No argument here.

COM+ and MTS are, hmm, out of the scope of this article. I don't know what they solve, but whatever it is that they overlap with COM, COM was probably fine already. I can't really tell. COM+ is the most related and has been cleaned up in some aspects, but has grown a few weird tentacles too, so you can't just say that it's better or worse. COM+ components are only in-process, I like that. They have some better partitioning, as you said, which can't be all that bad. But MTS?

For #7, I'd point out that Win32 didn't need COM to have ugly code! All this string mess is, well, a mess, that's clear. KDE is pointed out as having "fully capable C++ classes in dynamically loaded library", but that's only allowed because of an amazingly strong slant toward C++. That's not entirely non-sensical, but it's pretty biased anyway. You need a common ground, it has historically been C, but you could C++ as well. There are a few problems with this. On Linux, this isn't (too) much of a problem, but on Win32, they actually have different C++ compilers that can't link together, so it's not even a common ground. Also, while this is mostly an implementation issue, C++ seems to be pretty symbol-happy, producing incredibly large symbol tables in binaries and making dynamic loading very slow. Object prelinking and other tricks help, but in the end, they're just tricks. A shared object containing an COM component has three symbols, one containing an XPLC component has a single symbol, no matter how many components, interfaces or methods are in it (essentially, O(1) dynamic linking efficiency versus O(N) for C++). I sure like small, fast and efficient!

XPLC uses char* for strings, at least for the moment. They seem like a good common ground, but I am afraid that this might have to change (Perl and many other languages allow NULLs in their strings). But I practice YouArentGonnaNeedIt, so it's still a char* until then.

Wrapping every API in every language is worse than the COM monstruosity. Now, XPLC tries to avoid both. Wish me good luck!

murrayc's comment is spot-on, as far as I'm concerned. This is one of the reason that XPLC has no affiliation with any other projects or company. My employer allows me to work on it, and we want to use it for some things later, but I have made it very clear that I will allow no brain damage to XPLC in the name of our products. They can apply patches and make their own incompatible and broken version if they want, I don't care, but the upstream will contain no insanity. Mozilla's XPCOM has had some of the same problem, even though it isn't a corporate entity, and there are a number of not-so-bad component systems that have no traction whatsoever because people think they can only be used inside that project they are in (for example, GNOME's Bonobo or CrystalSpace's SCF).

Omnifarious is also right, this is why I am happy that COM+ dropped support for out-of-process components, and that is also why XPLC doesn't support them either. Use DBUS, SOAP, XML-RPC or whatever else you want, wrapped in an XPLC interface or not, I don't care, but it's not going in XPLC.

Finally, the diary entries..., posted 26 Sep 2003 at 23:40 UTC by pphaneuf » (Journeyer)

This one might be a bit confusing, because I started writing it first, then went back to it later, sorry!

Early on, mikehearn hit the nail on the head, with the first point of his first rant. It's been used (and misused) for a large number of purposes, which isn't really that much of a downside. I mean, for Microsoft, COM is basically meant to be a foundation technology, that would be like saying that a whole lot of different things have been done with C compilers, and that some might have been better done with Perl, Python, LISP or whatever.

But most importantly, it is mind-boggingly complicated, while being based on incredibly simple principles. Now, something's obviously wrong with this picture. In my experience with many Microsoft technologies, I have found that they often have a very simple and well abstracted design at the core, but somehow, they end up being clobbered by an amazing amount of crud.

My theory about this is that Microsoft has some excellent people in their Research branch coming up with the basic designs, then handing them off to product development, which is focused on getting a product out (not an altogether evil purpose, but is can bring evils), which generally messes up the design, often through misunderstanding of the technology. The Office people obviously don't understand COM at all, even though they use it extensively to implement their object models (they change published interfaces without changing the IID, which is breaking one of the most basic COM rule).

In the same rant, mikehearn mentions COM not being actually object-oriented. Well, our "equivalent" technology on Unix, C-level bindings and libraries (static or shared) isn't really more object-oriented, and I don't see anyone complaining. COM actually makes an easier job of exposing a set of classes and objects than flat C, if you ask me (but not very, just a little).

Yes, aggregation is garbage. :-)

While I'm on the subject of this rant, I would point out that in point #3, he says that CoCreateInstance should return a class factory, which isn't true. It returns an instance. This function is meant to simplify client code, by doing all the repetitive work for you, but classes do have to have a class factory. This is fairly simple, and while it is true that some IDE write code for you (awful!), I think that having the framework hide the extra work for you isn't bad (in the C++ binding of XPLC, there is a templated class factory, which in my opinion is no worse than using STL templated containers). Generating code is truly evil, since you often get to have to modify it while not really understanding it, and it gets out of sync, etc...

The whole IDispatch business is pure evil and never should have existed. If you have type libraries, you can do late binding without such an horrid hack. COM+ (.NET's lower component layer) and Mozilla's XPCOM do this (noticed how you could access XPCOM components from JavaScript without any ugly IDispatch crap?). IDispatch removes a large number of very basic and essential COM features when they get in the picture, so what it basically do is force you to do a lot of extra work whose effect is to largely remove most of the advantages of COM. For example, IDispatch allows you to partially implement an interface, in other words, to implement them while breaking their contract. An interface is a contract saying "I have these methods and they will do something like this", and you can just cheat using IDispatch. IDispatch also loses the meaning of the interface, since all you have is a single IDispatch interface pointer that can do everything, akin to getting a contract and just losing it on the ground on your way to some place.

DCOM is another story altogether, which I wouldn't want to be confused with COM. In .NET, they actually made COM+ work only in-process and removed all out-of-process and remote object access (you have to use Web Services or the older DCOM explicitly to do that).

In one of his diary entries, mrorganic says that COM is a binary rather than a protocol message standard, so he is obviously confused: COM doesn't have messages at all. How could its messages be text or binary if it doesn't have them? DCOM has messages (RPC, actually, calling them messages confuses the issues even more). I'm not saying that mrorganic is stupid or anything, just that this can confuse people very much, and it does!

The line between "components" and "RPC" somehow became really blurred, for some reason, and I don't like it. I love components and I hate RPC, they can't be that tied together!

I would point out DirectX as an example of some components that need very low latency and get it by simply not allowing themselves to be accessed out-of-process.

mx points this out: COM works, which merits some respect. No other component technology is as popular and as widely used. A good deal of this is due to Microsoft's pushing, obviously, but IBM, Sun and the other members of the OMG aren't exactly lightweights either, if CORBA was what the market wanted, we would see more of it.

berend says in his diary that COM solves a number of hard problems, which merits some respect. Whether all of these problems needed solving or if there is a cleaner way (hindsight is 20/20!), this is open for discussion. Myself, I think COM isn't the answer, otherwise I wouldn't have started XPLC.

mrorganic says it well in this diary entry: in concept, COM is simple, but in practice, it's a stinking mess. To which I'd say, once again: enter XPLC.

So that's why I'm interested in what you think of software components in general, so that XPLC doesn't stink too much. ;-)

This wouldn't be complete without referring people to mikehearn's diary entry that talks about the advantages of COM.

I'd really love to be able to scrape together a prototype of a complicated program in Perl, then notice some classes are inefficient, rewrite them in C or C++, have it all work with minimal fuss and make it possible to use the resulting hunk of code from any other language without any delay.

He also points out how things like network transparency and thread safety maybe shouldn't be glued into a "shared object framework" (which I'll read as "component system", as per the excellent "Component Software: Beyond Object-Oriented Programming" book's terminology) just because it can, but should rather be separate and clearly defined add-ons.

A few clarifications, posted 27 Sep 2003 at 13:11 UTC by mrorganic » (Journeyer)

pphaneuf seems to be confused about a few things (or feels that I am confused), so I'll try to get some points straight here:

1. COM is a binary standard. It is not a message-oriented protocol like HTTP or XML-RPC. See here for all the details. This was so COM wouldn't have to worry about endian-ness, character sets (EBCDIC or ASCII, for example) and so on. But all it really did was crud up the type system since no one really adopted COM except Microsoft.

2. DCOM is COM. pphaneuf appears to think they are separate technologies, but they're not. DCOM is simply COM with a longer wire.

3. ActiveX is COM. As before, pphaneuf thinks this is a separate technology; it's not. See here for details. ActiveX was mainly a marketing moniker for COM-based plug-ins.

4. COM was invented as a replacement for DDE. I don't have the book right here, and I can't find the link on the web, but during the early development of Microsoft Office, the designers really got into the "linking and embedding" thing in a big way (integration of Word and Excel was a high priority). DDE/OLE2 wasn't working out well, so they came up with COM. I'm sure other geeks in this forum can back me up on this -- COM was indeed intended simply as a replacement for the older document-embedding technologies Microsoft had.

5. Many COM libraries are DLL's, and thus are fully as prone to "DLL hell" as any other DLL. But at least a DLL can be used without needing elevated system privileges to register the damned thing.

Like I said: I came by my hatred of COM honestly, through years of suffering. It's not confusion about what COM is or does; rather it is familiarity with the myriad design flaws in COM that drove me away.

A few more clarifications, posted 27 Sep 2003 at 21:06 UTC by pphaneuf » (Journeyer)

1. Saying that having an ABI standard is bad design and that a message-oriented protocol like HTTP or XML-RPC would be better isn't a very good thing to say. If you want to join different things together, someone will have to make a compromise somewhere. For COM, they chose a very low level type system so that performance sensitive application could go down there and have efficient components, with most everyone using wrappers to make the low level type system more compatible with their native type system.

2. Windows 95 shows pervasive use of COM, but doesn't have DCOM, you have to install it separately. It is of course based on COM, but it only is enabled by COM, it is an optional part of it. Additional things, like the ORPC libraries and DCOM security management tools are needed. COM is like the Unix VFS, it only allows NFS to be used transparently, NFS isn't an integral part of it.

3. ActiveX is COM, there's no denying that. They added the ability to sign binaries, but this is more an extension to the PE binary format than to COM itself. But ActiveX as a "let's download COM components from the Internet willy-nilly and run them without user intervention" so-called feature of web browser is stupid. And it has nothing to do with COM. Take a hammer and hit yourself very hard on the forehead with it, who's stupid, you or the hammer?

4. OLE2 is based on COM. DDE is inter-process messaging (using Windows messages, like out-of-process COM). COM is component technology. COM was definitely developed within the OLE2 group, but the COMification of Office came only very late in the game, and with dubious success (you can see Office components break fundamental COM rules). For KDE-heads, here's a very clear comparison: DDE is like DCOP, COM is like KParts.

5. What is DLL hell? It's been a long time I used Windows, but I recall it being having conflicting versions of a same DLL installed. In Unix, we have sonames to improve the situation, but with COM, without changing the underlying technology of DLLs, you can have multiple versions of the same DLL installed without conflicts (since they are resolved using the CLSID of the components and using the full path stored in the registry). DLL hell is not specific to a file format, it is a specific problem. You can get DLL hell with ELF shared objects as well, if they are not build correctly (some package maintainers for example make the soname be the release number of the library, breaking binary compatibility with every release, even though they might be 100% compatible, or do not use sonames at all, giving the same effect as the usual Windows DLL hell).

I am not trying to defend COM itself, my XPLC project is even in direct competition with COM. But I want the issues to be clear. What are the failure of COM? What are the failure of component software? What are the advantages of the same? How can we build a better world?

Saying that "COM and anything like it sucks" don't help me, but telling me that building a transparent RPC mechanism into a component system is a bad idea helps. If you tell me that COM sucks because Office uses it in a bone-headed way, well, what can I say? You'd have given them the most perfect and well-designed technology in the world, they would have made a mess without nonetheless. XPLC isn't a cure for stupidity, unfortunately (I wish it was!).

Clarify this!, posted 29 Sep 2003 at 13:25 UTC by apenwarr » (Master)

4. I assume OLE1 fits in this picture somewhere, perhaps where mrorganic placed OLE2: as the thing (ie. failure) that prompted COM and OLE2.

5. I think DLL hell consists of two parts: a) programming DLLs is hell (gah! Shared globals! gah!) and b) lots of people install/upgrade "shared" DLLs in the windows\system directory, and they tend to have the same filenames, which means that if one person upgrades the DLL, *everybody* upgrades the DLL, and that often messes up everything. COM is supposed to (supposed to) alleviate this by letting new DLLs provide old interfaces, so you don't need the old DLL anymore. Good luck.

How can we build a better world?, posted 29 Sep 2003 at 14:20 UTC by Malx » (Journeyer)

And have you ever think about "initiator"?
Which part of code contains main logic of software? Is it a software at all or some infrastructure.

Could a component be stand-alone, or it is hardly dependent. Is a main program depends on component or component depends on it?

Example: if you have a script, which calls some binary components, it is likely, that main logic is inside that script. If you have app with plugins, then it is essential, where to find DIR with plug-ins, but software not more dependent on the number of plug-ins, or particular plug-in. Again that plug-ins could be stand alone or dependent on software (they try to be displayed as some menu item or button in main software UI).

Is software depentds on GUI library, or just uses it? Could software run without GUI? Or GUI absence will break(!) the logic of software (have you seen wizard-generated software - the logic is inside GUI-compatibility layers).

Yet another example - HTML+CSS+server-size+templates - these technologes have splited UI and software and design and data bindings. It was script-in-html and html-in-script, but now it is "templates" technologies. Why this is not in software still? (win32 Resources?)

Conclusion - regardless of component technology you still has to find a place for "initiator". That is key of software. Should it be a "component" or "a way components combined" or "script-like glue". Then you'll know if you could change that logic without touching a component(s) and interface(s).

Do not forget about Intellectual Agents ;) They are free of DLL-hell, tight dependencies etc. But they had own problems...

Huh, posted 30 Sep 2003 at 11:29 UTC by listen » (Journeyer)

pphaneuf has an interesting way of disregarding any problem with COM that XPLC shares. He simply says that thats part of the design, or the philosophy.

The point is that the design is bad.

The extreme pain in refactoring anything using COM is a direct consequence of the insistence on never breaking interfaces, even in development. It just sucks horribly. Its better to break things properly than maintain shit forever.

Never breaking interfaces, posted 30 Sep 2003 at 18:28 UTC by pphaneuf » (Journeyer)

listen, it's not that I want to disregard problems with COM that XPLC shares, it's in fact that I want to get to these problems. I want to avoid all these other non-problems (for example, ADO might suck as a database access component, I don't care, that's not a failure of COM!) and get to the real problems of COM and then be able to say whether XPLC shares them or not. If it does share them, is there a way to fix them? But most of the problems people have mentioned aren't even about COM, so how could they even possibly apply to XPLC?

He brings up a good point when he mentions "in development". For commercial applications, which is what Microsoft finds most interesting I suppose, "publishing an interface" only occurs rarely, when a product is released. If you do a release every two and a half year, you don't have very many interfaces to support if you want to be backward-compatible, and you can thus do without too much pain.

But in the open source world, this severely breaks down. You have some project making new releases every week or so, interfaces changing left and right all the time. Being backward-compatible all the time is a major pain in the ass!

But that's where listen gets it wrong: you don't have to be backward compatible all the time!

It's not because COM makes it possible that you have to do it! Go ahead, refactor and break your interfaces, that's fine, as long as you change the IID (to "break things properly"), as per COM rules. It's all right! Go and do it! Break things!

But the important thing is that while COM can easily do the same thing that a C-based shared object library can do with sonames (break everything if it isn't compatible and be able to run the two versions in parallel), it can't do what COM can do, and sometimes, it's exactly what you need.

In fact, since XPLC is an open source project targeted at other open source projects, I added a few extra things to allow for rapidly changing (in incompatible ways) interfaces. You can mark an interface as "unstable", and it will then #error on you if you do not build with -DUNSTABLE (to acknowledge that you are using unstable interfaces). So you can release an interface, but it doesn't really count as "published" if you mark it as unstable.

When we have an IDL compiler (which I'm afraid I won't really be able to work around, unless someone has a great idea), I want to be able to specify an interface without an IID and have the IDL compiler generate a new random IID every time the IDL file is compiled (so every new build is incompatible with the previous). This is a bit extreme, but definitely allows for hard core refactoring to happen, and is not expected to be used in "production".

In an earlier comment, listen mention the "standard battle cry of the COM programmer" being "NEVER break bincompat, EVERYTHING will break!!!!". That'd be the battle cry of the clueless COM programmer. Not that there's any lack of these, unfortunately (which is one root cause for people saying that "COM sucks", incidentally!)...

Interface-driven programming..., posted 1 Oct 2003 at 19:04 UTC by mrorganic » (Journeyer)

...has its place. But it's not a panacea. By trying to be too generic in how you handle things (the language-neutrality aspect), you will probably have to give up powerful features of whatever language you're using to implement XPLC itself (I assume C++). XPLC may be applicable to a wide problem-domain, but how is it better (from a C++ programmer's standpoint) than using the STL? Is the pain of learning a new API and feature-set worth the benefit of using said library in the first place?

This is not intended as a criticism of XPLC specifically; rather, it is a question as to whether we need yet another low-level helper library when there are already so many (like the STL, BOOST, or Loki) that are standards-compliant and foster good C++ programming paradigms (which COM does not do).

The most basic question is: What problem are you trying to solve? If you can't clearly explain the problem, you can't solve it very well. And even if the problem is clear, can you definitely say that no existing tools solve the problem?

?, posted 1 Oct 2003 at 21:11 UTC by Malx » (Journeyer)

can you definitely say that no existing tools solve the problem?

And what you should know to answer this question? Need you CS degree or just link to Google :)
Imho it is hard to find out if some tool is what you need before you try to use it for that sort of tasks. Better to write own, then to learn and try all of them :(

So what is the solution? Featurelist standart? ;)

what is XPLC trying to solve, posted 1 Oct 2003 at 22:32 UTC by pphaneuf » (Journeyer)

I think the mistake is often trying to use a tool for everything (the old "when you have a hammer, everything looks like a nail" saying). This leads to COM programmers enforcing "infinite" backward compatibility, just because they can, or making every single object a COM object, again just because they can (as opposed to CORBA, I dare you to make a system where every single object is a CORBA object!).

Comparing XPLC to STL, Boost or Loki is completely missing the point again. XPLC is all about interface and doesn't care about implementation, and these three are all about implementation. You can use all three simultaneously to implement XPLC interfaces. I don't but that's only because I'm trying to keep XPLC small (it is 17k currently) and I couldn't if I used them, but I could if I didn't care about the size that much.

COM is not about fostering good C++ programming paradigms, it's about being language neutral, which would probably make it actively anti-C++ programming paradigms, in fact.

What I want to solve is interactions between pieces of code in general. I want to take a piece of code and call it from this other piece of code, even though they might be in different languages or that the callee didn't expect to be called by me. I want extensibility, so that you can call code that didn't exist when the caller was written. It's all about the edges between the codes, the interfaces.

This is definitely a problem that needs solved, since it has been solved so many times before (that's where you might say "then don't do it again!", but hold on!), but everyone solves it in his own way. See this list of other component systems, many of them are very limited and very dumb. I obviously can't replace all of these, but I want to make XPLC the lightweight component system.

mikehearn, in a recent diary entry, talks about how GObject might be a good solution, and it probably is, but some of the problems are very difficult to get over, and they are not all technical. For example, having a name that starts with the letter "G" is a bigger problem than you'd imagine (I used to work on Mozilla's XPCOM, and while there is a standalone distribution and it is a fine component system, almost nobody uses it outside of Mozilla, just because it's "Mozilla's component system").

On the technical side, since GObject is C-oriented, a C++ binding would probably hurt a bit and be at least somewhat complicated to use OR be less efficient than the C binding. Scripting languages, we don't mind much if there is a little overhead in the binding since they are so slow to start with, but if you go the trouble of using C or C++, you probably want some of that efficiency in return for your pain. Qt has a component system as well, which I'm sure is very efficient in C++ and easy to use, but doesn't have a chance in hell of being anything useful in C, I'd guess. XPLC is equally easy to use in both C and C++ (that is, not 100% as easy as "normal" code, but not significantly harder) and is exactly equally efficient in both (C++-style virtual method calls, quite efficient compared to some other component systems).

So I can definitely there are other tools to solve this problem, I even made a more-or-less exhaustive list (if you think there's one missing, tell me!), but I'm doing XPLC anyway for a number of reasons:

  • I think they do it wrong, somehow, or have something that will critically prevent their acceptance.
  • Many had good ideas separately, but none have the union of good ideas.
  • I'm a Unix guy and I believe in simplicity over completeness.
  • I like component systems and I'm having way more fun than I'm allowed to. :-)

Dennis Ritchie and Ken Thompson once wrote that "The success of UNIX lies not so much in new inventions but rather in the full exploitation of a carefully selected set of fertile ideas." and I also agree with Richard Gabriel's Worse is Better (also known as the 90%-10% rule, the 80-20, or some other numbers in similar ratios). Contrast this with mikehearn's dismissal of COM-like technologies in his diary entry that I linked to. Microsoft .NET is closer to COM than you'd think, but it is also more complete than really needed. COM is not the best we can do, but go back and read "Worse is Better".

Finally, I have a "delivery vehicule" to carry XPLC unto people's systems, involving including it with a very popular package that comes by default on most Linux distributions. Since it is only 17k, I don't expect any complaints (with what people put up with nowaday, I'd be rather pissed if they'd whine about that!). See, I'm rather sneaky... ;-)

XPLC doesn't prevent you from using your favourite C++ features, posted 13 Oct 2003 at 17:24 UTC by apenwarr » (Master)

I work right next to pphaneuf and I'm not much a component nazi so I've had a lot of these arguments in person already. I usually lose.

I think the difference between XPLC and most other component systems is that XPLC doesn't force anything on you. It's really just a handy library of functions that allow you to be as cross-platform or as backward-compatible as you like, which is something that's missing from standard C++ (ie. if I add one more data member to my class, it usually isn't usable by old programs without recompiling them). But I don't have to be backwards compatible, cross language, or cross platform if I don't want.

One day I decided that I wanted my WvStreams library to support monikers. WvStreams have relatively uniform interfaces (read, write, select, geterror, etc) but there are a lot of kinds of streams. It would be nice if I could instantiate a collection of streams from the command line using strings (monikers) and maybe load new kinds of streams from plugin libraries. So I whacked in XPLC support in a couple of hours (pphaneuf gasping at my madness all the while), and I had what I wanted. It wasn't particularly compatible with anything, and I use lots of templates, and I've changed the interface lots of times without changing the IID (take that!), and it probably still only works in C++ and no other language, but I got the free features I wanted, and I expect to get more free features as XPLC matures.

Now, if I had tried to convert to COM in order to get those things, I would probably still be having nightmares. "Lightweight" is a key feature here. :)

Think of XPLC as just a fancy version of libdl (ie. dlopen()). libdl doesn't prevent you from using the STL. It really has nothing to do with the STL. It's just free stuff.

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!

X
Share this page