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

使用托管C++进行COM互操作

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.56/5 (9投票s)

2001年5月20日

4分钟阅读

viewsIcon

125631

downloadIcon

1390

演示 COM 互操作,展示了对 COM 组件的早期绑定和晚期绑定

摘要

此示例演示了使用托管 C++ 进行 COM 互操作。演示了早期绑定和晚期绑定,并使用 TlbImp 工具从非托管 COM 对象导入类型库

引言。

.NET 的设计目标之一是与 COM 客户端和服务器实现 100% 的兼容性。

这对开发人员来说是个好消息,因为许多公司拥有大量基于 COM 技术构建的软件。迁移到 .NET 仍然允许您重用所有 COM 对象而无需修改它们。

.NET 互操作支持早期绑定和晚期绑定。

1. 早期绑定。

使用早期绑定,您可以在编译时导入 COM 类的完整类型信息。编译器从为 COM 类生成的元数据程序集中获取此信息。创建 COM 类元数据程序集的最简单方法是使用一个名为 TlbImp.exe 的实用工具。

TlbImp 具有以下命令行语法(某些选项未显示,有关完整语法请参阅 MSDN)

TlbImp TlbFile /out: file 
  • TlbFile 可以是包含 COM 类型库的任何文件的名称。它可以是 .tlb、.dll、.odl、.ocx、.exe 文件或任何其他格式的类型库嵌入为资源的文件。
  • /out: file - 将把元数据定义写入的输出文件的名称。

此演示使用 TimeServer.dll ATL 服务器中的 DClock COM 对象(不要忘记使用 regsvr32.exe 实用工具注册 dll)。此类只有一个方法 GetTime,该方法接受一个类型为 short 的参数 bDate 并返回一个包含当前时间的字符串。时间格式为:hh:mm:ss。如果 bDate 不为 0,则返回的字符串还包含日期信息,格式为:yy/mm/dd hh/mm/ss。

要为 DClock 类创建元数据程序集,请使用以下命令行

tlbimp.exe timeserver.dll /out:timeserverlib.dll

在我们的示例中,我们使用托管 C++ 类作为客户端。要创建客户端,您可以使用 Visual Studio AppWizard。选择“C++ managed Console application”作为目标,我将项目命名为 DClockClient,因此主源文件名为 DClockClient.cpp

使用 COM 对象的客户端应导入元数据。在 C++ 中,您应该使用 #using 指令。语法是

#using "filename"

其中 filename 是元数据程序集的名称。

元数据在编译时和运行时都必须可访问,因此为了简单起见,请将 timeserverlib.dll 放在客户端代码所在的文件夹中,以及 debug 和 release 子文件夹中。作为替代方案,您可以将程序集放在客户端可执行文件所在的文件夹中,并使用相对路径从源引用它。

将以下行添加到 DClockClient.cpp 文件中

#using "timeserverlib.dll"

运行时,程序会找到元数据程序集,利用此信息创建一个运行时可调用包装器 (RCW) 对象,该对象负责

  • 保持对象的身份
  • 维护对象的生命周期
  • 代理自定义接口
  • 封送方法调用
  • 消耗选定的接口

RCW 本身是一个托管对象,因此它会被垃圾回收。要导入库命名空间,您应该使用 using namespace 指令。将以下行添加到您的代码中

using namespace TimeServerLib;

现在,您可以使用该对象,就像使用任何托管的 CLR 类一样。

以下代码演示了此技术。

#using <mscorlib.dll>
#using "timeserverlib.dll"

using namespace System;
using namespace TimeServerLib;

int main(void)
{
    DClock *pClock;
    String *strTime;

    pClock = new DClock;
    strTime = pClock->GetTime(0);

    Console::WriteLine(strTime);
    
    return 0;
}

让我们创建一个托管 C++ 类来包装 COM 对象。从功能角度来看,此示例中的包装器类什么都不做,您可以通过直接在 main 函数中实例化 COM 对象来实现相同的结果。这只是展示了另一种使用 COM 对象的方式。

