locked
Problem with Marshall RRS feed

  • Question

  • I have two problems with the marshall operation

    1) In a native C++ dll I have this struct

    typedef struct mystruct  
    {  
     char *str;   
     bool b;    
    } mystruct_t;  
     
     extern "C" __declspec(dllexport) char* test(mystruct_t* ms)  
     {  
       return ms[0].str;  
     }  
     
     


    In C# i do

     
    public class MYCLASS  
    {  
     [StructLayout(LayoutKind.Sequential)]  
     internal  struct  mystruct_t  
     {  
            public string str;   
            public bool b;   
     }  
     
     [DllImport("testing.dll")]  
     internal static extern string test(mystruct_t[] ms);  
          
     string a = null;   
     public void testnat()  
     {  
       mystruct_t[] ss = new mystruct_t[1];  
       ss[0].b = false;  
       ss[0].str = "hello";  
       a =  MYCLASS.test(ss);   
     }  
    }  
     
     

    In "a" i get "" insead of "hello". Why?



     


    2- In native C dll I have 
     

    extern "C" __declspec(dllexport) char* test2(char** strar)  
     {  
       return strar[0];  
     }  
     

    In C#

    publicclass MYCLASS  
    {  
     [DllImport("testing.dll")]  
     internal static extern string test2(String[] ar);  
     
     string h = null;  
     public void testnat2()  
     {  
       String[] abc = new string[1];  
       abc[0] = "hello";    
       h = MYCLASS.test2(abc);   
     }  
    }  
     
     

    In "h" I get ""°""  insead of "hello". Why?





     


     

    • Edited by chiara1990 Tuesday, December 2, 2008 11:13 AM
    Tuesday, December 2, 2008 8:25 AM

Answers

  • Hi,


    For the first one ,you can use ref .The sample bellow works here.

    The following sample demonstrates how to pass a structure that points to a second structure, pass a structure with an embedded structure, and pass a structure with an embedded array.

    http://msdn.microsoft.com/en-us/library/eadtsekz(VS.80).aspx

    typedef struct mystruct     
    {     
     char *str;      
     bool b;       
    } mystruct_t;     
        
     extern "C" __declspec(dllexport) char* test(mystruct_t* ms)  
     {            
       return ms[0].str;     
     }    

        [DllImport("PinvokeLib.dll")]  
        public static extern string test(ref mystruct_t ms);  
     
     
     
            string a = null;      
     
            mystruct_t[] ss = new mystruct_t[1];  
              
            ss[0].b = false;     
            ss[0].str = "hello";     
            a = LibWrap.test(ref ss[0]);  
     
            Console.Write("a's value = "+a); 

    2. From your post , you return  strar[0] in the second example.
    The reason it does not work is that: When the managed code calling into the native function it cannot passing the string array directly to the native side. Instead, clr allocates the memory and construct the native char array according to the contents in the string array.

    so that the interop layer did the following things for p/invoke:

        1. allocate the native char array

        2. fill in the char array according to the managed string array

        3. invoke the native method with the newly allocated char array

        4. assign the value returned from the native code to managed variable

        5. free the memory allocated in steup1.

     

    Therefore, strArray[0] points to the memory chunk allocated in step 1 and freed in step 5. When you got intptr in managed side, it is already pointed to a memory chunk with garbage.

    So a better fix is to re-write the native function to CoTaskMemAlloc a piece of mem and copy strArray[0] to it, then return the mem allocated by CoTaskMemAlloc.Please see the code below:

    extern "C" PINVOKELIB_API char* test2( char* ppStrArray[] )  
    {  
        int result = 0;  
        char* temp;  
     
            result += (int)strlen( ppStrArray[ 0 ] );  
     
            temp = (char*)CoTaskMemAlloc( sizeof(char) * 10 );  
            strcpy_s( temp, sizeof(char) * 10, "123456789" );  
     
           
     
           
             
        }  
        return temp;  
          
           
          

            String[] abc = new string[2];  
            abc[0] = "123456";  
            abc[1] = "sdffs";  
            string h = LibWrap.test2(ref abc[0]);  
            Console.Write("h="+h);  
     

    Please replace the the dll name and classname with the one you are using .

    The following document demonstrates the default Marshaling for Strings:
    http://msdn.microsoft.com/en-us/library/s9ts558h.aspx

    Best regards,
    Harry
    • Marked as answer by Harry Zhu Tuesday, December 9, 2008 1:42 AM
    Friday, December 5, 2008 9:35 AM

