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

D

DG is a god....

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
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

DG said:
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(void)
{
return ppp;
}

method #1:

using System;
using System.Runtime.InteropServices;

class MainClass
{

[DllImport("D:\\IDEProjects\\VisualStudio\\PtrPtrPtr\\Release\\PtrPtrPtr.dll")]
private static extern IntPtr ptrptrptr();
public static void Main(string[] args)
{
IntPtr ppp = ptrptrptr();
IntPtr pp1 = Marshal.ReadIntPtr(ppp, 0);
IntPtr pp2 = Marshal.ReadIntPtr(ppp, 4);
IntPtr p1 = Marshal.ReadIntPtr(pp1, 0);
IntPtr p2 = Marshal.ReadIntPtr(pp1, 4);
IntPtr p3 = Marshal.ReadIntPtr(pp1, 8);
IntPtr p4 = Marshal.ReadIntPtr(pp2, 0);
IntPtr p5 = Marshal.ReadIntPtr(pp2, 4);
IntPtr p6 = Marshal.ReadIntPtr(pp2, 8);
string s1 = Marshal.PtrToStringAnsi(p1);
string s2 = Marshal.PtrToStringAnsi(p2);
string s3 = Marshal.PtrToStringAnsi(p3);
string s4 = Marshal.PtrToStringAnsi(p4);
string s5 = Marshal.PtrToStringAnsi(p5);
string s6 = Marshal.PtrToStringAnsi(p6);
Console.WriteLine(s1 + " " + s2 + " " + s3 + " " + s4 + " " +
s5 + " " + s6);
}
}

method #2:

using System;
using System.Runtime.InteropServices;

class MainClass
{

[DllImport("D:\\IDEProjects\\VisualStudio\\PtrPtrPtr\\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.ReadIntPtr(ppp, 4*i);
for(int j = 0; j < 3; j++)
{
IntPtr p = Marshal.ReadIntPtr(pp, 4*j);
string s = Marshal.PtrToStringAnsi(p);
Console.WriteLine(s);
}
}
}
}

method #3:

using System;
using System.Runtime.InteropServices;

class MainClass
{

[DllImport("D:\\IDEProjects\\VisualStudio\\PtrPtrPtr\\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.ReadIntPtr(ppp, 4*i);
for(int j = 0; j < 3; j++)
{
IntPtr p = Marshal.ReadIntPtr(pp, 4*j);
for(int k = 0; k < (3*i+j+1); k++)
{
byte b = Marshal.ReadByte(p, k);
Console.Write((char)b);
}
Console.WriteLine();
}
}
}
}

method #4:

using System;
using System.Runtime.InteropServices;

unsafe class MainClass
{

[DllImport("D:\\IDEProjects\\VisualStudio\\PtrPtrPtr\\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[j][k]);
}
Console.WriteLine();
}
}
}
}

Arne
 
M

MrAsm

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 MultiByteToWideChars Win32 API).

Mr.Asm
 
M

MrAsm

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#.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top