C++ <--> C# modify a marshalled array of bytes - c#

I have an unmanaged C++ function which is calling a managed C# method in a DLL. The purpose of the C# method is to take an array of bytes (allocated by the C++ caller), populate the array, and return it. I can get the array INTO the C# method, but the populated data are lost when they get back to the C++ function. Right now, this is my test code to debug the process:
C# DLL Method:
// Take an array of bytes and modify it
public ushort GetBytesFromBlaster([MarshalAs(UnmanagedType.LPArray)] byte[] dataBytes)
{
dataBytes[0] = (byte)'a';
dataBytes[1] = (byte)'b';
dataBytes[2] = (byte)'c';
return 3;
}
C++ function which calls the DLL:
// bytes[] has been already allocated by its caller
short int SimGetBytesP2P(unsigned char bytes[])
{
unsigned short int numBytes = 0;
bytes[0] = 'x';
bytes[1] = 'y';
bytes[2] = 'z';
// bytes[] are {'x', 'y', 'z'} here
guiPtr->GetBytesFromBlaster(bytes, &numBytes);
// bytes[] SHOULD be {'a', 'b', 'c'} here, but they are still {'x', 'y', 'z'}
return(numBytes);
}
I believe it has something to do with C# turning the C++ pointer into a new managed array, but modifying the original one. I have tried several variations using the "ref" modifyer, etc., but no luck. Also, these data are NOT null-terminated strings; the date bytes are raw 1-byte values, not null-terminated.
Can anyone please shed some light on this? Thanks!
Stuart

You could do the marshaling yourself. Have the C# function accept a parameter by value of type IntPtr. Also a second parameter indicating array length. No special marshaling attributes are needed or wanted.
Then, use Marshal.Copy and copy the array from the unmanaged pointer to a managed byte[] array that you allocated. Do your thing, and then when you're done, use Marshal.Copy to copy it back to the C++ unmanaged array.
These particular overloads should get you started:
http://msdn.microsoft.com/en-us/library/ms146625.aspx
http://msdn.microsoft.com/en-us/library/ms146631.aspx
For example:
public ushort GetBytesFromBlaster(IntPtr dataBytes, int arraySize)
{
byte[] managed = new byte[arraySize];
Marshal.Copy(dataBytes, managed, 0, arraySize);
managed[0] = (byte)'a';
managed[1] = (byte)'b';
managed[2] = (byte)'c';
Marshal.Copy(managed, 0, dataBytes, arraySize);
return 3;
}
Alternatively you could implement a custom marshaller as described in http://msdn.microsoft.com/en-us/library/w22x2hw6.aspx if the default one isn't doing what you need it to. But that looks like more work.

I believe that you just need to add a SizeConst attribute:
public ushort GetBytesFromBlaster(
[MarshalAs(UnmanagedType.LPArray, SizeConst=3)]
byte[] dataBytes
)
and the default marshaller should do the rest for you.

Related

Marshaling a SAFEARRAY of VARIANTs that are BYTEs to C#

