6

文書化されたパラメータの1つが「バイトの配列」であるCOMオブジェクトのメソッドを呼び出そうとしています。実際の宣言は、表示している言語ごとのドキュメントによって異なります。

  • C 言語:

    byte[] TransformFinalBlock(
        byte[] inputBuffer,
        int inputOffset,
        int inputCount
    )
    
  • C++言語で;

    array<unsigned char>^ TransformFinalBlock(
        array<unsigned char>^ inputBuffer, 
        int inputOffset, 
        int inputCount
    )
    
  • VB言語

    Function TransformFinalBlock ( _
        inputBuffer As Byte(), _
        inputOffset As Integer, _
        inputCount As Integer _
    ) As Byte()
    
  • F 言語:

    abstract TransformFinalBlock : 
            inputBuffer:byte[] * 
            inputOffset:int * 
            inputCount:int -> byte[] 
    

私が使用しているオブジェクトには、COMを使用してアクセスすることもできます。ICryptoTransformオブジェクトは、メソッドを使用していると宣言する初期バインディングインターフェイスを提供しますSAFEARRAY

タイプライブラリから:

  • IDL構文を使用する

    [
      odl,
      uuid(8ABAD867-F515-3CF6-BB62-5F0C88B3BB11),
      version(1.0),
      dual,
      oleautomation,
      custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "System.Security.Cryptography.ICryptoTransform")    
    ]
    interface ICryptoTransform : IDispatch {
       ...
       [id(0x60020005)]
       HRESULT TransformFinalBlock(
                    [in] SAFEARRAY(unsigned char) inputBuffer, 
                    [in] long inputOffset, 
                    [in] long inputCount, 
                    [out, retval] SAFEARRAY(unsigned char)* pRetVal);
    };
    
  • Object Pascal構文の使用:

    ICryptoTransform = interface(IDispatch)
         ['{8ABAD867-F515-3CF6-BB62-5F0C88B3BB11}']
         ...
         function TransformFinalBlock(inputBuffer: PSafeArray; inputOffset: Integer; inputCount: Integer): PSafeArray; safecall;
    end;
    

つまり、アーリーバインディングを使用する場合は、メソッドaを渡す必要がありますSAFEARRAY。私が使用している言語はSafeArrayAPIをサポートしていますが、呼び出しを簡単に実行できますか?

var
   inputBuffer: PSafeArray;
   xform: ICryptoTransform;
   ...
begin
   ...

   xform.TransformFinalBlock(inputBuffer, ...);
   ...
end;

これがJavaのような言語の同じコードです:

PSafeArray inputBuffer;
ICryptoTransform xform;

...

xform.TransformFinalBlock(inputBuffer, ...);

そして、すべてが正常に機能します; しかし、それは私の質問ではありません。

注: COMは言語に依存しないテクノロジーであるため、これは言語に依存しない質問であるという点を理解しようとしています。しかし、ある時点で、コードをデモンストレーションする言語を実際に使用する必要があります。言語とテクノロジーを混同する人もいます。Knuthが発明した言語を知っていれば、それを使用したでしょう。

しかし、遅延バインディングはIDispatchどうですか?

COMオブジェクトにaを渡すことができることがわかったので(アーリーバインディングを使用する場合)、レイトバインディングを使用して配列を渡す問題を解決する必要があります。SAFEARRAY

:IDispatchを介してSAFEARRAYをCOMオブジェクトに渡す方法の質問は、以外の状況で役立ちますICryptoTransform

IDispatch一部の言語は、実行時にインターフェースを介してメソッドを呼び出す自動メカニズムを提供します(つまり、遅延バインディング)。実際IDispatch、遅延バインディングはVBScript用に発明されました。

Dim xform = CreateObject("System.Security.Cryptography.SHA256Managed");
Dim buffer;
o.TransformFinalBlock(buffer, 0, 8);

また、遅延バインディングコンパイラの自動マジックが.NET4.0に追加されました。

dynamic xform = Activator.CreateInstance(Type.GetTypeFromProgID("System.Security.Cryptography.SHA256Managed", true));
xform.TransformFinalBlock(buffer, 0, 8);

遅延バインディングコンパイラの魔法もDelphiに存在していました。

xform: OleVariant;
buffer: OleVariant;
xform.TransformFinalBlock(buffer, 0, 8);

私はたまたまDephiを使用していますが、この呼び出しは失敗します。

しかし、それは実際にはコンパイラの魔法ではありません

VBScript、C#ダイナミック、およびDelphiが行っていることは実際には魔法ではありません。彼らはただ呼んでいますIDispatch.Invoke

IDispatch = interface(IUnknown)
   ['{00020400-0000-0000-C000-000000000046}']
   function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
         Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall;
end;

混乱はこれらのパラメータを設定しています:

xform.Invoke(
      1610743820,      //DispID
      IID_NULL,        //riid (reserved for future use, must be IID_NULL)
      0,               //locale id (lcid)
      DISPATCH_METHOD, //flags
      dispParams,      //Pointer to a DISPPARAMS structure
      null,            //Pointer to the location where the result is to be stored, or NULL if the caller expects no result
      exceptInfo,      //Pointer to a structure that contains exception information
      null);           //This argument can be set to null. 

本当のトリックはdispParams、引数を含む構造です。

