c# - 通过 COM 将对象从 C++ 传递到 C#

标签 c# interop variant

您好,在 C# 中有一个 COM 可见 API,如下所示:

public void DoSomething(string par1, string par2, object additionalParameter)

我的想法是,基于字符串参数的值,我期望一个不同的类作为第三个参数,并在实现中适本地转换它(我知道这种设计不是最优的,但我在这里没有太多的灵 active )。

假设对于字符串参数的某种组合,附加参数的类型如下:

[ComVisible(true)]
[Serializable]
public class TestObject    
{
    public string String{ get; set; }

    public long Long { get; set; }
}

我需要从一些非托管代码获取我的 API 方法;但是我在创建第三个参数所需的适当 variant 时遇到了困难。

我正在使用 CComVariant(...) 传递指向我刚刚构建的 TestObject 的 IDispatch

假设 pTestObject 是指向我的 TestObject 的 IDispatch 指针,我有如下内容:

CComVariant pObjectVariant(pTestObject);
DoSomething(BSTR(L"first"), BSTR(L"second"), pObjectVariant);

但是,当最终调用 C# 函数时,我看到该对象的类型为 bool 而不是我期望的 TestObject

有什么想法吗?

斯特凡诺

最佳答案

我有几个建议。首先,为您在 COM 中接触的任何内容创建一个接口(interface),即使它只是一个没有方法而只有属性的沼泽标准 DTO。 COM 喜欢接口(interface)。它非常喜欢它们,以至于您在 COM 中触摸的一切都是一个界面。

另一个建议是将 GuidAttribute 放置在您在 COM 中触摸的任何对象上。这将确保您的注册表在您向 COM 注册托管程序集时不会崩溃。 COM 喜欢 GUID 胜过喜欢接口(interface),如果一个接口(interface)注册到多个 GUID,它很容易混淆,如果您不在代码中硬修复接口(interface) GUID,就会发生这种情况。具体类还需要 GUIDAttribute。

我知道这很糟糕,但这就是 MS 如此努力地让人们远离使用 COM 的原因。

话虽这么说,您可能想要这样的 C#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ClassLibrary1
{

    [ComVisible(true)]
    [Guid("1CEB24F2-BF13-417F-B5BC-DB8E82A56EAE")]
    public interface ITestEntity1 //This is how TestEntity1 is visible to the COM code...so it needs a Guid.
    {
        bool Thing { get; set; }
    }


    [ComVisible(true)]
    [Guid("C8B5A7C2-F67C-4271-A762-3642754F2233")]
    public class TestEntity1 : ITestEntity1  //Created by the COM runtime...needs a Guid.
    {
        public bool Thing { get; set; }
    }

    [ComVisible(true)]
    [Guid("8904A7EC-D865-4533-91EC-1F68524651D0")]
    public interface ITestEntity2
    {
        string Description { get; set; }
    }

    [ComVisible(true)]
    [Guid("668EE2E8-5A60-468B-8689-D9327090AA44")]
    public class TestEntity2 : ITestEntity2
    {
        public string Description { get; set; }
    }

    [ComVisible(true)]
    [Guid("2791082F-F505-49C4-8952-80C174E4FE96")]
    public interface ITestGateway
    {
        //MarshalAsAttribute is somewhat important, it tells the tlbexp.exe tool to mark
        // the comInputValue parameter as IUnknown* in the COM interface.
        //This is good because VARIANTS kinda suck...You'll see what I mean in the C++
        // side.  It also keeps some jack-wagon from passing a VARIANT_BOOL in
        // on your object parameter.
        void DoSomething(string a, [MarshalAs(UnmanagedType.Interface)]object comInputValue);
    }

    [ComVisible(true)]
    [Guid("C3D079F3-7869-4B3E-A742-263775C6EA63")]
    public class TestGateway : ITestGateway
    {
        public void DoSomething(string a, object comInputValue)
        {
            if (a == "yes")
            {
                var entity = (TestEntity1)comInputValue;
            }
            else
            {
                var entity = (TestEntity2) comInputValue;
            }
            //OR

            if(comInputValue is TestEntity1)
            {
                //Do whatever here, and you don't need to test
                // a string input value.
            }
            else if(comInputValue is TestEntity2)
            {
                //Other stuff is done here.
            }
            else
            {
                //Error condition??
            }
        }
    }
}

可以被下面的C++调用:

// ComClient.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#import <mscorlib.tlb> raw_interfaces_only

//This creates the CLSID_ and IID_ constants, and
// some strongly-typed interfaces.
#import "..\Debug\ClassLibrary1.tlb" no_namespace named_guids


int _tmain(int argc, _TCHAR* argv[])
{
    ITestGateway* test = NULL;

    char buffer[50];
    gets(buffer);  //Just a pause to attach the debugger in Managed + Native mode...hit enter in the console.

    CoInitialize(NULL);

    HRESULT hr = CoCreateInstance(CLSID_TestGateway,
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_ITestGateway,
        reinterpret_cast<void**>(&test));

    if(FAILED(hr)) {
        printf("Couldn't create the instance!... 0x%x\n", hr);
        gets(buffer);
    } else {
        _bstr_t someString("yes");

        //Instead of fooling with CComVariant,
        // just directly create a TestEntity1...which COM will return
        // as an ITestEntity1.
        ITestEntity1* testEntity1 = NULL;
        HRESULT hr = CoCreateInstance(CLSID_TestEntity1,
            NULL,
            CLSCTX_INPROC_SERVER,
            IID_ITestEntity1,
            reinterpret_cast<void**>(&testEntity1));

        if(FAILED(hr)) {
            printf("Entity was not created!... 0x%x\n", hr);
            gets(buffer);
            return 0;
        }

        //Set some kind of property just for show.
        testEntity1->PutThing(VARIANT_FALSE);



        //If you attached your debugger with Managed code & Native code,
        // you should be able to hit a C# break point during this call.
        //Also, notice that there is no cast for testEntity1.  All interfaces
        // in COM derive from IUnknown, so you can just pass it.
        //IDispatch also derives from IUnknown, so if that's what you already have,
        // you can just pass it as well, with no cast.
        test->DoSomething(someString, testEntity1);

        printf("Something was done.");

        testEntity1->Release(); //Release anything you make through CoCreateInstance()
    }

    test->Release();

    return 0;
}

关于c# - 通过 COM 将对象从 C++ 传递到 C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3364624/

相关文章:

c# - 没有 powershell 的 UWP 安装程序

c# - 我需要什么来进一步限定 DataContext 以进行绑定(bind)?

c# - 如何在 C# 中使用 [DllImport ("")]?

c++ - noexcept 访问 std::variant

delphi - 将<T>类型的值转换为Variant,可能吗?

c# - xamarin 形成清除选择器选择

excel - 通过互操作设置 Excel 单元的功能 - 本地化

.net - 使用 Interop 中的 Word 2007 拼写检查时,某些语言无法工作

interop - 为什么非托管结构不能成为托管类的成员?

C++17 构建两侧具有变体的运算符的最佳方法?