如何将负整数传递给 COM/OLE 函数?

How to Pass Negative Integer to COM/OLE Function?

我正在 Visual Studio 2010 年开发基于 C++/ATL 的 Microsoft Word 加载项。我也在使用基于 MFC 的 COleDispatchDriver 并支持 类 并使用Visual Studio 的类向导从 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 会正确响应,例如以下函数调用会将选择 "forward"(向文档末尾)移动一个字符。

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

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

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

它 "seems" 就像 Word 自动化将整数视为无符号整数,我的 -1 值变为 65535 导致选择向前跳转。通过 InvokeHelper 函数调用检查行上的 vaCount 变体,VS 调试器将 .iVal 值显示为 -1,但 vaCount 的 "value"变体显示为 65535。

为了在 COM 函数调用中适当地传递负整数,我错过了什么?

问题是您误用了 VARIANT

您正在将 vaCountvt 字段设置为 VT_INT 1,但随后将您的 int 值分配给它的 .iVal 字段而不是 .intVal 字段。 .iVal 字段是用于 VT_I2 的 16 位 short,而 .intVal 是用于 VT_INT.[=41 的 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 or _variant_t 包装器 class 而不是直接使用 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;
}