6

私はグーグルバースとスタックオーバーフローを見回し、これに似た質問をいくつか見ましたが、私が見つけた答えはどれもうまくいきませんでした. 私は新しいメンバーなので、他の人の質問の回答にコメントして説明を求めることは許可されていないため、自分自身に尋ねることに頼らなければなりませんでした.

わかりましたので、C# アプリケーションから C++ dll に文字列配列を渡してから、別の C# アプリケーションでその情報を取得しようとしています。C++ に正しく渡していると思いますが、dll から適切な文字列を取得できません。

私は次のようにC++に渡しています:

[DllImport("KinectPlugins.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern void SetGrammarData(string[] strArr, int size);


    public void SetGrammar(string[] strArr)
    {
        SetGrammarData(strArr, strArr.Length);
    }

私の C++ コードは次のようになります。

#define EXPORT_API __declspec(dllexport)
#pragma data_seg(".SHARED")
    char** grammarData;
    int grammarDataLength = 0;
#pragma data_seg()
#pragma comment(linker, "/section:.SHARED,RWS")

EXPORT_API void SetGrammarData(char** strArr, int size)
{
    grammarData = strArr;
    grammarDataLength = size;
}

EXPORT_API int GetGrammarDataLength()
{
    return grammarDataLength;
}
EXPORT_API char** GetGrammarData()
{
    return grammarData;
}

次に、他の C# アプリケーションで情報を取得するためのコードは次のようになります。

[DllImport("KinectPlugins.dll")]
private static extern IntPtr GetGrammarData();
[DllImport("KinectPlugins.dll")]
private static extern int GetGrammarDataLength();

public string[] GetGrammar()
{
    int size = GetGrammarDataLength();
    List<string> list = new List<string>();
    IntPtr ptr = GetGrammarData();
    IntPtr strPtr;
    for (int i = 0; i < size; i++)
    {
        Console.WriteLine("i = " + i);
        strPtr = Marshal.ReadIntPtr(ptr);
        list.Add(Marshal.PtrToStringAnsi(strPtr));
        ptr += Marshal.SizeOf(typeof(IntPtr));
    }
    return list.ToArray();
}

理論的には、これは私の調査に基づいて機能するはずです。他の何人かの人々がほぼ同じコードを使用しているのを見てきました。実際には、次のように渡します。

SetGrammar(new string[] { "b", "a" });

反対側に戻ってくるのは次のとおりです。

stringArray[0] = 
stringArray[1] = H-▬l☺

何らかの理由で表示できない場合や別の stringArray[1] が H、-、太い線、l、そしてハッピー フェイス記号に等しい場合。これは明らかに私が入れたものではありません。

誰かが私がこれで間違っている可能性があるという考えを持っていますか? 私はかなり長い間この問題に頭を悩ませてきましたが、ここで本当に単純なものが欠けているように感じられるので、本当に助けが必要です。

編集: antijon の提案に従って、SetGrammarData を変更して文字列のコピーを作成しましたが、まだ問題が発生しています。

新しいコード:

(inside the data_seg)
wchar_t* grammarData;
(end data_seg)

EXPORT_API void SetGrammarData(wchar_t* strArr, int size)
{
    delete[] grammarData;
    grammarData = new wchar_t[size];
    std::memcpy(grammarData, strArr, sizeof(wchar_t) * size);
    grammarDataLength = size;
}
EXPORT_API wchar_t* GetGrammarData()
{
    return grammarData;
}

今、私はこの出力で終わります:

stringArray[0] = 8
stringArray[1] = 

C# コードは同じままです。私が行方不明になっていることを変更する必要があるものは他にありますか?

Edit2: wchar_t は文字列ではなく char のようなものであることに気付きました。なぜ文字列のように振る舞うと思ったのかわかりません。設計図に戻り、wchar_t** を最適にコピーする方法を見つける必要があります。C++ の経験はありませんが、自分で渡さずに wchar_t* の長さを取得することはできないと思いますが、調べる必要があります。

Edit3: ようやく正常に動作するようになりました。

これが私が最終的に得たものです:

(inside the data_seg)
std::wstring* grammarData;
(end data_seg)

EXPORT_API void SetGrammarData(wchar_t** strArr, int size)
{
    delete[] grammarData;
    grammarDataLength = size;
    grammarData = new std::wstring[size];
    for(int i = 0; i < size; i++)
    {
        grammarData[i] = std::wstring(strArr[i]);
    }
}

EXPORT_API const wchar_t** GetGrammarData()
{
    const wchar_t** wct = new const wchar_t*[grammarDataLength];
    for(int i = 0;i<grammarDataLength;i++)
    {
        const wchar_t* t = grammarData[i].c_str();
        wct[i] = t;
    }
    return wct;
}

Edit4: 正しく動作していると思っていましたが、間違っていました。私はそれ自体に戻るexeでテストしていましたが、dllを別のexeに渡すと何も起こりませんでした。私は今それが働いています:

(inside the data_seg)
wchar_t grammarData[32][50] = {};
(end data_seg)

EXPORT_API void SetGrammarData(wchar_t** strArr, int size)
{
    grammarDataLength = size;
    for(int i = 0; i < size; i++)
    {
        wcscpy(grammarData[i], strArr[i]);
    }
    grammarDataChanged = 1;
}

EXPORT_API wchar_t** GetGrammarData()
{
    wchar_t** wct = new wchar_t*[grammarDataLength];
    for(int i = 0;i<grammarDataLength;i++)
    {
        wct[i] = grammarData[i];
    }

    grammarDataChanged = 0;
    return wct;
}
4

1 に答える 1

3

ここで考えられる問題のカップル:

  1. デフォルトでは、.NET はwchar_tではなくとしてマーシャリングしますchar。char を使用するには、入力/出力文字列パラメーターをMarshalAsAttributeでマークする必要があります。
  2. 文字列を保持したい場合は、C 関数内で文字列のコピーを作成する必要があります。への関数呼び出しで指定されたポインターは、SetGrammarData持続することが保証されていません。
于 2013-05-08T18:51:04.753 に答える