簡體   English   中英

如何通過COM公開可空類型

[英]How to expose nullable types via COM

我一直在努力解決這個問題一天半,希望有人可以幫助我。 假設我在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的新手),這仍然是一個不錯的選擇。

我想也許我可以使用不安全的代碼,但我無法弄清楚如何從雙轉換? 加倍*(我也是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類型? 雷德蒙德的那些家伙不是白痴(雖然有時候看起來確實如此)。

那么,我的選擇是什么? 做這個的最好方式是什么?

提前致謝。

你可以使用type 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

如果該字段設置為nulldouble? testValue = null;類型返回的VARIANTVT_EMPTY ,否則就VT_R8

另外,將類接口公開給COM 並不是一個好習慣 您可能想為此創建一個單獨的界面。 采用這種方法,您可以基於IUnknown公開您的界面,因為您不需要C ++的IDispatch管道:

[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)) );

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM