Click here to Skip to main content
15,896,557 members
Articles / Programming Languages / MSIL

Assembly Manipulation and C# / VB.NET Code Injection

Rate me:
Please Sign up or sign in to vote.
4.95/5 (200 votes)
26 Apr 2013MIT5 min read 927.4K   25K   486  
Reflexil is an assembly editor and runs as a plug-in for Reflector or JustDecompile. Reflexil is able to manipulate IL code and save the modified assemblies to disk. Reflexil also supports "on-the-fly" C#/VB.NET code injection.
// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
//     <version>$Revision: 3675 $</version>
// </file>

using System;
using System.Linq;
using System.Collections.Generic;

namespace ICSharpCode.SharpDevelop.Dom
{
	public class DefaultClass : AbstractEntity, IClass, IComparable
	{
		ClassType classType;
		DomRegion region;
		
		ICompilationUnit compilationUnit;
		
		IList<IReturnType> baseTypes;
		
		IList<IClass>    innerClasses;
		IList<IField>    fields;
		IList<IProperty> properties;
		IList<IMethod>   methods;
		IList<IEvent>    events;
		IList<ITypeParameter> typeParameters;
		IUsingScope usingScope;
		
		protected override void FreezeInternal()
		{
			baseTypes = FreezeList(baseTypes);
			innerClasses = FreezeList(innerClasses);
			fields = FreezeList(fields);
			properties = FreezeList(properties);
			methods = FreezeList(methods);
			events = FreezeList(events);
			typeParameters = FreezeList(typeParameters);
			base.FreezeInternal();
		}
		
		/*
		public virtual IClass Unfreeze()
		{
			DefaultClass copy = new DefaultClass(compilationUnit, DeclaringType);
			copy.FullyQualifiedName = this.FullyQualifiedName;
			copy.Attributes.AddRange(this.Attributes);
			copy.BaseTypes.AddRange(this.BaseTypes);
			copy.BodyRegion = this.BodyRegion;
			copy.ClassType = this.ClassType;
			copy.Documentation = this.Documentation;
			copy.Events.AddRange(this.Events);
			copy.Fields.AddRange(this.Fields);
			copy.InnerClasses.AddRange(this.InnerClasses);
			copy.Methods.AddRange(this.Methods);
			copy.Modifiers = this.Modifiers;
			copy.Properties.AddRange(this.Properties);
			copy.Region = this.Region;
			copy.TypeParameters.AddRange(this.TypeParameters);
			copy.UserData = this.UserData;
			return copy;
		}
		 */
		
		byte flags;
		const byte hasPublicOrInternalStaticMembersFlag = 0x02;
		const byte hasExtensionMethodsFlag              = 0x04;
		internal byte Flags {
			get {
				if (flags == 0) {
					flags = 1;
					foreach (IMember m in this.Fields) {
						if (m.IsStatic && (m.IsPublic || m.IsInternal)) {
							flags |= hasPublicOrInternalStaticMembersFlag;
						}
					}
					foreach (IProperty m in this.Properties) {
						if (m.IsStatic && (m.IsPublic || m.IsInternal)) {
							flags |= hasPublicOrInternalStaticMembersFlag;
						}
						if (m.IsExtensionMethod) {
							flags |= hasExtensionMethodsFlag;
						}
					}
					foreach (IMethod m in this.Methods) {
						if (m.IsStatic && (m.IsPublic || m.IsInternal)) {
							flags |= hasPublicOrInternalStaticMembersFlag;
						}
						if (m.IsExtensionMethod) {
							flags |= hasExtensionMethodsFlag;
						}
					}
					foreach (IMember m in this.Events) {
						if (m.IsStatic && (m.IsPublic || m.IsInternal)) {
							flags |= hasPublicOrInternalStaticMembersFlag;
						}
					}
					foreach (IClass c in this.InnerClasses) {
						if (c.IsPublic || c.IsInternal) {
							flags |= hasPublicOrInternalStaticMembersFlag;
						}
					}
				}
				return flags;
			}
			set {
				CheckBeforeMutation();
				flags = value;
			}
		}
		public bool HasPublicOrInternalStaticMembers {
			get {
				return (Flags & hasPublicOrInternalStaticMembersFlag) == hasPublicOrInternalStaticMembersFlag;
			}
		}
		public bool HasExtensionMethods {
			get {
				return (Flags & hasExtensionMethodsFlag) == hasExtensionMethodsFlag;
			}
		}
		
