/// <summary> /// REQUIRES ADMIN PRIVILEGES. Installs EasyHook and all given user NET assemblies into the GAC and /// ensures that all references are cleaned up if the installing application /// is shutdown. Cleanup does not depend on the calling application... /// </summary> /// <remarks> /// <para> /// ATTENTION: There are some problems when debugging processes whose libraries /// are added to the GAC. Visual Studio won't start the debug session! There is /// only one chance for you to workaround this issue if you want to install /// libraries AND debug them simultanously. This is simply to debug only one process /// which is the default setting of Visual Studio. Because the libraries are added /// to the GAC AFTER Visual Studio has initialized the debug session, there won't be /// any conflicts; at least so far... /// </para><para> /// In debug versions of EasyHook, you may also check the "Application" event log, which holds additional information /// about the GAC registration, after calling this method. In general this method works /// transactionally. This means if something goes wrong, the GAC state of all related libraries /// won't be violated! /// </para><para> /// The problem with NET assemblies is that the CLR only searches the GAC and /// directories starting with the application base directory for assemblies. /// To get injected assemblies working either all of them have to be located /// under the target base directory (which is not suitable in most cases) or /// reside in the GAC. /// </para><para> /// EasyHook provides a way to automatically register all of its own assemblies /// and custom ones temporarily in the GAC. It also ensures /// that all of these assemblies are removed if the installing process exists. /// So you don't need to care about and may write applications according to /// the XCOPY standard. If your application ships with an installer, you may /// statically install all of your assemblies and the ones of EasyHook into the /// GAC. In this case just don't call <see cref="Register"/>. /// </para><para> /// Of course EasyHook does also take care of multiple processes using the same /// injection libraries. So if two processes are sharing some of those DLLs, /// a stable reference counter ensures that the libraries are kept in the GAC /// if one process is terminated while the other continues running and so continues /// holding a proper GAC reference. /// </para><para> /// Please note that in order to add your library to the GAC, it has to be a valid /// NET assembly and expose a so called "Strong Name". Assemblies without a strong /// name will be rejected by this method! /// </para> /// </remarks> /// <param name="InDescription"> /// A description under which the installed files should be referenced. /// </param> /// <param name="InUserAssemblies"> /// A list of user assemblies as relative or absolute paths. /// </param> /// <exception cref="System.IO.FileNotFoundException"> /// At least one of the files specified could not be found! /// </exception> /// <exception cref="BadImageFormatException"> /// Unable to load at least one of the given files for reflection. /// </exception> /// <exception cref="ArgumentException"> /// At least one of the given files does not have a strong name. /// </exception> public static void Register( String InDescription, params String[] InUserAssemblies) { List <Assembly> AsmList = new List <Assembly>(); String RemovalList = ""; List <String> InstallList = new List <String>(); /* * Read and validate config file... */ List <String> Files = new List <string>(); Files.Add(typeof(Config).Assembly.Location); Files.AddRange(InUserAssemblies); for (int i = 0; i < Files.Count; i++) { Assembly Entry; String AsmPath = Path.GetFullPath(Files[i]); if (!File.Exists(AsmPath)) { throw new System.IO.FileNotFoundException("The given assembly \"" + Files[i] + "\" (\"" + AsmPath + "\") does not exist."); } // just validate that this is a NET assembly with valid metadata... try { Entry = Assembly.ReflectionOnlyLoadFrom(AsmPath); } catch (Exception ExtInfo) { throw new BadImageFormatException("Unable to load given assembly \"" + Files[i] + "\" (\"" + AsmPath + "\") for reflection. Is this a valid NET assembly?", ExtInfo); } // is strongly named? (required for GAC) AssemblyName Name = AssemblyName.GetAssemblyName(AsmPath); if ((Name.Flags & AssemblyNameFlags.PublicKey) == 0) { throw new ArgumentException("The given user library \"" + Files[i] + "\" is not strongly named!"); } AsmList.Add(Entry); InstallList.Add(AsmPath); RemovalList += " \"" + Name.Name + "\""; } /* * Install assemblies into GAC ... */ // create unique installation identifier Byte[] IdentData = new Byte[30]; new RNGCryptoServiceProvider().GetBytes(IdentData); // run cleanup service InDescription = InDescription.Replace('"', '\''); Config.RunCommand( "GACRemover", false, false, "EasyHook32Svc.exe", Process.GetCurrentProcess().Id.ToString() + " \"" + Convert.ToBase64String(IdentData) + "\" \"" + InDescription + "\"" + RemovalList); // install assemblies IntPtr GacContext = NativeAPI.GacCreateContext(); try { foreach (String Assembly in InstallList) { NativeAPI.GacInstallAssembly( GacContext, Assembly, InDescription, Convert.ToBase64String(IdentData)); } } finally { NativeAPI.GacReleaseContext(ref GacContext); } }