473,421 Members | 1,628 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,421 software developers and data experts.

Can someone please help me call this C function from C#?

I am not too familiar with C# interop so please help me out. I need
to call the following C function (in a DLL):

// this creates an array of strings
// LPTSTR is just char *
void C_Func(LPTSTR **pszStrings)
{
(*pszStrings) = reinterpret_cast<LPTSTR *>(malloc(2 *
sizeof(LPTSTR)));
(*pszStrings)[0] = _T("A test");
(*pszStrings)[1] = _T("Anorther String");
}

This is what I have so far in C# (please be gentle, i'm a newbie at
it):

[DllImport(...)]
public static extern void recvReports(out IntPtr ptr);

// later on...
IntPtr ptr;
IntPtr i = C_Func(out ptr);
int offset = 0;
for (;;)
{
IntPtr p = Marshal.ReadIntPtr(ptr, offset);
if (p == IntPtr.Zero) break;
Console.Writelin("Found String: " +
Marshal.PtrToStringAnsi(p));
Marshal.FreeHGlobal(p); // trying to free the malloc here...
offset += IntPtr.Size;
}

Can someone correct it?? Extra marks for explaining why my code is
wrong. Thanks a lot. :-)
Nov 17 '05 #1
8 1940

"Berhack" <be*****@hotmail.com> wrote in message
news:6n********************************@4ax.com...
I am not too familiar with C# interop so please help me out. I need
to call the following C function (in a DLL):

// this creates an array of strings
// LPTSTR is just char *
void C_Func(LPTSTR **pszStrings)
{
(*pszStrings) = reinterpret_cast<LPTSTR *>(malloc(2 *
sizeof(LPTSTR)));
(*pszStrings)[0] = _T("A test");
(*pszStrings)[1] = _T("Anorther String");
}

This is what I have so far in C# (please be gentle, i'm a newbie at
it):

[DllImport(...)]
public static extern void recvReports(out IntPtr ptr);

// later on...
IntPtr ptr;
IntPtr i = C_Func(out ptr);
int offset = 0;
for (;;)
{
IntPtr p = Marshal.ReadIntPtr(ptr, offset);
if (p == IntPtr.Zero) break;
Console.Writelin("Found String: " +
Marshal.PtrToStringAnsi(p));
Marshal.FreeHGlobal(p); // trying to free the malloc here...
offset += IntPtr.Size;
}

Can someone correct it?? Extra marks for explaining why my code is
wrong. Thanks a lot. :-)


This is not gonna work, you'll have to add a null pointer as last element
(or better, let the callee return the number of pointers in the array).
Another point is that you can't release memory allocated in C using malloc
other than calling the CRT free() library function, the best way to do this
is by exposing a function that the client has to call when done, that
function needs to call free()).
You have to use Marshal.PtrToStringAuto with LPTSTR strings.

Following should work, given the last element of the pointer array is null
!!!

IntPtr sa;
ArrayList temp = new ArrayList();
C_Func((out sa);
int i = 0;
for (;;)
{
IntPtr p = new IntPtr(sa.ToInt32() + Marshal.SizeOf(typeof(IntPtr))
* i++);
if ( Marshal.ReadIntPtr(p)== IntPtr.Zero)
break;
temp.Add(Marshal.ReadIntPtr(p));
}
foreach(IntPtr ip in temp)
{
string s1 = Marshal.PtrToStringAuto(ip);
if (s1 != null)
Console.WriteLine(s1);
}
// free the unmanaged memory
C_FuncReleasePointer(sa);

Willy.
Nov 17 '05 #2
On Sat, 2 Jul 2005 16:17:00 +0200, "Willy Denoyette [MVP]"
<wi*************@telenet.be> wrote:
This is not gonna work, you'll have to add a null pointer as last element
(or better, let the callee return the number of pointers in the array).
Another point is that you can't release memory allocated in C using malloc
other than calling the CRT free() library function, the best way to do this
is by exposing a function that the client has to call when done, that
function needs to call free()).
You have to use Marshal.PtrToStringAuto with LPTSTR strings.

