Here are my crib notes for "Effective COM: 50 Ways to Improve your COM and MTS-based Applications" by Don Box, Keith Brown, Tim Ewald, and Chris Sells, from Addison Wesley, 1999.
Remember how Meyer's "Effective C++" book stopped you from using about two-thirds of the gimmicks explained in earlier books? "Effective COM" will stop you from imitating at least 90% of the COM you see elsewhere. Only after reading this book did I feel I had any hope of achieving good style with COM.
Here are some lessons I learned, although not necessarily as the authors intended.
E_NOTIMPLfrom unimplemented methods. Use smaller interfaces that can be implemented completely. (Item 7.)
IDataObject. For type safety, define a unique interface for each type of data. To avoid race conditions, return related pieces of data together in a structure or from a single method call. (Item 8.)
IUnknown exclusively to share
components with other C++ or VB developers.
IDispatch very selectively for
customers who can't manage anything other
than VB scripting. The book's Epilogue says
"Curse the Visual Basic team for the
abomination that is
Remember "Visual Basic programmers can still implement externally defined pure vtable and pure dispatch interfaces" although they can only define new interfaces as dual interfaces. It's not your problem.
size_iswhen passing arrays, but don't use
length_is. Instead, pass a shifted array pointer. (Item 13)
IUnknownobject references because proxies and stubs won't always give you the one you want. Use
iid_isto specify the exact interface. (Item 14.)
[in,out]parameters that contain pointers because you won't know what resources to release after a failure. Use two separate
[out]arguments. Rethink how you pass structures that contain pointers. (Item 15.)
AddRef) won't work across apartment boundaries. The proxy will disappear. (Noticing a pattern here?) (Item 16.)
AddRef's. One will be
AddRef'd by third parties, and one will call
AddRefon those same third parties. (Item 16.)
cpp_quoteto alias data types or methods. They aren't handled properly by type libraries. (Item 17.)
[out]parameter pointers to null if you return an error. (Set
*ppThing=0.) Stubs will ignore your
HRESULTand marshal non-null
[out]parameters anyway. (Item 18.)
free. A method that safely copies a string should take a page of code. Check that callers provide non-null
[out]pointers to pointers. Before returning an error, free the memory of
[out]parameters. If allocation fails, return
E_OUTOFMEMORY. (Item 19.)
nullimmediately after releasing them. Use a macro if necessary. (Item 20.)
Releaseimmediately before and after a
QueryInterfaceto get an interface pointer that you return from a method. Otherwise, if
QueryInterfacefails, the implementer of
Releasewill have no opportunity to free resources. (Item 20.)
CoCreateInstanceto create a member with
thisas the controlling
IUnknown*. Since you can't call
Releasein a constructor, you must increment the internal counter by hand at the beginning of the constructor. (Item 20.)
Invoke, always call
static_castto the correct type, then call
void*. (Item 21.)
auto_ptrshowed that heap memory cannot be managed by scoping rules as if it were stack memory. You must have read and understood the implementation of your smart pointer, to know the limitations. If you are likely to confuse maintainers of your code, then don't use smart pointers. (Item 22.)
BSTRs. The C++ compiler can't distinguish their types. (Item 27.)
QueryInterface, return the interface with a
getSomeInterface()method. (Item 28.)
getSomeInterfacemethod or put the global into a
CoMarshalInterfaceand extract with
CoUnmarshalInterface. (Item 29.)
AddRefon passed interface pointers, and the new thread should call
Release. (Item 30.)
IMessageFilterto postpone other calls and messages. All outward calls are placed in a new thread so that the message pump can keep the GUI alive. Unfortunately the response may arrive after other calls that want to change the state of your object. (Item 35.)
CLSCTX_INPROC_SERVER. (Item 36.)
CoDisconnectObjectto inform the stub to release references to the object. Proxies will then return the
RPC_E_DISCONNECTEDto calls by clients. (Item 37.)
This book confirms the long-standing belief that MS development tools encourage fragile and unmaintainable code. The code may still compile, but it won't be easy to upgrade or integrate with next year's code. MS will constantly change their recommended patterns, frameworks, and developer tools. The book's epilogue says "Distrust your tools. The COM group produces COM, and various tool groups at Microsoft proceed to butcher it." Remember, clean maintainable code is not necessarily in Microsoft's interest.
You should be able to justify every line typed into your COM code, without a wizard's guidance. Find a safe core subset of COM and don't use clever tricks just because they are explained in every other COM book. If you stick to the safe subset, you may not have to rewrite current code every time you reuse it.
This book, and Don Box's previous book "Essential COM", unfortunately maintain the pretense that COM is a binary standard, independent of language or platform. COM is is none of these. COM is a C++ specification for Microsoft compilers on Microsoft operating systems. With any language other than C++ you are incompatible with the design of COM. With any other compiler or platform, you can forget about binary compatibility. The binary layout of C++ vtables is not a standard. You can use parts of COM in other Microsoft languages, but those languages introduce hacks that look horrible in C++.
Stick to C++ when implementing interfaces for other Microsoft developers, and stick to the cleanest possible C++ style. If you want Java, GNU C++, or other non-Microsoft languages, then use portable connections like sockets or JNI to talk to COM proxies and clients. (J++ isn't Java, and it isn't meant to be maintainable.)
The purpose of COM is to introduce components to each other, not to dictate how they interact or exchange data. The COM specification emphasizes, "COM, like a traditional system service API, provides the operations through which a client of some service can connect to multiple providers of that service in a polymorphic fashion. But once a connection is established, COM drops out of the picture. COM serves to connect a client and an object, but once that connection is established, the client and the object communicate directly without having to suffer overhead of being forced through a central piece of API code..."
Bill Harlan, March 1999
Return to parent directory.