FieldInfo.SetValue recodé avec GetFieldOffset
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:
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.