3

I've followed suggestions in threads like this one on how to pass arrays of values from c++ to c#. after @CodyGray's comment on my recent question here why I'm using pointers at all i tried to pass values without pointers and marshal code, also following this
and it works too! I'm confused, what's the correct/best way to do it? why use pointers here at all?

here's some code:

consider this exported c++ function

extern "C" { __declspec(dllexport) void PointerTest(char ** text, float* fArray, int * iArray); }

here's what I did until now in c#:

[System.Runtime.InteropServices.DllImport("myTest.dll")]
        private static extern void PointerTest(IntPtr text, IntPtr fArray, IntPtr iArray);

public static IntPtr NativeUtf8FromString(string managedString) {
            int len = Encoding.UTF8.GetByteCount(managedString);
            byte[] buffer = new byte[len + 1];
            Encoding.UTF8.GetBytes(managedString, 0, managedString.Length, buffer, 0);
            IntPtr nativeUtf8 = Marshal.AllocHGlobal(buffer.Length);
            Marshal.Copy(buffer, 0, nativeUtf8, buffer.Length);
            return nativeUtf8;
            }

public void DoSomething(String text, float[] F, int[] I, int numFloats, int numInts){

            IntPtr sPtr = NativeUtf8FromString(text);
            IntPtr fPtr = Marshal.AllocHGlobal(numFloats*sizeof(float));
            IntPtr iPtr = Marshal.AllocHGlobal(numInts*sizeof(int));

            try
            {           
                Marshal.Copy(F, 0, fPtr, numFloats);
                Marshal.Copy(I, 0, iPtr, numInts);

                PointerTest(sPtr, fPtr, iPtr);
            }
            finally
            {
                Marshal.FreeHGlobal(sPtr);
                Marshal.FreeHGlobal(fPtr);
                Marshal.FreeHGlobal(iPtr);      
            }

        }

this works fine when passing values from c# to c++; however, the only way of passing values back (reliably) using pointers i found was the return value of my function, causing me to stuff all kinds of values in one big array and return that (again by reference).

now consider this code:

[System.Runtime.InteropServices.DllImport("myTest.dll")]
        private static extern void PointerTest([In, Out] String text, [In, Out] float[] fArray, [In, Out] int[] iArray);

public void DoSomething(String text, float[] F, int[] I){   
            PointerTest(text, F, I);
            }

This does the same thing as far as I can tell. Plus by using the [In, Out] semantic I can easily access the modified arrays after I've called PointerTest.

Now what am I missing here? Obviously the second approach is much cleaner, easier to understand for beginners like me and still the standard answer online is use pointers/the first approach....which leads me to the conclusion there must be something wrong in the second approach, but then i got it from msdn....

please help me understand this. thanks in advance.

Community
  • 1
  • 1
  • Does `c` have anything to do with this? – Fantastic Mr Fox Aug 31 '16 at 00:04
  • 1
    How is the C++ dll built? That is, what calling convention is used (`stdcall`, `cdecl`, etc.)? C#'s default is `stdcall`, but if you're using `cdecl` for the C++ dll (i.e. compiling with `/Gd`), you'll need to have your `DllImport` look like this `[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]` .. The calling convention changes how the stack is cleaned up (caller/callee) which can affect how you marshal your code – txtechhelp Aug 31 '16 at 05:09
  • hm, thanks for clearing that...it has been built with /Gd, which I now changed to stdcall and left c# as is. Interestingly, having this wrong did not result in an error..... – digitalWannabe Aug 31 '16 at 22:29

1 Answers1

1

I know almost nothing about C++/C# interop, but according to this documentation on improving interop performance (see the bit about Marshaling), the choice of [In, Out] vs pointers should be based on your marshaling needs. "By declaring parameters and fields as IntPtr," the documentation says, "you can boost performance, albeit at the expense of ease of use, type safety, and maintainability." It also says to "use the [in] and [out] attributes carefully to reduce unnecessary marshaling."

My super-brief-skim-read of the documentation suggests to me that [In, Out] and pointers will both work. It seems the first thing to consider is performance. For the data you're working with, are the two approaches above both sufficiently performant? If so, tis probably best to go with the choice that is cleaner and easier to understand as you suggested. But don't take my word for it, check it out! (The documentation, that is.)

Jordan Wilcken
  • 316
  • 2
  • 10
  • aha...I can see how passing the address instead of the data itself can be faster- atm I'm dealing with rather small amount of data, so I couldn't tell a difference.... thanks for that link, a lot of good info! – digitalWannabe Aug 31 '16 at 22:35