Comtypes: How Dropbox learned to stop worrying and love the COM
tech.dropbox.com
tech.dropbox.com
The .NET framework actually does a pretty good job of hiding the complexity from you, but having done some serious integration with VS, let me say that's an abstraction leak that I wouldn't wish upon my worse enemy.
Most COM interfaces support late binding, so you can easily call them from a language like JavaScript, which is already installed in Windows as part of the platform. Ten years before node.js existed, you could run this JavaScript on Windows 2000 to read and print a file:
var fso = new ActiveXObject("Scripting.FileSystemObject");
if ( fso.FileExists("myfile.txt") ) {
var ts = fso.OpenTextFile("myfile.txt", ForReading);
WScript.Echo(ts.ReadAll());
WScript.Quit(0);
} else {
WScript.Echo("File Not Found");
WScript.Quit(1);
}I've always thought of COM as one of those almost brilliant pieces of technology that somehow got completely ignored. Of course, it's still commonplace in the Windows world, but the concept facilitates reusability so much, it's a shame it wasn't adopted by other platforms.
Other than that I don't know any other companies who did something like COM.
Edit: I'm not really being clear above. When I'm saying I've found the leaky abstraction to be around memory management, I'm not conflating "leaky abstractions" with "memory leaks," except insofar as the big point where I find problems with COM is around the need to be much more explicit with resource management. That the leaky abstraction happens to be around the...er, leaks, is coincidental.
It's a Spolsky-ism: http://www.joelonsoftware.com/articles/LeakyAbstractions.htm...
Edit: Egg meet face.
The memory management is the part of COM that leaks through the abstraction layer in my experience, but it has usually leaked through in MS's attempt to actually properly jacket the things in full-blown .NET objects. I've otherwise not had issues.
I have a hunch I'd also complain about the remote proxying if I used DCOM from .NET, but I haven't.
Also, parent works for Fog Creek.
Things go crazy once you decide that you want to give your COM object to somebody else in a framework-style callback pattern. For example, if you want to extend Visual Studio, you need to implement a bunch of interfaces, manage memory yourself, deal with object registration and GUIDs, worry about invalid interfaces and versioning, etc. And that's just all the standard COM stuff, that you're faced with 100% of the time.
But there are compounded complexities. COM uses HRESULT return values, .NET uses exceptions. Most public interfaces quickly start looking like dynamicly typed code: full of references to IUnknown and QueryInterface calls. You need to understand all the threading apartment model stuff, but the .NET libraries bundle up some best practices there that often don't play nice if you need to slightly bend the rules.
Finding documentation is impossible because the web is dominated by low quality questions with low quality answers using the same terminology. Using the Microsoft personas, the Morts and Elvises drown out the Einsteins in the Google results. However, even having the VS source (which I did), it's far too big to grep and far too many levels of QueryInterface indirection to grok.
Need some enum values? You'll need to redefine them yourself. Go find the C headers and copy out the values.
Need to deploy a new version? Good luck understanding the rules of AssemblyVersion, AssemblyFileVersion, the class loader, Locales, etc. Some random error happening somewhere in the class loader? You need to install some little utility program that some guy in DevDiv made that makes a secret registry key set up some secret logging to some secret IPC channel. Argh.
I was tasked with wiring up deploy/debug for WinPhone 7. I needed to implement a few dozen methods from the IVsDebugger interface, do all my own threading and networking, and then spend a few weeks debugging synchronization issues with the UI. Meanwhile, I had the exact same task for Familiar Linux on a Compaq iPaq several years prior to that. Despite Eclipse's bloat rivaling Visual Studio's, all I needed to do was change a config file from `$PROG $ARGS` to `ssh $HOST gdb $PROG $ARGS`.
EDIT: Just wanted to add that I found this post difficult to write. I, for the life of me, couldn't recall the interface name IUnknown. In the 3 years I've been gone, I've forgotten more about COM than most .NET programmers will ever know. I'm quite happy about that...both for me escaping, and for those .NET programmers lucky enough to remain ignorant of these things. In general, all of DevDiv does a great job keeping this complexity away from people writing line of business applications.
When I'm writing managed code that needs to work heavily with COM, I write it in C++ using ATL, and expose that a level higher for calling from .NET. This avoids 90% of the issues you're hitting, while making it trivial to use COM from .NET (or, for that matter, any other non-C++ language).
The good news is that WinRT does legitimately solve most issues managed languages have. To me, far more importantly than WinRT's ability to export full-blown classes, is its ability to export much, much richer metadata, providing a straightforward way to work with WinRT objects from any language that can process the new metadata format. No more secret enums in header files, no more HRESULT insanity (HRESULTs are transparently mapped to exceptions). Just clean, cross-language calling. We've finally realized the original promise of COM.
Of course, that goal was met by "cheating." In my opinion, Microsoft achieved their goal merely by pushing the abstraction layer further into C++. In practice, I think that's an overall win--it certainly makes most of your points about using COM from managed languages moot--but it also means that your "native" libraries are going to be significantly more WinRT-specific. Or, if you prefer: instead of writing an ATL jacket to make the COM interface clean for .NET, I'll instead write a WinRT jacket to make a C++11 library clean for .NET.
The more things change...
$a = New-Object -com Word.Application
$a.visible = $false
$a.Documents.Open("{absolute path}").DeleteAllComments()
Better examples: http://www.simple-talk.com/dotnet/.net-tools/com-automation-...
This was just word, com and nothing else. Even the application host was an instance of word.
I doubt it could have been done with anything else then or now.
1. Summary of COM, a widely used technology
2. Summary of comtypes, a python package for interacting with COM
3. One example of a gotcha they ran into with comtypes and arrays of COM objects.
Conclusion: meh.
Admittedly I have never seen the movie, which wikipedia describes as a "dark comedy satirizing the nuclear scare". I suspect the article doesn't follow along those lines either.
You should fix that.
In addition, the bomb in question was the result of an ill- conceived Doomsday device to prevent the use of bombs.
In other words, this title suggests that as part of a policy of mutually assured destruction, Microsoft created a device to unleash COM if it ever sensed it was being attacked. They just forgot to tell anyone about the device (why didn't you tell the world!?!) and now we are all living with the fallout.
(I haven't disassembled the dropbox dlls, but there aren't any obvious python signatures in the install directory)
If the above is wrong, I would love to know what they are using!
I've tried py2exe and all of the similar packagers that I could find, and every one of them requires brittle incantations to get everything packaged (I'm using a lot of graphics and number-crunching code beyond standard python, but all from the package index). I guess on the scale of Dropbox this is probably not a big issue. On my scale (1), it's one of the reasons I've started using C# (an unexpectedly awesome experience so far, btw). I need the fastest possible path to one-click exe files that non-technical (bio/chem) researchers and RAs can run. I've belatedly realized that the overhead of doing this in python is really too high.
[edit: found this SO answer: http://stackoverflow.com/questions/2678180/how-does-dropbox-... but it doesn't give specifics. In particular, AFAIK, none of the packagers can eat libpython.dll into a larger exe files]
[edit: interesting - http://blog.codepainters.com/2012/09/17/python-care-and-feed... ]
IronPython is actually very cool if you want/have to live in .Net Land: it's mature, simple, and supports Python 2.7.
http://www.py2exe.org/index.cgi/SingleFileExecutable#line-30...
But that doesn't say anything about other dependencies.
for i in range(100):
print idevice_item_array[0]
>actually crashes Python because it creates and destroys a hundred IDeviceItem objects, resulting in a hundred calls to Release on the real COM object. After the first Release call, the COM object is considered deleted.This doesn't sound like a "gotcha". It sounds like a huge bug that comtypes needs to address.
However it clearly didn't work as a whole as Microsoft felt the need to replace it with .Net for application level programmers.