如何构建一个指向VARIANT的指针的SAFEARRAY?

7

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

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

如何在C/C++中创建paramValues数组?
2个回答

8
为了方便日后读者参考,以下是问题的答案: 在IDL中,SAFEARRAY(...)表示一个指向数组描述符的指针。 但在C++中,SAFEARRAY表示一个数组描述符。 因此,IDL的SAFEARRAY(...)实际上相当于C++的SAFEARRAY *。这让我感到非常困惑。 更有趣的是,VB总是通过引用传递数组。因此,在C++中,VB的() As LongSAFEARRAY<int32_t> **。(我不知道是否实际上存在一个通用的头文件,允许你指定类型作为模板参数,但出于清晰起见,我插入了它。)

+1 对于在IDL上下文中的SAFEARRAY和C/C++结构中的SAFEARRAY之间差异的评论。 - meklarian

7
定义SAFEARRAY(VARIANT *)并不完全正确。它在IDL中声明为SAFEARRAY(VARIANT),但从锁定SAFEARRAY可用的指针实际上是VARIANT *。如果您花一点时间考虑这个问题,它应该会更有意义。 SAFEARRAY的索引指针(pvData成员)不可能适合其物理位置的整个VARIANT,因此至少应该能够存储可用于索引到VARIANT数组的指针。
如果您查看,约在第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)
 */

这里有一个头文件的副本链接。

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

我该如何将字符串数组从VB脚本传递给SAFEARRAY方法?您能否提供一个示例? - Venkataramana Madugula

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接