Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

string

c#

char

dll

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;
}
like image 489
Adam Avatar asked May 08 '13 18:05

Adam


People also ask

How do you pass an array of strings?

Passing Array To The Method In Java To pass an array as an argument to a method, you just have to pass the name of the array without square brackets. The method prototype should match to accept the argument of the array type. Given below is the method prototype: void method_name (int [] array);

How can we pass array to function in C?

To pass an entire array to a function, only the name of the array is passed as an argument. result = calculateSum(num); However, notice the use of [] in the function definition. This informs the compiler that you are passing a one-dimensional array to the function.

How do you pass a string to a function?

Passing one dimensional string to a function To pass a one dimensional string to a function as an argument we just write the name of the string array variable. In the following example we have a string array variable message and it is passed to the displayString function.

How do you pass a 2D array to a function pointer?

// Your original printarray() void printarray(char **array, int SIZE ){ int i; int j; for( j = 0; j < SIZE; j++ ){ for( i = 0; i < SIZE; i ++){ printf( "%c ", array[j][i] ); } printf( "\n" ); } } // main() char **array; int SIZE; // Initialization of SIZE is not shown, but let's assume SIZE = 50; // Allocate table ...


1 Answers

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.
like image 111
antijon Avatar answered Sep 21 '22 15:09

antijon