C# 与包含string/char*成员的nim返回结构数组进行互操作
从c#交互nim dll,我可以调用并执行下面的代码 如果我将添加另一个调用C# 与包含string/char*成员的nim返回结构数组进行互操作,c#,c,interop,marshalling,nim-lang,C#,C,Interop,Marshalling,Nim Lang,从c#交互nim dll,我可以调用并执行下面的代码 如果我将添加另一个调用GetPacks()的函数(proc),并尝试在每个元素的缓冲区上进行回显,我可以在C#控制台中正确地看到输出 但我不能像现在这样传输数据,我尝试了一切,但我无法完成任务 proc GetPacksPtrNim(parSze: int, PackArrINOUT: var DataPackArr){.stdcall,exportc,dynlib.} = PackArrINOUT.newSeq(parSze) va
GetPacks()
的函数(proc),并尝试在每个元素的缓冲区上进行回显,我可以在C#控制台中正确地看到输出
但我不能像现在这样传输数据,我尝试了一切,但我无法完成任务
proc GetPacksPtrNim(parSze: int, PackArrINOUT: var DataPackArr){.stdcall,exportc,dynlib.} =
PackArrINOUT.newSeq(parSze)
var dummyStr = "abcdefghij"
for i, curDataPack in PackArrINOUT.mpairs:
dummyStr[9] = char(i + int8'0')
curDataPack = DataPack(buffer:dummyStr, intVal: uint32 i)
type
DataPackArr = seq[DataPack]
DataPack = object
buffer: string
intVal: uint32
当我在c/c++中执行相同操作时,我使用的类型要么是IntPtr
要么是char*
很高兴包含返回的缓冲区
成员
EXPORT_API void __cdecl c_returnDataPack(unsigned int size, dataPack** DpArr)
{
unsigned int dumln, Index;dataPack* CurDp = {NULL};
char dummy[STRMAX];
*DpArr = (dataPack*)malloc( size * sizeof( dataPack ));
CurDp = *DpArr;
strncpy(dummy, "abcdefgHij", STRMAX);
dumln = sizeof(dummy);
for ( Index = 0; Index < size; Index++,CurDp++)
{
CurDp->IVal = Index;
dummy[dumln-1] = '0' + Index % (126 - '0');
CurDp->Sval = (char*) calloc (dumln,sizeof(dummy));
strcpy(CurDp->Sval, dummy);
}
}
C#结构
最后像这样调用函数:
public static unsafe List<DataPackg.TestC> PopulateLstPackC(int ArrL)
{
DataPackg.TestC* PackUArrOut;
List<DataPackg.TestC> RtLstPackU = new List<DataPackg.TestC>(ArrL);
c_returnDataPack((uint)ArrL, &PackUArrOut);
DataPackg.TestC* CurrentPack = PackUArrOut;
for (int i = 0; i < ArrL; i++, CurrentPack++)
{
RtLstPackU.Add(new DataPackg.TestC() { StrVal = CurrentPack->StrVal, Id = CurrentPack->Id });
}
//Console.WriteLine("Res={0}", Marshal.PtrToStringAnsi((IntPtr)RtLstPackU[1].StrVal));//new string(RtLstPackU[0].StrVal));
return RtLstPackU;
}
public静态不安全列表populatelstackc(int ArrL)
{
DataPackg.TestC*PackUArrOut;
列表RtLstPackU=新列表(ArrL);
c_返回数据包((uint)ArrL和PackUArrOut);
DataPackg.TestC*CurrentPack=PackUArrOut;
对于(int i=0;iStrVal,Id=CurrentPack->Id});
}
//Console.WriteLine(“Res={0}”,Marshal.PtrToStringAnsi((IntPtr)RtLstPackU[1].StrVal));//新字符串(RtLstPackU[0].StrVal));
返回RtLstPackU;
}
如何从Nim生成与上述类似的c代码?
它不必是相同的代码,但效果相同,在c#中,我可以读取字符串的内容。目前,int是可读的,但字符串不是
编辑:
这就是我试图让事情变得简单的原因
更新:
问题似乎与我在windows操作系统中的nim设置有关。
一旦发现问题所在,我将立即进行更新。尝试将您的结构更改为:
public unsafe static class DataPackg
{
[StructLayout(LayoutKind.Sequential)]
public struct TestC
{
public uint Id;
[MarshalAs(UnmanagedType.LPStr)]
public String StrVal;
}
}
Nim中的字符串
类型与C的常量char*
类型不同。Nim中的字符串表示为指针,指向堆分配的内存块,内存块具有以下布局:
NI length; # the length of the stored string
NI capacity; # how much room do we have for growth
NIM_CHAR data[capacity]; # the actual string, zero-terminated
请注意,这些类型是特定于体系结构的,它们实际上是编译器的一个实现细节,将来可以更改NI
是体系结构默认的整数类型,NIM\u CHAR
通常相当于8位字符,因为NIM倾向于使用UTF8
考虑到这一点,您有几个选择:
1) 您可以向C#教授这种布局,并在正确的位置访问字符串缓冲区(上述注意事项适用)。此方法的示例实现可在此处找到:
2) 您可以为Nim代码中的缓冲区字段使用不同的类型。可能的候选者是ptr char
或固定大小的数组[char]
。第一种方法要求您放弃自动垃圾收集,并为手动内存管理维护一点代码。第二个将放弃一点空间效率,并对这些缓冲区的大小进行严格限制
编辑:
使用cstring
看起来也很诱人,但最终还是很危险的。将常规字符串分配给cstring
时,结果将是一个正常的char*
值,指向上述Nim字符串的数据缓冲区。由于Nim垃圾收集器正确处理指向已分配值的内部指针,因此只要将cstring
值放置在堆栈之类的跟踪位置,这将是安全的。但是当你把它放在一个对象中时,cstring
将不会被跟踪,并且没有任何东西阻止GC释放内存,这可能会在你的C代码中创建一个悬空指针。kodre你搞错了吗,因为我没有发布C代码(我将编辑并添加),返回类型是一个结构数组,不是stringi我也会发布使用代码,谢谢你的帮助。你有什么建议?问题不在于c实现,而在于nim实现,c代码正在工作,c#调用和签名按预期转换内容,但nim代码返回的结果完全相同,但我只能正确地看到int值,而看不到string值。我认为在c#中将nim/c char*转换为string有问题。正因为如此,我为这个外部方法设置了[Marshallas(UnmanagedType.LPStr)],你能为返回类型上的字符串添加一个类似于[Marshallas(UnmanagedType.LPStr)]
的属性吗?返回类型是指向数组的指针,它将应用到数组的内容上?本着同样的精神,在c端尝试了所有可能的方法后数小时,将string
更改为cstring
怎么样?在你发布答案之前的几分钟,我就已经让它工作了,现在它使用cstring
作为nim端的缓冲区,请告诉我如何修复循环中的尾随数字值char,我将完成交易(:因此,在类似结构的类型上循环,为其cstring
成员分配一些随机值(这里trailingdigit只是模拟随机数据,可以是文件名或数据库查询)所以,直到什么时候它是安全的,它在哪里失去了它的安全性,一旦循环完成,控制返回到c#在那里我把它放在一个列表中,nim中的进程已经全部退出…@zach你能举一个小例子来说明如何使用ptr char
类型的缓冲区选项,因为我想测试它吗(同样由于我的代码在返回的集合上以c#循环时有点不稳定,我在打印到控制台时看到一些混乱)因此,我将对一些代码示例进行介绍,以实现候选的第1Le1的协作,其中包括一些最终的工作代码,我将在答案中包括:顺便说一下,如果创建一个使用IJW的魔法来实现所有编组的单个托管C++模块,则可以使所有事情变得简单一点:首先,非常感谢您的努力,我现在我们来看一下要点,谢谢,其次,我很乐意了解“C++模块”选项
public unsafe static class DataPackg
{
[StructLayout(LayoutKind.Sequential)]
public struct TestC
{
public uint Id;
[MarshalAs(UnmanagedType.LPStr)]
public String StrVal;
}
}
NI length; # the length of the stored string
NI capacity; # how much room do we have for growth
NIM_CHAR data[capacity]; # the actual string, zero-terminated