首页 > 解决方案 > 如何将负整数传递给 COM/OLE 函数?

问题描述

我正在使用 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 导致选择向前跳转。使用函数调用检查vaCount行上的变体InvokeHelper,VS 调试器将.iVal值显示为 -1,但vaCount变体的“值”显示为 65535。

在 COM 函数调用中适当地传递负整数,我缺少什么?

标签: c++mfccomatlvariant

解决方案


问题是您在滥用VARIANT.

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

同样,您将vaUnit's设置vtVT_I4但随后也将您的int值分配给它的.iVal字段,而不是它的.lVal字段,它是一个 32bit 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;
}

话虽如此,我建议你使用CComVariantor_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;
}

推荐阅读