可以在C#中实现此COM接口吗?

问题描述

COM组件当前以C ++实现,而下一版本必须以C#实现。该组件从C ++(不是CLI)代码调用。这个很小的组件的主要部分已经移植到C#中,但是我很难弄清楚如何翻译一种特定的方法

接口定义

IDL中使用了以下导入:

import "oaidl.idl";
import "ocidl.idl";

接口的主要部分已经转换为相应的C#属性,接口和类。大部分都能正常工作。

但是接口成员之一在IDL中定义为

    [
        object,uuid(<guid>),helpstring("Help"),pointer_default(unique)
    ]
    interface IStuff : IUnkNown
    {
...
        HRESULT GetShortText([in] int i,[in] BOOL b,[out,string] TCHAR shortText[10]);
...
    }

用法

要使用该接口,将传递本地TCHAR[10]数组作为该数组的名称(因此称为TCHAR*)。 COM服务器必须在TCHAR数组中放入一个短字符串。

问题

我无法调用方法。永远不会命中GetShortText方法中在C#COM服务器中设置的断点。 COM根本无法在.NET中找到我的实现。

如何用C#正确实现以固定大小TCHAR*调用的此方法

解决方法

这种接口可以在 .Net 中通过将参数指定为 IntPtr 并手动编组字符串来实现。完整的演示可以是found on Github

示例实现:

[ComVisible(true),Guid("E559D616-4C46-4434-9DF7-E9D7C91F3BA5"),InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IStuff
{
    void GetShortTest(int i,[MarshalAs(UnmanagedType.Bool)] bool b,IntPtr shortText);
}

[ComVisible(true),ProgId("InProcTargetTest.Class1"),Guid("BA5088D4-7F6A-4C76-983C-EC7F1BA51CAA"),ClassInterface(ClassInterfaceType.None)]
public class Class1 : IStuff
{
    public void GetShortTest(int i,bool b,IntPtr shortText)
    {
        var data = Encoding.Unicode.GetBytes("Hello");
        Marshal.Copy(data,shortText,data.Length);
    }
}

示例调用者:

if (FAILED(CoInitialize(nullptr))) {
    std::cout << "Failed to initialize COM\n";
}

IStuff *pStuff = nullptr;
CLSID clsClass1 = { 0 };
if (FAILED(CLSIDFromProgID(L"InProcTargetTest.Class1",&clsClass1))
    || FAILED(CoCreateInstance(clsClass1,nullptr,CLSCTX_INPROC_SERVER,IID_IStuff,(LPVOID*)&pStuff))) {
    std::cout << "Failed to create COM instance\n";
}

TCHAR test[10] = { 0 };
pStuff->GetShortTest(5,true,test);

std::cout << "From C#: " << test << "\n";

IDL 示例:

import "unknwn.idl";

[
    odl,uuid(E559D616-4C46-4434-9DF7-E9D7C91F3BA5),version(1.0),pointer_default(unique),custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9,"InProcTargetTest.IStuff")

]
interface IStuff : IUnknown {
    HRESULT _stdcall GetShortTest(
        [in] long i,[in] BOOL b,[out,string] TCHAR shortText[10]);
};
,

对于这种相当不寻常的结构,我不知道有任何标准的编组方法。但是,您可以执行以下操作:

void GetShortText(int i,[MarshalAs:UnmanagedType.Bool] bool b,IntPtr shortTextPtr)
{
    string s = "Test";
    byte[] buffer;
    if (UseUnicode)
        buffer = Encoding.Unicode.GetBytes(s + '\0');
    else
        buffer = Encoding.Default.GetBytes(s + '\0');
    Marshal.Copy(buffer,shortTextPtr,buffer.Length);
}

注意:

  • 对缓冲区进行范围检查。应添加长度(对于ANSI,
  • + '\0'用于对返回的字符串进行空终止