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

发布于 2024-08-12 01:04:11 字数 220 浏览 8 评论 0原文

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

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

How can I create in C/C++ the paramValues array?

I'm trying to use a COM component with the following method:

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

How can I create in C/C++ the paramValues array?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

哑剧 2024-08-19 01:04:11

添加以上答案以供未来读者参考:
在 IDL 中,SAFEARRAY(...) 表示指向数组描述符的指针。
但在 C++ 中,SAFEARRAY 表示数组描述符。
因此,IDL 的 SAFEARRAY(...) 实际上是 C++ 的 SAFEARRAY *。这让我困惑不已。
更有趣的是,VB 总是通过引用传递数组。所以VB的() As LongSAFEARRAY; ** 在 C++ 中。 (我不知道是否真的有一个常用的标头允许您将类型指定为模板参数,但为了清楚起见我插入了它。)

Adding to the answers above for reference by future readers:
In IDL, SAFEARRAY(...) means a pointer to an array descriptor.
But in C++, SAFEARRAY means an array descriptor.
So IDL's SAFEARRAY(...) is really C++'s SAFEARRAY *. This confused me to no end.
To make things even more interesting, VB always passes arrays by reference. So VB's () As Long is SAFEARRAY<int32_t> ** in C++. (I don't know if there actually is a commonly used header that allows you to specify the type as a template parameter, but I inserted it for clarity.)

月亮坠入山谷 2024-08-19 01:04:11

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)
 */

这是头文件副本的链接。

DOC.DDART.NET 上的 wtypes.h

从这里开始,您只需声明一个具有 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;
}

The definition SAFEARRAY(VARIANT *) is not quite correct. It is declared in an IDL as SAFEARRAY(VARIANT), but the pointer available from locking the SAFEARRAY is actually a VARIANT *. If you think about this for a moment, it should make some more sense. The index pointer of a SAFEARRAY (the pvData member) can't possibly fit an entire VARIANT in its physical location, so at the very least, it should be able to store a pointer that may be used to index into an array of VARIANTs.

If you look at <wtypes.h>, somewhere about line 1110+ you'll see the VT_ enumeration definitions. It is also shown there that VT_VARIANT actually implies VARIANT *. Also handy are the [S] tags noting what items may appear in a 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)
 */

Here's a link to a copy of the header file.

wtypes.h at DOC.DDART.NET

Proceeding from here, you would simply declare a SAFEARRAY with a variant type of VT_VARIANT, then treat pvData as VARIANT * when locking the array. Here is the source code for a sample win32 console app that demonstrates this by calling a function matching the same declaration as your function.

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

The component called by this test app can be created by creating an ATL dll project, and adding a simple ATL object called 'TestReader'.

Here's the IDL for ITestReader.

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

The member function corresponding to the IDL declaration just takes SAFEARRAY * (or LPSAFEARRAY) arguments.

public:
  STDMETHOD(Run)(LPSAFEARRAY paramNames, LPSAFEARRAY paramValues);

Here is the body of the method. Also included is a helper function PrintVariant() for brevity.

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文