Is std::byte in C++ 17 equivalent to byte in C#? - 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.

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

how to marshal c++ array to c# via IntPtr

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

PInvoke byte array to char not behaving properly in 64 bit [duplicate]

Follow up to PInvoke byte array to char not behaving properly in 64 bit. (Stale question and my suspicions were wrong, thus the title and descriptionwas unfitting).
I am using P/Invoke to call C++ code from C#. I have both the C# and C++ projects set to build in x64 in the build configurations of VS. when I run this program, the parameters of the P/Invoke call are shifted by 32 bits as follows
C# : |Parameter 1|Parameter 2|Parameter 3|Parameter 4|
| | | | |
V V V V V
C++: |Parameter 1|Parameter 2|Parameter 3|Parameter 4|
So if I pass 1,2,3,4 from the C# side, the C++ side receives 2,3,4,garbage.
I have worked around this by passing in an extra int in front of the C# parameters without changing the C++ side. this offsets the parameters by 32 bits and realigns them and the program works perfectly.
Does anyone know what is causing this strange offset and the proper way to correct it?
Here is a simplified example showing my method signatures
C# side:
[DllImport(#"C:\FullPath\CppCode.dll", EntryPoint = "MethodName",
CallingConvention = CallingConvention.Cdecl))]
private static extern bool MethodName(parameters);
C++ side:
extern "C" __declspec(dllexport)
bool CppClass::MethodName(parameters)
I suspect that since the parameters are off by 32 bits, there might be something that isn't really being done in 64 bit properly. Perhaps when calling a method, there is an implicit pointer that is passed before the variables? If that is only a 32 bit pointer on the C# side and the C++ is expecting a 64 bit pointer, that could cause this offset situation, but I'm not sure.
MethodName should not be a class instance method, as the first bytes (4 in x86 world, 8 in x64 world) will be used for the class instance pointer in the unmanaged world.
So, it should be a static method (or a C style method).

How do I pass a char and a char* from C# to C++/CLI?

The title contains all. How do I pass a char and char* from C# to C++/CLI.
Here is my cpp function declaration:
int ForexCpp::FXCrossDate(
char usdTypeOfPeriods,
char *holidayFile,
TDate% result); /* (O) Resulting FX cross date */
In C#, char and char * are exposed as sByte and sByte*...
Since you have chosen to use C++/CLI rather than P/invoke the natural way to handle this is to pass a .net string rather than a char*.
Have you done any research on this yet? The internet is full of this type of stuff.
Anyways, this page on MSDN lists various ways to convert between string types:
http://msdn.microsoft.com/en-us/library/ms235631.aspx
The one you want is at the bottom of the page.

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