文書化されたパラメータの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;
注:もちろん、私はこのセーフアレイを自分で作成するほど頭がおかしいわけではありません。私は
SafeArrayCreate
API関数を使用しています。私はそれがすべて知っていることを示しているだけであり、魔法ではありません。
言い換えれば、私はバイトの配列バリアントを渡します
言い換えれば、私はすべての呼び出しとして、バリアントにラップされたバイトの配列を渡します:
dispatch.Invoke(...);
でなければなりません。遅延バインディング呼び出しがエラーをスローすることを除いて:
The parameter is incorrect.
だから私はおそらく間違っているのですか?
バイトの配列をレイトバウンド IDispatch
呼び出しに渡すにはどうすればよいですか?
私の質問
IDispatchを介してSAFEARRAYをCOMオブジェクトに渡す方法は?