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

Visual Studio 2008 中的最简单 COM 教程

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.11/5 (37投票s)

2008年4月20日

CPOL

6分钟阅读

viewsIcon

128599

downloadIcon

1288

在 .NET 2008 环境下创建 COM/DCOM 服务器和客户端的入门指南。

1. 引言

一个月前,当我开始学习这种“过时”的 COM/DCOM 技术时,我还是个新手。我发现网上很难找到有用的教程。确实找到了一些教程,但我认为它们不是很好的学习资源,因为它们要么“太旧”,要么很难重现声称的结果。这些资源大多是在 Visual Studio 6 下开发的,它们通常侧重于 COM/DCOM 的一些深入技术细节,而不是为绝对的初学者编写的。

本教程有三个目标:第一,在最新的开发环境中开发 COM/DCOM 服务器/客户端;第二,使实现尽可能简单;第三,对实现过程进行详细描述。

2. 最简单的聊天服务器

本教程将创建一个简单的基于 COM 的聊天服务器,并使用一个控制台应用程序来消费服务器提供的服务。这是我实现过的最简单的服务器,因为

  1. 它是一个进程内服务器。它被加载到客户端的地址空间中,因此不需要代理/存根。代理/存根是 COM/DCOM 用来在不同地址空间或甚至不同主机上运行的服务器和客户端之间实现通信的工具。
  2. 服务器只允许一个客户端连接。
  3. 服务器只有一个接口,该接口只有一个方法,而该方法的功能是我在客户端/服务器环境中能想象到的最简单的。所谓的“最简单”,并不是指像“void FunctionName(void)”这样的函数,这种函数对于客户端/服务器架构来说毫无用处。如果客户端不向服务器发送任何东西,服务器也不向客户端发送任何东西,你怎么知道双向通信是否成功?所以该方法的形式如下:
SimplestMethod(client_send_something_to_server, server_send_something_to_client)

服务器实现需要四个步骤。

2.1 创建 ATL 项目

通过“ATL 项目”向导创建一个简单的外壳来容纳 COM 对象。要启动向导,请按照以下步骤操作:单击“文件”菜单,选择“新建”->“项目...”菜单项。此时将显示“新建项目”窗口。

我们选择“ATL 项目”,将解决方案名称输入为“SimpleCOM”,然后单击“确定”按钮打开“ATL 项目”向导。

向导中无需更改任何内容,因此我们只需单击“完成”按钮,让服务器代码即时生成。虽然我们不关心幕后细节,但有几点值得一提。

  1. 该向导创建了一个 DLL。
  2. DLL 在成功编译后将被注册到系统中。
  3. DLL 提供了 COM 对象正常运行所需的所有功能,这些功能对于所有 COM 对象都是通用的,因此它们足以满足这个简单的示例或其他更复杂的场景。

2.2 为服务器添加 COM 类

COM 类是从 COM 接口继承的类。COM 接口是继承自 IUnknown(直接或间接)的结构体(在 Visual C++ 中,结构体和类没有区别)。在此步骤中,COM 类和其继承的接口将同时生成。

要为服务器添加新的 COM 类,请打开刚刚创建项目的“类视图”,右键单击“SimpleCOM”项目,然后单击“添加”->“类...”以打开“新建类”向导。SimpleCOM 类是“新建 ATL”向导生成的两个类之一。另一个类 SimpleCOMPS 用于创建代理/存根,此处不讨论。

在下面的“新建类”向导中,我们选择“ATL”->“ATL 简单对象”并单击“添加”按钮。

此时将显示“ATL 简单对象”向导。

我们只需在“短名称”字段中键入“SimpleChatServer”,所有其他字段将自动填充。在这个简单的示例中,无需更改这些名称。这里需要提到的一点是:在窗口的下半部分,COM 部分,我们可以看到将创建一个 COM 类“SimpleChatServer”和一个接口“ISimpleChatServer”。

要继续向导,我们单击“下一步”按钮,此时将显示“选项”窗口。我们只需将“ATL 对象”的选项设置为最常用的选项,如下图所示。选项分为四类,每一类都值得用一整章来解释。由于本教程是为初学者准备的,目前我能告诉你的是,单击下一个屏幕上的“完成”按钮并关闭向导。

2.3 为接口添加方法

在项目的类视图中,展开“SimpleCOM”项目,右键单击“ISimpleChatServer”接口,然后单击“添加”->“添加方法...” 。

新方法命名为“CallMe”,其签名如下:

