Как передать 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-binding IDispatch?

Теперь, когда мы знаем, что мы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.

passing an array of 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 структура, которая содержит аргументы.

The argument will be an variant

Все аргументы, передаваемые через 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.

In other words i pass an array variant of bytes

Другими словами яam Передача массива байтов, заключенных в вариант, как и все вызовы:

dispatch.Invoke(...);

должно быть. За исключением того, чтоlate-binding вызов выдает ошибку:

The parameter is incorrect.

Так что я, возможно, делаю не так?

Как пройтиarray of byte кlate-bound IDispatch вызов?

My Question

Как передать SAFEARRAY в COM-объект через IDispatch?

Ответы на вопрос(1)

Ваш ответ на вопрос