Unmanaged C++ Dll / C# bool return problem

M

Mark Jerde

I'm trying to learn the very basics of using an unmanaged C++ DLL from C#.
This morning I thought I was getting somewhere, successfully getting back
the correct answers to a C++ " int SumArray(int ray[], int count)"

Now I'm having problems with C++ "return(false)" being True in C#.

Here is the C# code.
=========================
using System;
using System.Runtime.InteropServices; //


namespace GUI_03
{

class Class1
{
[DllImport("Test05.dll")]
public static extern bool IsEven(int num);

[DllImport("Test05.dll")]
public static extern bool retFalse(int num);

[STAThread]
static void Main(string[] args)
{
for (int i=0; i<5; i++)
{
Console.WriteLine(
"IsEven({0}) = {1} \t retFalse({0}) = {2}",
i, IsEven(i), retFalse(i));
}

Console.ReadLine();
}
}
}

=========================
Next, the initial C++ code.

// Starting point:
// http://www.codeproject.com/csharp/UseCDLLlibinCS.asp

#include <stdio.h>

extern "C"
{
__declspec(dllexport) bool IsEven(int somenum)
{
if (somenum == (somenum / 2 * 2))
return(true);
else
return(false);
}

__declspec(dllexport) bool retFalse(int somenum)
{
if (somenum == (somenum / 2 * 2))
return(true);
else
return(false);
}
}

=========================
This is the output.

IsEven(0) = True retFalse(0) = True
IsEven(1) = False retFalse(1) = False
IsEven(2) = True retFalse(2) = True
IsEven(3) = False retFalse(3) = False
IsEven(4) = True retFalse(4) = True

Both "return(true);" and "return(false);" are working.

=========================
This is the code after commenting out everything in retFalse() except
"return(false);"

// Starting point:
// http://www.codeproject.com/csharp/UseCDLLlibinCS.asp

#include <stdio.h>

extern "C"
{
__declspec(dllexport) bool IsEven(int somenum)
{
if (somenum == (somenum / 2 * 2))
return(true);
else
return(false);
}

__declspec(dllexport) bool retFalse(int somenum)
{
/*if (somenum == (somenum / 2 * 2))
return(true);
else */
return(false);
}
}

=========================
This is the output.

IsEven(0) = True retFalse(0) = True
IsEven(1) = False retFalse(1) = True
IsEven(2) = True retFalse(2) = True
IsEven(3) = False retFalse(3) = True
IsEven(4) = True retFalse(4) = True

Why isn't retFalse always False??
=========================
Here is another function showing things I tried that didn't work...

__declspec(dllexport) bool TorF(int num)
{
//return(true);
//return(false);
//return(1 == 0);
//bool ret = false;
//return(ret);
// if(num == (num / 2 * 2)) // IsEven(num)
//return(true);
return(false);
// else
//return(false);
// return(true);
}
=========================

Any suggestions?

Thanks.

-- Mark
 
D

Doug Harrison [MVP]

W

Willy Denoyette [MVP]

Mark Jerde said:
I'm trying to learn the very basics of using an unmanaged C++ DLL from C#.
This morning I thought I was getting somewhere, successfully getting back
the correct answers to a C++ " int SumArray(int ray[], int count)"

Now I'm having problems with C++ "return(false)" being True in C#.

Here is the C# code.
=========================
using System;
using System.Runtime.InteropServices; //


namespace GUI_03
{

class Class1
{
[DllImport("Test05.dll")]
public static extern bool IsEven(int num);

[DllImport("Test05.dll")]
public static extern bool retFalse(int num);

[STAThread]
static void Main(string[] args)
{
for (int i=0; i<5; i++)
{
Console.WriteLine(
"IsEven({0}) = {1} \t retFalse({0}) = {2}",
i, IsEven(i), retFalse(i));
}

Console.ReadLine();
}
}
}

=========================
Next, the initial C++ code.

// Starting point:
// http://www.codeproject.com/csharp/UseCDLLlibinCS.asp

#include <stdio.h>

extern "C"
{
__declspec(dllexport) bool IsEven(int somenum)
{
if (somenum == (somenum / 2 * 2))
return(true);
else
return(false);
}

__declspec(dllexport) bool retFalse(int somenum)
{
if (somenum == (somenum / 2 * 2))
return(true);
else
return(false);
}
}

=========================
This is the output.

