DCOM 揭秘:DCOM 教程,第一步






4.80/5 (25投票s)
本教程将向您展示如何以一种简单、直接的方式编写具有最新功能的 DCOM 软件。
引言
欢迎来到本教程。在本系列中,我将通过提供一个全面的教程和一个直接的示例,剥去 DCOM 的神秘面纱,消除头痛和困惑。好吧,不保证——但我会尽力而为。在本系列文章中,我将向您展示如何使用以下技术:
- ATL COM AppWizard;
- Windows NT 服务的实现;
- MFC;
- ATL;
- 智能指针;
- 连接点;
- MFC 对连接点的支持;
- MFC ClassWizard 对实现连接点的支持(是的,是真的!)。
这些将用于开发一个示例客户端/服务器系统,向用户显示“来自 < 机器 > 的‘Hello, world!’”!我没听说有人需要进行客户端/服务器开发来问候“Hello, world!”,但这就是我要做的。
如果您想跟随本教程,添加代码并使用 Visual C++ 向导,那太棒了。事实上,我非常非常强烈推荐这样做,否则本教程将是一大堆电子墨水(?)。不过,我在编写教程的过程中,也会严格按照教程的步骤进行,并像您应该做的那样使用 Visual C++ 向导来开发代码。事实上,截图就是我为每个步骤开发文件时的截图!要下载这些已开发好的代码以与您自己的代码进行比较,只需点击每个步骤顶部的“下载第 n 步文件 - n KB”链接。在本教程的问答页面上,还有一个包含所有步骤文件的存档。我仍然建议您跟着我们一起学习;这样,您可以在编码的同时学习。
环境
本教程的步骤由作者在 Windows NT 4.0 SP 5 网络上使用 Visual C++ 6.0 SP 4 完成。作者没有在 Windows 2000 上测试过,因为他没有副本。如果本教程的任何一步在 Windows 2000 下不起作用或工作方式不同,请在留言板上留言。鉴于向后兼容性,几乎所有在 Windows NT 4.0 SP 5 下正常工作的代码,在 Windows 2000 下也应该能正常工作,除非微软进行了更改。
本教程假设您
我有一个座右铭:永远不要写那些对读者要求太多而对作者要求太少的东西。为此,我将直接告诉您本教程假设您知道什么。我还会包含一个“谁需要本教程”的部分,就像他们包含“谁需要阅读本书”一样,但我会让您自己判断是否需要某个东西。
本教程假设您
- 知道如何使用基于 Windows 的计算机;
- 知道如何使用 AppWizard;
- 熟悉基本的 MFC 编程;
- 听说过 ATL、COM 和 DCOM;
- 知道 Windows NT 和 Windows 2000 下的服务是如何工作的;
- 并且您正在 Windows NT 4.0 或更高版本上开发所有这些。
我希望我没有假设太多。我将以与微软指导我们完成 Scribble 教程大致相同的水平或稍高的水平来指导您。也就是说,我不会过度照顾您,但也不会让您感到困惑(至少,我不认为微软的解释很令人困惑……)。
我欢迎您就我的解释方式提出任何意见;可以对我大喊大叫或赞美我(我都能接受),**请在这一步底部的留言板上发言。** 相信我,我一定会看到您的帖子。**这样,大家就可以看到您的问题和我的回答。**
使用的约定
由于我们将引用类名、符号名等,最好遵循一套约定,以使一切保持一致。我们这样做:
代码片段
代码片段将出现在页面上独立的黄色块中。您需要添加的新代码行将显示为**粗体**。如果您不需要添加任何内容,但只是为了我的说明而指出某段代码,那么它将不会显示为**粗体**。
void CClass::Func() { // NOTE: Add this code CAnotherClass class; // And this code class.AnotherFunc(); // And this code // Done adding code }
可以吗?好的。如果某个函数长达一千行,而我们只对函数开头的几行感兴趣,那该怎么办?那时我将使用省略号(...),就像 Visual C++ 文档那样。这仅仅意味着那里有一大堆代码是我们将忽略的。
void CClass::LongFunc() { ... HRESULT hResult = S_OK; CClass class; hResult = class.Func(); ... }
变量名
我承认,我是微软的匈牙利命名法的大粉丝。这也许是我的一个缺点,但我一直学习 Scribble 教程,直到它涉及到所有的 OLE 东西。这不幸地将 MS 匈牙利命名法深深印在了我的脑海里。我坚决支持使用 `m_` 来表示成员变量,`C` 来表示类,`I` 来表示接口,以及 `D` 来表示 dispinterfaces,或者我们用来触发连接点事件的那些东西。
m_szDisplayName
CHelloWorld
IHelloWorld
DHelloWorldEvents
而不是_IHelloWorldEvents
函数
当我引用函数、方法或变量时,我将使用固定宽度的字体显示。每当我引用编译器关键字时,我也会使用固定宽度的字体。无论谁使用符号名,都将使用固定宽度的字体。Chris 会到处对我们这些作家指手画脚,确保我们遵守这些约定。(开玩笑,哈哈哈哈……)这里有一些例子:
Function()
THIS_IS_A_SYMBOL
ISayHello::SayHello()
CServiceModule
[IDL_attribute]
__stdcall
和typedef
END_OBJECT_MAP()
致谢和归属
我想花一点时间感谢一些为此做出贡献的来源和人员,因为没有他们的工作和贡献,我可能仍然处于黑暗之中。我特别想感谢 Richard Grimes 博士,他写了优秀的著作《Professional DCOM Programming》。Grimes 博士是 DCOM 和 COM 编程领域一位知识渊博的权威,他擅长用易于理解的方式解释事物。
《Professional DCOM Programming》由Wrox Press出版,非常彻底地涵盖了 DCOM,并进行了深入的讨论、实际示例,并揭示了真正复杂的东西。我指的是线程、安全、IDL、封送处理以及 Microsoft Transaction Server 等。我强烈建议您购买这本书(售价 49.95 美元),您不必读过它就能理解本教程。
此外,我还想感谢《MSDN Magazine》(前身为 MSJ)上各种文章和栏目的贡献者,他们的作品在 MSDN 库中重印,帮助我度过了 DCOM 的丛林。这包括 George Shepard 和 Don Box 的专栏,他们都是非常有知识的 COM 专家。还要感谢 Charles Steinhardt 和 Tony Smith,他们是为 Visual C++ Developer 撰稿的两位作者。
方法
我们将一步一步进行,遵循微软用于编写 Scribble 教程解释的方法。在本教程中,您将开发
- 一个 DCOM 服务器,实现为 Windows NT 服务。该服务器将公开一个 '
SayHello()
' 方法,该方法将通过连接点向客户端用户打招呼。 - 一个 MFC 客户端,支持智能指针和连接点,使其轻松!我们甚至将使用 ClassWizard 来实现连接点处理(!)。
在开始编写任何软件之前,最好先设计一下(一点点!)它将做什么!本教程中我们将开发的内容将按照下面**图 1**所示的方式工作。
**图 1。** 事物将如何运作。
如**图 1**所示,我们的软件将遵循一个简单的模式:
- 客户端将调用一个 `SayHello()` 方法;
- 服务器将通过网络触发一个事件来向客户端打招呼;
- 客户端将显示一个消息作为对该事件的响应,以表明服务器确实说了“Hello!”
够了,别啰嗦了;开始写代码吧!!
是的,是的;我明白了。我们将在本文中从教程的第一步开始,然后您可以简单地点击此页面底部的“下一页”按钮进入教程的下一步。
- 第 1 步:使用 ATL COM AppWizard 创建服务器
HelloServ
。 - 第 2 步:修改 AppWizard 提供的启动文件。
- 第 3 步:使用 New ATL Object Wizard 向服务器添加一个简单的 COM 对象,即
HelloWorld
对象。 - 第 4 步:修改
IHelloWorld
接口以包含SayHello()
方法。 - 第 5 步:向连接点源接口
DHelloWorldEvents
添加一个事件方法OnSayHello()
。 - 第 6 步:构建服务器,并在服务器计算机上安装它。
- 第 7 步:创建一个 MFC 客户端
HelloCli
,该客户端调用服务器并处理连接点事件接收器。
完成上述步骤后,我们将拥有一个活跃的、功能齐全的 DCOM 应用程序。在本教程的每个步骤页面底部都有“上一步”和“下一步”链接,您可以使用它们导航到上一步或下一步。还有一个链接可以带您到“问答”页面。
问答页面为我提供了更多空间,并且在我回答读者问题时可以显示截图。请随时在每个步骤底部的 Code Project 留言板上提问。如果某个问题被频繁问到,或者答案不那么简单,我会看到并将其添加到问答页面。这样,答案就可以帮助 Code Project 的所有读者。我会在留言板上回复该问题,并通过电子邮件通知作者说“我在问答页面上回答了这个问题!”
我真的必须少说废话了。有些人喜欢说话来听自己说话;我想,也许我喜欢写作来阅读自己的文字……但总之,够了。让我们开始第一步吧。
第一步:使用 ATL COM AppWizard 创建骨架 `HelloServ` 服务器
好了;开始吧。关闭 Visual C++ 中您正在处理的所有内容并保存更改。然后关闭任何打开的工作区。接下来,单击“文件”菜单,然后单击“新建”。这将弹出“新建”对话框,如下图**图 2**所示。在列表中单击 ATL COM AppWizard,然后为“项目名称”键入 'HelloServ
'。
**图 2。** 在“新建”对话框中选择 ATL COM AppWizard。
当设置符合您的要求时,单击“确定”。这将显示 ATL COM AppWizard 的第一步(它只有一步)。如下图**图 3**所示,其中选择了“服务”选项按钮。没关系;由于 AppWizard 不允许我们设置其他任何内容,因此接下来的事情就是单击“完成”。
**图 3。** 选择我们要让 AppWizard 提供一个服务。
为什么选择服务?
Windows 服务是一个很棒的进程类型,可以用作您的 ATL/COM 服务器。根据我的经验,Windows 服务在 DCOM 服务器方面表现更好。服务可以在机器未登录时运行,而大多数 EXE 程序可能无法运行。此外,服务是完全非交互式的,这对于所有您希望组件执行的例行系统任务(例如读取文件、运行程序或提供监视服务)来说都很好。您不希望在您在爱尔兰的房间里运行客户端时,在印度有一个服务器计算机弹出窗口。此外,服务还可以使用控制面板进行启动、停止和跟踪。
那么,为什么选择 EXE 而不是 DLL?
关于独立 EXE 的内容,请参见上文。DLL 作为远程服务器的实用性不高,因为所有 DLL 都必须映射到服务器系统上某个 EXE 进程的地址空间。一个特殊的 EXE 进程,它将 DCOM 服务器 DLL 映射到其地址空间,然后远程化该 DLL 的组件,被称为代理服务器。DLL 和代理服务器在需要远程访问您的组件时,是相当复杂的东西,难以维护和配置。尤其是因为它们不受系统引用计数或监视,以防客户端数量降至零,或者代理服务器挂起,让您束手无策。所以,服务是我的首选。
收尾
单击“完成”后,AppWizard 会显示“新建项目信息”对话框,如下图**图 4**所示。新建项目信息框只有少量信息;它只是告诉我们 AppWizard 将为我们提供一个全新的 Windows NT 服务(同时也是 DCOM 服务器)的起点。但是,此服务还没有任何 COM 对象。我们将在第三步添加这些。
**图 4。** 新建项目信息对话框。
单击“确定”让 AppWizard 生成 `HelloServ` 启动程序。现在,我们可以继续下一步,修改源文件。我们将在本教程的下一步,即第二步,开始修改。单击下面的“下一页”链接以继续第二步。