【问题标题】:Returning values C#/COM to C++ client?将值 C#/COM 返回到 C++ 客户端?
【发布时间】:2012-05-16 14:52:54
【问题描述】:

我们有一个旧的 C++ COM .dll,客户从他们的 C++ 客户端调用它。

我们正在尝试用一个用 .NET 编写的新 COM 注册的替换旧的 .dll。

C++ 客户端可以调用我们的新 .dll,但在调用特定方法后崩溃。

似乎我们在变量“outNoOfChildren”或下面的 outChildList 数组中返回了错误。

(客户端中的错误消息为:“捕获到未知异常。 测量的子节点数:-2147467259 限制:= 2",它需要 2 个,我们正在尝试返回。)

奇怪的是,我们的新 .NET .dll 中的方法调用似乎在我们拥有的另一个测试客户端 (VB6) 上工作。

这是我们试图替换的 C++ .dll 中的函数:

STDMETHODIMP marcomBase::XgetChildren(VARIANT inUser,
                                  VARIANT inId,
                                  VARIANT *outNoOfChildren,
                                  VARIANT *outChildList,
                                  VARIANT *outErrMsg,
                                  VARIANT *status)
  long stat= 1;
  char user[255+1];
  char id[255+1];
  long noOfChildren = 0;
  char errMsg[255+1] = "";
  _bstr_t temp_bstr;
  long inparamType;
  long dumInt;
  SAFEARRAY *pArray;
  long ix[2];
  VARIANT var;
  SAFEARRAYBOUND rgsabound[2];
  ChildT childList;
  ChildT *childListTemp = NULL;
  ChildT *childListTempNext = NULL;

  childList.nextChild = NULL;

  getInParam( inUser, &inparamType, user, &dumInt);
  if (inparamType != VT_BSTR)
  {
    strcpy(errMsg, "Parameter 1 incorrect, type must be VT_BSTR");
    stat = 0;
  }

  if (stat == 1)
  {
    getInParam( inId, &inparamType, id, &dumInt);
    if (inparamType != VT_BSTR)
    {
      strcpy(errMsg, "Parameter 2 incorrect, type must be VT_BSTR");
      stat = 0;
    }
  }

  if (stat == 1)
  {
    stat = barApiObj.getChildren(user, id, &noOfChildren, childList, errMsg);
  }

  outNoOfChildren->vt = VT_I4;
  outNoOfChildren->lVal = noOfChildren;

  rgsabound[0].lLbound = 1;
  rgsabound[0].cElements = noOfChildren;
  rgsabound[1].lLbound = 1;
  rgsabound[1].cElements = 3;

  pArray = ::SafeArrayCreate(VT_VARIANT, 2, rgsabound);
  outChildList->vt = VT_ARRAY|VT_VARIANT;
  outChildList->parray = pArray;

  ix[0] = 1;

  childListTemp = childList.nextChild;

  while (childListTemp != NULL)
  {
    temp_bstr = childListTemp->child;
    var.vt = VT_BSTR;
    var.bstrVal = temp_bstr.copy();
    ix[1] = 1;
    ::SafeArrayPutElement(pArray, ix, &var);

    temp_bstr = childListTemp->prodNo;
    var.vt = VT_BSTR;
    var.bstrVal = temp_bstr.copy();
    ix[1] = 2;
    ::SafeArrayPutElement(pArray, ix, &var);

    temp_bstr = childListTemp->rev;
    var.vt = VT_BSTR;
    var.bstrVal = temp_bstr.copy();
    ix[1] = 3;
    ::SafeArrayPutElement(pArray, ix, &var);

    ix[0] = ix[0] + 1;

    childListTempNext = childListTemp->nextChild;
    delete childListTemp;
    childListTemp = childListTempNext;
  }

  temp_bstr = errMsg;
  outErrMsg->vt = VT_BSTR;
  outErrMsg->bstrVal = temp_bstr.copy();

  status->vt = VT_I4;
  status->lVal = stat;

  return S_OK;
}

这是我们编写的 .NET .dll:

.NET 接口:

...
[DispId(12)]
 [Description("method XgetChildren")]
object XgetChildren(object inUser, object inId, out object outNoOfChildren, out object outChildList, out object outErrMsg);
...

.NET 类

public object XgetChildren(object inUser, object inId, out object outNoOfChildren, out object outChildList, out object outErrMsg)
{
    outNoOfChildren = 0;
    outChildList = null;
    outErrMsg = "";

    List<IndividualInfo> children = null;
    try
    {
        PrevasMesExternalServiceClient client = getClient();
        children = client.GetChildren(new SerialNo() { Number = inId.ToString() });
    }
    catch (Exception ex)
    {
       return Constants.STATUS_FAIL;
    }

