服务器端定义了一系列的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,好像不可以吧。
支持一下。
直接用.h,好像不可以吧。
支持一下。
#2
谢谢支持。虽然转换不难,但是关键是量太大。而且一旦服务器端.h有变化,C#也要手工作出相应修改,不好维护。
#3
自己ding
#4
1.用C++开发核心的通讯COM类,使用C#调用.
2.用代码生成工具生成从C++Struct到C#的转换.(这个我认为比让一个还容易)
2.用代码生成工具生成从C++Struct到C#的转换.(这个我认为比让一个还容易)
#5
可以问您一下
1。序列化。为每个struct都写一个相应的C#类,并实现序列化接口。
这样的怎么实现呀。
1。序列化。为每个struct都写一个相应的C#类,并实现序列化接口。
这样的怎么实现呀。
#6
用C++开发核心的通讯COM类,使用C#调用.
agree
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不多的话,用上文提出的方法就可以解决你的问题了。
不过关于“用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通讯),难道就没有一个“标准”一点的解决方法吗?
另外它能支持这么复杂的代码生成吗?我是想有一个工具,输入一个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如何做这两者之间的“中介”呢?
我对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#
他的功能非常强.我的项目中就使用它.
他要求你自己写模块.他的功能强不强就看你的模块写的强不强了.他自己不会给你转换C++到C#
#15
我看了一下,大概了解一点它的基本原理。但是它支持这样的应用吗?就是输入一个C语言的头文件(或者粘贴一个struct定义代码),然后它就生成相应的C#类。也就是说,它必须知道如何解析C语言的struct定义。有可能吗?
#16
是你自己定义模板来实现转换.CODESMITH 可没这么智能.
#17
mark
#18
我知道要自己编写模板,其实就相当于写一个小程序。其实我用任何语言来写这个转换程序都可以的,关键就在于CODESMITH能不能简化这个过程。比如说,如果它可以将C语言的struct定义分析出一堆的语法对象(token?我也不懂),然后我就可以在template中直接操作这些对象,而不用自己手工去写一大堆的字符串解析程序。直觉上感觉这个似乎行不通,CODESMITH不可能这么厉害吧……
难道只能一个一个struct手工写自定义marshaler?我的命怎么这么苦呢?……
难道只能一个一个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));
}
}
}
这需要你懂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# 就可以了吧
然后反编译成C# 就可以了吧
#23
至于用工具把C的struct转换为C#的做法,不知道你们试过没有
首先你得建立C代码的抽象语法树(AST) — 就这个你得花多少时间来弄?CodeSmith之类根本不够用
然后你要用代码分析C里面struct的内存分布情况 — 这又是很复杂的,C不像C#那么简单,一个byte可能只占一个byte的空间,也可能占四个byte空间
还有各种各样的宏定义 — 你打算自己去解析这些东西吗?
首先你得建立C代码的抽象语法树(AST) — 就这个你得花多少时间来弄?CodeSmith之类根本不够用
然后你要用代码分析C里面struct的内存分布情况 — 这又是很复杂的,C不像C#那么简单,一个byte可能只占一个byte的空间,也可能占四个byte空间
还有各种各样的宏定义 — 你打算自己去解析这些东西吗?
#24
>>> 把C的那些结构定义转到C++/CLI中编译
>>> 然后反编译成C# 就可以了吧
impossible
>>> 然后反编译成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#中数组是作为对象(指针)来处理的。
初学者愚见,请多指教!再次感谢!
第二个方案中的“影射”是什么意思呢?有没有相关的资料?
对于上面的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里面的已经很详细了
这个方法只要不弹出异常,返回的数字就是可信任正确的,如果和用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
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。
关于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,似乎是专门做这个工作的
说实话我不太信任这个工具
这个没有直接的支持,但这种数组可以转换为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函数也基本不用重写。所以我觉得还是挺好使的:)但是既然你这样说,能不能说说你的理由,也许可以让我在使用时注意某些地方?谢谢!
我看了它生成的代码,我觉得挺巧妙的,虽然可能在一些方面效率上会有问题。
现在我用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 :)
今天的工作又有一些进展,基本上用SWIG这条路已经完全走通了。通过socket传来的字节流可以非常方便地用Marshal.Copy拷贝到非托管内存,然后用SWIG生成的C#封装类直接访问其中的每个成员变量。反过来也一样。
所以,如果以后谁碰到和我一样的问题,需要marshal一大堆struct的话,不妨试试SWIG :)
#1
其实转换挺容易的。
直接用.h,好像不可以吧。
支持一下。
直接用.h,好像不可以吧。
支持一下。
#2
谢谢支持。虽然转换不难,但是关键是量太大。而且一旦服务器端.h有变化,C#也要手工作出相应修改,不好维护。
#3
自己ding
#4
1.用C++开发核心的通讯COM类,使用C#调用.
2.用代码生成工具生成从C++Struct到C#的转换.(这个我认为比让一个还容易)
2.用代码生成工具生成从C++Struct到C#的转换.(这个我认为比让一个还容易)
#5
可以问您一下
1。序列化。为每个struct都写一个相应的C#类,并实现序列化接口。
这样的怎么实现呀。
1。序列化。为每个struct都写一个相应的C#类,并实现序列化接口。
这样的怎么实现呀。
#6
用C++开发核心的通讯COM类,使用C#调用.
agree
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不多的话,用上文提出的方法就可以解决你的问题了。
不过关于“用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通讯),难道就没有一个“标准”一点的解决方法吗?
另外它能支持这么复杂的代码生成吗?我是想有一个工具,输入一个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如何做这两者之间的“中介”呢?
我对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#
他的功能非常强.我的项目中就使用它.
他要求你自己写模块.他的功能强不强就看你的模块写的强不强了.他自己不会给你转换C++到C#
#15
我看了一下,大概了解一点它的基本原理。但是它支持这样的应用吗?就是输入一个C语言的头文件(或者粘贴一个struct定义代码),然后它就生成相应的C#类。也就是说,它必须知道如何解析C语言的struct定义。有可能吗?
#16
是你自己定义模板来实现转换.CODESMITH 可没这么智能.
#17
mark
#18
我知道要自己编写模板,其实就相当于写一个小程序。其实我用任何语言来写这个转换程序都可以的,关键就在于CODESMITH能不能简化这个过程。比如说,如果它可以将C语言的struct定义分析出一堆的语法对象(token?我也不懂),然后我就可以在template中直接操作这些对象,而不用自己手工去写一大堆的字符串解析程序。直觉上感觉这个似乎行不通,CODESMITH不可能这么厉害吧……
难道只能一个一个struct手工写自定义marshaler?我的命怎么这么苦呢?……
难道只能一个一个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));
}
}
}
这需要你懂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# 就可以了吧
然后反编译成C# 就可以了吧
#23
至于用工具把C的struct转换为C#的做法,不知道你们试过没有
首先你得建立C代码的抽象语法树(AST) — 就这个你得花多少时间来弄?CodeSmith之类根本不够用
然后你要用代码分析C里面struct的内存分布情况 — 这又是很复杂的,C不像C#那么简单,一个byte可能只占一个byte的空间,也可能占四个byte空间
还有各种各样的宏定义 — 你打算自己去解析这些东西吗?
首先你得建立C代码的抽象语法树(AST) — 就这个你得花多少时间来弄?CodeSmith之类根本不够用
然后你要用代码分析C里面struct的内存分布情况 — 这又是很复杂的,C不像C#那么简单,一个byte可能只占一个byte的空间,也可能占四个byte空间
还有各种各样的宏定义 — 你打算自己去解析这些东西吗?
#24
>>> 把C的那些结构定义转到C++/CLI中编译
>>> 然后反编译成C# 就可以了吧
impossible
>>> 然后反编译成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#中数组是作为对象(指针)来处理的。
初学者愚见,请多指教!再次感谢!
第二个方案中的“影射”是什么意思呢?有没有相关的资料?
对于上面的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里面的已经很详细了
这个方法只要不弹出异常,返回的数字就是可信任正确的,如果和用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
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。
关于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,似乎是专门做这个工作的
说实话我不太信任这个工具
这个没有直接的支持,但这种数组可以转换为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函数也基本不用重写。所以我觉得还是挺好使的:)但是既然你这样说,能不能说说你的理由,也许可以让我在使用时注意某些地方?谢谢!
我看了它生成的代码,我觉得挺巧妙的,虽然可能在一些方面效率上会有问题。
现在我用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 :)
今天的工作又有一些进展,基本上用SWIG这条路已经完全走通了。通过socket传来的字节流可以非常方便地用Marshal.Copy拷贝到非托管内存,然后用SWIG生成的C#封装类直接访问其中的每个成员变量。反过来也一样。
所以,如果以后谁碰到和我一样的问题,需要marshal一大堆struct的话,不妨试试SWIG :)