in my new project (windows-C#-vs2008)I want the executable to be able to autonomously update itself- perhaps from a network server
Here What I do is download any updated dlls to "isolated storage" (each as separate dlls) then modify the CurrentDomain_AssemblyResolve() method
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
such that it first checks for the presence of library in its isolated storage before loading it from executable .
But what if I want to change something in the main exe.
[I tried creating a loader project(console ap) from which, it calls my main program(changed output as dll) ,however this time the assembly resolve event is not getting trigger because the reference dlls are geting generated under bin folder of new loader project during compilation ]
Any help would be highly appreciated..
public static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
try
{
string assemName = new AssemblyName(args.Name).Name;
object i_StoreRootDir = i_StorageFile.GetType().GetField
("m_RootDir", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(i_StorageFile);
string i_StorePath = CombinePaths(i_StoreRootDir.ToString(), I_STOREDIR, assemName, DLL_EXT);
if (File.Exists(i_StorePath))
{
return Assembly.LoadFrom(i_StorePath);
}
else
{
//load it from resource.
return null;
}
}
catch (Exception)
{
throw;
}
You may take a look at ClickOnce deployment as part of the framework. You may also checkout wyUpdate. There's also BitsUpdater.
Related
I have a .net application that references the Microsoft.SqlServer.Smo assembly.
The assembly is not distributed with the application. Instead the sql sdk is installed in the system and the dll is registered in the GAC so that the application can load.
There is no problem with this except that on some target machines I have the v12 of the SDK, while on others I have the v13 of the SDK (that usually comes installed with SSMS).
I would like the application to load the latest version of whatever is available on the system, so v13 or, if not available, v12.
Is it possible to achieve this in code or through the application config?
The short answer to the question is to set SpecificVersion to false as correctly suggested by #sevzas.
Anyway, if on the system is installed SSMS 2016 update 13.0.16000.28, the 13.100.0.0 of the dll will be registered in the GAC and with the above change, this is the version that it will be loaded. Unfortunately this version is not meant to be used by 3rd party developers but only by Microsoft products, so trying to load it will generate an exception (see here). Someone could wonder at this point why they register it in the GAC if they don't want people to use it.
Anyway, I found a way to load the v13.0 (or previous, or future 14) with the below code by using the assembly resolve event.
static int Main(string[] args)
{
//we set an event handler at the begging of our program
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
//your stuff
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//if the dll is a sqlserver dll, we do our trick
if(args.Name.StartsWith("Microsoft.SqlServer"))
return LoadSqlAssembly(args.Name);
return null;
}
private static readonly int[] SqlVersions = new int[] {14, 13, 12, 11};
private static bool _reEntry = false;
private static Assembly LoadSqlAssembly(string name)
{
if (_reEntry)
return null;
name = name.Split(',')[0];
foreach (var version in SqlVersions)
{
try
{
_reEntry = true;
var ret = Assembly.Load($"{name}, Version={version}.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL");
//Logger.InfoFormat("Loaded {0} version {1}", name, version);
return ret;
}
catch (Exception)
{
//ignore exception
}
finally
{
_reEntry = false;
}
}
return null;
}
```
I'm looking for a way to run code by executing the following steps:
Receiving a list of NuGet packages (a list of tuples ("package name", "package version", "path to main class").
Retrieving them in a local directory (cf code sample #1)
Loading them in my program at run-time
Running the main classes by introspection (cf code sample #2)
By now I am struggling with the third step. I can't find out how to load my package at run-time.
My main question are:
How can I find out in which folders were stored the retrieved packages?
How can I load the content of those directories into my program?
Code Sample #1:
private static void getPackageByNameAndVersion(string packageID, string version)
{
IPackageRepository repo =
PackageRepositoryFactory.Default
.CreateRepository("https://packages.nuget.org/api/v2");
string path = "C:/tmp_repo";
PackageManager packageManager = new PackageManager(repo, path);
Console.WriteLine("before dl pkg");
packageManager.InstallPackage(packageID, SemanticVersion.Parse(version));
}
Code sample #2:
private static void loadByAssemblyNameAndTypeName(string assemblyName, string typeName)
{
AppDomain isolationAppDomain = AppDomain.CreateDomain("tmp");
object a = isolationAppDomain.CreateInstanceAndUnwrap(assemblyName, typeName);
Type x = a.GetType();
MethodInfo m = x.GetMethod("Main");
m.Invoke(a, new object[] { });
}
Grab a cup of coffee :)
Downloading the nuget package?
Nuget.Core (nuget package) is a good choice, and here is a snippet of code that I have that should be able to download a nuget package by id and version
var repo = PackageRepositoryFactory.Default
.CreateRepository("https://packages.nuget.org/api/v2");
string path = "c:\\temp";
var packageManager = new PackageManager(repo, path);
packageManager.PackageInstalled += PackageManager_PackageInstalled;
var package = repo.FindPackage("packageName", SemanticVersion.Parse("1.0.0"));
if (package != null)
{
packageManager.InstallPackage(package, false, true);
}
Notice that I plugged an event handler to the PackageInstalled event of the PackageManager class.
How do we load an assembly in an isolated app domain?
Since reflection API does not provide a way to load an assembly in a specific domain, We will create a proxy class that act as a loader in our isolated domain:
public class TypeProxy : MarshalByRefObject
{
public Type LoadFromAssembly(string assemblyPath, string typeName)
{
try
{
var asm = Assembly.LoadFile(assemblyPath);
return asm.GetType(typeName);
}
catch (Exception) { return null; }
}
}
And now, is how to put it all together?
Here comes the complex part:
private static void PackageManager_PackageInstalled(object sender,
PackageOperationEventArgs e)
{
var files = e.FileSystem.GetFiles(e.InstallPath, "*.dll", true);
foreach (var file in files)
{
try
{
AppDomain domain = AppDomain.CreateDomain("tmp");
Type typeProxyType = typeof(TypeProxy);
var typeProxyInstance = (TypeProxy)domain.CreateInstanceAndUnwrap(
typeProxyType.Assembly.FullName,
typeProxyType.FullName);
var type = typeProxyInstance.LoadFromAssembly(file, "<KnownTypeName>");
object instance =
domain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
}
catch (Exception ex)
{
Console.WriteLine("failed to load {0}", file);
Console.WriteLine(ex.ToString());
}
}
}
Notice that this method is the event handler that gets executed after downloading the nuget package
Also
Note that you will need to replace <KnownTypeName> with the expected type name coming from the assembly (or maybe run a discovery of all public types in the assembly)
Worth noting that I haven't executed this code myself and cannot guarantee that it will work out of the box, and still might need some tweaking. but Hopefully it is the concept that allows you to solve the problem.
Don't do that! You are probably trying to load NuGet content at a customers computer to save some space on distribution of your software. Isn't it that?
The common recommended approach is to download the NuGet content as the second step of an automated build (after downloading the source code), build the software and run the automated tests with the NuGet content you have downloaded. And then distribute the build with the NuGet content you have tested as the complex whole unit.
I have a structure:-
\bin\debug\abc.exe and
\Libs\win32\xyz.dll.
Now I need to refer xyz.dll so as to run my abc.exe. I tried with "probing" tag in app.config but in that case the possibility was only when I had 'Libs' folder in 'debug' folder i.e. where .exe is present. But I want to come 2 folders out from .exe and then go into \Libs\win32 to refer to .dll . Please suggest me what should I do.
One option is handling AssemblyResolve event, every time .NET couldn't find required assembly in current path, it will trigger AssemblyResolve event:
{
// Execute in startup
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
}
private Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
{
string RESOURCES = ".resources";
try
{
/* Extract assembly name */
string[] sections = args.Name.Split(new char[] { ',' });
if (sections.Length == 0) return null;
string assemblyName = sections[0];
/* If assembly name contains ".resource", you don't need to load it*/
if (assemblyName.Length >= RESOURCES.Length &&
assemblyName.LastIndexOf(RESOURCES) == assemblyName.Length - RESOURCES.Length)
{
return null;
}
/* Load assembly to current domain (also you can use simple way to load) */
string assemblyFullPath = "..//..//Libs//" + assemblyName;
FileStream io = new FileStream(assemblyNameWithExtension, FileMode.Open, FileAccess.Read);
if (io == null) return null;
BinaryReader binaryReader = new BinaryReader(io);
Assembly assembly = Assembly.Load(binaryReader.ReadBytes((int)io.Length));
return assembly;
}
catch(Exception ex)
{}
}
*Another option is loading all of your required assemblies to current domain at your project start-up.
You use ..\ in the file path to move up a directory.
So if you're in \bin\debug\abc.exe then your reference to \Libs\win32\xyz.dll would be
..\..\Libs\win32\xyz.dll
This should only be necessary when building your projects, when it's built if your executable is referencing the dll correctly it only needs to be put in the same folder as the dll.
Unless of course you're using dllimport or something where you need to know the exact path of the dll during runtime.
I am currently writing an application suite with a plugin system that loads plugins at runtime using the MEF framework.
I have currently setup one of my top level WPF applications to embed it's referenced DLLs as embedded resources and load them at runtime using the method described here.
This works fine and I get my single file WPF application that runs fine.
However, another of my top level console applications uses the MEF framework to load plugins at runtime (the WPF application is fixed and includes the plugins explicitly). My plugins have several dependencies themselves on various libraries and the extensions folder that the console application loads the plugins from is littered with all the various library dlls.
I would like to embed the dependencies of each plugin within itself so that my extensions directory contains only the top level DLL files. The method that I have used above does not cater for this approach as the plugin component cannot find the required dependency as it is only the executing assembly that is being searched for these embedded resources.
My current OnResolveAssembly method looks like this:
public static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
var assemblyName = new AssemblyName(args.Name);
string path = assemblyName.Name + ".dll";
if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
{
path = String.Format(#"{0}\{1}", assemblyName.CultureInfo, path);
}
using (Stream stream = executingAssembly.GetManifestResourceStream(path))
{
if (stream == null)
return null;
var assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
I'm thinking that the best way to proceed would be to add in functionality to keep track of all assemblies loaded in a list and once a new assembly has been loaded in this way, recursively do the same; load any embedded DLLs within those as you go. You can then add these DLLs to the list which will act as a cache.
Is there perhaps a better way to proceed with this?
I have implemented a very similar solution to yours and it works very fine for me. As you can see I keep track of already loaded assemblies in a _references dictionary.
In my case, I do not need to "eagerly" load all embedded dependencies in any recursive way, but rather my embedded assemblies do register themselves with the application host on-demand.
public static class ApplicationHost
{
private static readonly Dictionary<string, Assembly> _references = new Dictionary<string, Assembly>();
[STAThread]
private static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => _references.ContainsKey(args.Name) ? _references[args.Name] : null;
RegisterAssemblyAndEmbeddedDependencies();
// continue application bootstrapping...
}
public static void RegisterAssemblyAndEmbeddedDependencies()
{
var assembly = Assembly.GetCallingAssembly();
_references[assembly.FullName] = assembly;
foreach (var resourceName in assembly.GetManifestResourceNames())
{
using (var resourceStream = assembly.GetManifestResourceStream(resourceName))
{
var rawAssembly = new byte[resourceStream.Length];
resourceStream.Read(rawAssembly, 0, rawAssembly.Length);
var reference = Assembly.Load(rawAssembly);
_references[reference.FullName] = reference;
}
}
}
}
I have a huge application where one project of my solution makes reports.
I want to add new report (update report) without building my project, just add .dll files. I read about Assembly and
AppDomain, but I don't know is it really good way to add new dll for new report and how to update old report in runtime?
Here's my example, it takes my first dll, but second time it doesn't. First dll - sum, second - deducted.
static void Main(string[] args)
{
try
{
//first domain
AppDomain domain = AppDomain.CreateDomain("MyDomain");
AssemblyDll asb1 = new AssemblyDll();
Console.WriteLine(asb1.AssemblyMethod(1));
AppDomain.Unload(domain);
Console.ReadKey();
//second domain
AppDomain newDomain = AppDomain.CreateDomain("myNewDomain");
AssemblyDll asb2 = new AssemblyDll();
Console.WriteLine(asb2.AssemblyMethod(2));
AppDomain.Unload(newDomain);
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
public class AssemblyDll
{
public string AssemblyMethod(int version)
{
//loading .dll
Assembly assembly = Assembly.LoadFrom(#"../../../../Assembly/DynamicDLL" + version + ".dll");
Type type = assembly.GetType("DynamicDLL.Dynamic");
object instance = Activator.CreateInstance(type);
MethodInfo[] methods = type.GetMethods();
//invoke method
object result = methods[0].Invoke(instance, new object[] { 5, 3 });
return result.ToString();
}
}
My .dll file comes from:
namespace DynamicDLL
{
public class Dynamic
{
public int DynamicMethod(int a, int b)
{
return a + b;
//return a - b;
}
}
}
If you want to write something like plugins and like the plugin approach, you should take a look at MEF http://msdn.microsoft.com/en/library/vstudio/dd460648.aspx
MEF allows you to use any assembly dynamically and even drop dlls into a folder and build a MEF catalog out of it.
Actually Visual Studio and uses MEF internally for extensiblility (Plugins...)
Assemblies are generally loaded into an AppDomain once and you cannot unload them once loaded.
You can create a new AppDomain and load your assemblies into this and when you release this the assemblies will be unloaded. However the caveat here is you cannot directly communicate between two AppDomain you have to marshal between the two using some other method like remoting.
There's been much wrote on this in terms of plugins and making plugins unloadable, a quick Google search presented these:
http://www.brad-smith.info/blog/archives/500
http://adrianvintu.com/blogengine/post/Unloadable-plugins.aspx
Hopefully these will aid you.