macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

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.

Server Groups and Files 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

Client Groups and Files 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