要创建托管 C++ 类来包装 COM 对象,请将以下代码添加到 DClockClient.cpp 文件中

__gc class CTimeEB
{
public:
    // Constructor.
    CTimeEB()
    {
        // Create instance of DClock COM object.
        // Realy we create Runtime Callable Wrapper (RCW)
        // which is responsible for DClock instaniating.
        pClock = new DClock;
    }

    ~CTimeEB()
    {
        // Our object is an instance of managed, means garbage collected
        // class, so it is not neccessary to call delete for our object.
        // It is just demonstrates that we can release its resources at
        // a well-defined point. 
        delete pClock;
    }

    // Returns string that has current time.
    // If bAddDate is not 0, it returns date and time,
    // if bAddDate is 0, returns time only.
    String* PrintCurrentTime(short bAddDate)
    {
        // Get current time from COM server and concatinate it
        // with predefined string.
            return String::Concat(S"Current time is ",  
                                  pClock->GetTime(bAddDate));
    }

private:
    DClock *pClock;
};

CTimeEB 是一个托管 C++ 类,因此在代码中使用它没有什么特别之处。

2. 晚期绑定。

晚期绑定是通过使用 Namespace Reflection 机制实现的。

要在运行时导入元数据,将使用 System 命名空间中的 Type 类。Type 类有一个静态方法 GetTypeFromProgID("ProgID"),该方法根据 COM 对象的 ProgID 返回一个 Type 对象。

要获取 COM 对象的实例,我们使用 System 命名空间中的 Activator 类的静态成员 CreateInstance(Type type)。我们将上一步获取的 Type 对象作为参数传递。

现在我们可以使用 Type 对象的 InvokeMethod 成员来调用 COM 对象的方法。

以下代码演示了此技术

#using <mscorlib.dll>
#using "timeserverlib.dll"

using namespace System;
using namespace TimeServerLib;

int main(void)
{
    Type *typ;
    Object *obj;
    Object* args[];
    String *strTime;

    using namespace Reflection;

    // Get Type object for COM server. It uses ProgID of DClock class.
    typ = Type::GetTypeFromProgID("TimeServer.DClock");

    // obtain instance of COM object.
    obj = Activator::CreateInstance(typ);

    // Create array of parameters.
    args = new Object*[1];


    // Set parameter.
    args[0] =__box(1);

    // Call COM object method by its name.
    strTime = (String*)typ->InvokeMember("GetTime", 
                           BindingFlags::InvokeMethod, 
                           Type::DefaultBinder, obj, args);  

    Console::WriteLine(strTime);

    return 0;
}

TimeLB 类演示了在托管 C++ 类中重用 COM 对象。

// Class to demonstrate late binding.
__gc class CTimeLB
{
public:
    // Constructor
    CTimeLB()
    {
        // Get Type object for COM server. It uses ProgID of DClock class.
        typ = Type::GetTypeFromProgID("TimeServer.DClock");

        // obtain instance of COM object.
        obj = Activator::CreateInstance(typ);

        // Create array of parameters.
        args = new Object*[1];
    }

    // Returns string that has current time.
    // If bAddDate is not 0, it returns date and time,
    // if bAddDate is 0, returns time only.
    String* PrintCurrentTime(short bAddDate)
    {
        String *strTime;

        // Set parameter.
        args[0] =__box(1);

        // Call COM object method by its name.
        strTime = (String*)typ->InvokeMember("GetTime",
                                             BindingFlags::InvokeMethod, 
                                             Type::DefaultBinder, obj, args);  

        // Concatinate current time with predefined string.
        return String::Concat(S"Current time is ", strTime);
    }

private:
    Type *typ;
    Object *obj;
    Object* args[];
};

历史

2001 年 10 月 18 日 - 更新为 .NET Beta 2

© . All rights reserved.