|
Program examples compiled using Visual Studio/C++ .Net 2003 compiler on Windows XP Pro machine with Service Pack 2. Topics and sub topics for this tutorial are listed below. Don’t forget to read Tenouk’s small disclaimer. The supplementary note for this tutorial is .NET.
Using Visual C++ and attributes, you can speed up and simplify the process of COM programming. This tutorial uses attributes to implement both a client and a server application. During the course of this tutorial, you will use attributes and events. The tutorial develops a singleton server object (an object that can have only one instance) that has its own dual interface and a dispatch interface used for firing off events. The server object takes data, given to it through the Send() method of its dual interface, and transmits it to all connected components through the Transfer() event on its dispatch interface. In addition, the tutorial implements a client (an ActiveX control) that contains a server object. The control responds to the Transfer() event fired by the server object and has its own dual interface that implements several methods: Connect(), Send(), and Disconnect(). If the Transfer() event is fired with a variant containing a BSTR, the string is displayed in the center of the control. The tutorial is divided into seven steps, each building on the application developed in the previous step. Keep in mind that all the coding part was done manually, copy and paste, instead of using 'wizard'.
▪ Step 1: Creating the Projects. ▪ Step 2: Adding the Server Object. ▪ Step 3: Implementing the Server. ▪ Step 4: Adding the Client Object. ▪ Step 5: Adding the Client Interfaces. ▪ Step 6: Implementing the Client. ▪ Step 7: Using the Client Control.
Step 1: Creating the Projects
In this step, you will create an initial solution containing two ATL projects. The two projects will implement the server and client objects of the tutorial.
To create a new solution
Figure 1: Creating a new Visual C++ .Net project, starting with blank solution.
|

Figure 2: Entering the blank solution name.
Once the solution has been created, add the two ATL projects to the empty solution.
To create a new project

Figure 3: Adding new project to Visual C++ .Net solution.

Figure 4: Adding DispClient, a new ATL project to solution.

Figure 5: ATL Project Wizard Application Settings page.

Figure 6: The generated files and resources for DispClient ATL project.
|
File |
Description |
|
DispClient.cpp |
Contains the module attribute, which implements DLLMain(), DLLRegisterServer(), and DLLUnregisterServer(). The module type also defines the GUID for the type library. Notice that the GUID and helpstring have been automatically generated. |
|
DispClient.h |
A MIDL-generated file that will contain interface definitions. For purposes of this tutorial, this file will be unnecessary. |
|
DispClient.rc |
The resource file, which initially contains the version information and a string containing the project name. |
|
DispClient.rgs |
Contains entries that are added to the registry, which will register your COM object. |
|
DispClient.vcproj |
A file containing the project settings. |
|
DispClientps.def |
The module definition file for the proxy/stub DLL. For purposes of this tutorial, this is unnecessary. |
|
ReadMe.txt |
A file containing an explanation of the files generated by the application wizard. |
|
Resource.h |
The header file for the resource file. |
|
StdAfx.cpp |
The file that will #include the ATL implementation files. |
|
StdAfx.h |
The file that will #include the ATL header files. |
|
Table 1. |
|
You will also notice a DispClientPS project. This project creates the proxy and stub that allow your object to be accessed from outside of its COM apartment.
To create a new server project

Figure 7: Adding another new project to solution.

Figure 8: Adding new DispServer, an ATL project to solution.

Figure 9: Modifying Application Settings options.
For the DispServer project, the application wizard creates a similar set of files compared to the DispClient project. The only difference is in the file DispServer.cpp, where the module type is exe instead of dll.
The next step focuses on the implementation of the server object.
Step 2: Adding the Server Object
In this step, you will use Class View to add objects to the project. You need to add a single ATL object (named CDispServ) to the server. This object also serves as an event source.
To add a class to the project

Figure 10: Adding new class to DispServer.

Figure 11: Adding an ATL Simple Object template to DispServer.

