I'm primarily a C# dev (not much C++ since college), but working on integrating a large collection of existing C++ code into an application. I have a C++/CLI assembly that is buffering the two worlds and have communication from C# through to C++ working fine. The question I have, is that the C++ class has a method call that generates a binary blob (think array of bytes in C# world) that I need to get in C# and process (pass around like a solid bag).
What I'm looking for is advice on how to handle the buffer/wrapper method (C++/CLI) between the two worlds. I assumed that I'd pass a char* and length, but C# sees that as a byte* (I'm assuming that is some C++/CLI "magic").
I also tried passing array<Byte ^>^ but that haven't had much luck translating the char* from the rest of the C++ lib to the byte array... and what I have doesn't smell right.
Have the C++/CLI code hand off a System::IO::UnmanagedMemoryStream to the C# code, which can then use a System.IO.BinaryReader on said stream to extract data as needed.
As an aside, C++/CLI's char is synonymous with C#'s sbyte, C++/CLI's unsigned char is synonymous with C#'s byte, C++/CLI's wchar_t is synonymous with C#'s char, and C++/CLI's array<unsigned char>^ is synonymous with C#'s byte[]. Note that it's array<unsigned char>^ or array<System::Byte>^ rather than array<unsigned char^>^ or array<System::Byte^>^, as System.Byte is a value type rather than a ref type.
You can use an UnmanagedMemoryStream, like so:
byte[] message = UnicodeEncoding.Unicode.GetBytes("Here is some data.");
IntPtr memIntPtr = Marshal.AllocHGlobal(message.Length);
byte* memBytePtr = (byte*) memIntPtr.ToPointer();
UnmanagedMemoryStream writeStream = new UnmanagedMemoryStream(memBytePtr, message.Length, message.Length, FileAccess.Write);
writeStream.Write(message, 0, message.Length);
writeStream.Close();
The reverse route, roughly:
UnmanagedMemoryStream readStream = new UnmanagedMemoryStream(memBytePtr, message.Length, message.Length, FileAccess.Read);
byte[] outMessage = new byte[message.Length];
readStream.Read(outMessage, 0, message.Length);
readStream.Close();
// Convert back into string for this example
string converted = UnicodeEncoding.Unicode.GetString(outMessage);
Marshal.FreeHGlobal(memIntPtr);
I'm sure that MSDN will have more resources
I took a shot at it and came up with this. Nothing crazy, just allocate the managed array, copy the data and return it.
header:
#pragma once
using namespace System;
using namespace System::Runtime::InteropServices;
namespace CLRLib
{
public ref class TwiddlerFunctions
{
public:
static array< Byte >^ GetArray();
};
}
implementation:
#include "CLRLib.h"
array< Byte >^ CLRLib::TwiddlerFunctions::GetArray()
{
unsigned char data[] = { 1, 2, 34, 5 };
// convert the unmanaged array to a managed array
array< Byte >^ arr = gcnew array< Byte >(sizeof data);
Marshal::Copy((IntPtr)data, arr, 0, arr->Length);
return arr;
}
C# side:
using System;
using CLRLib;
class Program
{
static void Main(string[] args)
{
byte[] arr = TwiddlerFunctions.GetArray();
Console.WriteLine(String.Join(" ", arr)); // 1 2 34 5
}
}
Related
C# code:
class Hello{
public void helloWorld(char[] chars){
//do something
}
}
C++ code to call C#:
MyCSDLL::Hello* hello;
//init hello, some calls are ok.
char* myCharPtr;
//init with message
HRESULT result = hello->helloWorld(safeArray, (MyCSDLL::_MyRetVal) _retValPtr);
Adapting from How to create and initialize SAFEARRAY of doubles in C++ to pass to C#
void createSafeArray(SAFEARRAY** saData, char* charPtr)
{
char* iterator = charPtr;
SAFEARRAYBOUND Bound;
Bound.lLbound = 0;
Bound.cElements = 10;
*saData = SafeArrayCreate(VT_R8, 1, &Bound);
char HUGEP *pdFreq;
HRESULT hr = SafeArrayAccessData(*saData, (void HUGEP* FAR*)&pdFreq);
if (SUCCEEDED(hr))
{
do {
*pdFreq++ = *iterator;
} while (*iterator++);
}
}
How to call hello->helloWorld()? it is expecting SAFEARRAY*. The current code gives 80131538 error. How to fix it?
C++ Project is not CLR.
Let's suppose the C# code is this:
namespace ClassLibrary1
{
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Hello
{
public void helloWorld(char[] chars)
{
...
}
}
}
Then, you can call it with this C/C++ code, for example:
#import "C:\mycode\ClassLibrary1\bin\Debug\classlibrary1.tlb" raw_interfaces_only
using namespace ClassLibrary1;
HRESULT CallHello(wchar_t* charPtr, int count)
{
CComPtr<_Hello> p;
HRESULT hr = p.CoCreateInstance(__uuidof(Hello));
if (FAILED(hr))
return hr;
SAFEARRAY* psa = SafeArrayCreateVector(VT_UI2, 0, count);
if (!psa)
return E_OUTOFMEMORY;
LPVOID pdata;
hr = SafeArrayAccessData(psa, &pdata);
if (SUCCEEDED(hr))
{
CopyMemory(pdata, charPtr, count * 2); // count is the number of chars
SafeArrayUnaccessData(psa);
hr = p->helloWorld(psa);
}
SafeArrayDestroy(psa);
return hr;
}
.NET's char type is unicode, so the binary size is two bytes, the C equivalent is wchar_t (or unsigned short, etc...). So the safearray element type must match that, that's why I used VT_UI2 (VT_R8 that you used is Real of size 8 bytes, so it's equivalent to .NET's double type).
If you really want to use C's char, then you must do some kind of conversion to a 2-byte character.
Also, you can use the SafeArrayCreateVector function which directly allocates a 1-dimension safe array. Don't forget to call cleanup methods.
Let's say we have a pure virtual C++ class:
class INativeInterface {
public:
virtual ~INativeInterface () {};
virtual LPCWSTR GetString () = 0;
};
and then we need to provide an implementation of this interface in C++/CLI:
class HalfManagedImplementation : public INativeInterface {
public:
virtual LPCWSTR GetString () override {
// need to return wchar_t const * pointer which points to the
// data of our managedData string
// pin_ptr is not suitable as it will go out of scope
// what other options do we have here?
// perhaps copying managed string contents to unmanaged heap?
wchar_t * unmanagedString = new wchar_t [managedData->Length + 1];
pin_ptr<const wchar_t> pinnedString = PtrToStringChars (managedData);
wcscpy_s (unmanagedString, managedData->Length + 1, pinnedString);
return unmanagedString;
}
private:
String^ managedData;
void SetString (String^ param){
// do something in .net
managedData = param;
}
};
My main questions are:
Can I allocate a native string on CRT heap and return a pointer to it to the native C++ code as I did above given that the memory allocated by managed code will be de-allocated by the native code?
An example of usage:
LPCWSTR data = cppCliObject->GetString ();
// do stuff with returned data or persist it by copying it somewhere else
delete[] data;
Is the first point valid when native C++ code is in a different dll than C++/CLI one?
Are there any other alternatives or best practices when returning wchar_t data to native C++?
As part of a WinRT C++cx component, what's the most efficient way to convert an unmanaged buffer of bytes (say expressed as a std::string) back and forth with a Windows::Web::Http::HttpBufferContent?
This is what I ended up with, but it doesn't seem very optimal:
std::string to HttpBufferContent:
std::string m_body = ...;
auto writer = ref new DataWriter();
writer->WriteBytes(ArrayReference<unsigned char>(reinterpret_cast<unsigned char*>(const_cast<char*>(m_body.data())), m_body.length()));
auto content = ref new HttpBufferContent(writer->DetachBuffer());
HttpBufferContent to std::string:
HttpBufferContent^ content = ...
auto operation = content->ReadAsBufferAsync();
auto task = create_task(operation);
if (task.wait() == task_status::completed) {
auto buffer = task.get();
size_t length = buffer->Length;
if (length > 0) {
unsigned char* storage = static_cast<unsigned char*>(malloc(length));
DataReader::FromBuffer(buffer)->ReadBytes(ArrayReference<unsigned char>(storage, length));
auto m_body = std::string(reinterpret_cast<char*>(storage), length);
free(storage);
}
} else {
abort();
}
UPDATE: Here's the version I ended up using (you can trivially create a HttpBufferContent^ from an Windows::Storage::Streams::IBuffer^):
void IBufferToString(IBuffer^ buffer, std::string& string) {
Array<unsigned char>^ array = nullptr;
CryptographicBuffer::CopyToByteArray(buffer, &array); // TODO: Avoid copy
string.assign(reinterpret_cast<char*>(array->Data), array->Length);
}
IBuffer^ StringToIBuffer(const std::string& string) {
auto array = ArrayReference<unsigned char>(reinterpret_cast<unsigned char*>(const_cast<char*>(string.data())), string.length());
return CryptographicBuffer::CreateFromByteArray(array);
}
I think you are making at least one unnecessary copy of your data in your current approach for HttpBufferContent to std::string, you could improve this by accessing the IBuffer data directly, see the accepted answer here: Getting an array of bytes out of Windows::Storage::Streams::IBuffer
I think it's better to use smart pointer (no memory management needed) :
#include <wrl.h>
#include <robuffer.h>
#include <memory>
using namespace Windows::Storage::Streams;
using namespace Microsoft::WRL;
IBuffer^ buffer;
ComPtr<IBufferByteAccess> byte_access;
reinterpret_cast<IInspectable*>(buffer)->QueryInterface(IID_PPV_ARGS(&byte_access));
std::unique_ptr<byte[]> raw_buffer = std::make_unique<byte[]>(buffer->Length);
byte_access->Buffer(raw_buffer.get());
std::string str(reinterpret_cast<char*>(raw_buffer.get())); // just 1 copy
I'm trying to migrate some managed c++ code to 64bits.
I have a function that gets varargs, and when I pass a System::String variable to it, it appears not to pass correctly.
Here is a simplification of the code that shows the problem:
#include <stdio.h>
#include <stdarg.h>
void test(char* formatPtr, ...)
{
va_list args;
int bufSize;
char buffer[2600];
/////////////////////////////////////
//parse arguments from function stack
/////////////////////////////////////
va_start(args, formatPtr);
bufSize = vsprintf(buffer, (const char*) formatPtr, args);
printf(buffer);
va_end(args);
}
void main() {
System::String^ s;
s = "Shahar";
test("Hello %s", s);
getchar();
}
When this code runs in 32 bits, it displays Hello Shahar.
When it runs in 64 bits, it displays Hello Çz∟⌠■.
Assuming I want to make the least amount of changes to the code, how should I fix this?
It looks as though the problem is in the mix between managed code and varargs. It appears that they are not compatible with each other.
I don't know why this works in 32-bits, but it looks like the wrong thing to do.
I changed the code, so as to be only managed code, with no varargs.
The %s specifier expects a C-style null-terminated string, not a System::String^. C++/CLI headers provide some methods that can convert System::String^ to std::string, which can be converted to a C-string, and can probably just convert straight to a C-string.
You have other problems too. void main()? Assigning a literal to a char*? Fixed-size buffer?
Is it possible to change strings (content and size) in Lua bytecode so that it will still be correct?
It's about translating strings in Lua bytecode. Of course, not every language has the same size for each word...
Yes, it is if you know what you're doing. Strings are prefixed by their size stored as an int. The size and endianness of that int is platform-dependent. But why do you have to edit bytecode? Have you lost the sources?
After some diving throught Lua source-code I found such a solution:
#include "lua.h"
#include "lauxlib.h"
#include "lopcodes.h"
#include "lobject.h"
#include "lundump.h"
/* Definition from luac.c: */
#define toproto(L,i) (clvalue(L->top+(i))->l.p)
writer_function(lua_State* L, const void* p, size_t size, void* u)
{
UNUSED(L);
return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0);
}
static void
lua_bytecode_change_const(lua_State *l, Proto *f_proto,
int const_index, const char *new_const)
{
TValue *tmp_tv = NULL;
const TString *tmp_ts = NULL;
tmp_ts = luaS_newlstr(l, new_const, strlen(new_const));
tmp_tv = &f_proto->k[INDEXK(const_index)];
setsvalue(l, tmp_tv, tmp_ts);
return;
}
int main(void)
{
lua_State *l = NULL;
Proto *lua_function_prototype = NULL;
FILE *output_file_hnd = NULL;
l = lua_open();
luaL_loadfile(l, "some_input_file.lua");
lua_proto = toproto(l, -1);
output_file_hnd = fopen("some_output_file.luac", "w");
lua_bytecode_change_const(l, lua_function_prototype, some_const_index, "some_new_const");
lua_lock(l);
luaU_dump(l, lua_function_prototype, writer_function, output_file_hnd, 0);
lua_unlock(l);
return 0;
}
Firstly, we have start Lua VM and load the script we want to modify. Compiled or not, doesn't matter. Then build a Lua function prototype, parse and change it's constant table. Dump Prototype to a file.
I hope You got that for the basic idea.
You can try using the decompiler LuaDec. The decompiler would allow the strings to be modified in generated Lua code similar to the original source.
ChunkSpy has A No-Frills Introduction to Lua 5.1 VM Instructions that may help you understand the compiled chunk format and make the changes directly to bytecode if necessary.