Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

DCOM D-Mystified: A DCOM Tutorial, Step 2

0.00/5 (No votes)
11 Aug 2000 2  
We modify starter files, provided by the ATL COM AppWizard, to improve the user-friendliness of our server.

Introduction

Welcome to Step 2 of our DCOM tutorial. In this series, I will strip the mystique, the headache, and 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 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. If you ever have problems along the way with this tutorial, feel free to:

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!":

Diagram of our DCOM client/server set-up.
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, 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 SayHello() method.
  • Step 5: Add an event method, OnSayHello(), to the connection point source interface, DHelloWorldEvents.
  • 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.

We're currently on Step 2 of the tutorial, where we modify some things in the starter source code provided to us by the ATL COM AppWizard. We do this to:

  • Provide a user-friendly "Display Name" for this service;
  • Add code to initialize security properly.

Step 2: Modify the Starter Files Provided by AppWizard

To start, we need to provide Windows with a user-friendly name it can use to display this service to the user. This is called the "Display Name" of the service. AppWizard already set up and specified a "Service Name" for us: HelloServ. For the user's sake, let's come up with something which is easier to understand: Hello World Server. To do this, we'll add a m_szDisplayName member variable to the CServiceModule class, and a String Table entry to hold the display name. We do things this way so that it's easy for us to change the display name of the service to whatever we wish.

First, double-click on the CServiceModule icon in ClassView. This will jump you to where the CServiceModule class is declared, in STDAFX.H. We need to add a m_szDisplayName member variable to the class. Move the cursor to the // data members section, and add the code shown below in bold:

class CServiceModule : public CComModule
{
    ...

// data members
public:
    TCHAR    m_szDisplayName[256];        // display name of service
    TCHAR    m_szServiceName[256];

    ...
};
Listing 1. Adding the m_szDisplayName member variable to the CServiceModule class.

The next thing to do is to add an entry to the String Table, IDS_DISPLAY_NAME, to hold the display name we want to use. Figure 2, shown below, illustrates adding the String Table entry. To do this, complete these steps:

  1. Click the ResourceView tab.
  2. Double-click the String Table folder to open it.
  3. Double-click the String Table icon in the folder to open the String Table.
  4. Find the blank entry in the list, and double-click it. The Properties window appears, as in Figure 2, below.
  5. In the ID box, type IDS_DISPLAY_NAME.
  6. Press TAB to move to the Caption box, which contains what the IDS_DISPLAY_NAME symbol maps to in your code.
  7. In the Caption box, type Hello World Server, as shown in Figure 2.
  8. Press Enter. This saves the new String Table entry to the String Table.

Adding the IDS_DISPLAY_NAME entry to the String Table.
Figure 2. Adding the IDS_DISPLAY_NAME entry to the String Table.

On to the next part of Step 2. We have a member variable, m_szDisplayName, which we want to fill with the contents of the String Table entry IDS_DISPLAY_NAME. To do this, double-click the CServiceModule::Init() function in ClassView, and then add the line shown in bold:

inline void CServiceModule::Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE h, 
    UINT nServiceNameID, const GUID* plibid)
{
    ...

    LoadString(h, nServiceNameID, m_szServiceName,
                sizeof(m_szServiceName) / sizeof(TCHAR));

    LoadString(h, IDS_DISPLAY_NAME, m_szDisplayName,
                sizeof(m_szDisplayName) / sizeof(TCHAR));

    ...
}
Listing 2. Adding a LoadString() call to CServiceModule::Init().

Now we have made sure that the IDS_DISPLAY_NAME string gets loaded into the m_szDisplayName member variable of CServiceModule. The next thing to do is to change the call to CreateService() in the CServiceModule::Install() function. The call is shown in Listing 3 below in bold. The call is again shown in Listing 4, also below, but this time the argument which you need to replace is shown in bold:

inline BOOL CServiceModule::Install()
{
    ...

    SC_HANDLE hService = ::CreateService(
        hSCM, m_szServiceName, m_szServiceName,
        SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
        SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
        szFilePath, NULL, NULL, _T("RPCSS\0"), NULL, NULL);

    ...
}
Listing 3. The call to the Windows CreateService() function, as called by AppWizard.

Change the second passing of m_szServiceName to m_szDisplayName, as shown in Listing 4:

inline BOOL CServiceModule::Install()
{
    ...

    SC_HANDLE hService = ::CreateService(
        hSCM, m_szServiceName, m_szDisplayName,
        SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
        SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
        szFilePath, NULL, NULL, _T("RPCSS\0"), NULL, NULL);

    ...
}
Listing 4. Changing the second m_szServiceName to m_szDisplayName.

Finally, the last part of Step 2 is to initialize everything properly. Go to the place in the HELLOSERV.CPP file shown // PLACE THE CURSOR HERE comment in Listing 5:

#include "stdafx.h"
#include "resource.h"
#include < initguid.h >
#include "HelloServ.h"

#include "HelloServ_i.c"


#include < stdio.h >

CServiceModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
END_OBJECT_MAP()

// PLACE THE CURSOR HERE

LPCTSTR FindOneOf(LPCTSTR p1, LPCTSTR p2)
{
    ...
Listing 5. Where in the HELLOSERV.CPP file to place the cursor.

Add all of the following code at where I've just told you to place the cursor:

extern "C" BOOL WINAPI InitApplication()
{
    HRESULT hResult = CoInitialize(NULL);
        if (FAILED(hResult))
            return FALSE;        // failed to initialize COM

    // Turn security off so that everyone has access to us
    CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_NONE,
        RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);

   // Initialization successful
   return TRUE;
}
Listing 6. Adding the InitApplication() function to the server. Next, we need to replace some of the code in CServiceModule::Run() with a call to the InitApplication() function which we added in Listing 6 above. Using ClassView, go to the CServiceModule::Run() function, and delete the code shown in bold:
void CServiceModule::Run()
{
    ...

    HRESULT hr = CoInitialize(NULL);
//  If you are running on NT 4.0 or higher you can use the following call
//  instead to make the EXE free threaded.
//  This means that calls come in on a random RPC thread
//  HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

    _ASSERTE(SUCCEEDED(hr));

    // This provides a NULL DACL which will allow access to everyone.
    CSecurityDescriptor sd;
    sd.InitializeFromThreadToken();
    hr = CoInitializeSecurity(sd, -1, NULL, NULL,
        RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
    _ASSERTE(SUCCEEDED(hr));

    ...
}
Listing 7. Code to delete from the implementation of CServiceModule::Run().

Now replace what I told you to delete with the code shown in Listing 8 below. The code to add is shown in bold:

void CServiceModule::Run()
{
    ...

    HRESULT hr = S_OK;

    if (!InitApplication())
        return;

    ...
}
Listing 8. Code to add in order to replace what we've deleted.

Notes From the Rear

We've now completed Step 2 of this tutorial. We added a display name to help the user, and we fixed the security-initialization code for the service. Click Next to move on to the next step of this tutorial, Step 3, click Back to go back to Step 1 of this tutorial, or click the Questions and Answers button to jump to the Questions and Answers page! Good luck.

<< Back | Next >>

Questions and Answers

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here