none
How do I declare and get back the equivalent to a C unsigned char** or to a pointer to an array of structs

    Question

  • Hi, thanks for the help,

    I have a further question about parameters marshaling to unmanaged dlls:

    I was able to pass and get back simple integers or strings with the use of ref or out.

    How do I declare and get back the equivalent to a C unsigned char** or to a pointer to an array of structs where the struct is something like:

    struct Camera {

    int id;

    char camname 30;

    }

    and the structs are filled by the callee ?

    Thank you

    Marco

     


    marco Furlan
    • Split by Cookie Luo Thursday, December 30, 2010 3:19 AM new topic
    Wednesday, December 29, 2010 11:03 AM

Answers

  • Hello Marco Furlan,

     

    1. There are 2 ways to declare the CameraInfo structure :

    1.1

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct CameraInfo
    {
        public Int32 id;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
        public char [] camname;

    }

    The "camname" field is declared as a char [] array and attributed with MarshalAsAttribute with the UnmanagedType.ByValArray enumeration value set.

    1.2

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct CameraInfo
    {
        public Int32 id;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 30)]
        public string camname;

    }

    The second way is to declare "camname" as a string and attributed with MarshalAsAttribute with the UnmanagedType.ByValTStr enumeration value set.

    1.3 Note that in both cases the SizeConst field is cricual in determining how many characters "camname" contains.

     

    2. The version of "tvcc_get_cameras()" suitable for use is :

    public static extern int tvcc_get_cameras(int ch, IntPtr[] cams, out int ncams);

    2.1 It will work with the sample code that you provided :

    IntPtr[] ps = new IntPtr[128]; 
    for (int n = 0; n < 128; n++)
        ps[n] =  Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CameraInfo)));

    result = BettiniMethods.tvcc_get_cameras(chnd, ps,  out ncams);

    ...

    CameraInfo[] s =  new CameraInfo[ncams];

    for (int i = 0; i < ncams; i++)
      s[i] = (CameraInfo)Marshal.PtrToStructure(ps[i], typeof(CameraInfo));

    2.2 Remember to call FreeHGlobal() :

                for (int n = 0; n < 128; n++)
                {
                    Marshal.FreeHGlobal(ps[n]);
                }

    3. A few more points to remember :

    3.1 The struct pack alignment of the CameraInfo struct as declared in the unmanaged code must match that of the struct as declared in the managed code.

    Hence if CameraInfo as been declared in C++ as :

    #pragma pack(1)
    struct Camera
    {
     int id;
     char camname[30];
    };

    Then it must be declared as follows in C# :

    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
    public struct CameraInfo
    {
       ...
       ...
       ...
    }

    3.2 The character set (Ansi or Unicode) used in the unmanaged code must also match that of the managed one. Both must use the same character set.

    3.3 If the structure pack alignment and/or the character sets do not match, trouble will surface (in the form of garbage field data) when the unmanaged struct is marshaled to the managed world via Marshal.PtrToStructure().

     

    - Bio.

     

    • Marked as answer by MarcoFurlan Sunday, January 09, 2011 5:13 PM
    Saturday, January 08, 2011 2:38 PM

