P/Invoke and out parameter

  • Thread starter Thread starter Pollux
  • Start date Start date
P

Pollux

I'm having a problem with something I thought was quite simple.

I have a function in a C DLL. Let's call it SomeFunction. This is the
prototype for SomeFunction:

void SomeFunction(char * anArrayOfChars);

where anArrayOfChars is an out parameter.

I thought all I had to do was declare it as follows:

[DllImport("someDLL", CharSet = CharSet.Auto)]
public static extern void SomeFunction(StringBuilder sBuf)


and within the Main function call it like that:

StringBuilder sBuf = new StringBuilder(128);
SomeFunction(sBuf);

And I should have an sBuf that was filled by some function.
Unfortunately, I'm getting the following exception:

Object Reference not set to an instance of an object. Any ideas what I'm
doing wrong?

In case you wonder, SomeFunction just does a strcpy(anArrayOfChars,
"Hello, World!);

Thanks in advance.
 
I thought all I had to do was declare it as follows:

[DllImport("someDLL", CharSet = CharSet.Auto)]
public static extern void SomeFunction(StringBuilder sBuf)

You should be using CharSet.Ansi rather than CharSet.Auto here. I
don't know if that would cause such an exception though.



Mattias
 
Pollux said:
I'm having a problem with something I thought was quite simple.

I have a function in a C DLL. Let's call it SomeFunction. This is the
prototype for SomeFunction:

void SomeFunction(char * anArrayOfChars);

where anArrayOfChars is an out parameter.

I thought all I had to do was declare it as follows:

[DllImport("someDLL", CharSet = CharSet.Auto)]
public static extern void SomeFunction(StringBuilder sBuf)


and within the Main function call it like that:

StringBuilder sBuf = new StringBuilder(128);
SomeFunction(sBuf);

And I should have an sBuf that was filled by some function.
Unfortunately, I'm getting the following exception:

Object Reference not set to an instance of an object. Any ideas what I'm
doing wrong?

In case you wonder, SomeFunction just does a strcpy(anArrayOfChars,
"Hello, World!);

Thanks in advance.

The exception is obviously not thrown by the call of SomeFunction. Could you
provide a call stack dump?

Because you return an ansi string, you need to change your signature into:
[DllImport("someDLL")]
public static extern void
SomeFunction([MarshalAs(UnmanagedType.LPStr)]StringBuilder sBuf)

Willy.
 
Pollux said:
I'm having a problem with something I thought was quite simple.

I have a function in a C DLL. Let's call it SomeFunction. This is the
prototype for SomeFunction:

void SomeFunction(char * anArrayOfChars);

where anArrayOfChars is an out parameter.

I thought all I had to do was declare it as follows:

[DllImport("someDLL", CharSet = CharSet.Auto)]
public static extern void SomeFunction(StringBuilder sBuf)


and within the Main function call it like that:

StringBuilder sBuf = new StringBuilder(128);
SomeFunction(sBuf);

And I should have an sBuf that was filled by some function.
Unfortunately, I'm getting the following exception:

Object Reference not set to an instance of an object. Any ideas what I'm
doing wrong?

In case you wonder, SomeFunction just does a strcpy(anArrayOfChars,
"Hello, World!);

Thanks in advance.

The exception is obviously not thrown by the call of SomeFunction. Could you
provide a call stack dump?

Because you return an ansi string, you need to change your signature into:
[DllImport("someDLL")]
public static extern void
SomeFunction([MarshalAs(UnmanagedType.LPStr)]StringBuilder sBuf)

Willy.

Hi Willy,

Sorry about that, I should have mentioned this. The call stack suggests
that the exception was thrown from SomeFunction. If i remove the strcpy
statment and simply return or display a messagebox, I don't get an
exception.

I would guess that somehow the char * doesn't point to a valid memory
address, but I'm not sure why this would be hapenning since the
Framework should take care of this for me right?
 
Pollux said:
Sorry about that, I should have mentioned this. The call stack suggests
that the exception was thrown from SomeFunction. If i remove the strcpy
statment and simply return or display a messagebox, I don't get an
exception.

I would guess that somehow the char * doesn't point to a valid memory
address, but I'm not sure why this would be hapenning since the
Framework should take care of this for me right?

I assume this is not exactly what you are doing in your C function (missing
quote).
Could you post the exact C code function?

Willy.
 
Pollux said:
I'm having a problem with something I thought was quite simple.

I have a function in a C DLL. Let's call it SomeFunction. This is the
prototype for SomeFunction:

void SomeFunction(char * anArrayOfChars);

where anArrayOfChars is an out parameter.

I thought all I had to do was declare it as follows:

[DllImport("someDLL", CharSet = CharSet.Auto)]
public static extern void SomeFunction(StringBuilder sBuf)


and within the Main function call it like that:

StringBuilder sBuf = new StringBuilder(128);
SomeFunction(sBuf);

And I should have an sBuf that was filled by some function.
Unfortunately, I'm getting the following exception:

Object Reference not set to an instance of an object. Any ideas what I'm
doing wrong?

In case you wonder, SomeFunction just does a strcpy(anArrayOfChars,
"Hello, World!);

Thanks in advance.

The exception is obviously not thrown by the call of SomeFunction. Could you
provide a call stack dump?

Because you return an ansi string, you need to change your signature into:
[DllImport("someDLL")]
public static extern void
SomeFunction([MarshalAs(UnmanagedType.LPStr)]StringBuilder sBuf)

Willy.

Ok, I might not have given you the whole picture. I just tried doing a
simple program with just the function I mentioned from scratch and it
all worked fine. I didn't even need to specify the Charset property or
use [MarshalAs(UnmanagedType.LPStr)]. I passed in a StringBuilder and it
was correctly filled by the C function.

The problem I'm experiencing is hapenning on a C dll that was created
some time ago.

I have have modified the function I'm calling to just do a strcpy and
return to simplify testing, so it is pretty much the same as my test
function.

The Call Stack wasn't all that useful in that it points to the offendin
C function which we know shouldn't be returning an exception.

I have tried passing a simple string to the function in the DLL and
displaying it in a Messagebox and that comes out as empty, so there is
definitely a problem there.

I can only assume that the problem must be with the project options in
either the C# program (unlikely I suspect) or the C dll. Any ideas what
that might be?

Thanks for your help and sorry for the confusion.
 
Does the unmanaged function use the cdecl calling convention? P?Invoke defaults to winapi

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk

Ok, I might not have given you the whole picture. I just tried doing a
simple program with just the function I mentioned from scratch and it
all worked fine. I didn't even need to specify the Charset property or
use [MarshalAs(UnmanagedType.LPStr)]. I passed in a StringBuilder and it
was correctly filled by the C function.

The problem I'm experiencing is hapenning on a C dll that was created
some time ago.

I have have modified the function I'm calling to just do a strcpy and
return to simplify testing, so it is pretty much the same as my test
function.

The Call Stack wasn't all that useful in that it points to the offendin
C function which we know shouldn't be returning an exception.

I have tried passing a simple string to the function in the DLL and
displaying it in a Messagebox and that comes out as empty, so there is
definitely a problem there.

I can only assume that the problem must be with the project options in
either the C# program (unlikely I suspect) or the C dll. Any ideas what
that might be?

Thanks for your help and sorry for the confusion.
 
Does the unmanaged function use the cdecl calling convention? P?Invoke defaults to winapi

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk

Ok, I might not have given you the whole picture. I just tried doing a
simple program with just the function I mentioned from scratch and it
all worked fine. I didn't even need to specify the Charset property or
use [MarshalAs(UnmanagedType.LPStr)]. I passed in a StringBuilder and it
was correctly filled by the C function.

The problem I'm experiencing is hapenning on a C dll that was created
some time ago.

I have have modified the function I'm calling to just do a strcpy and
return to simplify testing, so it is pretty much the same as my test
function.

The Call Stack wasn't all that useful in that it points to the offendin
C function which we know shouldn't be returning an exception.

I have tried passing a simple string to the function in the DLL and
displaying it in a Messagebox and that comes out as empty, so there is
definitely a problem there.

I can only assume that the problem must be with the project options in
either the C# program (unlikely I suspect) or the C dll. Any ideas what
that might be?

Thanks for your help and sorry for the confusion.

Unfortunately I have thought about this. Both my test dll and my work
dll use the cdecl calling convention, but the framework seems to manage
calling the test dll just fine without me having to specify anything. I
tried manually specifying the calling convention but that didn't help
either. Could it be that the framework defaults to cdecl?

Anyway I think if I was using the wrong calling convetion, the problem
would be that it couldn't find the entry point whereas here it can in
both cases.

Thanks for your help.
 
Ok, I might not have given you the whole picture. I just tried doing a
simple program with just the function I mentioned from scratch and it
all worked fine. I didn't even need to specify the Charset property or
use [MarshalAs(UnmanagedType.LPStr)]. I passed in a StringBuilder and it
was correctly filled by the C function.

The problem I'm experiencing is hapenning on a C dll that was created
some time ago.

I have have modified the function I'm calling to just do a strcpy and
return to simplify testing, so it is pretty much the same as my test
function.

The Call Stack wasn't all that useful in that it points to the offendin
C function which we know shouldn't be returning an exception.

I have tried passing a simple string to the function in the DLL and
displaying it in a Messagebox and that comes out as empty, so there is
definitely a problem there.

I can only assume that the problem must be with the project options in
either the C# program (unlikely I suspect) or the C dll. Any ideas what
that might be?

Thanks for your help and sorry for the confusion.

True, the MarshalAs attribute is not needed as it's the PInvoke interop's
default.
The calling convention is __stdcall by default, and you better change your C
function signature or you specify the CallingConvention.Cdecl attribute
accordingly, failing to do so may corrupt the call stack.
Also make sure you Stringbuilder buffer is large enough to contain the
string.

Willy.
 
Ok, I might not have given you the whole picture. I just tried doing a
simple program with just the function I mentioned from scratch and it
all worked fine. I didn't even need to specify the Charset property or
use [MarshalAs(UnmanagedType.LPStr)]. I passed in a StringBuilder and it
was correctly filled by the C function.

The problem I'm experiencing is hapenning on a C dll that was created
some time ago.

I have have modified the function I'm calling to just do a strcpy and
return to simplify testing, so it is pretty much the same as my test
function.

The Call Stack wasn't all that useful in that it points to the offendin
C function which we know shouldn't be returning an exception.

I have tried passing a simple string to the function in the DLL and
displaying it in a Messagebox and that comes out as empty, so there is
definitely a problem there.

I can only assume that the problem must be with the project options in
either the C# program (unlikely I suspect) or the C dll. Any ideas what
that might be?

Thanks for your help and sorry for the confusion.

True, the MarshalAs attribute is not needed as it's the PInvoke interop's
default.
The calling convention is __stdcall by default, and you better change your C
function signature or you specify the CallingConvention.Cdecl attribute
accordingly, failing to do so may corrupt the call stack.
Also make sure you Stringbuilder buffer is large enough to contain the
string.

Willy.

Unfortunately changing the calling convention to cdecl didn't solve it,
though it is probably a good thing to have set!

As I said, the when I created a dll with the functions that I mentioned
in my original post, I was sucessful in getting the C function in
filling the buffer.

This is what I have in my C# program:

[DllImport("somedll", CallingConvention=CallingConvention.Cdecl)]
public static extern void SomeFunction(long aLong,
[MarshalAs(UnmanagedType.LPStr)] StringBuilder aBuffer, [MarshalAs
(UnmanagedType.LPStr)] StringBuilder anotherBuffer);

The call to the function is as follows:

long aLong = 1;
StringBuilder aBuffer = new StringBuilder(128); //This size should be
more than ample
StringBuilder anotherBuffer = new StringBuilder(128);

SomeFunction(aLong, aBuffer, anotherBuffer);

The C function is as follow:

extern "C" __declspec(dllexport) void SomeFunction(long aLong, char
*aBuffer, char * anotherBuffer)
{
strcpy(aBuffer, "Hello");
strcpy(anotherBuffer, "Hello");
}

Note that I didn't copy and paste so there might be a typo or two, but
it compiles fine! :-)
 
Ok, I might not have given you the whole picture. I just tried doing a
simple program with just the function I mentioned from scratch and it
all worked fine. I didn't even need to specify the Charset property or
use [MarshalAs(UnmanagedType.LPStr)]. I passed in a StringBuilder and it
was correctly filled by the C function.

The problem I'm experiencing is hapenning on a C dll that was created
some time ago.

I have have modified the function I'm calling to just do a strcpy and
return to simplify testing, so it is pretty much the same as my test
function.

The Call Stack wasn't all that useful in that it points to the offendin
C function which we know shouldn't be returning an exception.

I have tried passing a simple string to the function in the DLL and
displaying it in a Messagebox and that comes out as empty, so there is
definitely a problem there.

I can only assume that the problem must be with the project options in
either the C# program (unlikely I suspect) or the C dll. Any ideas what
that might be?

Thanks for your help and sorry for the confusion.

True, the MarshalAs attribute is not needed as it's the PInvoke interop's
default.
The calling convention is __stdcall by default, and you better change your C
function signature or you specify the CallingConvention.Cdecl attribute
accordingly, failing to do so may corrupt the call stack.
Also make sure you Stringbuilder buffer is large enough to contain the
string.

Willy.

Unfortunately changing the calling convention to cdecl didn't solve it,
though it is probably a good thing to have set!

Ok, I was able to reproduce the problem on my test dll as well. It seems
that it happens if you call it with more than one parameter.

[DllImport("somedll", CallingConvention=CallingConvention.Cdecl)]
public static extern void SomeFunction(StringBuilder sBuf);

worked, but this failed:

[DllImport("somedll", CallingConvention=CallingConvention.Cdecl)]
public static extern void SomeFunction(long aLong,
StringBuilder sBuf, StringBuilder anotherBuf);
 
[DllImport("somedll", CallingConvention=CallingConvention.Cdecl)]
public static extern void SomeFunction(StringBuilder sBuf);

worked, but this failed:

[DllImport("somedll", CallingConvention=CallingConvention.Cdecl)]
public static extern void SomeFunction(long aLong,
StringBuilder sBuf, StringBuilder anotherBuf);

Your problem is the first argument. A long in C# in 64 bit, a long in C is
32 bit, change the C# long into int and it should work.

Willy.
 
[DllImport("somedll", CallingConvention=CallingConvention.Cdecl)]
public static extern void SomeFunction(StringBuilder sBuf);

worked, but this failed:

[DllImport("somedll", CallingConvention=CallingConvention.Cdecl)]
public static extern void SomeFunction(long aLong,
StringBuilder sBuf, StringBuilder anotherBuf);

Your problem is the first argument. A long in C# in 64 bit, a long in C is
32 bit, change the C# long into int and it should work.

Willy.

duh, I kept on seeing int64 in the stack trace and it never even crossed
my mind. I even went as far as noticing that the long was causing the
problem. Somebody please slap me!

Thanks for your help! Much appreciated.
 
This can happen to anyone of us ;-).
And this illustrates how easy it is to "corrupt" the call stack and get some
weird exceptions thrown on you when using PInvoke.

Willy.

Pollux said:
[DllImport("somedll", CallingConvention=CallingConvention.Cdecl)]
public static extern void SomeFunction(StringBuilder sBuf);

worked, but this failed:

[DllImport("somedll", CallingConvention=CallingConvention.Cdecl)]
public static extern void SomeFunction(long aLong,
StringBuilder sBuf, StringBuilder anotherBuf);

Your problem is the first argument. A long in C# in 64 bit, a long in C
is
32 bit, change the C# long into int and it should work.

Willy.

duh, I kept on seeing int64 in the stack trace and it never even crossed
my mind. I even went as far as noticing that the long was causing the
problem. Somebody please slap me!

Thanks for your help! Much appreciated.
 
One could be to use the MarshalAs attribute and the other is related to
Out parameter which I will tell you after reading your post.

with regards,


J.V.Ravichandran
- http://www.geocities.com/
jvravichandran
- http://www.411asp.net/func/search?
qry=Ravichandran+J.V.&cob=aspnetpro
- http://www.southasianoutlook.com
- http://www.MSDNAA.Net
- http://www.csharphelp.com
- http://www.poetry.com/Publications/
display.asp?ID=P3966388&BN=999&PN=2
- Or, just search on "J.V.Ravichandran"
at http://www.Google.com
 
Back
Top