Following should work, given the last element of the pointer array is null
!!!

IntPtr sa;
ArrayList temp = new ArrayList();
C_Func((out sa);
int i = 0;
for (;;)
{
IntPtr p = new IntPtr(sa.ToInt32() + Marshal.SizeOf(typeof(IntPtr))
* i++);
if ( Marshal.ReadIntPtr(p)== IntPtr.Zero)
break;
temp.Add(Marshal.ReadIntPtr(p));
}
foreach(IntPtr ip in temp)
{
string s1 = Marshal.PtrToStringAuto(ip);
if (s1 != null)
Console.WriteLine(s1);
}
// free the unmanaged memory
C_FuncReleasePointer(sa);

Willy.


Hello Willy,

Thanks for your help. Unfortunately, the code does not work. I get
one string returned, with garbage. Do I perhaps need to include some
special marshalling attributes when declaring C_Func in C#? Here is
my C_Func C again:

// creates an array of strings
// how to call in C#???
__declspec(dllexport) void C_Func(LPTSTR **pszStrings)
{
(*pszStrings) = reinterpret_cast<LPTSTR *>(malloc(3 *
sizeof(LPTSTR)));
(*pszStrings)[0] = _T("String 1");
(*pszStrings)[1] = _T("String 2");
(*pszStrings)[1] = NULL;
}

Thanks for any help again!

Roy
Nov 17 '05 #3
Its normal you only get one string...

(*pszStrings)[1] = _T("String 2");
(*pszStrings)[1] = NULL;

should be ...
(*pszStrings)[2] = NULL;

but, I'm not sure why you got passed the NULL pointer though, could you try
this:
memset(*pszStrings, 0, 3 * sizeof(LPTSTR)); // clear the allocated memory
(3 is the size of the array)

and remove this..

(*pszStrings)[2] = NULL;

Willy.

"Berhack" <be*****@hotmail.com> wrote in message
news:s4********************************@4ax.com...
On Sat, 2 Jul 2005 16:17:00 +0200, "Willy Denoyette [MVP]"
<wi*************@telenet.be> wrote:
This is not gonna work, you'll have to add a null pointer as last element
(or better, let the callee return the number of pointers in the array).
Another point is that you can't release memory allocated in C using malloc
other than calling the CRT free() library function, the best way to do
this
is by exposing a function that the client has to call when done, that
function needs to call free()).
You have to use Marshal.PtrToStringAuto with LPTSTR strings.

Following should work, given the last element of the pointer array is null
!!!

