Very first time I am developing a utility using T4 to generate .aspx, .aspx.cs files as well as stored procedures. For that I need to pass some parameters like tablename, pagename etc. from windows form to the T4 template. And I have written templates and also a customHost as given http://msdn.microsoft.com/en-us/library/bb126579.aspx to call from windows form. Now I can call T4 template from windows form, but I don't know how to pass parameters to T4 template. I have tried to create property in template and call template from Win form but it gives me error on this line "var sessionHost = (ITextTemplatingSessionHost) this.Host;" because I use CustomHost. Please help me out.
Here is the code sample for T4 Template
Text Template Host Test
<## template debug="true" #>
<## parameter name="MyParameter" type="System.String" #>
<# for (int i=0; i<3; i++)
{
WriteLine("This is a test");
}
#>
Parameter in statement block: <# Write(MyParameter); #>
CustomHost.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TextTemplating;
using System.CodeDom.Compiler;
using System.IO;
namespace FormGenerator
{
class CustomHost : ITextTemplatingEngineHost
{
//the path and file name of the text template that is being processed
//---------------------------------------------------------------------
internal string TemplateFileValue;
public string TemplateFile
{
get { return TemplateFileValue; }
}
//This will be the extension of the generated text output file.
//The host can provide a default by setting the value of the field here.
//The engine can change this value based on the optional output directive
//if the user specifies it in the text template.
//---------------------------------------------------------------------
private string fileExtensionValue = ".txt";
public string FileExtension
{
get { return fileExtensionValue; }
}
//This will be the encoding of the generated text output file.
//The host can provide a default by setting the value of the field here.
//The engine can change this value based on the optional output directive
//if the user specifies it in the text template.
//---------------------------------------------------------------------
private Encoding fileEncodingValue = Encoding.UTF8;
public Encoding FileEncoding
{
get { return fileEncodingValue; }
}
//These are the errors that occur when the engine processes a template.
//The engine passes the errors to the host when it is done processing,
//and the host can decide how to display them. For example, the host
//can display the errors in the UI or write them to a file.
//---------------------------------------------------------------------
private CompilerErrorCollection errorsValue;
public CompilerErrorCollection Errors
{
get { return errorsValue; }
}
//The host can provide standard assembly references.
//The engine will use these references when compiling and
//executing the generated transformation class.
//--------------------------------------------------------------
public IList<string> StandardAssemblyReferences
{
get
{
return new string[]
{
//If this host searches standard paths and the GAC,
//we can specify the assembly name like this.
//---------------------------------------------------------
//"System"
//Because this host only resolves assemblies from the
//fully qualified path and name of the assembly,
//this is a quick way to get the code to give us the
//fully qualified path and name of the System assembly.
//---------------------------------------------------------
typeof(System.Uri).Assembly.Location
};
}
}
//The host can provide standard imports or using statements.
//The engine will add these statements to the generated
//transformation class.
//--------------------------------------------------------------
public IList<string> StandardImports
{
get
{
return new string[]
{
"System"
};
}
}
//The engine calls this method based on the optional include directive
//if the user has specified it in the text template.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
//The included text is returned in the context parameter.
//If the host searches the registry for the location of include files,
//or if the host searches multiple locations by default, the host can
//return the final path of the include file in the location parameter.
//---------------------------------------------------------------------
public bool LoadIncludeText(string requestFileName, out string content, out string location)
{
content = System.String.Empty;
location = System.String.Empty;
//If the argument is the fully qualified path of an existing file,
//then we are done.
//----------------------------------------------------------------
if (File.Exists(requestFileName))
{
content = File.ReadAllText(requestFileName);
return true;
}
//This can be customized to search specific paths for the file.
//This can be customized to accept paths to search as command line
//arguments.
//----------------------------------------------------------------
else
{
return false;
}
}
//Called by the Engine to enquire about
//the processing options you require.
//If you recognize that option, return an
//appropriate value.
//Otherwise, pass back NULL.
//--------------------------------------------------------------------
public object GetHostOption(string optionName)
{
object returnObject;
switch (optionName)
{
case "CacheAssemblies":
returnObject = true;
break;
default:
returnObject = null;
break;
}
return returnObject;
}
//The engine calls this method to resolve assembly references used in
//the generated transformation class project and for the optional
//assembly directive if the user has specified it in the text template.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
public string ResolveAssemblyReference(string assemblyReference)
{
//If the argument is the fully qualified path of an existing file,
//then we are done. (This does not do any work.)
//----------------------------------------------------------------
if (File.Exists(assemblyReference))
{
return assemblyReference;
}
//Maybe the assembly is in the same folder as the text template that
//called the directive.
//----------------------------------------------------------------
string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), assemblyReference);
if (File.Exists(candidate))
{
return candidate;
}
//This can be customized to search specific paths for the file
//or to search the GAC.
//----------------------------------------------------------------
//This can be customized to accept paths to search as command line
//arguments.
//----------------------------------------------------------------
//If we cannot do better, return the original file name.
return "";
}
//The engine calls this method based on the directives the user has
//specified in the text template.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
public Type ResolveDirectiveProcessor(string processorName)
{
//This host will not resolve any specific processors.
//Check the processor name, and if it is the name of a processor the
//host wants to support, return the type of the processor.
//---------------------------------------------------------------------
if (string.Compare(processorName, "XYZ", StringComparison.OrdinalIgnoreCase) == 0)
{
//return typeof();
}
//This can be customized to search specific paths for the file
//or to search the GAC
//If the directive processor cannot be found, throw an error.
throw new Exception("Directive Processor not found");
}
//A directive processor can call this method if a file name does not
//have a path.
//The host can attempt to provide path information by searching
//specific paths for the file and returning the file and path if found.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
public string ResolvePath(string fileName)
{
if (fileName == null)
{
throw new ArgumentNullException("the file name cannot be null");
}
//If the argument is the fully qualified path of an existing file,
//then we are done
//----------------------------------------------------------------
if (File.Exists(fileName))
{
return fileName;
}
//Maybe the file is in the same folder as the text template that
//called the directive.
//----------------------------------------------------------------
string candidate = Path.Combine(Path.GetDirectoryName(this.TemplateFile), fileName);
if (File.Exists(candidate))
{
return candidate;
}
//Look more places.
//----------------------------------------------------------------
//More code can go here...
//If we cannot do better, return the original file name.
return fileName;
}
//If a call to a directive in a text template does not provide a value
//for a required parameter, the directive processor can try to get it
//from the host by calling this method.
//This method can be called 0, 1, or more times.
//---------------------------------------------------------------------
public string ResolveParameterValue(string directiveId, string processorName, string parameterName)
{
if (directiveId == null)
{
throw new ArgumentNullException("the directiveId cannot be null");
}
if (processorName == null)
{
throw new ArgumentNullException("the processorName cannot be null");
}
if (parameterName == null)
{
throw new ArgumentNullException("the parameterName cannot be null");
}
//Code to provide "hard-coded" parameter values goes here.
//This code depends on the directive processors this host will interact with.
//If we cannot do better, return the empty string.
return String.Empty;
}
//The engine calls this method to change the extension of the
//generated text output file based on the optional output directive
//if the user specifies it in the text template.
//---------------------------------------------------------------------
public void SetFileExtension(string extension)
{
//The parameter extension has a '.' in front of it already.
//--------------------------------------------------------
fileExtensionValue = extension;
}
//The engine calls this method to change the encoding of the
//generated text output file based on the optional output directive
//if the user specifies it in the text template.
//----------------------------------------------------------------------
public void SetOutputEncoding(System.Text.Encoding encoding, bool fromOutputDirective)
{
fileEncodingValue = encoding;
}
//The engine calls this method when it is done processing a text
//template to pass any errors that occurred to the host.
//The host can decide how to display them.
//---------------------------------------------------------------------
public void LogErrors(CompilerErrorCollection errors)
{
errorsValue = errors;
}
//This is the application domain that is used to compile and run
//the generated transformation class to create the generated text output.
//----------------------------------------------------------------------
public AppDomain ProvideTemplatingAppDomain(string content)
{
//This host will provide a new application domain each time the
//engine processes a text template.
//-------------------------------------------------------------
return AppDomain.CreateDomain("Generation App Domain");
//This could be changed to return the current appdomain, but new
//assemblies are loaded into this AppDomain on a regular basis.
//If the AppDomain lasts too long, it will grow indefintely,
//which might be regarded as a leak.
//This could be customized to cache the application domain for
//a certain number of text template generations (for example, 10).
//This could be customized based on the contents of the text
//template, which are provided as a parameter for that purpose.
}
}
}
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using Microsoft.VisualStudio.TextTemplating;
using System.IO;
using FormGenerator;
using System.CodeDom.Compiler;
namespace FormGenerator
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
ProcessTemplate("Path\\TestTemplate.tt");
}
static void ProcessTemplate(string templateFileName)
{
if (templateFileName == null)
{
throw new ArgumentNullException("the file name cannot be null");
}
if (!File.Exists(templateFileName))
{
throw new FileNotFoundException("the file cannot be found");
}
CustomHost host = new CustomHost();
Engine engine = new Engine();
host.TemplateFileValue = templateFileName;
//Read the text template.
string input = File.ReadAllText(templateFileName);
//Transform the text template.
string output = engine.ProcessTemplate(input, host);
string outputFileName = Path.GetFileNameWithoutExtension(templateFileName);
outputFileName = Path.Combine(Path.GetDirectoryName(templateFileName), outputFileName);
outputFileName = outputFileName + "1" + host.FileExtension;
File.WriteAllText(outputFileName, output, host.FileEncoding);
foreach (CompilerError error in host.Errors)
{
Console.WriteLine(error.ToString());
}
}
}
}
I have tried but gives error "Unable to cast object of type 'FormGenerator.CustomHost' to type 'Microsoft.VisualStudio.TextTemplating.ITextTemplatingSessionHost'."
CustomHost Host = new CustomHost();
Host.TemplateFileValue = templateFileName;
string templateFile = Host.ResolvePath("TestTemplate.tt");
string templateContent = File.ReadAllText(templateFile);
TextTemplatingSession session = new TextTemplatingSession();
session["MyParameter"] = "SessionValue";
var sessionHost = (ITextTemplatingSessionHost)Host;
sessionHost.Session = session;
Engine engine = new Engine();
string generatedContent = engine.ProcessTemplate(templateContent, Host);
Please help me out how can I resolve this issue.
Thanks in advance.
You need to make your CustomHos class serializable
[Serializable()]
class CustomCmdLineHost : ITextTemplatingEngineHost
{
}
Related
I've created a simple class that writes and reads json data with a txt file:
using System;
using System.IO;
using Newtonsoft.Json;
namespace JsonTest1
{
class Program
{
private const string filePath = #"..\jsonData.txt";
static void Main(string[] args)
{
JsonFileTest();
NewPerson();
DeSerializer();
Console.ReadLine();
}
//Tests if the project's storage file exists.
public static void JsonFileTest()
{
bool exists = File.Exists(filePath);
if (exists)
{
Console.WriteLine("File exists at filepath " + filePath);
}
else
{
Console.WriteLine(filePath + " not found.");
}
}
//Creates a test object of 'Person' then passes it to the serializer method.
public static void NewPerson()
{
Person person = new Person();
person.Name = "John Wick";
person.Age = 999;
SerializeMethod(person);
}
//Turns an object into JSON data and writes it to file.
static void SerializeMethod(Person person)
{
File.WriteAllText(filePath, JsonConvert.SerializeObject(person));
Console.WriteLine("Test name and age copied to file.");
}
//Turns JSON data from file into an object.
static void DeSerializer()
{
Person person2 = JsonConvert.DeserializeObject<Person>(File.ReadAllText(filePath));
if (person2 != null)
{
Console.WriteLine("Json-to-C# test data: " + person2.Name + ", " + person2.Age);
}
else
{
Console.WriteLine("No data received for json-to-C# test object.");
}
}
}
}
The issue is, if I set filePath to be something like #"C:\Users\User\Documents\json.txt", then it will create a text file that I can see the data in afterwards. If filePath is local, e.g. #"..\jsonData.txt", I open the file and it's empty, even though my program can read correctly from it at runtime. Why isn't the data saving when I use the local file route?
Things I've already tried: Using a .json file instead of a .txt file. Running Visual Studio as administrator.
You're looking in the wrong folder. Your program will be executed from the bin\debug folder or something. .. then is the bin folder.
A general approach for resolving "file not found" problems is to use SysInternals Process Monitor, a free program. Add a filter with the properties Path, contains, jsonData.txt, click Add and then let your program run.
This will reveal the full path:
Then click on Jump to... in the context menu to reveal that path.
You can check the full path for any path expression like this
var fp = System.IO.Path.GetFullPath(#"..\jsonData.txt");
I have a solution with two projects, a master and a slave. Both these projects are WinForms Applications.
There's always one instance of the master running and multiple instances of the slaves. The slaves are started by the master.
Right now I have the executable name of the slave hardcoded into the master's code, which works fine.
However, I want to be able to get the executable name of the slave without hardcoding it, but by getting it through its reference.
Is this possible, and how should I go about doing this?
This is what I currently have:
ProcessStartInfo startInfo = new ProcessStartInfo("Slave.exe") { Arguments = Args };
Process.Start(startInfo);
But I would like to replace "Slave.exe" with something dynamical. I have added an assembly reference to the slave project, having namespace Slave.
Thanks to #Postlagerkarte's suggestion, I solved my problem by using post build events. I have 4 different slave processes, let's call them SlaveProcessA to SlaveProcessD. First thing I did was make a class which can hold the executable names, and (de)serialize them:
public class ExeNames {
[XmlIgnore]
public bool IsLoaded {get; private set;}
[XmlIgnore]
const string Filename = "exenames.xml";
public string SlaveAExe {get; set;}
public string SlaveBExe {get; set;}
public string SlaveDExe {get; set;}
public string SlaveCExe {get; set;}
public void Load() {
var serializer = new XmlSerializer(typeof(ExeNames));
using (StreamReader reader = new StreamReader(Filename)) {
ExeNames End = serializer.Deserialize(reader) as ExeNames;
if (End == null) return;
SlaveAExe = End.SlaveAExe;
SlaveBExe = End.SlaveBExe;
SlaveDExe = End.SlaveDExe;
SlaveCExe = End.SlaveCExe;
IsLoaded = true;
}
}
public void Save() {
var serializer = new XmlSerializer(typeof(ExeNames));
using (StreamWriter writer = new StreamWriter(Filename)) {
serializer.Serialize(writer, this);
}
}
}
After that, I created a new project called ExeNameSaver. This program takes 2 parameters: the code of the process, and the executable name:
namespace ExeNameSaver {
class Program {
static void Main(string[] args) {
if (args.Length < 2) throw new ArgumentException("2 Parameters are required");
var Code = args[0];
var Exe = args[1];
ExeNames.ExeNames Exn = new ExeNames.ExeNames();
try {
Exn.Load();
}
catch {
Console.WriteLine("Starting new file...");
}
switch (Code) {
case "SlaveA":
Exn.SlaveAExe = Exe;
break;
case "SlaveB":
Exn.SlaveBExe = Exe;
break;
case "SlaveC":
Exn.SlaveCExe = Exe;
break;
case "SlaveD":
Exn.SlaveDExe = Exe;
break;
default:
Console.WriteLine("Invalid process code: " + Code);
break;
}
Exn.Save();
}
}
}
Then, I make sure that the ExeNameSaver is built first by altering the dependencies. Now I can use the ExeNameSaver program to save my executable names to an XML-file, by putting the following in the Post-build event command line of SlaveProcessA:
$(OutDir)ExeNameSaver.exe SlaveA $(TargetFileName)
And similarly for the other 3 slaves.
$(OutDir)ExeNameSaver.exe SlaveB $(TargetFileName)
...
$(OutDir)ExeNameSaver.exe SlaveC $(TargetFileName)
...
$(OutDir)ExeNameSaver.exe SlaveD $(TargetFileName)
Now after each build, the exenames.xml file will contain the executable names of my 4 processes. I can then load this XML using the same ExeNames class in my master program and use their names from there.
I am trying to create a DLL file in runtime ,as a matter of fact i need to save an encoded data to DLL .My code is like this :
class DllFile
{
public static void CreateDllFile(string source)
{
source = #"using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LicensecodeDll
{
class Check
{
public string returnValue()
{
return " + source + ";" + "}}}";
var provider = new CSharpCodeProvider();
var options = new CompilerParameters
{
OutputAssembly = "test.dll"
};
var results = provider.CompileAssemblyFromSource(options, new[] { source });
}
}
}
every thing works fine and my ddl is created ,but i need to read the value that is saved on the dll file ,i mean i need the returnValue.how can i do that?
Best regards.Any ideas will be appreciated.
You can load the assembly dynamically and use reflection to call that method. The code should be like this.
Assembly a = Assembly.Load("test.dll");
Type myType = a.GetType("LicensecodeDll.Check");
MethodInfo myMethod = myType.GetMethod("returnValue");
object obj = Activator.CreateInstance(myType);
myMethod.Invoke(obj, null);
More detail is on MSDN: How to: Load Assemblies into an Application Domain
I have a command handler which basically works like this:
ControlList.Handlers[CommandType.MyCommandComesHere].Handle(data);
Handlers is a Dictionary<CommandType, ICommandHandler> and CommandType is a enum.
Handle by its turn would lead it to this:
using System;
using log4net;
namespace My_Application
{
public class MyCommand : ICommandHandler
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(MyCommand));
public void Handle(Events data)
{
Console.WriteLine("I can load cs files on the fly yay!!");
}
}
}
My question is how can I make so my application would compile and let me use that cs file while its running?
Any simple example of this would be greatly appreciated but not required as long as I can get some pointers as to what I need to look for as I am not even sure what do I need to make this happen.
To put it simple I am currently trying to understand how could I load a cs file into my application that is already compiled and is currently running.
Using CodeDOM, you need to first create a compiler provider. (You might want to set GenerateExecutable to false and GenerateInMemory to true for your purposes.)
var csc = new CSharpCodeProvider();
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = true;
Then, you can compile the assembly using CompileAssemblyFromSource and get the CompilerResults returned from it. From this returned object, get a reference to the generated assembly, using its CompiledAssembly property.
var results = csc.CompileAssemblyFromSource(parameters, "contents of the .cs file");
var assembly = results.CompiledAssembly;
Then you can use reflection to create instances from that assembly and call methods on them.
var instance = assembly.CreateInstance("MyCommand");
// etc...
Alternatively, if you're only interested in short code snippets, it might be worth it to use Roslyn instead. You need to create a ScriptEngine first.
var engine = new ScriptEngine();
Then you can just Execute strings on it - or Execute<T> if you're confident that the expression in the string returns a type assignable to T.
var myObject = engine.Execute("1+1");
var myInt = engine.Execute<int>("1+1");
It's definitely more immediate, so it's worth looking into if it serves your purpose.
I have looked for different ways to achieve this and found cs script library lightweight and usable. Here is code snippet how I use it. It runs cs code within app domain so it presumes, that the cs script being compiled comes form trusted source.
using CSScriptLibrary;
using csscript;
using System.CodeDom.Compiler;
using System.Reflection;
//Method example - variable script contains cs code
//This is used to compile cs to DLL and save DLL to a defined location
public Assembly GetAssembly(string script, string assemblyFileName)
{
Assembly assembly;
CSScript.CacheEnabled = true;
try
{
bool debugBuild = false;
#if DEBUG
debugBuild = true;
#endif
if (assemblyFileName == null)
assembly = CSScript.LoadCode(script, null);
else
assembly = CSScript.LoadCode(script, assemblyFileName, debugBuild, null);
return assembly;
}
catch (CompilerException e)
{
//Handle compiler exceptions
}
}
/// <summary>
/// Runs the code either form script text or precompiled DLL
/// </summary>
public void Run(string script)
{
try
{
string tmpPath = GetPathToDLLs(); //Path, where you store precompiled DLLs
string assemblyFileName;
Assembly assembly = null;
if (Directory.Exists(tmpPath))
{
assemblyFileName = Path.Combine(tmpPath, GetExamScriptFileName(exam));
if (File.Exists(assemblyFileName))
{
try
{
assembly = Assembly.LoadFrom(assemblyFileName); //Načtení bez kompilace
}
catch (Exception exAssemblyLoad)
{
Tools.LogError(exAssemblyLoad.Message);
assembly = null;
}
}
}
else
assemblyFileName = null;
//If assembly not found, compile it form script string
if (assembly ==null)
assembly = GetAssembly(script, assemblyFileName);
AsmHelper asmHelper = new AsmHelper(assembly);
//This is how I use the compiled assembly - it depends on your actual code
ICalculateScript calcScript = (ICalculateScript)asmHelper.CreateObject(GetExamScriptClassName(exam));
cex = calcScript.Calculate(this, exam);
Debug.Print("***** Calculated {0} ****", exam.ZV.ZkouskaVzorkuID);
}
catch (Exception e)
{
//handle exceptions
}
}
I'm trying to load dll libraries during runtime using the following code so that I don't have to provide the user with lot of dll files along with the main executable file. I have inlude all the dll files as an embedded resource and also in the reference part I have include them and have set the CopyLocal property to false. But the problems here are:1. All the dll are getting copied to Bin\Debug folder2. I'm getting FileNotFoundException.I did lot of searches to get these things resolved and finally I'm here. I got a similar code here but still couldn't do anything. What should I do to prevent this exception...??
Is there a better way to do the same thing for a Windows Form Application(Not WPF)...??
using System;
using System.Linq;
using System.Windows.Forms;
using System.Diagnostics;
using System.Reflection;
using System.Collections.Generic;
using System.IO;
namespace MyNameSpace
{
static class Program
{
static int cnt;
static IDictionary<string, Assembly> assemblyDictionary;
[STAThread]
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
if (cnt != 1)
{
cnt = 1;
Assembly executingAssembly = Assembly.GetExecutingAssembly();
string[] resources = executingAssembly.GetManifestResourceNames();
foreach (string resource in resources)
{
if (resource.EndsWith(".dll"))
{
using (Stream stream = executingAssembly.GetManifestResourceStream(resource))
{
if (stream == null)
continue;
byte[] assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
try
{
assemblyDictionary.Add(resource, Assembly.Load(assemblyRawBytes));
}
catch (Exception ex)
{
MessageBox.Show("Failed to load: " + resource + " Exception: " + ex.Message);
}
}
}
}
Program.Main();
}
if (cnt == 1)
{
cnt = 2;
System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest;
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
AssemblyName assemblyName = new AssemblyName(args.Name);
string path = assemblyName.Name + ".dll";
if (assemblyDictionary.ContainsKey(path))
{
return assemblyDictionary[path];
}
return null;
}
}
}
If I'm using something unnecessarily in my code then you can show me the right way...
I'm a student working on Windows Form Application v4.0 project for my papers to be submitted.
If it is still the case that you must do this, then use this OnResolveAssembly method. There is no need to preload them into an array if you don't want to. This will load them the first time they are actually needed.
Then just:
add the some.assembly.dll file to the project.
probably not a reference to the project's output
but the file that is the result of the DLL project.
mark it as a Resource in the file properties.
// This function is not called if the Assembly is already previously loaded into memory.
// This function is not called if the Assembly is already in the same folder as the app.
//
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs e)
{
var thisAssembly = Assembly.GetExecutingAssembly();
// Get the Name of the AssemblyFile
var assemblyName = new AssemblyName(e.Name);
var dllName = assemblyName.Name + ".dll";
// Load from Embedded Resources
var resources = thisAssembly.GetManifestResourceNames().Where(s => s.EndsWith(dllName));
if (resources.Any())
{
// 99% of cases will only have one matching item, but if you don't,
// you will have to change the logic to handle those cases.
var resourceName = resources.First();
using (var stream = thisAssembly.GetManifestResourceStream(resourceName))
{
if (stream == null) return null;
var block = new byte[stream.Length];
// Safely try to load the assembly.
try
{
stream.Read(block, 0, block.Length);
return Assembly.Load(block);
}
catch (IOException)
{
return null;
}
catch (BadImageFormatException)
{
return null;
}
}
}
// in the case the resource doesn't exist, return null.
return null;
}
-Jesse
PS: This comes from http://www.paulrohde.com/merging-a-wpf-application-into-a-single-exe/
Try the following:
For each .dll resource:
If the file allready exists on the AppDomain.Current.BaseDirectory then continue to the next resource
Else save the resource to the AppDomain.Current.BaseDirectory. Do this in a try-catch and if it fails, notify the user. For this step to complete successfully you will need write access on the installation folder (usually a subfolder of "Program Files"). This will be solved by running the program as an administrator the first time only ao that the files are written on the file system.
Ιf the assemblies are referenced by your VS project then you do not have to load them yourself. To understand why this work's you will need to understand how assemblies are located by the CLR.
Else you will need to load each assembly yourself using one of the Assembly.Load that take either a string or and AssemblyName as a parameter.