如何建立一个指向VARIANTs的SAFEARRAY指针?

时间:2009-11-25 10:48:18

标签: c++ com variant safearray

我正在尝试使用以下方法的COM组件:

HRESULT _stdcall Run(
    [in] SAFEARRAY(BSTR) paramNames,
    [in] SAFEARRAY(VARIANT *) paramValues
    );

如何在C / C ++中创建paramValues数组?

2 个答案:

答案 0 :(得分:8)

添加上述答案供未来读者参考: 在IDL中,SAFEARRAY(...)表示指向数组描述符的指针。 但在C ++中,SAFEARRAY表示数组描述符。 所以IDL的SAFEARRAY(...)实际上是C ++的SAFEARRAY *。这使我无所适从。 为了使事情变得更有趣,VB总是通过引用传递数组。所以VB的() As Long在C ++中是SAFEARRAY<int32_t> **。 (我不知道是否有一个常用的标题允许您将类型指定为模板参数,但为了清楚起见我插入了它。)

答案 1 :(得分:6)

定义SAFEARRAY(VARIANT *)不太正确。它在IDL中声明为SAFEARRAY(VARIANT),但锁定SAFEARRAY时可用的指针实际上是VARIANT *。如果你想一下这一点,它应该更有意义。 SAFEARRAY(pvData成员)的索引指针不可能在其物理位置上适合整个VARIANT,因此至少应该能够存储可用于索引VARIANT数组的指针。

如果你看一下关于1110+行的&lt; wtypes.h&gt;,你会看到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)
 */

这是指向头文件副本的链接。

wtypes.h at DOC.DDART.NET

从这里开始,您只需声明一个变体类型为VT_VARIANT的SAFEARRAY,然后在锁定数组时将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;
}