4

通过 COM 接收和发送数组的正确方法是什么?到目前为止,这是我的尝试:一个包含在变体中的双精度数的安全数组。

//takes variant holding safearray of doubles
//returns a similar variant having multipled every element by 2
STDMETHODIMP MyComClass::safearraytimestwo(VARIANT in, VARIANT* out)
{
    CComSafeArray<double> sa_in;
    sa_in.Attach(*in.pparray);
    ULONG size = sa_in.GetCount();

    CComSafeArray<double> *out_sa = new CComSafeArray<double>(size);

    for (long i=0;i<size;i++)
        out_sa->SetAt(i,sa_in[i]*2);

    out = new CComVariant(out_sa);
    return S_OK;
}

问题: - 当前编译在循环操作上失败:error C2679: binary '=' : no operator found which takes a right-hand operand of type 'ATL::_ATL_AutomationType<DOUBLE>::_typewrapper' (or there is no acceptable conversion) 编辑:使用SetAt()而不是operator[]解决 - 我应该out_sa在堆上声明吗?当被解除分配时它会out被解除分配(我只能假设客户会这样做吗?)

任何帮助将不胜感激!

编辑 2:这是一个部分实现,它试图返回一个安全数组。

STDMETHODIMP CSpatialNet::array3(VARIANT in, VARIANT* out)
{
    CComSafeArray<double> out_sa;
    out_sa.Create(2);
    out_sa.SetAt(0,1.2);
    out_sa.SetAt(1,3.4);
    *out = CComVariant(out_sa);
    out_sa.Detach();
    return S_OK;
}

这也失败了;lisp 报告

(vl-load-com)
(setq n (vlax-create-object "sdnacomwrapper.SpatialNet"))
(setq v (vlax-make-variant 1.0))
(vlax-invoke-method n 'array3 v 'newvar)
; error: ActiveX Server returned an error: The parameter is incorrect

CComSafeArray<double>用一组变体替换会产生相同的错误。

4

3 回答 3

6

得到这个工作 - 我的代码是这样的(编辑:虽然显然不是没有错误 - 见迪特里希的回答):

STDMETHODIMP MyComClass::arraytimestwo(VARIANT in, VARIANT* out)
{
    CComSafeArray<double> sa_in;
    sa_in.Attach(in.parray);
    ULONG size = sa_in.GetCount();
    CComSafeArray<double> out_sa;
    out_sa.Create(size);
    for (long i=0;i<size;i++)
        out_sa.SetAt(i,sa_in.GetAt(i)*2);

    CComVariant(out_sa).Detach(out);
    return S_OK;
}

在 Lisp 中...

