-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathProgram.cs
More file actions
105 lines (88 loc) · 3.78 KB
/
Copy pathProgram.cs
File metadata and controls
105 lines (88 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
// A span containing three bytes.
// The structs will be marshalled on top of the "1", and use field offsets to access the other bytes in the span.
Span<byte> bytes = [255, 1, 2];
// An offset of 0 will point to the "1" byte.
// An offset of 1 will point to the "2" byte.
// An offset of -1 will point to the "255" byte.
foreach (int offset in new int[] { 0, 1, -1 })
{
try
{
byte b = TestWithOffset(ModuleBuilder, offset, bytes);
Console.WriteLine($"With an offset of {offset}, the value of MyByte is {b}");
}
catch (Exception e)
{
Console.WriteLine($"Attempt to create type with offset {offset} threw an {e.GetType().Name}: {e.Message}");
}
}
Console.ReadLine();
static partial class Program
{
public static ModuleBuilder ModuleBuilder { get; } = CreateModuleBuilder();
static unsafe byte TestWithOffset(ModuleBuilder moduleBuilder, int offset, Span<byte> bytes)
{
// Create a new type with the specified field offset.
Type type = CreateTypeWithOffset(moduleBuilder, offset);
// Get a pointer to the "1" byte of the span.
fixed (byte* ptr = &bytes[1..][0])
{
// Create an instance of our struct on top of the "1" byte of the span.
dynamic d = Marshal.PtrToStructure(new nint(ptr), type)!;
return d.MyByte;
}
}
/// <summary>
/// Creates an assembly and module at runtime.
/// </summary>
/// <returns></returns>
static ModuleBuilder CreateModuleBuilder()
{
string assemblyName = "DynamicallyGeneratedAssembly";
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(
new AssemblyName(assemblyName),
AssemblyBuilderAccess.Run);
return assemblyBuilder.DefineDynamicModule(assemblyName);
}
/// <summary>
/// Creates a new type at runtime, with byte field "<c>MyByte</c>" which has a field offset of <paramref name="offset"/>.
/// </summary>
/// <param name="moduleBuilder"></param>
/// <param name="offset"></param>
/// <returns></returns>
static Type CreateTypeWithOffset(ModuleBuilder moduleBuilder, int offset)
{
// Add a GUID to the type name, to ensure it is unique.
string typeName = $"DynamicallyGeneratedType_{Guid.NewGuid()}";
string fieldName = "MyByte";
TypeBuilder typeBuilder = moduleBuilder.DefineType(
typeName,
TypeAttributes.Public,
typeof(ValueType));
// Decorates the type with StructLayoutAttribute, setting its layout to explicit.
typeBuilder.SetCustomAttribute(GetAttributeConstructor<StructLayoutAttribute, LayoutKind>(LayoutKind.Explicit));
FieldBuilder fieldBuilder = typeBuilder.DefineField(
fieldName,
typeof(byte),
FieldAttributes.Public);
// Decorates the field with FieldOffsetAttribute, setting its field offset to the specified offset.
fieldBuilder.SetCustomAttribute(GetAttributeConstructor<FieldOffsetAttribute, int>(offset));
return typeBuilder.CreateType();
}
/// <summary>
/// Returns a builder for <typeparamref name="TAttribute"/>, using the constructor with a single parameter of type <typeparamref name="TConstructorArg1"/>
/// </summary>
/// <typeparam name="TAttribute"></typeparam>
/// <typeparam name="TConstructorArg1"></typeparam>
/// <param name="arg1"></param>
/// <returns></returns>
static CustomAttributeBuilder GetAttributeConstructor<TAttribute, TConstructorArg1>(TConstructorArg1 arg1)
{
return new CustomAttributeBuilder(
typeof(TAttribute).GetConstructor([typeof(TConstructorArg1)])!,
[arg1]);
}
}