Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/298.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 与包含string/char*成员的nim返回结构数组进行互操作_C#_C_Interop_Marshalling_Nim Lang - Fatal编程技术网

C# 与包含string/char*成员的nim返回结构数组进行互操作

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

从c#交互nim dll,我可以调用并执行下面的代码

如果我将添加另一个调用
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