user1741137
user1741137

Reputation: 5058

Use of SAFEARRAY for C++ C#

I am trying to make use from C++ of a library that is written in C#. For this I was told to use COM Interop, about which I knew very little. I am trying to pass a 1D array one way and get a 1D array back, but my return array ends up being just full of zeros. In order to simplify the problem for this forum I have simplified my program: I don't even pass an input array.

My C# program looks like this

using System;
using System.Runtime.InteropServices;

namespace FillArray
{
    [ComVisible(true)]
    [Guid("DABAEF7C-D2B8-4769-98C8-1AF211EF7D48")]
    public interface IFillArray
    {
        int fillTheArrayWithSquares(int[] array);
    }

    [ComVisible(true)]
    [Guid("124A66FD-CED3-4a8e-B0F0-BAA88B421E97")]
    public class Class1: IFillArray
    {
        public int fillTheArrayWithSquares(int[] array)
        {
            int len = array.Length;
            int i;
            for (i = 0; i < len; i++)
                array[i] = i * i;

            return len;
        }
    }
}

In the same solution I have a C++ Client that looks like this:

#import "D:\dev\CSharp\FillArray\FillArray\bin\Release\FillArray.tlb" raw_interfaces_only
#include "stdafx.h"
using namespace FillArray;

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr = CoInitialize(NULL);
    long retval;
    IFillArrayPtr pIFillArray(__uuidof(Class1));
    SAFEARRAY *output;
    SAFEARRAYBOUND  BoundOutput;
    BoundOutput.cElements = 10;
    BoundOutput.lLbound = 0;
    output = SafeArrayCreate(VT_I4, 1, &BoundOutput);
    int *p_output_contents;
    HRESULT hrFill  = pIFillArray->fillTheArrayWithSquares( output, &retval);
    HRESULT hrOutput = SafeArrayAccessData(output, (void HUGEP**)&p_output_contents);
    if(SUCCEEDED(hrFill) && SUCCEEDED(hrOutput))
    {

    for(int i = 0; i < 10; i++)
        printf("%d ",p_output_contents[i]);
    printf("\n");
    printf("retval = %d\n",retval);
    SafeArrayUnaccessData(output);// not robust error handling
    SafeArrayDestroy(output);//not robust error handling logic
    }

    CoUninitialize();

    return 0;
}

I have configured the C++ project to use /clr: oldsyntax because I am adapting an old article that recommended this. Unfortunately it looks like this is deprecated in VS 2008 (which I am using) and I will have trouble porting this to say VS2013. So I have two questions: 1) Why is my C++ application printing 10 zeros? 2) How would I update my syntax to avoid /clr:oldsyntax.

One more remark: I have seen on some discussions that C# arrays dont correspond to SAFEARRAYS, but the choice of the type is what Intellisense tells me I should be using. Thx.

Upvotes: 2

Views: 3859

Answers (1)

Matt
Matt

Reputation: 6050

Fir of all, there're 2 types of C++ projects in VS:

  1. Native C++ project, without /clr option by default.
  2. C++/CLI project, with /clr option by default. C++/CLI can access both native and managed world, so if you want use a C# DLL, just add this DLL as a reference and use it directly, you don't need COM for this case.

If the native C++ project wants to use C# DLL, one way is via COM, another way is use C++CLI project as a bridge.

If you use COM, then you have to deal with the SAFEARRAY, which is error prone. As to your question 1, you're using SAFEARRAY as an out parameter, this is complex. I suggest you just change the definition of function fillTheArrayWithSquares to:

public int[] fillTheArrayWithSquares( )

So it will return a SAFEARRAY to C++ code, and the lengh of SAFEARRAY is already contained in the SAFEARRAY.

Upvotes: 2

Related Questions