pass an array of strings from C# to a C++ dll and back again

会有一股神秘感。 提交于 2021-02-17 16:57:32

问题


I have looked around the googleverse and stack overflow and have seen several similar questions to this but none of the answers I have found have worked for me. I am a new member so I am not allowed to comment on answers in someone else's question to ask for clarification so I have had to resort to asking my own.

Ok so I am trying to pass a string array from a C# application to a C++ dll and then grab that information in another C# application. I believe I am passing to C++ properly but I can't get proper strings back from the dll.

I am passing to C++ like so:

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


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

My C++ code looks like this:

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

My code for then grabbing the information in my other C# application looks like this:

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

In theory this should work based on my research as I have seen several other people use almost the same code. In practice, what happens is I pass in:

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

and what comes back out the other side is:

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

In case some can't view it for some reason or another stringArray[1] is equal to H, -, a thick line, l and a happy face symbol. This is obviously not what I put in.

Does anyone have an idea where I could be going wrong with this? I have been banging my head against this problem for quite a while and could really use some help as it feels like I am missing something really simple here.

Edit: as per antijon's suggestion I did change my SetGrammarData to make a copy of the strings but I am still running into an issue.

new code:

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

Now I end up with this output:

stringArray[0] = 8
stringArray[1] = 

The C# code has remained the same. Is there something else I need to change that I am missing?

Edit2: Just realized that wchar_t is like a char, not a string, not sure why I thought it behaved like a string. Back to the drawing board, need to figure out how to best copy a wchar_t**. Not that experienced with C++ but I don't think it's possible to get the length of a wchar_t* without passing it in myself but I will have to look into it.

Edit3: Finally got it working properly.

Here is what I ended up with:

(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: Thought I had it working properly, was incorrect. I was testing with an exe passing back to itself but when passing through the dll to another exe nothing would come through. I now have it working:

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

回答1:


Couple of possible problems here:

  1. By default, .NET will marshal as wchar_t, not char. You will need to mark your input/output string parameters with MarshalAsAttribute to use char.
  2. If you want to keep the strings, you will need to make copies of them within the C function. The pointers given to you in the function call to SetGrammarData are not guaranteed to persist.


来源:https://stackoverflow.com/questions/16447506/pass-an-array-of-strings-from-c-sharp-to-a-c-dll-and-back-again

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!