Hey All, I think I figured out what I was doing wrong.
Here is the snippet of code that was incorrect: var proxyReturnRefOpen = injectorModuleDef.GetType(typeof(Injector).FullName ).Methods .First(m => m.FullName == "T TestInjector.Injector::ReturnProxy(T)" ); var genericArg = method.ReturnType.Resolve(); var proxyReturnRefClosed = new GenericInstanceMethod(proxyReturnRefOpen); proxyReturnRefClosed.GenericArguments.Add(genericArg); var proxyReturnInstruction = processor.Create(OpCodes.Call, method.Module. Import(proxyReturnRefClosed)); The problem was, that I was passing proxyReturnRefClosed into the Import method. What I should have done, was pass proxyReturnRefOpen to the Import method, and then instantiate the GenericInstanceMethod class based on the MethodReference returned by Import. Here is a correct implementation that doesn't throw an exception: var genericArg = method.ReturnType; var proxyReturnRef = injectorModuleDef.GetType(typeof(Injector).FullName). Methods .First(m => m.FullName == "T TestInjector.Injector::ReturnProxy(T)" ); var proxyReturnRefOpen = method.Module.Import(proxyReturnRef); var proxyReturnRefClosed = new GenericInstanceMethod(proxyReturnRefOpen); proxyReturnRefClosed.GenericArguments.Add(genericArg); var proxyReturnInstruction = processor.Create(OpCodes.Call, proxyReturnRefClosed); Thanks for your help guys -- Camden Reslink On Monday, October 23, 2017 at 12:52:44 AM UTC-4, Camden Reslink wrote: > > Hey Jb, > > Thank you for your reply. When I try to change the code, so it reads: > > var genericArg = method.ReturnType; // No Resolve() called here. > > When the value of method.ReturnType defined above has either of the > following properties true: IsGenericInstance, or IsGenericParameter, I > receive a null reference exception originating from Cecil on the following > line: > > proxyReturnInstruction = processor.Create(OpCodes.Call, method.Module. > Import(proxyReturnRefClosed)); > > Here is the stack trace: > at Mono.Cecil.ImportGenericContext.MethodParameter(String method, Int32 > position) > at Mono.Cecil.MetadataImporter.ImportTypeSpecification(TypeReference > type, ImportGenericContext context) > at Mono.Cecil.MetadataImporter.ImportType(TypeReference type, > ImportGenericContext context) > at Mono.Cecil.MetadataImporter.ImportTypeSpecification(TypeReference > type, ImportGenericContext context) > at Mono.Cecil.MetadataImporter.ImportType(TypeReference type, > ImportGenericContext context) > at Mono.Cecil.MetadataImporter.ImportMethodSpecification( > MethodReference method, ImportGenericContext context) > at Mono.Cecil.MetadataImporter.ImportMethod(MethodReference method, > ImportGenericContext context) > at Mono.Cecil.ModuleDefinition.Import(MethodReference method, > IGenericParameterProvider context) > at Mono.Cecil.ModuleDefinition.Import(MethodReference method) > at Profiler.Inject.Injectors.ReturnInjector.InjectReturnProxy( > MethodDefinition method) > > In the case where the return type that I'm trying to intercept is > "System.Collections.Generic.List`1<T>", Here is what the FullName property > of proxyReturnRefClosed looks like: > T TestInjector.Injector::ReturnProxy<System.Collections.Generic.List > `1<T>>(T) > > I'm thinking it should actually look something like: > System.Collections.Generic.List`1<T> > TestInjector.Injector::ReturnProxy<System.Collections.Generic.List`1<T>>( > System.Collections.Generic.List`1<T>) > > Because, the proxy method should be accepting a List<T> and returning a > List<T>. Should I be looping through proxyReturnRefClosed.Parameters, and > also somehow alter proxyReturnRefClosed.ReturnType (because above it looks > like the generic argument gets set correctly, but the types of the > parameters/return type stays the same)? > > I tried something like that here: > var genericArg = method.ReturnType; > var proxyReturnRefClosed = new GenericInstanceMethod(proxyReturnRefOpen); > proxyReturnRefClosed.GenericArguments.Add(genericArg); > proxyReturnRefClosed.ReturnType = genericArg; > proxyReturnRefClosed.Parameters[0].ParameterType = genericArg; > proxyReturnInstruction = processor.Create(OpCodes.Call, method.Module. > Import(proxyReturnRefClosed)); > > This gives me the proxyReturnRefClosed FullName property that I expect: > System.Collections.Generic.List`1<T> > TestInjector.Injector::ReturnProxy<System.Collections.Generic.List`1<T>>( > System.Collections.Generic.List`1<T>) > > But alas, I get the following exception at the same line as the above > exception => System.InvalidOperationException: 'Operation is not valid due > to the current state of the object.' > Here is the stack trace for that exception: > at Mono.Cecil.ImportGenericContext.MethodParameter(String method, Int32 > position) > at Mono.Cecil.MetadataImporter.ImportTypeSpecification(TypeReference > type, ImportGenericContext context) > at Mono.Cecil.MetadataImporter.ImportType(TypeReference type, > ImportGenericContext context) > at Mono.Cecil.MetadataImporter.ImportTypeSpecification(TypeReference > type, ImportGenericContext context) > at Mono.Cecil.MetadataImporter.ImportType(TypeReference type, > ImportGenericContext context) > at Mono.Cecil.MetadataImporter.ImportMethod(MethodReference method, > ImportGenericContext context) > at Mono.Cecil.MetadataImporter.ImportMethodSpecification( > MethodReference method, ImportGenericContext context) > at Mono.Cecil.MetadataImporter.ImportMethod(MethodReference method, > ImportGenericContext context) > at Mono.Cecil.ModuleDefinition.Import(MethodReference method, > IGenericParameterProvider context) > at Mono.Cecil.ModuleDefinition.Import(MethodReference method) > at Profiler.Inject.Injectors.ReturnInjector.InjectReturnProxy( > MethodDefinition method) > > I've tried debugging/stepping through the Cecil source, but didn't have > much luck figuring out where I'm going wrong (although the context variable > in the method calls in the stack traces always seems to be null, which I > thought was curious). This is a complicated edge case, so any help would be > very much appreciated. > > Thanks, > Camden Reslink > > On Friday, October 6, 2017 at 3:34:08 PM UTC-4, Jb Evain wrote: >> >> Hey Camden, >> >> In: >> >> var genericArg = method.ReturnType.Resolve(); >> var proxyReturnRefClosed = new GenericInstanceMethod( >> proxyReturnRefOpen); >> proxyReturnRefClosed.GenericArguments.Add(genericArg); >> >> Why do you Resolve the return type? I imagine you want to return exactly >> the type of method.ReturnType, why don't you use that directly? >> >> Jb >> >> On Mon, Oct 2, 2017 at 11:41 AM, Camden Reslink <camden...@gmail.com> >> wrote: >> >>> Hello, >>> >>> I'm using version 0.9.6.4 consumed as a NuGet package from nuget.org. >>> >>> I'm trying to inject a call to a static method, which intercepts values >>> that would have been returned, and logs/traces/manipulates them in some way >>> and then returns them. An example of an intercepting method could look like >>> this: >>> public static T1 ReturnProxy<T1>(T1 value) >>> { >>> // Do something useful with value, like logging it. >>> var timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff", >>> CultureInfo.InvariantCulture); >>> StackTrace stackTrace = new StackTrace(); >>> var callingMethod = stackTrace.GetFrame(1).GetMethod(); >>> File.AppendAllLines(@"C:\temp\test_app.txt", new string[] { >>> timestamp + ", " + callingMethod.Name + ", " + Newtonsoft.Json. >>> JsonConvert.SerializeObject(value) }); >>> >>> // Now return it just like the calling method would have without >>> this method intercepting it >>> return value; >>> } >>> >>> >>> And here is what a before/after would look like for injecting the >>> instructions into the calling method: >>> // This method is in the assembly being injected into >>> >>> //BEFORE >>> public T2 MethodWithAGenericParameter<T2>(int someVariable) >>> { >>> T2 obj = (T2)Activator.CreateInstance(typeof(T2)); >>> obj.GetType().GetProperty("MyProperty").SetValue(obj, someVariable); >>> return obj; >>> } >>> >>> //AFTER >>> public T2 MethodWithAGenericParameter<T2>(int someVariable) >>> { >>> T2 obj = (T2)Activator.CreateInstance(typeof(T2)); >>> obj.GetType().GetProperty("MyProperty").SetValue(obj, someVariable); >>> return Injector.ReturnProxy<T2>(obj); >>> } >>> >>> >>> In the case where the return type of the calling method is not generic, >>> the following code is working correctly to inject the call instructions >>> into the methods: >>> public static void InjectReturnProxy(MethodDefinition method) >>> { >>> var injectorModuleDef = ModuleDefinition.ReadModule(typeof(Injector >>> ).Module.FullyQualifiedName); >>> var proxyReturnRefOpen = injectorModuleDef.GetType(typeof(Injector). >>> FullName).Methods >>> .First(m => m.FullName == "T >>> TestInjector.Injector::ReturnProxy(T)"); >>> ILProcessor processor = method.Body.GetILProcessor(); >>> Instruction proxyReturnInstruction = null; >>> >>> var genericArg = method.ReturnType.Resolve(); >>> var proxyReturnRefClosed = new GenericInstanceMethod( >>> proxyReturnRefOpen); >>> proxyReturnRefClosed.GenericArguments.Add(genericArg); >>> proxyReturnInstruction = processor.Create(OpCodes.Call, method. >>> Module.Import(proxyReturnRefClosed)); >>> >>> var returnInstructions = method.Body.Instructions >>> .Where(i => i.OpCode.Name == "ret").ToArray(); >>> >>> method.Body.OptimizeMacros(); >>> for (var i = 0; i < returnInstructions.Length; i++) >>> { >>> var instruction = returnInstructions[i]; >>> processor.InsertBefore(instruction, proxyReturnInstruction); >>> } >>> method.Body.SimplifyMacros(); >>> } >>> >>> However, when the return type of the calling method is generic, like the >>> MethodWithAGenericParameter example above, I can't seem to get the >>> correct return type to pass as a generic type argument to the >>> ReturnProxy<T1> static method. The call to method.ReturnType.Resolve() >>> gives >>> a value of null in that case, and the type of method.ReturnType is >>> Mono.Cecil.GenericParameter. I also see problems with a return type of a >>> generic array (like T[]). I haven't tried to resolve when the return >>> type of the calling method is a generic type parameter at the class level >>> instead of the method level, but I suspect I'll have similar issues in that >>> case. Any thoughts on how to perform the above injection of a method call >>> passing in as the generic argument the generic parameter of the parent >>> method? Thanks for any help that could be provided! >>> >>> -- >>> -- >>> -- >>> mono-cecil >>> --- >>> You received this message because you are subscribed to the Google >>> Groups "mono-cecil" group. >>> To unsubscribe from this group and stop receiving emails from it, send >>> an email to mono-cecil+...@googlegroups.com. >>> For more options, visit https://groups.google.com/d/optout. >>> >> >> -- -- -- mono-cecil --- You received this message because you are subscribed to the Google Groups "mono-cecil" group. To unsubscribe from this group and stop receiving emails from it, send an email to mono-cecil+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.