如何封送一个指向一系列以空结束的字符串的指针

本文关键字:一系列 结束 字符串 指针 何封送 一个 | 更新日期: 2023-09-27 18:09:33

我需要一些帮助。我有一个c++ API(无法访问源代码),我正在努力使用返回char*属性的方法,或者返回包含char*属性的结构。根据API的文档,返回值如下:


返回值如果函数成功,返回值是一个指针,指向一系列以空结束的字符串,每个字符串对应主机系统上的一个项目,以第二个空字符结束。下面的示例显示了缓冲区内容,其中<null>表示终止null字符:

project1<null>project2<null>project3<null><null>

如果函数失败,返回值为NULL

我遇到的问题是c#中返回的指针只包含第一个值…在本例中是Project1。如何获得完整的列表,以便能够在托管端遍历它们?

下面是c#代码:
    [DllImport("vmdsapi.dll", EntryPoint = "DSGetProjectList", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr DSGetProjectList();
调用方法:

   IntPtr ptrProjectList = DSAPI.DSGetProjectList();
   string strProjectList = Marshal.PtrToStringAnsi(ptrProjectList).ToString();

strProjectList只包含第一个项目。
这是API头文件中的信息…

   DllImport char *DSGetProjectList dsproto((void));

下面是我用于测试目的的c++控制台应用程序的一些示例代码…

   char *a;
   a = DSGetProjectList( );
   while( *a ) { 
    printf("a=%s'n", a); 
    a += 1 + strlen(a); 
   } 

每次迭代都正确地显示列表中的每个项目。

如何封送一个指向一系列以空结束的字符串的指针

问题是,当使用Marshal.PtrToStringAnsi将c++字符*转换为c#字符串时,它在第一个空字符处停止。

你不应该直接把char*转换成字符串

您可以使用Marshal.CopyIntPtr表示的char*复制到byte[],然后根据需要提取尽可能多的字符串(参见Matthew Watson对于从托管数组中提取字符串的回答),但是您需要首先获得多字符串大小。

正如leppie建议的那样,您也可以使用Marshal.PtrToStringAnsi提取第一个字符串,然后将指针增加该字符串的大小并提取下一个字符串,以此类推。当它提取一个空字符串(从最后一个NULL字符)时,您停止。

例如:

IntPtr ptrProjectList = DSAPI.DSGetProjectList();
List<string> data;
string buffer;
do {
    buffer = Marshal.PtrToStringAnsi(ptrProjectList);
    ptrProjectList += buffer.size() + 1;
    data.Add(buffer);
}while(buffer.size() > 0)

这种字符串称为Multi-String,在Windows API中很常见。

封送它们是很繁琐的。您需要做的是将其封送为char[]数组,而不是字符串,然后将char[]数组转换为一组字符串。

查看这里的示例解决方案。我已经将相关代码复制到这个答案中,但它是从我给出的链接复制的:

static List<string> MultiStringToList(char[] multistring)
{
    var stringList = new List<string>();
    int i = 0;
    while (i < multistring.Length)
    {
        int j = i;
        if (multistring[j++] == ''0') 
            break;
        while (j < multistring.Length)
        {
            if (multistring[j++] == ''0')
            {
                stringList.Add(new string(multistring, i, j - i - 1));
                i = j;
                break;
            }
        }
    }
    return stringList;
}