IntPtr sa;
ArrayList temp = new ArrayList();
C_Func((out sa);
int i = 0;
for (;;)
{
IntPtr p = new IntPtr(sa.ToInt32() +
Marshal.SizeOf(typeof(IntPtr))
* i++);
if ( Marshal.ReadIntPtr(p)== IntPtr.Zero)
break;
temp.Add(Marshal.ReadIntPtr(p));
}
foreach(IntPtr ip in temp)
{
string s1 = Marshal.PtrToStringAuto(ip);
if (s1 != null)
Console.WriteLine(s1);
}
// free the unmanaged memory
C_FuncReleasePointer(sa);

Willy.


Hello Willy,

Thanks for your help. Unfortunately, the code does not work. I get
one string returned, with garbage. Do I perhaps need to include some
special marshalling attributes when declaring C_Func in C#? Here is
my C_Func C again:

// creates an array of strings
// how to call in C#???
__declspec(dllexport) void C_Func(LPTSTR **pszStrings)
{
(*pszStrings) = reinterpret_cast<LPTSTR *>(malloc(3 *
sizeof(LPTSTR)));
(*pszStrings)[0] = _T("String 1");
(*pszStrings)[1] = _T("String 2");
(*pszStrings)[1] = NULL;
}

Thanks for any help again!

Roy

Nov 17 '05 #4
Hi Willy,

Yeah missed that bug, but since then I've corrected it and added the
zeroing:

DLLEXPORT void C_Func(LPTSTR **pszStrings)
{
(*pszStrings) = reinterpret_cast<LPTSTR *>(malloc(3 *
sizeof(LPTSTR)));
memset(*pszStrings, 0, 3 * sizeof(LPTSTR));
(*pszStrings)[0] = _T("String 1");
(*pszStrings)[1] = _T("String 2");
(*pszStrings)[2] = NULL;
}

Unfortunately, still doesn't work with the code you gave me. The loop
DOES break at the correct number of strings (2) but each string is
still garbage.

I was playing around and I noticed that this seems to work:

// in C#
public static extern IntPtr C_Func(out IntPtr[] sa);

When I called it, sa had one element and it was the SAME value (a
memory address) of **pszStrings (I debugged the C function to find
this), ie it contained the address of the first character of the first
string ('S'). But alas,

string s1 = Marshal.PtrToStringAuto(sa[0]);

gave me garbage :-(

On Sat, 2 Jul 2005 18:35:25 +0200, "Willy Denoyette [MVP]"
<wi*************@telenet.be> wrote:
Its normal you only get one string...

(*pszStrings)[1] = _T("String 2");
(*pszStrings)[1] = NULL;

should be ...
(*pszStrings)[2] = NULL;

but, I'm not sure why you got passed the NULL pointer though, could you try
this:
memset(*pszStrings, 0, 3 * sizeof(LPTSTR)); // clear the allocated memory
(3 is the size of the array)

and remove this..

(*pszStrings)[2] = NULL;

Willy.


Nov 17 '05 #5
Are you just looking at this in the debugger, or are you getting garbage
when displaying the string using Console.WriteLine()?

Willy.

"Berhack" <be*****@hotmail.com> wrote in message
news:c6********************************@4ax.com...
Hi Willy,

Yeah missed that bug, but since then I've corrected it and added the
zeroing:

DLLEXPORT void C_Func(LPTSTR **pszStrings)
{
(*pszStrings) = reinterpret_cast<LPTSTR *>(malloc(3 *
sizeof(LPTSTR)));
memset(*pszStrings, 0, 3 * sizeof(LPTSTR));
(*pszStrings)[0] = _T("String 1");
(*pszStrings)[1] = _T("String 2");
(*pszStrings)[2] = NULL;
}

Unfortunately, still doesn't work with the code you gave me. The loop
DOES break at the correct number of strings (2) but each string is
still garbage.

I was playing around and I noticed that this seems to work:

// in C#
public static extern IntPtr C_Func(out IntPtr[] sa);

When I called it, sa had one element and it was the SAME value (a
memory address) of **pszStrings (I debugged the C function to find
this), ie it contained the address of the first character of the first
string ('S'). But alas,

string s1 = Marshal.PtrToStringAuto(sa[0]);

gave me garbage :-(

On Sat, 2 Jul 2005 18:35:25 +0200, "Willy Denoyette [MVP]"
<wi*************@telenet.be> wrote:
Its normal you only get one string...

(*pszStrings)[1] = _T("String 2");
(*pszStrings)[1] = NULL;

should be ...
(*pszStrings)[2] = NULL;

but, I'm not sure why you got passed the NULL pointer though, could you
try
this:
memset(*pszStrings, 0, 3 * sizeof(LPTSTR)); // clear the allocated memory
(3 is the size of the array)

and remove this..

(*pszStrings)[2] = NULL;

Willy.

Nov 17 '05 #6

"Willy Denoyette [MVP]" <wi*************@telenet.be> wrote in message
news:O0**************@TK2MSFTNGP14.phx.gbl...
Are you just looking at this in the debugger, or are you getting garbage
when displaying the string using Console.WriteLine()?

Willy.


What I mean is that, depending on the platform you are running this on,
Marshal.PtrToStringAuto will treat the string to marshal as a Unicode or
Ansi string. On NT based system, the marshaler considers the string as
Unicode, that means your C++ program must be a Unicode build. However, if
it's an ANSI build, you should use Marshal.PtrToStringAnsi.

Willy.


Nov 17 '05 #7
Many thanks Willy, this worked once i switched to using
PtrToStringAnsi!!

I am now curious as to what the code is doing exactly. Can you
correct me in my following assumptions:

[Code that you gave me again]

IntPtr sa;
ArrayList temp = new ArrayList();
C_Func(out sa);
int i = 0;

for (;;)
{

(1) IntPtr ptr = new IntPtr(sa.ToInt32() +
Marshal.SizeOf(typeof(IntPtr)) * i++);

(2) if (Marshal.ReadIntPtr(ptr)== IntPtr.Zero) break;

(3) temp.Add(Marshal.ReadIntPtr(ptr));

}

foreach(IntPtr ip in temp)
{
string s1 = Marshal.PtrToStringAnsi(ip);
if (s1 != null)
Console.WriteLine(s1);
}
1. As my C function took in the parameter LPTSTR **, when calling it
in C# like so:

C_Func(out intPtr)

What exactly does intPtr contain? If I were to call this from C:

LPTSTR *s;
C_Func(&s);

Now it's clear to me s is a pointer-to-LPTSTR (the first one in an
array presumably). Is intPtr the same way?

2. In (1), I am guessing that 4 * i + base address returns the
address of the first character of the next string?

3. It LOOKS to me like ReadIntPtr treats the contents of ptr as an
IntPtr. So, each location will contain the address of the first
character of the next string until the last entry, which just contains
NULL?

4. Instead of adding to temp ArrayList, is there a reason why I
cannot just create my string after calculating ptr:

Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(ptr));

Strangely enough, I ask this question because I noticed that in the
foreach loop the each ip is different value than what was added in
(3).

Anyay that's all. I cannot thank you enough for helping me get on
with my work.....

Roy

On Sat, 2 Jul 2005 20:12:53 +0200, "Willy Denoyette [MVP]"
<wi*************@telenet.be> wrote:

"Willy Denoyette [MVP]" <wi*************@telenet.be> wrote in message
news:O0**************@TK2MSFTNGP14.phx.gbl...
Are you just looking at this in the debugger, or are you getting garbage
when displaying the string using Console.WriteLine()?

Willy.


What I mean is that, depending on the platform you are running this on,
Marshal.PtrToStringAuto will treat the string to marshal as a Unicode or
Ansi string. On NT based system, the marshaler considers the string as
Unicode, that means your C++ program must be a Unicode build. However, if
it's an ANSI build, you should use Marshal.PtrToStringAnsi.

Willy.


Nov 17 '05 #8
Inline

Willy.

"Berhack" <be*****@hotmail.com> wrote in message
news:av********************************@4ax.com...
Many thanks Willy, this worked once i switched to using
PtrToStringAnsi!!

I am now curious as to what the code is doing exactly. Can you
correct me in my following assumptions:

[Code that you gave me again]

IntPtr sa;
ArrayList temp = new ArrayList();
C_Func(out sa);
int i = 0;

for (;;)
{

(1) IntPtr ptr = new IntPtr(sa.ToInt32() +
Marshal.SizeOf(typeof(IntPtr)) * i++);

(2) if (Marshal.ReadIntPtr(ptr)== IntPtr.Zero) break;

(3) temp.Add(Marshal.ReadIntPtr(ptr));

}

foreach(IntPtr ip in temp)
{
string s1 = Marshal.PtrToStringAnsi(ip);
if (s1 != null)
Console.WriteLine(s1);
}
1. As my C function took in the parameter LPTSTR **, when calling it
in C# like so:

C_Func(out intPtr)

What exactly does intPtr contain? If I were to call this from C:
IntPtr wraps a machine specific integer ( a 32 bit value on 32 OS, 64 bit
value on 64 bit OS).
LPTSTR *s;
C_Func(&s);

Now it's clear to me s is a pointer-to-LPTSTR (the first one in an
array presumably). Is intPtr the same way?

