C# 如何使用标准C中定义的struct结构?

时间:2022-08-30 19:56:00
用C#做客户端,服务器端是UNIX用ANSI C,两端通过socket通讯。

服务器端定义了一系列的struct结构(在一个.h文件中),将这些结构的内容按字节流发给客户端。由于客户端用C#,无法直接include服务器端定义的.h文件,而且托管代码的的内存结构和非托管的不同,所以不能直接使用收到的字节流。

解决的方法:
1。序列化。为每个struct都写一个相应的C#类,并实现序列化接口。
2。自定义Marshal。为每个struct都写一个相应的自定义Marshal类。

问题:
struct类型太多了!大约有上百个。以上两种方法都需要为每个struct写C#代码,不太现实。

有没有可能用C++/CLI开发socket通讯这一块,这样就能直接使用.h文件了。但是,其他部分还是用C#实现的话,又如何使用C++/CLI编写的模块呢?

或者有没有其他的解决方法?

多谢了!

32 个解决方案

#1


其实转换挺容易的。

直接用.h,好像不可以吧。

支持一下。

#2


谢谢支持。虽然转换不难,但是关键是量太大。而且一旦服务器端.h有变化,C#也要手工作出相应修改,不好维护。

#3


自己ding

#4


1.用C++开发核心的通讯COM类,使用C#调用.
2.用代码生成工具生成从C++Struct到C#的转换.(这个我认为比让一个还容易)

#5


可以问您一下
1。序列化。为每个struct都写一个相应的C#类,并实现序列化接口。

这样的怎么实现呀。

#6


用C++开发核心的通讯COM类,使用C#调用.

agree

#7


我也碰到这样的问题,C++与C#用socket进行通讯,C++使用结构和ntohl,htonl函数发送字节流,但是我用C#怎写C++能正确的接收

#8


多谢各位老大支持。

不过关于“用C++开发核心的通讯COM类,使用C#调用”我有疑问:即便做好了COM类,在C#程序中使用不一样要Marshal吗?其实最终的问题还是:如何将C语言struct结构描述的内存结构方便地转换C#语言支持的class或者struct。

TO:lidong6(立冬)
我也想过用工具来转换,不过没有找到合适的,你有什么推荐吗?

TO:zuozhiqiang_legend(一个人在家)
请参考:http://book.chinaz.com/others/jishidaquan/17/40459.html
如果struct不多的话,用上文提出的方法就可以解决你的问题了。

#9


工具可以使用codesmith.很好.

#10


学习

#11


codesmith还没用过,现在是不是已经不是免费的了?

另外它能支持这么复杂的代码生成吗?我是想有一个工具,输入一个C语言的头文件,直接就给我生成所有的C#类源代码,有这种东西吗?

除了使用工具,有没有别的办法呢?照理说这种情况很常见的啊(.NET程序和UNIX程序通过socket通讯),难道就没有一个“标准”一点的解决方法吗?

#12


个人认为写COM是非常好的解决方法。
不知到哪位高手有更好的方法

#13


TO: yuetoby(TaRot) 

我对COM不很了解,看过书,明白咋回事,不过没有实际用过。能不能讲得稍微具体一点呢?另外,上面也说过,即便用COM来实现通讯部分,当需要把数据“引出”到别的C#写的程序的时候,不也要做额外的Marshal工作吗?毕竟COM中使用的数据类型和C#中的还是没法一一对应的吧?

比如说,我有一个struct是关于日志的:
struct LogMsg
{
  unsigned char LogType;
  char Msg[1024];
}

在C#中可能描述成:
class LogMsg
{
  public unsigned byte LogType;
  public string Msg;
}

用COM如何做这两者之间的“中介”呢?

#14


CODESMITH有破解,
他的功能非常强.我的项目中就使用它.


他要求你自己写模块.他的功能强不强就看你的模块写的强不强了.他自己不会给你转换C++到C#

#15


我看了一下,大概了解一点它的基本原理。但是它支持这样的应用吗?就是输入一个C语言的头文件(或者粘贴一个struct定义代码),然后它就生成相应的C#类。也就是说,它必须知道如何解析C语言的struct定义。有可能吗?