		/// <summary>
		/// Gets the using scope of contains this class.
		/// </summary>
		public IUsingScope UsingScope {
			get { return usingScope; }
			set {
				if (value == null)
					throw new ArgumentNullException("UsingScope");
				CheckBeforeMutation();
				usingScope = value;
			}
		}
		
		public DefaultClass(ICompilationUnit compilationUnit, string fullyQualifiedName) : base(null)
		{
			if (compilationUnit == null)
				throw new ArgumentNullException("compilationUnit");
			if (fullyQualifiedName == null)
				throw new ArgumentNullException("fullyQualifiedName");
			this.compilationUnit = compilationUnit;
			this.FullyQualifiedName = fullyQualifiedName;
			this.UsingScope = compilationUnit.UsingScope;
		}
		
		public DefaultClass(ICompilationUnit compilationUnit, IClass declaringType) : base(declaringType)
		{
			if (compilationUnit == null)
				throw new ArgumentNullException("compilationUnit");
			this.compilationUnit = compilationUnit;
			this.UsingScope = compilationUnit.UsingScope;
		}
		
		public DefaultClass(ICompilationUnit compilationUnit, ClassType classType, ModifierEnum modifiers, DomRegion region, IClass declaringType) : base(declaringType)
		{
			if (compilationUnit == null)
				throw new ArgumentNullException("compilationUnit");
			this.compilationUnit = compilationUnit;
			this.region = region;
			this.classType = classType;
			Modifiers = modifiers;
			this.UsingScope = compilationUnit.UsingScope;
		}
		
		// fields must be volatile to ensure that the optimizer doesn't reorder accesses to it
		// or causes DefaultReturnType to return null when the local copy of this.defaultReturnType is
		// optimized away.
		volatile IReturnType defaultReturnType;
		bool hasCompoundClass;
		
		public IReturnType DefaultReturnType {
			get {
				IReturnType defaultReturnType = this.defaultReturnType;
				if (defaultReturnType == null) {
					lock (this) {
						this.defaultReturnType = defaultReturnType = CreateDefaultReturnType();
					}
				}
				return defaultReturnType;
			}
		}
		
		protected virtual IReturnType CreateDefaultReturnType()
		{
			if (hasCompoundClass) {
				return new GetClassReturnType(ProjectContent, FullyQualifiedName, TypeParameters.Count);
			} else {
				return new DefaultReturnType(this);
			}
		}
		
		bool IClass.HasCompoundClass {
			get { return hasCompoundClass; }
			set {
				if (hasCompoundClass != value) {
					lock (this) {
						hasCompoundClass = value;
						defaultReturnType = null;
					}
				}
			}
		}
		
		public bool IsPartial {
			get {
				return (this.Modifiers & ModifierEnum.Partial) == ModifierEnum.Partial;
			}
			set {
				CheckBeforeMutation();
				if (value)
					this.Modifiers |= ModifierEnum.Partial;
				else
					this.Modifiers &= ~ModifierEnum.Partial;
			}
		}
		
		public IClass GetCompoundClass()
		{
			return this.DefaultReturnType.GetUnderlyingClass() ?? this;
		}
		
		protected override void OnFullyQualifiedNameChanged(EventArgs e)
		{
			base.OnFullyQualifiedNameChanged(e);
			defaultReturnType = null; // re-create default return type
		}
		
		public ICompilationUnit CompilationUnit {
			[System.Diagnostics.DebuggerStepThrough]
			get {
				return compilationUnit;
			}
		}
		
		public IProjectContent ProjectContent {
			[System.Diagnostics.DebuggerStepThrough]
			get {
				return CompilationUnit.ProjectContent;
			}
		}
		
		public ClassType ClassType {
			get {
				return classType;
			}
			set {
				CheckBeforeMutation();
				classType = value;
			}
		}
		
		public DomRegion Region {
			get {
				return region;
			}
			set {
				CheckBeforeMutation();
				region = value;
			}
		}
		