I create a SAFEARRAY storing VARIANTs that are BYTEs in C++.
When this structure is marshaled to C#, a weird thing happens.
If I print the content of this structure in C# to a WinForms ListBox, e.g.:
byte data[]
TestSafeArray(out data);
lstOutput.Items.Clear();
foreach (byte x in data)
{
lstOutput.Items.Add(x); // Strange numbers
}
I get some numbers that seem unrelated to the original ones. Moreover, each time I run the C# client for a new test, I get a different set of numbers.
Note that if I inspect the content of that data array with the Visual Studio debugger, I get the correct numbers, as the following screenshot shows:
However, if I CopyTo the marshaled data array to a new one, I get the correct numbers:
byte[] data;
TestSafeArray(out data);
// Copy to a new byte array
byte[] byteData = new byte[data.Length];
data.CopyTo(byteData, 0);
lstOutput.Items.Clear();
foreach (byte x in byteData)
{
lstOutput.Items.Add(x); // ** WORKS! **
}
This is the C++ repro code I use to build the SAFEARRAY (this function is exported from a native DLL):
extern "C" HRESULT __stdcall TestSafeArray(/* [out] */ SAFEARRAY** ppsa)
{
HRESULT hr = S_OK;
try
{
const std::vector<BYTE> v{ 11, 22, 33, 44 };
const int count = static_cast<int>(v.size());
CComSafeArray<VARIANT> sa(count);
for (int i = 0; i < count; i++)
{
CComVariant var(v[i]);
hr = sa.SetAt(i, var);
if (FAILED(hr))
{
return hr;
}
}
*ppsa = sa.Detach();
}
catch (const CAtlException& e)
{
hr = e;
}
return hr;
}
And this is the C# P/Invoke I used:
[DllImport("NativeDll.dll", PreserveSig = false)]
private static extern void TestSafeArray(
[Out, MarshalAs(UnmanagedType.SafeArray,
SafeArraySubType = VarEnum.VT_VARIANT)]
out byte[] result);
Note that if in C++ I create a SAFEARRAY storing BYTEs directly (instead of a SAFEARRAY(VARIANT)), I get the correct values immediately in C#, without the intermediate CopyTo operation.
[Out, MarshalAs(UnmanagedType.SafeArray,
SafeArraySubType = VarEnum.VT_VARIANT)]
out byte[] result);
You fibbed. You told the marshaller that you wanted an array of variants, indeed compatible with what the C++ compiler produced. It will dutifully produce an object[], object is the standard marshaling for a VARIANT. The elements of the array are boxed bytes.
That did not fool the debugger, it ignored the program declaration and looked at the array type and discovered object[], readily visible in your screenshot. So it properly accessed the boxed bytes. And it did not fool Array.CopyTo(), it takes an Array argument so is forced to look at the element type. And properly converted the boxed bytes to bytes, it knows how to do that.
But the C# compiler is fooled badly. It has no idea that it needs to emit an unbox instruction. Not actually sure what goes wrong, you are probably getting the low byte of the object address. Pretty random indeed :)
Fibbing in pinvoke declarations can be very useful. Works just fine in this particular case if the array contains actual objects, like strings, arrays or interface pointers. The probable reason the marshaller doesn't scream bloody murder. Not here, the boxing trips it up bad. You'll have to fix the declaration, use object[].

How to get back the data after Marshal.Copy(byte,0,ptr,len)?

