【发布时间】:2016-04-20 10:49:00
【问题描述】:
我有一个 C++/CLI DLL,我计划将其用作我的 C# DLL 和本机 C++ 客户端之间的适配器。我需要在两个方向上传递字符串。该适配器使用 VS2013 编译,但需要支持使用 VS2008 构建的客户端,因此我在 API 中使用 const char*。但是,即使两者都是 VS2013 构建的,我所拥有的也无法正常工作。
在其他地方,我找到了使用 msclr\marshal.h 的建议,因此我创建了:
using namespace msclr::interop;
System::String^ ToCliString(const char* s)
{
System::String ^result = marshal_as<System::String^>(s);
return result;
}
const char* ToCppString(System::String^ s)
{
msclr::interop::marshal_context context;
const char* result = context.marshal_as<const char*>(s);
return result;
}
为了测试这些,我在我的 C++/CLI DLL 中创建了一个往返转换方法:
const char* Foo(const char *cstar)
{
System::String^ cli = ::ToCliString(cstar);
if (cli == "abc")
{
MessageBox::Show("const char* -> CLI: OK");
}
const char* cstar2 = ::ToCppString(cli);
if (std::strcmp(cstar2, "abc") == 0)
{
MessageBox::Show("CLI -> const char*: OK");
}
else if (std::strcmp(cstar2, "") == 0)
{
MessageBox::Show("ToCppString returned empty string");
}
else
{
MessageBox::Show("ToCppString returned something else");
}
return cstar2;
}
当本地 C++ 客户端调用 Foo("abc") 时,会显示第一条消息,然后显示“返回其他内容”消息,并且客户端会收到垃圾邮件 (îþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþÞ›vÚ§)。所以看起来我的第二次转换不起作用。
更新
这是我使用的最终解决方案。
我使用 zneaks 的建议来编组到 std::string 和 PaulMcKenzie 的建议来传递调用者分配的 char* 而不是返回 const char*。谢谢两位。
void ToCppString(System::String^ input, char* output)
{
std::string temp= marshal_as<std::string>(input);
strcpy(output, temp.c_str());
}
void Foo(const char* input, char* output)
{
System::String^ cli = ::ToCliString(input);
::ToCppString(cli, output);
}
【问题讨论】:
-
您正在返回一个指向局部变量
cstar2的指针,这是未定义的行为。 -
@PaulMcKenzie 因此,要将字符串从 C# 传递到本机 C++ 客户端,客户端需要传递适配器“填充”的 char *?
-
这是通常的做法。客户端提供缓冲区,C++ 代码用数据填充缓冲区。
-
@PaulMcKenzie 那么客户端是否需要调用 delete 来防止内存泄漏?我猜他们可以分配一次缓冲区,在我的 API 中调用许多函数,并在完成后删除。所以我的 API 应该包含另一个参数,以便他们可以告诉我可以返回的最大大小消息?或者更适合让属性客户端可以查询以确定我的最大消息长度?
-
客户端负责正确维护缓冲区。它可能已分配有
new[] / delete[],甚至可能是std::vector<char>,而您正在传递std::vector<char>::data(),等等。