Jak przekazać SAFEARRAY do obiektu COM przez IDispatch?

Usiłuję wywołać metodę obiektu COM, gdzie jeden zudokumentowane parametry to„tablica bajtów„. Rzeczywista deklinacja zależy od dokumentacji danego języka:

wDO# język:

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

wC ++ język;

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

wVB język:

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

wFA# język:

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

Dostęp do obiektu, którego używam, można również uzyskać za pomocąCOM. Obiekt zapewnia wczesny interfejs wiązania,ICryptoTransform, który deklaruje metodę jako używanąSAFEARRAY.

Odbiblioteka typów:

za pomocąIDL składnia

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

za pomocąobiekt Pascal składnia:

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

Oznacza to, że podczas używaniawczesne wiązanie musisz przejść metodę aSAFEARRAY. Język, którego używam, obsługuje interfejsy API SafeArray, czy mogę dość łatwo wykonać połączenie:

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

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

Oto ten sam kod w języku java:

PSafeArray inputBuffer;
ICryptoTransform xform;

...

xform.TransformFinalBlock(inputBuffer, ...);

I wszystko działaw porządku; ale to nie moje pytanie.

Uwaga: próbuję wracać do tematu, że to jestagnostyk językowy pytanie, jakCOM jest technologią agnostyczną języka. Ale w pewnym momencie musimy to zrobićposługiwać się język, w którym pokażemy kod.Niektórzy ludzie mylą język z technologią. Gdybym znał wymyślony język Knutha, użyłbym tego.

Ale co z późnym wiązaniemIDispatch?

Teraz, gdy my wiemy, że jesteśmymogą przekazaćSAFEARRAY do obiektu COM (podczas używaniawczesne wiązanie), muszę rozwiązać problem przekazywania tablicy za pomocąpóźne wiązanie.

Uwaga: Pytanie o to, jak przekazać SAFEARRAY do obiektu COM za pomocą IDispatch, jest przydatne w innych okolicznościachICryptoTransform.

przekazując tablicęBSTR do kontroli MSHTMLprzekazanie tablicy bajtów do innych obiektów COM

Niektóre języki zapewniają automatyczne mechanizmy wywoływania metod za pomocąIDispatch interfejs w czasie wykonywania (tj.późne wiązanie). w rzeczywistościIDispatch późne wiązanie zostało wynalezione dla VBScript:

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

I późno wiążąca kompilacja automatycznej magii została dodana w .NET 4.0:

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

Magia kompilatora późnego wiązania istniała również w Delphi:

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

i zdarzyć korzystać z Dephi, a to połączenie nie powiedzie się.

Ale to nie jest magia kompilatora

To naprawdę nie jest magia, co robią VBScript, C # dynamic i Delphi. Oni tylko dzwonią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;

Bałagan konfiguruje te parametry:

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. 

Prawdziwa sztuczka todispParams struktura, która zawiera argumenty.

Argument będzie wariantem

Wszystkie argumenty przekazywane przez DISPPARAMS to wszystkie warianty:

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

Więc nie ważne co się stanie, mój„tablica bajtów” będzie wariantem.

A VARIANT, w Win32, jest po prostu związkiem, który zawiera:

VARTYPE vt: Rodzaj danych w związku.

odpowiedni członek związku, np .:

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

Do tej pory mijałem wariant typu:

vt = VT_ARRAY | VT_UI1

MSDN dokumentuje, co musisz zrobić, gdy chcesz użyćparray związek zVT_ARRAY | *:

Wartość: VT_ARRAY | <anything>

Opis: Przekazano tablicę typu danych. VT_EMPTY i VT_NULL są nieprawidłowymi typami do połączenia z VT_ARRAY. Wskaźnik wpbyrefVal wskazuje deskryptor tablicy, który opisuje wymiary, rozmiar i lokalizację w pamięci tablicy.

Oznacza to, że używającparray członek:

    SAFEARRAY  *parray;

Musisz ustawićparray członek do wskaźnika na aSAFEARRAY Struktura:

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

W moim przypadku, mójtablica bajtów jest w rzeczywistości aSAFEARRAY, który jest następnie przechowywany w wariancie:

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;

Uwaga: Oczywiście, że nie jestem na tyle szalony, że sam stworzyłem tę broń; UżywamSafeArrayCreate funkcja api. Po prostu pokazuję, że to wszystko jest poznawalne, a nie magiczne.

Innymi słowy, przekazuję wariant tablicy bajtów

Innymi słowy:rano przekazanie tablicy bajtów, zapakowanych w wariancie, jako wszystkie wywołania do:

dispatch.Invoke(...);

musi być. Tyle żepóźne wiązanie wywołanie powoduje błąd:

The parameter is incorrect.

Więc co robię źle?

Jak przejśćtablica bajtu do aspóźniony IDispatch połączenie?

Moje pytanie

Jak przekazać SAFEARRAY do obiektu COM przez IDispatch?

questionAnswers(1)

yourAnswerToTheQuestion