65.9K
CodeProject 正在变化。 阅读更多。
Home

DCOM 揭秘:DCOM 教程,第 2 步

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (9投票s)

2000 年 8 月 6 日

CPOL
viewsIcon

242659

downloadIcon

2232

我们修改了 ATL COM AppWizard 提供的入门文件,以提高我们服务器的用户友好性。

引言

欢迎来到我们的 DCOM 教程的第 2 步。在本系列教程中,我将通过一个全面的教程和简单的示例,为您揭开 DCOM 的神秘面纱,消除其中的烦恼和困惑。好吧,不敢保证——但我会尽力而为。

如果您想跟随本教程学习,并在过程中添加代码并使用 Visual C++ 向导,那太好了。事实上,我强烈建议您这样做,否则本教程将是电子墨水(?)的巨大浪费。不过,我在编写本教程时,自己也严格遵循着教程,并按照我说的那样开发代码并使用 Visual C++ 向导。实际上,屏幕截图就来自我为每个步骤开发的文件!要下载这些已经开发好的代码以供您自行比较,只需单击每个步骤顶部的“下载第 n 步文件 - n KB”链接。在教程的问答页面还有一个所有步骤文件存档。我*仍然*建议您跟随我们一起学习;这样,您可以在编码的同时学习。如果您在本教程过程中遇到任何问题,请随时

图 1 显示了我们的软件最终将如何工作的示意图。客户端调用服务器上的一个方法,然后服务器通过连接点向客户端触发一个事件。这个连接点的事件接收器是在客户端实现的(使用 MFC 和 ClassWizard!!!),客户端向其用户显示一条消息,告诉用户服务器说“Hello!”

Diagram of our DCOM client/server set-up.
图 1。 我们的 DCOM 客户端/服务器设置图。

请记住,本教程中我们开发软件的步骤如下:

  • 第 1 步:使用 ATL COM AppWizard 创建服务器 HelloServ
  • 第 2 步:修改 AppWizard 提供的启动文件。
  • 第 3 步:使用 New ATL Object Wizard 向服务器添加一个简单的 COM 对象,即 HelloWorld 对象。
  • 第 4 步:修改 IHelloWorld 接口以包含 SayHello() 方法。
  • 第 5 步:向连接点源接口 DHelloWorldEvents 添加一个事件方法 OnSayHello()
  • 第 6 步:构建服务器,并在服务器计算机上安装它。
  • 第 7 步:创建一个 MFC 客户端 HelloCli,该客户端调用服务器并处理连接点事件接收器。

我们目前处于教程的第 2 步,在此我们将修改 ATL COM AppWizard 提供的一些入门源代码。我们这样做是为了

  • 为该服务提供一个用户友好的“显示名称”;
  • 添加代码以正确初始化安全设置。

第 2 步:修改 AppWizard 提供的入门文件

首先,我们需要为 Windows 提供一个用户友好的名称,它可以使用这个名称向用户显示该服务。这被称为服务的“显示名称”。AppWizard 已经为我们设置并指定了一个“服务名称”:HelloServ。为了用户方便,让我们来创建一个更易于理解的名称:Hello World Server。为此,我们将向 CServiceModule 类添加一个 m_szDisplayName 成员变量,以及一个 String Table 条目来存储显示名称。我们采用这种方式,以便于我们随意更改服务的显示名称。

首先,在 ClassView 中双击 CServiceModule 图标。这将跳转到 STDAFX.H 中 CServiceModule 类声明所在的位置。我们需要在类中添加一个 m_szDisplayName 成员变量。将光标移到 // data members 部分,然后添加下面用粗体显示的几行代码

class CServiceModule : public CComModule
{
    ...

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

    ...
};
列表 1。m_szDisplayName 成员变量添加到 CServiceModule 类。

接下来要做的是向 String Table 添加一个条目 IDS_DISPLAY_NAME,用于存储我们要使用的显示名称。下面显示的图 2 说明了添加 String Table 条目的过程。要做到这一点,请完成以下步骤

  1. 单击 ResourceView 选项卡。
  2. 双击 String Table 文件夹以打开它。
  3. 双击文件夹中的 String Table 图标以打开 String Table。
  4. 找到列表中的空白条目,然后双击它。属性窗口将出现,如下图图 2 所示。
  5. 在 ID 框中,键入 IDS_DISPLAY_NAME
  6. 按 TAB 键移动到 Caption 框,该框包含 IDS_DISPLAY_NAME 符号在代码中映射的值。
  7. 在 Caption 框中,键入 Hello World Server,如图 2 所示。
  8. 按 Enter 键。这将把新的 String Table 条目保存到 String Table 中。

Adding the IDS_DISPLAY_NAME entry to the String Table.
图 2。IDS_DISPLAY_NAME 条目添加到 String Table。

继续第 2 步的下一部分。我们有一个成员变量 m_szDisplayName,我们希望用 String Table 条目 IDS_DISPLAY_NAME 的内容来填充它。为此,在 ClassView 中双击 CServiceModule::Init() 函数,然后添加下面用粗体显示的行

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));

    ...
}
列表 2。CServiceModule::Init() 中添加一个 LoadString() 调用。

现在我们已经确保 IDS_DISPLAY_NAME 字符串被加载到 CServiceModulem_szDisplayName 成员变量中。接下来要做的是更改 CServiceModule::Install() 函数中对 CreateService() 的调用。下面列表 3 中用粗体显示的行是该调用。下面列表 4 中也再次显示了该调用,但这次您需要替换的参数用粗体显示

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);

    ...
}
列表 3。 Windows CreateService() 函数的调用,由 AppWizard 调用。

将第二次传递的 m_szServiceName 更改为 m_szDisplayName,如列表 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);

    ...
}
列表 4。 将第二个 m_szServiceName 更改为 m_szDisplayName

最后,第 2 步的最后一部分是正确地初始化所有内容。转到 HELLOSERV.CPP 文件中列表 5 中用 // PLACE THE CURSOR HERE 注释指示的位置

#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)
{
    ...
列表 5。 光标在 HELLOSERV.CPP 文件中的放置位置。

将下面所有代码添加到我刚才告诉您放置光标的位置

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;
}
列表 6。InitApplication() 函数添加到服务器。接下来,我们需要用对 InitApplication() 函数的调用替换 CServiceModule::Run() 函数中的部分代码,该函数已在上面列表 6 中添加。使用 ClassView,转到 CServiceModule::Run() 函数,然后删除下面用粗体显示的的代码
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));

    ...
}
列表 7。 需要从 CServiceModule::Run() 实现中删除的代码。

现在,用下面列表 8 中的代码替换我告诉您删除的代码。要添加的代码用粗体显示

void CServiceModule::Run()
{
    ...

    HRESULT hr = S_OK;

    if (!InitApplication())
        return;

    ...
}
列表 8。 为了替换我们已删除的代码需要添加的代码。

后记

我们现在已经完成了本教程的第 2 步。我们添加了一个显示名称以帮助用户,并且我们修复了服务的安全初始化代码。单击“下一步”继续本教程的下一步,第 3 步;单击“上一步”返回本教程的第 1 步;或单击“问答”按钮跳转到问答页面!祝您好运。

<< 上一步 | 下一步 >>

问答

© . All rights reserved.