DCOM 揭秘:DCOM 教程,第 2 步






4.79/5 (9投票s)
我们修改了 ATL COM AppWizard 提供的入门文件,以提高我们服务器的用户友好性。
引言
欢迎来到我们的 DCOM 教程的第 2 步。在本系列教程中,我将通过一个全面的教程和简单的示例,为您揭开 DCOM 的神秘面纱,消除其中的烦恼和困惑。好吧,不敢保证——但我会尽力而为。
如果您想跟随本教程学习,并在过程中添加代码并使用 Visual C++ 向导,那太好了。事实上,我强烈建议您这样做,否则本教程将是电子墨水(?)的巨大浪费。不过,我在编写本教程时,自己也严格遵循着教程,并按照我说的那样开发代码并使用 Visual C++ 向导。实际上,屏幕截图就来自我为每个步骤开发的文件!要下载这些已经开发好的代码以供您自行比较,只需单击每个步骤顶部的“下载第 n 步文件 - n KB”链接。在教程的问答页面还有一个所有步骤文件存档。我*仍然*建议您跟随我们一起学习;这样,您可以在编码的同时学习。如果您在本教程过程中遇到任何问题,请随时
- 发送电子邮件给我:brian@harttechservices.com。
- 在此页面底部的留言板上发帖。
- 查看本教程的问题和解答页面。
图 1 显示了我们的软件最终将如何工作的示意图。客户端调用服务器上的一个方法,然后服务器通过连接点向客户端触发一个事件。这个连接点的事件接收器是在客户端实现的(使用 MFC 和 ClassWizard!!!),客户端向其用户显示一条消息,告诉用户服务器说“Hello!”
图 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 条目的过程。要做到这一点,请完成以下步骤
- 单击 ResourceView 选项卡。
- 双击 String Table 文件夹以打开它。
- 双击文件夹中的 String Table 图标以打开 String Table。
- 找到列表中的空白条目,然后双击它。属性窗口将出现,如下图图 2 所示。
- 在 ID 框中,键入
IDS_DISPLAY_NAME
。 - 按 TAB 键移动到 Caption 框,该框包含
IDS_DISPLAY_NAME
符号在代码中映射的值。 - 在 Caption 框中,键入
Hello World Server
,如图 2 所示。 - 按 Enter 键。这将把新的 String Table 条目保存到 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
字符串被加载到 CServiceModule
的 m_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 步;或单击“问答”按钮跳转到问答页面!祝您好运。