这个COM接口可以用C#实现吗?
Can this COM interface be implemented in C#?
一个 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*
调用的方法?
我不知道这个相当不寻常的构造有任何标准编组。但是,您可以执行以下操作:
void GetShortText(int i, [MarshalAs:UnmanagedType.Bool] bool b, IntPtr shortTextPtr)
{
string s = "Test";
byte[] buffer;
if (UseUnicode)
buffer = Encoding.Unicode.GetBytes(s + '[=10=]');
else
buffer = Encoding.Default.GetBytes(s + '[=10=]');
Marshal.Copy(buffer, 0, shortTextPtr, buffer.Length);
}
备注:
- 应添加对 buffer.Length 的范围检查(<= 10 对于 ANSI,<= 20 对于 UNICODE)
+ '[=11=]'
用于 null-terminating 返回的字符串
这种接口可以在 .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]);
};
一个 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*
调用的方法?
我不知道这个相当不寻常的构造有任何标准编组。但是,您可以执行以下操作:
void GetShortText(int i, [MarshalAs:UnmanagedType.Bool] bool b, IntPtr shortTextPtr)
{
string s = "Test";
byte[] buffer;
if (UseUnicode)
buffer = Encoding.Unicode.GetBytes(s + '[=10=]');
else
buffer = Encoding.Default.GetBytes(s + '[=10=]');
Marshal.Copy(buffer, 0, shortTextPtr, buffer.Length);
}
备注:
- 应添加对 buffer.Length 的范围检查(<= 10 对于 ANSI,<= 20 对于 UNICODE)
+ '[=11=]'
用于 null-terminating 返回的字符串
这种接口可以在 .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]);
};