I have a C# application that (P/)invokes an ANSI C library, provided by the manufacturer, to access an RFID-Reader over the network. This works absolutely fine with .NET Framework versions 2.0 up to 4.5 if I manually set the platform to x86.
With the platform set to 64 Bit on the other hand, it only works with .NET Framework versions <= 3.5. The error code I get is defined as "InvalidHandle", where the handle is an int I'm given by the library identifying the reader. I don't get what an impact the Framework Version could have on a native library, especially if only so on a 64 Bit platform.
What I've observed, although I'm not sure if it is relevant at all: - In the case of failure, the handle is 10 digits long, sometimes positive, sometimes negative - In the case of success, the handle is 9 digits long, always positive
Based on this observation I've tried other data types (int, uint, long, ulong) without success (and with the same behaviour).
The "setup" of the component consists of multiple steps (method calls), which are: 1. Init (where I get the handle from) 2. SetChannel (takes a byte as a parameter) 3. SetAddress (takes a connection string containing an IP and a Port as parameter) 4. Open() Note: All those methods take the handle as an int out-parameter. Because the method SetAddress fails, when Init and SetChannel also take the handle as a parameter but don't, I suspect it might have something to do with the string, although I haven't found any evidence to support this theory.
Unfortunately the documentation is close to non-existent.
So any ideas are greatly appreciated. If you need more information, I'd be glad to provide it.
Regards,
Pascal
[DllImport("/x64/BLUEBOXLib.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
static extern int BLUEBOX_Init(out int Handle);
[DllImport("/x64/BLUEBOXLib.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
static extern int BLUEBOX_SetAddress(ref int Handle, byte Address);
[DllImport("/x64/BLUEBOXLib.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
static extern int BLUEBOX_SetChannel(ref int Handle, String Channel, String Settings);
Excerpt from the BLUEBOXLib.h file:
BLUEBOXLib_API BLUEBOX_ErrorCodes __stdcall BLUEBOX_Init (BLUEBOX_Handle *Handle);
BLUEBOXLib_API BLUEBOX_ErrorCodes __stdcall BLUEBOX_SetChannel (BLUEBOX_Handle *Handle, char *Channel, char *Settings);
BLUEBOXLib_API BLUEBOX_ErrorCodes __stdcall BLUEBOX_SetAddress (BLUEBOX_Handle *Handle, unsigned char Address);
typedef int BLUEBOX_Handle;
That is a problem, one you cannot fix because it doesn't appear in your code. There are multiple ways to implement a handle but they basically fall into two distinct categories. Either it a simple index into an internal array or it is a plain pointer. When you see "10 digits long, sometimes positive, sometimes negative" then it is 99% odds for a pointer.
That pointer takes 8 bytes in 64-bit mode, it cannot fit in an int. And yes, this sometimes still works, it depends on the operating system and the simplicity of the test program. On the x64 version of Vista or Win7, a 64-bit pointer can still fit in a 32-bit value by accident, allocations often happen in the lower 2 GB address range. You'll have zero odds for success on Win8, also a hint why it might seem to work on .NET 3.5. And yes, negative values won't work because they'll sign-extend when the C code casts the handle to the real pointer type, producing a garbage pointer value.
You cannot fix this bug, it has to be fixed by the vendor. They have to change the typedef to
void*
, now you can use IntPtr on your end. Give them a call. If they are unresponsive, then consider running this code in a separate 32-bit helper process that you interop with.