Я боролся с этой проблемой в течение полутора дней, надеюсь, кто-то может мне помочь. Допустим, у меня есть такие структуры в C #:
public struct Part
{
public double? x; // or System.Nullable<double> x, doesn't really matter
}
(эти структуры представляют таблицы базы данных, преобразованные из Linq в код SQL, созданный SQLMetal)
Мне нужно иметь возможность выставить эти структуры, содержащие обнуляемые типы, для 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).
Я подумал, может быть, я мог бы использовать небезопасный код, но я не могу понять, как конвертировать из двойного? удвоить * (я тоже новичок в 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 создает типы .NET, которые нельзя использовать в COM? Эти парни в Редмонде не идиоты (хотя иногда это так кажется).
Итак, каковы мои варианты? Каков наилучший способ сделать это?
Заранее спасибо.
Вы могли бы использовать тип object
вместо double?
для вашей структуры поля и применить [MarshalAs(UnmanagedType.Struct)]
чтобы маршал это как VARIANT
, Вот отличная статья на эту тему.
Пример кода:
C #:
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;
}
}
}
регистр (для 32-битной сборки):
C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /codebase /tlb InteropTest.dll
C ++:
#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;
}
Выход:
Value: 1.100000
Если поле установлено в null
(double? testValue = null;
), тип из возвращенных VARIANT
будет VT_EMPTY
иначе это VT_R8
,
На заметку, Это не очень хорошая практика выставить интерфейс класса к COM. Вы можете создать отдельный интерфейс для этого. Используя этот подход, вы можете выставить свой интерфейс как основанный на IUnknown
потому что тебе не нужно 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;
}
}
C ++:
InteropTest::ITestClassPtr testClass;
_S( testClass.CreateInstance(__uuidof(InteropTest::TestClass)) );
Других решений пока нет …