Как выставить обнуляемые типы через COM

Я боролся с этой проблемой в течение полутора дней, надеюсь, кто-то может мне помочь. Допустим, у меня есть такие структуры в 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? Эти парни в Редмонде не идиоты (хотя иногда это так кажется).

Итак, каковы мои варианты? Каков наилучший способ сделать это?

Заранее спасибо.

8

Решение

Вы могли бы использовать тип 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)) );
3

Другие решения

Других решений пока нет …