请教:C#的共用体运行出错的疑问?

yaoike 2012-04-28 12:00:43
我定义了一个共用体。如下:


[StructLayout(LayoutKind.Explicit)]
public struct MyStruct
{
[FieldOffset(0)]
public UInt32 dwValue;


[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
[FieldOffset(0)]
public char[] s1;
}

MyStruct ms = new MyStruct(); // 运行到这里就会出错,为什么啊?
// 出错信息是:An unhandled exception of type 'System.TypeLoadException'
// occurred in System.Windows.Forms.dll

而如果是把结构体的声明改为:
public struct MyStruct
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public byte[] bHigh;

[FieldOffset(64)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public byte[] bLow;


[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public char[] s1;
}

就不会出错,这是为什么啊?


...全文
258 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
xboxeer 2012-04-28
  • 打赏
  • 举报
回复
两个字段都布局到同一个内存里面去了?你这边用了字段内存布局方面的特性 感觉这个东西还是让CLR或者编译器自己去决定的比较好
真相重于对错 2012-04-28
  • 打赏
  • 举报
回复
关于union msdn上都有,需要声明两次
.NET Framework 开发人员指南
Unions 示例
请参见
语言筛选器: 全部 语言筛选器: 多个 语言筛选器: Visual Basic 语言筛选器: C# 语言筛选器: C++ 语言筛选器: J# 语言筛选器: JScript
Visual Basic(声明)
Visual Basic(用法)
C#
C++
J#
JScript

该示例说明如何将两种结构作为参数传递给需要联合的非托管函数,一种结构是只包含值类型的结构,一种结构是同时包含值类型和字符串的结构。联合表示一个可以由两个或多个变量共享的内存位置。Microsoft .NET Framework SDK 在 Samples\Technologies\Interop\Platform-Invoke 目录中包括此示例的完整 Visual Basic 2005 版本和 C# 版本。

Unions 示例使用以下非托管函数(这里同时显示其原始函数声明):

从 PinvokeLib.dll 导出的 TestUnion。

复制代码
void TestUnion(MYUNION u, int type);


PinvokeLib.dll 是一个自定义非托管库,它包含前面列出的函数的实现和以下两个联合:MYUNION 和 MYUNION2。这两个联合包含以下元素:

复制代码
union MYUNION
{
int number;
double d;
}

union MYUNION2
{
int i;
char str[128];
};


在托管代码中,将联合定义为结构。MyUnion 结构包含两个值类型作为其成员:一个整型和一个双精度型。StructLayoutAttribute 属性经过设置,以控制每个数据成员的准确位置。FieldOffsetAttribute 属性提供字段在联合的非托管表示形式中的物理位置。请注意,这两个成员具有相同的偏移量值,因此这两个成员可以定义相同的内存块。

MyUnion2_1 和 MyUnion2_2 分别包含一个值类型(整型)和一个字符串。在托管代码中,值类型和引用类型不允许重叠。该示例使用方法重载以使调用方能够在调用同一非托管函数时同时使用这两种类型。MyUnion2_1 的布局是显式的,并具有准确的偏移量值。与此相反,MyUnion2_2 的布局则是连续的,原因是引用类型不允许使用显式布局。MarshalAsAttribute 属性将 UnmanagedType 枚举设置为 ByValTStr,它用于标识在联合的非托管表示形式中出现的内联的定长字符数组。

LibWrap 类包含 TestUnion 和 TestUnion2 方法的原型。TestUnion2 被重载以便将 MyUnion2_1 或 MyUnion2_2 声明为参数。

声明原型
Visual Basic 复制代码
' Declares managed structures instead of unions.
< StructLayout( LayoutKind.Explicit )> _
Public Structure MyUnion
< FieldOffset( 0 )> Public i As Integer
< FieldOffset( 0 )> Public d As Double
End Structure 'MyUnion

< StructLayout( LayoutKind.Explicit, Size := 128 )> _
Public Structure MyUnion2_1
< FieldOffset( 0 )> Public i As Integer
End Structure 'MyUnion2_1

< StructLayout( LayoutKind.Sequential )> _
Public Structure MyUnion2_2
< MarshalAs( UnmanagedType.ByValTStr, SizeConst := 128 )> _
Public str As String
End Structure 'MyUnion2_2

Public Class LibWrap
' Declares managed prototypes for unmanaged function.
Declare Sub TestUnion Lib "..\LIB\PinvokeLib.dll" ( _
ByVal u As MyUnion, ByVal type As Integer )
Overloads Declare Sub TestUnion2 Lib "..\LIB\PinvokeLib.dll" ( _
ByVal u As MyUnion2_1, ByVal type As Integer )
Overloads Declare Sub TestUnion2 Lib "..\LIB\PinvokeLib.dll" ( _
ByVal u As MyUnion2_2, ByVal type As Integer )
End Class 'LibWrap


C# 复制代码
// Declares managed structures instead of unions.
[ StructLayout( LayoutKind.Explicit )]
public struct MyUnion
{
[ FieldOffset( 0 )]
public int i;
[ FieldOffset( 0 )]
public double d;
}
[ StructLayout( LayoutKind.Explicit, Size=128 )]
public struct MyUnion2_1
{
[ FieldOffset( 0 )]
public int i;
}
[ StructLayout( LayoutKind.Sequential )]
public struct MyUnion2_2
{
[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=128 )]
public String str;
}

public class LibWrap
{
// Declares managed prototypes for unmanaged function.
[ DllImport( "..\\LIB\\PinvokeLib.dll" )]
public static extern void TestUnion( MyUnion u, int type );
[ DllImport( "..\\LIB\\PinvokeLib.dll" )]
public static extern void TestUnion2( MyUnion2_1 u, int type );
[ DllImport( "..\\LIB\\PinvokeLib.dll" )]
public static extern void TestUnion2( MyUnion2_2 u, int type );
}


调用函数
Visual Basic 复制代码
Public Class App
Public Shared Sub Main()
Dim mu As New MyUnion()
mu.i = 99
LibWrap.TestUnion( mu, 1 )

mu.d = 99.99
LibWrap.TestUnion( mu, 2 )

Dim mu2_1 As New MyUnion2_1()
mu2_1.i = 99
LibWrap.TestUnion2( mu2_1, 1 )

Dim mu2_2 As New MyUnion2_2()
mu2_2.str = "*** string ***"
LibWrap.TestUnion2( mu2_2, 2 )
End Sub 'Main
End Class 'App


C# 复制代码
public class App
{
public static void Main()
{
MyUnion mu = new MyUnion();
mu.i = 99;
LibWrap.TestUnion( mu, 1 );

mu.d = 99.99;
LibWrap.TestUnion( mu, 2 );

MyUnion2_1 mu2_1 = new MyUnion2_1();
mu2_1.i = 99;
LibWrap.TestUnion2( mu2_1, 1 );

MyUnion2_2 mu2_2 = new MyUnion2_2();
mu2_2.str = "*** string ***";
LibWrap.TestUnion2( mu2_2, 2 );
}
}


请参见
概念
封送类、结构和联合
平台调用数据类型
在托管代码中创建原型


要提出有关“帮助”或本产品其他功能的建议或错误报告,请转到反馈站点。
xboxeer 2012-04-28
  • 打赏
  • 举报
回复
在3.5下面编译运行也没问题 结构体声明和你一样 我这样调用尝试了下
var myStruct = new MyStruct();
myStruct.struct1 = new MyStruct1()
{
ucSelectData = new byte[1]
};
MessageBox.Show("hh");
也木有问题~
yaoike 2012-04-28
  • 打赏
  • 举报
回复
哦,可能我的版本太低了吧,我的是VS2008, .net3.5的平台。
xboxeer 2012-04-28
  • 打赏
  • 举报
回复
我的还真没错 直接复制的你的代码 不过我是.net 4.0下的
[Quote=引用 10 楼 的回复:]

MyStruct ms = new MyStruct(); // 运行到这里就会出错,出错信息是:An unhandled exception
// of type 'System.TypeLoadException'
// occurred in System.Windo……
[/Quote]
yaoike 2012-04-28
  • 打赏
  • 举报
回复
MyStruct ms = new MyStruct(); // 运行到这里就会出错,出错信息是:An unhandled exception
// of type 'System.TypeLoadException'
// occurred in System.Windows.Forms.dll


你的不会出错吗?不可能吧?!
xboxeer 2012-04-28
  • 打赏
  • 举报
回复
报的异常是什么?我用你的代码运行了下没问题 断点去看每个成员也没问题
yaoike 2012-04-28
  • 打赏
  • 举报
回复
有问题,运行的时候会出错。
xboxeer 2012-04-28
  • 打赏
  • 举报
回复
LS的代码没有问题吧~
yaoike 2012-04-28
  • 打赏
  • 举报
回复
谢谢兄弟的指导,还有几点疑问,可否再解答一下,谢谢!
1.
请问一下,你是怎么知道这个问题的原因的,引用自你的原话:
包含引用类型和值类型的结构体,在同一个FieldOffset位置,不能声明成不同的类型
要么都是值类型,要么都是引用类型
对于出现的问题,我上了google,搜索错误也找不到问题的根源,请问你是怎么直击到根源的啊?

2.

如果我的结构是这样的话,
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct1
{
public UInt16 wTime;
public Byte ucOption;
public UInt16 wAntenaFlag;

public UInt32 dwSelectAddr;
public Byte ucSelectDataLength;

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public Byte[] ucSelectData;
};

[StructLayout(LayoutKind.Sequential)]
public struct MyStruct2
{
UInt16 wTime;
Byte ucOption;
UInt16 uAntenaFlag;
UInt32 uMetadataFlag;
UInt32 uSelectAddr;
Byte ucSelectDataLength;

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
Byte[] ucSelectData;
};


[StructLayout(LayoutKind.Explicit)]
public struct MyStruct
{
[FieldOffset(0)]
public MyStruct1 myStruct1;

[FieldOffset(0)]
public MyStruct2 myStruct2;
}

MyStruct ms = new MyStruct();



那要怎么改呢?可以再给一个例子吗?
bloodish 2012-04-28
  • 打赏
  • 举报
回复
不好意思,验证了一把,以为可空类型能解决问题,但其实不行,改成
public Object dwValue;
UINT16->Object,不会报错,但不知是否符合楼主要求
bloodish 2012-04-28
  • 打赏
  • 举报
回复
包含引用类型和值类型的结构体,在同一个FieldOffset位置,不能声明成不同的类型
要么都是值类型,要么都是引用类型


[StructLayout(LayoutKind.Explicit)]
public struct MyStruct
{
[FieldOffset(0)]
public UInt32? dwValue;


[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
[FieldOffset(0)]
public char[] s1;
}


比较接近的写法,改成可空类型
xboxeer 2012-04-28
  • 打赏
  • 举报
回复
没有用结构体模拟过union 所以帮不上忙~
[Quote=引用 2 楼 的回复:]

那我要怎么定义啊?可以给一个例子吗?
[/Quote]
yaoike 2012-04-28
  • 打赏
  • 举报
回复
那我要怎么定义啊?可以给一个例子吗?

110,535

社区成员

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

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

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