i have a class in Native C++ for which i need to create a wrapper in c# using PInvoke. I'm facing issues while returning std::wstring to string. Is there any Marshal methods or attributes provided by dotnet? I don't want to manually write char or byte conversions as like other answers.
Node.h
#ifndef MYAPI // defined export in preprocessor
#define MYAPI __declspec(dllimport)
#endif
class MYAPI Node
{
public:
Node();
~Node();
inline std::wstring GetName() { return mName; }
inline void SetName(const wchar_t* name) { mName = std::wstring(name); }
private:
std::wstring mName;
};
//c extern methods for PInvoke
#ifdef __cplusplus
extern "C" {
#endif
MYAPI const wchar_t* GetNodeName(NativeCore::Node* obj);
#ifdef __cplusplus
}
#endif
in my Node.cpp
MYAPI const wchar_t * GetNodeName(NativeCore::Node* obj)
{
if (obj != NULL)
return obj->GetName().c_str();
return NULL;
}
In my c# wrapper
UnManagedWrapper.cs
class UnMangedWrapper {
[DllImport("NativeCore.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string GetNodeName(IntPtr ptr);
}
it is not converting the return type const wchar_t* to string when using the above conversion. Is there any other method to convert std::wstring to string in this Pinvoke?
I don't to manually convert it by getting the string buffer as like below.
[DllImport( "my.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode )]
private static extern void GetMyString(StringBuffer str, int len);
public string GetMyStringMarshal()
{
StringBuffer buffer = new StringBuffer(255);
GetMyString(buffer, buffer.Capacity);
return buffer.ToString();
}
In your Node.cpp
file, use SysAllocString
from oleauto.h
(or include Windows.h
) to allocate the string for you like this:
MYAPI BSTR GetNodeName(NativeCore::Node* obj)
{
if (obj != NULL)
return SysAllocString(obj->GetName().c_str());
return NULL;
}
Then adjust your native method wrapper to use UnmanagedType.BStr
instead of UnmanagedType.LPWStr
:
class UnMangedWrapper
{
[DllImport("NativeCore.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string GetNodeName(IntPtr ptr);
}
Using BSTR
has the advantage that you don't have to call into the unmanaged code twice (once for the buffer length and another time for the actual string content) and the marshaller can automatically take care of deallocating the unmanaged string.