		public override string DotNetName {
			get {
				string fullName;
				if (this.DeclaringType != null) {
					fullName = this.DeclaringType.DotNetName + "+" + this.Name;
				} else {
					fullName = this.FullyQualifiedName;
				}
				IList<ITypeParameter> typeParameters = this.TypeParameters;
				if (typeParameters == null || typeParameters.Count == 0) {
					return fullName;
				} else {
					return fullName + "`" + typeParameters.Count;
				}
			}
		}
		
		public override string DocumentationTag {
			get {
				return "T:" + DotNetName;
			}
		}
		
		public IList<IReturnType> BaseTypes {
			get {
				if (baseTypes == null) {
					baseTypes = new List<IReturnType>();
				}
				return baseTypes;
			}
		}
		
		public virtual IList<IClass> InnerClasses {
			get {
				if (innerClasses == null) {
					innerClasses = new List<IClass>();
				}
				return innerClasses;
			}
		}
		
		public virtual IList<IField> Fields {
			get {
				if (fields == null) {
					fields = new List<IField>();
				}
				return fields;
			}
		}
		
		public virtual IList<IProperty> Properties {
			get {
				if (properties == null) {
					properties = new List<IProperty>();
				}
				return properties;
			}
		}
		
		public virtual IList<IMethod> Methods {
			get {
				if (methods == null) {
					methods = new List<IMethod>();
				}
				return methods;
			}
		}
		
		public virtual IList<IEvent> Events {
			get {
				if (events == null) {
					events = new List<IEvent>();
				}
				return events;
			}
		}
		
		public virtual IList<ITypeParameter> TypeParameters {
			get {
				if (typeParameters == null) {
					typeParameters = new List<ITypeParameter>();
				}
				return typeParameters;
			}
			set {
				CheckBeforeMutation();
				typeParameters = value;
			}
		}
		
		public virtual int CompareTo(IClass value)
		{
			int cmp;
			
			if(0 != (cmp = base.CompareTo((IEntity)value))) {
				return cmp;
			}
			
			if (FullyQualifiedName != null) {
				cmp = FullyQualifiedName.CompareTo(value.FullyQualifiedName);
				if (cmp != 0) {
					return cmp;
				}
				return this.TypeParameters.Count - value.TypeParameters.Count;
			}
			return -1;
		}
		
		int IComparable.CompareTo(object o)
		{
			return CompareTo((IClass)o);
		}
		
		volatile List<IClass> inheritanceTreeCache;
		
		public IEnumerable<IClass> ClassInheritanceTree {
			get {
				List<IClass> visitedList = inheritanceTreeCache;
				if (visitedList != null)
					return visitedList;
				visitedList = new List<IClass>();
				Queue<IReturnType> typesToVisit = new Queue<IReturnType>();
				bool enqueuedLastBaseType = false;
				bool hasErrors = false;
				IClass currentClass = this;
				IReturnType nextType;
				do {
					if (currentClass != null) {
						if (!visitedList.Contains(currentClass)) {
							visitedList.Add(currentClass);
							foreach (IReturnType type in currentClass.BaseTypes) {
								typesToVisit.Enqueue(type);
							}
						}
					}
					if (typesToVisit.Count > 0) {
						nextType = typesToVisit.Dequeue();
					} else {
						nextType = enqueuedLastBaseType ? null : GetBaseTypeByClassType(this);
						enqueuedLastBaseType = true;
					}
					if (nextType != null) {
						currentClass = nextType.GetUnderlyingClass();
						if (currentClass == null)
							hasErrors = true;
					}
				} while (nextType != null);
				
				
				// A SearchType request causes the inheritance tree to be generated, but if it was
				// this classes' base type that caused the SearchType request, the GetUnderlyingClass()
				// will fail and we will produce an incomplete inheritance tree.
				// So we don't cache incomplete inheritance trees for parsed classes (fixes SD2-1474).
				if (!hasErrors || KeepInheritanceTree) {
					inheritanceTreeCache = visitedList;
					if (!KeepInheritanceTree)
						DomCache.RegisterForClear(delegate { inheritanceTreeCache = null; });
				}
				return visitedList;
			}
		}
		
		/// <summary>
		/// Specifies whether to keep the inheritance tree when the DomCache is cleared.
		/// </summary>
		protected bool KeepInheritanceTree = false;
		
		public IReturnType GetBaseType(int index)
		{
			return BaseTypes[index];
		}
		
		IReturnType cachedBaseType;
		