引数はバリアントになります

DISPPARAMSを介して渡される引数は、すべてバリアントです。

typedef struct tagDISPPARAMS {
  VARIANTARG *rgvarg;
  DISPID     *rgdispidNamedArgs;
  UINT       cArgs;
  UINT       cNamedArgs;
} DISPPARAMS;

したがって、何が起こっても、私の「バイト配列」はバリアントになります。

Win32のAVARIANTは、以下を含む単なる結合です。

  • VARTYPE vt:ユニオン内のデータのタイプ。
  • 適切な組合員、例:

    BYTE        bVal;
    IDispatch  *pdispVal;
    SAFEARRAY  *parray;
    BYTE       *pbVal;
    IDispatch  *ppdispVal;
    SAFEARRAY  *pparray;
    VARIANT    *pvarVal;
    PVOID       byref;
    CHAR        cVal;
    

今まで私はタイプの変種を渡してきました:

vt = VT_ARRAY | VT_UI1

MSDNには、 parrayユニオンをVT_ARRAY | *次のコマンドで使用する場合に実行する必要があることが記載されています。

VT_ARRAY | <anything>

説明:データ型の配列が渡されました。VT_EMPTYおよびVT_NULLは、VT_ARRAYと組み合わせるには無効な型です。pbyrefValのポインタは、配列記述子を指します。配列記述子は、配列の次元、サイズ、およびメモリ内の場所を記述します。

これが意味するのは、parrayメンバーを使用することです。

    SAFEARRAY  *parray;

メンバーを構造体parrayへのポインターに設定する必要があります。SAFEARRAY

typedef struct tagSAFEARRAY {
  USHORT         cDims;
  USHORT         fFeatures;
  ULONG          cbElements;
  ULONG          cLocks;
  PVOID          pvData;
  SAFEARRAYBOUND rgsabound[1];
} SAFEARRAY, *LPSAFEARRAY;

私の場合、バイトの配列は実際にはSAFEARRAYであり、バリアントに格納されています。

VARIANT *inputBuffer;
SAFEARRAY *safeArray;

//Setup our SAFEARRAY of data
safeArray.cDims = 1;
safeArray.fFeatures = FADF_HAVEVARTYPE;
safeArray.cbElements = 1;
safeArray.cbLocks = 0;
safeArray.pvData = pMyData;
safeArray.rgsabound[0].ElementCount = 1;
safeArray.rgsabound[0].LowBound = 0;

//Wrap the safearray in a variant
inputBuffer.vt = VT_ARRAY | VT_UI1; //$2011
vt.parray = safeArray;

:もちろん、私はこのセーフアレイを自分で作成するほど頭がおかしいわけではありません。私はSafeArrayCreateAPI関数を使用しています。私はそれがすべて知っていることを示しているだけであり、魔法ではありません。

言い換えれば、私はバイトの配列バリアントを渡します

言い換えれば、私すべての呼び出しとして、バリアントにラップされたバイトの配列を渡します:

dispatch.Invoke(...);

でなければなりません。遅延バインディング呼び出しがエラーをスローすることを除いて:

The parameter is incorrect.

だから私はおそらく間違っているのですか?

バイトの配列をレイトバウンド IDispatch呼び出しに渡すにはどうすればよいですか?

私の質問

IDispatchを介してSAFEARRAYをCOMオブジェクトに渡す方法は?

4

1 に答える 1

2

これにより、いくつかの洞察が得られるはずです。

呼び出し側では、C# コード:

Foo foo = new Foo();
byte[] input = new byte[] { 1, 2, 3, 4 };
byte[] output = foo.Bar(input);
byte[] referenceOutput = new byte[] { 4, 3, 2, 1 };
Debug.Assert(Enumerable.SequenceEqual(output, referenceOutput));

Foo.BarIDL :

interface IFoo : IDispatch
{
    [id(1)] HRESULT Bar([in] VARIANT vInput, [out, retval] VARIANT* pvOutput);
};

セーフ配列を使用した C++ (ATL) サーバーの実装:

// IFoo
    STDMETHOD(Bar)(VARIANT vInput, VARIANT* pvOutput) throw()
    {
        _ATLTRY
        {
            ATLENSURE_THROW(vInput.vt == (VT_ARRAY | VT_UI1), E_INVALIDARG);
            CComSafeArray<BYTE> pInputArray(vInput.parray);
            ATLASSERT(pInputArray.GetDimensions() == 1);
            const ULONG nCount = pInputArray.GetCount();
            CComSafeArray<BYTE> pOutputArray;
            ATLENSURE_SUCCEEDED(pOutputArray.Create(nCount));
            for(ULONG nIndex = 0; nIndex < nCount; nIndex++)
                pOutputArray[(INT) nIndex] = pInputArray[(INT) ((nCount - 1) - nIndex)];
            ATLASSERT(pvOutput);
            VariantInit(pvOutput);
            CComVariant vOutput(pOutputArray.Detach());
            ATLVERIFY(SUCCEEDED(vOutput.Detach(pvOutput)));
        }
        _ATLCATCH(Exception)
        {
            return Exception;
        }
        return S_OK;
    }

ソース: TracSubversion - Visual Studio 2012 に注意してください。

于 2012-08-17T18:54:43.027 に答える