public bool PatchAssembly() { try { MessageIntegration.Info("CSCli is searching for calls to replace. Assemblyname: " + _assembly.FullName); var module = _assembly.MainModule; foreach (var type in module.Types) { ProcessType(type); } MessageIntegration.Info(String.Format("CSCli processed {0} types.", module.Types.Count)); MessageIntegration.Info(String.Format("CSCli replaced {0} method-calls by Calli-instruction.", _replacedCallsCount)); MessageIntegration.Info(String.Format("Cleaning up assembly. {0} types to remove from assembly.", _typesToRemove.Count)); foreach (var typeToRemove in _typesToRemove) { module.Types.Remove(typeToRemove); } } catch (Exception ex) { MessageIntegration.WriteError("Unknown error: " + ex.ToString()); return(false); } return(true); }
private void ProcessMethod(MethodDefinition method) { if (method.HasBody) { ILProcessor ilProcessor = method.Body.GetILProcessor(); //process every instruction of the methods body for (int n = 0; n < ilProcessor.Body.Instructions.Count; n++) //foreach won't work because iterator length is bigger than count?? { var instruction = ilProcessor.Body.Instructions[n]; //check whether the instruction is a call to a method if (instruction.OpCode.Code == Code.Call && instruction.Operand is MethodReference) { //get the called method MethodReference methodDescription = (MethodReference)instruction.Operand; //get the attributes of the called method var attributes = GetAttributes(methodDescription.Resolve()); //check whether the called method is marked with the given calli attribute if (attributes.Contains(_calliAttributeName)) { MessageIntegration.Info("Patching [{0}] @ [{1}].", method.ToString(), instruction.ToString()); if (!method.Name.EndsWith("Native")) { MessageIntegration.Info(String.Format("CallI-Comimport Methodnames should end with a \"Native\". Method: \"{0}\".", method.FullName)); } //create a callsite for the calli instruction using stdcall calling convention var callSite = new CallSite(methodDescription.ReturnType) { CallingConvention = MethodCallingConvention.StdCall }; //iterate through every parameter of the original method-call for (int j = 0; j < methodDescription.Parameters.Count - 1; j++) //foreach won't work because iterator length is bigger than count?? { var p = methodDescription.Parameters[j]; if (p.ParameterType.FullName == "System.Boolean") { MessageIntegration.WriteWarning("Native bool has a size of 4 bytes. Make sure to use a 16bit Integer for \"VARIANT_BOOL\" and a 32bit Integer for \"BOOL\".", "CI0001"); } //append every parameter of the method-call to the callsite of the calli instruction callSite.Parameters.Add(p); } //create a calli-instruction including the just built callSite var calliInstruction = ilProcessor.Create(OpCodes.Calli, callSite); //replace the method-call by the calli-instruction ilProcessor.Replace(instruction, calliInstruction); _replacedCallsCount++; } } } } }
private static void Main(string[] args) { string calliAttributeName = "CalliAttribute"; string removeTypeAttributeName = "RemoveTypeAttribute"; string filename = null; string pdbfile = null; MessageIntegration.Info("CSCli started."); MessageIntegration.Info("CSCli was copied from http://www.codeproject.com/Articles/644130/NET-COM-Interop-using-Postbuild."); MessageIntegration.Info(String.Empty); //new line MessageIntegration.Info(String.Format("CSCli-Argments: [{0}].", String.Concat(args.Select(x => x + " ")).Trim())); /* * Parse parameters */ foreach (var a in args) { if (a.StartsWith("-file:")) { filename = a.Substring("-file:".Length); MessageIntegration.Info("Filename: " + filename); } else if (a.StartsWith("-c:")) { calliAttributeName = a.Substring("-c:".Length); } else if (a.StartsWith("-r:")) { removeTypeAttributeName = a.Substring("-r:".Length); } else if (a.StartsWith("-pdb:")) { pdbfile = a.Substring("-pdb:".Length); } else { MessageIntegration.WriteWarning(String.Format("Unknown parameter: \"{0}\".", a)); } } /* * Load and process assembly */ if (!File.Exists(filename)) { MessageIntegration.WriteError(String.Format("Could not find file \"{0}\".", filename)); Environment.Exit(-1); } WriterParameters wp = new WriterParameters(); ReaderParameters rp = new ReaderParameters(); var strongNameKey = Path.ChangeExtension(filename, "snk"); if (File.Exists(strongNameKey)) { MessageIntegration.Info("Signing with Key : " + strongNameKey); wp.StrongNameKeyPair = new StrongNameKeyPair(File.OpenRead(strongNameKey)); } //check whether the pdbfile has been passed through application parameters if (pdbfile == null) { //if not use the default pdbfilepath by changing the extension of the assembly to .pdb pdbfile = Path.ChangeExtension(filename, "pdb"); } //check whether the original pdb-file exists bool generatePdb = File.Exists(pdbfile); //if the original pdb-file exists -> prepare for rewriting the symbols file wp.WriteSymbols = generatePdb; rp.ReadSymbols = generatePdb; if (rp.ReadSymbols) { rp.SymbolReaderProvider = new PdbReaderProvider(); } MessageIntegration.Info("Generating pdb: " + generatePdb.ToString()); //open assembly var assembly = AssemblyDefinition.ReadAssembly(filename, rp); //add the directory assembly directory as search directory to resolve referenced assemblies ((BaseAssemblyResolver)assembly.MainModule.AssemblyResolver).AddSearchDirectory(Path.GetDirectoryName(filename)); //path the assembly AssemblyPatcher patcher = new AssemblyPatcher(assembly, calliAttributeName, removeTypeAttributeName); if (patcher.PatchAssembly()) { try { //if the assembly was patched successfully -> replace the old assembly file with the new, patched assembly file //the symbols file will be created automatically File.Delete(filename); assembly.Write(filename, wp); } catch (Exception ex) { MessageIntegration.WriteError("Creating new assembly failed: " + ex.ToString()); Environment.Exit(-1); } MessageIntegration.Info(String.Format("CSCli patched assembly \"{0}\" successfully.", Path.GetFileName(filename))); Environment.Exit(0); } else { MessageIntegration.WriteError(String.Format("\"{0}\" could not be patched.", filename)); Environment.Exit(-1); } }