【问题标题】:How to Pass Negative Integer to COM/OLE Function?如何将负整数传递给 COM/OLE 函数?
【发布时间】:2019-07-07 22:28:17
【问题描述】:

我正在 Visual Studio 2010 中开发基于 C++/ATL 的 Microsoft Word 加载项。我还使用基于 MFC 的 COleDispatchDriver 和支持类,并使用 Visual Studio 的 ClassWizard 从 Microsoft Word 生成包装类类型库。下面是 ClassWizard 生成的 Selection.Move 函数的示例包装器。

long Move(VARIANT * Unit, VARIANT * Count)
{
    long result;
    static BYTE parms[] = VTS_PVARIANT VTS_PVARIANT ;
    InvokeHelper(0x6d, DISPATCH_METHOD, VT_I4, (void*)&result, parms, Unit, Count);
    return result;
}

对于上述函数,我还编写了辅助函数来处理 VARIANT 参数传递,如下所示。

long Move(int Unit, int Count)
{
      long result;
      static BYTE parms[] = VTS_PVARIANT VTS_PVARIANT ;

      VARIANT vaUnit;
      ::VariantInit(&vaUnit);
      vaUnit.vt = VT_I4;
      vaUnit.iVal = Unit;

      VARIANT vaCount;
      ::VariantInit(&vaCount);
      vaCount.vt = VT_INT;
      vaCount.iVal = Count;

      InvokeHelper(0x6d, DISPATCH_METHOD, VT_I4, (void*)&result, parms, &vaUnit, &vaCount);

      ::VariantClear(&vaUnit);
      ::VariantClear(&vaCount);

      return result;
}

当我使用 Count 参数的正整数调用我的函数时,Word 会正确响应,例如以下函数调用会将选择“向前”(向文档末尾)移动一个字符。

m_oSelection.Move(1 /* wdCharacter */, 1);

但是,如果我尝试使用以下函数调用将所选内容“向后”移动一个字符(朝向文档的开头),Word 不会按预期响应。

m_oSelection.Move(1 /* wdCharacter */, -1);

“似乎”Word 自动化将整数视为无符号整数,而我的 -1 值变为 65535,导致选择向前跳转。通过InvokeHelper 函数调用检查vaCount 变体,VS 调试器将.iVal 值显示为-1,但vaCount 变体的“值”显示为65535。

为了将负整数作为 COM 函数调用的一部分适当地传递,我缺少什么?

【问题讨论】:

  • 底层函数应该不支持负值。也许是其他方法或不同的Unit 参数...
  • 使用int16_t(65535) 获取-1,或使用VT_I2(而不是VT_I4)指定16 位整数。
  • vaCount 使用 VT_INT 而不是 VT_I4,就像 vaUnit 使用一样。如果将VT_INT 更改为VT_I4,问题会消失吗? .iVal 字段仅对 VT_I2 有效,VT_I4 使用 .lValVT_INT 使用 .intVal。在 .lVal.intVal 中存储 32 位 -1 将用所有 1 位填充所有 4 个字节,当在 .iVal 中截断为 16 位时,它们仍然是全 1,因此仍然是 -1signed 16bit -1unsigned 16bit 65535 的位相同。因此,您需要弄清楚为什么您的 signed 值被 interprettedunsigned 值。
  • 原始辅助函数使用VT_I4 替换vaCount,结果如上所述。我发布的代码来自一个实验,看看如果我切换到使用VT_INT 会发生什么,上面的代码没有按预期运行。但是,当我修复 .iVal/.lVal 错误时,代码现在可以按预期运行!至于为什么VT_I4 不允许我传递一个负整数,这仍然是一个谜……但是@RemyLebeau 如果您将评论移至答案,我很乐意将其标记为正确!
  • @user986713 完成

标签: c++ mfc com atl variant


【解决方案1】:

问题是你误用了VARIANT

您将vaCountvt 字段设置为VT_INT 1 但随后将您的int 值分配给它的.iVal 字段而不是它的.intVal 字段。 .iVal 字段是用于 VT_I2 的 16 位 short,而 .intVal 是用于 VT_INT 的 32 位 int

同样,您将vaUnitvt 设置为VT_I4,但随后也将int 值分配给它的.iVal 字段,而不是它的.lVal 字段,这是一个32 位long.

1:你为什么要使用VT_INT,而不是更传统的VT_I4

试试这个:

long Move(int Unit, int Count)
{
      long result;
      static BYTE parms[] = VTS_PVARIANT VTS_PVARIANT ;

      VARIANT vaUnit;
      ::VariantInit(&vaUnit);
      vaUnit.vt = VT_I4;
      vaUnit.lVal = Unit;

      VARIANT vaCount;
      ::VariantInit(&vaCount);
      vaCount.vt = VT_I4;
      vaCount.lVal = Count;

      InvokeHelper(0x6d, DISPATCH_METHOD, VT_I4, (void*)&result, parms, &vaUnit, &vaCount);

      ::VariantClear(&vaUnit);
      ::VariantClear(&vaCount);

      return result;
}

话虽如此,我建议你使用CComVariant_variant_t 包装类,而不是直接使用VARIANT,让它为你处理这些细节。另外,因为InvokeHelper() 在失败时会抛出异常,所以当它超出范围时让包装器为您调用VariantClear()

long Move(int Unit, int Count)
{
      long result;
      static BYTE parms[] = VTS_PVARIANT VTS_PVARIANT ;

      CComVariant vaUnit(Unit);
      CComVariant vaCount(Count);

      InvokeHelper(0x6d, DISPATCH_METHOD, VT_I4, (void*)&result, parms, &vaUnit, &vaCount);

      return result;
}

long Move(int Unit, int Count)
{
      long result;
      static BYTE parms[] = VTS_PVARIANT VTS_PVARIANT ;

      _variant_t vaUnit(Unit);
      _variant_t vaCount(Count);

      InvokeHelper(0x6d, DISPATCH_METHOD, VT_I4, (void*)&result, parms, &vaUnit, &vaCount);

      return result;
}

【讨论】:

  • 使用 CComVariant 辅助类可以让我省去很多麻烦!感谢您提供有用的评论以及令人难以置信的彻底和有用的答案!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-18
  • 1970-01-01
  • 2019-03-20
  • 1970-01-01
  • 2016-02-01
相关资源
最近更新 更多