@@ -70,7 +70,7 @@ static TypeDefinition CreateHookEventArgs(TypeDefinition hookType, MethodDefinit
7070 "" , //hookType.Namespace,
7171 hookEventName ,
7272 TypeAttributes . Class | TypeAttributes . BeforeFieldInit | TypeAttributes . NestedPublic | TypeAttributes . Sealed ,
73- hookType . Module . ImportReference ( typeof ( EventArgs ) )
73+ hookType . Module . ImportReference ( typeof ( Object ) )
7474 ) ;
7575 hookType . NestedTypes . Add ( hookEvent ) ;
7676
@@ -88,28 +88,83 @@ static TypeDefinition CreateHookEventArgs(TypeDefinition hookType, MethodDefinit
8888 // for each parameter in the method, create a field
8989 foreach ( var param in hookDefinition . Parameters )
9090 {
91- var paramType = param . ParameterType ;
91+ var paramType = param . ParameterType . IsByReference ? param . ParameterType . GetElementType ( ) : param . ParameterType ;
9292 FieldDefinition paramField = new ( param . Name , FieldAttributes . Public , paramType ) ;
9393 hookEvent . Fields . Add ( paramField ) ;
9494 }
9595
9696 // create ctor, calling base ctor
97- MethodDefinition ctor = new ( ".ctor" , MethodAttributes . Public | MethodAttributes . HideBySig | MethodAttributes . SpecialName | MethodAttributes . RTSpecialName , hookDefinition . Module . TypeSystem . Void ) ;
98- ctor . Body = new MethodBody ( ctor ) ;
97+ MethodDefinition ctor = new ( ".ctor" , MethodAttributes . Public | MethodAttributes . HideBySig | MethodAttributes . SpecialName | MethodAttributes . RTSpecialName , hookDefinition . Module . TypeSystem . Void ) ;
98+ //ctor.Body = new(ctor)
99+ //{
100+ // InitLocals = true
101+ //};
99102 var il = ctor . Body . GetILProcessor ( ) ;
103+
100104 il . Emit ( OpCodes . Ldarg_0 ) ;
101105 il . Emit ( OpCodes . Call , hookDefinition . Module . ImportReference ( typeof ( object ) . GetConstructors ( ) . Single ( ) ) ) ;
106+
102107 // Set ContinueExecution to true
103108 il . Emit ( OpCodes . Ldarg_0 ) ;
104109 il . Emit ( OpCodes . Ldc_I4_1 ) ;
105110 il . Emit ( OpCodes . Stfld , resultField ) ;
111+
112+ //// Set the defaults for HookReturnValue, e.g. var = default(var)
113+ //if (hasReturnValue)
114+ //{
115+ // il.Emit(OpCodes.Ldarg_0);
116+ // il.Append(CreateDefaultValueInstruction(hookDefinition.ReturnType));
117+ // il.Emit(OpCodes.Stfld, hookEvent.Fields.Single(x => x.Name == HookReturnValueName));
118+ //}
119+
106120 il . Emit ( OpCodes . Ret ) ;
107121 hookEvent . Methods . Add ( ctor ) ;
108122
109123 hookEvent . Fields . Add ( resultField ) ;
110124 return hookEvent ;
111125 }
112126
127+ public static Instruction CreateDefaultValueInstruction ( TypeReference type )
128+ {
129+ return type . MetadataType switch
130+ {
131+ MetadataType . Boolean or MetadataType . Byte or MetadataType . SByte or MetadataType . Int16 or MetadataType . UInt16 or MetadataType . Int32 or MetadataType . UInt32 => Instruction . Create ( OpCodes . Ldc_I4_0 ) ,
132+ MetadataType . Int64 or MetadataType . UInt64 => Instruction . Create ( OpCodes . Ldc_I8 , 0L ) ,
133+ MetadataType . Single => Instruction . Create ( OpCodes . Ldc_R4 , 0f ) ,
134+ MetadataType . Double => Instruction . Create ( OpCodes . Ldc_R8 , 0d ) ,
135+ MetadataType . ValueType => Instruction . Create ( OpCodes . Initobj , type ) ,
136+ _ => Instruction . Create ( OpCodes . Ldnull )
137+ } ;
138+ }
139+
140+ public static Instruction CreateLoadIndirectInstruction ( TypeReference type )
141+ {
142+ return type . MetadataType switch
143+ {
144+ MetadataType . Boolean => Instruction . Create ( OpCodes . Ldind_U1 ) ,
145+ MetadataType . Byte or MetadataType . SByte or MetadataType . Int16 or MetadataType . UInt16 or MetadataType . Int32 or MetadataType . UInt32 => Instruction . Create ( OpCodes . Ldind_I4 ) ,
146+ MetadataType . Int64 or MetadataType . UInt64 => Instruction . Create ( OpCodes . Ldind_I8 ) ,
147+ MetadataType . Single => Instruction . Create ( OpCodes . Ldind_R4 ) ,
148+ MetadataType . Double => Instruction . Create ( OpCodes . Ldind_R8 ) ,
149+ MetadataType . ValueType => Instruction . Create ( OpCodes . Ldobj , type ) ,
150+ _ => Instruction . Create ( OpCodes . Ldind_Ref )
151+ } ;
152+ }
153+
154+ public static Instruction CreateStoreIndirectFunction ( TypeReference type )
155+ {
156+ return type . MetadataType switch
157+ {
158+ MetadataType . Boolean => Instruction . Create ( OpCodes . Stind_I1 ) ,
159+ MetadataType . Byte or MetadataType . SByte or MetadataType . Int16 or MetadataType . UInt16 or MetadataType . Int32 or MetadataType . UInt32 => Instruction . Create ( OpCodes . Stind_I4 ) ,
160+ MetadataType . Int64 or MetadataType . UInt64 => Instruction . Create ( OpCodes . Stind_I8 ) ,
161+ MetadataType . Single => Instruction . Create ( OpCodes . Stind_R4 ) ,
162+ MetadataType . Double => Instruction . Create ( OpCodes . Stind_R8 ) ,
163+ MetadataType . ValueType => Instruction . Create ( OpCodes . Stobj , type ) ,
164+ _ => Instruction . Create ( OpCodes . Stind_Ref )
165+ } ;
166+ }
167+
113168 static MethodDefinition CreateInvokeMethod ( TypeDefinition hookType , FieldDefinition eventField , TypeDefinition hookEventArgsType , string ? name = null )
114169 {
115170 var methodName = name ?? $ "Invoke{ eventField . Name . TrimStart ( '_' ) } ";
@@ -139,7 +194,8 @@ static MethodDefinition CreateInvokeMethod(TypeDefinition hookType, FieldDefinit
139194 // instead of an event args, intake the parameters so each call doesnt need to new up itself.
140195 foreach ( var field in hookEventArgsType . Fields . Where ( x => x . Name != ContinueExecutionName && x . Name != HookReturnValueName ) )
141196 {
142- ParameterDefinition prm = new ( field . Name , ParameterAttributes . None , field . FieldType ) ;
197+ var fieldType = field . FieldType is ByReferenceType byRef ? byRef . ElementType : field . FieldType ;
198+ ParameterDefinition prm = new ( field . Name , ParameterAttributes . None , fieldType ) ;
143199 invokeMethod . Parameters . Add ( prm ) ;
144200 }
145201
@@ -162,42 +218,39 @@ static MethodDefinition CreateInvokeMethod(TypeDefinition hookType, FieldDefinit
162218 } ;
163219
164220 // Add parameters to the invokeMethodReference
165- invokeMethodReference . Parameters . Add ( new ( hookType . Module . TypeSystem . Object ) ) ; // sender
166- invokeMethodReference . Parameters . Add ( new ( eventHandlerInvokeMethod . Parameters [ 1 ] . ParameterType ) ) ; // args - see EventHandler<>.Invoke, il is !0
221+ invokeMethodReference . Parameters . Add ( new ( hookType . Module . TypeSystem . Object ) ) ; // sender
222+ invokeMethodReference . Parameters . Add ( new ( eventHandlerInvokeMethod . Parameters [ 1 ] . ParameterType ) ) ; // args - see EventHandler<>.Invoke, il is !0
167223
168224 // Generate IL for the Invoke method
169225 var il = invokeMethod . Body . GetILProcessor ( ) ;
170226 var returnLabel = il . Create ( OpCodes . Ldloc_0 ) ;
171227
172228 // Create the event args instance from the method parameters
173- VariableDefinition vrb = new ( hookEventArgsType ) ;
229+ VariableDefinition vrb = new ( hookEventArgsType ) ;
174230 invokeMethod . Body . Variables . Add ( vrb ) ;
231+ invokeMethod . Body . InitLocals = true ;
175232 il . Emit ( OpCodes . Newobj , hookEventArgsType . Methods . Single ( x => x . Name == ".ctor" ) ) ; // Create a new instance of the event args
176233 // Set the fields of the event args instance
177234 foreach ( var prm in invokeMethod . Parameters . Skip ( 1 /*sender*/ ) )
178235 {
179- il . Emit ( OpCodes . Dup ) ; // Load the event args instance
180- il . Emit ( OpCodes . Ldarg , prm ) ; // Load the parameter
236+ il . Emit ( OpCodes . Dup ) ; // Load the event args instance
237+ il . Emit ( OpCodes . Ldarg , prm ) ; // Load the parameter
181238 il . Emit ( OpCodes . Stfld , hookEventArgsType . Fields . Single ( x => x . Name == prm . Name ) ) ; // Set the field
182239 }
183- il . Emit ( OpCodes . Stloc_0 ) ; // Store the event args instance
240+ il . Emit ( OpCodes . Stloc_0 ) ; // Store the event args instance
184241
185242 // Check if the event is not null
186- il . Emit ( OpCodes . Ldsfld , eventField ) ; // Load the static event field
187- il . Emit ( OpCodes . Brfalse_S , returnLabel ) ; // If null, skip invocation
243+ il . Emit ( OpCodes . Ldsfld , eventField ) ; // Load the static event field
244+ il . Emit ( OpCodes . Brfalse_S , returnLabel ) ; // If null, skip invocation
188245
189246 // Invoke the event delegate
190- il . Emit ( OpCodes . Ldsfld , eventField ) ; // Load the static event field
191- il . Emit ( OpCodes . Ldarg_0 ) ; // Load the sender (first parameter)
192- //il.Emit(OpCodes.Ldarg_1); // Load the args (second parameter)
193- il . Emit ( OpCodes . Ldloc_0 ) ; // Load the event args instance
194- il . Emit ( OpCodes . Callvirt , invokeMethodReference ) ; // Call the Invoke method on the delegate
247+ il . Emit ( OpCodes . Ldsfld , eventField ) ; // Load the static event field
248+ il . Emit ( OpCodes . Ldarg_0 ) ; // Load the sender
249+ il . Emit ( OpCodes . Ldloc_0 ) ; // Load the event args instance
250+ il . Emit ( OpCodes . Callvirt , invokeMethodReference ) ; // Call the Invoke method on the delegate
195251
196- // Return args.Result
197- il . Append ( returnLabel ) ;
198- //il.Emit(OpCodes.Ldfld, hookEventArgsType.Fields.Single(x => x.Name == "ContinueExecution")); // Load the Result field
199- // Return the event args variable
200- il . Emit ( OpCodes . Ldloc_0 ) ;
252+ // Return args
253+ il . Append ( returnLabel ) ; // Return the event args (Ldloc_0 from earlier)
201254 il . Emit ( OpCodes . Ret ) ;
202255
203256 // Add the Invoke method to the type
@@ -211,14 +264,15 @@ static MethodDefinition CreateInvokeMethod(TypeDefinition hookType, FieldDefinit
211264 /// </summary>
212265 /// <param name="original"></param>
213266 /// <param name="eventInvoke"></param>
267+ /// <param name="name"></param>
214268 /// <returns></returns>
215- static MethodDefinition CreateReplacement ( MethodDefinition original , MethodDefinition eventInvoke )
269+ static MethodDefinition CreateReplacement ( MethodDefinition original , MethodDefinition eventInvoke , string ? name = null )
216270 {
217271 var eventArgs = eventInvoke . ReturnType . Resolve ( ) ;
218272 var hookReturnValueField = eventArgs . Fields . SingleOrDefault ( x => x . Name == HookReturnValueName ) ;
219273
220274 MethodDefinition methodDefinition = new (
221- original . Name ,
275+ name ?? original . Name ,
222276 original . Attributes ,
223277 original . ReturnType
224278 ) ;
@@ -231,29 +285,68 @@ static MethodDefinition CreateReplacement(MethodDefinition original, MethodDefin
231285 VariableDefinition eventArgsVariable = new ( eventInvoke . ReturnType ) ;
232286 methodDefinition . Body . Variables . Add ( eventArgsVariable ) ;
233287
288+ // if any out parameters, initialise them with default values
289+ foreach ( var param in methodDefinition . Parameters . Where ( x => x . IsOut ) )
290+ {
291+ var type = param . ParameterType . GetElementType ( ) ;
292+ il . Emit ( OpCodes . Ldarg_S , param ) ;
293+ var defaultValue = CreateDefaultValueInstruction ( type ) ;
294+ il . Append ( defaultValue ) ;
295+ //il.Emit(OpCodes.Stind_Ref);
296+ if ( defaultValue . OpCode != OpCodes . Initobj )
297+ il . Append ( CreateStoreIndirectFunction ( type ) ) ;
298+ //il.Emit(OpCodes.Stind_I1);
299+ }
300+
234301 il . Emit ( original . IsStatic ? OpCodes . Ldnull : OpCodes . Ldarg_0 ) ;
235302 for ( int i = 0 ; i < methodDefinition . Parameters . Count ; i ++ )
236- il . Emit ( OpCodes . Ldarg , methodDefinition . Parameters [ i ] ) ;
303+ {
304+ var isByRef = methodDefinition . Parameters [ i ] . ParameterType . IsByReference ;
305+ var opCode = isByRef ? OpCodes . Ldarg_S : OpCodes . Ldarg ;
306+ il . Emit ( opCode , methodDefinition . Parameters [ i ] ) ;
307+ if ( isByRef )
308+ il . Append ( CreateLoadIndirectInstruction ( methodDefinition . Parameters [ i ] . ParameterType . GetElementType ( ) ) ) ;
309+ }
237310 il . Emit ( OpCodes . Call , eventInvoke ) ;
238311
239312 // store the event args in a local variable
240313 il . Emit ( OpCodes . Stloc , eventArgsVariable ) ;
241314
242- // use ContinueExecutionName to determine whether to continue or not
315+ // if any out parameters, set them from the args
316+ foreach ( var param in methodDefinition . Parameters . Where ( x => x . ParameterType . IsByReference ) )
317+ {
318+ var field = eventArgs . Fields . Single ( x => x . Name == param . Name ) ;
319+ il . Emit ( OpCodes . Ldarg_S , param ) ;
243320 il . Emit ( OpCodes . Ldloc , eventArgsVariable ) ;
321+ il . Emit ( OpCodes . Ldfld , field ) ;
322+ //il.Emit(OpCodes.Stind_Ref);
323+ il . Append ( CreateStoreIndirectFunction ( field . FieldType . GetElementType ( ) ) ) ;
324+ }
325+
326+ // use ContinueExecutionName to determine whether to continue or not
327+ il . Emit ( OpCodes . Ldloc , eventArgsVariable ) ;
244328 il . Emit ( OpCodes . Ldfld , eventInvoke . ReturnType . Resolve ( ) . Fields . Single ( x => x . Name == ContinueExecutionName ) ) ;
245329
246330 // the event invoke is a boolen, if false, return else invoke the original method
247331 var returnLabel = hookReturnValueField is not null ? il . Create ( OpCodes . Ldloc , eventArgsVariable ) : il . Create ( OpCodes . Ret ) ;
248-
249332 il . Emit ( OpCodes . Brfalse_S , returnLabel ) ;
250-
251- il . Emit ( OpCodes . Ldarg_0 ) ;
333+
334+ if ( ! original . IsStatic )
335+ il . Emit ( OpCodes . Ldarg_0 ) ;
252336 // for each event arg field, load it onto the stack to the original method
253337 foreach ( var field in eventArgs . Fields . Where ( x => x . Name != ContinueExecutionName && x . Name != HookReturnValueName ) )
254338 {
255- il . Emit ( OpCodes . Ldloc , eventArgsVariable ) ;
256- il . Emit ( OpCodes . Ldfld , field ) ;
339+ var parameter = methodDefinition . Parameters . Single ( x => x . Name == field . Name ) ;
340+ // if byref, pass through original args which are updated by the event
341+ if ( parameter . ParameterType . IsByReference )
342+ {
343+ il . Emit ( OpCodes . Ldarg_S , parameter ) ;
344+ }
345+ else
346+ {
347+ il . Emit ( OpCodes . Ldloc , eventArgsVariable ) ;
348+ il . Emit ( OpCodes . Ldfld , field ) ;
349+ }
257350 }
258351 il . Emit ( OpCodes . Call , original . Module . ImportReference ( original ) ) ;
259352 il . Emit ( OpCodes . Ret ) ;
@@ -290,15 +383,36 @@ public static void CreateHook(this MethodDefinition definition, ModFwModder modd
290383 var ( hookField , _) = definition . CreateEvent ( hookType , hookEventArgs , name : uniqueName ) ;
291384 var newMethod = CreateInvokeMethod ( hookType , hookField , hookEventArgs , name : $ "Invoke{ uniqueName } ") ;
292385
293- var replacement = CreateReplacement ( definition , newMethod ) ;
386+ //var originalName = definition.Name;
387+ //definition.Name = $"hooked_{definition.Name}";
388+
389+ var replacement = CreateReplacement ( definition , newMethod , name : $ "hooked_{ definition . Name } ") ;
390+
294391 definition . DeclaringType . Methods . Add ( replacement ) ;
295392
296- // rename the original method
297- definition . Name = $ "hooked_{ definition . Name } ";
393+ //// rename the original method
394+ // definition.Name = $"hooked_{definition.Name}";
298395
299396 // remove any overrides etc
300- definition . Attributes &= ~ MethodAttributes . Virtual ;
301- definition . Attributes &= ~ MethodAttributes . NewSlot ;
397+ replacement . Attributes &= ~ MethodAttributes . Virtual ;
398+ replacement . Attributes &= ~ MethodAttributes . NewSlot ;
399+ replacement . Attributes &= ~ MethodAttributes . SpecialName ;
400+
401+ // swap bodies instead
402+
403+ // swap the method references
404+ foreach ( var instr in replacement . Body . Instructions )
405+ {
406+ if ( instr . Operand is MethodReference mr && mr . Name == definition . Name )
407+ instr . Operand = replacement ;
408+ //mr.Name = replacement.Name;
409+ }
410+
411+ var temp = definition . Body ;
412+ definition . Body = replacement . Body ;
413+ replacement . Body = temp ;
414+
415+ //
302416 }
303417
304418 /// <summary>
@@ -314,7 +428,7 @@ public static void CreateHooks(this TypeDefinition definition, ModFwModder modde
314428 // not a constructor
315429 ! x . IsConstructor &&
316430 // not an event
317- ! ( definition . Events . Any ( evt => evt . AddMethod == x || evt . RemoveMethod == x ) )
431+ ! ( definition . Events . Any ( evt => evt . AddMethod == x || evt . RemoveMethod == x || evt . InvokeMethod == x ) )
318432 ) . ToList ( ) )
319433 method . CreateHook ( modder ) ;
320434 }
0 commit comments