I have a C++ function that is called with a char* buffer to be used for the output. Now i want to write to it using C# so i need to call a managed method from the C++ function. What i can't figure out is how to pass it as a StringBuilder.
I previously used Robert Giesecke's Unmanaged Exports which worked fine and did this automatically using a default string marshalling scheme, but i want to use the size parameter for the MaxCapacity of the StringBuilder.
Is there a nicer way to do this than creating a new StringBuilder instance, writing to it, getting a CLR string with ToString(), and then copying the contents to the buffer? The strings in question might be as long as 10,000 characters and i don't really like the idea of copying it twice each time.
C#
public static void MyMethod(StringBuilder buffer)
{
//...
}
C++/CLI
extern "C" __declspec(dllexport)
void __stdcall MyFunction(char* buffer, int length)
{
MyNamespace::MyClass::MyMethod( /* ? */ );
}
Use a byte [] then c++ 8 bit character array will align correctly.
I prefer to do something similar to this because I use it for unmanaged code:
String ^ ToManagedString(const char * pString) {
return Marshal::PtrToStringAnsi(IntPtr((char *)pString)); // Marshal pString into Managed Memory. return as a C# string reference (^).
}
const std::string ToStdString(String ^ strString) {
IntPtr ptrString = IntPtr::Zero;
std::string strStdString;
try {
ptrString = Marshal::StringToHGlobalAnsi(strString); // Marshal a C# String reference into unmanaged HEAP memory space.
strStdString = (char *)ptrString.ToPointer(); // Convert C# IntPtr to char* implicitly, call assignment operator of std::string to copy.
}
finally {
if (ptrString != IntPtr::Zero) {
Marshal::FreeHGlobal(ptrString);
}
}
return strStdString; // return std::string copied out of interop unmanaged heap space
}
Related
I'm trying to figure out how to return a string value from a C++/CLI method back to unmanaged C++ that calls it. In my current implementation, I have a string stored in a local String ^ variable in a (managed) C++/CLI method that I w/like the method to return back to the unmanaged C++ program that calls it. If using a String ^ variable is not a good choice, what construct/type w/be better? Note, I'm leaving out a part where a C# method returns the string value back to the C++/CLI method as it is not a problem.
I'm using VS2017.
CODE Example - for simplicity, code has been reduced.
Unmanaged C++ -----------------------------
_declspec(dllexport) void GetMyString();
int main()
{
GetMyString();
}
(managed) C++/CLI -------------------------
__declspec(dllexport) String GetMyString()
{
String ^ sValue = "Return this string";
return (sValue);
}
Any help is greatly appreciated. Thanks ahead of time.
You cannot return a String ^ to c++, since it will not recognize it. There is some conversions using the InteropServices though. From microsoft
using namespace System;
void MarshalString ( String ^ s, std::string& os ) {
using namespace Runtime::InteropServices;
const char* chars =
(const char*)(Marshal::StringToHGlobalAnsi(s)).ToPointer();
os = chars;
Marshal::FreeHGlobal(IntPtr((void*)chars));
}
I ended up converting a System::String^ to a std::string in a managed C++ method, returning the later to the unmanaged C++ caller.
Managed C++ file excerpts:
#include <msclr\marshal_cppstd.h>
__declspec(dllexport) std::string MyManagedCppFn()
{
System::String^ managed = "test";
std::string unmanaged2 = msclr::interop::marshal_as<std::string>(managed);
return unmanaged2;
}
Unmanaged C++ file excerpts:
_declspec(dllexport) std::string MyMangedCppFn();
std::string jjj = MyMangedCppFn(); // call Managed C++ fn
Credit goes to an answer/edit from tragomaskhalos and Juozas Kontvainis to a stackoverflow question asking how to convert a System::String^ to std::string.
(sorry if this has been asked before, but most of the examples that I've seen are passing data from managed -> native, not the other way around).
Short question: How do I fetch a string from the managed world from within native C++ code?
Long question + background:
I'm working with some legacy C++ code that formerly had the capability to get and set name/value string pairs (to greatly simplify the design). I wanted to move that name/value pair mechanism up into C# managed code with the rest of our application, so I put in function callbacks in the C++ world that call up into the managed code for getting and setting. The C++ function pointer types are as follows:
typedef int (GetConfigParamCallback)(const char* paramName, char* value);
typedef GetConfigParamCallback* LPGetConfigParamCallback;
typedef int (SetConfigParamCallback)(const char* paramName, const char* value);
typedef SetConfigParamCallback* LPSetConfigParamCallback;
As you can see, the tricky one is the get, where I want to provide memory to the caller to fill up. This will be the managed code moving forward.
These callbacks are represented by delegates in the C# world like so:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int Native_GetConfigParamCallBackMethodDelegate(
string paramName, StringBuilder paramValue);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int Native_SetConfigParamCallBackMethodDelegate(
string paramName, string paramValue);
And then my GetConfig wrapper function in the managed code that acquires the values looks like this (and puts the correct value into paramValue as seen by my debugging):
static int GetConfigParamCallBackWrapper(
string paramName,
System.Text.StringBuilder paramValue)
{
string valueTemp = // Fetch the string value here
if (valueTemp == null)
{
return 0;
}
paramValue.Append(valueTemp);
return 1;
}
So when the managed C# starts up, it sets these callback functions in the native world. Then, I have the native code run a series of what amounts to unit test methods that are getting and setting these strings. Now, on desktop this works fine, but when I try to run this on iOS with a Xamarin built app, the string comes back to the native world as garbage, AFAICT.
I’ve tried doing manual marshaling with IntPtr as well, and also no luck.
You can marshal the string as a BSTR:
C++:
typedef int (GetConfigParamCallback)(const char* paramName, BSTR* value);
typedef GetConfigParamCallback* LPGetConfigParamCallback;
// Usage:
LPGetConfigParamCallback pCallback = // ...
CComBSTR value;
int result = (*pCallback)("...", &value);
C#:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int Native_GetConfigParamCallBackMethodDelegate(
string paramName,
[MarshalAs(UnmanagedType.BStr)] ref string paramValue
);
static int GetConfigParamCallBackWrapper(
string paramName,
ref string paramValue
)
{
string valueTemp = // Fetch the string value here
if (valueTemp == null)
{
return 0;
}
paramValue = valueTemp;
return 1;
}
So I was able to find a slightly less pretty, but valid solution, thanks to this excellent blog post: http://randomencoding.tumblr.com/post/48564128118/returning-a-string-from-a-c-callback-to-c.
I used an IntPtr and did manual Marshaling:
static int GetConfigParamCallBackWrapper(
string paramName,
IntPtr paramValue)
{
string valueTemp = // Acquire valueTemp here.
IntPtr sPtr = Marshal.StringToHGlobalAnsi(valueTemp);
try
{
// Create a byte array to receive the bytes of the unmanaged string
var sBytes = new byte[valueTemp.Length + 1];
// Copy the the bytes in the unmanaged string into the byte array
Marshal.Copy(sPtr, sBytes, 0, valueTemp.Length);
// Copy the bytes from the byte array into the buffer passed into this callback
Marshal.Copy(sBytes, 0, paramValue, sBytes.Length);
// Free the unmanaged string
}
finally
{
Marshal.FreeHGlobal(sPtr);
}
The question in short words is :
How to free memory returned from Native DLL as ItrPtr in managed code?
Details :
Assume we have simple function takes two parameters as OUTPUT, The first one is Reference Pointer to byte array and the second one is Reference Int .
The function will allocate amount of bytes based on some rules and return the pointer of memory and the size of bytes and the return value (1 for success and 0 for fail) .
The code below works fine and I can get the byte array correctly and the count of bytes and the return value, but when I try to free the memory using the pointer (IntPtr) I get exception :
Windows has triggered a breakpoint in TestCppDllCall.exe.
This may be due to a corruption of the heap, which indicates a bug in TestCppDllCall.exe or any of the DLLs it has loaded.
This may also be due to the user pressing F12 while TestCppDllCall.exe has focus.
The output window may have more diagnostic information.
To make things clear :
The next C# code work correctly with other DLL function have the same signature and freeing the memory works without any problem .
Any modification in (C) code accepted if you need to change allocation memory method or adding any other code .
All the functionality I need is Native DLL function accept Two Parameter by reference (Byte array and int , In c# [IntPtr of byte array and int]) fill them with some values based on some rules and return the function result (Success or Fail) .
CppDll.h
#ifdef CPPDLL_EXPORTS
#define CPPDLL_API __declspec(dllexport)
#else
#define CPPDLL_API __declspec(dllimport)
#endif
extern "C" CPPDLL_API int writeToBuffer(unsigned char *&myBuffer, int& mySize);
CppDll.cpp
#include "stdafx.h"
#include "CppDll.h"
extern "C" CPPDLL_API int writeToBuffer(unsigned char*& myBuffer, int& mySize)
{
mySize = 26;
unsigned char* pTemp = new unsigned char[26];
for(int i = 0; i < 26; i++)
{
pTemp[i] = 65 + i;
}
myBuffer = pTemp;
return 1;
}
C# code :
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace TestCppDllCall
{
class Program
{
const string KERNEL32 = #"kernel32.dll";
const string _dllLocation = #"D:\CppDll\Bin\CppDll.dll";
const string funEntryPoint = #"writeToBuffer";
[DllImport(KERNEL32, SetLastError = true)]
public static extern IntPtr GetProcessHeap();
[DllImport(KERNEL32, SetLastError = true)]
public static extern bool HeapFree(IntPtr hHeap, uint dwFlags, IntPtr lpMem);
[DllImport(_dllLocation, EntryPoint = funEntryPoint, CallingConvention = CallingConvention.Cdecl)]
public static extern int writeToBuffer(out IntPtr myBuffer, out int mySize);
static void Main(string[] args)
{
IntPtr byteArrayPointer = IntPtr.Zero;
int arraySize;
try
{
int retValue = writeToBuffer(out byteArrayPointer, out arraySize);
if (retValue == 1 && byteArrayPointer != IntPtr.Zero)
{
byte[] byteArrayBuffer = new byte[arraySize];
Marshal.Copy(byteArrayPointer, byteArrayBuffer, 0, byteArrayBuffer.Length);
string strMyBuffer = Encoding.Default.GetString(byteArrayBuffer);
Console.WriteLine("Return Value : {0}\r\nArray Size : {1}\r\nReturn String : {2}",
retValue, arraySize, strMyBuffer);
}
}
catch (Exception ex)
{
Console.WriteLine("Error calling DLL \r\n {0}", ex.Message);
}
finally
{
if (byteArrayPointer != IntPtr.Zero)
HeapFree(GetProcessHeap(), 0, byteArrayPointer);
}
Console.ReadKey();
}
}
}
When I debug this code i set break point in the line (return 1) and the value of the buffer was :
myBuffer = 0x031b4fc0 "ABCDEFGHIJKLMNOPQRSTUVWXYZ««««««««î"
And I got the same value in C# code when the function call return and the value was :
52121536
The result I Got the correct Memory pointer and i am able to get the byte array value , how to free these memory blocks with this pointer in C# ?
Please let me know if there anything is not clear or if there any typo, I am not native English speaker .
Short answer: you should add a separate method in the DLL that frees the memory for you.
Long answer: there are different ways in which the memory can be allocated inside your DLL implementation. The way you free the memory must match the way in which you have allocated the memory. For example, memory allocated with new[] (with square brackets) needs to be freed with delete[] (as opposed to delete or free). C# does not provide a mechanism for you to do it; you need to send the pointer back to C++.
extern "C" CPPDLL_API void freeBuffer(unsigned char* myBuffer) {
delete[] myBuffer;
}
If you are allocating your own memory in native code, use CoTaskMemAlloc and you can free the pointer in managed code with Marshal.FreeCoTaskMem. CoTaskMemAlloc is described as "the only way to share memory in a COM-based application" (see http://msdn.microsoft.com/en-us/library/windows/desktop/aa366533(v=vs.85).aspx )
if you need to use the memory allocated with CoTaskMemAlloc with a native C++ object, you can use placement new to initialize the memory as if the new operator was used. For example:
void * p = CoTaskMemAlloc(sizeof(MyType));
MyType * pMyType = new (p) MyType;
This doesn't allocate memory with new just calls the constructor on the pre-allocated memory.
Calling Marshal.FreeCoTaskMem does not call the destructor of the type (which isn't needed if you just need to free memory); if you need to do more than free memory by calling the destructor you'll have to provide a native method that does that and P/Invoke it. Passing native class instances to managed code is not supported anyway.
If you need to allocate memory with some other API, you'll need to expose it in managed code through P/Invoke in order to free it in managed code.
HeapFree(GetProcessHeap(), 0, byteArrayPointer);
No, that can't work. The heap handle is wrong, the CRT creates its own heap with HeapCreate(). It's buried in the CRT data, you can't get to it. You could technically find the handle back from GetProcessHeaps(), except you don't know which one it is.
The pointer can be wrong too, the CRT may have added some extra info from the pointer returned by HeapAlloc() to store debugging data.
You'll need to export a function that calls delete[] to release the buffer. Or write a C++/CLI wrapper so you can use delete[] in the wrapper. With the extra requirement that the C++ code and the wrapper use the exact same version of the CRT DLL (/MD required). This almost always requires that you can recompile the C++ code.
I would like a clean way to increase the size of a StringBuilder() as required for population by native code, the callback method below seems clean, but somehow we get a copy of the buffer instead of the actual buffer - I'm interested in explanations and solutions (preferably sticking to the callback type allocation as it would be nice and clean if only it could be made work).
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace csharpapp
{
internal class Program
{
private static void Main(string[] args)
{
var buffer = new StringBuilder(12);
// straightforward, we can write to the buffer but unfortunately
// cannot adjust its size to whatever is required
Native.works(buffer, buffer.Capacity);
Console.WriteLine(buffer);
// try to allocate the size of the buffer in a callback - but now
// it seems only a copy of the buffer is passed to native code
Native.foo(size =>
{
buffer.Capacity = size;
buffer.Replace("works", "callback");
return buffer;
});
string s = buffer.ToString();
Console.WriteLine(s);
}
}
internal class Native
{
public delegate StringBuilder AllocateBufferDelegate(int bufsize);
[DllImport("w32.dll", CharSet = CharSet.Ansi)]
public static extern long foo(AllocateBufferDelegate callback);
[DllImport("w32.dll", CharSet = CharSet.Ansi)]
public static extern void works(StringBuilder buf, int bufsize);
}
}
native header
#ifdef W32_EXPORTS
#define W32_API __declspec(dllexport)
#else
#define W32_API __declspec(dllimport)
#endif
typedef char*(__stdcall *FnAllocStringBuilder)(int);
extern "C" W32_API long foo(FnAllocStringBuilder fpAllocate);
extern "C" W32_API void works(char *buf, int bufsize);
native code
#include "stdafx.h"
#include "w32.h"
#include <stdlib.h>
extern "C" W32_API long foo(FnAllocStringBuilder fpAllocate)
{
char *src = "foo X";
int len = strlen(src) + 1;
char *buf = fpAllocate(len);
return strcpy_s(buf,len,src);
}
extern "C" W32_API void works(char *buf, int bufsize)
{
strcpy_s(buf,bufsize,"works");
}
I have a theory for why this happens. I suspect that the marshalling of StringBuilder involves making a copy of the data, passing it to the P/Invoke call, and then copying back into the StringBuilder. I couldn't actually verify this though.
The only alternative to this would require the StringBuilder to be flattened first (it is internally a linked list of char[]'s), and the char[] pinned, and even then this would only work for marshalling to pointer-to-Unicode-chars strings, but not to ANSI or COM strings.
Thus, when you pass a StringBuilder as an argument, there's an obvious place for .NET to copy any changes back: right after the P/Invoke returns.
The same isn't true for when you pass a delegate returning a StringBuilder. In this case .NET needs to create a wrapper which converts an int => StringBuilder function into an int => char* function. This wrapper will create the char* buffer and populate it, but obviously can't copy any changes back yet. It also can't do this after the function that takes the delegate returns: it's still too early!
In fact, there is no obvious place at all where the reverse copy could occur.
So my guess is that this is what happens: when marshalling a StringBuilder-returning delegate, .NET can only perform a one-way conversion, hence any changes you make aren't reflected in the StringBuilder. This is slightly better than being completely unable to marshal such delegates.
As for solutions: I would recommend first asking the native code how large the buffer needs to be, and then passing a buffer of the appropriate size in a second call. Or, if you need better performance, guess a large enough buffer, but allow the native method to communicate that more space is required. This way most calls would involve only one P/Invoke transition.
This can be wrapped into a more convenient function that you can just call from the managed world without worrying about buffers.
In addition to the input provided by romkyns I will share the minimal changes solution I came up with. If anyone uses this be careful of your encodings!
the principal modification is:
private static void Main(string[] args)
{
byte[] bytes = null;
var gcHandle = new GCHandle();
Native.foo(size =>
{
bytes = new byte[size];
gcHandle = GCHandle.Alloc(bytes,GCHandleType.Pinned);
return gcHandle.AddrOfPinnedObject();
});
if(gcHandle.IsAllocated)
gcHandle.Free();
string s = ASCIIEncoding.ASCII.GetString(bytes);
Console.WriteLine(s);
}
with the delegate signature changeing to:
public delegate IntPtr AllocateBufferDelegate(int bufsize);
This question already has answers here:
Marshal "char *" in C#
(2 answers)
Closed 4 years ago.
I am using PInvoke for interoperability between Native Code (C++) and Managed Code (C#).
I just write a simple function which gets a string from C++ code. My code looks like
C# Code:
[DllImport("MyDll.dll")]
private static extern string GetSomeText();
public static string GetAllValidProjects() {
string s = GetSomeText();
return s;
}
C++ Code
char* GetSomeText() {
std::string stri= "Some Text Here";
char * pchr = (char *)stri.c_str();
return pchr;
}
All works fine at C++ end, i.e the variable pchr contains "Some Text Here" but at C# the string s contains noting in it. I don't know what I am doing wrong. Any help would be appreciated
First of all, as others have pointed out, your C++ is broken even before trying interop. You are returning a pointer to stri's buffer. But because stri is destroyed as soon as the function returns, the return value is not valid.
What's more, even if you fixed this, you need to do more. It won't work allocating memory in your C++ code which you would need the C# code to deallocate.
There are a few options to do it right.
Your C# code can ask the C++ code how long the string is. Then a C# StringBuilder is created and allocated to the appropriate size. Next the StringBuilder object is passed to the C++ code and its default marshalling is as a LPWSTR. In this approach the C# code allocates the string and your C++ code receives a C string to which it must copy the buffer.
Alternatively you can return a BSTR from the C++ which allows allocation in the native C++ code and deallocation in the C# code.
The BSTR approach is probably how I would do it. It looks like this:
C++
#include <comutil.h>
BSTR GetSomeText()
{
return ::SysAllocString(L"Greetings from the native world!");
}
C#
[DllImport(#"test.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText();
Update
Hans Passant added a couple of useful observations in the comments. First of all, most P/Invoke interop is done against an existing interface which cannot be changed and you do not have the luxury of picking your preferred interop interfacing approach. It would appear that is not the case here, so which approach should be chosen?
Option 1 is to allocate the buffer in the managed code, after having first asked the native code how much space is needed. Perhaps it is enough to use a fixed size buffer that both parties agree on.
Where option 1 falls down is when assembling the string is expensive and you don't want to do it twice (e.g. once to return its length, and once again for the contents). This is where option 2, the BSTR comes into play.
Hans pointed out one drawback of the BSTR, namely that it carries a UTF-16 payload but your source data may well char*, which is a "bit of a hassle".
To overcome the hassle you can wrap up the conversion from char* to BSTR like this:
BSTR ANSItoBSTR(char* input)
{
BSTR result = NULL;
int lenA = lstrlenA(input);
int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0);
if (lenW > 0)
{
result = ::SysAllocStringLen(0, lenW);
::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW);
}
return result;
}
That's the hardest one out of the way, and now it's easy to add other wrappers to convert to BSTR from LPWSTR, std::string, std::wrstring etc.
Another way to get a string from C++. In case the you cannot modify your c++ dll. You can declare the DllImport with IntPtr instead of string. When the function is invoked, you can marshal the Ptr back to String.
[DllImport("MyDll.dll")]
private static extern IntPtr GetSomeText();
public static string GetAllValidProjects()
{
string s = Marshal.PtrToStringAnsi(GetSomeText());
return s;
}
Note : as mention in the previous post. "Some Text Here" is allocated on the stack so as soon as the function returns, the stack will unwire. Therefore the data is potential be overridden. Hence you shall use Marshal.PtrToStringAnsi right after the call. Don't hold to the IntPtr.
char* GetSomeText()
{
std::string stri= "Some Text Here";
char * pchr = (char *)stri.c_str();
return pchr;
}
It is because
std::string stri= "Some Text Here";
is a stack object and it is getting destroyed after GetSomeText() call. After the call pchr pointer which you are returning is invalid.
You may need to allocate space for the text dynamically to be able to access it latter.
Change your C++ function definition to:
char* GetSomeText()
{
std::string stri = "Some Text Here";
return strcpy(new char[stri.size()], stri.c_str());
}
Something like above. You got the idea...
GetSomeText() is returning pointer of char is wont work if u declare string variable to store it.
try
[DllImport("MyDll.dll")]
private static extern string GetSomeText();
public static string GetAllValidProjects()
{
string s = new string(GetSomeText());
return s;
}
there's a constructor that takes char*