3

ATL / COMを使用してC#コードからアンマネージC++に画像データを渡したい

C#コード側から、私は次のようなことをします:

void SendFrame([In, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UI1)] ref byte[] frameData);

しかし、C++コードでこの関数をどのように処理すればよいかわかりません。

今のところ私はこのようなものを持っています:

_ATL_FUNC_INFO OnSendFrameDef = { CC_STDCALL, VT_EMPTY, 1, { VT_SAFEARRAY | VT_UI1 } };

void __stdcall OnSendFrame(SAFEARRAY* ppData)
{
   BYTE* pData;
   SafeArrayAccessData(ppData, (void **) &pData);

   // Doing some stuff with my pData

   SafeArrayUnaccessData(ppData);
}

誰かが私にこれを機能させる方法についていくつかの提案をすることができますか?

ありがとう。

4

4 に答える 4

2

SAFEARRAYはすでにアンマネージコードにマーシャリングされており、ATLを使用しているため、インスタンスを操作するときにすべてのクリーンアップを処理するクラスを使用CComSafeArrayできます。SAFEARRAY

_ATL_FUNC_INFO OnSendFrameDef = { CC_STDCALL, VT_EMPTY, 1, 
    { VT_SAFEARRAY | VT_UI1 } };

void __stdcall OnSendFrame(SAFEARRAY* ppData)
{
    // Wrap in a CComSafeArray.
    // On the stack means all calls to cleanup
    // will be cleaned up when the stack
    // is exited.
    CComSafeArray<byte> array;
    array.Attach(ppData);

    // Work with the elements, get the first one.
    byte b = array.GetAt(0);

    // And so on.  The destructor for CComSafeArray
    // will be called here and cleaned up.
}
于 2012-11-08T16:52:18.437 に答える
2

なんとか目標を達成できました!興味のある方へ:

私のイベントハンドラ記述子は次のようになります。

_ATL_FUNC_INFO Stream::OnStreamFrameCallbackDef = { CC_STDCALL, VT_EMPTY, 1, { VT_DISPATCH } };

私のC++関数:

void __stdcall Stream::OnStreamFrameCallback(IDispatch* pFrame)
{
   // NOTE that this "IStreamFramePtr" is COM's Ptr of my "IStreamFrame"
   MyCOM::IStreamFramePtr pStreamFrame = pFrame;

   // Thanks casperOne♦ for this:
   CComSafeArray<byte> array;
                   array.Attach(pStreamFrame->GetBuffer());

   // Now I can do stuff that I need...
   byte* pBuffer = &array.GetAt(0);
}

.tlhファイルの「IStreamFrame」は次のようになります。

struct __declspec(uuid("1f6efffc-0ac7-3221-8175-5272a09cea82"))
IStreamFrame : IDispatch
{
    __declspec(property(get=GetWidth))
    long Width;
    __declspec(property(get=GetHeight))
    long Height;
    __declspec(property(get=GetBuffer))
    SAFEARRAY * Buffer;

    long GetWidth ( );
    long GetHeight ( );
    SAFEARRAY * GetBuffer ( );
};

私のC#コードには、次のようなものがあります。

[ComVisible(true)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface IStreamFrame
{
    int     Width   { [return: MarshalAs(UnmanagedType.I4)]         get; }
    int     Height  { [return: MarshalAs(UnmanagedType.I4)]         get; }
    byte[]  Buffer  { [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_UI1)]  get; }
};


[ComVisible(false)]
public delegate void StreamFrameCallback(IStreamFrame frame);

[ComVisible(true)]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMyCOMStreamEvents
{
    [DispId(1)]
    void OnStreamFrameCallback([In, MarshalAs(UnmanagedType.IDispatch)] IStreamFrame frame);
}

物事はうまく機能しているようです。しかし、誰かが何か提案があるか、私が何か間違ったことをしていることに気づいたら、私に知らせてください。

