在C#中封送C数组-简单HelloWorld

本文关键字:数组 简单 HelloWorld | 更新日期: 2023-09-27 18:01:03

根据我的编组helloworld问题,我遇到了将C中分配的数组编组到C#的问题。我花了几个小时研究我可能出错的地方,但我所尝试的一切都以AccessViolationException等错误告终。

下面是处理在C中创建数组的函数。

__declspec(dllexport) int __cdecl import_csv(char *path, struct human ***persons, int *numPersons)
{
    int res;
    FILE *csv;
    char line[1024];
    struct human **humans;
    csv = fopen(path, "r");
    if (csv == NULL) {
        return errno;
    }
    *numPersons = 0; // init to sane value
    /*
     * All I'm trying to do for now is get more than one working.
     * Starting with 2 seems reasonable. My test CSV file only has 2 lines.
     */
    humans = calloc(2, sizeof(struct human *));
    if (humans == NULL)
        return ENOMEM;
    while (fgets(line, 1024, csv)) {
        char *tmp = strdup(line);
        struct human *person;
        humans[*numPersons] = calloc(1, sizeof(*person));
        person = humans[*numPersons]; // easier to work with
        if (person == NULL) {
            return ENOMEM;
        }
        person->contact = calloc(1, sizeof(*(person->contact)));
        if (person->contact == NULL) {
            return ENOMEM;
        }
        res = parse_human(line, person);
        if (res != 0) {
            return res;
        }
        (*numPersons)++;
    }
    (*persons) = humans;
    fclose(csv);
    return 0;
}

C#代码:

IntPtr humansPtr = IntPtr.Zero;
int numHumans = 0;
HelloLibrary.import_csv(args[0], ref humansPtr, ref numHumans);
HelloLibrary.human[] humans = new HelloLibrary.human[numHumans];
IntPtr[] ptrs = new IntPtr[numHumans];
IntPtr aIndex = (IntPtr)Marshal.PtrToStructure(humansPtr, typeof(IntPtr));
// Populate the array of IntPtr
for (int i = 0; i < numHumans; i++)
{
    ptrs[i] = new IntPtr(aIndex.ToInt64() +
            (Marshal.SizeOf(typeof(IntPtr)) * i));
}
// Marshal the array of human structs
for (int i = 0; i < numHumans; i++)
{
    humans[i] = (HelloLibrary.human)Marshal.PtrToStructure(
        ptrs[i],
        typeof(HelloLibrary.human));
}
// Use the marshalled data
foreach (HelloLibrary.human human in humans)
{
    Console.WriteLine("first:'{0}'", human.first);
    Console.WriteLine("last:'{0}'", human.last);
    HelloLibrary.contact_info contact = (HelloLibrary.contact_info)Marshal.
        PtrToStructure(human.contact, typeof(HelloLibrary.contact_info));
    Console.WriteLine("cell:'{0}'", contact.cell);
    Console.WriteLine("home:'{0}'", contact.home);
}

第一个human struct被整理得很好。我在第一个异常之后得到访问违规异常。我觉得我在用结构指针对结构进行编组时遗漏了一些东西。我希望我忽略了一些简单的错误。你看到这个代码有什么问题吗?

请参阅此GitHub要点以获取完整的源代码。

在C#中封送C数组-简单HelloWorld

  // Populate the array of IntPtr

这就是你做错的地方。您正在返回指向指针数组的指针。第一个是正确的,实际上是从数组中读取指针值。但是,你的for((循环出错了,只在第一个指针值上加了4(或8(。而不是从数组中读取它们。修复:

    IntPtr[] ptrs = new IntPtr[numHumans];
    // Populate the array of IntPtr
    for (int i = 0; i < numHumans; i++)
    {
        ptrs[i] = (IntPtr)Marshal.PtrToStructure(humansPtr, typeof(IntPtr));
        humansPtr = new IntPtr(humansPtr.ToInt64() + IntPtr.Size);
    }

或者更干净,因为已经支持封送简单类型的数组:

    IntPtr[] ptrs = new IntPtr[numHumans];
    Marshal.Copy(humansPtr, ptrs, 0, numHumans);

我使用Debug+Windows+Memory+Memory1发现了这个错误。将humansPer放在Address字段中,切换到4字节整数视图,并观察到C代码做得正确。然后很快发现ptrs[]不包含我在Memory窗口中看到的值。

不知道你为什么要写这样的代码,而不是作为一种心理锻炼。这不是正确的方法,例如,你完全忽略了再次释放内存的必要性。这是非常重要的。在C#中解析CSV文件非常简单,而且速度和在C中一样快,它是I/O绑定的,而不是执行绑定的。您将很容易地避免这些几乎不可能调试的错误,并从.NET Framework中获得大量帮助。