2017-06-07 3 views
3

1つのdll関数から複数の文字列を返す良い方法は何ですか?現在8つの文字列がありますが、さらに多くの文字列があります。簡単にするために、私は今すべての文字列が等しい長さを持つと考えます。複数の文字列をdllから返す

extern "C" int DLLNAME_ _stdcall GetResult(TestResults* testResults); 

ここ

struct TestResults 
{ 
    int stringLengths; 
    char* string1; 
    char* string2; 
    char* string3; 
    char* string4; 
    ... 
}; 

又は第二のオプション:

struct TestResults 
{ 
    int stringLengths; 
    char string1[64]; 
    char string2[64]; 
    char string3[64]; 
    char string4[64]; 
    ... 
}; 

第三のオプション: のextern "C" INT DLLNAME_ _stdcallのgetResult(stringLengths INT、チャー*の文字列1、チャー*文字列2 、char * string3、...);

dllはシリアル回線を介して通信し、文字列に入力される情報を取得します。メモリを割り当てる必要がある場所は議論の場であり、答えの一部とすることができます。

バックグラウンドでは、2番目の方法を好むVB6アプリケーションチームと、1番目の方法を好むC++/C#チームがあるということです。最後の方法は両方のチームに合っているように見えますが、非常に多くのパラメータで私には少し奇妙に見えます。

多分もっと多くのオプションがあります。 Windowsではよくあることは何ですか? Windows APIの引数や引数を選択する引数はどれですか?

編集:文字列は、名前、姓、メールのような意味を持ちます。現在は8つありますが、将来は住所などのカップルを追加する可能性があります。配列は正しい選択ではありませんが、元のコンテキストからは明らかではありませんでした。

+3

「char *」を使用すると、どのようにメモリの割り当て/割り当て解除が管理されますか?また、言語間型(定義されたメモリ管理を持つ)としての 'BSTR'を参照してください。 –

+0

あなたのコメントに答えるために質問が更新されました。私たちは配分がどこに起こる必要があるかを言う厳しいルールはありません。これはおそらく、良い/共通のアイデアが何であるかわからなくなる理由の一部です。 –

+1

VB 6と相互運用する場合は、絶対に* BSTRを使用する必要があります。これは、VB 6が文字列型として使用するものです。標準のCOMタイプなので、.NETアプリケーションからもうまく動作します。 –

答えて

4

安全アレイを使用しているのが最も良い方法は、おそらくBSTR個の文字列を格納することです。

はVBとC#の両方が非常によくセーフ配列を理解する:C#で、BSTRストリングの安全な配列は自動的にstring[]配列に変換あります。

C++側では、ATL::CComSafeArrayヘルパークラスを使用して、安全な配列プログラミングを簡素化できます。

this MSDN Magazine articleに興味深い資料があります(特に、段落の安全な文字列の作成を参照してください)。上記の記事から


:あなたはこれを使用することができ、C#の側では

extern "C" HRESULT MyDllGetStrings(/* [out] */ SAFEARRAY** ppsa) 
{ 
    try { 
    // Create a SAFEARRAY containing 'count' BSTR strings 
    CComSafeArray<BSTR> sa(count); 

    for (LONG i = 0; i < count; i++) { 
     // Use ATL::CComBSTR to safely wrap BSTR strings in C++ 
     CComBSTR bstr = /* your string, may build from std::wstring or CString */ ; 

     // Move the the BSTR string into the safe array 
     HRESULT hr = sa.SetAt(i, bstr.Detach(), FALSE); 

     if (FAILED(hr)) { 
     // Error... 
     return hr; 
     } 
    } 

    // Return ("move") the safe array to the caller 
    // as an output parameter (SAFEARRAY **ppsa) 
    *ppsa = sa.Detach(); 

    } catch (const CAtlException& e) { 
    // Convert ATL exceptions to HRESULTs 
    return e; 
    } 

    // All right 
    return S_OK; 
} 

:C++側では、あなたがこのように機能をエクスポート、C-インタフェース DLLを実装することができますPInvoke宣言:

[DllImport("MyDll.dll", PreserveSig = false)] 
public static extern void MyDllGetStrings(
    [Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] 
    out string[] result); 
1

あなたの関数をextern "C"と宣言しているので、戻り値の型としてstd::vector<std::string>を使用することはできません。

別の可能性は次のようになります。あなたが好きなだけの文字列を返すように柔軟であることで

extern "C" int DLLNAME_ _stdcall GetResult(TestResults* testResults); 

struct String 
{ 
    int   size; /* size of string */ 
    const char* str; /* actual string */ 
} 
struct TestResults 
{ 
    int  size; /* number of strings    */ 
    String* arr; /* pointer to an array of String */ 
}; 

し、同じ

以前のように。ループスルーも TestResultsは簡単です。

編集#1:としては、コメントで述べている:BSTRを使用しています。だからあなたの構造体は次のようになります。BSTR MyBstr = SysAllocString(L"I am a happy BSTR");

struct TestResults 
{ 
    int size; /* number of strings   */ 
    BSTR* arr; /* pointer to an array of BSTR */ 
}; 

BSTRによってallocatedになります。この割り当てでは、文字列の長さを含むメンバーも設定されます。割り当てられたメモリは、SysFreeString(MyBstr);で解放する必要があります。また、配列BSTR*全体を割り当てる必要があります。

+0

'String'型をMS-Windows型' BSTR'と比較します(BSTRは言語間メモリ管理を定義しています) –

+2

とにかく、STL型をDLLの戻り値として使用しないでください。ランタイムライブラリの異なるバージョン間で交換可能に使用することはできません。 –

+0

とにかく別のfreeResult関数を提供することを強くお勧めします。これにより、どのように多くの異なる配列を割り当て解除する必要があるにせよ、ユーザーにとっては割り振り解除が容易になります。これでBSTRは必要なくなり、単純なC文字列に戻すことができます。構造体をポインタとして返すこともできます(エラー時NULL)...これらの2つの関数は、Sys(Alloc | Free)String pairと同等になります... – Aconcagua

関連する問題