J'ai enfin trouvé où était caché ce satané FieldOffset dans l'objet RtFieldInfo.
Pour ce faire j'ai mis un data break point sur l'adresse d'un champ, puis j'ai appelé Field.SetValue, ça m'a amené au code assembleur de clr.dll et au champ non typé RuntimeFieldHandle.m_ptr
(clr.dll c'est du code non managé mais heureusement ils ont laissé dans le .pdb les noms des fonctions)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
 
// RuntimeFieldHandle.cpp : main project file.
 
#include "stdafx.h"
 
using namespace System;
using namespace System::Reflection;
 
// Je définis un type .NET avec plein de champs, 
// et je retrouve bien l'offset 28 (en 32bit) pour le champ String ^s
 
ref struct A {
	Object ^h;
	Int32 ^k,^l,^m,pp;
	UInt32 a,b,c,d,e;
	Object ^h2;
private :
	Type ^t;
	Boolean p;
	UInt32 a2,b2,c2,d2,e2;
public:
	String ^s;
};
 
generic <typename T> value struct FieldReference {
	UInt32 offset;
	Object ^obj;
 
	value struct RuntimeFieldHandle_m_ptr {
		UInt32 a,b,offset; // attention UInt32 est peut-être uniquement valable en 32bit
	};
 
	static UInt32 GetFieldOffset(FieldInfo ^f) {
		RuntimeFieldHandle h = f->FieldHandle;
		IntPtr value = h.Value;
		RuntimeFieldHandle_m_ptr * m_ptr = (RuntimeFieldHandle_m_ptr *)(void*)value; // le type de value n'est pas communiqué par .NET : il faut le trouver soi-même
		UInt32 res = m_ptr->offset;
                // le premier bit est probablement un flag ?
		res &= 0x7FFFFFF; // attention 0x7FFFFFF est peut-être uniquement valable en 32bit
 
		return res +  sizeof(void*); // ajout de la VMT
	}
 
 
	FieldReference(Object ^obj, UInt32 offset) : obj(obj), offset(offset) {}
 
	FieldReference(Object ^obj, FieldInfo^ fi) : obj(obj) {
		offset = GetFieldOffset(fi);
	}
 
	FieldReference(Object ^obj, String ^fieldName) : obj(obj) {
		Type ^t = obj->GetType();
		FieldInfo ^fi = t->GetField(fieldName);
		offset = GetFieldOffset(fi);
	}
 
	property T Value {
 
		T get() {
			pin_ptr<Object^> tmp = &this->obj;
			void *ptr = (void*)tmp;
			char *objadr = *(char**)ptr;
			void *fieldadr = objadr + this->offset;
 
			return (T)*(Object^*)fieldadr;
		}
		void set(T t) {
			pin_ptr<Object^> tmp = &this->obj;
			void *ptr = (void*)tmp;
			char *objadr = *(char**)ptr;
			void *fieldadr = objadr + this->offset;
 
			*(Object^*)fieldadr = t;
		}
	};
};
 
int main(array<System::String ^> ^args)
{
	A ^a = gcnew A();
	a->s = "avant";
 
	FieldReference<String^> a_s(a,"s");
	a_s.Value = "apres";
	Console::WriteLine(a->s);
    return 0;
}
Je n'ai pas testé en 64 bit : la constante 0x7FFFFFF et les UInt32 de RuntimeFieldHandle_m_ptr deviennent peut-être des UInt64.