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

ivandova 2006-02-08 09:57:51
用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编写的模块呢?

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

多谢了!
...全文
1347 32 打赏 收藏 转发到动态 举报
写回复
用AI写文章
32 条回复
切换为时间正序
请发表友善的回复…
发表回复
速马 2006-02-16
  • 打赏
  • 举报
回复
说不信任,实际上是缘于不了解。我还不知道它的工作原理是怎样的,因为如我前面的reply所说,要完全匹配数据类型,需要部分的实现C/C++的编译器,包括lexer和部分的parser,也就是说你得用程序去分析它的语义。
而据我所知,大部分类似的工具还是基于源代码替换的,包括正则表达式替换,这不是很可靠的做法,生成的代码往往都不能运行。

不过,事实胜于雄辩。你既然在实际使用后还觉得它能解决你的问题,那看来我还真的要好好了解一下这个工具了 :-)
ivandova 2006-02-16
  • 打赏
  • 举报
回复
“说实话我不太信任这个工具” 《-- 为什么?:)

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

现在我用SWIG已经基本解决我提出的问题,虽然它对数组的支持也不是很好,但可以用C/C++来写一些helper函数来弥补。如此一来,转换实际上等于半自动了,不过工作量的确小了很多很多。而且即便以后struct发生变化,那些helper函数也基本不用重写。所以我觉得还是挺好使的:)但是既然你这样说,能不能说说你的理由,也许可以让我在使用时注意某些地方?谢谢!
ivandova 2006-02-16
  • 打赏
  • 举报
回复
你说的不错。不过SWIG正好是这样一个厉害的工具,根据它的文档,它的确是实现了部分的C编译器的功能。也就是说,它是用编译器的原理去“理解”C/C++源代码,而不是简单地字符串替换之类的。

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

所以,如果以后谁碰到和我一样的问题,需要marshal一大堆struct的话,不妨试试SWIG :)
ivandova 2006-02-15
  • 打赏
  • 举报
回复
多谢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。
速马 2006-02-15
  • 打赏
  • 举报
回复
>>> 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,似乎是专门做这个工作的
说实话我不太信任这个工具
速马 2006-02-14
  • 打赏
  • 举报
回复
我这里没有C++/CLI的例子,但是Managed C++的倒是有一个(VC7.1):
http://www.sunmast.com/soft/IdeInfo.zip
速马 2006-02-14
  • 打赏
  • 举报
回复
关于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里面的已经很详细了
ivandova 2006-02-14
  • 打赏
  • 举报
回复
非常感谢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#中数组是作为对象(指针)来处理的。

初学者愚见,请多指教!再次感谢!
HopeInDark 2006-02-13
  • 打赏
  • 举报
回复
mark
lidong6 2006-02-13
  • 打赏
  • 举报
回复
是你自己定义模板来实现转换.CODESMITH 可没这么智能.
ivandova 2006-02-13
  • 打赏
  • 举报
回复
我看了一下,大概了解一点它的基本原理。但是它支持这样的应用吗?就是输入一个C语言的头文件(或者粘贴一个struct定义代码),然后它就生成相应的C#类。也就是说,它必须知道如何解析C语言的struct定义。有可能吗?
速马 2006-02-13
  • 打赏
  • 举报
回复
>>> 把C的那些结构定义转到C++/CLI中编译
>>> 然后反编译成C# 就可以了吧

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

还有各种各样的宏定义 — 你打算自己去解析这些东西吗?
tiaoci 2006-02-13
  • 打赏
  • 举报
回复
把C的那些结构定义转到C++/CLI中编译

然后反编译成C# 就可以了吧
速马 2006-02-13
  • 打赏
  • 举报
回复
我还是建议你用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));
}
}
}
ivandova 2006-02-13
  • 打赏
  • 举报
回复
死命UP
ivandova 2006-02-13
  • 打赏
  • 举报
回复
难道就没有人用C#写客户端与UNIX服务器通讯吗?

已经在这个问题上浪费不少时间了,真是想不到啊……是不是我脑子哪根筋坏了?
ivandova 2006-02-13
  • 打赏
  • 举报
回复
我知道要自己编写模板,其实就相当于写一个小程序。其实我用任何语言来写这个转换程序都可以的,关键就在于CODESMITH能不能简化这个过程。比如说,如果它可以将C语言的struct定义分析出一堆的语法对象(token?我也不懂),然后我就可以在template中直接操作这些对象,而不用自己手工去写一大堆的字符串解析程序。直觉上感觉这个似乎行不通,CODESMITH不可能这么厉害吧……

难道只能一个一个struct手工写自定义marshaler?我的命怎么这么苦呢?……
lidong6 2006-02-10
  • 打赏
  • 举报
回复
CODESMITH有破解,
他的功能非常强.我的项目中就使用它.


他要求你自己写模块.他的功能强不强就看你的模块写的强不强了.他自己不会给你转换C++到C#
ivandova 2006-02-10
  • 打赏
  • 举报
回复
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如何做这两者之间的“中介”呢?
加载更多回复(12)

110,571

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