第一个参数“BSTR name”是客户端的名称,当客户端调用方法时会传递给服务器。另一个参数“BSTR** helloMessage”是返回给客户端的消息。该方法可以证明双工通信是否已实现。单击“完成”按钮返回到项目的类视图。

2.4 编写方法代码

在下面的类视图窗口中,选择上窗格中的 COM 类“CSimpleChatServer”,然后双击“CallMe”方法以打开 SimpleChatServer.cpp 文件。

SimpleChatServer.cpp 文件中,最终的 CallMe 方法如下。要包含类“CString”的定义,您需要在“SimpleChatServer.cpp”文件的开头包含 <atlstr.h> 头文件。

STDMETHODIMP CSimpleChatServer::CallMe(BSTR name, BSTR** helloMessage)
{
    // TODO: Add your implementation code here
    CString temp = _T("Hi ");
    temp += name;
    temp += ", welcome to the simple chat server!";
    BSTR str = temp.AllocSysString();
    *helloMessage = &str;
    return S_OK;
}

2.5 编译服务器

编译服务器就像按“F7”键或选择“生成”菜单并单击“生成解决方案”菜单项一样简单。Visual Studio 2008 将编译服务器并将 COM 类和接口注册到操作系统。因此,我们可以在注册表中找到相应的条目和 ClassID/InterfaceID。客户端将使用此信息来定位和使用服务器提供的服务,更具体地说,是 CallMe() 方法。

3. 最简单的客户端

我们将把客户端项目添加到创建服务器项目的解决方案中。为此,请打开“解决方案资源管理器”,右键单击解决方案“SimpleCOM”,然后选择“添加”->“新建项目...”。

我们向解决方案添加一个名为“ConsoleComClient”的新 Win32 控制台应用程序,然后单击“确定”。在接下来的窗口中,我们只需单击“完成”即可接受新项目的所有默认设置。

新的 Win32 控制台应用程序相当简单。最简单的客户端需要七个步骤。其代码已完全注释,因此我在此处仅提供代码。

/*
** This client shows the procedure of creating the simplest COM client.
** This client is a CONSOLE application and uses IN-PROCESS COM server.
** The server serves ONLY one client.
** Two-way communication is achieved in a synchronous manner by passing
** pointer to server so that server can pass something back to client.
*/
#include "stdafx.h"
/*
**                STEP 1
** including two files created by the server
** these files contain the information about ATL object and interfaces
** such as CLASSID and INTERFACEID.
*/
#include "../SimpleCOM/SimpleCOM_i.h"
#include "../SimpleCOM/SimpleCOM_i.c"

int _tmain(int argc, _TCHAR* argv[])
{
    BSTR* message;                //used to accept return value from server.
    HRESULT hr;                    //COM error code;
    ISimpleChatServer *chat;    //pointer to the interface
    /*
    **            STEP 2
    ** Initialize COM and check for success
    */
    hr = CoInitialize(0);
    if(SUCCEEDED(hr))
    {
        /*
        **        STEP 3
        ** Create an instance of the COM server object.
        ** The most important result of this function call
        ** is that we get a pointer to the server interface.
        ** The function takes 5 parameters
        ** param #1: class ID of server COM class
        ** param #2: outer interface(used in aggregation)
        ** param #3: class context.
        ** param #4: interface ID.
        ** param #5: pointer to the interface of interest.
        */
        hr = CoCreateInstance(
            CLSID_SimpleChatServer,
            NULL,
            CLSCTX_INPROC_SERVER,
            IID_ISimpleChatServer,
            (void**) &chat);
        if(SUCCEEDED(hr))
        {
            /*
            **    STEP 4
            ** call method via Interface ID.
            */  
            
            hr = chat -> CallMe(_T("Zhiwei"), &message);
            MessageBox(NULL,*message, _T("Message returned from chat server"),0);
            /*
            **  STEP 5
            ** de-allocation of BSTR to prevent memory leak
            ** string handling is big topic in COM
            */
            ::SysFreeString(*message);
            /*
            **    STEP 6
            ** Decrease server's object reference counter.
            ** this is a important step and has been done at client side,
            ** so that server knows that it can de-allocate an object.
            */
            hr = chat -> Release();
        }
    }
    /*
    **            STEP 7
    ** close COM.
    */
    CoUninitialize();

    return 0;
}

最后,您需要将“ConsoleComClient”设置为“启动项目”并进行编译。本教程的最后阶段是运行项目,您将看到一个弹出窗口显示欢迎消息。

© . All rights reserved.