ありがとう。

于 2012-11-09T11:51:20.543 に答える
1

COMインターフェイスを設計するためにIDLファイルを作成することを強くお勧めします。

あなたの答えの例を考えると、かなり最小限のIDLファイルは次のようになります。

import "oaidl.idl"; 

[object,
 uuid(1f6efffc-0ac7-3221-8175-5272a09cea82),
 dual,
 oleautomation]
interface IStreamFrame : IDispatch {
    [propget]
    HRESULT Width([out, retval] long *pWidth);
    
    [propget]
    HRESULT Height([out, retval] long *pHeight);
    
    [propget]
    HRESULT Buffer([out, retval] SAFEARRAY(byte) *pBuffer);
};

[uuid(1f6efffc-0ac7-3221-8175-5272a09cea83)]
library ContosoStreamFrame {
    importlib("stdole32.tlb");
    
    interface IStreamFrame;
};

次にmidl.exe、C / C ++インターフェイスを使用した.h、C / C ++リンク用のCLSIDおよびIID定数用の_i.c、RPC登録用のdlldata.c、プロキシおよびスタブマーシャリング関連のものを使用した_p.cを生成するために使用します。 .tlbは、一般的に.idlファイルの解析された表現です。これはすべて、ドキュメントで詳しく説明されています。

編集:C /C++ファイルの生成を回避する方法はないようです

EDIT2:回避策を見つけました。不要nulなものの出力ファイルとして使用してください。たとえば、次のコマンドは以下を生成するだけfile.tlbです。

midl.exe /header nul /iid nul /proxy nul /dlldata nul file.idl

注:IStreamFrameインターフェースがプロセス間で使用されることを意図していない場合は、インターフェースにlocal属性を追加してください。

C / C ++では、生成された特定のファイル、または#importTLBファイルを使用できます。tlbimp.exe.NETでは、.NETアセンブリを生成するTLBファイルで実行できます。

tlbexp.exeプロジェクトが.NET中心の場合にも使用できます。ただし、.NET COMアノテーションと、IDLに関してそれらが何を意味するかを知っておく必要があるため、多くの装飾を犠牲にして、1つの余分なソースファイルを別の言語で保存することにメリットがあるかどうかはわかりません。インターフェイスとクラス定義のノイズ。ソースレベルでクラスとインターフェイスを完全に制御し、.NETコードで可能な限り簡単に(読み取り、使いやすさ、場合によっては速度を最適化)したい場合は、おそらく良いオプションです。

最後に、プロジェクトを作成することにより、VisualStudioでこれらすべてを自動化できます。IDLアプローチを使用する場合は、カスタムビルドステップを追加しmidl.exetlbimp.exe、依存プロジェクトがこのプロジェクトに依存して正しいビルド順序になるようにします。.NETアプローチを使用する場合はtlbexp.exe、依存するC /C++プロジェクトを呼び出してこのプロジェクトに依存させるカスタムビルドステップを追加します。

編集:から生成されたC / C ++ファイルが必要ない場合は、特定の出力ファイルのカスタムビルドステップにコマンドをmidl.exe追加できます。del

EDIT2:または、nul上記の回避策を使用してください。

タイプライブラリが既に存在する場合に使用される一般的なアプローチは、VisualStudioを使用してそれを.NETにインポートすることです。ただし、この方法では、IDLファイルを更新する場合は、TLBを再生成し、再度インポートすることを忘れないでください。

于 2012-11-10T16:29:56.373 に答える
0

C ++ / CLIを検討しましたか?C ++ / CLIref classで、メンバー関数を記述します。

void SendFrame(cli::array<System::Byte> frameData)
{
    pin_ptr<System::Byte> p1 = &frameData[0];
    unsigned char *p2 = (unsigned char *)p1;

    // So now p2 is a raw pointer to the pinned array contents
}
于 2012-11-08T16:33:55.317 に答える