DCOM 揭秘 .NET 2003:DCOM 教程,第 1 步






4.56/5 (11投票s)
本教程将引导您逐步了解如何使用 Visual Studio .NET 2003 开发 DCOM 客户端/服务器应用程序。
引言
欢迎来到本教程。您可能有一个客户端/服务器项目或应用程序,并且已决定使用 DCOM 来实现功能。因此,您打开 Visual Studio .NET 2003 并选择启动一个新项目。为了开发 DCOM 软件,您会选择哪种类型的项目?Server EXE 还是 Windows Service?哪个向导会在哪里添加代码?您又能在哪里填写应用程序的实现?
我在这里不是要告诉您 DCOM 是什么以及它是如何工作的底层理论。The Code Project 上有几篇非常棒的文章可以帮助您在这方面有所了解。
我们在本文中将使用和探讨的一些 VS.NET 功能 - 以及 Windows 系统编程概念 - 是
- ATL 项目向导
- Windows 服务的实现
- MFC
- ATL
- 智能指针
- 连接点
- MFC 对连接点的支持
- MFC 对实现连接点的支持
我们将使用这些来开发一个示例客户端/服务器系统,向用户显示“Hello, world from < machine >”。正如基本的“Hello, World!”示例可以阐明开发各种应用程序类型的起点一样,此示例也将引导您完成最基本的 DCOM 客户端/服务器系统的开发。
撰写本教程系列的目的是详细记录在开发 DCOM 客户端/服务器系统时所遵循的确切步骤以及 Visual Studio .NET 2003 的哪些功能会发挥作用。需要采取一系列的操作;在某些情况下,遗漏某个操作或按错误的顺序执行步骤可能导致不正确的结果。
请跟随操作或参考示例代码
可以通过两种并行的方式来学习本教程。第一种也是推荐的方式是打开 Visual Studio .NET 2003,并在屏幕上参考本教程,同时进行开发(或者您可以随意打印教程页面),并按照教程中的步骤在 Visual Studio .NET 2003 环境中边阅读边完成。
学习本教程的另一种方式是参考示例代码,可以通过每个教程步骤顶部的下载链接访问(例如,本页面顶部的下载链接)。但是,最好只在完成本教程中的步骤时使用示例代码来检查您的工作。
根据我过去对本系列的前几个版本的经验,那些直接跳到教程最后的人往往会遇到最大的困难,因为他们不了解开发过程中各个阶段的相互作用以及最终结果如何契合。
环境
因此,用于制作本教程的开发环境是
- 操作系统:Microsoft Windows XP Professional, Service Pack 2
- 开发环境:Microsoft Visual Studio .NET 2003
推荐的操作是在一台机器上完成所有开发,并且 - 出于生产目的 - 使用两台网络连接的计算机(一台作为“服务器”,另一台作为“客户端”)来执行最终结果。其中一台计算机可以是你同样用于开发的机器;在这种情况下,建议你将其设置为“服务器”。建议两台计算机都安装 Windows,并且“服务器”计算机应安装更新更高级的 Windows 版本。在开始之前,请花点时间记下你测试计算机的计算机名或网络地址。
本教程的假设
我有一句座右铭:永远不要写任何对读者要求过高而对自己要求不足的东西。为此,我将直接告诉你本教程假设你知道什么。我还会包含一个“谁需要本教程”部分,就像他们包含“谁需要阅读本书”一样,但我会让你自己判断是否需要。
本教程假设您
- 知道如何使用基于 Windows 的计算机;
- 知道如何使用 Visual Studio .NET 2003(尽管只需要初学者级别的知识);
- 熟悉基本的 MFC 编程;
- 熟悉 COM 概念,并认识到 ATL 是实现 COM 程序和组件的有用工具;
- 知道 Windows 2000 和 XP 下的服务是如何工作的;
- 并且您正在 Windows 2000 和 XP 或更高版本上开发所有这些。
本教程被指定为初学者级别的教程;即,COM/DCOM 使用初学者。如果您有任何疑问,请在每个步骤文章下方的消息板上提问。请尽量将您的问题发布到相应步骤的消息板上。我也会收到您的问题的电子邮件。请勿提一般性的 COM/DCOM 编程问题。
使用的约定
本教程使用的约定如下。
代码片段
代码片段将显示在页面上各自的黄色块中,并使用等宽字体。我要求您添加的代码行将显示为粗体,如下所示
void CClass::Func()
{
// NOTE: Add this code
CAnotherClass class; // And this code
class.AnotherFunc(); // And this code
// Done adding code
}
如果代码片段相当长,代码列表中将使用省略号(...)代替省略的代码,以保持简洁。例如
void CClass::LongFunc()
{
...
HRESULT hResult = S_OK;
CClass class;
hResult = class.Func();
...
}
变量名
在本教程中,我采用了 Microsoft 的变量名和命名约定,包括匈牙利命名法。属于类的变量前缀为“m_”,例如 m_szDisplayName
。所有用户定义类型的名称都以首字母大写开头,类名以“C”为前缀,接口名以“I”为前缀,事件源接口(所谓的 dispinterfaces)的名称以“D”为前缀,而不是 Microsoft 的约定“_I”。例如
class CHelloWorld
interface IHelloWorld
dispinterface DHelloWorldEvents
而不是_IHelloWorldEvents
函数
文本中的函数名、方法名、变量、符号和编译器关键字都将显示为固定宽度的字体。以下是一些示例
Function()
THIS_IS_A_SYMBOL
ISayHello::SayHello()
CServiceModule
[IDL_attribute]
__stdcall
和typedef
END_OBJECT_MAP()
客户端/服务器系统设计
我们将逐步进行。在本教程中,您将开发
- 一个 DCOM 服务器,实现为一个 Windows 服务。此服务器将公开一个‘
SayHello()
’方法,该方法将通过连接点向客户端用户打招呼。 - 一个 MFC 客户端,支持智能指针和连接点。我们甚至会使用类向导(VS.NET 版本)来实现连接点处理(!)。
在本教程中我们将开发的软件设计如下面图 1 所示
正如您在图 1 中所见,我们的软件将遵循一个简单的模式
- 客户端将调用一个
SayHello()
方法。 - 服务器将通过网络触发一个事件来向客户端打招呼。
- 客户端将向用户显示一条消息以响应此事件,表明服务器确实打了“Hello!”。
教程步骤
我们将在本文中从教程的第 1 步开始,然后您可以通过单击页面底部的下一步 >> 链接继续进行教程的下一步。
- 第 1 步:使用 ATL 项目向导创建服务器,HelloWorldServ.NET。
- 第 2 步:修改 ATL 项目向导提供的启动文件。
- 第 3 步:在服务器中添加一个简单的 ATL 对象
HelloWorld
,以公开我们的功能。 - 第 4 步:在服务器中添加一个方法
SayHello()
,该方法触发客户端处理的事件。 - 第 5 步:我们查看连接点并设置服务器端的连接点。
- 更多步骤即将推出 - 本教程系列仍在完成中。
完成上述步骤后 - 包括即将推出的步骤,我们将拥有一个与图 1 中设计相符的可工作系统。我们从第 1 步开始教程系列,在这一步中,我们将使用 Visual Studio .NET 2003 中的 ATL 项目向导为服务器项目生成骨架代码和启动文件,该项目我们将命名为HellWorldServ.NET。
第 1 步:使用 ATL 项目向导创建骨架 HelloWorldServ.NET 服务器
关闭 Visual C++ .NET 中所有正在进行的工作并保存更改。然后关闭所有打开的解决方案。与 VS6 不同,没有单个“文件 > 新建”命令可供选择来启动一个新的 __________。相反,如下图 2 所示,单击“文件”菜单,指向“新建”,然后单击“项目”
该命令会打开“新建项目”对话框,如下面的图 3 所示。打开“Visual C++ 项目”文件夹,然后打开“ATL”文件夹,然后单击“ATL 项目”。请注意,在“新建项目”对话框的右侧窗格中,选项有 ATL 项目、ATL 服务器项目和 ATL 服务器 Web 服务。以下是这三个选项生成的项目类型
- ATL 项目:一个传统的 ATL/COM 服务器项目,就像使用 VC6 的 ATL COM AppWizard 获得的一样。这个选择是我们想要的。
- ATL 服务器项目:一个托管在 Web 服务器上的 Web 应用程序项目。
- ATL 服务器 Web 服务:一个 XML Web 服务,它还公开 COM 对象的功能。有关更多信息,请参阅未来的教程。
我为该项目输入了名称“HelloWorldServ.NET”;但是,您可以随意为项目指定任何您喜欢的名称(以及计算机上的文件夹)。下图显示了我填写的对话框。请注意对话框左下角的更多按钮。单击“更多”按钮以显示其他设置。我们实际上想将客户端和服务器都放在同一个工作区中,或者用 Visual Studio .NET 的说法,同一个解决方案中。一个解决方案可以托管多个项目,就像 VC6 中的工作区一样。由于我们将在本教程中同时创建一个服务器和客户端,因此我们最好为解决方案和项目指定不同的名称,并让解决方案拥有自己的目录,如下面的图 4 所示。
如您所见,我将解决方案命名为“HelloWorld.NET”,并要求 Visual Studio .NET 为解决方案创建一个专用目录。请注意,我随后将项目名称更改为“HelloWorldServ.NET”,因为这是使用 Visual Studio .NET 创建的项目,这是一个“Hello, World!”示例,并且我们正在创建应用程序的服务器端。一旦满意,请单击“确定”以继续。这将显示 ATL 项目向导,如下面的图 5 所示。向导打开时,将选择“概述”选项卡。要更改向导的设置,请单击“应用程序设置”选项卡,并选择如显示所示的设置
请注意,我在上面未选中“属性”复选框。属性到底是什么?嗯,正如文档解释的那样,从 Visual C++ .NET 开始,编译器会识别源文件中属性的存在,并在编译过程中动态解析和验证它们。因此,属性可以帮助您指定程序的特殊元素,而无需编写以前与之相关的代码和声明。
但是,我们在这里不使用属性,因为这是一个教程,而如果您在进行教程时隐藏内容,那又有什么意义呢?所以,请将“属性”框保持未选中状态,如上图 5 所示。
为什么是服务?
Windows 服务是一种很棒的进程类型,可以用作您的 ATL/COM 服务器。根据我的经验,Windows 服务作为 DCOM 服务器的性能更好。服务可以在计算机未登录的情况下运行,而大多数 EXE 程序可能无法运行。此外,服务完全是非交互式的,这对于您只想让组件执行常规系统任务(如读取文件、运行程序或提供监控服务)的情况来说是很好的。您不希望 Windows 在您在印度使用客户端时,在爱尔兰的一台服务器计算机上随意弹出窗口。此外,服务可以使用控制面板启动、停止和跟踪。
同样,为什么选择 EXE 而不是 DLL?
关于独立 EXE,请参阅上文。DLL 作为远程服务器不太有用,因为所有 DLL 都*必须*映射到服务器系统上某个 EXE 进程的地址空间。一个将 DCOM 服务器 DLL 映射到其地址空间,然后远程访问该 DLL 组件的特殊 EXE 进程称为*代理服务器*。DLL 和代理服务器在需要远程访问组件时,通常是维护和配置的复杂工具。尤其是,如果客户端数量降至零或者代理服务器卡死,系统不会对其进行引用计数或监视,这可能会让您陷入困境。因此,服务是我的首选。
收尾
要查看我们的设置,请单击“概述”选项卡。显示的设置应与图 6 中所示的相似。如果不同,请返回。单击“完成”以让 ATL 项目向导生成HelloWorldServ.NET 启动程序。如果一切顺利,Visual Studio 解决方案资源管理器窗口应显示类似于下图 7 中所示的内容。
现在我们已准备好进行下一步,修改源文件。我们将在第 2 步开始修改,这是本教程的下一步。单击下面的下一步 >> 链接继续进行第 2 步。单击问答(即将推出!)转到教程的 FAQ 和源代码下载存档。
提示:如果您遇到麻烦或不理解某些内容,通常是因为您在教程中进展太快而没有彻底遵循,并且下载了已完成的最新步骤的代码。也许回到前面的步骤,并彻底完成那些不清楚的地方,可能会有帮助。另外,也可能是因为还有更多步骤需要编写!敬请关注!
提示:另外,如果您有疑问,请直接在下面的消息板上提问,该消息板位于本文页面底部。当我收到提问时,我会收到电子邮件,然后大家都可以看到您的问题和我的回答。别忘了给这篇文章评分!如果您给的分数低于 5,请在消息板上说明原因,以便我能为所有人改进这些文章。
致谢
感谢 Dr. Richard Grimes,他的书《Professional DCOM Programming》(1998 年,Wrox Press)是关于 DCOM 的宝贵技术参考资料和资料来源。要理解或完成本教程系列,阅读此书并非必需。
我想感谢《MSDN Magazine》(前身为 Microsoft Systems Journal,或 MSJ)各种文章和专栏的贡献者,他们的作品被转载在 MSDN 库中,帮助我穿越了 DCOM 的丛林。这包括 George Shepard 和 Don Box 的专栏,他们是两位非常有知识的 COM 专家。还要感谢 Charles Steinhardt 和 Tony Smith,这两位作者曾为 Pinnacle publishing 的《Visual C++ .NET Developer》撰稿。