10

私はこの問題に1日半苦労しています。誰かが私を助けてくれることを願っています. C# で次のような構造があるとします。

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

(これらの構造は、Linq から SQLMetal によって作成された SQL コードに変換されたデータベース テーブルを表します)

null 許容型を含むこれらの構造体を COM に公開して、別のアプリケーション (C++) で使用できるようにする必要があります。しかし、私の人生では、これを行う方法を理解できません。null 許容型をカプセル化するクラスを作成できると思いました。

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 から変換する方法がわかりませんか? to 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 許容型ごとに構造体にブール値のメンバーを作成し、それを使用して、値が null であるかのように扱われるかどうかを示します。しかし、それはばかげているようです。確かに、COM を介して null 許容型を公開する何らかの方法が必要です。Microsoft が COM で使用できない .NET 型を作成するのはなぜですか? レドモンドの連中は馬鹿ではない (確かにそう思われることもあるが)。

それで、私のオプションは何ですか?これを行う最善の方法は何ですか?

前もって感謝します。

4

1 に答える 1

4

構造体フィールドobjectの代わりにtype を使用し、それに適用して としてマーシャリングすることができます。これは、この件に関する優れた記事です。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;) に設定されている場合、返されるのタイプは になります。それ以外の場合はです。VARIANTVT_EMPTYVT_R8

余談ですが、クラス インターフェイスを COM に公開することはお勧めできません。そのための別のインターフェイスを作成することをお勧めします。このアプローチを採用すると、C++ の配管がIUnknown必要ないため、 に基づいてインターフェイスを公開できます。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)) );
于 2013-08-26T02:21:03.900 に答える