2016-10-05 62 views
2

我想讓一個DLL公開某個API,因此想實現一個安全的方式來複制DLL邊界上的字符串。 DLL實現非常簡單 - 所有返回字符串值的函數都帶有兩個參數 - char*size_t&。如果大小足夠大,則將DLL內部字符串的內容寫入給定指針,將大小設置爲實際大小並返回成功的返回碼。如果不是,我將它的大小設置爲它應該返回的錯誤代碼。 DLL方面非常簡單。通過DLL邊界複製安全字符串

現在更復雜 - 如何製作一個很好的模板函數,該函數給出指向DLL中某個函數的指針將執行所有正確的操作來填充std::string的實例。這是我來到了:

template<typename I> 
CErrorCode GetStringValue(const I& Instance, CErrorCode(I::*pMethod)(char*, size_t&) const, std::string& sValue) 
{ 
    std::string sTemporaryValue; 
    size_t nValueLength = sTemporaryValue.capacity(); 
    sTemporaryValue.resize(nValueLength); 
    do 
    { 
     auto eErrorCode = (Instance.*pMethod)(const_cast<char*>(sTemporaryValue.c_str()), nValueLength); 
     if (eErrorCode == CErrorCode::BufferTooSmall) 
     { 
      sTemporaryValue.resize(nValueLength); 
     } 
     else 
     { 
      if (eErrorCode == CErrorCode::NoError) 
      { 
       sTemporaryValue.resize(nValueLength); 
       sValue = std::move(sTemporaryValue); 
      } 
      return eErrorCode; 
     } 
    } 
    while (true); 
} 

所以我做了初步resize,因爲我不能這樣做,第一個電話後(因爲那就刪除的內容,因爲該字符串最初是空的)。但resize用零字符填充字符串,這會讓我感到不安(我的意思是我知道我將會自己做填充)。然後,即使在成功運行後,我也需要resize,因爲如果長度實際上更小,我需要調整大小。任何建議,如果這可以做我更好的方式?

+0

我會建議一個不同的設計模式:不是傳遞一個緩衝區和一個'size_t&'引用,傳遞一個函數指針和一個不透明的指針。 DLL調用函數指針,傳遞給它的字符串所需的'size_t'和不變的指針。函數指針負責分配足夠多的'char []',並返回一個指向它的指針。無需處理笨拙的'BufferTooSmall',並將其包裝到C++類中變得微不足道。 –

+0

好吧,但如果我知道大多數時間所有的調用者都會使用'std :: string',那麼我在傳輸到字符串時會進行分配和複製。現在我有機會在第一次調用時填充值,從而只執行默認的'std :: string'構造函數中完成的內部分配。 –

+0

沒有禁止實現將不透明指針轉換爲'std :: string *'的函數的規則,然後相應地調整它的大小並返回一個指向該字符串數據的指針,然後將一個指針傳遞給該函數,並將一個「std :: string',到你的DLL。 –

回答

0

由於您使用的是DLL,我的理解是您在Windows上,因此我將建議一個Windows特定的解決方案。

嗯,跨模塊邊界安全地傳遞字符串問題已經由COM內存分配器解決了在Windows和BSTR

因此,不是傳遞一個字符串指針和一個大小指針,而是檢查調用者分配的緩衝區是否足夠大,並且如果沒有返回所需的大小等等,對於我來說似乎簡單得多從BSTR該DLL。

BSTR s是使用COM分配器分配的,因此您可以分配它們並在之間分配不同的模塊邊界。

圍繞BSTR也有很好的C++包裝類, _bstr_t或ATL's CComBSTR。您可以在來電者的網站上使用這些內容。

一旦你有一個BSTR實例,可能在呼叫者的站點正確包裝在一個方便的RAII包裝中,你可以很容易地將它轉換爲std::wstring

或者,如果你想使用Unicode UTF-8編碼和std::string,您可以從BSTR的原生UTF-16編碼設置爲UTF-8轉換,使用WideCharToMultiByte()this MSDN Magazine article可以派上用場)。

獎金閱讀:Eric’s Complete Guide To BSTR Semantics

附:
如果您不想使用BSTR類型,仍然可以使用由COM提供的公共內存分配器:例如,您可以在DLL中使用CoTaskMemAlloc()分配您的(UTF-8)字符串內存,並在調用者的網站上使用CoTaskMemFree()釋放它。

+0

感謝您的回答。我非常瞭解COM,但是我沒有被綁定到Windows上,所以也會有Linux的共享對象版本。所以我最初的目標是通過標準的C/C++技術。 –