簡體   English   中英

如何安全地將字符串引用從C#傳遞給C ++?

[英]How to safely pass string reference from c# to c++?

我正在使用C ++ dll項目開發C#dll項目。

假設C ++ dll登錄到某個網站,並在Web服務器上進行一些查詢。

C ++ dll必須返回網站的html代碼。

同時,C ++ dll必須保存網站上的cookie數據。

因此,我將StringBuilder對象傳遞給C ++函數。

我已經知道如何使用C#中的HttpWebRequest和HttpWebResponse從網站獲取html代碼,但是不幸的是,我必須在C ++ dll項目中進行操作。

因此請記住,我不需要任何C#代碼。

我試過在ac#dll和c ++ dll之間傳遞字符串變量作為ref

從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();

但是沒有運氣,我仍然遇到堆錯誤。

您的問題是標題詢問有關將字符串引用從c#傳遞到c ++的問題,但是稍后在文本中,您將詢問如何將字符串從c + +返回給c#。 另外,您告訴您C ++的新手。 考慮到這一點,我將告訴我上一次我必須如何進行這種交互。 我只是讓C ++端分配並釋放了所有內存,僅將IntPtr傳遞給C#作為Marshal.PtrToStringAnsi(p) ed。 就我而言,在C ++中以線程局部存儲recerences並在每次函數調用時釋放它們就足夠了,但是您可以使C ++函數釋放給出的所有引用。 雖然不是很聰明,也不一定是最有效的方法,但是它確實有效。

upd:確實按照他們所說的去做。 一些快速谷歌搜索又發表了這篇文章 我認為它非常好,因此您可以參考它而不是我的建議。 如果不能自行釋放指針(例如,像舊的Delphi / C ++ Builder樣式的字符串一樣),並且希望在托管方面而不是在本機方面更麻煩,則傳遞raw IntPtr很好。

例如,一段代碼進行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;
    }

看來您的問題很簡單。 您正在創建一個StringBuilder ,它最多可以容納1024個字符。 如果您的C ++函數返回的值超過該值,則您的應用程序將崩潰(早或晚)。

因此,要解決您的問題,請將StringBuilder的大小增加到最大可能的輸出長度。 更多詳細信息:將StringBuilder傳遞給PInvoke函數 ,該函數引用:

唯一的警告是,必須為StringBuilder分配足夠的空間用於返回值,否則文本將溢出,從而導致P / Invoke引發異常。

對於動態字符串長度,在C ++函數中使用BSTR參數實際上可能更好。 然后,您可以在C#中使用[MarshalAs(UnmanagedType.AnsiBStr), Out] ref string ...在C ++中使用BSTR*

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM