P/Invoke Problem

G

Guest

Hello,

I have the following C functions exported from a DLL:

void copyString2(const char * sSrcBuf, long nSrcBufSize, char ** sDestBuf);
void freeString(char * sBuf);

The idea is to copy the source buffer into the dynamically allocated
destination buffer returning the pointer to the buffer to C# then pushing the
pointer back to the DLL for deallocation.

I'm having a tough time declaring the appropriate type for the char **
sDestBuf parameter. I've been trying to use various combination of IntPtr
declarations but no matter what I define at runtime the pointer passed to the
C library is NULL for the sDestBuf.

For example:
[DllImport("nativelibtest.dll", CharSet = CharSet.Ansi)]
public static extern unsafe
bool copyString2(string sSrcStr, long nSrcSize, IntPtr * sDestBuf);

IntPtr pPtr = new IntPtr();

bool bSuccess = NativeLibTest.copyString2(sMsg, sMsg.Length, &pPtr);

In the debugger, &pPtr certainly has an address but that address is not
passed to the DLL.

Any help would be appreciated.
 
N

Nicholas Paldino [.NET/C# MVP]

Mike,

You don't need unsafe code to do this. You can just pass the last
parameter as a ref IntPtr, and then the IntPtr should have a value written
to it. You then have to pass that IntPtr to the static PtrToStringAnsi
method on the Marshal class to get a String instance you can work with.

Of course, you have to make sure you pass the IntPtr back to freeString.
You can just declare the parameter of that function as an IntPtr, and it
should work fine.
 
M

Mattias Sjögren

void copyString2(const char * sSrcBuf, long nSrcBufSize, char ** sDestBuf);
[...]
[DllImport("nativelibtest.dll", CharSet = CharSet.Ansi)]
public static extern unsafe
bool copyString2(string sSrcStr, long nSrcSize, IntPtr * sDestBuf);

nSrcSize should be an int in C# to match the long in C. And how did
the void return type became bool on the C# side?


Mattias
 
G

Guest

Oh, that was just me switching back and forth between copy/paste and
typing...the both should have been bool.
 
G

Guest

Well, I tried the following based to your suggestion.

//C Declaration
NATIVELIBTEST_API
bool copyString2
(const char * sSrcBuf, long nSrcBufSize, char ** sDestBuf);

//C# Declaration
[DllImport("nativelibtest.dll", CharSet = CharSet.Ansi)]
public static extern
bool copyString2(string sSrcStr, long nSrcSize, ref IntPtr sDestBuf);

//Code
string sBlah = "blah blah blah";
IntPtr pPtr = new IntPtr()
bool bSuccess = copyString2(sBlah, sBlah.Length, ref pPtr);

Unfortunately the C function's 3rd parameter char ** sDestBuf is zero on
entry.

Since the C function is using a 2 levels of indirection, I tried the
following...

unsafe
{
IntPtr pPtr = new IntPtr();
IntPtr ppPtr = new IntPtr(&pPtr);

bool bSuccess = copyString2(sBlah, sBlah.Length, ref ppPtr);
etc...
}

No difference...the address of the IntPtr is not being passed over to the
native library.

Then I found this article in MSDN.
http://msdn2.microsoft.com/en-us/library/awbckfbz(VS.71).aspx

Looks like I need to use a reference to a class in order to deal with 2
levels of indirection. I'm going to give it a try now.
 
G

Guest

Nope, not having too much luck.

Anybody have any p/invoke samples calling a library function with a
parameter using 2 level of indirection where the C function allocates memory
and returns the pointer back to C#? Then the C# caller is responsible for
calling a "free" function exposed by the native library.
 
W

Willy Denoyette [MVP]

Mike M said:
Nope, not having too much luck.

Anybody have any p/invoke samples calling a library function with a
parameter using 2 level of indirection where the C function allocates
memory
and returns the pointer back to C#? Then the C# caller is responsible for
calling a "free" function exposed by the native library.


// C# code
[DllImport("xxxxxx"), SuppressUnmanagedCodeSecurity] static extern void
Test(out IntPtr s);

....
IntPtr buffer;
Test(out buffer);
Console.WriteLine(Marshal.PtrToStringAnsi(buffer));

....

// C code

extern "C"
{
void __declspec(dllexport) __stdcall Test(void **ptr)
{
int bufferSize = 25;
*ptr = new unsigned char(bufferSize );
strcpy_s((char*)*ptr, bufferSize, "Hello from C");
}
}

Willy.
 
G

Guest

Thanks for the help.

I've taking your sample and added it to my DLL and C# unit-tester but the
results are the same as before. Dereferencing the char **ptr causes an
access violation. Other then your use of the SuppressUnmanagedCodeSecurity
attribute and the out versus ref parameter modifier there is essentially no
difference between what I've been doing and your sample.

Based on the MSDN article I found earlier, I'm expecting to use a reference
or output modifier to a reference type and not a value type in order to
specify 2 levels of indirection.

I'm trying not to resort to a completely "unsafe" solution but I'm pretty
much at the end of the rope...I've been spending too much time for something
that shouldn't be this difficult.

Again, thank you for your time.

Cheers,
Mike
 
W

Willy Denoyette [MVP]

Mike M said:
Thanks for the help.

I've taking your sample and added it to my DLL and C# unit-tester but the
results are the same as before. Dereferencing the char **ptr causes an
access violation. Other then your use of the
SuppressUnmanagedCodeSecurity
attribute and the out versus ref parameter modifier there is essentially
no
difference between what I've been doing and your sample.

Based on the MSDN article I found earlier, I'm expecting to use a
reference
or output modifier to a reference type and not a value type in order to
specify 2 levels of indirection.

I'm trying not to resort to a completely "unsafe" solution but I'm pretty
much at the end of the rope...I've been spending too much time for
something
that shouldn't be this difficult.

Again, thank you for your time.

Cheers,
Mike

You must be doing something wrong, herewith a complete sample that
illustrates what you are trying to achieve...

// C file - sample.cpp
#include <cstdio>
#include <strsafe.h>
extern "C"
{
void __declspec(dllexport) __stdcall GetBuffer(char* s, int length, void
**ptr)
{
int bufferSize = 100;
*ptr = new char[bufferSize];
memset(*ptr, '0', size);
strcpy_s(static_cast< char*>(*ptr), length + 1, s);
}
void __declspec(dllexport) __stdcall FreeBuffer(void *ptr)
{
printf_s("In C %x\n", ptr);
delete[] ptr;
}
}

// C# file
using System;
using System.Runtime.InteropServices;
using System.Security;

namespace Test
{

class Program
{
[DllImport("sample"), SuppressUnmanagedCodeSecurity] static extern void
GetBuffer(string s, int size, out IntPtr o);
[DllImport("sample"), SuppressUnmanagedCodeSecurity] static extern void
FreeBuffer(IntPtr s);

static void Main()

{
IntPtr buffer;
string s = "aaaaaaabbbbbbbbbbb";
GetBuffer(s, s.Length, out buffer);
Console.WriteLine(Marshal.PtrToStringAnsi(buffer));
if(buffer != IntPtr.Zero)
FreeBuffer(buffer);
}
}
}
 
G

Guest

Thank you for your time.

My problem was the bit width of the nSrcSize integer parameter in my C#
declaration. Should have been using int as Mattias pointed out earlier.

Thanks again,
Mike
 

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