建立在我的 marshalling helloworld question 之上,我遇到了将 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 gist获取完整源代码。
最佳答案
// 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);
}
或者更干净,因为已经支持简单类型的编码(marshal)处理数组:
IntPtr[] ptrs = new IntPtr[numHumans];
Marshal.Copy(humansPtr, ptrs, 0, numHumans);
我通过使用 Debug + Windows + Memory + Memory 找到了这个错误 1. 将 humansPtr 放在地址字段中,切换到 4 字节整数 View 并观察到 C 代码正确执行.然后很快发现 ptrs[] 不包含我在 Memory 窗口中看到的值。
除了作为脑力锻炼之外,不确定您为什么要编写这样的代码。这不是正确的方法,例如,您完全忽略了再次释放内存的需要。这是非常不平凡的。在 C# 中解析 CSV 文件非常简单,并且与在 C 中解析一样快,它是 I/O 绑定(bind)的,而不是执行绑定(bind)的。您将轻松避免这些几乎不可能调试的错误并获得 lots of help来自 .NET Framework。
关于c# - 在 C# 中编码 C 数组 - 简单的 HelloWorld,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23411460/