Figure 12: An ATL Simple Object Wizard, Names page.

Figure 13: ATL Simple Object Wizard, Options page.
Note in Class View that the CDispServ class, as well as the IDispServ and _IDispServEvents interfaces, have been created and are now visible. To implement the new class, the wizard added two new files to the project:
▪ DispServ.h contains most of the implementation of the CDispServ class, as well as the interfaces. The CDispServ class has automatically been made a COM event source through the event_source attribute with the _IDispServEvents interface automatically specified as the event interface for CDispServ. UUIDs, progids, help strings, and version numbers have been automatically generated for the class and the interfaces.
▪ DispServ.cpp contains the remainder of the CDispServ class. At this point, it includes a few necessary files.

Figure 14: The added interfaces and events (of course respective files also added)
You can build the application by clicking Build DispServer from the Build menu, though DispServer does not actually do anything interesting yet. However, it does have the capability to register itself. This is done automatically when the project is built. The next step implements the functionality of the DispServer object.
Step 3: Implementing the Server
In this step, you will add the functionality to make the class do something interesting. In the last two steps, you have created the CDispServ class, which now exposes a custom interface (IDispServ), and an event source (the _IDispServEvents event interface). Beyond this implementation, however, it does not actually do anything.
The main purpose of the CDispServ class is to receive data using the Send() method and transmit the information using the Transfer() event. Therefore, a DispClient object connected to a DispServer object can call Send() through the IDispServ interface, passing it data. The Send() method then fires the Transfer() event, propagating the data to all connected DispClient objects. Previously, this required creating a connection point proxy class to fire the events. With the new events feature, the situation is simplified; you will add the Transfer() event to the _IDispServEvents interface.
To add and define the Transfer event
[id(1), helpstring("method Transfer")] HRESULT Transfer(VARIANT data);
Your interface definition should now match the following definition:
__interface _IDispServEvents : public IDispatch
{
[id(1), helpstring("method Transfer")] HRESULT Transfer(VARIANT data);
};

Listing 1.
To fire the Transfer() event, make a call to _IDispServEvents_Transfer() from your event source. The form for firing an event is always InterfaceName_EventName.
To add and define the Send method
[id(1), helpstring("method Send")] HRESULT Send(VARIANT data);
Your interface definition should now match the following definition:
__interface IDispServ : IDispatch
{
[id(1), helpstring("method Send")] HRESULT Send(VARIANT data);
};

Listing 2.
To implement the Send() method for the CDispServ class, scroll down to the bottom of DispServ.h and add the following code directly below the last public: section of the class:
STDMETHOD(Send)(VARIANT data)
{
_IDispServEvents_Transfer(data);
return (S_OK);
}

Listing 3.
The full CDispServ class should now look like this:
class ATL_NO_VTABLE CDispServ : public IDispServ
{
public:
CDispServ()
{
}
__event __interface _IDispServEvents;
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
public:
STDMETHOD(Send)(VARIANT data)
{
_IDispServEvents_Transfer(data);
return S_OK;
}
};
The final task implements the CDispServ class as a singleton server, which means that all clients will connect to the same server.
To define CDispServ as a singleton server
DECLARE_CLASSFACTORY_SINGLETON(CDispServ);

Listing 4.
The server is now complete. You can build the server by selecting Build DispServer from the Build menu. Once the server is successfully built, it will register itself.
The Visual Studio development environment also provides wizards that let you add properties and methods. Just right click on the interface node in Class View and select a wizard from the context menu. The next step adds a simple client object to the DispClient project.
Further reading and digging:
MSDN MFC 6.0 class library online documentation - used throughout this Tutorial.
MSDN MFC 7.0 class library online documentation - used in .Net framework and also backward compatible with 6.0 class library
DCOM at MSDN.
COM+ at MSDN.
COM at MSDN.
Unicode and Multibyte character set: Story and program examples.