All replies

  • Try changing your pinvoke return values from string to IntPtr.

    Then call Marshal.PtrToStringAnsi() or Marshal.PtrToStringUni() on the return value (or one of the other PtrToString????() methods, as appropriate).
    Tuesday, December 2, 2008 10:39 AM
  • I tryed

    [DllImport("testing.dll")]
    internal static extern IntPtr test2(String[] f);
    string s = null;
    IntPtr p;

    public void testnat2()  
     {  
       String[] abc = new string[1];  
       abc[0] = "hello";    
       p = MYCLASS.test2(abc);
       s = Marshal.PtrToStringAuto(p);  // and all others Marshal.PtrToString___   
     }  

    but everytime I get ""㐀觠Ņ\b" or "@Ý"    

    :( 

    Tuesday, December 2, 2008 10:50 AM
  • You also might need to set the CharSet for the string parameter.

    Is your C program Unicode or Ansi?

    You might need something like:

     [StructLayout(LayoutKind.Sequential)]  
     internal  struct  mystruct_t  
     { 
            [MarshalAs(UnmanagedType.LPStr)]
            public string str;   
            public bool b;   
     }  

    Or some other decoration on the string member of the struct.
    Tuesday, December 2, 2008 10:58 AM
  •  

    [DllImport("testing.dll")]
    internal static extern IntPtr test(mystruct_t[] f);
    IntPtr p;
    string s = null;

    public void testnat()  
     {  
       mystruct_t[] ss = new mystruct_t[1];
       ss[0].b = false;
       ss[0].str = "hello";
       p= MYCLASS.test(ss);
     
       s = Marshal.PtrToStringAuto(p);  // and all others Marshal.PtrToString___   
     }  

    [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
    internal struct berry_grammar_st
    {
       [
    MarshalAs(UnmanagedType.LPStr)] //and i tryed also with all LP___
       public string str;
      
    public bool b;
    }

    but  everytime the result is ""

    The C++ program is Ansi.

     


    Tuesday, December 2, 2008 11:13 AM
  • Sorry, I'm out of ideas now.
    Tuesday, December 2, 2008 11:58 AM
  • Hi,


    For the first one ,you can use ref .The sample bellow works here.

    The following sample demonstrates how to pass a structure that points to a second structure, pass a structure with an embedded structure, and pass a structure with an embedded array.

    http://msdn.microsoft.com/en-us/library/eadtsekz(VS.80).aspx

    typedef struct mystruct     
    {     
     char *str;      
     bool b;       
    } mystruct_t;     
        
     extern "C" __declspec(dllexport) char* test(mystruct_t* ms)  
     {            
       return ms[0].str;     
     }    

        [DllImport("PinvokeLib.dll")]  
        public static extern string test(ref mystruct_t ms);  
     
     
     
            string a = null;      
     
            mystruct_t[] ss = new mystruct_t[1];  
              
            ss[0].b = false;     
            ss[0].str = "hello";     
            a = LibWrap.test(ref ss[0]);  
     
            Console.Write("a's value = "+a); 

    2. From your post , you return  strar[0] in the second example.
    The reason it does not work is that: When the managed code calling into the native function it cannot passing the string array directly to the native side. Instead, clr allocates the memory and construct the native char array according to the contents in the string array.

    so that the interop layer did the following things for p/invoke:

        1. allocate the native char array

        2. fill in the char array according to the managed string array

        3. invoke the native method with the newly allocated char array

        4. assign the value returned from the native code to managed variable

        5. free the memory allocated in steup1.

     

    Therefore, strArray[0] points to the memory chunk allocated in step 1 and freed in step 5. When you got intptr in managed side, it is already pointed to a memory chunk with garbage.

    So a better fix is to re-write the native function to CoTaskMemAlloc a piece of mem and copy strArray[0] to it, then return the mem allocated by CoTaskMemAlloc.Please see the code below:

    extern "C" PINVOKELIB_API char* test2( char* ppStrArray[] )  
    {  
        int result = 0;  
        char* temp;  
     
            result += (int)strlen( ppStrArray[ 0 ] );  
     
            temp = (char*)CoTaskMemAlloc( sizeof(char) * 10 );  
            strcpy_s( temp, sizeof(char) * 10, "123456789" );  
     
           
     
           
             
        }  
        return temp;  
          
           
          

            String[] abc = new string[2];  
            abc[0] = "123456";  
            abc[1] = "sdffs";  
            string h = LibWrap.test2(ref abc[0]);  
            Console.Write("h="+h);  
     

    Please replace the the dll name and classname with the one you are using .

    The following document demonstrates the default Marshaling for Strings:
    http://msdn.microsoft.com/en-us/library/s9ts558h.aspx

    Best regards,
    Harry
    • Marked as answer by Harry Zhu Tuesday, December 9, 2008 1:42 AM
    Friday, December 5, 2008 9:35 AM