2014-04-28 8 views
6

Mam bibliotekę .Net dostarczoną przez stronę trzecią. Zrobiłem refleksję na temat jednej z ich klas i znalazłem metodę członka. Podpis był ...Jak obejść błąd "Wartość powrotu ByRef nie jest obsługiwana w wywołaniu odbicia" w języku C#?

Byte& FooBar() 

Więc chciałem wywołać tej metody poprzez odbicie i dostał wyjątek „ByRef powrócić wartość nie jest obsługiwana w wywołaniu refleksji.”

Oto co próbowałem ...

 var strm = new TheirClass(); 
     var t = strm.GetType(); 
     var ms = t.GetMembers(
        BindingFlags.Static|BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 
     foreach (var m in ms) 
     { 
      Debug.WriteLine(String.Format("Name: {0}: {1}", m.Name, m.ToString())); 
      // ... 
      // Name: FooBar: Byte& FooBar() 
      // ... 
     } 
     var meth = t.GetMethod("FooBar"); 
     object returnValue = meth.Invoke(strm, new object[] { }); //throw exception 

Próbowałem dostarczanie parametry jak w wywoływanie funkcji z parametrami ref, ale to nie miało znaczenia.

Chciałbym obejść ten wyjątek w języku C#.

+2

Interesujące, ale wyjątek wydaje się dość jasny - funkcja zwraca odwołanie do bajtu, a wywołanie nie obsługuje funkcji, które zwracają odwołania (dlaczego, nie wiem ... może na wypadek, gdyby coś na stosie było zwrócony). Czy ta metoda jest publiczna, może mogłaby być wywołana przez zarządzaną bibliotekę DLL C++? –

+0

@ T.Kiley - zgodzili się, wyjątek jest jasny, tzn. "Nieobsługiwany". Ale moje pytanie brzmi: jak to obejść. Tak, może zrobi to DLL w C++, ale chciałbym pracować w C#, jeśli to możliwe. Zaktualizuję pytanie. Dzięki. – Les

+0

Czy TypeDescriptors będzie warta posiadania efektu w. Wiem, że zapewniają dodatkowe możliwości metadanych w porównaniu do refleksji, jestem pewien, że tam też jest trochę rzeczy wywołujących. – brumScouse

Odpowiedz

5

Za komentarze: oto jak można to zrobić z CIL, który można wygenerować z C#.

Miałem nadzieję, że skorzystam z DynamicMethod, ale nie uda mi się uzyskać tego działa bez tworzenia niestandardowego typu delegata w czasie wykonywania, więc musiałem użyć AssemblyBuilder zamiast tego.

using System; 
using System.Reflection; 
using System.Reflection.Emit; 

public delegate void CallBadFunction(Delegate d, Callback c); 
public delegate void Callback(ref int i); 

static class Program 
{ 
    static int i; 
    static object BadMethod() 
    { 
     return i; 
    } 

    static MethodInfo GetBadMethod() 
    { 
     return typeof(Program).GetMethod("BadMethod", BindingFlags.Static | BindingFlags.NonPublic); 
    } 

    static void Main() 
    { 
     var badMethod = GetBadMethod(); 

     var assembly = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("-"), AssemblyBuilderAccess.Run); 
     var module = assembly.DefineDynamicModule("-"); 

     var badDelegate = module.DefineType("BadDelegateType", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed, typeof(MulticastDelegate)); 
     var badDelegateCtor = badDelegate.DefineConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { typeof(object), typeof(IntPtr) }); 
     badDelegateCtor.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); 
     var badDelegateInvoke = badDelegate.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.HideBySig, typeof(int).MakeByRefType(), Type.EmptyTypes); 
     badDelegateInvoke.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed); 
     var badDelegateType = badDelegate.CreateType(); 

     var method = module.DefineGlobalMethod("-", MethodAttributes.Public | MethodAttributes.Static, typeof(void), new[] { typeof(Delegate), typeof(Callback) }); 
     var il = method.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_1); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Castclass, badDelegate); 
     il.Emit(OpCodes.Callvirt, badDelegateInvoke); 
     il.Emit(OpCodes.Callvirt, typeof(Callback).GetMethod("Invoke")); 
     il.Emit(OpCodes.Ret); 
     module.CreateGlobalFunctions(); 

     var callBadFunction = (CallBadFunction)Delegate.CreateDelegate(typeof(CallBadFunction), module.GetMethod("-")); 
     callBadFunction(badMethod.CreateDelegate(badDelegateType), (ref int i) => 
     { 
      i++; 
     }); 
    } 
} 