#16


是你自己定义模板来实现转换.CODESMITH 可没这么智能.

#17


mark

#18


我知道要自己编写模板,其实就相当于写一个小程序。其实我用任何语言来写这个转换程序都可以的,关键就在于CODESMITH能不能简化这个过程。比如说,如果它可以将C语言的struct定义分析出一堆的语法对象(token?我也不懂),然后我就可以在template中直接操作这些对象,而不用自己手工去写一大堆的字符串解析程序。直觉上感觉这个似乎行不通,CODESMITH不可能这么厉害吧……

难道只能一个一个struct手工写自定义marshaler?我的命怎么这么苦呢?……

#19


难道就没有人用C#写客户端与UNIX服务器通讯吗?

已经在这个问题上浪费不少时间了,真是想不到啊……是不是我脑子哪根筋坏了?

#20


死命UP

#21


我还是建议你用C++/CLI做个托管wrapper,这样C#可以直接调用
这需要你懂C++/CLI新的行为,但既然你的struct如此之多,这是方便的做法

第二个方案是,在C#为每个C的struct做一个对应的struct(class也行)
关于影射,如果你以前没做过的话,可能得先花点时间学一下
(实际上用熟了还是很方便的)

然后用这些方法进行对象和字节的转换:
unsafe class BinarySerializer
{
    public static byte[] Struct2Bytes<T>(T obj)
    {
        int size = Marshal.SizeOf(obj);
        byte[] bytes = new byte[size];
        fixed (byte* pb = &bytes[0])
        {
            Marshal.StructureToPtr(obj, (IntPtr)pb, true);
        }
        return bytes;
    }

    public static byte[] Struct2Bytes<T>(T[] array)
    {
        int size = Marshal.SizeOf(typeof(T));
        byte[] bytes = new byte[size * array.Length];
        for (int i = 0; i < array.Length; i++)
        {
            Array.Copy(Struct2Bytes(array[i]), 0, bytes, i * size, size);
        }
        return bytes;
    }

    public static T Bytes2Struct<T>(byte[] bytes)
    {
        fixed (byte* pb = &bytes[0])
        {
            return (T)Marshal.PtrToStructure((IntPtr)pb, typeof(T));
        }
    }
}

#22


把C的那些结构定义转到C++/CLI中编译

然后反编译成C# 就可以了吧

#23


至于用工具把C的struct转换为C#的做法,不知道你们试过没有
首先你得建立C代码的抽象语法树(AST) — 就这个你得花多少时间来弄?CodeSmith之类根本不够用
然后你要用代码分析C里面struct的内存分布情况 — 这又是很复杂的,C不像C#那么简单,一个byte可能只占一个byte的空间,也可能占四个byte空间

还有各种各样的宏定义 — 你打算自己去解析这些东西吗?

#24


>>> 把C的那些结构定义转到C++/CLI中编译
>>> 然后反编译成C# 就可以了吧

impossible

#25


非常感谢Sunmast的建议!能不能将“用C++/CLI做个托管wrapper,这样C#可以直接调用”讲详细点?我之前是用VC+MFC的,对C++还是很熟的。C++/CLI没用过,看过一些入门文章,感觉也不是那么难,毕竟相当于C++的超集。只是对“C#直接调用C++/CLI”做的wrapper不知道具体如何弄。能不能给个小小的例子?

第二个方案中的“影射”是什么意思呢?有没有相关的资料?

对于上面的BinarySerializer类,我有一些疑问:
如果我的struct(或者class)定义中有引用别的struct或class,那Marshal.SizeOf()返回的大小恐怕不能真正反映该对象在内存中的大小吧?比如:

struct Inner
{
   int a, b, c;
}

struct Outer
{
   int a;
   Inner innerObj;
}

这样如果取Outer类对象的大小,应该是返回8 Byte不是16 Byte吧?这样是不是意味着无法做Struct到byte的转换了?struct中包含数组也有同样的问题,因为C#中数组是作为对象(指针)来处理的。

初学者愚见,请多指教!再次感谢!

#26


