I have a wierd application loading mechanism. I have a boostrapping exe file with all the other DLLs and the application itself inside it's resources. These files (assemblies) are extracted from resources and loaded ond emand because i'm attaching to the Assemblyresolve event of the current AppDomain.
[STAThread]
static void Main()
{
// if the command line contains extract then extract the dlls, next run will resolve assemblies from disk
bool saveDllsToDisk = new List<string>(Environment.GetCommandLineArgs()).Contains("extract");
// if the command line contains bin then use the bin folder instead of temp to extract dlls
bool useBinFolder = new List<string>(Environment.GetCommandLineArgs()).Contains("bin");
if (!Directory.Exists(tempFolder)) {
Directory.CreateDirectory(tempFolder);
}
// the assembly resolver will get here because it will not find the dlls in the bin folder
// we load assemblies in our specific way:
// - if exists in our temp/bin folder load from there
// - else load from resources
// - if specified extract DLLs to the temp/bin folder
AppDomain.CurrentDomain.AssemblyResolve += (sender, args2) =>
{
string name = new AssemblyName(args2.Name).Name;
Debug.WriteLine("START LOADING " + name);
Assembly assembly = null;
string folder = useBinFolder ? binFolder : Path.Combine(tempFolder, APP_NAME);
string fileName = name.Replace(".","_").Replace("#EXE#", ""); // in resources we use _ instead of .
string extension = name.Contains("#EXE#") ? "exe" : "dll"; // hack for our embedded exe files
name = name.Replace("#EXE#", ""); // hack for our embedded exe files
if (File.Exists(Path.Combine(folder,String.Format("{0}.{1}", name, extension))))
{
// load from file in app temp folder
assembly = Assembly.LoadFile(Path.Combine(folder, String.Format("{0}.{1}", name, extension)));
}
else
{
// extract assembly from resources
byte[] assemblyBytes = (byte[])resMan.GetObject(fileName, CultureInfo.InvariantCulture);
assembly = Assembly.Load(assemblyBytes);
// if selected save to file so the next run JIT will resolve from disk
if (saveDllsToDisk)
{
string outDll = Path.Combine(folder, String.Format("{0}.{1}", name, extension));
using (var fs = File.Create(outDll))
{
fs.Write(assemblyBytes, 0, assemblyBytes.Length);
}
}
}
Debug.WriteLine("LOADED " + name);
return assembly;
};
splashScreen = new frmSplash();
// as soon as splashscreen starts animating assembly preloading will be launched on this eventhandler
splashScreen.Started += new EventHandler(splash_Started);
// splashscreen has finished fadein we must now wait for all libraries to be preloaded and set CanContinue
splashScreen.Finishing += new EventHandler(splash_Finishing);
splashScreen.CanContinue = false;
// run splashscreen while preloading of DLLs is going on
Application.Run(splashScreen);
}
This bootstrapper has only one reference and that is to the main application, but it does not reference it in the main method to avoid assembly search at start. What I do is force assembly loading during the splashscreen animation:
ObjectHandle instance;
string[,] assemblies = {
{"WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35","dummy"}, // will go from GAC, not our resolver
{"PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35","dummy"}, // will go from GAC, not our resolver
{"PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35","dummy"}, // will go from GAC, not our resolver
{"WindowsFormsIntegration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35","dummy"}, // will go from GAC, not our resolver
{"PresentationFramework.Aero, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35","dummy"}, // will go from GAC, not our resolver
{"System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089","dummy"}, // will go from GAC, not our resolver
{"AvalonDock, Version=1.3.3585.0, Culture=neutral, PublicKeyToken=85a1e0ada7ec13e4", "dummy"}, // out reference inside resources
{"AvalonDock.Themes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "dummy"}, // out reference inside resources
{"ICSharpCode.AvalonEdit, Version=4.0.0.5950, Culture=neutral, PublicKeyToken=9cc39be672370310", "dummy"}, // out reference inside resources
{"WPG, Version=2.0.4120.37542, Culture=neutral, PublicKeyToken=null","dummy"}, // out reference inside resources
{"WPGBrushEditor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null","dummy"}, // out reference inside resources
{"HLSLEditor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null","dummy"} // preload even our main file that will launch after the splashscreen
};
for (int i = 0; i <= assemblies.GetUpperBound(0); i++)
{
try
{
instance = AppDomain.CurrentDomain.CreateInstance(assemblies[i,0], assemblies[i,1]);
}
catch (Exception ex) {
/* must not raise errors, it will fail because we are not actually asking for a
* valid type and we only need this assembly loaded right now*/
}
}
Now you see that there are no actual dll files in any folder but are assemblies directly loaded from resources.
My problem now is: How to register a COM object delared in one of my dll files?
RegAsm uses filepaths to register a COM object... :(
Any help appreciated!