Po kompilacji program, użyj ILDASM aby go zdemontować i zastąpić definicję BadMethod „s poprzez

.method private hidebysig static int32& 
     BadMethod() cil managed 
{ 
    ldsflda  int32 Program::i 
    ret 
} 

ta zamienia ją w funkcję powracającego int32&, która następujący kod następnie udaje się zadzwonić . Jedynym miejscem C# pozwala int32& typów jest w parametrach funkcji (ref int), więc aby wynik był użyteczny, użyłem funkcji wywołania zwrotnego, która przekazuje wartość zwracaną BadMethod.

+1

+1 Świetna odpowiedź - to hardcore kodu! – briantyler

+0

Nałożyłeś mnie na coś, gdy zasugerowałeś DynamicMethod. Zaznaczam twoją odpowiedź jako odpowiedź, ponieważ twoje podejście może być bardziej ogólne. Ale spójrz na to, co wymyśliłem po twoim wcześniejszym komentarzu. – Les

0

Dziękuję "hvd" za skierowanie mnie we właściwym kierunku. Po pewnym zastanowieniu, oto co wymyśliłem. To prostsze, ale proszę powiedz mi, jeśli widzisz wadę.

 var theirObj = new TheirClass(); 
     var t = theirObj.GetType(); 
     var fooBar = t.GetMethod("FooBar"); // Byte& FooBar() 

     DynamicMethod dm = new DynamicMethod(
      "MyFooBar", 
      MethodAttributes.Public | MethodAttributes.Static, 
      CallingConventions.Standard, 
      typeof(IntPtr), 
      new Type[] { typeof(TheirClass) }, 
      typeof(TheirClass), 
      true); 

     var ilg = dm.GetILGenerator(); 
     ilg.Emit(OpCodes.Ldarg_0); 
     ilg.Emit(OpCodes.Call, foobar); 
     ilg.Emit(OpCodes.Ret); 

     var del = dm.CreateDelegate(typeof(Func<TheirClass,IntPtr>)); 
     var ret = (IntPtr)del.DynamicInvoke(theirObject); 

     byte[] buf = new byte[theirObj.FooBarSize()]; //no need for reflection/IL here 
     // not sure about the following, it works, but should it be inside an "unsafe" section? 
     Marshal.Copy(ret, buf, 0, buf.Length); 

kładę prostego owijkę wokół sposobu naruszającego i IL nie obchodzi, że traktuję bajt & jako IntPtr. Nadal nie mam pewności, czy wykonać kopię bez niebezpiecznego opakowania. Ale na razie jest to dobre.

+0

Och, to sprytnie! To ryzykowne. Środowisko wykonawcze może (i robi) przenosić obiekty w pamięci, chyba że zablokujesz je do stałego adresu, a jeśli tak się stanie, surowe wskaźniki nie zostaną zaktualizowane do nowej lokalizacji. To pokazuje, że moja odpowiedź jest bardziej skomplikowana niż to konieczne. Bezskutecznie starałem się unikać tworzenia nowej dynamicznej metody dla każdej zapakowanej funkcji, ale oczywiście mogę po prostu zakodować "MethodInfo" dokładnie tak, jak robisz. – hvd

Powiązane problemy