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.
Related
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.
consider following setup: c# application with c++ library. c# elements are filled from c++ via callback. on c# side callback is defined like this:
void callbackTester(IntPtr pData, UInt32 length)
{
int[] data = new int[length];
Marshal.Copy(pData, data, (int)0, (int)length);
//using data.. on c#
}
now, on c++ side callback is defined like this:
typedef void (__stdcall *func)(uint8_t* pdata, uint32_t length);
and c++ is using callback like this:
void onData()
{
std::vector<uint8_t> dataToCallback;
// fill in dataToCallback
_callback(&(dataToCallback[0]), dataToCallback.size());
// where _callback is a pointer to that c# function
}
my task: get array from c++ side on c# side using callback.
so, when c++ object is calling onData() function, it calls my callback from c#. so far so good. i have made a c++ tester program, which uses this, and i am receiving array correctly on callback side. if i am using it on c# tester, i am receiving crap.
for example: if i am sending uint8_t array of {1, 1}, i am getting {1, 1} for c++ tester, and i am getting {0xfeeeabab, 0xfeeefeee} on c# side... obviously, the conversion between uint8_t* c++ pointer and IntPtr c# is working not as i expect.
any suggestions? thanks a lot.
The issue appears to be that C++ uint8_t is an unsigned byte, and C# int is a signed 4 byte integer. So you have a simple mismatch of types. The C# type that matches uint8_t is byte.
Your callback should be:
void callbackTester(IntPtr pData, uint length)
{
byte[] data = new byte[length];
Marshal.Copy(pData, data, 0, (int)length);
}
one thing to check is that on C# side you are expecting int-4 bytes per element ("int[] data") but on C++ you only allocating uint8-1 byte per element.
adjust the allocation or length usage, you could be getting access violation, that why you see magic bytes [1].
[1] http://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_debug_values
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.
I'm having trouble figuring out the best way to have a delphi function operate on a byte array from .net.
The delphi signature looks like this:
procedure Encrypt(
var Bytes: array of byte;
const BytesLength: Integer;
const Password: PAnsiChar); stdcall; export;
The C# code looks like this:
[DllImport("Encrypt.dll",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void Encrypt(
ref byte[] bytes,
int bytesLength,
string password);
Omitting var and ref before the byte array declaration seemed to fail, but is it required since I'll be changing only the contents of the array and not the array itself?
Also, for some reason I can't seem to get the length of the array in delphi, if I remove the BytesLength parameter than Length(Bytes) will not work, if I add the BytesLength parameter, Length(Bytes) starts to work but BytesLength has a wrong value.
Make the first parameter of the Delphi Encrypt be Bytes: PByte and you should be good to go.
An open array, as you have it, expects to be passed both the pointer to the first element and the length which explains what you describe in your question.
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[].