关于Marshal.SizeOf:
这个方法只要不弹出异常,返回的数字就是可信任正确的,如果和用C/C++里面的sizeof大小不同,那往往是字段不匹配原因,还有内存布局不一致等等
只是这个方法在.NET 1.x有点bug:
http://blog.joycode.com/sunmast/archive/2005/12/13/dotnet20_pinvoke_enhance.aspx

但你这里的取Outer类对象的大小是可以的,因为两者都是struct,Outer里面的innerObj字段并不是指针/引用,所以会返回4 + 4 * 3 = 16

关于数组,你可以通过[MarshalAs(UnmanagedType.ByValArray,SizeConst=...)]来解决
定长的字符串则是UnmanagedType.ByValTStr,SizeConst=...
微软在这些方面都已经为你考虑了,只是你得多学一些p/invoke的基本概念

我说的影射,意思就是怎样用.NET类型去匹配native类型,比如uint和DWORD是可以匹配的

>>> 只是对“C#直接调用C++/CLI”做的wrapper不知道具体如何弄
这个实际上很简单,C++/CLI可以在托管的class里面直接调用非托管的东西,so..
C++/CLI的文档在SDK里面的已经很详细了

#27


我这里没有C++/CLI的例子,但是Managed C++的倒是有一个(VC7.1):
http://www.sunmast.com/soft/IdeInfo.zip

#28


多谢sunmast!

关于Marshal.SizeOf,我实验了一下,的确是16字节。

关于影射,之前我已经接触过一些。通过直接映射(必要时使用MarshalAs属性)的确可以解决简单的struct,但是对于一些比较复杂的情况似乎需要做很多额外的工作,比如:
- struct中包含多维数组;如char TGA[12][20];
- struct中有嵌套struct,甚至嵌套struct数组,如Something some[100];

其实我最大的问题是在工作量和可维护性上面,因为struct有很多。如果只有一个两个,手工写几个wrapper完全可以解决问题。关键就在于,如果要手工写100个wrapper,那就很悲惨了。

不过,昨天偶然发现一个工具SWIG,似乎是专门做这个工作的,不知道你听说过没有(www.swig.org)。正在研究它。目前为止,感觉这个古旧的东东是最接近我需要的。它生成的代码也是使用P/Invoke来调用native DLL。

#29


>>> struct中包含多维数组;如char TGA[12][20];
这个没有直接的支持,但这种数组可以转换为char TGA[12 * 20];的嘛,调用时用个Helper方法Fill之

>>> struct中有嵌套struct
我的上个reply已经说了

>>> 甚至嵌套struct数组,如Something some[100];
.NET 2.0可以marshal,但.NET 1.1不行

>>> 昨天偶然发现一个工具SWIG,似乎是专门做这个工作的
说实话我不太信任这个工具

#30


“说实话我不太信任这个工具” 《-- 为什么?:)

我看了它生成的代码,我觉得挺巧妙的,虽然可能在一些方面效率上会有问题。

现在我用SWIG已经基本解决我提出的问题,虽然它对数组的支持也不是很好,但可以用C/C++来写一些helper函数来弥补。如此一来,转换实际上等于半自动了,不过工作量的确小了很多很多。而且即便以后struct发生变化,那些helper函数也基本不用重写。所以我觉得还是挺好使的:)但是既然你这样说,能不能说说你的理由,也许可以让我在使用时注意某些地方?谢谢!

#31


说不信任,实际上是缘于不了解。我还不知道它的工作原理是怎样的,因为如我前面的reply所说,要完全匹配数据类型,需要部分的实现C/C++的编译器,包括lexer和部分的parser,也就是说你得用程序去分析它的语义。
而据我所知,大部分类似的工具还是基于源代码替换的,包括正则表达式替换,这不是很可靠的做法,生成的代码往往都不能运行。

不过,事实胜于雄辩。你既然在实际使用后还觉得它能解决你的问题,那看来我还真的要好好了解一下这个工具了 :-)

#32


你说的不错。不过SWIG正好是这样一个厉害的工具,根据它的文档,它的确是实现了部分的C编译器的功能。也就是说,它是用编译器的原理去“理解”C/C++源代码,而不是简单地字符串替换之类的。