    int[] myLengthsArray = { children.Count, 3 }; //Length of each array 
    int[] myBoundsArray = { 1, 1 }; //Start index of each array

    Array myArray = Array.CreateInstance(typeof(string), myLengthsArray, myBoundsArray); //Create 1-based array of arrays

    for (int i = 0; i < children.Count; i++)
    {
        myArray.SetValue(Utils.getStrValue(children[i].SerialNumber, Constants.pmesIDNO_LENGTH), i+1, 1);
        myArray.SetValue(Utils.getStrValue(children[i].ProductNumber, Constants.pmesPRODUCTNO_LENGTH), i+1, 2);
        myArray.SetValue(Utils.getStrValue(children[i].Revision, Constants.pmesRSTATE_LENGTH), i+1, 3);
    }

    outChildList = myArray;
    outNoOfChildren = children.Count;

    return Constants.STATUS_OK;
}

更新: 原始 C++ .dll 在 OLE/COM 对象查看器中如下所示:

    [id(0x0000000c), helpstring("method XgetChildren")]
    HRESULT XgetChildren(
                    [in] VARIANT inUser, 
                    [in] VARIANT inId, 
                    [out] VARIANT* outNoOfChildren, 
                    [out] VARIANT* outChildList, 
                    [out] VARIANT* outErrMsg, 
                    [out, retval] VARIANT* status);

注册我们的新 .NET .dll 后,它在 OLE/COM 对象查看器中如下所示:

    [id(0x0000000c), helpstring("method ")]
    HRESULT XgetChildren(
                    [in] VARIANT inUser, 
                    [in] VARIANT inId, 
                    [out] VARIANT* outNoOfChildren, 
                    [out] VARIANT* outChildList, 
                    [out] VARIANT* outErrMsg, 
                    [out, retval] VARIANT* pRetVal);

更新 2:我开始怀疑错误可能不在“outNoOfChildren”中,而是在数组“outChildList”中。我已经更新了问题并为此修改了代码示例。

非常感谢任何想法!

【问题讨论】:

  • status 参数怎么了?
  • 我认为最后一个参数是函数的返回值。
  • 我更新了问题,了解它在交换到 .NET .dll 之前和之后在 OLE/COM 对象查看器中的外观。我假设 C# 函数的返回值被翻译成“[out, retval] VARIANT*”并且没问题。但我同意在 C++ sn-p 中既有状态参数又有 S_OK 的返回,这看起来很奇怪?

标签: c++ .net com


【解决方案1】:

测量:-2147467259 限制:= 2"

这是一个神奇的数字。将其转换为十六进制,您将得到 0x80004005。这是一个错误代码,臭名昭著的 E_FAIL 或“未指定错误”。因此,在我们看不到的点中,您将错误返回值解释为数字。可能是 vtError 类型的变体并忽略变体类型。这就是从问题中可以得出的全部结论,请查看代码中的错误处理。

【讨论】:

  • 汉斯,谢谢您的回复。但是这个错误是我们在客户的客户端中看到的(我们对此无能为力)。问题是为什么会出现错误?我猜也许我们来自 .NET COM .dll 的返回值可能超出了客户的预期? (我已经包含了整个原始 C++ .dll 代码和我们新的 .NET 替换代码)。但我真的在黑暗中摸索...
  • 在 C++ .dll 中,他们使用称为“SafeArray”的东西(参见 pArray)。这可能是问题吗?例如。客户的客户期望“SafeArray”,但我们的 .NET .dll 只返回一个“常规”数组?我必须在 .NET .dll 中执行任何操作以返回“SafeArray”吗?
  • 不,CLR 会自动进行转换。
  • 汉斯,谢谢!您的评论让我们相信我们没有做任何根本性的错误。事实证明,C++ 客户端期望 VARIANT 的安全数组,但我们返回的是 BSTR 的安全数组。将 .NET .dll 中的 Array myArray = Array.CreateInstance(typeof(string)...) 更改为 Array myArray = Array.CreateInstance(typeof(object)...) 就可以了。 (为什么它在 VB6 中起作用仍然是个谜,但是...)
  • 类似 VB6 的变体。或者更确切地说,VB6 程序员喜欢 Dim without As。
猜你喜欢
  • 2011-05-11
  • 1970-01-01
  • 1970-01-01
  • 2017-08-30
  • 2012-04-17
  • 2012-06-22
  • 1970-01-01
  • 2013-09-03
  • 2021-06-02
相关资源
最近更新 更多