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!":
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
{
...
public:
TCHAR m_szDisplayName[256]; 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:
- Click the ResourceView tab.
- Double-click the String Table folder to open it.
- Double-click the String Table icon in the folder to open the String Table.
- Find the blank entry in the list, and double-click it. The Properties window appears, as in Figure 2, below.
- In the ID box, type
IDS_DISPLAY_NAME
.
- Press TAB to move to the Caption box, which contains what the
IDS_DISPLAY_NAME
symbol maps to in your code.
- In the Caption box, type
Hello World Server
, as shown in Figure 2.
- Press Enter. This saves the new String Table 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()
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;
CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_NONE,
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
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);
_ASSERTE(SUCCEEDED(hr));
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