今天的工作又有一些进展,基本上用SWIG这条路已经完全走通了。通过socket传来的字节流可以非常方便地用Marshal.Copy拷贝到非托管内存,然后用SWIG生成的C#封装类直接访问其中的每个成员变量。反过来也一样。

所以,如果以后谁碰到和我一样的问题,需要marshal一大堆struct的话,不妨试试SWIG :)

#1


其实转换挺容易的。

直接用.h,好像不可以吧。

支持一下。

#2


谢谢支持。虽然转换不难,但是关键是量太大。而且一旦服务器端.h有变化,C#也要手工作出相应修改,不好维护。

#3


自己ding

#4


1.用C++开发核心的通讯COM类,使用C#调用.
2.用代码生成工具生成从C++Struct到C#的转换.(这个我认为比让一个还容易)

#5


可以问您一下
1。序列化。为每个struct都写一个相应的C#类,并实现序列化接口。

这样的怎么实现呀。

#6


用C++开发核心的通讯COM类,使用C#调用.

agree

#7


我也碰到这样的问题,C++与C#用socket进行通讯,C++使用结构和ntohl,htonl函数发送字节流,但是我用C#怎写C++能正确的接收

#8


多谢各位老大支持。

不过关于“用C++开发核心的通讯COM类,使用C#调用”我有疑问:即便做好了COM类,在C#程序中使用不一样要Marshal吗?其实最终的问题还是:如何将C语言struct结构描述的内存结构方便地转换C#语言支持的class或者struct。

TO:lidong6(立冬)
我也想过用工具来转换,不过没有找到合适的,你有什么推荐吗?

TO:zuozhiqiang_legend(一个人在家)
请参考:http://book.chinaz.com/others/jishidaquan/17/40459.html
如果struct不多的话,用上文提出的方法就可以解决你的问题了。

#9


工具可以使用codesmith.很好.

#10


学习

#11


codesmith还没用过,现在是不是已经不是免费的了?

另外它能支持这么复杂的代码生成吗?我是想有一个工具,输入一个C语言的头文件,直接就给我生成所有的C#类源代码,有这种东西吗?

除了使用工具,有没有别的办法呢?照理说这种情况很常见的啊(.NET程序和UNIX程序通过socket通讯),难道就没有一个“标准”一点的解决方法吗?

#12


个人认为写COM是非常好的解决方法。
不知到哪位高手有更好的方法

#13


TO: yuetoby(TaRot) 

我对COM不很了解,看过书,明白咋回事,不过没有实际用过。能不能讲得稍微具体一点呢?另外,上面也说过,即便用COM来实现通讯部分,当需要把数据“引出”到别的C#写的程序的时候,不也要做额外的Marshal工作吗?毕竟COM中使用的数据类型和C#中的还是没法一一对应的吧?

比如说,我有一个struct是关于日志的:
struct LogMsg
{
  unsigned char LogType;
  char Msg[1024];
}

在C#中可能描述成:
class LogMsg
{
  public unsigned byte LogType;
  public string Msg;
}

用COM如何做这两者之间的“中介”呢?

#14


CODESMITH有破解,
他的功能非常强.我的项目中就使用它.


他要求你自己写模块.他的功能强不强就看你的模块写的强不强了.他自己不会给你转换C++到C#

#15


我看了一下,大概了解一点它的基本原理。但是它支持这样的应用吗?就是输入一个C语言的头文件(或者粘贴一个struct定义代码),然后它就生成相应的C#类。也就是说,它必须知道如何解析C语言的struct定义。有可能吗?

#16


是你自己定义模板来实现转换.CODESMITH 可没这么智能.

#17


mark

#18


我知道要自己编写模板,其实就相当于写一个小程序。其实我用任何语言来写这个转换程序都可以的,关键就在于CODESMITH能不能简化这个过程。比如说,如果它可以将C语言的struct定义分析出一堆的语法对象(token?我也不懂),然后我就可以在template中直接操作这些对象,而不用自己手工去写一大堆的字符串解析程序。直觉上感觉这个似乎行不通,CODESMITH不可能这么厉害吧……

