c++ typedef struct转换成c#类型,急!!!(在线等答案)

q345852047 2011-09-03 02:54:49
由于代码量大,我列出部分代码以示说明:
typedef	struct 
{
int m_nCmdType;
int m_nDevId;
int m_nChannelID;
ERRTYPE m_errType;

union
{
int m_nVal;
USERLOGINRESULT m_loginResult ;
CONFIGINFO m_ConfigInfo;
};

}COMMINFO, *PCOMMINFO;

typedef struct _tagUserLoginResult    	
{
unsigned int nUserPrivilege; //用户权限
//0xAAAAAAAA 管理员
//0xAAAAA 一般用户
//0xAAA 来宾
unsigned char nUserID; //用户ID。由IPC指定,整个字节有效。
unsigned char nloginResult; //登录结果。
unsigned char nAutoConnect ; //重连标记,1,表示该次登录结果为自动重连返回
unsigned char nResv; //预留字段
// 0x00 登录成功
// 其他 登录失败。详见错误原因码
unsigned int nUserSymbol; /*在用户第一次登录时,设备端返回的一个
随机数,用于用户重连时验证身份;*/
}USERLOGINRESULT , *LPUSERLOGINRESULT;


//设备配置信息
typedef struct
{
int m_devId;
char m_pstrIp[20];
short m_sPort;
}CONFIGINFO, *LPCONFIGINFO;



求高手指点~~~~
...全文
1255 35 打赏 收藏 转发到动态 举报
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
lsly_xy 2011-11-23
  • 打赏
  • 举报
回复
顶楼上的2位兄弟,现在就是在这方面碰到问题了。
sdl2005lyx 2011-09-07
  • 打赏
  • 举报
回复
楼上,能否就这个例子,给你实际代码,我估计很多人对联合体,尤其是比较复杂的联合体转换,都不大熟悉,
你把转换的代码贴出给大家看看,我也来学习学习。。。
qldsrx 2011-09-07
  • 打赏
  • 举报
回复
[Quote=引用 32 楼 sdl2005lyx 的回复:]
如有实践中就是这种数组方式,C#怎么来对应呢?
[/Quote]
使用属性来转换类型,统一使用byte[]存储,无论你使用何种方式访问它,存储的长度不变,只不过get方法中手动指定byte[]中的一定位数来封装一个类型返回。set方法则是往byte[]中部分位数进行赋值。
sdl2005lyx 2011-09-07
  • 打赏
  • 举报
回复
如有实践中就是这种数组方式,C#怎么来对应呢?
qldsrx 2011-09-07
  • 打赏
  • 举报
回复
USERLOGINRESULT 不是基本数据类型,但凡非基本数据类型,都要去找对应的类型定义。而你另一个例子中都是基本数据类型,不需要找类型定义,因此没事。
sdl2005lyx 2011-09-07
  • 打赏
  • 举报
回复
但是我发现一个奇怪问题:
即使我屏蔽了: [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string m_pstrIp;      //原来这里多了[20]

,即屏蔽引用类型,如果最后那个结构体,是数组方式:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
[FieldOffset(12)] public CONFIGINFO[] m_ConfigInfo;

还是会报地址重叠的错误,这个我就没法解释清楚了!
sdl2005lyx 2011-09-07
  • 打赏
  • 举报
回复
“同一个地址对象类型是明确的,当你既要存放这种类型的对象,又要存放那种类型的对象,就要出现问题,因为长度不一样。”,楼上,我的那个结构体里,也有好几种类型啊:float,byte,word,int等,为什么能成功呢?

其实,问题的本质我倒是找出来了:在托管代码中,值类型和引用类型不允许重叠!而我原来的结构体刚好没有引用类型,所以成功了!
问题就处在这个结构体:

[StructLayout(LayoutKind.Sequential)]
public struct CONFIGINFO
{
public int m_devId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string m_pstrIp;      //原来这里多了[20]
public short m_sPort;
}
如果屏蔽第二个成员变量,同时,把最后那个结构体最后一个成员:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
[FieldOffset(12)] public CONFIGINFO[] m_ConfigInfo;

变成:[FieldOffset(12)] public CONFIGINFO m_ConfigInfo;
即不是数组的方式,就可以成功!

MSDN对于含有引用类型和值类型的联合体,建议是拆分为两个结构体,如:
typedef uion Simple
{
int i;
char str[128];
}

C#:
[StructLayout(LayoutKind.Explicit,size=128)]//精确控制布局
public struct Simple_1
{
[FieldOffset(0)] public int i;
}

[StructLayout(LayoutKind.Sequential)]//引用类型不允许使用显示布局
public struct Simple_2
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string str;      
}

