C#获取结构偏移量的命令?
假设我有这样一个C#结构:C#获取结构偏移量的命令?,c#,C#,假设我有这样一个C#结构: [StructLayout(LayoutKind.Explicit)] struct IMAGE_DOS_HEADER { [FieldOffset(60)] public int e_lfanew; } byte[] data = new byte[4096]; FileStream f = new FileInfo(filename).Open(FileMode.Open, FileAccess.Read); int n = f.Read(data, 0
[StructLayout(LayoutKind.Explicit)]
struct IMAGE_DOS_HEADER {
[FieldOffset(60)] public int e_lfanew;
}
byte[] data = new byte[4096];
FileStream f = new FileInfo(filename).Open(FileMode.Open, FileAccess.Read);
int n = f.Read(data, 0, 4096);
if (n >= offsetof(IMAGE_DOS_HEADER.e_lfanew) + sizeof(int)) {
...
}
现在假设我从一个文件中读取数据,如下所示:
[StructLayout(LayoutKind.Explicit)]
struct IMAGE_DOS_HEADER {
[FieldOffset(60)] public int e_lfanew;
}
byte[] data = new byte[4096];
FileStream f = new FileInfo(filename).Open(FileMode.Open, FileAccess.Read);
int n = f.Read(data, 0, 4096);
if (n >= offsetof(IMAGE_DOS_HEADER.e_lfanew) + sizeof(int)) {
...
}
现在我想测试n
,以确保我已经读取了足够的字节来重新获得e\u lfanew
的值。有没有办法不用重新键入就可以得到值60(FieldOffset)?我在找这样的东西:
[StructLayout(LayoutKind.Explicit)]
struct IMAGE_DOS_HEADER {
[FieldOffset(60)] public int e_lfanew;
}
byte[] data = new byte[4096];
FileStream f = new FileInfo(filename).Open(FileMode.Open, FileAccess.Read);
int n = f.Read(data, 0, 4096);
if (n >= offsetof(IMAGE_DOS_HEADER.e_lfanew) + sizeof(int)) {
...
}
有这样的命令吗?在我的实际代码中,我必须进行几个这样的测试,手动键入数字似乎很乏味,而且容易出错,方法是将结构中先前的字段相加,或者从FieldOffset属性中复制值。有更好的方法吗?是的,您可以使用反射来实现这一点
FieldOffsetAttribute fieldOffset =
(FieldOffsetAttribute)typeof(IMAGE_DOS_HEADER)
.GetField("e_lfanew")
.GetCustomAttributes(
typeof(FieldOffsetAttribute),
true
)[0];
Console.WriteLine(fieldOffset.Value);
您甚至可以将其转化为一种很好的方法:
static int FieldOffset<T>(string fieldName) {
if (typeof(T).IsValueType == false) {
throw new ArgumentOutOfRangeException("T");
}
FieldInfo field = typeof(T).GetField(fieldName);
if (field == null) {
throw new ArgumentOutOfRangeException("fieldName");
}
object[] attributes = field.GetCustomAttributes(
typeof(FieldOffsetAttribute),
true
);
if (attributes.Length == 0) {
throw new ArgumentException();
}
FieldOffsetAttribute fieldOffset = (FieldOffsetAttribute)attributes[0];
return fieldOffset.Value;
}
static int FieldOffset(字符串字段名){
if(typeof(T).IsValueType==false){
抛出新ArgumentOutOfRangeException(“T”);
}
FieldInfo field=typeof(T).GetField(fieldName);
如果(字段==null){
抛出新ArgumentOutOfRangeException(“fieldName”);
}
对象[]属性=field.GetCustomAttributes(
类型(FieldOffsetAttribute),
真的
);
如果(attributes.Length==0){
抛出新ArgumentException();
}
FieldOffsetAttribute fieldOffset=(FieldOffsetAttribute)属性[0];
返回fieldOffset.Value;
}
用法:
Console.WriteLine(FieldOffset<IMAGE_DOS_HEADER>("e_lfanew"));
Console.WriteLine(FieldOffset(“e_lfanew”);
嗯,您已经在结构声明中使用了一个幻数。不妨这样做:
private const int LfaNewOffset = 60;
[StructLayout(LayoutKind.Explicit)]
struct IMAGE_DOS_HEADER {
[FieldOffset(LfaNewOffset)] public int e_lfanew;
}
...
if (n >= LfaNewOffset + sizeof(int)) {
...
}
使用Marshal.OffsetOf:
Marshal.OffsetOf(typeof(IMAGE_DOS_HEADER), "e_lfanew")
+我同意。反射是解决这个问题的最安全(确定性和可维护性)的方法。在避免代码重复的上下文中,定义常量(正如nobugz所建议的)几乎总是比使用反射更好的选择。在某些情况下,反射是适当的,但避免代码重复通常可以通过其他方式完成。也就是说,反思可能更简单,因为有很多这样的数字。注:我不是向下投票人。@Brian:不清楚他是否有权访问结构的定义。如果他这样做,我同意。如果他没有,则需要另一种方法。@Jason:现在,在看到wj32的答案后,我觉得你的答案在这种情况下过于复杂了。@Brian:请看我对wj32答案的评论。如果他无法访问结构的定义怎么办?我同意,他到底做还是不做还不清楚。@Jason:如果他不做,那么这就行不通了。但是我不认为有必要指出这是这个方法的一个缺陷,因为它是不言而喻的。哇,这正是他所要求的(尽管我仍然对nobugz的答案感到更舒服,假设它是可行的)<代码>封送处理用于在托管和非托管之间切换。因此,从文档中“
offset of
提供了非托管结构布局的偏移量,它不一定对应于托管结构布局的偏移量。”因此在某些情况下,这可能给出错误的答案。您为什么想知道托管结构的偏移量?即使您知道您将无法以任何方式使用它,因为即使获得指向该结构的指针,也意味着它是可轻击的。你的观察与任何实际目的都完全无关。这适用于问题中的具体情况。一般来说,这是行不通的。例如,如果您有一个包含一个或多个bool类型字段的结构,则sizeof(bool)==1,但Marshal.sizeof(type(bool))=4。从Marshal.OffsetOf开始,偏移量也同样不正确。换句话说,Marshal.OffsetOf函数给出的是封送结构的非托管偏移量,这可能不同于非封送偏移量。Marshal.OffsetIf是否可以用于结构的结构中的字段/道具?这需要如何命名?比如说:Struct1.Struct2.Prop1 Strct1中Prop1的标识符是什么?哇,我没想到我会得到任何真正的答案(除了nobugz的),这里我有三个选择!我几乎不知道该选哪个,所以我把他们全都投了赞成票。简单地定义常量是一种合理的方法,但它混淆结构布局的方式有点烦人。我仍在学习托管/非托管的微妙之处,但我认为wj32是正确的,因为编译器已经允许我获取指向结构的指针,我知道托管/非托管的offet是相同的。所以我选择他的答案,因为它似乎产生了最容易阅读的代码。谢谢大家这么好的回复!