we have used the foll. code to marshal the byte array - ie:copy to unmanaged memory space;
Marshal.Copy(byte,0,ptr,len)?
How do I put the data back into a byte array in another program?
Pls advice if my approach is correct :-
string aString = "some text";
byte[] theBytes = System.Text.Encoding.Default.GetBytes(aString);
// Marshal the managed struct to a native block of memory.
int myByteSize = theBytes.Length;
IntPtr pmyByte = Marshal.AllocHGlobal(myByteSize ); //this is pointer
try
{
Marshal.Copy(theBytes, 0, pmyByte , myByteSize );
.............
Following this, I would like to retrieve the data with in this unmanaged memory into a string variable, how do I achieve that?
In VB6 I am doing it using (may be helpful for someone who wants to pass data from c#.net to vb6 app):-
Call CopyMemory(buf(1), ByVal cds.lpData, cds.cbData)
a$ = StrConv(buf, vbUnicode)
a$ = Left$(a$, InStr(1, a$, Chr$(0)) - 1)
Form1.Print a$
How do I pick up the marshaled data in C#.NET?
This code assumes, that you know the unmanaged data length (someArraySize) and character encoding:
// create new managed array
var array = new byte[someArraySize];
// copy data from unmanaged memory, pointed by ptr, into managed array
Marshal.Copy(ptr, array, 0, someArraySize);
// convert array to string; this assumes, that array contains string in UTF-8 encoding
var s = Encoding.UTF8.GetString(array);

Casting int on a c++ logic layer

I am new to c++ and as for now I have quite a heavy task on my work, I have a gui made in wpf and I need to send parameters from the gui to the c++ the (which as for now I already handled)
My problem is that on the c++ layer I get the info as a BYTE* I need to reinterprete the values to their "original" state (the first translation from ont\float to byte array is being made on the C# level using the static BitConvertor class) as for now I used this little method -
void GetNextValue(byte* bytes, deque<BYTE> *buffer)
{
bytes[3] = buffer->front();
buffer->pop_front();
bytes[2] = buffer->front();
buffer->pop_front();
bytes[1] = buffer->front();
buffer->pop_front();
bytes[0] = buffer->front();
buffer->pop_front();
}
But for an integer value of 1 I get a really high number, on the other hand going directly for the int value in the whole buffer will yield the correct answer...(i.e. int x = pBuffer[4]), any help or suggestions will be gladly accepted..
BTW-
I used
_rxBuffer.insert( _rxBuffer.end(), pBuffer, pBuffer + nLength);
To convert the BYTE* of data to -
deque<BYTE> _rxBuffer;
If you have an array of byte[4] you can just convert it to integer by this:
byte bytes[4];
int value = *(int*)bytes;
But beware, depending on endianess of your platform you may or may need not swap bytes order (try to replace 3<>0 and 2<>1 in bytes).

Convert IntPtr to byte[] c#

I receive IntPtr value from C++ library method and i need to get byte[] array from this received IntPtr. When I try do this:
byte[] myArray = new byte[Marshal.SizeOf(myReceivedIntPtr)];
Marshal.Copy(myReceivedIntPtr, myArray, 0, Marshal.SizeOf(myReceivedIntPtr));
I receive an exception: AccessViolationException.
What I missed?
EDIT:
Here is Method name and params in header of C++ library:
int MSR_DecodeTrack(char *AscBuff, unsigned char *BinBuff, unsigned char bpc, unsigned char parity, unsigned char ss, unsigned char es);
And I call it from C# this way:
UInt32 result;
IntPtr myReceivedIntPtr;
IntPtr BinBuff;
byte BPC1 = 7, Parity1 = 1, SS1 = 0x05, ES1 = 0x1F;
result = MSR_DecodeTrack(ref myReceivedIntPtr, ref BinBuff, BPC1, Parity1, SS1, ES1);
And here is else code:
byte[] myArray = new byte[Marshal.SizeOf(myReceivedIntPtr)];
Marshal.Copy(myReceivedIntPtr, myArray, 0, Marshal.SizeOf(myReceivedIntPtr));
I'm pretty sure Marshal.SizeOf(myReceivedIntPtr) will return the size of the IntPtr object, not the size of the unmanaged array. You will need to get the size of the unmanaged array from the c++ library.
I haven't had to do this in a while so I'm a bit rusty but from looking at the c++ headers I think it is expecting a pointer to a buffer. You are giving it a pointer to a pointer (intptr is a pointer and passing it by ref means you are passing the c++ code a pointer to that pointer). Firstly, don't use ref. Secondly, I suspect you are supposed to be passing it an already sized buffer - is there any documentation describing what you are supposed to pass to the function? What is the return code from the function? You are checking it for success aren't you? ;)
The method named MSR_DecodeTrack should also return the length of each array it allocates.
As far as I know, when you allocate an array in C/C++ environment, you get a pointer to the zero element of that array, nothing more. For that reason, you should also somehow return the length of the allocated array back to the caller. It can be done by an additional "ref" parameter.
HTH.

Create empty native C++ byte* in C# to PInvoke with

I need to create a binary blob of empty data to PInvoke a native C++ dll that needs a unsigned char* of nulls.
The native C++ program is expecting a structure of data, and there's a nulled area of bytes in the middle, but in C# I can't just make a struct with an initialized byte[] in the middle.
My struct in C++ looks like this
struct myStruct
{
byte command;
byte returncode;
void* Source (a pointer to a string, rather a null term char*)
void* Destination (same thing)
byte filler [99]
byte options;
}
I've already figured that I can take a string and convert it to an array of bytes using myStruct.Source = (void*)Marshal.StringtToGlobalAnsi(source) (correct me if I'm wrong).
But I don't know how to fill out that empty array of bytes in the middle.
This is my C# struct so far.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct Trans_s
{
public byte command;
public byte returnCode;
public void* pSource;
public void* pDest;
public byte* filler;
public byte options;
}
For the array of bytes, you need to mark the field in the C# struct as a byvalarray. The default marshaller uses LPArray if you do not.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 99)]
public byte[] filler;
You need to allocate the memory for filler when you create an instance of the struct.
No, the C++ code doesn't expect an unsigned char* of nulls. It expects that many padding bytes inside the struct.
The C# fixed keyword might help you here.

Categories