我搜索一下,网上有两个类似帖子:
http://www.cnblogs.com/flier/archive/2004/07/08/22358.html

http://www.cnblogs.com/allenlooplee/archive/2004/12/25/81917.html

qldsrx 2011-09-07
  • 打赏
  • 举报
回复
很容易解释,你以前的代码,两个重叠的对象长度是一样的,因此正常,.NET构建对象时,同一个地址对象类型是明确的,当你既要存放这种类型的对象,又要存放那种类型的对象,就要出现问题,因为长度不一样。
设想一下,.NET中是否可能出现为了一个一字节的byte申请20字节长度空间来存放的可能,答案是否定的。C++中却可以,因为C++中在读取这一字节byte的时候,可以只读取20字节的第一个字节,类似C#的属性转换器,get方法处理下的返回效果。
qldsrx 2011-09-06
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 sdl2005lyx 的回复:]
对于联合体,一定要是从相同的偏移开始,否则,两边的结果大小不一致,运行报异常!
[/Quote]
你到底会不会测试程序?PS:我是在帮谁解决问题啊?

都说了你这个运行时会异常,你却还以为我在说你的低级语法错误。基于你上述结构体,你写这么一段代码测试下:
COMMINFO a = new COMMINFO();
a.m_nVal = 1;

如果没有第二行代码,正常,但是出现了第二行代码就报错(你不用它初始化干嘛?),错误信息是:
“……在 12 偏移位置处包含一个对象字段,该字段已由一个非对象字段不正确地对齐或重叠。”
sdl2005lyx 2011-09-06
  • 打赏
  • 举报
回复
你也找找原因,看看到底是什么问题!
sdl2005lyx 2011-09-06
  • 打赏
  • 举报
回复
楼上,这个问题很奇怪,就是这句有问题:
“ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
[FieldOffset(12)] public CONFIGINFO[] m_ConfigInfo;”

屏蔽就没问题了!但是就是不知是什么原因!
我以前转换的代码,都很正常:

C++:

//数据采集遥信变位报警结构
struct DA_ALARM {
WORD wNetCmd; //子命令码 = NETDA_ALARM
BYTE byAlarmSrc; //报警来源
BYTE byAlarmType; //报警类型
BYTE byAlarmLevel; //报警级别
union {
struct {
BYTE byStation; //厂站号
WORD wPointNo; //报警点号
};
struct {
BYTE byChannel; //主通道号
BYTE byDummyCh; //链路号
};
};
union { //当前值
BYTE byValue; //遥信报警时遥信值
float fValue; //遥测报警时遥测值
BYTE byChannelEvt; //通道报警时事件值
};
DWORD dwRes; //保留
};

C#:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DA_ALARM
{
public ushort wNetCmd; //子命令码 = NETDA_ALARM
public byte byAlarmSrc; //报警来源
public byte byAlarmType; //报警类型
public byte byAlarmLevel; //报警级别

[StructLayout(LayoutKind.Explicit)]
public struct Att1
{
[FieldOffset(0)]public byte byStation; //厂站号
[FieldOffset(2)]public ushort wPointNo; //报警点号
[FieldOffset(0)]public byte byChannel; //主通道号
[FieldOffset(1)]public byte byDummyCh; //链路号
}

public Att1 att1;
[StructLayout(LayoutKind.Explicit)]
public struct Attr2
{ //当前值
[FieldOffset(0)]public byte byValue; //遥信报警时遥信值
[FieldOffset(0)]public float fValue; //遥测报警时遥测值
[FieldOffset(0)]public byte byChannelEvt; //通道报警时事件值
}
public Attr2 att2;
public uint dwRes; //保留
}



q345852047 2011-09-05
  • 打赏
  • 举报
回复
谢谢 sdl2005lyx 这位朋友的回答,如果我定义一个结构体数组应该怎么转换?如下:

typedef struct
{
int m_nCmdType;
int m_nDevId;
int m_nChannelID;
union
{
int m_nVal;
USERLOGINRESULT m_loginResult ;
CONFIGINFO m_ConfigInfo[10]; };

}COMMINFO, *PCOMMINFO;

q345852047 2011-09-05
  • 打赏
  • 举报