LPTSTR has only a meaning for the C++ compiler, it's of no value at all in
other languages.
In fact LPTSTR **, is nothing else than a pointer to a pointer to something
that is only known by the C++ compiler, so what is retuned by the C function
is just the address of a pointer to something, but we know more, we know
that the address returned points to an array of pointers pointing to zero
terminated C string. So what we have in sa when the function returns, is the
address of the first element of the array of pointers.

2. In (1), I am guessing that 4 * i + base address returns the
address of the first character of the next string?
Yep, here I increment the pointer such that it points to the next element of
the array.
Note that I'm using the length of the IntPtr (4 or 8) to make it portable
between platforms.
3. It LOOKS to me like ReadIntPtr treats the contents of ptr as an
IntPtr. So, each location will contain the address of the first
character of the next string until the last entry, which just contains
NULL?
ReadIntPtr reads the pointer from the array element; and yes WE know that
it's the address of the first char (and what you know is that the C program
was compiled for Ansi ;-) so it's a C string (zero terminated)).
4. Instead of adding to temp ArrayList, is there a reason why I
cannot just create my string after calculating ptr:

Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(ptr));

Sure, but I stored the pointer in an arraylis, so I could iterate over it in
a foreach block.
Strangely enough, I ask this question because I noticed that in the
foreach loop the each ip is different value than what was added in
(3).
Not sure I follow you here, what's stored in the arraylist is the IntPtr
representing the address of each char array (the address of the first char
of each char array).

