Welcome to Step 5 of my DCOM tutorial. In this series, I will strip the mystique, the headache, and the confusion from DCOM by giving you a comprehensive tutorial with a straightforward example. OK, no promises -- but I will give it a good try.
If you want to follow along with this tutorial and add code and use the Visual C++ Wizards as we go along, that's great. In fact, I very highly recommend that, because otherwise this tutorial is a big waste of electronic ink (?). However, I follow along exactly with the tutorial myself, as I write it, and develop the code and use the Visual C++ wizards just as I say you should. The screenshots, in fact, are from my development of the files for each step! To download this already-developed code to compare with your own, simply click the 'Download the Step n Files - n KB" link at the top of each step. There's also an archive (coming soon) of the files for all the steps at the Questions and Answers page (coming soon) for this tutorial. I still recommend that you follow along with me as we go; this way, you can learn while you code. If you ever have problems along the way with this tutorial, feel free to:
- Post a message to the message board at the bottom of this page.
- Check out this tutorial's Questions and Answers page -- coming soon.
A diagram of how our software will eventually work is shown in Figure 1. The client calls a method on the server, which then fires an event back to the client using a connection point. This connection point's event sink is implemented in the client (using MFC and ClassWizard!!!), and the client shows its user a message telling the user that the server said "Hello!":
Figure 1. Diagram of our DCOM client/server set-up.
Remember, our steps in developing the software in this tutorial are as follows:
- Step 1: Create the server,
HelloWorldServ.NET, using the ATL Project Wizard.
- Step 2: Modify the starter files provided by the ATL Project Wizard.
- Step 3: Add a simple ATL Object,
HelloWorld, to the server, to expose our functionality.
- Step 4: Add a method,
SayHello(), to the server, which fires the event the client handles.
- Step 5: We look at connection points and set up the server's end of one.
- More steps coming soon!
We're currently on Step 5 of the tutorial. This is the step where we complete the basic design of the server, as illustrated above in Figure 1. We've already defined and implemented the
IHelloWorld::SayHello() method which still doesn't do what it's supposed to -- say "Hello, World!" back to the client. Now is the time to make it do this. But first, how about a little primer -- or refresher? -- on what Connection Points are, before we begin with Step 5?
Connection Points Demystified
Before we plunge in with Step 5 of our tutorial, let's just take a moment for me to rip the shrouds of mystery off of Connection Points. Figure 2 below shows a generic scenario which is true for COM, DCOM, and even function callbacks, for goodness' sake.
Figure 2. A source and a sink.
This involves two objects, a "source" and a "sink". Think of the "source" like the water faucet of the kitchen sink at home. You turn a handle, and stuff comes out of it (hopefully water). Where does it go? If nothing's backed up, this water flows down into the bottom and goes into the drain (which can be thought of as the "sink"). OK, so things flow from the source, to the sink. In the kitchen sink analogy above, this is water. However, I've never seen a computer network system run with water flowing across the wires, so obviously something else is at work in DCOM.
In DCOM, there is a "client" somewhere on the network, and there is a "server" also somewhere on the network. Without the use of connection points, things flow only one way: method calls replace our water, the client replaces our faucet, and the server replaces the drain. This is way oversimplifying things, but the user "turns a handle" (that is, clicks a button, for example), and "stuff" (that is, method calls) "comes out of" the client. This "stuff that comes out" then "flows" over the network using DCOM. These calls "flow" to the server, which then collects them and acts like the "drain", or our sink. Here's Figure 3, which is almost exactly like Figure 2, but puts the client in place of the "source" and the server in place of the "sink", with the network in between:
Figure 3. Our client and server as the source and the sink.
OK, so now we have method calls flowing like water; wonderful. However, when the client calls methods, the server does all kinds of things that might be interesting to clients. So the server fires events all over the place. If our client doesn't care if the server fires events, it will just ignore them. However, if it cares, it will
Advise() the server. Then, the source-sink relationship of Figure 3 can be thought of in reverse:
Figure 4. The reverse of Figure 3.
Connection points come in when you have the following happening:
- The client is the source of a method call.
- The server sinks (that is, acts as the sink for) a method call.
- An "event call" comes out of the now-server-as-source.
- The client sinks the event call and does something.
As you can see, this is a round-trip. A method call goes from the client to the server, and then an event call goes from the server, to the client, as seen in Figure 5.
Figure 5. A round-trip. This is just a reiteration of our design, which is shown in Figure 1.
Advise() step is done before item number 1 above, and the
Unadvise() step (where the client goes back to being aloof) happens after item number 4. The points of contact on both the client and server, and the
Unadvise()ing that happens, all together form a so-called connection point. Now, we're ready to begin Step 5.
Step 5: Add the OnSayHello Event to the Event Source Interface DHelloWorldEvents
Let's plunge in, shall we? Visual C++ .NET 2003 makes it a trivial task to add an event to the source. Just use the Visual C++ Wizards! However, you have to know where to click. To add a method that actually makes our server say 'hello', open Class View, and click the plus sign to expand the
HelloWorldServNETLib icon, as shown in Figure 6, below:
Figure 6. Opening the icon for
HelloWorldServNETLib, and then adding a method to
DHelloWorldEvents interface icon, point to Add, and then click Add Method, as shown above. When you do so, the Add Method Wizard appears, as shown below in Figure 7:
Figure 7. Adding the
OnSayHello() method to the
We want to call our new method
OnSayHello. Leave the Return Type field set to
HRESULT, and type
OnSayHello in the Method Name box, as shown.
Next, we're going to add a parameter to our method. This will be the name of the computer the server is running on. This will help us make sure -- when testing -- that DCOM is really working and we're in fact running on a different computer than the one on which the client is installed. To do this, complete the following steps:
- Under Parameter Attributes, check the In checkbox.
- Then, type
BSTR in the Parameter Type box.
BSTR is a special COM-compatible string type.
- The parameter should give the name of the computer which the server is running on; so let's give the parameter the name
bstrHostName in the Parameter Name box, and then click Add.
- Once you're done, the Add Method Wizard should look as is shown in Figure 7 above.
You may notice another tab in the Wizard dialog box, IDL Attributes. This contains higher-level settings; for our purposes, the settings' default values suit us, so leave the defaults on that tab. Click Finish to close the Wizard, and add the
OnSayHello() method to
DHelloWorldEvents. Visual C++ then opens the HelloWorldServNET.idl file, which we don't care about editing any more, so this file can be closed.
To check your work, go back to Class View. Click the plus sign to expand the
HelloWorldServNETLib icon, and do the same in order to expand the
DHelloWorldEvents icon, as shown below in Figure 8:
Figure 8. Verifying the existence of our new
OnSayHello() event method.
Server: Last Steps
Now it's time to complete our implementation of this simple COM server. Switch to Solution Explorer. Next, right-click the HelloWorldServNET.idl file, and click Compile. Make sure this step is done.
Figure 9. Re-implementing the connection point,
DHelloWorldEvents, now that it has the
Now, in Class View, right-click the
CHelloWorld class, point to Add, and then click Add Connection Point, as shown above in Figure 9. I know we already have a connection point, but doing the process over again -- like we are now -- serves to refresh the project to give us access to fire the
OnSayHello() event from the implementation of
Figure 10. Using the Implement Connection Point Wizard to select the
DHelloWorldEvents connection point.
Once you've chosen the Add Connection Point command, the Implement Connection Point Wizard appears, as shown in Figure 10. Click the > button to move the
DHelloWorldEvents dispinterface name from the Source Interfaces to the Implement Connection Points box, and then click Finish. After the wizard is done, there is actually some duplicate code we now must remove. Once the Wizard finishes, the DHelloWorldEvents_CP.h file will open, as shown below in Figure 11. The top of the file will look as shown below:
Figure 11. The editor now has a new file open, and this file is called DHelloWorldEvents_CP.h.This shows the top lines of our new file.
Remember, back in Step 2 (go ahead and click the Step name if you want a refresher), when we replaced all occurrences of
DHelloWorldEvents throughout the project with the new features of Replace?
_IHelloWorldEvents was defined in a file called _IHelloWorldEvents_CP.h, a file also
#include'd in HelloWorld.h. So, we have to clean up the mess. To do so, do the following:
- First, scroll down in the file DHelloWorldEvents_CP.h and remove any *extra* definitions of the
CProxyDHelloWorldEvents<T> class which you may find. Only one class should be defined in this file. If this is already the case, then skip this step. The correct definition looks like that shown above, in Figure 11.
- Save the DHelloWorldEvents_CP.h file, and close it.
- Now, open Solution Explorer, and click the icon for the _IHelloWorldEvents_CP.h file.
- Type the Backspace key to remove the _IHelloWorldEvents_CP.h file from the project.
- In Solution Explorer, double-click the icon for the HelloWorld.h file.
- In the HelloWorld.h file, remove the line:
- Save the HelloWorld.h file, and close it.
Finally, click the File menu, and then click Save All. When you're finished, the Class View should resemble what is shown in Figure 12, below:
Figure 12. How the Class View should properly look.
Now, as we have things, in order to say "hello" to the client, we simply call the
Fire_OnSayHello() member function from within
CProxyDHelloWorldEvents<T> is in fact a base class of the
CHelloWorld class, this works. In the Class View, double-click the icon for the
CHelloWorld::SayHello() member function, to open the function definition in the editor. Now, add the code shown in bold in Listing 1, below:
TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1;
if (!GetComputerName(szComputerName, &dwSize))
Listing 1. Code to add to finish the
CHelloWorld::SayHello() member function.
Notes From the Rear
Now we've finished Step 5 of the tutorial. This is the end of the coding of the server. The next step, which is Step 6, guides you through the final build process, and how to set up your new server (and the client) on the server and client computers. To go to the previous step, Step 4, click << Back below. Or click Next >> to go on to Step 6 (coming soon!). If you have questions, try clicking Questions and Answers (coming soon!) to go to a page which might help you.
<< Back | Next >> - coming soon!
Questions and Answers - coming soon!
Tip: If you're having trouble or can't understand something, it's often the case that you just went ahead as far as you could in this tutorial without following through and downloaded the code for the latest Step that was done. Perhaps if you go back to previous Steps, and work through the tutorial in the places where it wasn't clear, this may help. Also, it could be because there's still more Steps yet to be written! Stay tuned!
Tip: Also, if you have a question, go ahead and post it to the message board, below, which is at the bottom of this article page. I will get an email when you do so, and then everyone can see your question and my answer. Don't forget to rate this article either! If you gave it anything less than 5, then post to the message board (below) as to the reason why, so I can make these articles better for everyone.