I am having a data conversion issue that need your help.
My project is an InterOp between C and C#, all the data from C is char * type, the data itself could be binary or displayable chars, I.e. each byte is in 0x00 to 0xFF range.
I am using Data marshal::PtrToStringAnsi to convert the char* to String^ in CLI code, but I found some bytes value changed. for example C382 converted to C32C. I guess it is possibly because ANSI is only capable of converting 7-bit char, but 82 is over the range? Can anyone explain why and what is the best way?
Basically what I want to do is, I don't need any encoding conversion, I just want to convert any char * face value to a string, e.g. if char *p = "ABC" I want to String ^s="ABC" as well, if *p="C382"(represents binary value) I also want ^s="C382".
Inside my .NET code, two subclasses will take the input string that either represents binary data or real string, if it is binary it will convert "C382" to byte[]=0xC3 0x82;
When reading back the data, C382 will be fetched from database as binary data, eventually it need be converted to char* "C382".
Does anybody have similar experience how to do these in both directions? I tried many ways, they all seem to be encode ways.
The Marshal class will do this for you.
When converting from char* to byte[] you need to pass the pointer and the buffer length to the managed code. Then you can do this:
byte[] FromNativeArray(IntPtr nativeArray, int len)
{
byte[] retval = new byte[len];
Marshal.Copy(nativeArray, retval, 0, len);
return retval;
}
And in the other direction there's nothing much to do. If you have a byte[] then you can simply pass that to your DLL function that expects to receive a char*.
C++
void ReceiveBuffer(char* arr, int len);
C#
[DllImport(...)]
static extern void ReceiveBuffer(byte[] arr, int len);
....
ReceiveBuffer(arr, arr.Length);
Related
I have an imported C++ method that receives a byte parameter, but according to the documentation, I can send a letter to that parameter, this is the C++ and C# method:
int WINAPI Sys_InitType(HID_DEVICE device, BYTE type)
public static extern int Sys_InitType(IntPtr device, byte type);
This causes me a syntax error in C#, how do I send a letter in that parameter?
My code (A bit random):
//CRASHES
byte random = Convert.ToByte("A");
_ = RFIDReader.Sys_SetAntenna(g_hDevice, 0);
int lol = RFIDReader.Sys_InitType(g_hDevice, random);
_ = RFIDReader.Sys_SetAntenna(g_hDevice, 1);
CError.Text = lol.ToString();
Convert.ToByte(string); doesn't do what you think it does, according to the documentation
Converts the specified string representation of a number to an equivalent 8-bit unsigned integer.
This would work byte random = Conver.ToByte("52"); which will return the byte 52.
See here:
https://learn.microsoft.com/en-us/dotnet/api/system.convert.tobyte?view=net-6.0#system-convert-tobyte(system-string)
As was pointed out in the comment already, you will have to use character instead of string, so either this
byte random = Convert.ToByte('A');
or a simple cast to byte
byte random = (byte)'A'
In case it is unknown to you, which I didn't assume, a byte can only contain values of the range 0 - 255, while a character can contain everything within the specs of UTF-16.
So this will not work
byte random = Convert.ToByte('\u4542');
And result in the error:
Value was either too large or too small for an unsigned byte.
https://dotnetfiddle.net/anjxt5
I am using a function from a c++ .dll from within C#. The .dll is partially documented, it was created to read out results from a result database. The function has the following signature:
cdb_get(int index, int kwh, int kwl, void *s, int *ls, int nrew);
In many cases I know the structure of the data I am reading, so I can just create a struct with the right format and read into it using regular pInvoke.
However in some cases, I need to know the first bytes of the data to figure out the correct data type. Ideally I would thus like to read the data into a byte array and then convert this further to the struct I need. I'd like to do something like this:
[DllImport("name_of_dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int cdb_get(
int index,
int kwh,
int kwl,
ref byte[] data,
ref int dataLen,
int pos);
But that doesn't seem to work (should it in theory?). Is there a way to make this work with a byte array and passing it by reference?
So far I am working arount it by using an IntPtr and then marshalling by hand. I need to guess the pointer size though, I guess that's unavoidable? Is there a different way to handle the marshalling?
IntPtr data = Marshal.AllocHGlobal(128);
int size = 128;
int returnValue = cdbGet(index, kwh, kwl, data, ref size, 1);
byteArray = new byte[size];
Marshal.Copy(data, byteArray, 0, size);
Marshal.FreeHGlobal(data);
Any help is appreciated, thanks a lot.
I can not post a comment, so I write here.
This seems same to your question, it may help you.
I have a struct in C++ which has a char[10] field.
struct Package
{
char str[10];
};
I convert the struct into char array and send it to and c# application over a TCP socket and there I convert it back to a c# struct.
[StructLayout(LayoutKind.Sequential)]
public struct Package
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string str;
};
The convertion is done properly and I get the message but the problem is when the length of the message is less than the size of the array and I think that it's due to the null terminator in c++ char array.
For instance if the I send "Hello\0" from C++ the char array is something like:
H e l l o \0 \0 \0 \0 \0
And when I get it in c# application it is something like:
H e l l o Ì Ì Ì Ì Ì
And I really don't know what to do with this (personally like to call) junk character 'Ì'.
please help me on this. Any help is appreciated.
Update:
I simply cast the struct to char* and send it over the socket;
Package pkg;
strcpy_s(pkg.str, 'Hello\0');
char* bytes = (char*)&pkg;
send(socket, bytes, sizeof(pkg), NULL);
I don't know any C#, but may be you can solve the problem this way
Package pkg;
strcpy_s(pkg.str, 10, "Hello");
char* bytes = (char*)&pkg;
send(socket, bytes, sizeof(pkg), NULL);
The strcpy_s function takes the size of the dst buffer as an argument to avoid an overflow (see here). I asked in the comments about the way you inovke it, because it doesn't even look like it's valid C++.
The terminating null byte is added to pkg.str automatically and also "Hello" has a terminating null byte after the o character, and you don't have to add it manually.
I assumed that C# knows how to handle the recieved string, so if you send the right string, it should be recived correctly.
You are sending sizeof array instead of strlen of str.
Take notes that if you send with strlen you need to add 1 to send the null termination of string
This is what I usually do in my wrappers dll with serial line
public byte[] RXArray = new byte[length];
byte[] appo = new byte[MsgLength];
Array.ConstrainedCopy(RXArray, NcharsToCopy, appo, 0, MsgLength);
string ConvertedToString = System.Text.Encoding.UTF8.GetString(appo);
I cant figure out how to pass a char * to this C++ function from C#.
extern "C" __declspec(dllexport)
unsigned int extractSegment(char * startPoint, unsigned int sizeToExtract)
{
//do stuff
shared_ptr<std::vector<char>> content(new std::vector<char>(startPoint,startPoint+sizeToExtract));
//do more stuff
return content->size();
}
This function is used to read a segment from a file and do some binary operations on it (why i use the vector of chars). The startPoint is the start of the segment i want to read. I cannot change this function.
In C# I tried reading the file into a byte[] array and defining the DllImport to use StringBuilder where the export had char *. I tried to call it as such:
byte[] arr = File.ReadAllBytes(filename);
StringBuilder sb = new StringBuilder(System.Text.Encoding.Unicode.GetString(arr, startPoint,arr.Length - startPoiunt));
extractSegment(sb,200);
This resulted in SEHException.
A char * can have several different meanings. In your case it appears to be a preallocated and filled array of bytes that is used as an input parameter for the extractSegment function.
The equivalent C# method would then take a byte[] parameter, i.e.
[DllImport(...)]
public static extern int extractSegment(byte[] startPoint, uint sizeToExtract);
I use byte[] because you mention binary operations, however if it is actually a string then you can also marshal it as such, setting the correct encoding in the DllImport attribute.
And just for further information, other possible options for char * that I can think of right now would be ref byte, out byte, ref char, out char or string. StringBuilder on the other hand is used when the calling function allocates and returns a string, i.e. char **.
Greetings Overflowers,
I love the flexibility of memory mapped files in that you can read/write any value type.
Is there a way to do the same with byte arrays without having to copy them into for e.g. a memory map buffers ?
Regards
You can use the BitConverter class to convert between base data types and byte arrays.
You can read values directly from the array:
int value = BitConverter.ToInt32(data, pos);
To write data you convert it to a byte array, and copy it into the data:
BitConverter.GetBytes(value).CopyTo(data, pos);
You can bind a MemoryStream to a given byte array, set it's property Position to go to a specific position within the array, and then use a BinaryReader or BinaryWriter to read / write values of different types from/to it.
You are searching the MemoryStream class which can be initialised (without copying!) from a fixed-size byte array.
(Using unsafe code)
The following sample shows how to fill a 16 byte array with two long values, which is something BitConverter still can't do without an additional copy operation:
byte[] bar = new byte[16];
long lValue1 = 1;
long lValue2 = 2;
unsafe {
fixed (byte* bptr = &bar[0]) {
long* lptr = (long*)bptr;
*lptr = lValue1;
// pointer arithmetic: for a long* pointer '+1' adds 8 bytes.
*(lptr + 1) = lValue2;
}
}
Or you could make your own StoreBytes() method:
// here the dest offset is in bytes
public static void StoreBytes(long lValue, byte[] dest, int iDestOffset) {
unsafe {
fixed (byte* bptr = &dest[iDestOffset]) {
long* lptr = (long*)bptr;
*lptr = lValue;
}
}
}
Reading values from a byte array is no problem with BitConverter since you can specify the offset in .ToInt64.
Alternative : use Buffer.BlockCopy, which can convert between array types.