P/在结构体中调用定义长度的C char*数组[duplicate]

时间:2022-09-06 10:53:34

This question already has an answer here:

这个问题已经有了答案:

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:

我一直在用一个结构体进行P/调用,这很好,但我发现:

char* infoString[SIDTUNE_MAX_CREDIT_STRINGS];

char * infoString[SIDTUNE_MAX_CREDIT_STRINGS];

where SIDTUNE_MAX_CREDIT_STRINGS is defined as 10.

其中sidtune_max_credit_string定义为10。

So inlining everything, the struct member is defined as:

因此,将所有的元素都包含在内,结构体成员被定义为:

char* infoString[10]

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.

char*数组包含指向其他C字符串的指针。

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

在这种情况下,只使用3个索引,而保留其余索引。指标如下:

  • infoString[0] = Song title

    infoString[0]=歌曲标题

  • infoString[1] = Artist name

    infoString[1]=艺术家的名字

  • infoString[2] = Copyright/Publisher.

    infoString[2]=版权/出版商。

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.

我如何P/调用它,以一种能够从c#访问这些字符串的方式?实现每个单独返回的c++函数不是一个选项。

1 个解决方案

#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.

假设函数是GetSongInfo(int songID, LPInfostring songinfo),那么可以将结构定义为一个IntPtr数组。但是,您必须注意内存泄漏,因为调用函数可能希望释放为返回的字符串分配的内存。

target.h:

target.h:

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

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

target.c:

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:

P / Invoke签名:

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

#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.

假设函数是GetSongInfo(int songID, LPInfostring songinfo),那么可以将结构定义为一个IntPtr数组。但是,您必须注意内存泄漏,因为调用函数可能希望释放为返回的字符串分配的内存。

target.h:

target.h:

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

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

target.c:

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:

P / Invoke签名:

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