IsEven(0) = True retFalse(0) = True
IsEven(1) = False retFalse(1) = False
IsEven(2) = True retFalse(2) = True
IsEven(3) = False retFalse(3) = False
IsEven(4) = True retFalse(4) = True

Both "return(true);" and "return(false);" are working.

=========================
This is the code after commenting out everything in retFalse() except
"return(false);"

// Starting point:
// http://www.codeproject.com/csharp/UseCDLLlibinCS.asp

#include <stdio.h>

extern "C"
{
__declspec(dllexport) bool IsEven(int somenum)
{
if (somenum == (somenum / 2 * 2))
return(true);
else
return(false);
}

__declspec(dllexport) bool retFalse(int somenum)
{
/*if (somenum == (somenum / 2 * 2))
return(true);
else */
return(false);
}
}

=========================
This is the output.

IsEven(0) = True retFalse(0) = True
IsEven(1) = False retFalse(1) = True
IsEven(2) = True retFalse(2) = True
IsEven(3) = False retFalse(3) = True
IsEven(4) = True retFalse(4) = True

Why isn't retFalse always False??
=========================
Here is another function showing things I tried that didn't work...

__declspec(dllexport) bool TorF(int num)
{
//return(true);
//return(false);
//return(1 == 0);
//bool ret = false;
//return(ret);
// if(num == (num / 2 * 2)) // IsEven(num)
//return(true);
return(false);
// else
//return(false);
// return(true);
}
=========================

Any suggestions?

Thanks.

-- Mark

Should be...
[return:MarshalAs(UnmanagedType.I1)]
public static extern bool retFalse(int num);

