none
How to use callback function to output a struct from C/C++ DLL to C#? RRS feed

  • Question

  • I can output a string using callback, but I don't know how to output a struct.

    Because all tutorials of Marshal Struct in MSDN Library are set in/out attribute as parameter to return a sturct from unmanage code. They must pass the struct from manage code to unmange code first, then return from unmange code.

    Thank you!

    Wednesday, August 11, 2010 3:30 AM

Answers

  • Hi,

    Sorry for the late reply.

    I wrote the following complete sample to demonstrate how to pass a struct containing many  things (including strings) from C DLL to C#. I also implemented your callback mechanism.

    C# code: 

    using System;
    using System.Runtime.InteropServices;
    
    namespace MarshalStruct
    {
    
     [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
     public delegate void p_func(msg_struct arg);
    
     [StructLayout(LayoutKind.Sequential)]
     public struct msg_struct
     {
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public String msg1;
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public String msg2;
     public Int32 msg3;
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public String msg4;
     }
    
     class Program
     {
    
     static void Main(string[] args)
     {
    
     p_func pf = new p_func(callback);
     try
     {
     DLLMethods.c_method(pf);
     }
     catch (Exception e)
     {
     Console.WriteLine(e.Message);
     }
     }
    
     public static void callback(msg_struct arg)
     {
     Console.WriteLine(arg.msg1);
     Console.WriteLine(arg.msg2);
     Console.WriteLine(arg.msg3);
     Console.WriteLine(arg.msg4);
     }
     }
    
     public class DLLMethods
     {
     [DllImport(@"dummyDLL.dll's fullpath", EntryPoint = "c_method")]
     public extern static void c_method(p_func pf);
     }
    }
    
    

     dummyDLL.DLL:

    #include "stdafx.h"
    
    struct myStruct
    {
    	char msg1[32];
    	char msg2[32];
    	int msg3;
    	char msg4[32];
    };
    
    
    typedef void (*p_func)(const myStruct);
    
    
    
    extern "C" __declspec(dllexport) void c_method(p_func f)
    {
    	::myStruct msg_struct;
    	::strcpy(msg_struct.msg1,"msg1");
    	::strcpy(msg_struct.msg2,"msg2");
    	msg_struct.msg3=33333;
    	::strcpy(msg_struct.msg4,"msg4");
    	f(msg_struct);
    }
    
    
    
    

    Output:

    msg1
    msg2
    33333
    msg4

     

     Below are some references:

    1. UnmanagedType Enumeration

    2. How to marshal structure containing variable length array?


    Please mark the right answer at right time.
    Thanks,
    Sam
    • Edited by SamAgain Wednesday, August 18, 2010 11:41 AM refine
    • Marked as answer by SamAgain Friday, August 20, 2010 1:07 AM
    Wednesday, August 18, 2010 11:32 AM
  • Hi,

    Here is a refined version, only an IntPtr type is passed this time. The result is the same.

    C# Code:

     

    using System;
    using System.Runtime.InteropServices;
    
    namespace MarshalStruct
    {
    
     [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
     public delegate void p_func(IntPtr p_arg); // <-- Use IntPtr instead!
    
     [StructLayout(LayoutKind.Sequential)]
     public struct msg_struct
     {
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public String msg1;
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public String msg2;
     public Int32 msg3;
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public String msg4; 
     }
    
     class Program
     {
    
     static void Main(string[] args)
     {
      
      p_func pf = new p_func(callback);
      try
      {
      DLLMethods.c_method(pf);
      }
      catch (Exception e)
      {
      Console.WriteLine(e.Message);
      }
     }
    
     public static void callback(IntPtr p_arg)
     {
      msg_struct msg = (msg_struct)Marshal.PtrToStructure(p_arg, typeof(msg_struct));
      // The above line get the struct from the IntPtr!
    
      Console.WriteLine(msg.msg1);
      Console.WriteLine(msg.msg2);
      Console.WriteLine(msg.msg3);
      Console.WriteLine(msg.msg4);
      
      //Console.WriteLine(p_arg.msg1);
      //Console.WriteLine(p_arg.msg2);
      //Console.WriteLine(p_arg.msg3);
      //Console.WriteLine(p_arg.msg4);
     }
     }
    
     public class DLLMethods
     {
     [DllImport(@"full path of the dummyDLL.dll", EntryPoint = "c_method")]
     public extern static void c_method(p_func pf);
     }
    }
    
    

     

     

    dummyDLL.dll:

     

    #include "stdafx.h"
    
    struct myStruct
    {
    	char msg1[32];
    	char msg2[32];
    	int msg3;
    	char msg4[32];
    };
    
    
    typedef void (*p_func)(const myStruct*); // <--Use pointer now!
    
    
    extern "C" __declspec(dllexport) void c_method(p_func f)
    {
    
    	::myStruct msg_struct;
    	::strcpy(msg_struct.msg1,"msg1");
    	::strcpy(msg_struct.msg2,"msg2");
    	msg_struct.msg3=33333;
    	::strcpy(msg_struct.msg4,"msg4");
    	f(&msg_struct); // Use pointer now!!
    }
    

     


    Please mark the right answer at right time.
    Thanks,
    Sam
    • Edited by SamAgain Wednesday, August 18, 2010 2:18 PM refine
    • Marked as answer by SamAgain Friday, August 20, 2010 1:07 AM
    Wednesday, August 18, 2010 2:16 PM
  • Both versions involve memory copying, so I don't think there's performance difference.
    Please mark the right answer at right time.
    Thanks,
    Sam
    • Marked as answer by SamAgain Friday, August 20, 2010 1:07 AM
    Wednesday, August 18, 2010 2:36 PM

All replies

  • Hi,

    Thanks for your post. Could you be more specific about what you want to achieve? Some code snippets?


    Please mark the right answer at right time.
    Thanks,
    Sam
    • Edited by SamAgain Wednesday, August 11, 2010 6:37 AM refine
    Wednesday, August 11, 2010 6:37 AM
  • I have invoked the C# function successfully by using the callback in unmange code as:

    C:

    typedef bool (*showErrorMsg)(const char*);

    extern "C" __declspec (dllexport)  bool setErrMsg(showErrorMsg outMsg)

     char errMsg[64] ;
     strcpy(errMsg, "Error Message!"); 
     outMsg(errMsg);
     return true;
    }

    the outMsg is pointer of function which pass from C#

    ________________________________________________________________________

    C#:

     [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            public delegate bool showListBox(StringBuilder str);

     [return: MarshalAs(UnmanagedType.I1)]
            [DllImport("MyDll.dll")]
            public static extern bool setErrMsg(showListBox errMsg);

            public bool showErrorMsg(StringBuilder msg)
            {
                txtErrMsg.AppendText(msg.ToString());
                txtErrMsg.Multiline = true;
                return true;
            }

     private void btn_Click(object sender, EventArgs e)
            {

            showListBox showErrLB = new showListBox(showErrorMsg);

            setErrMsg(showErrLB);

            }

    So, it shows the error message in the form. But if I want to export a struct instead of string, the string is a member of this struct, how can manage code accept it?Set a prototype for this struct?

    Thank you!

    Wednesday, August 11, 2010 7:14 AM
  • Does anybody know this?

    Thank you!

    Wednesday, August 11, 2010 4:25 PM
  • Hi,

    Sorry for the late reply.

    I wrote the following complete sample to demonstrate how to pass a struct containing many  things (including strings) from C DLL to C#. I also implemented your callback mechanism.

    C# code: 

    using System;
    using System.Runtime.InteropServices;
    
    namespace MarshalStruct
    {
    
     [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
     public delegate void p_func(msg_struct arg);
    
     [StructLayout(LayoutKind.Sequential)]
     public struct msg_struct
     {
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public String msg1;
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public String msg2;
     public Int32 msg3;
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public String msg4;
     }
    
     class Program
     {
    
     static void Main(string[] args)
     {
    
     p_func pf = new p_func(callback);
     try
     {
     DLLMethods.c_method(pf);
     }
     catch (Exception e)
     {
     Console.WriteLine(e.Message);
     }
     }
    
     public static void callback(msg_struct arg)
     {
     Console.WriteLine(arg.msg1);
     Console.WriteLine(arg.msg2);
     Console.WriteLine(arg.msg3);
     Console.WriteLine(arg.msg4);
     }
     }
    
     public class DLLMethods
     {
     [DllImport(@"dummyDLL.dll's fullpath", EntryPoint = "c_method")]
     public extern static void c_method(p_func pf);
     }
    }
    
    

     dummyDLL.DLL:

    #include "stdafx.h"
    
    struct myStruct
    {
    	char msg1[32];
    	char msg2[32];
    	int msg3;
    	char msg4[32];
    };
    
    
    typedef void (*p_func)(const myStruct);
    
    
    
    extern "C" __declspec(dllexport) void c_method(p_func f)
    {
    	::myStruct msg_struct;
    	::strcpy(msg_struct.msg1,"msg1");
    	::strcpy(msg_struct.msg2,"msg2");
    	msg_struct.msg3=33333;
    	::strcpy(msg_struct.msg4,"msg4");
    	f(msg_struct);
    }
    
    
    
    

    Output:

    msg1
    msg2
    33333
    msg4

     

     Below are some references:

    1. UnmanagedType Enumeration

    2. How to marshal structure containing variable length array?


    Please mark the right answer at right time.
    Thanks,
    Sam
    • Edited by SamAgain Wednesday, August 18, 2010 11:41 AM refine
    • Marked as answer by SamAgain Friday, August 20, 2010 1:07 AM
    Wednesday, August 18, 2010 11:32 AM
  • Hi,

    Here is a refined version, only an IntPtr type is passed this time. The result is the same.

    C# Code:

     

    using System;
    using System.Runtime.InteropServices;
    
    namespace MarshalStruct
    {
    
     [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
     public delegate void p_func(IntPtr p_arg); // <-- Use IntPtr instead!
    
     [StructLayout(LayoutKind.Sequential)]
     public struct msg_struct
     {
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public String msg1;
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public String msg2;
     public Int32 msg3;
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
     public String msg4; 
     }
    
     class Program
     {
    
     static void Main(string[] args)
     {
      
      p_func pf = new p_func(callback);
      try
      {
      DLLMethods.c_method(pf);
      }
      catch (Exception e)
      {
      Console.WriteLine(e.Message);
      }
     }
    
     public static void callback(IntPtr p_arg)
     {
      msg_struct msg = (msg_struct)Marshal.PtrToStructure(p_arg, typeof(msg_struct));
      // The above line get the struct from the IntPtr!
    
      Console.WriteLine(msg.msg1);
      Console.WriteLine(msg.msg2);
      Console.WriteLine(msg.msg3);
      Console.WriteLine(msg.msg4);
      
      //Console.WriteLine(p_arg.msg1);
      //Console.WriteLine(p_arg.msg2);
      //Console.WriteLine(p_arg.msg3);
      //Console.WriteLine(p_arg.msg4);
     }
     }
    
     public class DLLMethods
     {
     [DllImport(@"full path of the dummyDLL.dll", EntryPoint = "c_method")]
     public extern static void c_method(p_func pf);
     }
    }
    
    

     

     

    dummyDLL.dll:

     

    #include "stdafx.h"
    
    struct myStruct
    {
    	char msg1[32];
    	char msg2[32];
    	int msg3;
    	char msg4[32];
    };
    
    
    typedef void (*p_func)(const myStruct*); // <--Use pointer now!
    
    
    extern "C" __declspec(dllexport) void c_method(p_func f)
    {
    
    	::myStruct msg_struct;
    	::strcpy(msg_struct.msg1,"msg1");
    	::strcpy(msg_struct.msg2,"msg2");
    	msg_struct.msg3=33333;
    	::strcpy(msg_struct.msg4,"msg4");
    	f(&msg_struct); // Use pointer now!!
    }
    

     


    Please mark the right answer at right time.
    Thanks,
    Sam
    • Edited by SamAgain Wednesday, August 18, 2010 2:18 PM refine
    • Marked as answer by SamAgain Friday, August 20, 2010 1:07 AM
    Wednesday, August 18, 2010 2:16 PM
  • Both versions involve memory copying, so I don't think there's performance difference.
    Please mark the right answer at right time.
    Thanks,
    Sam
    • Marked as answer by SamAgain Friday, August 20, 2010 1:07 AM
    Wednesday, August 18, 2010 2:36 PM