November 1999
Code for this article: Nov99Wicked.exe (94KB) Jeff Prosise is the author of Programming Windows 95 with MFC (Microsoft Press, 1996). He also teaches Visual C++, MFC and COM programming seminars. For more information, visit http://www.solsem.com.
|
As COM matures and becomes less of a novelty and
more of a mainstream programming technology,
the body of COM programming literature grows exponentially. So does the volume of available sample code. But no matter how expansive these resources become, the subject of monikerswhat they are, how they work, and when they should be usedremains a mystery to the average COM developer.
One reason monikers seem so abstruse to rank-and-file programmers is that much of the published documentation discusses monikers in the context of OLE. Ironically, OLE is a relatively minor subset of the larger problem set that monikers was designed to solve. Predefined moniker types or system monikers, such as URL monikers and file monikers, enable COM developers to download resources from URLs and identify object instances based on the names of the files in which they store their persistent data. Custom monikers can be combined with MkParseDisplayName to extend the COM namespace. One use for custom monikers is to build a generic object-naming facility that allows clients to identify and connect to existing object instances. Which brings me to the subject of this month's column. Recently I spoke to a fellow programmer who's incorporating COM into his company's development plans in a big way, and he posed the following scenario. Suppose you write a COM class to monitor a hardware device and provide information about the device's status to interested clients. Furthermore, suppose that the target machine contains several such devices, and that each device will be monitored by a unique instance of said COM class. When a client starts up, how can it connect to the object associated with a particular device if all it knows is the device name? As usual, there are many ways to attack this problem. One solution is a custom moniker that permits object instances to be namedand connected tousing the name of the corresponding device. Since monikers are often defined as objects that are used to name other objects, you'd think that sample code would be easy to come by. It's not. So I wrote a custom moniker I call the instance moniker that is essentially a general-purpose object-naming mechanism. It can be used to identify object instances by device name or any other name, and to retrieve interface pointers to running objects. Besides being useful in its own right, the instance moniker is a wonderful example of how custom monikers are written and how they're used to solve a classic COM programming problem. Instance Monikers at Work
Once the builds are complete, run two instances of DevMonClient.exe side by side. Type a fictitious device name such as USB001 into the Device Name boxes, and then click the Start buttons. Given identical device names, both clients will connect to the same device monitor object. You can prove it by clicking the Get Device ID buttons. Both should display the same device ID, indicating that both are connected to the same device monitor object (see Figure 1). Start a third client and enter a different device name, however, and you'll be greeted with another device ID. |
![]() |
Figure 1 Two Clients Connected to the Same Object |
The secret to its ability to connect to a running device monitor object is that before it creates a new object, DevMonClient attempts to connect to an existing object by binding to it through an instance moniker created from the name typed into the Device Name box. Here's the pertinent source code from DevMonClientDlg.cpp: |
|
If BindToObject fails, DevMonClient creates a brand new object instance. It then connects the object to a device by calling IDeviceMonitor::StartMonitoring and passing in the device name found in the Device Name box: |
|
The device monitor object's StartMonitoring method doesn't really connect to a device; it only pretends to. But it does something else that's no less important: it creates an instance moniker that wraps the device name in pszDeviceName, and it registers that moniker in the running object table. Here's an excerpt from DeviceMonitor.cpp: |
|
Now that the device monitor object is up and running and registered in the running object table, the next client that binds through a moniker encapsulating the object's device name will succeed in retrieving an interface pointer.
That's a high-level explanation of how instance monikers work. The rest is just detail. Creating Instance Monikers
|
|
This is precisely the technique used by DevMonClient and DevMonServer to create instance monikers. Both formulate the string passed to MkParseDisplayName on the fly. To create an input string of the form Instance:devicename, device monitor clients use the device name found in the Device Name box. Device monitor objects use the device name referenced by IDeviceMonitor::StartMonitoring's pszDeviceName parameter.
Note that an inherent race condition occurs if two clients call BindToObject at about the same time. If the timing is right (or wrong, depending on your point of view), it's entirely possible that both clients will succeed in creating new object instances, even if the monikers on which they called BindToObject are identical. If that's a concern, you'll need to institute some sort of synchronization mechanism in your clients to ensure that they don't call BindToObject at the same time. That mechanism could be as simple as an interprocess mutex that each client claims before calling BindToObject. CInstanceMoniker and CMonikerFactory
|
|
directs ATL to use CMonikerFactory as the class object class for CInstanceMoniker objects. Because CMonikerFactory is a template class, you can use it with other moniker classes if you'd like by changing the class name passed to it as a template parameter and modifying its implementation of ParseDisplayName.
The CDeviceMonitor Class
Device Monitor Clients Drop Me a Line
|
Have a tricky issue dealing with Windows? Send your questions via email to Jeff Prosise: JeffPro@msn.com |
From the November 1999 issue of Microsoft Systems Journal.
|