您好,在 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/