回复
谢谢 sdl2005lyx 这位朋友的回答,如果我定义一个结构体数组应该怎么转换?如下:

typedef    struct 
{
int m_nCmdType;
int m_nDevId;
int m_nChannelID;
union
{
int m_nVal;
USERLOGINRESULT m_loginResult ;
CONFIGINFO m_ConfigInfo[10]; };

}COMMINFO, *PCOMMINFO;

sdl2005lyx 2011-09-05
  • 打赏
  • 举报
回复
对于联合体,一定要是从相同的偏移开始,否则,两边的结果大小不一致,运行报异常!
sdl2005lyx 2011-09-05
  • 打赏
  • 举报
回复
楼上,多谢你的指出,我考到程序理编译编译一下,是有问题,但是不是你说的问题,只是因为手写的,几个拷贝的地方没改过来。现在我把编译正确的代码贴出,你看看还有没有问题:

[StructLayout(LayoutKind.Sequential)]
public struct USERLOGINRESULT
{
public uint nUserPrivilege;
public byte nUserID;
public byte nloginResult;
public byte nAutoConnect ;
public byte nResv;
public uint nUserSymbol;
}

[StructLayout(LayoutKind.Sequential)]
public struct CONFIGINFO
{
public int m_devId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string m_pstrIp;      //原来这里多了[20]
public short m_sPort;
}

[StructLayout(LayoutKind.Explicit)]
public struct COMMINFO
{
[FieldOffset(0)] public int m_nCmdType;
[FieldOffset(4)] public int m_nDevId;
[FieldOffset(8)] public int m_nChannelID;

[FieldOffset(12)]public int m_nVal; //原来这里是m_nChannelID,明显拷贝错误!
[FieldOffset(12)] public USERLOGINRESULT loginResult ;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
[FieldOffset(12)] public CONFIGINFO[] m_ConfigInfo;
}
qldsrx 2011-09-05
  • 打赏
  • 举报
回复
你所想到的写法,我第一时间就想到并测试过了,结果在调用是报错,我猜想是.NET在托管内存分配对象时,不允许将同一地址给2个不同类型的对象,这个是.NET内部在管理内存,因此只有在运行时才会报错。
qldsrx 2011-09-05
  • 打赏
  • 举报
回复
楼上的,实践出真理,你调试下那个结构体就知道了,编译可以,运行时报错。

PS:你的是VS2005的帮助文档路径,我这里不存在。
sdl2005lyx 2011-09-05
  • 打赏
  • 举报
回复
楼上的,你看清楚了,我用的是:[StructLayout(LayoutKind.Explicit)]//精确控制结构体内存偏移布局。

对于联合体,在哪个语言理都是使用的共同地址,这个你不清楚吗?

你仔细看看MSDN的例子:
ms-help://MS.MSDNQTR.v80.chs/MS.MSDN.v80/MS.VisualStudio.v80.chs/dv_fxinterop/html/0068fbf2-3ab4-4f43-abd8-4d34ae17bd7c.htm
qldsrx 2011-09-05
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 sdl2005lyx 的回复:]
StructLayout(LayoutKind.Explicit)]
public struct COMMINFO
{
[FieldOffset(0)] public int m_nCmdType;
[FieldOffset(4)] public int m_nDevId;
[FieldOffset(8)] public int m_nChannelID;

[Fie……
[/Quote]
你先去试试自己的代码吧,FieldOffset指定的偏移地址是不允许重复的,否则使用时必报错。

TO:q345852047
结构体数组其实很好理解,在C#中Byte 、Int32这些基本类型就是结构体,只不过你现在需要替换为自己定义的结构体而已,所以写法等同于Byte[]的写法。
另外没必要使用FieldOffset指定偏移,你还要去计算偏移地址,算错就全完蛋,最好是直接指定LayoutKind.Sequential让其自动连续分布。
sdl2005lyx 2011-09-05
  • 打赏
  • 举报
回复
StructLayout(LayoutKind.Explicit)]
public struct COMMINFO
{
[FieldOffset(0)] public int m_nCmdType;
[FieldOffset(4)] public int m_nDevId;
[FieldOffset(8)] public int m_nChannelID;

[FieldOffset(12)] public int m_nChannelID;
[FieldOffset(12)] public USERLOGINRESULT loginResult ;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
[FieldOffset(12)] public CONFIGINFO[] m_ConfigInfo;
}
}
加载更多回复(13)

110,539

社区成员

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

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

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