¿Cómo pasar SAFEARRAY al objeto COM a través de IDispatch?

Estoy tratando de llamar a un método de objeto COM, donde uno de losdocumentado parámetros es un"matriz de bytes". La declaración real depende de la documentación por idioma que esté viendo:

enDO# idioma:

byte[] TransformFinalBlock(
    byte[] inputBuffer,
    int inputOffset,
    int inputCount
)

enC ++ idioma;

array<unsigned char>^ TransformFinalBlock(
    array<unsigned char>^ inputBuffer, 
    int inputOffset, 
    int inputCount
)

enVB idioma:

Function TransformFinalBlock ( _
    inputBuffer As Byte(), _
    inputOffset As Integer, _
    inputCount As Integer _
) As Byte()

enF# idioma:

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

También se puede acceder al objeto que estoy usando usandoCOM. El objeto proporciona una interfaz de enlace temprano,ICryptoTransform, que declara que el método usaSAFEARRAY.

Desde elbiblioteca de tipos:

utilizandoIDL sintaxis

[
  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);
};

utilizandoobjeto Pascal sintaxis:

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

Esto significa que al usarunión temprana debes pasar el método aSAFEARRAY. El lenguaje que uso es compatible con las API de SafeArray, ¿puedo realizar la llamada con la suficiente facilidad?

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

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

Aquí está el mismo código en un lenguaje tipo java:

PSafeArray inputBuffer;
ICryptoTransform xform;

...

xform.TransformFinalBlock(inputBuffer, ...);

Y todo funcionamulta; Pero esa no es mi pregunta.

Nota: Estoy tratando de conducir a casa el punto de que esto es unlenguaje agnóstico pregunta, comoCOM es una tecnología agnóstica del lenguaje.. Pero en algún momento tenemos que en realidadutilizar Un lenguaje en el que demostraremos código.Algunas personas confunden un lenguaje con una tecnología.. Si supiera el lenguaje inventado de Knuth, lo habría usado.

Pero ¿qué pasa con la unión tardíaIDispatch?

Ahora que sabemos quepuede pasar unSAFEARRAY a un objeto COM (cuando se usaunión temprana), necesito resolver el problema de pasar una matriz usandoencuadernación tardía.

Nota: La cuestión de cómo pasar un SAFEARRAY a un objeto COM a través de IDispatch me resulta útil en circunstancias ademásICryptoTransform.

pasando una serie deBSTR al control de MSHTMLpasar una matriz de bytes a otros objetos COM

Algunos lenguajes proporcionan mecanismos automáticos para invocar métodos a través de unIDispatch interfaz en tiempo de ejecución (es decir,encuadernación tardía). De hechoIDispatch la unión tardía fue inventada para VBScript:

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

Y la compilación automática de vinculación tardía se agregó en .NET 4.0:

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

La magia del compilador de vinculación tardía también existía en Delphi:

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

i ocurrir estar usando Dephi, y esta llamada falla.

Pero no es realmente magia compiladora.

No es realmente mágico lo que VBScript, C # dynamic y Delphi están haciendo. Solo estan llamandoIDispatch.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;

El lío está configurando estos parámetros:

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. 

El verdadero truco es eldispParams Estructura, que contiene los argumentos.

El argumento será una variante.

Los argumentos que se pasan a través de DISPPARAMS son todas las variantes:

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

Así que no importa lo que pase, mi"matriz de bytes" va a ser una variante

A VARIANT, en Win32, es simplemente una unión que contiene:

VARTYPE vt: El tipo de datos en la unión.

el miembro de la unión apropiada, por ej .:

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

Hasta ahora he estado pasando una variante de tipo:

vt = VT_ARRAY | VT_UI1

MSDN documenta lo que debe hacer cuando quiere usar elparray unión conVT_ARRAY | *:

Valor: VT_ARRAY | <anything>

Descripción: Se pasó una matriz de tipo de datos. VT_EMPTY y VT_NULL son tipos no válidos para combinar con VT_ARRAY. El puntero enpbyrefVal apunta a un descriptor de matriz, que describe las dimensiones, el tamaño y la ubicación en memoria de la matriz.

Lo que esto significa es que usando elparray miembro:

    SAFEARRAY  *parray;

Necesitas establecerparray miembro a un puntero a unSAFEARRAY estructura:

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

En mi caso, mimatriz de bytes es en realidad unSAFEARRAY, que luego se almacena en una variante:

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;

Nota: Por supuesto, no estoy lo suficientemente loco como para haber creado este safearray yo mismo; estoy usando elSafeArrayCreate función api. Solo estoy demostrando que todo es conocible, y no magia.

En otras palabras, paso una matriz variante de bytes

En otras palabras yoa.m pasando una matriz de bytes, envuelta en una variante, como todas las llamadas a:

dispatch.Invoke(...);

debe ser. Excepto que elencuadernación tardía la llamada lanza un error:

The parameter is incorrect.

Entonces, ¿qué es lo que posiblemente estoy haciendo mal?

Como pasa unomatriz de bytes a uncon retraso IDispatch ¿llamada?

Mi pregunta

¿Cómo pasar SAFEARRAY al objeto COM a través de IDispatch?

Respuestas a la pregunta(1)

Su respuesta a la pregunta