【问题标题】:Can this COM interface be implemented in C#?这个COM接口可以用C#实现吗?
【发布时间】:2021-02-03 02:46:43
【问题描述】:

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 数组中放入一个短字符串。

问题

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

这个以固定大小TCHAR* 调用的方法如何在 C# 中正确实现?

【问题讨论】:

  • “短字符串”是什么意思? Short 通常是 int16。 TCHAR 也称为 WCHAR(W 表示宽),它是一个 unicode 字符串。所以我认为问题是从 UTF-8 编码字符串到 unicode 字符串。在 c# 中,字符串是一个类,其中字符是一个字节或两个字节,并且是一个受保护的属性,指示字符是一个字节还是两个字节。因此,要将 c# 类字符串转换为 TCHAR,您需要使用编码 1) byte[] bytes Encoding.Unicode.GetBytes(string) 2) string s = Encoding.Unicode.GetString(bytes)
  • 我不完全清楚 C# 代码是否应该调用此方法或实现它。此外,TCHAR 可以是 char 或 wchar_t,具体取决于您如何编译 C++ 代码。这也需要修复并让 C# 代码知道。
  • @jdweng 和一个 short 字符串我的意思是只有几个字符(C++ 中的 TCHAR 类型),而不是一系列短裤。问题是如何将 TCHAR* 编组为 .NET。我还没有在 C# 中尝试过 byte[] 。感谢您的建议。
  • @KlausGütter C# 应该实现这个。可以让 C# 实现知道使用的是 char 还是 wchar_t。两者确实是可能的。使用 COM 接口的 C++ 应用程序有 unicode 和非 unicode 变体。
  • 作为最后的手段,您可以使用GetShortText(int i, [MarshalAs:UnmanagedType.Bool] bool b, IntPtr shortTextPtr),然后使用Marshal 类中的方法将字符填充到缓冲区中。

标签: c# .net com com-interop idl


【解决方案1】:

我不知道这个相当不寻常的构造有任何标准编组。但是,您可以执行以下操作:

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, 0, shortTextPtr, buffer.Length);
}

注意事项:

  • 应添加对 buffer.Length 的范围检查(ANSI 为
  • + '\0' 用于以 null 结尾的返回字符串

【讨论】:

  • 此实现不会被 C++ 客户端应用程序调用,因此它不能与给定 IDL 定义的 COM 服务器互换。
  • 您是否使用 regasm 注册了您的程序集?客户端应用程序是否收到任何错误?可以调试客户端应用吗?
  • 您说“COM 服务器”:所以这不是进程内场景? “应用程序事件日志中记录了一个错误”:哪个错误?
  • 这是一个进程内场景,其中使用 CoCreateInstance 创建了一个 coclass 实例。消费部分和 COM“服务器”(或者我应该怎么称呼它?)不能同时调试。事件 id 1000 类别 100 的应用程序错误。有 WER 文件,但它们不包含有用的信息。错误是找不到 COM 接口成员。我以前遇到过这个。
  • "不能同时调试" 为什么会这样?你对环境的描述越多,它看起来就越陌生。
【解决方案2】:

这种接口可以在 .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, 0, 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]);
};

【讨论】:

  • 我以为你已经有一个你想见的 IDL?你为什么使用.Net TLB? (从 Win32 的二进制角度看,LONGLPVOIDTCHAR[] 都是一样的)
  • 没错。这是问题的正确答案(忽略字符串编码)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-29
  • 2011-03-03
  • 2011-06-15
相关资源
最近更新 更多