难道只能一个一个struct手工写自定义marshaler?我的命怎么这么苦呢?……

#19


难道就没有人用C#写客户端与UNIX服务器通讯吗?

已经在这个问题上浪费不少时间了,真是想不到啊……是不是我脑子哪根筋坏了?

#20


死命UP

#21


我还是建议你用C++/CLI做个托管wrapper,这样C#可以直接调用
这需要你懂C++/CLI新的行为,但既然你的struct如此之多,这是方便的做法

第二个方案是,在C#为每个C的struct做一个对应的struct(class也行)
关于影射,如果你以前没做过的话,可能得先花点时间学一下
(实际上用熟了还是很方便的)

然后用这些方法进行对象和字节的转换:
unsafe class BinarySerializer
{
    public static byte[] Struct2Bytes<T>(T obj)
    {
        int size = Marshal.SizeOf(obj);
        byte[] bytes = new byte[size];
        fixed (byte* pb = &bytes[0])
        {
            Marshal.StructureToPtr(obj, (IntPtr)pb, true);
        }
        return bytes;
    }

    public static byte[] Struct2Bytes<T>(T[] array)
    {
        int size = Marshal.SizeOf(typeof(T));
        byte[] bytes = new byte[size * array.Length];
        for (int i = 0; i < array.Length; i++)
        {
            Array.Copy(Struct2Bytes(array[i]), 0, bytes, i * size, size);
        }
        return bytes;
    }

    public static T Bytes2Struct<T>(byte[] bytes)
    {
        fixed (byte* pb = &bytes[0])
        {
            return (T)Marshal.PtrToStructure((IntPtr)pb, typeof(T));
        }
    }
}

#22


把C的那些结构定义转到C++/CLI中编译

然后反编译成C# 就可以了吧

#23


至于用工具把C的struct转换为C#的做法,不知道你们试过没有
首先你得建立C代码的抽象语法树(AST) — 就这个你得花多少时间来弄?CodeSmith之类根本不够用
然后你要用代码分析C里面struct的内存分布情况 — 这又是很复杂的,C不像C#那么简单,一个byte可能只占一个byte的空间,也可能占四个byte空间

还有各种各样的宏定义 — 你打算自己去解析这些东西吗?

#24


>>> 把C的那些结构定义转到C++/CLI中编译
>>> 然后反编译成C# 就可以了吧

impossible

#25


非常感谢Sunmast的建议!能不能将“用C++/CLI做个托管wrapper,这样C#可以直接调用”讲详细点?我之前是用VC+MFC的,对C++还是很熟的。C++/CLI没用过,看过一些入门文章,感觉也不是那么难,毕竟相当于C++的超集。只是对“C#直接调用C++/CLI”做的wrapper不知道具体如何弄。能不能给个小小的例子?

第二个方案中的“影射”是什么意思呢?有没有相关的资料?

对于上面的BinarySerializer类,我有一些疑问:
如果我的struct(或者class)定义中有引用别的struct或class,那Marshal.SizeOf()返回的大小恐怕不能真正反映该对象在内存中的大小吧?比如:

struct Inner
{
   int a, b, c;
}

struct Outer
{
   int a;
   Inner innerObj;
}

这样如果取Outer类对象的大小,应该是返回8 Byte不是16 Byte吧?这样是不是意味着无法做Struct到byte的转换了?struct中包含数组也有同样的问题,因为C#中数组是作为对象(指针)来处理的。

初学者愚见,请多指教!再次感谢!

#26


关于Marshal.SizeOf:
这个方法只要不弹出异常,返回的数字就是可信任正确的,如果和用C/C++里面的sizeof大小不同,那往往是字段不匹配原因,还有内存布局不一致等等
只是这个方法在.NET 1.x有点bug:
http://blog.joycode.com/sunmast/archive/2005/12/13/dotnet20_pinvoke_enhance.aspx

但你这里的取Outer类对象的大小是可以的,因为两者都是struct,Outer里面的innerObj字段并不是指针/引用,所以会返回4 + 4 * 3 = 16

