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
.
BSTR
do kontroli MSHTMLprzekazanie tablicy bajtów do innych obiektów COMNiektó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 kompilatoraTo 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.
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: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?
Jak przekazać SAFEARRAY do obiektu COM przez IDispatch?