0

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

5
  • 1
    This mindless downvoting needs to stop. If you people are going to downvote someone, the least you could do is provide the reason for doing so. Commented Aug 8, 2009 at 23:36
  • 1
    I didn't downvote, but this was completely unreadable before Jim Puls edited it and the title of the question was "Senior Software Engineer" which possibly indicates this is an interview question and / or test. Commented Aug 8, 2009 at 23:38
  • 1
    Always worth taking note of the edit history when you see "strange" downvotes. Sometimes the explanation is there... Commented Aug 8, 2009 at 23:41
  • Sorry for the formatting. I hope I can be read now. -Greg Commented Aug 8, 2009 at 23:42
  • As an FYI, if you're passing arrays to your unmanaged code like this, you should pass in the length as a parameter so the unmanaged code won't go walking through your managed heap. Commented Aug 10, 2009 at 15:02

2 Answers 2

1

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. [...]

Sign up to request clarification or add additional context in comments.

1 Comment

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.
0

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;
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.