Lookup the MSDN docs, an ansi C "bool" is a single byte value, true = 1, 0 =
false, you should declare it as a I1 return when PInvoking !
A BOOL in C (typedef used in a great number of windows API's) is a 4 byte
value wich is marshaled correctly to bool.

Willy.
 
W

Willy Denoyette [MVP]

Doug Harrison said:
I'm trying to learn the very basics of using an unmanaged C++ DLL from
C#.
This morning I thought I was getting somewhere, successfully getting back
the correct answers to a C++ " int SumArray(int ray[], int count)"

Now I'm having problems with C++ "return(false)" being True in C#.

There's a known bug in this area. See if this message applies to your
problem:

http://groups-beta.google.com/group.../a6d9c1e3015207f0?hl=en&lr=&ie=UTF-8&oe=UTF-8

Doug ,

It' somewhat related but it's not a bug in PInvoke interop, it was a bug in
managed C++ IJW interop.
The problem is that ansi C defines bool as a single byte intrinsic value,
but most of windows API's (predating ansi C) return a BOOL which is a 4 byte
value (a typedef), therefore the Interop team decided to marshal by default
to/from BOOL for PInvoke interop.
If you need to marshal to/from a C bool you have to marshal as a
UnmanagedType.I1,
or as in OP's case...
[return:MarshalAs(UnmanagedType.I1)]

Willy.
 
M

Mark Jerde

Willy said:
Should be...
[return:MarshalAs(UnmanagedType.I1)]
public static extern bool retFalse(int num);

Lookup the MSDN docs, an ansi C "bool" is a single byte value, true =
1, 0 = false, you should declare it as a I1 return when PInvoking !
A BOOL in C (typedef used in a great number of windows API's) is a 4
byte value wich is marshaled correctly to bool.

Willy.

Willy -- Thanks for the detailed notes to Doug and me, I'll try to figure
out their meaning. It would certainly be better to fix it on the C# side.

-- Mark
 
W

William DePalo [MVP VC++]

Mark Jerde said:
I'm trying to learn the very basics of using an unmanaged C++ DLL from C#.
This morning I thought I was getting somewhere, successfully getting back
the correct answers to a C++ " int SumArray(int ray[], int count)"

Now I'm having problems with C++ "return(false)" being True in C#.

I see that Doug and Willy have already weighed in on the matter.

FWIW, this bug bit me too. For native functions that return bool and which
may be invoked from managed code, I have taken to changing what I might have
written like this

bool foo(...)
{
bool ok;

// ... set ok

return ok;
}

to this

bool foo(...)
{
bool ok;

// ... set ok

__asm xor eax, eax
return ok;
}

Regards,
Will
 
M

Mark Jerde

Willy said:
Should be...
[return:MarshalAs(UnmanagedType.I1)]
public static extern bool retFalse(int num);

Lookup the MSDN docs, an ansi C "bool" is a single byte value, true =
1, 0 = false, you should declare it as a I1 return when PInvoking !
A BOOL in C (typedef used in a great number of windows API's) is a 4
byte value wich is marshaled correctly to bool.

Willy -- Thanks, the below works fine, and I even finally found the
UnmanagedType Enumeration. ;-)

[DllImport("Test05.dll")]
[return:MarshalAs(UnmanagedType.I1)]
public static extern bool retFalse(int num);

A further question is how to have known about this beforehand. I'm
wondering about my other data types and whether they will marshall correctly
in *all* cases. My other return is an int which I presume is ok.
Arguements include integers, arrays of integers, arrays of bytes (arrays of
unsigned chars in C++) and arrays of structures. (The structure members are
just integers, no string stuff.) I have a sample program that uses

[StructLayout(LayoutKind.Sequential)]

Do you see anything in these types I should look out for?

Thanks.

-- Mark
 
W

Willy Denoyette [MVP]

Mark Jerde said:
Willy said:
Should be...
[return:MarshalAs(UnmanagedType.I1)]
public static extern bool retFalse(int num);

Lookup the MSDN docs, an ansi C "bool" is a single byte value, true =
1, 0 = false, you should declare it as a I1 return when PInvoking !
A BOOL in C (typedef used in a great number of windows API's) is a 4
byte value wich is marshaled correctly to bool.

Willy -- Thanks, the below works fine, and I even finally found the
UnmanagedType Enumeration. ;-)

[DllImport("Test05.dll")]
[return:MarshalAs(UnmanagedType.I1)]
public static extern bool retFalse(int num);

A further question is how to have known about this beforehand. I'm
wondering about my other data types and whether they will marshall
correctly
in *all* cases. My other return is an int which I presume is ok.
Arguements include integers, arrays of integers, arrays of bytes (arrays
of
unsigned chars in C++) and arrays of structures. (The structure members
are
just integers, no string stuff.) I have a sample program that uses

[StructLayout(LayoutKind.Sequential)]

Do you see anything in these types I should look out for?

Thanks.

-- Mark

Mark,

Note that we must make a clear distinction between PInvoke interop, and the
IJW (It Just Works) interop feature available in managed C++ only.
The former uses the CLR to marshal arguments and return values when calling
into unmanaged code from managed code, the latter doesn't use the CLR
marshaling but instead puts the burden on the user to correctly marshal
argument/return types when they tend to be incompatible.
Each managed type has a default marshaling behavior associated when using
PInvoke interop.
For instance a managed int (32 bit signed value) will be passed unchanged,
however, a managed string (Unicode char string) will be converted to/from a
char string (Ansi char string) by default.
This default behavior can be changed by tagging the MarshalAs attribute to
the arg. or return type.
For instance if your unmanaged counter part expects a string to be UNICODE,
you should tag the string argument with a MarshalAs(UnmanagedType.LPWSTR)
attribute and the CLR will pass the string unchanged.
Now back to our bool type. The default marshaling passes a bool as a 32 bit
integer, which is a reasonable default when the unmanaged code expects a
BOOL (32 bit integer value) as used by most of the Win32 API's. However,
when passing ANSI C bool type, you have to change the default behavior by
means of the
MarshalAs(UnmanagedType.I1) attribute. Failing to do this results in
unexpected behavior as you noticed, using the trick as suggested by William
is not needed when using PInvoke interop.
I suggest you to check the list of managed types and try to understand their
default marshaling behavior when using PInvoke marshaling, also take some
time to understand the default alignment/packing used at both the managed
side and the unmanaged side.

Willy.
 
M

Mark Jerde

Willy said:
I suggest you to check the list of managed types and try to
understand their default marshaling behavior when using PInvoke
marshaling, also take some time to understand the default
alignment/packing used at both the managed side and the unmanaged
side.

Willy -- Thanks for the reply. I think I'm starting to understand.


For the possible benefit of future googlers here are MSDN links.

Interoperating with Unmanaged Code
http://msdn.microsoft.com/library/d...html/cpconInteroperatingWithUnmanagedCode.asp

Interop Marshaling
http://msdn.microsoft.com/library/d...en-us/cpguide/html/cpconinteropmarshaling.asp

An Overview of Managed/Unmanaged Code Interoperability
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/manunmancode.asp

-- Mark
 

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