views:

205

answers:

2

This is my second post.

Here is what I am trying to do: Call an unmanaged c++ program from c#, passing in a array of structs from the c# program and return a updated version of the array of structs from the c++ program.

Here is the calling c# program:

using System;
using System.Runtime.InteropServices;

namespace TestCallingC
{
    class Program
    {
        [DllImport(@"..\..\..\Debug\TestCProgram.dll")]
        public static extern void testfunc(teststruc[] ts);

        static void Main(string[] args)
        {
            teststruc[] teststructs = new teststruc[6];
            for (int i = 0; i < teststructs.Length; i++)
            {
                teststructs[i].int1 = (i + 1) * 100;
                teststructs[i].int2 = (i + 1) * 100;
            }

            testfunc(teststructs);
            for (int i = 0; i < teststructs.Length; i++)
            {
                Console.WriteLine("Int1 = {0}", teststructs[i].int1);
                Console.WriteLine("Int2 = {0}", teststructs[i].int2);
            }
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct teststruc
    {
        [MarshalAs(UnmanagedType.I4)]
        public int int1;
        [MarshalAs(UnmanagedType.I4)]
        public int int2;
    }
}

Here is the returning c++ program:

extern "C" __declspec(dllexport) void testfunc (teststruc* ts)
{
    int i;

    for (i = 0; i < 6; i++)
    {

     ts[i].int1 = i;
     ts[i].int2 = i;
    }

    for (i = 0; i < 6; i++)
    {
     printf("ts[%d].int1 = %d\n", i, ts[i].int1);
     printf("ts[%d].int2 = %d\n", i, ts[i].int2);
    }

    return;
}

The version that I presented above allows the c++ program to see and print out the inbound struct passed from the c# program. When the control is passed back to the c# program, the data is the same as it was originally set. It down allow the the structure to be updated by the called c++ program. Here is the console output. The first part shows the updated fields from teh called c++ program; the second part is what ws originally set by the c# caller:

ts[0].int1 = 0
ts[0].int2 = 0
ts[1].int1 = 1
ts[1].int2 = 1    
ts[2].int1 = 2
ts[2].int2 = 2    
ts[3].int1 = 3
ts[3].int2 = 3    
ts[4].int1 = 4
ts[4].int2 = 4
ts[5].int1 = 5
ts[5].int2 = 5

Int1 = 100
Int2 = 100
Int1 = 200
Int2 = 200
Int1 = 300
Int2 = 300
Int1 = 400
Int2 = 400
Int1 = 500
Int2 = 500
Int1 = 600
Int2 = 600

If I add the "ref" tag to the c# calling signature, The struct returned back from the c++ program is null:

[DllImport(@"..\..\..\Debug\TestCProgram.dll")]
        public static extern void testfunc(ref teststruc[] ts);

testfunc(ref teststructs);

Question: What updates to the interfaces in the c++ and c# program need to be made in order to allow the struct to be properly updated in the c++ program and returned to the c# program?

I've discovered a lot of information about similar things, but nothing has given me the right combination to make it happen. Any advise is welcome.

Thank you. -Greg

+1  A: 

Random wild guess:

[DllImport(@"..\..\..\Debug\TestCProgram.dll")]
public static extern void testfunc([In, Out] teststruc[] ts);

From MSDN:

Combining the InAttribute and OutAttribute is particularly useful when applied to arrays and formatted, non-blittable types. Callers see the changes a callee makes to these types only when you apply both attributes. [...]

dtb
The [In, Out] worked. I was able to pass the struct to the C++ and then return updates from the C++ program to the calling C# program. Now I can apply this technique to my larger application. Thanks again. This board is a great resource.
Glad I could help. Don't forget to accept an answer if it solves your problem. :)
dtb
A: 

Final solution:

C++ (no change from from the intial version).

C# code:

using System; using System.Runtime.InteropServices;

namespace TestCallingC { class Program { [DllImport(@"......\Debug\TestCProgram.dll")] public static extern void testfunc([In, Out] teststruc[] ts);

    static void Main(string[] args)
    {
        teststruc[] teststructs = new teststruc[6];
        for (int i = 0; i < teststructs.Length; i++)
        {
            teststructs[i].int1 = (i + 1) * 100;
            teststructs[i].int2 = (i + 1) * 100;
        }

        testfunc(teststructs);
        for (int i = 0; i < teststructs.Length; i++)
        {
            Console.WriteLine("Int1 = {0}", teststructs[i].int1);
            Console.WriteLine("Int2 = {0}", teststructs[i].int2);
        }
    }
}

[StructLayout(LayoutKind.Sequential)]
public struct teststruc
{
    [MarshalAs(UnmanagedType.I4)]
    public int int1;
    [MarshalAs(UnmanagedType.I4)]
    public int int2;
}

}