How to deduce types with zero value through COM

I have been struggling with this problem for a day and a half, I hope someone can help me. Let's say I have structures like this in C #:

public struct Part { public double? x; // or System.Nullable<double> x, doesn't really matter } 

(these structures represent database tables converted from Linq to SQL code created by SQLMetal)

I need to provide these structures containing types with a null value, COM, so that they can be used in another application (C ++). But I can’t understand for me how to do this. I thought I could create classes to encapsulate types with a null value:

 public class NullableDouble { private double _Value; // class methods... } public struct Part { public NullableDouble x; } 

Similar works, but on the C ++ side, I get a pointer to a class, but not a class definition (just an interface):

 interface DECLSPEC_UUID("{E8EE4597-825D-3F4C-B20B-FD6E9026A51C}") _NullableDouble; struct Part { MyDll_tlb::_NullableDouble* x; } 

Thus, I cannot dereference this pointer without defining a class from which you can access data elements / methods on the C ++ side. This still seems like a good option if I can just figure out how to get the class definition in C ++ (I'm new to COM).

I thought maybe I can use unsafe code, but I can’t figure out how to convert from a double? double * (I am also new to C #!):

 unsafe public struct Part { public double* x; } Part part = new Part() { x = AnotherObject.x // AnotherObject.x is a System.Nullable<double> } 

I was thinking maybe I can use System.Variant, but C # doesn't like it either (inconsistent availability, whatever that means).

 public struct Part { public Variant x; // produces 2 errors: // 1) System.Variant inaccessible, // 2) inconsistent accessibility } 

I have been using C ++ for 20 years, but I'm pretty new to COM and C #, so for me this is a battle.

In the worst case ... I simply create a logic element in the structure for each of the types with a null value and use this to indicate whether the value should be processed as if it were null. But it just seems stupid. Of course, some way to show nullable types through COM. Why has Microsoft created .NET types that cannot be used in COM? These guys in Redmond are not idiots (although sometimes it just looks like that).

So what are my options? What is the best way to do this?

Thanks in advance.

+7
c ++ c # com-interop
source share
1 answer

Can you use type object instead of double? for your field structure and apply to it [MarshalAs(UnmanagedType.Struct)] to marshal it as VARIANT . Here's a great article on this .

Code example:

FROM#

 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; } } } 

Register (for 32-bit build):

 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; } 

Output:

 Value: 1.100000 

If the field is set to null ( double? testValue = null; ), the return type of VARIANT will be VT_EMPTY , otherwise VT_R8 .

On the side of the note, it is not good practice to open the class interface for COM. You can create a separate interface for this. Using this approach, you can open your interface based on IUnknown , because C ++ does not require IDispatch plumbing:

 [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)) ); 
+2
source share

All Articles