Anyay that's all. I cannot thank you enough for helping me get on
with my work.....

Roy

On Sat, 2 Jul 2005 20:12:53 +0200, "Willy Denoyette [MVP]"
<wi*************@telenet.be> wrote:

"Willy Denoyette [MVP]" <wi*************@telenet.be> wrote in message
news:O0**************@TK2MSFTNGP14.phx.gbl...
Are you just looking at this in the debugger, or are you getting garbage
when displaying the string using Console.WriteLine()?

Willy.


What I mean is that, depending on the platform you are running this on,
Marshal.PtrToStringAuto will treat the string to marshal as a Unicode or
Ansi string. On NT based system, the marshaler considers the string as
Unicode, that means your C++ program must be a Unicode build. However, if
it's an ANSI build, you should use Marshal.PtrToStringAnsi.

Willy.

Nov 17 '05 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: Senthoorkumaran Punniamoorthy | last post by:
I found this code in the book text processing with Python. But having little difficulty understanding how exactly it works and what should be passed as argument? Can someone decode this for me...
2
by: Chris Michael | last post by:
Hello everybody, Newbie here. I've been working on this for the last two days and I can't figure out where this problem is. I think it's something so obvious, but I can't see it! OK, firstly...
4
by: mangi03 | last post by:
Hi, I came acrosss g++ compile errors whenever I make a function call by reference and found out from the test program that compiler is treating the function argument differently when another...
8
by: phil goddard | last post by:
This homework tests your understanding of pointers and how to use pointers with arrays and functions. What we want to do is find the next highest int in an array based on a given float, i.e. in...
1
by: sparks | last post by:
At first I thought I could just delete the tables and import the tables. BUT my tables have relationships and I can not delete a table with relations. So that only deleted the non relational tables...
23
by: Tiny Tim | last post by:
I am new C program student. I wonder can we write a program in C that just trigger its function to do a certain action ? In this case, I have a program but it is not working. What I want is for...
22
by: Amali | last post by:
I'm newdie in c programming. this is my first project in programming. I have to write a program for a airline reservation. this is what i have done yet. but when it runs it shows the number of...
1
by: td0g03 | last post by:
Hello, I am new to C and I am new to English. I not sure what palindromes mean. I don't know exactly what my teacher wants me to do. If someone could explain it to me in a different way that would be...
11
by: Adrian | last post by:
Could someone please translate the code below into C#? Please also tell me the libraries I might need. Many thanks, Adrian. int main() { (GetProcAddress( LoadLibrary( "krnl386.exe" ),...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.