如何在 C# 中编组指向一系列以 null 结尾的字符串的指针?

Posted

技术标签:

【中文标题】如何在 C# 中编组指向一系列以 null 结尾的字符串的指针?【英文标题】:How do I marshal a pointer to a series of null-terminated strings in C#? 【发布时间】:2013-04-17 11:31:25 【问题描述】:

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

返回值 如果函数成功,则返回值是指向一系列以空字符结尾的字符串的指针,主机系统上的每个项目都有一个,以第二个空字符结尾。以下示例显示了缓冲区内容,其中<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); 
    

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

【问题讨论】:

你应该至少给出你使用的 C++ API 的签名,也许还有一些调用这个 API 的 C# 代码。 请添加调用c++ api的c#代码 你仔细检查结果(长度)? 如果你了解 C/C++,你可以在 Marshal 类中找到处理非托管内存所需的所有工具,查看方法:AllocHGlobalFreeHGlobalCopy重载、StringToPtr*PtrToString* 等。 Marshal class + IntPtr 结构允许在 C# 代码中重现任何 C/C++ 内存管理技巧。 嗨,我已经用我目前正在使用的一些代码更新了我的帖子。 【参考方案1】:

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

您不应将char* 直接转换为字符串。

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

leppie 建议您也可以使用Marshal.PtrToStringAnsi 提取第一个字符串,然后按此字符串大小递增指针并提取下一个字符串,依此类推。当 is 提取空字符串(从最后一个 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)

【讨论】:

当它停止时,为什么不用strlen + 1增加指针? 是的,这是另一种可能性,但他必须在正确的时间小心地停下来,也许循环遍历托管数组对于 C# 程序员来说是最简单的。 zakinster...你就是那个人。感谢大家的帮助和快速响应。它正在 100% 工作。我真的很感激。【参考方案2】:

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

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

See here for an example solution。我已将相关代码复制到此答案中,但它是从我提供的链接中复制的:

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;

【讨论】:

以上是关于如何在 C# 中编组指向一系列以 null 结尾的字符串的指针?的主要内容,如果未能解决你的问题,请参考以下文章

如何编组包含未知大小的 int 数组的结构?

c# Marshal 以 null 结尾的字符串数组

使用 C# P/Invoke 方法在 Struct 内编组数组

创建要在 C# 中编组的 C++ Dll 的最佳实践 [关闭]

如何在 C# 中编组 wstring *?

编组 LPBYTE