Ian*_*oyd 6 com winapi variant safearray
我试图调用COM对象的方法,其中一个记录的参数是"字节数组 ".实际声明取决于您正在查看的每种语言文档:
在C#语言中:
byte[] TransformFinalBlock(
byte[] inputBuffer,
int inputOffset,
int inputCount
)
Run Code Online (Sandbox Code Playgroud)用C++语言;
array<unsigned char>^ TransformFinalBlock(
array<unsigned char>^ inputBuffer,
int inputOffset,
int inputCount
)
Run Code Online (Sandbox Code Playgroud)用VB语言:
Function TransformFinalBlock ( _
inputBuffer As Byte(), _
inputOffset As Integer, _
inputCount As Integer _
) As Byte()
Run Code Online (Sandbox Code Playgroud)在F#语言中:
abstract TransformFinalBlock :
inputBuffer:byte[] *
inputOffset:int *
inputCount:int -> byte[]
Run Code Online (Sandbox Code Playgroud)我正在使用的对象也可以使用COM访问.该对象提供了一个早期绑定接口ICryptoTransform,它将该方法声明为using SAFEARRAY.
从类型库:
使用IDL语法
[
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);
};
Run Code Online (Sandbox Code Playgroud)使用对象Pascal语法:
ICryptoTransform = interface(IDispatch)
['{8ABAD867-F515-3CF6-BB62-5F0C88B3BB11}']
...
function TransformFinalBlock(inputBuffer: PSafeArray; inputOffset: Integer; inputCount: Integer): PSafeArray; safecall;
end;
Run Code Online (Sandbox Code Playgroud)这意味着在使用早期绑定时,您必须传递方法a SAFEARRAY.我使用的语言支持SafeArray API,我可以轻松地执行调用:
var
inputBuffer: PSafeArray;
xform: ICryptoTransform;
...
begin
...
xform.TransformFinalBlock(inputBuffer, ...);
...
end;
Run Code Online (Sandbox Code Playgroud)
这是类似java语言的相同代码:
PSafeArray inputBuffer;
ICryptoTransform xform;
...
xform.TransformFinalBlock(inputBuffer, ...);
Run Code Online (Sandbox Code Playgroud)
一切正常 ; 但这不是我的问题.
注意:我正在尝试将驱动器归结为这是一个与语言无关的问题,因为COM是一种与语言无关的技术.但在某些时候,我们必须实际使用一种我们将演示代码的语言.有些人将语言与技术混淆.如果我知道Knuth发明的语言,我就会用它.
IDispatch呢?既然我们知道我们可以传递SAFEARRAY给一个COM对象(当使用早期绑定时),我需要解决使用后期绑定传递数组的问题.
注意:如何通过IDispatch将SAFEARRAY传递给COM对象的问题对我来说很有用
ICryptoTransform.
有些语言提供了自动机制,可以IDispatch在运行时通过接口调用方法(即后期绑定).事实上,IDispatch后期绑定是为VBScript发明的:
Dim xform = CreateObject("System.Security.Cryptography.SHA256Managed");
Dim buffer;
o.TransformFinalBlock(buffer, 0, 8);
Run Code Online (Sandbox Code Playgroud)
在.NET 4.0中添加了后期绑定编译器自动魔术:
dynamic xform = Activator.CreateInstance(Type.GetTypeFromProgID("System.Security.Cryptography.SHA256Managed", true));
xform.TransformFinalBlock(buffer, 0, 8);
Run Code Online (Sandbox Code Playgroud)
Delphi中也存在后期绑定编译器魔法:
xform: OleVariant;
buffer: OleVariant;
xform.TransformFinalBlock(buffer, 0, 8);
Run Code Online (Sandbox Code Playgroud)
我碰巧使用的是Dephi,这次调用失败了.
VBScript,C#动态和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;
Run Code Online (Sandbox Code Playgroud)
混乱是设置这些参数:
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.
Run Code Online (Sandbox Code Playgroud)
真正的技巧是dispParams包含参数的结构.
通过DISPPARAMS传递的参数都是变体:
typedef struct tagDISPPARAMS {
VARIANTARG *rgvarg;
DISPID *rgdispidNamedArgs;
UINT cArgs;
UINT cNamedArgs;
} DISPPARAMS;
Run Code Online (Sandbox Code Playgroud)
所以无论发生什么,我的"字节数组"都将是一个变种.
VARIANT在Win32中,A 只是一个包含以下内容的联合:
VARTYPE vt:联合中的数据类型.适当的工会会员,例如:
BYTE bVal;
IDispatch *pdispVal;
SAFEARRAY *parray;
BYTE *pbVal;
IDispatch *ppdispVal;
SAFEARRAY *pparray;
VARIANT *pvarVal;
PVOID byref;
CHAR cVal;
Run Code Online (Sandbox Code Playgroud)到目前为止,我一直在传递一种类型的变体:
vt = VT_ARRAY | VT_UI1
Run Code Online (Sandbox Code Playgroud)
MSDN记录了当您想要使用parray联合时必须执行的操作VT_ARRAY | *:
价值:
VT_ARRAY | <anything>描述:传递了一组数据类型.VT_EMPTY和VT_NULL是与VT_ARRAY组合的无效类型.pbyrefVal中的指针指向数组描述符,该描述符描述数组的维度,大小和内存位置.
这意味着使用该parray成员:
SAFEARRAY *parray;
Run Code Online (Sandbox Code Playgroud)
您需要将parray成员设置为指向SAFEARRAY结构的指针:
typedef struct tagSAFEARRAY {
USHORT cDims;
USHORT fFeatures;
ULONG cbElements;
ULONG cLocks;
PVOID pvData;
SAFEARRAYBOUND rgsabound[1];
} SAFEARRAY, *LPSAFEARRAY;
Run Code Online (Sandbox Code Playgroud)
在我的例子中,我的字节数组实际上是a 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;
Run Code Online (Sandbox Code Playgroud)
注意:当然,我自己并没有疯狂创造这个安全阵列; 我正在使用
SafeArrayCreateapi功能.我只是在证明这一切都是可知的,而不是魔术.
换句话说,我正在传递的字节的阵列,包装在一个变型中,因为所有的调用:
dispatch.Invoke(...);
Run Code Online (Sandbox Code Playgroud)
一定是.除了后期绑定调用抛出错误:
The parameter is incorrect.
Run Code Online (Sandbox Code Playgroud)
那么我可能做错了什么?
如何将一个字节数组传递给后期绑定 IDispatch调用?
如何通过IDispatch将SAFEARRAY传递给COM对象?
这应该会给你一些见解:
在调用方,C#代码:
Foo foo = new Foo();
byte[] input = new byte[] { 1, 2, 3, 4 };
byte[] output = foo.Bar(input);
byte[] referenceOutput = new byte[] { 4, 3, 2, 1 };
Debug.Assert(Enumerable.SequenceEqual(output, referenceOutput));
Run Code Online (Sandbox Code Playgroud)
IDL Foo.Bar:
interface IFoo : IDispatch
{
[id(1)] HRESULT Bar([in] VARIANT vInput, [out, retval] VARIANT* pvOutput);
};
Run Code Online (Sandbox Code Playgroud)
以及带有安全数组的 C++ (ATL) 服务器实现:
// IFoo
STDMETHOD(Bar)(VARIANT vInput, VARIANT* pvOutput) throw()
{
_ATLTRY
{
ATLENSURE_THROW(vInput.vt == (VT_ARRAY | VT_UI1), E_INVALIDARG);
CComSafeArray<BYTE> pInputArray(vInput.parray);
ATLASSERT(pInputArray.GetDimensions() == 1);
const ULONG nCount = pInputArray.GetCount();
CComSafeArray<BYTE> pOutputArray;
ATLENSURE_SUCCEEDED(pOutputArray.Create(nCount));
for(ULONG nIndex = 0; nIndex < nCount; nIndex++)
pOutputArray[(INT) nIndex] = pInputArray[(INT) ((nCount - 1) - nIndex)];
ATLASSERT(pvOutput);
VariantInit(pvOutput);
CComVariant vOutput(pOutputArray.Detach());
ATLVERIFY(SUCCEEDED(vOutput.Detach(pvOutput)));
}
_ATLCATCH(Exception)
{
return Exception;
}
return S_OK;
}
Run Code Online (Sandbox Code Playgroud)
来源:Trac、Subversion - 当心 Visual Studio 2012。