Как передать SAFEARRAY в COM-объект через IDispatch?
я пытаюсь вызвать метод COM-объекта, где один изdocumented параметры является"array of bytes& Quot ;. Фактическое заявление зависит от документации на каждый язык, которую вы просматриваете:
in C# language:
byte[] TransformFinalBlock(
byte[] inputBuffer,
int inputOffset,
int inputCount
)
in C++ language;
array<unsigned char>^ TransformFinalBlock(
array<unsigned char>^ inputBuffer,
int inputOffset,
int inputCount
)
in VB language:
Function TransformFinalBlock ( _
inputBuffer As Byte(), _
inputOffset As Integer, _
inputCount As Integer _
) As Byte()
in F# language:
abstract TransformFinalBlock :
inputBuffer:byte[] *
inputOffset:int *
inputCount:int -> byte[]
Доступ к объекту, который я использую, также можно получить с помощьюCOM, Объект предоставляет интерфейс раннего связывания,ICryptoTransform
, который объявляет метод как использующийSAFEARRAY
.
Отtype library:
using IDL syntax
[
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);
};
using object Pascal syntax:
ICryptoTransform = interface(IDispatch)
['{8ABAD867-F515-3CF6-BB62-5F0C88B3BB11}']
...
function TransformFinalBlock(inputBuffer: PSafeArray; inputOffset: Integer; inputCount: Integer): PSafeArray; safecall;
end;
Это означает, что при использованииearly-binding Вы должны пройти метод аSAFEARRAY
, Язык, который я использую, поддерживает API SafeArray, могу ли я выполнить вызов достаточно легко:
var
inputBuffer: PSafeArray;
xform: ICryptoTransform;
...
begin
...
xform.TransformFinalBlock(inputBuffer, ...);
...
end;
Вот тот же код на Java-подобном языке:
PSafeArray inputBuffer;
ICryptoTransform xform;
...
xform.TransformFinalBlock(inputBuffer, ...);
И все работаетfine; но это не мой вопрос.
Note: i'm trying drive home the point that this is a language-agnostic question, as COM is a language agnostic technology. But at some point we have to actually use a language that we will demonstrate code in. Some people confuse a language with a technology. If i knew Knuth's invented language, i would have used that.
But what about late-bindingIDispatch
?
Теперь, когда мы знаем, что мыcan передатьSAFEARRAY
в COM-объект (при использованииearly-binding), мне нужно решить проблему передачи массива с помощьюlate-binding.
Note: The question of how to pass a SAFEARRAY to a COM object through IDispatch is useful me to in circumstances besides ICryptoTransform
.
BSTR
to MSHTML control
passing an array of byte to other COM objects
Некоторые языки предоставляют автоматические механизмы для вызова методов черезIDispatch
интерфейс во время выполнения (т.е.late-binding). по фактуIDispatch
Позднее связывание было изобретено для VBScript:
Dim xform = CreateObject("System.Security.Cryptography.SHA256Managed");
Dim buffer;
o.TransformFinalBlock(buffer, 0, 8);
И автоматическое волшебство компилятора позднего связывания было добавлено в .NET 4.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);
яhappen использовать Dephi, и этот вызов не удается.
But it's not really compiler magicНе совсем волшебно то, что делают VBScript, C # dynamic и 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;
Так что, что бы ни случилось, мой"array of bytes" будет вариант.
VARIANT
в Win32 это просто объединение, которое содержит:
VARTYPE vt
: The type of data in the union.
the appropriate union member, e.g.:
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 | *
:
Value: VT_ARRAY | <anything>
Description: An array of data type was passed. VT_EMPTY and VT_NULL are invalid types to combine with VT_ARRAY. The pointer in pbyrefVal points to an array descriptor, which describes the dimensions, size, and in-memory location of the array.
Это означает, что с помощьюparray
член:
SAFEARRAY *parray;
Вам нужно установитьparray
член указатель наSAFEARRAY
состав:
typedef struct tagSAFEARRAY {
USHORT cDims;
USHORT fFeatures;
ULONG cbElements;
ULONG cLocks;
PVOID pvData;
SAFEARRAYBOUND rgsabound[1];
} SAFEARRAY, *LPSAFEARRAY;
В моем случае мойarray of bytes на самом деле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;
Note: Of course i'm not crazy enough to have created this safearray myself; i'm using the SafeArrayCreate
api function. i'm just demonstrating that it's all knowable, and not magic.
Другими словами яam Передача массива байтов, заключенных в вариант, как и все вызовы:
dispatch.Invoke(...);
должно быть. За исключением того, чтоlate-binding вызов выдает ошибку:
The parameter is incorrect.
Так что я, возможно, делаю не так?
Как пройтиarray of byte кlate-bound IDispatch
вызов?
Как передать SAFEARRAY в COM-объект через IDispatch?