All replies

  • Hi Marco

    This is a new question. I will split it as a new thread.

    Best Regards,

     


    Cookie Luo[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, December 30, 2010 3:17 AM
  • Hi Marco

    Supposing we have following c++ code.

    struct Camera 
    {
     int id;
     char* camname;
    };
    
    extern "C" __declspec(dllexport) void Test1(char** strArray)
    {
     *strArray = "efg";
    }
    
    extern "C" __declspec(dllexport) void Test2(Camera* value)
    {
     /*value->camname = "name";
     value->id=*/
    }
    

    you should have following code in c# to call the function in c++.

    class Program
     {
     [DllImport("cpplib.dll")]
     static extern string Test1(ref string teststring);
     [DllImport("cpplib.dll")]
     static extern void Test2(ref Camera value);
     static void Main(string[] args)
     {
    
      string str = "abc";
    
    
      Test1(ref str);
    
      Camera value = new Camera();
      value.id = 10;
      value.camname = "name";
      Test(ref value);
      Console.WriteLine(str);
     }
     }
     [StructLayout(LayoutKind.Sequential)]
     struct Camera
     {
     public Int32 id;
     
     [MarshalAs(UnmanagedType.LPStr, SizeConst=30)]
     public string camname;
     }
    

    This is simple sample. I f you have any other questions, Please let me know.

    Best Regards,

     


     

    Cookie Luo[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, December 30, 2010 7:17 AM
  • Hi, thanks for your reply, nevertheless I think my problem is a bit more complex:

    I must call some methods from a dll, probably written in delphi or old C++ Builder (Borland world).

    these are the signatures

    int WINAPI tvcc_get_cameras(int server_handle, struct Camera *camera[],int *num_cameras)

    struct Camera {

    int id;

    char coder_name[40];

    char camera_name[40];

    int status;

    int is_controllable;

    };

    and

    int WINAPI tvcc_get_frame(int server_handle,int *handle,unsigned char **frame, int *size,int *status,int *width,int *height,long *tv_sec, long *tv_usec)

    to get the array of structures cameras filled and the buffer frame to show it on a bitmap.

    ------------------------------------------------------------------------------------------

    I declared the methods as

    [

     

    StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi),Serializable]

     

     

    struct CameraInfo1

    {

     

     

    public Int32 id;

    [

     

    MarshalAs(UnmanagedType.LPStr, SizeConst = 40)]

     

     

    public string codername;

    [

     

    MarshalAs(UnmanagedType.LPStr, SizeConst = 40)]

     

     

    public string camname;

     

     

    public Int32 status;

     

     

    public Int32 is_controllable;

    }

     

     

     

    unsafe class BettiniMethods

    {

     

     

    public const int CAMERA_LENGTH = 40;

    [

     

    DllImport("GamsSDK.dll", EntryPoint = "@tvcc_get_cameras$qqsipp6Camerapi", SetLastError = true, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]

     

     

    public static extern int tvcc_get_cameras(int ch, ref CameraInfo1[] cam, out int ncams);

     

     

    //public static extern int tvcc_get_cameras(int ch, IntPtr[] cams, out int ncams);

    [

     

    DllImport("GamsSDK.dll", EntryPoint = "@tvcc_get_frame$qqsipippct2t2t2t2plt8", SetLastError = true, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]

     

     

    public static extern int tvcc_get_frame(int ch, ref int lconn, ref IntPtr frame, out int size, out int stat, out int w, out int h, out long sec, out long usec);

    }

    and I called the methods as:

     

     

    CameraInfo1[] cams = new CameraInfo1[64];

    result =

     

    BettiniMethods.tvcc_get_cameras(chnd, ref cams, out ncams);

    and

    IntPtr

    pF = new IntPtr(); 

     

    pF = new IntPtr(); 

     

    pF = new IntPtr(); 

    pF = new IntPtr(); 

     

     

    result =

     

    BettiniMethods.tvcc_get_frame(chnd, ref lconn, ref pF, out size, out stat, out w, out h, out sec, out usec);

    The result is that I don't get any error, the number of cameras is right (ncams), the integer values of the structures are ok, but the names have just the first character.

    For get_frame, again no error, the size, width and height are reasonable, but I'm not able to build up a bitmap from the address I get back.

    I know it's a long question, though I would really appreciate your help.

    best regards

    Marco

    Thursday, December 30, 2010 9:31 AM
  • Hi, thanks for your reply.

    Sorry to bother you but I can't get out of it.

    Let me be more precise
    Suppose I have the following structure in C
    struct Camera 
    {
     int id;
     char camname[30];
    };
    
    suppose this unmanaged dll exposes a method as  

    int WINAPI tvcc_get_cameras(int server_handle, struct Camera *camera[],int *num_cameras);

    so there is a pointer to an array of structures
    I tried to translate it in managed code both as
    [StructLayout(LayoutKind.Sequential)]
     unsafe public struct Camera
     {
     public Int32 id;
     
     [MarshalAs(UnmanagedType.LPStr, SizeConst=30)]
     public string camname;
     }
    and

    [

     

    StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi),Serializable]

    unsafe

     

     

    public struct CameraInfo

    {

     

     

    public int id;

     

     

    //[MarshalAs(UnmanagedType.ByValTStr, SizeConst = BettiniMethods.CAMERA_LENGTH)]

     

     

    //public string camera_name;

     

     

    public fixed char camera_name[30];

     

     

    }

    and I declared the method as

    [

     

    DllImport("GamsSDK.dll", EntryPoint = "@tvcc_get_cameras$qqsipp6Camerapi", SetLastError = true, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]

     

     

    1)public static extern int tvcc_get_cameras(int ch, ref Camera[] cam, out int ncams);

     

     

    2)public static extern int tvcc_get_cameras(int ch, IntPtr[] cams, out int ncams);

    for the two options of the structures.
    and I call the method in the following way:

     

     

    Camera[] cams = new Camera[64];

     

     

    result = BettiniMethods.tvcc_get_cameras(chnd, ref cams, out ncams);

    for the structure camera
    and as 

     

     

    IntPtr[] ps = new IntPtr[128];

     

     

    for (int n = 0; n < 128; n++)

    ps[n] =

     

    Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CameraInfo)));

    result = BettiniMethods.tvcc_get_cameras(chnd, ps,

     

    out ncams);

    listBox1.Items.Add(

     

    string.Format("Get cameras ritorna {0} telecamere", ncams.ToString()));

    CameraInfo[] s =

     

    new CameraInfo[ncams];

     

     

    for (int i = 0; i < ncams; i++)

    s[i] = (CameraInfo0)

     

    Marshal.PtrToStructure(ps[i], typeof(CameraInfo));

    In the first case I get an access violation; in the second case I can retrieve just the first letter of the name.
    I will really appreciate your help
    best regards
    Marco

    marco Furlan
    Sunday, January 02, 2011 7:35 PM
  • Hi marco Furlan,

    Sorry for late response to you. I am just back from a holiday.

    We are doing on research on it. It will take some to reply to you.

    Best Regards,


    Cookie Luo[MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, January 06, 2011 8:09 AM
  • Hello Marco Furlan,

     

    1. There are 2 ways to declare the CameraInfo structure :

    1.1

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct CameraInfo
    {
        public Int32 id;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)]
        public char [] camname;

    }

    The "camname" field is declared as a char [] array and attributed with MarshalAsAttribute with the UnmanagedType.ByValArray enumeration value set.

    1.2

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct CameraInfo
    {
        public Int32 id;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 30)]
        public string camname;

    }

    The second way is to declare "camname" as a string and attributed with MarshalAsAttribute with the UnmanagedType.ByValTStr enumeration value set.

    1.3 Note that in both cases the SizeConst field is cricual in determining how many characters "camname" contains.

     

    2. The version of "tvcc_get_cameras()" suitable for use is :

    public static extern int tvcc_get_cameras(int ch, IntPtr[] cams, out int ncams);

    2.1 It will work with the sample code that you provided :

    IntPtr[] ps = new IntPtr[128]; 
    for (int n = 0; n < 128; n++)
        ps[n] =  Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CameraInfo)));

    result = BettiniMethods.tvcc_get_cameras(chnd, ps,  out ncams);

    ...

    CameraInfo[] s =  new CameraInfo[ncams];

    for (int i = 0; i < ncams; i++)
      s[i] = (CameraInfo)Marshal.PtrToStructure(ps[i], typeof(CameraInfo));

    2.2 Remember to call FreeHGlobal() :

                for (int n = 0; n < 128; n++)
                {
                    Marshal.FreeHGlobal(ps[n]);
                }

    3. A few more points to remember :

    3.1 The struct pack alignment of the CameraInfo struct as declared in the unmanaged code must match that of the struct as declared in the managed code.

    Hence if CameraInfo as been declared in C++ as :

    #pragma pack(1)
    struct Camera
    {
     int id;
     char camname[30];
    };

    Then it must be declared as follows in C# :

    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
    public struct CameraInfo
    {
       ...
       ...
       ...
    }

    3.2 The character set (Ansi or Unicode) used in the unmanaged code must also match that of the managed one. Both must use the same character set.

    3.3 If the structure pack alignment and/or the character sets do not match, trouble will surface (in the form of garbage field data) when the unmanaged struct is marshaled to the managed world via Marshal.PtrToStructure().

     

    - Bio.

     

    • Marked as answer by MarcoFurlan Sunday, January 09, 2011 5:13 PM
    Saturday, January 08, 2011 2:38 PM