none
c function that return a struct to c# RRS feed

  • Question

  • Hello,

    i have a c function that return a structure, i have build a .dll and i want to call it from c# like this:

    typedef struct predtionresult {
    
        float prediction[5];
        char *name[5];
    }PREDICTIONRESULT;
    
    
    PREDICTIONRESULT predict()
    {
        PREDICTIONRESULT predres ;
    
        for(i = 0; i < 5; ++i){
    
            predres.name[i]= names[i]; //names[i] are not empty 
            predres.prediction[i] = predictions[i] // predictions[i] are not empty there is 
                                                  //some part of code that i can't share
        }
    
       return predres;
    }

    i have declared the c# structure like this:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    	public struct PREDICTIONRESULT{
    		
    		[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    		public float[] prediction;
    
    		[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)]
    		public string[] name;
    	};

    and call the function like this:

    PREDICTIONRESULT prediction;
    
    prediction = predict();

    The problem is the prediction array and the name array are empty, i didn't get the returned value from the c function, so i think that my c# structure have a problem that i didn't get.

    Thank you 

    Lafi



    • Edited by Raed lafi Saturday, May 19, 2018 2:57 PM
    Saturday, May 19, 2018 2:55 PM

All replies

  • This worked nicely for me -

    C DLL function -

    void predict(PREDICTIONRESULT *predres)
    {
    	for (int i = 0; i < 5; ++i)
    	{
    		predres->name[i] = names[i];
    		predres->prediction[i] = predictions[i];
    	}
    }
    
    


            [DllImport("Bytes.dll", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
            static extern void predict(out PREDICTIONRESULT predresult);
    


            [StructLayout(LayoutKind.Sequential)]
            public struct PREDICTIONRESULT
            {
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
                public float[] prediction;
    
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.LPStr)]
                public string[] name;
            };
    
    


                PREDICTIONRESULT predres;
    
                predict( out predres);
    

    I found that p/invoke threw exceptions when trying to return a structure as a return value.  No problems when using an out parameter.

    In order to marshal to a string[] array on the C# side the C dll had to alloc it's strings using an allocator that p/invoke interop marshalling understands such as CoTaskMemAlloc.  Otherwise, the PREDICTIONRESULT structure can declare an array of IntPtrs and manually marshal the pointer to managed strings.

    Saturday, May 19, 2018 6:38 PM
  • Hello,

    i have tested your solution  but it seems not working, can you explain " in order to marshal to a string[] array on the C# side the C dll had to alloc it's strings using an allocator that p/invoke interop marshalling understands such as CoTaskMemAlloc.  Otherwise, the PREDICTIONRESULT structure can declare an array of IntPtrs and manually marshal the pointer to managed strings." please

    Thank you
    Sunday, May 20, 2018 1:30 AM
  • The C dll names array contains pointers to null-terminated C strings.  These pointers are copied into the PREDICTIONRESULT names member.  What I meant in my comment was that the memory for the C strings should be allocated with CoTaskMemAlloc, not malloc.  When memory on the C side was allocated with CoTaskMemAlloc then the p/invoke call correctly marshaled the C structure's array of char* pointers to the C# string[] array.

    If malloc was used in the C dll to allocate the memory fo the C strings the p/invoke call corrupted the heap when the  C# struct contained a string[] array.  To make it work if the C dll used malloc (or pointers to string literals) I found it necessary to declare the struct on the C# side to contain an IntPtr[] array, not a string[] array.  Then the p/invoke call executed without error.  Upon returning from the call then each IntPtr contained in the C# struct's array could be marshaled to a managed string with Marshal.PtrToStringAnsi




    • Edited by RLWA32 Sunday, May 20, 2018 10:34 AM changed "corrupted the stack" to "corrupted the heap"
    Sunday, May 20, 2018 2:03 AM
  • okay sounds very good so the c# structure will be like this:

    StructLayout(LayoutKind.Sequential)]
            public struct PREDICTIONRESULT
            {
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
                public float[] prediction;
    
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.LPStr)]
                public IntPtr[] name;
            };
    right?

    Sunday, May 20, 2018 1:20 PM
  • okay sounds very good so the c# structure will be like this:

    StructLayout(LayoutKind.Sequential)]
            public struct PREDICTIONRESULT
            {
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
                public float[] prediction;
    
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5, ArraySubType = UnmanagedType.LPStr)]
                public IntPtr[] name;
            };
    right?

    Yes, that's what I described above.

    Then upon return the IntPtr[] array values are marshaled to strings by calling Marshal.PtrToStringAnsi

    Sunday, May 20, 2018 1:28 PM
  • Any progress?  Has  the issue been resolved?
    Monday, May 28, 2018 3:09 PM