Help calling function from DLL and processing returned value (Can not marshal return value)

A

Angel

Hi again,
I'm trying to call functions from a proprietary DLL but it's turned out to
be more difficult than I thought.

I have this W32.DLL which was written in C by USPS. They don't provide the
code so I only have the documentation.
I'm trying to call a function called z4date that, according to the docs,
returns the date as "an 8-byte character string in the "YYYYMMDD" format".
When I run it with this code I've written , I get "Can not marshal return
value".

My real concern is that, if I'm having trouble with these functions that
return simple data types, how am I going to interact with functions that
return user-defined data types?

This is what I've written in the class so far:

....
using System.Runtime.InteropServices;
namespace ZM7
{
/// <summary>
/// Summary description for AMS.
/// </summary>
public class AMS
{
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern int z4open(); //another function from the dll
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")] /* Do I need to use
DllImport for every fiunction I'm using? */
public static extern byte[] z4date();
....

public byte[] getDate()
{
Byte[] myDate;
myDate = new Byte[8];
myDate = z4date(); //function call where I receive error "Can not
marshal return value"
return myDate();
}

Any help is appreciated.
 
N

Nicholas Paldino [.NET/C# MVP]

Angel,

You will not be able to do this. The reason that it doesn't work with
your definitions is because the pointer that is being returned by the
function is of an indeterminate nature. True, the documentation states that
it returns eight bytes, but there is nothing in the code that indicates that
is the case.

To get around this, you should declare your return type as an IntPtr,
and then pass that to the static PtrToStringAnsi method on the Marshal
class.

The only thing to be considerate of is the fact that the function
allocated memory and returned it. To prevent a leak, you will have to
dispose of the memory pointed to by the IntPtr. How you do this is
dependent on how the DLL allocates it, and will require another call through
P/Invoke.

Also, you do have to use the attribute for every declaration.

Hope this helps.
 
S

Stoitcho Goutsev \(100\) [C# MVP]

Hi Angel,
If your have *z4date* returning a string. How do you expect PInvoke to
marshal it as an array of bytes
Instead of declare the function as:
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern byte[] z4date();

try to declare it as:
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern string z4date();

If you need that byte array you should convert the string to that array. We
cannot expect the marshaler to convert from any to any type we want.


Yes, you have to use DllImport for each and any function that you import
from unmanaged library.
 
N

Nicholas Paldino [.NET/C# MVP]

Stoitcho,

The problem with declaring the return value as a string is that the
memory allocated on the heap for the string in the unmanaged realm will now
not be released correctly, and cause a memory leak.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Stoitcho Goutsev (100) said:
Hi Angel,
If your have *z4date* returning a string. How do you expect PInvoke to
marshal it as an array of bytes
Instead of declare the function as:
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern byte[] z4date();

try to declare it as:
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern string z4date();

If you need that byte array you should convert the string to that array. We
cannot expect the marshaler to convert from any to any type we want.


Yes, you have to use DllImport for each and any function that you import
from unmanaged library.
--
HTH
B\rgds
100

Angel said:
Hi again,
I'm trying to call functions from a proprietary DLL but it's turned out to
be more difficult than I thought.

I have this W32.DLL which was written in C by USPS. They don't provide the
code so I only have the documentation.
I'm trying to call a function called z4date that, according to the docs,
returns the date as "an 8-byte character string in the "YYYYMMDD" format".
When I run it with this code I've written , I get "Can not marshal return
value".

My real concern is that, if I'm having trouble with these functions that
return simple data types, how am I going to interact with functions that
return user-defined data types?

This is what I've written in the class so far:

...
using System.Runtime.InteropServices;
namespace ZM7
{
/// <summary>
/// Summary description for AMS.
/// </summary>
public class AMS
{
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern int z4open(); //another function from the dll
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")] /* Do I need to use
DllImport for every fiunction I'm using? */
public static extern byte[] z4date();
...

public byte[] getDate()
{
Byte[] myDate;
myDate = new Byte[8];
myDate = z4date(); //function call where I receive error "Can not
marshal return value"
return myDate();
}

Any help is appreciated.
 
A

Angel

I had tried it before but I kept getting "Object reference not set to an
instance of an object" when I moved the return value of the function to a
string variable.I tried it without the static modifier and got the same
results.

[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]

public static extern string z4date();

....

public static string getDate()

{

string mystr;

mystr = z4date(); // error: Object reference not set to an instance
of an object

return mystr;

}

I'm trying with Nicholas' suggestion to see if it works.
Thanks.


Stoitcho Goutsev (100) said:
Hi Angel,
If your have *z4date* returning a string. How do you expect PInvoke to
marshal it as an array of bytes
Instead of declare the function as:
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern byte[] z4date();

try to declare it as:
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern string z4date();

If you need that byte array you should convert the string to that array. We
cannot expect the marshaler to convert from any to any type we want.


Yes, you have to use DllImport for each and any function that you import
from unmanaged library.
--
HTH
B\rgds
100

Angel said:
Hi again,
I'm trying to call functions from a proprietary DLL but it's turned out to
be more difficult than I thought.

I have this W32.DLL which was written in C by USPS. They don't provide the
code so I only have the documentation.
I'm trying to call a function called z4date that, according to the docs,
returns the date as "an 8-byte character string in the "YYYYMMDD" format".
When I run it with this code I've written , I get "Can not marshal return
value".

My real concern is that, if I'm having trouble with these functions that
return simple data types, how am I going to interact with functions that
return user-defined data types?

This is what I've written in the class so far:

...
using System.Runtime.InteropServices;
namespace ZM7
{
/// <summary>
/// Summary description for AMS.
/// </summary>
public class AMS
{
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern int z4open(); //another function from the dll
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")] /* Do I need to use
DllImport for every fiunction I'm using? */
public static extern byte[] z4date();
...

public byte[] getDate()
{
Byte[] myDate;
myDate = new Byte[8];
myDate = z4date(); //function call where I receive error "Can not
marshal return value"
return myDate();
}

Any help is appreciated.
 
A

Angel

Thanks for the help.
I think I understand what you're saying but since I'm pretty new with this
type of programming, how would that look like?
So far, I have:

[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern IntPtr z4date();

....

public static IntPtr getDate()

{

IntPtr ptr ;

ptr = z4date(); // ptr.ToString() = "2"

string str = Marshal.PtrToStringAnsi(ptr);

MessageBox.Show (str); //str is null

return str;

}

But the debugger tells me that str is null, so I know I'm doing something
wrong. If I don't move it to a string (eg. MessageBox.Show
(ptr.ToString());), it displays a "2".

Angel







Nicholas Paldino said:
Angel,

You will not be able to do this. The reason that it doesn't work with
your definitions is because the pointer that is being returned by the
function is of an indeterminate nature. True, the documentation states that
it returns eight bytes, but there is nothing in the code that indicates that
is the case.

To get around this, you should declare your return type as an IntPtr,
and then pass that to the static PtrToStringAnsi method on the Marshal
class.

The only thing to be considerate of is the fact that the function
allocated memory and returned it. To prevent a leak, you will have to
dispose of the memory pointed to by the IntPtr. How you do this is
dependent on how the DLL allocates it, and will require another call through
P/Invoke.

Also, you do have to use the attribute for every declaration.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Angel said:
Hi again,
I'm trying to call functions from a proprietary DLL but it's turned out to
be more difficult than I thought.

I have this W32.DLL which was written in C by USPS. They don't provide the
code so I only have the documentation.
I'm trying to call a function called z4date that, according to the docs,
returns the date as "an 8-byte character string in the "YYYYMMDD" format".
When I run it with this code I've written , I get "Can not marshal return
value".

My real concern is that, if I'm having trouble with these functions that
return simple data types, how am I going to interact with functions that
return user-defined data types?

This is what I've written in the class so far:

...
using System.Runtime.InteropServices;
namespace ZM7
{
/// <summary>
/// Summary description for AMS.
/// </summary>
public class AMS
{
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern int z4open(); //another function from the dll
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")] /* Do I need to use
DllImport for every fiunction I'm using? */
public static extern byte[] z4date();
...

public byte[] getDate()
{
Byte[] myDate;
myDate = new Byte[8];
myDate = z4date(); //function call where I receive error "Can not
marshal return value"
return myDate();
}

Any help is appreciated.
 
R

Ralph

Angel said:
Hi again,
I'm trying to call functions from a proprietary DLL but it's turned out to
be more difficult than I thought.

I have this W32.DLL which was written in C by USPS. They don't provide the
code so I only have the documentation.
I'm trying to call a function called z4date that, according to the docs,
returns the date as "an 8-byte character string in the "YYYYMMDD" format".
When I run it with this code I've written , I get "Can not marshal return
value".

My real concern is that, if I'm having trouble with these functions that
return simple data types, how am I going to interact with functions that
return user-defined data types?

This is what I've written in the class so far:

...
using System.Runtime.InteropServices;
namespace ZM7
{
/// <summary>
/// Summary description for AMS.
/// </summary>
public class AMS
{
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern int z4open(); //another function from the dll
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")] /* Do I need to use
DllImport for every fiunction I'm using? */
public static extern byte[] z4date();
...

public byte[] getDate()
{
Byte[] myDate;
myDate = new Byte[8];
myDate = z4date(); //function call where I receive error "Can not
marshal return value"
return myDate();
}

Any help is appreciated.

Just go one step further and encapsulate calling your "C" functions within a
C++ managed wrapper class. Use that class to 'interact' with the rest of
your C# application. Remove your imports from your class above and place
them within the module/namespace. Create methods that then call your
external "C" functions.

The only real way to remain sane is to isolate the CTS from the C types and
don't try to half step. <g>

Question, is the DLL really written in 'C' and therefore using a cdecl
calling convention, or is it written using stdcall (a la Win API)? If you
are really accessing exported C then you have to use the extern "C"
attribute.

-ralph
 
S

Stoitcho Goutsev \(100\) [C# MVP]

Hi Nicholas,
You are right. It frees only memory allocated with CoTaskMemAlloc.
--
B\rgds
100

Nicholas Paldino said:
Stoitcho,

The problem with declaring the return value as a string is that the
memory allocated on the heap for the string in the unmanaged realm will now
not be released correctly, and cause a memory leak.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Stoitcho Goutsev (100) said:
Hi Angel,
If your have *z4date* returning a string. How do you expect PInvoke to
marshal it as an array of bytes
Instead of declare the function as:
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern byte[] z4date();

try to declare it as:
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern string z4date();

If you need that byte array you should convert the string to that array. We
cannot expect the marshaler to convert from any to any type we want.


Yes, you have to use DllImport for each and any function that you import
from unmanaged library.
--
HTH
B\rgds
100

Angel said:
Hi again,
I'm trying to call functions from a proprietary DLL but it's turned
out
to
be more difficult than I thought.

I have this W32.DLL which was written in C by USPS. They don't provide the
code so I only have the documentation.
I'm trying to call a function called z4date that, according to the docs,
returns the date as "an 8-byte character string in the "YYYYMMDD" format".
When I run it with this code I've written , I get "Can not marshal return
value".

My real concern is that, if I'm having trouble with these functions that
return simple data types, how am I going to interact with functions that
return user-defined data types?

This is what I've written in the class so far:

...
using System.Runtime.InteropServices;
namespace ZM7
{
/// <summary>
/// Summary description for AMS.
/// </summary>
public class AMS
{
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")]
public static extern int z4open(); //another function from the dll
[DllImport("C:\\zm7\\Developm\\DLL\\W32.DLL")] /* Do I need to use
DllImport for every fiunction I'm using? */
public static extern byte[] z4date();
...

public byte[] getDate()
{
Byte[] myDate;
myDate = new Byte[8];
myDate = z4date(); //function call where I receive error "Can not
marshal return value"
return myDate();
}

Any help is appreciated.
 
G

Guest

Hi Angel,

Thanks for posting in this community!!

I am monitoring this group. After reviewing your discussion in this post, I
think you meet the problem of P/invoke marshal.

I think you should follow Nicholas's suggestion to marshal the return value
as IntPtr. But, I think you may can not use Marshal.PtrToStringAnsi to
convert the value. Because the return value of z4date may be a character
array without a '\0' at the end of the array. So maybe you should read the
byte one by one(Use Marshal.ReadByte method). Like this:

//Test C function, which return value without a '\0' at the end of the
character array:
extern "C" __declspec(dllexport) char* RetAChar()
{
pChar = new char[8];
for(int i=0; i<=7; i++)
{
*pChar = 'd';
pChar++;
}
pChar -= 8;
return pChar;
}

[DllImport("DllDemo.dll", CharSet=CharSet.Ansi)]
private static extern IntPtr RetAChar();
private void button3_Click(object sender, System.EventArgs e)
{
IntPtr str=RetAChar();

byte b;
for(int i=0;i<8;i++)
{
b=Marshal.ReadByte(str);
Console.WriteLine(Convert.ToChar(b));
}
}

But as you stated, your problem should not related to this, can you show me
more information of your problem?

====================================
Thank you for your patience and cooperation. If you have any questions or
concerns, please feel free to post it in the group. I am standing by to be
of assistance.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
G

Guest

Hi Angel,

Does my reply make sense to you?

Is your problem resolved? Please feel free to feedback. Thanks.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
G

Guest

Hi VM,(Oh, I think you should be "Angel")

Thanks very much for your feedback.

I am glad it works, if you have any further question, please feel free to
post, we will help you.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 

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