【发布时间】:2014-04-05 02:36:45
【问题描述】:
我正在用 C++ dll 项目开发一个 C# dll 项目。
假设 C++ dll 登录到某个网站,并在 Web 服务器上进行一些查询。
而 C++ dll 必须返回网站的 html 代码。
同时,C++ dll 必须保存来自网站的 cookie 数据。
所以,我将 StringBuilder 对象传递给 C++ 函数。
我已经知道如何使用 C# 中的 HttpWebRequest 和 HttpWebResponse 从网站获取 html 代码,但不幸的是我必须在 C++ dll 项目中进行。
请记住,我不需要任何 C# 代码。
我试过Passing a string variable as a ref between a c# dll and c++ dll。
从 C# 传递 StringBuilder 并将其作为 LPTSTR 获取。
它工作正常,但结果中缺少一些字符串。
我找不到原因。
无论如何,这是我的代码。
C++
extern "C" __declspec(dllexport) BSTR LoginQuery(const char* UserID, const char* UserPW, char Cookies[])
{
std::string html;
try
{
std::map<std::string, std::string> cookies;
MyClass *myclass = new MyClass();
html = myclass->LoginQuery(UserID, UserPW, cookies);
// Response cookies
std::string cookieData;
for (std::map<std::string, std::string>::iterator iterator = cookies.begin(); iterator != cookies.end(); iterator++)
{
cookieData += iterator->first;
cookieData += "=";
cookieData += iterator->second;
cookieData += ";";
}
sprintf(Cookies, cookieData.c_str(), 0);
delete myclass;
}
catch (...)
{
}
return ::SysAllocString(CComBSTR(html.c_str()).Detach());
}
C#
[DllImport(@"MyDll.dll", EntryPoint="LoginQuery", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern void LoginQuery(string UserID, string UserPW, StringBuilder Cookies);
void SomeThing()
{
StringBuilder _cookies = new StringBuilder(1024);
string result = LoginQuery("test", "1234", _cookies);
}
效果很好。
将StringBuilder作为cookie,我可以进行网站的下一个url。
(我在 C++ 项目中使用 libcurl。)
但问题是我有大约 100 个 id。
当它运行大约 3~40 时,它返回堆错误。
Debug Assertion Failed!
Program: ~~~~mics\dbgheap.c
Line: 1424
Expression: _pFirstBlock == pHead
我无法单击中止、重试或忽略按钮。
看起来 C# 应用程序挂起。
我阅读了很多关于 dbgheap 调试断言失败的文章。
类似于另一个堆中的空闲内存对象。
我是 C++ 的新手。
我在http://bytes.com/topic/c-sharp/answers/812465-best-way-interop-c-system-string-c-std-string 上阅读了 Edson 的问题。
但错误并不总是在特定时间出现。
所以我猜测,当 .NET 运行垃圾收集器时会发生这种情况。
.NET 垃圾收集器尝试释放一些从 C++ dll 创建的内存,我得到了堆错误。
我说的对吗?
我认为他和我的问题一样。
避免堆错误并将正确字符串从 C++ dll 返回到 C# dll 的最佳方法是什么?
P/S:只有当我在调试模式下运行时才会出现堆错误。我运行发布模式时没有收到错误消息。
编辑
根据 WhozCraig 的回答,我将代码更改如下。
//return ::SysAllocString(CComBSTR(html.c_str()).Detach());
CComBSTR res(html.c_str());
return res.Detach();
但运气不好,我仍然收到堆错误。
【问题讨论】:
-
相关,这个:
return ::SysAllocString(CComBSTR(html.c_str()).Detach());是内存泄漏。您正在分离 CComBSTR(这意味着它现在是原始 BSTR)然后返回 that 的 SysAllocString,永远泄漏分离的 BSTR。只需这样做:CComBSTR res(html.c_str()); return res.Detach(); -
您是否有理由不将 C++ 置于 C++/CLR 模式并传入 system.string 并在 C++ 级别处理此问题? ;) 这使得事情更容易使用。您使用的互操作旨在与“遗留纯 C”接口集成 - 如果您拥有 C++ 代码,只需打开集成。
-
当 cookie 过多时,您的 sprintf() 函数调用会损坏堆。 Cookies 参数除了使您的程序崩溃之外没有任何用处。去掉它。请注意,它也不会在 C++ 代码中执行任何操作,因此只需删除 sprintf() 调用即可。在尝试调用之前对 C++ 代码进行彻底的单元测试。
-
@WhozCraig 我会尝试更改我的代码。
-
@TomTom C++/CLR 是否可以使用 JustDecompile 等反编译器进行反编译?如果是这样,我不能使用它。