我試圖使用以下方法COM組件:如何構建指向VARIANT的SAFEARRAY?
HRESULT _stdcall Run(
[in] SAFEARRAY(BSTR) paramNames,
[in] SAFEARRAY(VARIANT *) paramValues
);
我怎麼能在C/C++的paramValues陣列創建?
我試圖使用以下方法COM組件:如何構建指向VARIANT的SAFEARRAY?
HRESULT _stdcall Run(
[in] SAFEARRAY(BSTR) paramNames,
[in] SAFEARRAY(VARIANT *) paramValues
);
我怎麼能在C/C++的paramValues陣列創建?
定義SAFEARRAY(VARIANT *)不完全正確。它在IDL中聲明爲SAFEARRAY(VARIANT),但鎖定SAFEARRAY的指針實際上是VARIANT *。如果你暫時考慮這個問題,它應該更有意義。 SAFEARRAY(pvData成員)的索引指針不可能適合整個VARIANT的物理位置,所以至少它應該能夠存儲可用於索引到VARIANT數組中的指針。
如果你看<wtypes.h>,在1110+行的某處你會看到VT_枚舉定義。它也顯示那裏VT_VARIANT實際上暗示VARIANT *。 [S]標籤也很方便標記SAFEARRAY中可能出現的項目。
/*
* VARENUM usage key,
*
* * [V] - may appear in a VARIANT
* * [T] - may appear in a TYPEDESC
* * [P] - may appear in an OLE property set
* * [S] - may appear in a Safe Array
*
*
* VT_EMPTY [V] [P] nothing
* VT_NULL [V] [P] SQL style Null
* VT_I2 [V][T][P][S] 2 byte signed int
* VT_I4 [V][T][P][S] 4 byte signed int
* VT_R4 [V][T][P][S] 4 byte real
* VT_R8 [V][T][P][S] 8 byte real
* VT_CY [V][T][P][S] currency
* VT_DATE [V][T][P][S] date
* VT_BSTR [V][T][P][S] OLE Automation string
* VT_DISPATCH [V][T] [S] IDispatch *
* VT_ERROR [V][T][P][S] SCODE
* VT_BOOL [V][T][P][S] True=-1, False=0
* VT_VARIANT [V][T][P][S] VARIANT *
... (remaining definitions omittted)
*/
這是一個指向頭文件副本的鏈接。
從這裏出發,您只需聲明SAFEARRAY具有一個變體類型VT_VARIANT的,然後把pvData爲VARIANT *鎖定數組的時候。下面是一個示例win32控制檯應用程序的源代碼,通過調用與您的函數匹配相同聲明的函數來演示此應用程序。
#include "stdafx.h"
#include "SFAComponent.h"
#include "SFAComponent_i.c"
int _tmain(int argc, _TCHAR* argv[])
{
::CoInitialize(NULL);
SAFEARRAYBOUND nameBounds;
nameBounds.cElements = 2;
nameBounds.lLbound = 0;
LPSAFEARRAY psaNames = SafeArrayCreate(VT_BSTR, 1, &nameBounds);
BSTR bstrApple = SysAllocString(L"apple");
BSTR bstrOrange = SysAllocString(L"orange");
SafeArrayLock(psaNames);
BSTR *nameArray = (BSTR *)psaNames->pvData;
nameArray[0] = bstrApple;
nameArray[1] = bstrOrange;
SafeArrayUnlock(psaNames);
SAFEARRAYBOUND valueBounds;
valueBounds.cElements = 2;
valueBounds.lLbound = 0;
LPSAFEARRAY psaValues = SafeArrayCreate(VT_VARIANT, 1, &valueBounds);
SafeArrayLock(psaValues);
VARIANT *valueArray = (VARIANT *)psaValues->pvData;
VariantClear(&valueArray[0]);
VariantClear(&valueArray[1]);
valueArray[0].vt = VT_BSTR;
valueArray[0].bstrVal = SysAllocString(L"hello");
valueArray[1].vt = VT_I4;
valueArray[1].iVal = 42;
{
CComPtr<ITestReader> p;
p.CoCreateInstance(CLSID_TestReader);
p->Run(psaNames, psaValues);
p.Release(); // not explicitly necessary.
}
SafeArrayDestroy(psaValues);
SafeArrayDestroy(psaNames);
::CoUninitialize();
return 0;
}
通過此測試應用程序調用的組件可通過創建一個ATL DLL項目,並添加稱爲「TestReader」簡單ATL對象被創建。
以下是ITestReader的IDL。
[
object,
uuid(832EF93A-18E8-4655-84CA-0BA847B52B77),
dual,
nonextensible,
helpstring("ITestReader Interface"),
pointer_default(unique),
oleautomation
]
interface ITestReader : IDispatch{
[id(1), helpstring("method Run")] HRESULT Run([in] SAFEARRAY(BSTR) paramNames, [in] SAFEARRAY(VARIANT) paramValues);
};
對應於IDL聲明的成員函數只需要SAFEARRAY *(或LPSAFEARRAY)參數。
public:
STDMETHOD(Run)(LPSAFEARRAY paramNames, LPSAFEARRAY paramValues);
這裏是方法的主體。爲簡潔起見,還包括一個輔助函數PrintVariant()。
void PrintVariant(VARIANT *pV)
{
switch(pV->vt)
{
case VT_BSTR:
wprintf(L" BSTR: %s\r\n", pV->bstrVal);
break;
case VT_I4:
wprintf(L" Integer: %d\r\n", pV->iVal);
break;
default:
wprintf(L" Unrecognized Type: vt=%d\r\n", pV->vt);
break;
}
}
STDMETHODIMP CTestReader::Run(LPSAFEARRAY paramNames, LPSAFEARRAY paramValues)
{
SafeArrayLock(paramNames);
SafeArrayLock(paramValues);
BSTR *nameArray = (BSTR *)paramNames->pvData;
VARIANT *valueArray = (VARIANT *)paramValues->pvData;
wprintf(L"Item 0 is %s, variant type %d\r\n", nameArray[0], valueArray[0].vt);
PrintVariant(&valueArray[0]);
wprintf(L"Item 1 is %s, variant type %d\r\n", nameArray[1], valueArray[1].vt);
PrintVariant(&valueArray[1]);
SafeArrayUnlock(paramNames);
SafeArrayUnlock(paramValues);
return S_OK;
}
上述用於由未來的讀者參考答案: 在IDL,SAFEARRAY(...)
指一個指針數組描述符。 但是在C++中,SAFEARRAY
意味着一個數組描述符。因此,IDL的SAFEARRAY(...)
真的是C++的SAFEARRAY *
。這讓我困惑不已。爲了讓事情更有趣,VB總是通過引用傳遞數組。所以VB的() As Long
是在C++中的SAFEARRAY<int32_t> **
。 (我不知道是否實際上有一個常用的標題,允許您指定類型作爲模板參數,但爲了清晰起見,我插入了它。)
對IDL上下文中的SAFEARRAY與SAFEARRAY C/C++結構之間的差異發表評論+1 – meklarian 2011-06-07 18:29:07
如何將字符串數組從VB腳本傳遞到SAFEARRAY方法?你能舉個例子嗎? – 2018-03-09 10:29:44