C++ / C# Interop - Returning a char **

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • DG is a god....

    #1

    C++ / C# Interop - Returning a char **

    Dear All
    ,
    This is my first post - please go easy...!

    I have a DLL written in C++ that has the following function exported
    from it -:

    char** ListHandles(int *processID);

    The processID parameter is used to find all associated open file
    handles , this value is then written to to return the number of strings
    returned in the char**. The return value is an array of strings
    listing a set of file handles. I have written a harness in C++ to test
    the DLL and it works well in C++...

    My task is to now use this DLL in C#.

    I have yet to find ANY documentation on how to pass the char ** into a
    C# application.

    I have the following two requests -:

    1. What should the interop definition be to return the char** into C#

    2. How do I then extra the strings from the C# type? Are there any
    pre-requisites in C++ (ie should the strings be Uni Code?)

    I look forward to any response

    Richard

  • Arne Vajhøj

    #2
    Re: C++ / C# Interop - Returning a char **

    DG is a god.... wrote:
    I have a DLL written in C++ that has the following function exported
    from it -:
    >
    char** ListHandles(int *processID);
    >
    The processID parameter is used to find all associated open file
    handles , this value is then written to to return the number of strings
    returned in the char**. The return value is an array of strings
    listing a set of file handles. I have written a harness in C++ to test
    the DLL and it works well in C++...
    >
    My task is to now use this DLL in C#.
    >
    I have yet to find ANY documentation on how to pass the char ** into a
    C# application.
    I don't have an example for char** - can you use one for char*** ?

    :-)

    The following code is not intended for anything else than showing
    how to use ReadIntPtr and unsafe.

    C++ code:

    ....

    char *p1 = "A";
    char *p2 = "BB";
    char *p3 = "CCC";
    char *p4 = "DDDD";
    char *p5 = "EEEEE";
    char *p6 = "FFFFFF";
    char *pp1[] = { p1, p2, p3 };
    char *pp2[] = { p4, p5, p6 };
    char **ppp[] = { pp1, pp2 };

    PTRPTRPTR_API char ***ptrptrptr(vo id)
    {
    return ppp;
    }

    method #1:

    using System;
    using System.Runtime. InteropServices ;

    class MainClass
    {

    [DllImport("D:\\ IDEProjects\\Vi sualStudio\\Ptr PtrPtr\\Release \\PtrPtrPtr.dll ")]
    private static extern IntPtr ptrptrptr();
    public static void Main(string[] args)
    {
    IntPtr ppp = ptrptrptr();
    IntPtr pp1 = Marshal.ReadInt Ptr(ppp, 0);
    IntPtr pp2 = Marshal.ReadInt Ptr(ppp, 4);
    IntPtr p1 = Marshal.ReadInt Ptr(pp1, 0);
    IntPtr p2 = Marshal.ReadInt Ptr(pp1, 4);
    IntPtr p3 = Marshal.ReadInt Ptr(pp1, 8);
    IntPtr p4 = Marshal.ReadInt Ptr(pp2, 0);
    IntPtr p5 = Marshal.ReadInt Ptr(pp2, 4);
    IntPtr p6 = Marshal.ReadInt Ptr(pp2, 8);
    string s1 = Marshal.PtrToSt ringAnsi(p1);
    string s2 = Marshal.PtrToSt ringAnsi(p2);
    string s3 = Marshal.PtrToSt ringAnsi(p3);
    string s4 = Marshal.PtrToSt ringAnsi(p4);
    string s5 = Marshal.PtrToSt ringAnsi(p5);
    string s6 = Marshal.PtrToSt ringAnsi(p6);
    Console.WriteLi ne(s1 + " " + s2 + " " + s3 + " " + s4 + " " +
    s5 + " " + s6);
    }
    }

    method #2:

    using System;
    using System.Runtime. InteropServices ;

    class MainClass
    {

    [DllImport("D:\\ IDEProjects\\Vi sualStudio\\Ptr PtrPtr\\Release \\PtrPtrPtr.dll ")]
    private static extern IntPtr ptrptrptr();
    public static void Main(string[] args)
    {
    IntPtr ppp = ptrptrptr();
    for(int i = 0; i < 2; i++)
    {
    IntPtr pp = Marshal.ReadInt Ptr(ppp, 4*i);
    for(int j = 0; j < 3; j++)
    {
    IntPtr p = Marshal.ReadInt Ptr(pp, 4*j);
    string s = Marshal.PtrToSt ringAnsi(p);
    Console.WriteLi ne(s);
    }
    }
    }
    }

    method #3:

    using System;
    using System.Runtime. InteropServices ;

    class MainClass
    {

    [DllImport("D:\\ IDEProjects\\Vi sualStudio\\Ptr PtrPtr\\Release \\PtrPtrPtr.dll ")]
    private static extern IntPtr ptrptrptr();
    public static void Main(string[] args)
    {
    IntPtr ppp = ptrptrptr();
    for(int i = 0; i < 2; i++)
    {
    IntPtr pp = Marshal.ReadInt Ptr(ppp, 4*i);
    for(int j = 0; j < 3; j++)
    {
    IntPtr p = Marshal.ReadInt Ptr(pp, 4*j);
    for(int k = 0; k < (3*i+j+1); k++)
    {
    byte b = Marshal.ReadByt e(p, k);
    Console.Write(( char)b);
    }
    Console.WriteLi ne();
    }
    }
    }
    }

    method #4:

    using System;
    using System.Runtime. InteropServices ;

    unsafe class MainClass
    {

    [DllImport("D:\\ IDEProjects\\Vi sualStudio\\Ptr PtrPtr\\Release \\PtrPtrPtr.dll ")]
    private static extern byte ***ptrptrptr();
    public static void Main(string[] args)
    {
    byte ***ppp = ptrptrptr();
    for(int i = 0; i < 2; i++)
    {
    for(int j = 0; j < 3; j++)
    {
    for(int k = 0; k < (3*i+j+1); k++)
    {
    Console.Write(( char)ppp[i][j][k]);
    }
    Console.WriteLi ne();
    }
    }
    }
    }

    Arne

    Comment

    • MrAsm

      #3
      Re: C++ / C# Interop - Returning a char **

      On 25 Oct 2006 13:31:49 -0700, "DG is a god...."
      <richardmatthew hill@hotmail.co mwrote:
      >Dear All
      >,
      This is my first post - please go easy...!
      Don't warry. Also me I'm new in the newsgroup :)
      >I have a DLL written in C++ that has the following function exported
      >from it -:
      >
      char** ListHandles(int *processID);
      >
      >The processID parameter is used to find all associated open file
      >handles , this value is then written to to return the number of strings
      >returned in the char**. The return value is an array of strings
      >listing a set of file handles.
      I'm absolutely not a C# expert.
      But I think your function prototype hides a problem.
      i.e. I suppose that your strings are dynamically allocated on the heap
      by your C++ code, right? For example, you use new char[len+1].
      So, to avoid memory leaks, you should clear the dynamic strings from
      the heap.

      If you allocate string buffer with new[], you should use delete[].
      If you allocate string buffer with malloc(), you should use free().

      I don't know if the C# run-time could know what allocation method you
      used, and so do proper cleanup. I think it cannot.

      If I were passed string arrays from C++ to Visual Basic 6, I would use
      SAFEARRAY of BSTRs. BSTRs are COM strings (Unicode null-terminated
      strings, with the character count preceding the first string
      character), allocated in C++ using SysAllocString.
      So the run-time of the caller (Visual Basic, C#...) should be able to
      correctly free them using SysFreeString() internally (I suppose).

      So, my suggestion to you is to write C++ code to pack your "char **"
      into a more safe and robust data-structure: a SAFEARRAY of BSTRs, and
      pass it to C#. I think the C# run-time could be able to correctly
      manage it, like Visual Basic 6 runtime does.

      BTW: BSTRs are Unicode (UTF16, i.e. 16 bits chars or wchar_t) strings,
      so if your string are ANSI, you should convert them to Unicode first
      (e.g. use MultiByteToWide Chars Win32 API).

      Mr.Asm

      Comment

      • MrAsm

        #4
        Re: C++ / C# Interop - Returning a char **

        On Fri, 27 Oct 2006 07:50:04 GMT, MrAsm <invalid@invali d.comwrote:
        >So, my suggestion to you is to write C++ code to pack your "char **"
        >into a more safe and robust data-structure: a SAFEARRAY of BSTRs, and
        >pass it to C#. I think the C# run-time could be able to correctly
        >manage it, like Visual Basic 6 runtime does.
        Moreover, if you want to make your life easier, and ignore the
        SAFEARRAY of BSTR construction, you could do the following:

        1. Write a C++ code that wraps your DLL function, and that packs all
        the string returned by your DLL function into a single big string.
        The sub-strings can be separated using some character (e.g. space).

        2. You pass this big string from C++ to C#, e.g using BSTR

        3. Write C# code to unpack the big string into the sub-strings (I'm
        sure C# has library code to do this with simple method calls).

        Another alternative could be to use managed C++ to transfer your
        string array to C#.

        Comment

        Working...