Component Object Model (COM) Development on Mac OS X
Pages: 1, 2
Creating a COM Interface
The centerpiece to any COM development is the creation of one or more interfaces. This interface describes the class and the interfaces that the class supports. We use C++ to describe our interface; however, you can also describe the same interface in other programming languages, if you like.
Our COM interface describes a fictitious IPhoneDialer component. The following code is taken from Sample Portable COM Interface/IPhoneDialer.h:
// 9B5FD3E4-83A2-11D8-8ED2-000393C360A2
DEFINE_GUID(
CLSID_PhoneDialer,
0x9B5FD3E4,
0x83A2,
0x11D8,
0x8E, 0xD2, 0x00, 0x03, 0x93, 0xC3, 0x60, 0xA2
);
// AE809768-83A2-11D8-B171-000393C360A2
DEFINE_GUID(
IID_IPhoneDialer,
0xAE809768,
0x83A2,
0x11D8,
0xB1, 0x71, 0x00, 0x03, 0x93, 0xC3, 0x60, 0xA2
);
class IPhoneDialer : public IUnknown
{
public:
// IPhoneDialer methods
STDMETHOD(Dial(char* inPhoneNumP)) PURE;
};
The first two lines declare the class and interface's unique identifiers. Unique identifiers are used so that Mac OS X can be told which interface of a class is being referred to; e.g., when a COM component is to be instantiated. The identifiers have a high probability of being unique across networks and machines and can be generated from the command line by using the uuidgen tool.
Incidentally, Universally Unique Identifiers (UUIDs) and Globally Unique Identifiers (GUIDs) are one and the same. Microsoft coined the term GUID.
The class declaration defines our interface. All COM interfaces inherit from IUnknown so that they can be queried and reference counted. Our interface declares just one method (Dial) and is declared using the regular COM convention of STDMETHOD. The method is also declared abstract (PURE) so that client programs can only instantiate the interface via COM. COM needs to control the instantiation so that components can be reference counted.
Creating a COM Server
Now we have created our COM interface declaration, we are ready to create our COM server -- the dynamic link library that will house our COM component.
Creating a COM server on OS X involves using the CFPlugin Bundle template when generating a new project
with Xcode. The image to the right shows the groups and files declared with my sample COM server.
PhoneDialer.h declares a concrete subclass of IPhoneDialer that also declares methods for IUnknown so that reference counting and querying can be implemented. The header file also declares a class factory, and some counters for reference counting the COM objects housed by this server. Class factories are responsible for instantiating an interface of a COM class.
PhoneDialer.cpp is the implementation of our COM IPhoneDialer component. The implementation describes what the fictitious Dial method does, how the COM component is reference counted and queried, and how the class factory instantiates our COM component. This file is the meat of our server, and where you will undoubtedly spend most of your time.
Server.h declares a Core Foundation UUID for our PhoneDialer factory. This UUID is only used within Server.cpp.
Server.cpp provides all that is necessary to create a PhoneDialer factory and manage components created by the factory. CFPhoneDialer is a private subclass of PhoneDialer for Core Foundation activities and records the factory that creates it. It is important to register components that have been created with the Core Foundation so that OS X can unload the dynamic link library when it is not being used. CFPhoneDialer provides this registration mechanism.
Carbon COM provides the COM, Automation header files, and implementation required so that you can develop COM components with platform independence.
Carbon COM projects also depend on the Core Foundation and CoreServices frameworks. Core Foundation is used to manage CFPlugin activities and CoreServices provides some utility functions for performing memory management across threads.
In terms of what you will generally end up changing for your implementation, the following applies:
- PhoneDialer.h changes: New filename, your own interface, and new GUIDs.
- PhoneDialer.cpp changes: New filename and your own interfaces.
- Server.h changes: New UUIDs.
- Server.cpp changes: Include your interface file and rename PhoneDialer to the name of your interface. Additionally, you can declare additional factories for different interfaces if you need to.
- Carbon COM: This should never change, unless you want to extend its functionality. (Or unless you find a bug in my code!)
Core Foundation Plugins also require that you make a couple of declarations in the info.plist file under our project's Resources grouping. Essentially, you need to declare the UUIDs of the factories in your server, what function handles factory creation, and how the UUIDs of COM classes map to factories. The following code does this:
<key>CFPlugInFactories</key>
<dict>
<key>AB3831E4-83AB-11D8-B989-000393C360A2</key>
<string>PluginFactory</string>
</dict>
<key>CFPlugInTypes</key>
<dict>
<key>9B5FD3E4-83A2-11D8-8ED2-000393C360A2</key>
<array>
<string>AB3831E4-83AB-11D8-B989-000393C360A2</string>
</array>
</dict>
Creating a COM Client
Creating a COM client is much less work! The image to the right shows the groups and files associated with the
client project.
Once again, you need the interface declaration of the COM component you wish to use -- IPhoneDialer.h. This is the only file that you need to share between the client and the server.
main.cpp demonstrates how to use a COM component in a platform-independent manner. The source of this was included in the previous section, A COM Client Example.
Carbon COM provides the COM, Automation header files, and implementation as per the server project.
Creating a COM client on Mac OS X can be done with most templates provided by Xcode -- ours is based on the C++ Tool template. Just like its server counterpart, the client project depends on the Core Foundation and CoreServices frameworks.
Running the COM client will instantiate our PhoneDialer COM component and call upon its Dial method. Impress your friends and give it a run!
Final Thoughts
We use cross-platform COM to create plugins for our Mac OS X and Win32 products. Doing so provides the potential to save on reimplementing code. We save time and money and are able to more easily produce a result that is well-tested and of high quality. If this is something that you or your organization considers important, then I hope that the above is insightful.
While writing this article it became apparent to me that what I have actually done is ported some of Win32's Automation functionality to OS X. COM is already part of OS X -- it just needed a little help to make it something more than just being useful for Core Foundation Plugins.
Happy COM programming!
Christopher Hunt has 22 years experience in the software development industry. Chris founded and runs Class Action, which has developed a number of "best of category"software products.
Return to the Mac DevCenter
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 13 of 13.
-
Endian issues?
2004-04-21 09:10:02 rufwork [Reply | View]
What good's a COM architecture if you still have to recompile on another platform? That is, why not just keep things simple C and wrap whatever's most approrpriate for each platform instead of writing COM both places?
Let's face it, porting to the Mac is nontrivial, and using COM on the Mac won't remove the reverse of those issues to get to the PC. There are still, unf, easier ways to hit the two platforms, even with C, than COM. -
Endian issues?
2004-04-21 15:08:52 ChristopherHunt [Reply | View]
Thank you for your questions rufwork.
Using COM is not a Silver Bullet to cross-platform development. However COM *helps* you write portable software components given the methods that I describe in the article.
COM also allows you to express interfaces in an object oriented way which you might consider an advantage over regular C interfaces.
Endianess still has to be catered for and has nothing to do with COM.
-
Welcome home?
2004-04-19 09:17:11 tlaurenzo1 [Reply | View]
While it is great to see another cross-platform tool integrated into OS X, I would hardly call it a "welcome home"! :)
After doing a lot of programming with ATL, MFC and VB I spent the better part of a couple years trying to forget that this technology existed.
I recognize that the core COM system is nice, but any advanced usage just plain sucks.
Just two cents from a recovering Windows developer! :)
-
Apple Dev Link
2004-04-19 01:22:15 vikl [Reply | View]
http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFPlugIns/index.html
-
Relic from NeXT?
2004-04-17 20:29:17 kbixler [Reply | View]
Is the COM support a leftover from Distributed OLE stuff in OpenStep?
Check out http://web.archive.org/web/19970616182601/www.next.com/OPENSTEP/Products/DOLE/DOLE_DataSheet.html for more info.
-
Relic from NeXT?
2004-04-17 21:02:23 ChristopherHunt [Reply | View]
My feeling is that Core Foundation's COM is more recent and is there to support the Core Foundation Plug-Ins only. The Core Foundation COM support is pretty basic. I'd imagine that you'd have been given much more with D'OLE's COM e.g. CoCreateInstance and an API naming scheme much more in line with Microsoft.
-
Well, yes, OK, but
2004-04-16 20:25:09 kiyookasan [Reply | View]
I've spelunked around in the COM header files on MacOS X, and it is fine that there is some kind of COM on X.
But here's the problem. Microsoft Office X has its own implementation of COM bundled into the CFM shared libraries that ship with Office, buried in the depths of the directory structure, private (but shared within Office apps) to Office applications.
The whole point of COM is interapplication communication.
I did my own investigation into Microsoft's support of COM on Macintosh, and the latest official blurb I could find was in the form of an out-of-print book and a MacTech article which included a StuffIt archive of a 'beta' version of the ActiveX SDK (COM's name at that time). Also, at that time Microsoft was hyping support for ActiveX/COM brower plug-ins.
Summary: the great thing about standards is that there are so many to choose from.
Hopefully the new version of Office for OS X will either expose the SDK interface publicly, or utilize the COM headers/API (which you've documented in this article) included in the Apple Developer tools.
-
Well, yes, OK, but
2004-04-18 18:37:25 eredhuin [Reply | View]
Thanks for this commentary.
So is it possible to use the not-quite-apple COM stuff to integrate with Excel using Xcode?
-
Well, yes, OK, but
2004-04-21 15:12:54 ChristopherHunt [Reply | View]
Hi eredhuin,
I do not believe it possible to integrate with Excel using Xcode using the CoreFoundation COM. The methods I describe in my article are more to do with managing your own interfaces.
I am unfamiliar with "non-quite-apple COM". -
Well, yes, OK, but
2004-04-17 01:13:38 ChristopherHunt [Reply | View]
Thanks for your comment kiyookasan.
COM is still useful for your own interapplication communication particularly where you find yourself porting code between OS X and Win32. In simplistic terms, COM provides a mechanism to share an object oriented library as a dynamic link library.






I beleive
unsigned short* theStrP = new unsigned short[theStrSize];should read
unsigned short* theStrP = new unsigned short[theStrSize + 1];to avoid a buffer overrun when
theStrP[theIter] = 0;is executed.
Regards