2016-10-05 11 views
2

特定のAPIを公開するDLLを作成しようとしています。そのため、DLL境界で文字列をコピーする安全な方法を実装する必要がありました。 DLL実装は非常に単純です。文字列値を返すすべての関数は、char*size_t&という2つの引数をとります。サイズが十分大きい場合は、memcpyの文字列の内容をDLL内から指定されたポインタに移動し、サイズを実際のものに設定し、成功したリターンコードを返します。そうでない場合は、サイズをエラーコードに戻してください。 DLL側は非常に簡単です。DLL境界での安全な文字列のコピー

これ以上複雑なのは、DLLの関数へのポインタを指定した素敵なテンプレート関数を作成して、std::stringのインスタンスを記入するための正しい操作を行う方法です。私は最初の呼び出しの後に(文字列は、最初は空であるので、それ以来、それは、コンテンツを消去するだろう)ことはできませんので、だから私は、最初のresizeをした

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が必要です。なぜなら、実際には長さが小さかったので、サイズを変更する必要があるからです。これは私がより良い方法で行うことができればどんな提案?

+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メモリアロケータとBSTRによってWindows上で解決されています。

したがって、文字列ポインタとサイズポインタを渡す代わりに、呼び出し側の割り当てバッファが十分に大きいかどうかを確認し、必要なサイズなどを返さない場合は、BSTRを返すだけで簡単です。 DLL。

BSTRは、COMアロケータを使用して割り当てられているため、それらを割り当てて、異なるモジュール境界で解放することができます。

BSTRの周りに素晴らしいC++ラッパークラスもあります。 _bstr_tまたはATL's CComBSTR。発信者のサイトでそれらを使用することができます。

BSTRインスタンスがあり、発信者のサイトで便利なRAIIラッパーに適切にラップされていると、簡単にstd::wstringに変換できます。

または、あなたは、Unicode UTF-8エンコーディングと std::stringを使用したい場合、あなたは(便利になることができます this MSDN Magazine articleWideCharToMultiByte()を使用して、UTF-8から BSTRのネイティブUTF-16エンコーディングから変換することができます。

ボーナス読み:Eric’s Complete Guide To BSTR Semantics

P.S.
BSTRタイプを使用しない場合は、引き続きCOMによって提供される共通のメモリアロケータを使用できます。DLLにCoTaskMemAlloc()を使用して(UTF-8)文字列メモリを割り当て、呼び出し元のサイトでCoTaskMemFree()を使用して解放します。

+0

答えをありがとう。私はCOMをよく知っていますが、私はWindowsに縛られていないので、Linux用の共有オブジェクトバージョンも存在します。だから、私の最初の目標は標準のC/C++テクニックでこれを行うことでした。 –

関連する問題