Welcome to this tutorial. In this series, I will strip the mystique, the headache, and confusion from DCOM by giving you a comprehensive tutorial with a straight forward example. OK, no promises - but I will give it a good try. In this series of articles, I will show you how to use the following technologies:
- the ATL COM AppWizard;
- implementation of a Windows NT Service;
- smart pointers;
- Connection Points;
- MFC Support for Connection Points;
- MFC ClassWizard support for implementing Connection Points (yes, it's true!).
These will be used to develop a sample client/server system to say "Hello, world from < machine >" to the user! I haven't heard of anybody needing to do client/server development in order to say, "Hello, world!", but that's what I'm doing.
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 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" links at the top of each step. There's also an archive of the files for all the steps at the Questions and Answers page for this tutorial. I still recommend that you follow along with us as we go; this way, you can learn while you code.
The tutorial's steps were completed by the author on a Windows NT 4, SP 5 network with Visual C++ 6.0 SP 4. The author hasn't tested this on Windows 2000 because he doesn't have a copy. If anything in a particular step of this tutorial doesn't work or works differently under Windows 2000, please post a message to the message board. Pretty much anything which works under Windows NT 4 SP 5 should work the same under Windows 2000, because of backward-compatibility, unless Microsoft's been making changes.
What This Tutorial Assumes
I have a motto: never write anything if it asks too much of the reader and not enough of the writer. To this end, I am going to tell you straight away what this tutorial assumes that you know. I would also include a 'Who Needs This Tutorial" section, much like they include a "Who Needs to Read This Book," but I'll let you be the judge of whether or not you need something.
This tutorial assumes that:
- You know how to use a Windows-based computer;
- You know how to use AppWizard;
- You're familiar with basic MFC programming;
- You've heard about ATL, COM, and DCOM;
- You know how Services work under Windows NT and Windows 2000;
- And that you're developing all this on Windows NT 4.0 or higher.
I hope I'm not assuming too much of you. I'll guide you along doing this at the same level or a little higher as how Microsoft guided us all through the Scribble tutorial. That is, I won't hold your hand too much, but I won't confuse you either (at least, I don't think Microsoft's explanations are confusing...).
I welcome any and all input to how I explain things; to yell at me or praise me (I can take both), post on the message boards at the bottom of this step. Believe me, I will get your post. This way, people can see both your question, and my answer.
Since we'll be referring to class names, symbol names, and such, it's good to follow a convention with this so that everything is consistent. Here's what we'll do:
Code fragments will appear in their own yellowish blocks on the page. The lines of new code that you'll have to add will appear in bold. If you don't have to add anything, but I'm just pointing some code out for my health, then nothing will be in bold:
// NOTE: Add this code
CAnotherClass class; // And this code
class.AnotherFunc(); // And this code
// Done adding code
Fair enough? OK. How about when some functions are a bajillion lines long, and we're only interested in the three lines at the top of the function? Then I'll use an ellipsis (. . .), like the Visual C++ docs do. This just means that there's a bunch of code there that we'll ignore:
HRESULT hResult = S_OK;
hResult = class.Func();
I confess, I am a big fan of Microsoft's version of Hungarian notation. I guess that's a failing of mine, but I went through the Scribble tutorial until it got involved with all the OLE stuff. This drilled the MS Hungarian notation into my brain, unfortunately. I am a strong supporter of using
m_ for member variables,
C for classes,
I for interfaces, and
D for dispinterfaces, or those things that we use to fire off connection point events:
DHelloWorldEvents instead of
When I refer to a function, method, or variable, I will show it in a fixed-width font. Whenever I refer to a compiler keyword, I'll also put it in a fixed-width font. Symbol names, regardless of who uses them, will be in the fixed-width font. Chris goes around shaking his fingers at us writers and making sure that we follow these conventions. (Just kidding, ha ha ha...) Here are some examples:
Acknowledgements and Attributions
I would like to take just a quick second and acknowledge some sources and people which led to this, because without their work and contributions, I might still be in the dark. I would like to thank, in particular, Dr. Richard Grimes, who wrote the excellent book Professional DCOM Programming. Dr. Grimes is a highly knowledgeable authority on DCOM and COM programming and he has a talent for explaining things in a way that they're easy to understand.
Professional DCOM Programming, published by Wrox Press, very thoroughly covers DCOM with intelligent discussion, working samples, and demystification of the really complicated stuff. By this, I mean threading, security, IDL, marshaling, and the Microsoft Transaction Server, to name just a few. I highly recommend that you buy this book (it's $49.95), you don't have to have read it in order to understand this tutorial.
Also, I would like to acknowledge the contributors to various articles and columns in MSDN Magazine (formerly MSJ), whose work, reprinted in the MSDN Library, helped me through the jungle of DCOM. This included columns by George Shepard and Don Box, two very knowledgeable COM experts. Thanks also go to Charles Steinhardt, and Tony Smith, two authors who have written for Visual C++ Developer.
We will proceed step by step, following the methodology employed by Microsoft for writing the explanation of the Scribble tutorial. In this tutorial, you will develop:
- A DCOM server, implemented as a Windows NT Service. This server will expose a '
SayHello()' method which will say hello to the client user with a connection point.
- An MFC client, with support for smart pointers and connection points made easy! We'll even use ClassWizard to implement the connection point handling (!).
Before we plunge into writing any software, it's always good to design (just a little!) what it's going to do! What we'll develop in this tutorial will work as shown in Figure 1 below:
Figure 1. The way things will work.
As you can see in Figure 1, our software will follow a simple pattern:
- The client will call a
- The server will say hello to the client by firing an event over the network;
- The client will show the user a message in response to this event to indicate that the server did indeed say, "Hello!"
Enough With the Blathering; On to the Code!!
Yes, yes; I get the hint. We'll start by doing Step 1 of the tutorial in this article, and then you can go on to the next step of the tutorial by simply clicking the Next button at the bottom of this page.
- Step 1: Create the server,
HelloServ, using the ATL COM AppWizard.
- Step 2: Modify the starter files provided by AppWizard.
- Step 3: Use the New ATL Object Wizard to add a simple COM object, the
HelloWorld object, to the server.
- Step 4: Modify the
IHelloWorld interface to include a
- Step 5: Add an event method,
OnSayHello(), to the connection point source interface,
- Step 6: Build the server, and install it on the server computer.
- Step 7: Create a MFC client,
HelloCli, which calls the server and handles the connection point event sink.
When the steps above are done, we'll have a living, breathing, fully functional DCOM application. At the bottom of the page for each step of this tutorial are Back and Next links, which you can use to go to the previous or next step. There's also a link which takes you to the 'Questions and Answers' page.
The Questions and Answers page gives me more space and the capability to display screenshots when I post answers to readers' questions. Feel free to post your question to The Code Project's message board at the bottom of each step. If there's a question that gets asked often or whose answer is not so simple, I'll see it and put it on the Questions and Answers page. This way, the answers can be helpful to all of the readers of The Code Project. I'll then reply to the question on the message board and e-mail the author saying "I answered this question on the Questions And Answers page!".
I really must stop flapping my gums so much. Some people like to talk to hear themselves talk; I think that, maybe I like to write to read my own writing... But anyway, enough. Let's plunge in with Step 1.
Step 1: Create the Skeleton
HelloServ Server With the ATL COM AppWizard
OK; time to get started. Close anything you're working on in Visual C++ and save the changes. Then close any open Workspace too. Next, click the File menu, and then click New. This should bring up the New dialog box, as shown in Figure 2 below. Click the ATL COM AppWizard in the list, and type '
HelloServ' for the Project Name:
Figure 2. Selecting the ATL COM AppWizard in the New dialog box.
When the settings look the way you want them to, click OK. This brings up Step 1 of the ATL COM AppWizard (which only has one step). This is shown in Figure 3, below, with the 'Service' option button selected. This is OK; since the AppWizard doesn't let us set anything else, the next thing to do is to click Finish:
Figure 3. Selecting that we want AppWizard to give us a service.
Why A Service?
A Windows Service is a great type of process to have as your ATL/COM server. Windows Services, in my experience, have performed better as DCOM servers. Services can live while the machine is not logged on, whereas most EXE programs may not run. Also, Services are totally non-interactive, which is just fine when all you want your components to do is to perform routine system tasks, such as reading files or running programs, or providing monitoring services. You don't want windows popping up all willy-nilly, say, on your server computer sitting in a room in Ireland when you are running the client over in India. Plus, services are able to be started and stopped, and kept track of, using the Control Panel.
And for that matter, why an EXE and not DLL?
See above as far as the stand-alone EXE is concerned. A DLL is not very utilitarian as a remote server, because all DLLs *must* be mapped into the address space of an EXE process on the server system. A special EXE process which maps DCOM server DLLs into its address space, and then remotes the DLLs' component(s), is known as a surrogate. DLLs and surrogates are altogether complicated beasts to maintain and configure when you need remote access to your components. Especially since these are not reference-counted or monitored by the system, in case the number of clients drops to zero or if the surrogate hangs, leaving you dead in the water. So a Service is my favorite choice.
After you click Finish, the AppWizard displays the New Project Information dialog box, shown in Figure 4. The New Project Information box only has a little bit of information in it; it just tells us that AppWizard is going to give us the starting point of a brand-new Windows NT Service which is also a DCOM server. However, this service doesn't have any COM objects yet. We'll add those in Step 3.
Figure 4. The New Project Information dialog box.
Click OK to have AppWizard generate the
HelloServ starter program. Now, we are ready to go on to the next step, modifying the source files. We'll start with the modifying in Step 2, which is the next step of this tutorial. Click the Next link below to proceed to Step 2.
Questions and Answers