		public IReturnType BaseType {
			get {
				if (cachedBaseType == null) {
					foreach (IReturnType baseType in this.BaseTypes) {
						IClass baseClass = baseType.GetUnderlyingClass();
						if (baseClass != null && baseClass.ClassType == this.ClassType) {
							cachedBaseType = baseType;
							break;
						}
					}
				}
				if (cachedBaseType == null) {
					return GetBaseTypeByClassType(this);
				} else {
					return cachedBaseType;
				}
			}
		}
		
		internal static IReturnType GetBaseTypeByClassType(IClass c)
		{
			switch (c.ClassType) {
				case ClassType.Class:
				case ClassType.Interface:
					if (c.FullyQualifiedName != "System.Object") {
						return c.ProjectContent.SystemTypes.Object;
					}
					break;
				case ClassType.Enum:
					return c.ProjectContent.SystemTypes.Enum;
				case ClassType.Delegate:
					return c.ProjectContent.SystemTypes.Delegate;
				case ClassType.Struct:
					return c.ProjectContent.SystemTypes.ValueType;
			}
			return null;
		}
		
		public IClass BaseClass {
			get {
				foreach (IReturnType baseType in this.BaseTypes) {
					IClass baseClass = baseType.GetUnderlyingClass();
					if (baseClass != null && baseClass.ClassType == this.ClassType)
						return baseClass;
				}
				IReturnType defaultBaseType = GetBaseTypeByClassType(this);
				if (defaultBaseType != null)
					return defaultBaseType.GetUnderlyingClass();
				else
					return null;
			}
		}
		
		public bool IsTypeInInheritanceTree(IClass possibleBaseClass)
		{
			if (possibleBaseClass == null) {
				return false;
			}
			foreach (IClass baseClass in this.ClassInheritanceTree) {
				if (possibleBaseClass.FullyQualifiedName == baseClass.FullyQualifiedName)
					return true;
			}
			return false;
		}
		
		/// <summary>
		/// Searches the member with the specified name. Returns the first member/overload found.
		/// </summary>
		public IMember SearchMember(string memberName, LanguageProperties language)
		{
			if (memberName == null || memberName.Length == 0) {
				return null;
			}
			StringComparer cmp = language.NameComparer;
			foreach (IProperty p in Properties) {
				if (cmp.Equals(p.Name, memberName)) {
					return p;
				}
			}
			foreach (IEvent e in Events) {
				if (cmp.Equals(e.Name, memberName)) {
					return e;
				}
			}
			foreach (IField f in Fields) {
				if (cmp.Equals(f.Name, memberName)) {
					return f;
				}
			}
			foreach (IMethod m in Methods) {
				if (cmp.Equals(m.Name, memberName)) {
					return m;
				}
			}
			return null;
		}
		
		public IClass GetInnermostClass(int caretLine, int caretColumn)
		{
			foreach (IClass c in InnerClasses) {
				if (c != null && IsInside(c, caretLine, caretColumn)) {
					return c.GetInnermostClass(caretLine, caretColumn);
				}
			}
			return this;
		}
		
		internal static bool IsInside(IClass c, int caretLine, int caretColumn)
		{
			return c.Region.IsInside(caretLine, caretColumn)
				|| c.Attributes.Any((IAttribute a) => a.Region.IsInside(caretLine, caretColumn));
		}
		
		public List<IClass> GetAccessibleTypes(IClass callingClass)
		{
			List<IClass> types = new List<IClass>();
			List<IClass> visitedTypes = new List<IClass>();
			
			IClass currentClass = this;
			do {
				if (visitedTypes.Contains(currentClass))
					break;
				visitedTypes.Add(currentClass);
				bool isClassInInheritanceTree = callingClass != null ? callingClass.IsTypeInInheritanceTree(currentClass) : false;
				foreach (IClass c in currentClass.InnerClasses) {
					if (c.IsAccessible(callingClass, isClassInInheritanceTree)) {
						types.Add(c);
					}
				}
				currentClass = currentClass.BaseClass;
			} while (currentClass != null);
			return types;
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer (Senior) Microsoft
United States United States
Sebastien Lebreton is a Software Engineer at Microsoft.

He is particularly interested in optimization, reverse engineering and distributed objects technologies.

Comments and Discussions