关于数组,你可以通过[MarshalAs(UnmanagedType.ByValArray,SizeConst=...)]来解决
定长的字符串则是UnmanagedType.ByValTStr,SizeConst=...
微软在这些方面都已经为你考虑了,只是你得多学一些p/invoke的基本概念

我说的影射,意思就是怎样用.NET类型去匹配native类型,比如uint和DWORD是可以匹配的

>>> 只是对“C#直接调用C++/CLI”做的wrapper不知道具体如何弄
这个实际上很简单,C++/CLI可以在托管的class里面直接调用非托管的东西,so..
C++/CLI的文档在SDK里面的已经很详细了

#27


我这里没有C++/CLI的例子,但是Managed C++的倒是有一个(VC7.1):
http://www.sunmast.com/soft/IdeInfo.zip

#28


多谢sunmast!

关于Marshal.SizeOf,我实验了一下,的确是16字节。

关于影射,之前我已经接触过一些。通过直接映射(必要时使用MarshalAs属性)的确可以解决简单的struct,但是对于一些比较复杂的情况似乎需要做很多额外的工作,比如:
- struct中包含多维数组;如char TGA[12][20];
- struct中有嵌套struct,甚至嵌套struct数组,如Something some[100];

其实我最大的问题是在工作量和可维护性上面,因为struct有很多。如果只有一个两个,手工写几个wrapper完全可以解决问题。关键就在于,如果要手工写100个wrapper,那就很悲惨了。

不过,昨天偶然发现一个工具SWIG,似乎是专门做这个工作的,不知道你听说过没有(www.swig.org)。正在研究它。目前为止,感觉这个古旧的东东是最接近我需要的。它生成的代码也是使用P/Invoke来调用native DLL。

#29


>>> struct中包含多维数组;如char TGA[12][20];
这个没有直接的支持,但这种数组可以转换为char TGA[12 * 20];的嘛,调用时用个Helper方法Fill之

>>> struct中有嵌套struct
我的上个reply已经说了

>>> 甚至嵌套struct数组,如Something some[100];
.NET 2.0可以marshal,但.NET 1.1不行

>>> 昨天偶然发现一个工具SWIG,似乎是专门做这个工作的
说实话我不太信任这个工具

#30


“说实话我不太信任这个工具” 《-- 为什么?:)

我看了它生成的代码,我觉得挺巧妙的,虽然可能在一些方面效率上会有问题。

现在我用SWIG已经基本解决我提出的问题,虽然它对数组的支持也不是很好,但可以用C/C++来写一些helper函数来弥补。如此一来,转换实际上等于半自动了,不过工作量的确小了很多很多。而且即便以后struct发生变化,那些helper函数也基本不用重写。所以我觉得还是挺好使的:)但是既然你这样说,能不能说说你的理由,也许可以让我在使用时注意某些地方?谢谢!

#31


说不信任,实际上是缘于不了解。我还不知道它的工作原理是怎样的,因为如我前面的reply所说,要完全匹配数据类型,需要部分的实现C/C++的编译器,包括lexer和部分的parser,也就是说你得用程序去分析它的语义。
而据我所知,大部分类似的工具还是基于源代码替换的,包括正则表达式替换,这不是很可靠的做法,生成的代码往往都不能运行。

不过,事实胜于雄辩。你既然在实际使用后还觉得它能解决你的问题,那看来我还真的要好好了解一下这个工具了 :-)

#32


你说的不错。不过SWIG正好是这样一个厉害的工具,根据它的文档,它的确是实现了部分的C编译器的功能。也就是说,它是用编译器的原理去“理解”C/C++源代码,而不是简单地字符串替换之类的。

今天的工作又有一些进展,基本上用SWIG这条路已经完全走通了。通过socket传来的字节流可以非常方便地用Marshal.Copy拷贝到非托管内存,然后用SWIG生成的C#封装类直接访问其中的每个成员变量。反过来也一样。

所以,如果以后谁碰到和我一样的问题,需要marshal一大堆struct的话,不妨试试SWIG :)