【发布时间】:2019-02-26 11:08:28
【问题描述】:
我在 Native C++ 中有一个类,我需要使用 PInvoke 在 C# 中创建一个包装器。我在将 std::wstring 返回到字符串时遇到问题。 dotnet 是否提供任何 Marshal 方法或属性?我不想像其他答案一样手动编写字符或字节转换。
节点.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 用于 PInvoke 的外部方法
#ifdef __cplusplus
extern "C" {
#endif
MYAPI const wchar_t* GetNodeName(NativeCore::Node* obj);
#ifdef __cplusplus
}
#endif
在我的 Node.cpp 中
MYAPI const wchar_t * GetNodeName(NativeCore::Node* obj)
{
if (obj != NULL)
return obj->GetName().c_str();
return NULL;
}
在我的 c# 包装器中
UnManagedWrapper.cs
类 UnMangedWrapper {
[DllImport("NativeCore.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string GetNodeName(IntPtr ptr);
}
在使用上述转换时,它不会将返回类型 const wchar_t* 转换为字符串。在这个 Pinvoke 中有没有其他方法可以将 std::wstring 转换为字符串?
我不会像下面这样通过获取字符串缓冲区来手动转换它。
[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();
}
【问题讨论】:
-
你有很深的问题。一旦函数返回,您返回的字符串就无效了。此外,pinvoke 假定它需要被销毁并调用 CoTaskMemFree。如果你用 CoTaskMemAlloc 分配一个字符数组并返回它,这一切都会起作用。
-
此外,还有其他解决问题的方法,这里有数百篇关于这个主题的帖子。接下来尝试一些研究。
-
@DavidHeffernan 我在任何地方都找不到相关答案。我想知道是否有任何简单的 MarshalAs 属性可以直接转换它。
-
您可以在搜索引擎中输入这两个函数名称并阅读文档。这对我来说更容易。
-
@S.FrankRicharrd 如答案中所述,使用如下所示的
BSTRs 不会导致内存问题,因为Marshal类将为您处理BSTR的释放。 (请注意,如果您自己打电话给Marshal.PointerToStringBSTR,情况并非如此。)