Strange C# platform invoke / DLLImport behaviour - c#

I have an unmanaged DLL I am referencing in my project using [DLLImport], but am receiving odd results when I match method signatures.
Here is an example signature from the DLL:
DLLEXPORT unsigned long OpenPort(unsigned long ulPort,
unsigned long ulBaudRate,
unsigned long ulByteSize,
unsigned long ulPartity,
unsigned long ulStopBits,
unsigned long ulFlowControl)
And here is my C# code to import the function:
[DllImport("C:/my.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint OpenPort(ulong ulPort, ulong ulBaudRate,
ulong ulByteSize, ulong ulParity, ulong ulStopBits, ulong ulFlowControl);
Notice I declare this with a return type of uint, as when I attempt to use ulong I get unexpected results (long numbers usually that look a bit like memory addresses).
However, the function works and returns expected results if I use a return type of int/uint. Can anyone shed any light on this behaviour for me?
Thanks.

I'm assuming that your target platform is Windows, based on the name of your library in the DllImport attribute. On Windows, the C++ long type (unsigned as well as signed, obviously) is 4 bytes wide, for both 32 and 64 bit. So, you need to declare your p/invoke using uint rather than ulong.
The correct declaration is:
[DllImport("C:/my.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint OpenPort(
uint ulPort,
uint ulBaudRate,
uint ulByteSize,
uint ulParity,
uint ulStopBits,
uint ulFlowControl
);
Now, if your target platform is other than Windows, then you'd need to know what unsigned long is on that platform to give specific advise.

Related

Getting Memory Exception Error when calling C++ method from C#

I have C++ dll which has below method signature. I would like to know what should be compatible C# method to call C++ dll. I am getting error on execution as attempted to read write protected memory. The dll used here is third party dll. I am able to call simple methods without pointers from C++ dll.
C++ Declaration:
int __stdcall Deduct(int V, unsigned char *AI);
C# Declaration
[System.Runtime.InteropServices.DllImport("rwl.dll",EntryPoint = "_Deduct#8", CallingConvention = CallingConvention.Cdecl)]
public static extern long Deduct(long i,ref string AI);
As per the document of third party dll.
AI Used as input and output buffer. The buffer shall have at least 80 bytes
Input Additional Information for the transaction. It is a byte pointer containing 7 bytes.
e.g. Assume the usage information is not used,
if receipt number = 1234567,
hex value = 0x12D687,
7 bytes AI = D6 87 00 00 00 D6 87
Output On return, the AI contains the UD.
Please help.
I think the c# signature looks wrong.
[System.Runtime.InteropServices.DllImport("rwl.dll",EntryPoint = "_Deduct#8", CallingConvention = CallingConvention.Cdecl)]
public static extern long Deduct(int i,ref char[] AI);
if it is expecting a char[80] as it's input you may need to declare it as such and null terminate it yourself as whatever is reading from it might be reading past the end of the array.
usage
char[] tmp = new char[80];
tmp[0] = (char)0x00;
int i = 0;
Deduct(i, ref tmp);
I'm not sure if this will work but hopefully it helps.
Besides string marshaling, I found a likely reason for getting an exception.
You have specified the return type and the first parameter as int in C, but long int C#. This could cause problems.
C language specifies minimum bits of integer types which means that different for environments.
int in C : unsigned integer, at least 16 bits
long in C : unsigned integer, at least 32 bits
int in C# : 32-bit unsigned integer, always
long in C# : 64-bit unsigned integer, always
Most of major 32/64-bit OS/compilers use 32 bits for type int in C. However long is always 64-bit in C#. So the signature mismatches. I would suggest to revise:
public static extern int Deduct(int i, ref string AI); // Still not sure about string

Is std::byte in C++ 17 equivalent to byte in C#?

I just noticed std::byte in the C++ 17.
I am asking this question because I use the code below to send byte array to C++ to play audio sound.
C#:
[DllImport ("AudioStreamer")]
public static extern void playSound (byte[] audioBytes);
C++:
#define EXPORT_API __declspec(dllexport)
extern "C" void EXPORT_API playSound(unsigned char* audioBytes)
With the new byte type in C++ 17, it looks like I might be able to do this now:
C#:
[DllImport ("AudioStreamer")]
public static extern void playSound (byte[] audioBytes);
C++:
#define EXPORT_API __declspec(dllexport)
extern "C" void EXPORT_API playSound(byte[] audioBytes)
I am not sure if this will even work because the compiler I use does not support byte in C++ 17 yet.
So, is std::byte in C++ 17 equivalent to byte in C#? Is there a reason to not use std::byte over unsigned char* ?
According to C++ reference,
Like the character types (char, unsigned char, signed char) std::byte can be used to access raw memory occupied by other objects.
This tells me that you can freely replace
unsigned char audioBytes[]
with
std::byte audioBytes[]
in a function header, and everything is going to work, provided that you plan to treat bytes as bytes, not as numeric objects.
std::byte is equivalent to both unsigned char and char in C++ in a sense that it is a type to represent 1 byte of raw memory.
If you used unsigned char* in your interface, you can easily replace it with std::byte.
In your C# code this will result in no changes at all, on the C++ side this will make your type system more strict (which is a good thing) due to the fact that you will not be able to treat your std::bytes as text characters or as small integers.
Of course this is C++17 feature which may or may not be properly supported by your compiler.

C# Marshalling unsigned char* array from C++ DLL (in and out)

I am having trouble marshalling data between a C# application and an existing C++ DLL. The caveats that are making this difficult are that it is an unsigned char pointer to an array, and I need access to the data (from all fields) after the call in C#. I would like to avoid using unsafe code, if at all possible.
Here is the C++ signature:
BYTE GetData(unsigned char *Data_Type, unsigned char *Data_Content, unsigned int *Data_Length);
I've tried a bunch of things in C#, but here's what I have now:
[DllImport("somecpp.dll")]
public static extern byte GetData([In][Out] ref byte Data_Type, [In][Out] ref byte[] Data_Content, [In][Out] ref int Data_Length);
Then calling it, I am attempting this:
byte retrievedData = GetData(ref data_type, ref data_content, ref data_length);
This is definitely not working, and I'm not sure what to try next. Any ideas? Thanks!
Your ref byte[] parameter matches unsigned char**. That's one level of indirection too many.
The p/invoke should be
[DllImport("somecpp.dll")]
public static extern byte GetData(
ref byte Data_Type,
[In,Out] byte[] Data_Content,
ref uint Data_Length
);
It is plausible that the function uses cdecl. We can't tell that from here.
I also suspect that the Data_Type argument should be out rather than ref.

Why does this code for dealing with timestamps use signed integer?

Here's a piece of code for obtaining the time when a .NET assembly was built. Note this line:
int secondsSince1970 = System.BitConverter.ToInt32(b, i + c_LinkerTimestampOffset);
this code extracts the TimeDateStamp member of IMAGE_FILE_HEADER structure that is stored inside the assembly. The structure is defined as follows:
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
and DWORD is defined as follows:
typedef unsigned long DWORD;
and the struct description says that TimeDateStamp is a number of seconds since an arbitrary moment in the past, so it can't be negative.
Why does the C# code use signed type int to store that unsigned value?
It is because unsigned int is not a CLS compliant variable type and all .NET libraries should follow Common Language Specification.
More info about CLS compliance:
http://msdn.microsoft.com/en-us/library/12a7a7h3.aspx

c -> c# data types conversion when using native functions from dll

Is this list correct?
unsigned int(c) -> uint(c#)
const char*(c) -> String(c#)
unsigned int*(c) -> uint[](c#)
unsigned char*(c) -> byte[](c#)
I think there's a mistake here because with these 4 parameters for native function I have PInvokeStackImbalance.
C function is:
bool something
(unsigned char *a,
unsigned int a_length,
unsigned char *b,
unsigned int *b_length);
PInvoke is:
[DllImport(#"lib.dll", EntryPoint = "something")]<br>
public static extern bool something(
byte[] a,
uint a_length,
byte[] b,
uint[] b_length);
First, PInvoke.net is your friend.
Second, You conversions are correct except that you should use a StringBuilder for functions that take a char* as a buffer to fill ([in out]).
Your stack imbalance may be due to the use of different calling conventions. The default calling convention for C# is __stdcall, but your C function is probably __cdecl. If that is the case you will need to add the CallingConvention to your DLLImport attribute.
EDIT: Also, as Groo pointed out, if the pointer arguments in your C function are actually just pointers to unsigned int (for example, as opposed to expecting an array of int) then you should use ref uint instead of an int[].

Categories

Resources