3

私はC#で次のインターフェイスを使用しており、同じ名前のクラス(Iなし)を実装しています。

[ComVisible(true)]
[Guid("B2B134CC-70A6-43CD-9E1E-B3A3D9992C3E")]
public interface IOrder
{
    long GetQuantity();
    long GetOrderType();
    long GetPositionType();
}

パブリッククラスOrder:IOrderの実装は、3つのプライベートフィールドと、必要な3つのパラメーターを持つコンストラクターです。

他の場所で、次のメソッドを使用して、COMファイルと.tlb/.tlhファイルを介してC++アンマネージコード内で作業したい結果を取得しました。

public ScOrder[] GetOrders()
{
    //constant return value for simplicity
    return new Order[] {    
        new Order(1, 2, 3),
        new Order(4, 5, 6)
    };
}

私はすでに、C#マネージコードを使用してC++アンマネージコード間で基本的な作業を行うことができました。

しかし、クラス配列は別の課題であることが証明されました...

私にとって、COMは新しく、残酷に混乱し、C ++は長い間忘れられていたことを認めます...しかし、私は両方のライブラリを開発しているので、あきらめません。C ++ DLLをプログラムとC#コード間のプロキシとして機能させたい。

明確化:私はMFCもATLも使用していません。C ++コードで#importを使用して、C#で生成されたインターフェイスとクラスポインター、およびまだ完全には理解していないその他のCOMのものを取得します。

1時間の調査の後、私はここに行って助けを求めています>。<

以下は、私が達成しようとしていることのC++コードです。

//this is how the instance of C# gets created, read it from the internets
//this type has the method GetOrders
IProxyPtr iPtr;
CoInitialize(NULL);
iPtr.CreateInstance(CLSID_Proxy);

IOrderPtr* ordArr; 
//IOrderPtr is just a pointer to the interface type transferred
//right? So IOrderPtr* should represent the array of those pointers, right? 

SAFEARRAY* orders;
iPtr->GetOrders(&orders);

この時点で、SAFEARRAY*をIOrderPtr*などに変換するために、まだ理解していないCOMマジックが必要です。これにより、返された配列全体を反復処理して、「Order」タイプのメソッドを呼び出すことができます。

  • GetQuantity()
  • GetOrderType()
  • GetPositionType()

したがって、最初のサイクルでは値1、2、3を取得し、2番目のサイクルでは値4、5、6を取得します。

私はC++とC#の両方のライブラリの作成者なので、このCOMのクレイジーなものをすべてスキップして、コレクション数を取得するメソッドや、特定のインデックスのプロパティの値を取得する他のメソッドを作成できます。

しかし、それは良くないようです。私が欲しいものの仕組みは簡単だと思いますが、グーグルで見つけたすべての答えは常に何かが欠けています。

4

2 に答える 2

3

C++ クライアントで MFC、ATL、またはその他のライブラリを使用しているかどうかがわからない場合、単純化するのは難しいため、Win32 API を使用します (これらのライブラリは、セーフ配列をより簡単に使用するためのヘルパー クラスを提供します)。

#importただし、生成されたスマート ポインター クラスを使用できるように、Interop タイプ ライブラリを介して C# lib を使用することを前提としています。また、IUnknowns を含むバリアントの SAFEARRAY ではなく、IUnknowns の SAFEARRAY を返すと仮定します。これは、C# インターフェイスで適切なマーシャリング属性を指定することで変更できます。

[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)] 
// [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)]
IOrder[] GetOrders();

C# 型の実装を次に示します (サンプル ソリューションへのリンクは回答の下部にあります)。

[ComVisible(true)]
[Guid("F3071EE2-84C9-4347-A5FC-E72736FC441F")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IProxy
{
    [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)] 
    IOrder[] GetOrders();
}

[ComVisible(true)]
[Guid("8B6EDB6B-2CF0-4eba-A756-B6E92A71A48B")]
[ClassInterface(ClassInterfaceType.None)]
public class Proxy : IProxy
{
    public IOrder[] GetOrders() { return new[] {new Order(3), new Order(4)};        }
}

[ComVisible(true)]
[Guid("CCFF9FE7-79E7-463c-B5CA-B1A497843333")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOrder
{
    long GetQuantity();
}

[ComVisible(true)]
[Guid("B0E866EB-AF6D-432c-9560-AFE7D171B0CE")]
[ClassInterface(ClassInterfaceType.None)]
public class Order : IOrder
{
    private int m_quantity;
    public Order(int quantity) { m_quantity = quantity; }
    public long GetQuantity() { return m_quantity; }
}

サーバーを構築し、 に登録する必要がありますRegasm。簡単にregasm /codebase /tlb $pathするために、GAC での署名と登録を避けるようにします。

クライアント コードは次のようになります。

#import "Server.tlb" no_namespace // you should use namespaces! this is a demo

int _tmain(int argc, _TCHAR* argv[])
{
  CoInitialize(NULL);      // init COM

  IProxyPtr proxy(__uuidof(Proxy));         // instantiate the proxy
  SAFEARRAY* orders = proxy->GetOrders();   // to return orders

  LPUNKNOWN* punks;   
  HRESULT hr = SafeArrayAccessData(orders, (void**)&punks); // direct access to SA memory
  if (SUCCEEDED(hr))
  {
    long lLBound, lUBound;  // get array bounds
    SafeArrayGetLBound(orders, 1 , &lLBound);
    SafeArrayGetUBound(orders, 1, &lUBound);

    long cElements = lUBound - lLBound + 1; 
    for (int i = 0; i < cElements; ++i)  // iterate through returned objects
    {                              
      LPUNKNOWN punk = punks[i];     // for VARIANTs: punk = punks[i].punkVal
      IOrderPtr order(punk);         // access the object via IOrder interface
      long q = order->GetQuantity(); // and voila!
      std::cout << "order " << i + 1 << ": Quantity " << q << std::endl;
    }       
    SafeArrayUnaccessData(orders);
  }
  SafeArrayDestroy(orders);
  return 0;
}

サンプル プロジェクトは、ここにあります。最初にビルドするときに .tlb を手動で登録する必要があることに注意してください。プロジェクトはそれを行いませんが、必要に応じてビルド後のステップを追加できます。

于 2012-09-14T08:17:25.107 に答える
0

SAFEARRAYS での作業は首の痛みです。それを回避する方法はありません。

SAFEARRAY は構造体であるため、便利な IOrder* 配列にキャストして、C# のように項目を操作することはできません。

グーグルが私に示したいくつかのことを次に示します。彼らはかなり役に立ちます。

http://edn.embarcadero.com/article/22016

http://digital.ni.com/public.nsf/allkb/7382E67B95238D2B862569AD005977F0

C++ プロジェクトで ATL を使用している場合は、CComSafeArray ラッパーがあります。

http://msmvps.com/blogs/gdicanio/archive/2011/02/04/simplifying-safearray-programming-with-ccomsafearray.aspx

于 2012-09-14T01:55:04.550 に答える