(vl-load-com)
(setq n (vlax-create-object "mycomdll.MyComClass"))
(setq sa (vlax-make-safearray vlax-vbDouble '(0 . 1)))
(vlax-safearray-fill sa '(1 2))
(vlax-safearray->list sa)
(vlax-invoke-method n 'arraytimestwo sa 'newvar)
(vlax-safearray->list newvar)

最初的尝试有特别错误的地方:

  • 需要使用Detach方法来赋值out
  • 需要附加到in.parray*in.pparray(不是同一件事)
于 2012-02-14T16:05:21.330 回答
2

采用 VARIANT 参数的 COM 方法负责检查参数、捕获异常并且它不会实际破坏[in]数组,因此在 C++ 端更准确的实现将是:

STDMETHODIMP Foo(VARIANT in, VARIANT* out)
{
    _ATLTRY
    {
        ATLENSURE_THROW(in.vt == (VT_ARRAY | VT_R8), E_INVALIDARG);
        ATLENSURE_THROW(out, E_POINTER);
        VariantInit(out);
        CComSafeArray<DOUBLE>& sa_in =
            reinterpret_cast<CComSafeArray<DOUBLE>&>(in.parray);
        ULONG size = sa_in.GetCount();
        CComSafeArray<DOUBLE> out_sa;
        ATLENSURE_SUCCEEDED(out_sa.Create(size));
        for(ULONG nIndex = 0; nIndex < size; nIndex++)
            out_sa.SetAt(nIndex, sa_in.GetAt(nIndex) * 2);
        // NOTE: Constructor copies data so it's accurate just inefficient
        ATLVERIFY(SUCCEEDED(CComVariant(out_sa).Detach(out)));
    }
    _ATLCATCH(Exception)
    {
        return Exception;
    }
    return S_OK;
}
于 2013-12-27T11:44:36.540 回答
1

Sideshow Bob 和 Roman R. 的解决方案使用

ComVariant(out_sa).Detach(out);

这有一个严重的缺点。被 SAFEARRAY out_sa传递给CComVariant's构造函数,构造函数将复制SAFEARRAY. 为了避免复制更好地使用

::VariantInit(out);
out->vt = (VT_ARRAY | VT_R8);
out->parray = out_sa.Detach();

正如 Roman 所指出的,您还应该从检查是否in真的是 type开始VT_ARRAY | VT_R8。Bob 的解决方案有一个严重的错误:in.parray附加到sa_in但未分离,因此析构函数将销毁in.parray。但是根据 COM 的规则,函数arraytimestwo(VARIANT in,...是不允许修改参数的in。COM充满了陷阱。因此,我认为最好in通过引用传递参数。

我给出了一个(希望如此!)改进的解决方案和一个测试功能:

STDMETHODIMP arraytimestwo(const VARIANT &in, VARIANT* out)
{
  try
  {
    if (in.vt != (VT_ARRAY | VT_R8)) return E_INVALIDARG;
    CComSafeArray<double> sa_out;
    variant_t wrapCopyIn(in);
    sa_out.Attach(wrapCopyIn.parray);
    if (sa_out.GetDimensions() > 1) return E_INVALIDARG;

    for (long i = sa_out.GetLowerBound(0); i <= sa_out.GetUpperBound(0); i++)
       sa_out[i] *= 2;
    //Don't forget
    sa_out.Detach();

    *out = wrapCopyIn.Detach();
  }
  catch (const CAtlException& e)
  {
    // Exception object implicitly converted to HRESULT,
    // and returned as an error code to the caller
    return e;
  }
  return S_OK;
}

void TestArraytimestwo()
{
  CComSafeArray<double> vec(5, 1);
  for (int i = vec.GetLowerBound(); i <= vec.GetUpperBound(); i++) vec[i] = i * 1.1;

  variant_t in, out;
  in.vt = (VT_ARRAY | VT_R8);
  in.parray = vec.Detach();

  if (!SUCCEEDED(arraytimestwo(in, &out)))
  {
   std::cout << "Something went wrong!" << "\n";
   return;
  }

  CComSafeArray<double> sa_out;
  sa_out.Attach(out.parray);
  vec.Attach(in.parray);
  for (int i = vec.GetLowerBound(); i <= vec.GetUpperBound(); i++)
     std::cout << vec[i] << "  " << sa_out[i] << std::endl;

  //Not necessary, but I do it out of habit
  vec.Detach();
  sa_out.Detach();
}

备注:Bob原来的函数应该是这样的(跳过try ... catch)

STDMETHODIMP arraytimestwoBob(const VARIANT &in, VARIANT* out)
{
  CComSafeArray<double> sa_in;
  sa_in.Attach(in.parray);
  CComSafeArray<double> out_sa(sa_in.GetCount(), sa_in.GetLowerBound());
  for (long i = sa_in.GetLowerBound(); i <= sa_in.GetUpperBound(); i++) out_sa[i] = 2 * sa_in[i];
  sa_in.Detach();//Detach, this function doesn't own in 
  ::VariantInit(out);
  out->vt = (VT_ARRAY | VT_R8);
  out->parray = out_sa.Detach();
  return S_OK;
}
于 2020-03-06T21:29:18.863 回答