title: Returning values C#/COM to C++ client?
body: <p>We have an old C++ COM .dll that a customer calls from their C++ client. </p>
  
  <p>We are trying to replace our old .dll with a new COM registered one written in .NET. </p>
  
  <p>The C++ client can call our new .dll but crashes after a particular method call to us. </p>
  
  <p>It seems we are returning something wrong in our variable "outNoOfChildren" or in the outChildList array below. </p>
  
  <p>(The error message in the client reads: "Unknown exception caught.
  Number of childs Measured: -2147467259    Limits: = 2", it expects 2 which we are trying to return.)</p>
  
  <p>Strange thing is that the method call in our new .NET .dll seems to work from another test client we have (VB6). </p>
  
  <p>This is the function in the C++ .dll that we are trying to replace: </p>
  
  <pre><code>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, &amp;inparamType, user, &amp;dumInt);
    if (inparamType != VT_BSTR)
    {
      strcpy(errMsg, "Parameter 1 incorrect, type must be VT_BSTR");
      stat = 0;
    }
  
    if (stat == 1)
    {
      getInParam( inId, &amp;inparamType, id, &amp;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, &amp;noOfChildren, childList, errMsg);
    }
  
    outNoOfChildren-&gt;vt = VT_I4;
    outNoOfChildren-&gt;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-&gt;vt = VT_ARRAY|VT_VARIANT;
    outChildList-&gt;parray = pArray;
  
    ix[0] = 1;
  
    childListTemp = childList.nextChild;
  
    while (childListTemp != NULL)
    {
      temp_bstr = childListTemp-&gt;child;
      var.vt = VT_BSTR;
      var.bstrVal = temp_bstr.copy();
      ix[1] = 1;
      ::SafeArrayPutElement(pArray, ix, &amp;var);
  
      temp_bstr = childListTemp-&gt;prodNo;
      var.vt = VT_BSTR;
      var.bstrVal = temp_bstr.copy();
      ix[1] = 2;
      ::SafeArrayPutElement(pArray, ix, &amp;var);
  
      temp_bstr = childListTemp-&gt;rev;
      var.vt = VT_BSTR;
      var.bstrVal = temp_bstr.copy();
      ix[1] = 3;
      ::SafeArrayPutElement(pArray, ix, &amp;var);
  
      ix[0] = ix[0] + 1;
  
      childListTempNext = childListTemp-&gt;nextChild;
      delete childListTemp;
      childListTemp = childListTempNext;
    }
  
    temp_bstr = errMsg;
    outErrMsg-&gt;vt = VT_BSTR;
    outErrMsg-&gt;bstrVal = temp_bstr.copy();
  
    status-&gt;vt = VT_I4;
    status-&gt;lVal = stat;
  
    return S_OK;
  }
  </code></pre>
  
  <p>This is the .NET .dll that we have written:</p>
  
  <p>.NET Interface: </p>
  
  <pre><code>...
  [DispId(12)]
   [Description("method XgetChildren")]
  object XgetChildren(object inUser, object inId, out object outNoOfChildren, out object outChildList, out object outErrMsg);
  ...
  </code></pre>
  
  <p>.NET Class</p>
  
  <pre><code>public object XgetChildren(object inUser, object inId, out object outNoOfChildren, out object outChildList, out object outErrMsg)
  {
      outNoOfChildren = 0;
      outChildList = null;
      outErrMsg = "";
  
      List&lt;IndividualInfo&gt; 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 &lt; 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;
  }
  </code></pre>
  
  <p>Update:
  The original C++ .dll looks like this in OLE/COM Object Viewer:</p>
  
  <pre><code>    [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);
  </code></pre>
  
  <p>After registering our new .NET .dll it looks like this in OLE/COM Object Viewer:</p>
  
  <pre><code>    [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);
  </code></pre>
  
  <p>Update 2: I'm beginning to suspect the error may not be in "outNoOfChildren" but in the array "outChildList". I have updated the question and modified the code samples for this.</p>
  
  <p>Any ideas much appreciated! </p>