如何安全地将字符串引用从c#传递给c ++?

时间:2014-04-05 02:36:46

标签: c# c++ string reference

我正在使用C ++ dll项目开发一个C#dll项目。

让我们说C ++ dll登录到某个网站,并在网络服务器上进行一些查询。

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,我可以继续使用网站的下一个网址。

(我在C ++项目中使用libcurl。)

但问题是我有大约100个ID。

当它运行大约3~40时,它会返回堆错误。

Debug Assertion Failed!
Program: ~~~~mics\dbgheap.c
Line: 1424

Expression: _pFirstBlock == pHead

enter image description here

我无法点击中止,重试或忽略按钮。

看起来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();

但没有运气,我仍然得到堆错误。

2 个答案:

答案 0 :(得分:1)

你的问题是标题要求将字符串引用从c#传递给c ++,但是在文本后面你会问如何将字符串从C ++返回到C#。另外,你告诉你是C ++的新手。考虑到这一点,我将告诉我上次必须这样做时是如何进行这种互动的。我只是让C ++端分配并释放所有内存,只传递给C#IntPtrMarshal.PtrToStringAnsi(p)。在我的例子中,在C ++中存储线程局部接收并在每次函数调用时释放它们就足够了,但你可以创建一个C ++函数来释放它给出的任何ref。不是很有智慧,也不一定是最有效的方式,但它有效。

<强> UPD: 它完成they say它的作用。一些快速的谷歌搜索提出了this article。我认为它非常好,所以你可以参考它而不是我的建议。传递原始IntPtr是好的,如果指针不能自行释放(例如旧的Delphi / C ++ Builder样式字符串),并且您更喜欢在管理端比在本地方。

作为一个例子,一段代码执行Delphi交互(也适用于C ++ Builder):

    // function Get_TI_TC(AuraFileName:PAnsiChar; var TI,TC:PAnsiChar):Boolean; stdcall; external 'AuraToIec104.dll' name 'Get_TI_TC';
    [DllImport("AuraToIec104")]
    [return: MarshalAs(UnmanagedType.I1)]
    private static extern bool Get_TI_TC(string AuraFileName, out IntPtr TI, out IntPtr TC);
    public static bool Get_TI_TC(string AuraFileName, out string TI, out string TC)
    {
        IntPtr pTI, pTC;
        bool result = Get_TI_TC(AuraFileName, out pTI, out pTC);
        TI = Marshal.PtrToStringAnsi(pTI);
        TC = Marshal.PtrToStringAnsi(pTC);
        return result;
    }

答案 1 :(得分:0)

看起来你的问题很简单。您正在创建一个StringBuilder,可以容纳多达1024个字符。如果您的C ++函数返回的不止于此,那么您的应用程序将会崩溃(迟早)。

因此,要解决您的问题,请将StringBuilder的大小增加到可能的最大输出长度。更多详情:Passing StringBuilder to PInvoke function引用:

  

唯一的   警告是StringBuilder必须   为...分配足够的空间   返回值,或文本将   溢出,导致异常   由P / Invoke抛出。

在动态字符串长度的情况下,在C ++函数中使用BSTR参数实际上可能更好。然后,您可以在C#中使用[MarshalAs(UnmanagedType.AnsiBStr), Out] ref string ...,在C ++中使用BSTR*