0

I've looked for a while, but found no article that provides an answer to this so hopefully it isn't a duplicate.

I've been doing some P/Invoking with a struct, which is nice and everything, but then I saw this:

char* infoString[SIDTUNE_MAX_CREDIT_STRINGS];

where SIDTUNE_MAX_CREDIT_STRINGS is defined as 10.

So inlining everything, the struct member is defined as:

char* infoString[10]

Now this is where it gets slightly different from the other issues I've looked through to try and solve this.

The char* array contains pointers to other C strings.

In this specific case, only 3 of the indexes are used while the rest are reserved. The indexes are as follows:

  • infoString[0] = Song title

  • infoString[1] = Artist name

  • infoString[2] = Copyright/Publisher.

How would I P/Invoke this in a way I can access each of those strings from C#? Making C++ functions that return each one individually is not an option.

3
  • See stackoverflow.com/questions/1985067/… Commented Jun 6, 2013 at 15:05
  • That question only provides an answer on how to handle an actual string in C (ie, a char array), not on how to P/Invoke an array of pointers that point to C strings. Commented Jun 6, 2013 at 15:26
  • oops... didn't notice that. See answer below. Commented Jun 6, 2013 at 17:46

1 Answer 1

0

Assuming the function something like GetSongInfo(int songID, LPInfostring songinfo), you can define the struct as having an array of IntPtr. However, you will have to watch out for memory leaks as the calling function may expect you to free the memory allocated for the strings returned.

target.h:

typedef struct SongInfo
{
    char* infoString[10];
} *LPSongInfo;

extern "C" __declspec(dllexport) int GetSongInfo(int songID, LPSongInfo info);

target.c:

extern "C" __declspec(dllexport) int GetSongInfo(int songID, LPSongInfo demo)
{
    demo->infoString[0] = "Hello world";
    demo->infoString[1] = "Hello 1";
    demo->infoString[2] = "Hello 2";

    return TRUE;
}

P/Invoke Signature:

[DllImport("PInvokeDll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int GetSongInfo(int songID, out SongInfo ts);

[StructLayout(LayoutKind.Sequential)]
struct SongInfo
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
    public IntPtr[] infoString;
};

Example Use:

SongInfo siStruct;
var num2 = GetSongInfo(101, out siStruct);

// copy the results to managed memory
var results = new string[10];
for (int i = 0; i < 10; i++)
{
    if (siStruct.infoString[i] != IntPtr.Zero)
    {
        // if these were Unicode strings, this would change to PtrToSTringUni
        results[i] = Marshal.PtrToStringAnsi(siStruct.infoString[i]);
    }
}

// results now holds the .Net strings
// if there is an expectation of the caller to free the struct 
// strings, that should happen now

As an alternative for functions which do not allocate memory, you can use a struct like the following to marshal the strings automatically. However, it will free the unmanaged memory unconditionally, which may or may not be desirable.

[StructLayout(LayoutKind.Sequential)]
struct SongInfo2
{
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.LPStr, SizeConst = 10)]
    public string[] infoString;
};
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.