我一直在努力解决这个问题一天半,希望有人可以帮助我。假设我在C#中有这样的结构:
public struct Part
{
public double? x; // or System.Nullable<double> x, doesn't really matter
}
(这些结构表示数据库表,从Linq转换为SQLMetal创建的SQL代码)
我需要能够将包含可空类型的这些结构公开给COM,以便它们可以在另一个应用程序(C ++)中使用。但我无法弄清楚,对于我的生活,如何做到这一点。我以为我可以创建类来封装可空类型:
public class NullableDouble
{
private double _Value;
// class methods...
}
public struct Part
{
public NullableDouble x;
}
这种方法有效,但在C ++方面,我最终得到了一个指向类但没有类定义的指针(只是一个接口):
interface DECLSPEC_UUID("{E8EE4597-825D-3F4C-B20B-FD6E9026A51C}") _NullableDouble;
struct Part
{
MyDll_tlb::_NullableDouble* x;
}
因此,我无法取消引用没有类定义的指针,从中可以访问C ++端的数据成员/方法。如果我能弄清楚如何在C ++中获取类定义(我是COM的新手),这似乎仍然是一个不错的选择。
我想也许我可以使用不安全的代码,但我无法弄清楚如何从double转换?加倍*(我也是C#的新手!):
unsafe public struct Part
{
public double* x;
}
Part part = new Part()
{
x = AnotherObject.x // AnotherObject.x is a System.Nullable<double>
}
我想也许我可以使用System.Variant,但C#也不喜欢它(不一致的可访问性,无论这意味着什么)。
public struct Part
{
public Variant x;
// produces 2 errors:
// 1) System.Variant inaccessible,
// 2) inconsistent accessibility
}
我已经使用C ++已有20年了,但我对COM和C#相当新,所以这对我来说有点困难。
最坏情况......我将在结构中为每个可空类型创建一个布尔成员,并使用它来指示是否将值视为null。但这似乎很愚蠢。当然必须有一些方法通过COM公开可空类型。为什么Microsoft会创建无法在COM中使用的.NET类型?雷德蒙德的那些家伙不是白痴(尽管有时肯定会这样)。
那么,我的选择是什么?这样做的最佳方式是什么?
提前致谢。
答案 0 :(得分:4)
您可以为结构字段使用object
而不是double?
,并将[MarshalAs(UnmanagedType.Struct)]
应用于VARIANT
以将其编组为using System.Runtime.InteropServices;
namespace InteropTest
{
[ComVisible(true)]
public struct TestStruct
{
[MarshalAs(UnmanagedType.Struct)]
public object testField;
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
[Guid("6E0DD830-1BF9-41E0-BBEB-4CC314BBCB55")]
public class TestClass
{
public void GetTestStruct(ref TestStruct p)
{
double? testValue = 1.1;
p.testField = testValue;
}
}
}
。这是一个excellent article on the subject。
代码示例:
<强> C#:强>
C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /codebase /tlb InteropTest.dll
注册(对于32位程序集):
#include "stdafx.h"
#import "InteropTest.tlb" raw_interfaces_only
#define _S(a) \
{ HRESULT hr = (a); if (FAILED(hr)) return hr; }
int _tmain(int argc, _TCHAR* argv[])
{
_S( CoInitialize(NULL) )
InteropTest::_TestClassPtr testClass;
_S( testClass.CreateInstance(__uuidof(InteropTest::TestClass)) );
InteropTest::TestStruct s;
VariantInit(&s.testField);
_S( testClass->GetTestStruct(&s) );
printf("Value: %f", V_R8(&s.testField));
CoUninitialize();
return 0;
}
<强> C ++:强>
Value: 1.100000
<强>输出:强>
null
如果该字段设置为double? testValue = null;
(VARIANT
),则返回的VT_EMPTY
的{{3}}将为VT_R8
,否则为IUnknown
}。
注意,它type以向COM公开类接口。您可能想为此创建一个单独的界面。采用这种方法,您可以基于IDispatch
公开您的界面,因为您不需要{C ++的[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("E3B77594-8168-4C12-9041-9A7D3FE4035F")]
public interface ITestClass
{
void GetTestStruct(ref TestStruct p);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(ITestClass))]
[Guid("6E0DD830-1BF9-41E0-BBEB-4CC314BBCB55")]
public class TestClass : ITestClass
{
public void GetTestStruct(ref TestStruct p)
{
double? testValue = 1.1;
p.testField = testValue;
}
}
管道:
InteropTest::ITestClassPtr testClass;
_S( testClass.CreateInstance(__uuidof(InteropTest::TestClass)) );
<强> C ++:强>
{{1}}