从C#传递一个字符串数组到C++动态链接库,再返回。

6
我已经查看了Google和Stack Overflow,并看到了几个类似的问题,但我找到的答案都没有解决我的问题。由于我是一个新成员,所以不允许我在别人的问题中评论以请求澄清,因此我不得不自己提问。
好的,我正在尝试从一个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、 - 、a粗线、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#代码没有改变。我有遗漏什么需要更改的吗?

编辑2:刚刚意识到wchar_t类似于char,不是字符串,不确定为什么我认为它的行为类似于字符串。回到起点,需要弄清楚如何最好地复制wchar_t**。我对C++不是很熟悉,但我认为不可能在没有自己传递参数的情况下得到wchar_t*的长度,但我将进一步研究。

编辑3:终于让它正常工作了。

这是我的最终结果:

(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;
}

编辑4: 我以为已经正确地做好了,但实际上是错误的。我在测试时使用了一个将exe文件传回本身的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;
}

你尝试过将数组作为“ref”参数传递吗? - Tejas Sharma
请查看以下链接:https://dev59.com/Z3E95IYBdhLWcg3wSsCD 和 http://stackoverflow.com/questions/2345945/attempted-to-read-or-write-protected-memory-this-is-often-an-indication-that-o 以及 http://stackoverflow.com/questions/2344929/pointers-in-c-sharp-to-retrieve-reference-from-dllimport-function。 - user195488
将非托管非 .NET DLL 导入到 .NET 项目并表示 char 和 void:http://stackoverflow.com/questions/2343272/dllimport-unmanaged-non-net-dll-to-net-project-representing-char-and-void - user195488
Tejas:你的意思是在第二个C#应用程序中通过引用传递char到GetGrammarData,而不是返回char来检索信息吗?0A0D:我会查看那些链接。 - Adam
1个回答

3
这里可能存在几个问题:
  1. 默认情况下,.NET将作为而不是进行编组。您需要使用MarshalAsAttribute标记输入/输出字符串参数以使用char。
  2. 如果要保留这些字符串,则需要在C函数内部创建它们的副本。传递给您的SetGrammarData函数调用中的指针不能保证持久存在。

嗯,我甚至没有考虑过第二点。这说明我已经在解决这个问题上工作了太长时间,并且有太多的迭代,以至于忘记了这样的事情。我会尝试修复它,因为你很可能是正确的,这是我的主要问题。今天我可能无法测试它,因为我必须很快离开。也许需要等到明天才能接受这个答案。 - Adam
嗯,我尝试了一下: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] =你知道我还做错了什么吗? - Adam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接