Merge DLL into EXE? - c#

I have two DLL files which I'd like to include in my EXE file to make it easier to distribute it. I've read a bit here and there how to do this, even found a good thread here, and here, but it's far too complicated for me and I need real basic instructions on how to do this.
I'm using Microsoft Visual C# Express 2010, and please excuse my "low standard" question, but I feel like I'm one or two level below everyone else's expercise :-/ If someone could point out how to merge these DDL files into my EXE in a step-by-step guide, this would be really awesome!

For .NET Framework 4.5
ILMerge.exe /target:winexe /targetplatform:"v4,C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0" /out:finish.exe insert1.exe insert2.dll
ILMerge
Open CMD and cd to your directory. Let's say: cd C:\test
Insert the above code.
/out:finish.exe replace finish.exe with any filename you want.
Behind the /out:finish.exe you have to give the files you want to be
combined.

Use Costura.Fody.
You just have to install the nuget and then do a build. The final executable will be standalone.

Download ilmerge and ilmergre gui . makes joining the files so easy
ive used these and works great

Reference the DLL´s to your Resources and and use the AssemblyResolve-Event to return the Resource-DLL.
public partial class App : Application
{
public App()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
Assembly thisAssembly = Assembly.GetExecutingAssembly();
//Get the Name of the AssemblyFile
var name = args.Name.Substring(0, args.Name.IndexOf(',')) + ".dll";
//Load form Embedded Resources - This Function is not called if the Assembly is in the Application Folder
var resources = thisAssembly.GetManifestResourceNames().Where(s => s.EndsWith(name));
if (resources.Count() > 0)
{
var resourceName = resources.First();
using (Stream stream = thisAssembly.GetManifestResourceStream(resourceName))
{
if (stream == null) return null;
var block = new byte[stream.Length];
stream.Read(block, 0, block.Length);
return Assembly.Load(block);
}
}
return null;
};
}
}

Download
ILMerge
Call
ilmerge /target:winexe /out:c:\output.exe c:\input.exe C:\input.dll

Install ILMerge
as the other threads tell you to
Then go to the installation folder, by default
C:\Program Files (x86)\Microsoft\ILMerge
Drag your Dll's and Exes to that folder
Shift-Rightclick in that folder and choose open command prompt
Write
ilmerge myExe.exe Dll1.dll /out:merged.exe
Note that you should write your exe first.
There you got your merged exe. This might not be the best way if your going to
do this multiple times, but the simplest one for a one time use, I would
recommend putting Ilmerge to your path.

static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
/* PUT THIS LINE IN YOUR CLASS PROGRAM MAIN() */
AppDomain.CurrentDomain.AssemblyResolve += (sender, arg) => { if (arg.Name.StartsWith("YOURDLL")) return Assembly.Load(Properties.Resources.YOURDLL); return null; };
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
First add the DLL´s to your project-Resources. Add a folder "Resources"

2019 Update (just for reference):
Starting with .NET Core 3.0, this feature is supported out of the box. To take advantage of the single-file executable publishing, just add the following line to the project configuration file:
<PropertyGroup>
<PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>
Now, dotnet publish should produce a single .exe file without using any external tool.
More documentation for this feature is available at https://github.com/dotnet/designs/blob/master/accepted/single-file/design.md.

Also you can use ilmergertool at codeplex with GUI interface.

Here is the official documentation. This is also automatically downloaded at step 2.
Below is a really simple way to do it and I've successfully built my app using .NET framework 4.6.1
Install ILMerge nuget package either via gui or commandline:
Install-Package ilmerge
Verify you have downloaded it. Now Install (not sure the command for this, but just go to your nuget packages):
Note: You probably only need to install it for one of your solutions if you have multiple
Navigate to your solution folder and in the packages folder you should see 'ILMerge' with an executable:
\FindMyiPhone-master\FindMyiPhone-master\packages\ILMerge.2.14.1208\tools
Now here is the executable which you could copy over to your \bin\Debug (or whereever your app is built) and then in commandline/powershell do something like below:
ILMerge.exe myExecutable.exe myDll1.dll myDll2.dll myDlln.dll myNEWExecutable.exe
You will now have a new executable with all your libraries in one!

I answered a similar question for VB.NET. It shouldn't however be too hard to convert. You embedd the DLL's into your Ressource folder and on the first usage, the
AppDomain.CurrentDomain.AssemblyResolve event gets fired.
If you want to reference it during development, just add a normal DLL reference to your project.
Embedd a DLL into a project

NOTE: if you're trying to load a non-ILOnly assembly, then
Assembly.Load(block)
won't work, and an exception will be thrown:
more details
I overcame this by creating a temporary file, and using
Assembly.LoadFile(dllFile)

I Found The Solution Below are the Stpes:-
Download ILMerge.msi and Install it on your Machine.
Open Command Prompt
type cd C:\Program Files (x86)\Microsoft\ILMerge Preess Enter
C:\Program Files (x86)\Microsoft\ILMerge>ILMerge.exe /target:winexe /targetplatform:"v4,C:\Windows\Microsoft.NET\Framework\v4.0.30319"
/out:NewExeName.exe SourceExeName.exe DllName.dll
For Multiple Dll :-
C:\Program Files (x86)\Microsoft\ILMerge>ILMerge.exe /target:winexe /targetplatform:"v4,C:\Windows\Microsoft.NET\Framework\v4.0.30319"
/out:NewExeName.exe SourceExeName.exe DllName1.dll DllName2.dll DllName3.dll

The command should be the following script:
ilmerge myExe.exe Dll1.dll /target:winexe /targetplatform:"v4,c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\" /out:merged.exe /out:merged.exe

Related

CefSharp.offscreen in LinqPad

LinqPad is my goto REPL and there isn't much I throw at it that it cant handle.
However I cannot for the life of me get CefSharp (specifically OffScreen) to run.
I'm constantly met with either of the below errors
Could not load file or assembly 'CefSharp.Core.Runtime, Version=95.7.141.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138' or one of its dependencies. The system cannot find the file specified.
BadImageFormatException: Could not load file or assembly 'CefSharp.Core.Runtime, Version=95.7.141.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138'. An attempt was made to load a program with an incorrect format.
I have tried
LP5/6 32 and 64 bit
Adding Cefsharp via nuget
Referencing .dll's manually from the file system
Referencing x86 or x64 .dll's
Copying .dll's into assembly search paths
Adding nuget paths to Environment path
And what seems like every combination of the above.
I don't understand the assembly resolution process that Visual Studio uses with the nuget package, but whatever it does I would like to at least simulate in Linqpad so I can avoid the VS ceremony when testing something simple.
I assume that manually referencing the correct .dll's and maybe setting a path somewhere should be sufficient, but I'm ideas=>EOF.
Can CefSharp be run outside of VS / MSBuild ?
It doesn't work because of the shadow-copying that LinqPad is using. Here is a hack to make your problem go away (spoiler alert: not really, read on):
For LinqPad v5
Copy all CefSharp libraries to a separate folder (don't forget cef.redist).
In LinqPad Preferences dialog (Advanced/Execution), set Do not shadow assembly references to True, restart LinqPad.
Write your code in the LinqPad query.
Reference CefSharp libraries from the folder you've set up on step 1.
Run the query.
For previous LinqPad (earlier than v5)
Write your code in the LinqPad query.
Reference CefSharp libraries, so you get an exception from your question
Find a LinqPad working directory (usually something like C:\Users\<user>\AppData\Local\Temp\LINQPad5\_yyigmhzg).
Copy all CefSharp libraries to this folder (don't forget cef.redist).
In LinqPad, click Ctrl + Shift + F5; this will reset the query state.
Rerun the query.
Now all the referenced libraries should load. But you will likely face more problems after that.
I couldn't make CefSharp.MinimalExample work. LinqPad kept crashing for me with the cryptic message Query ended because an uncatchable exception was thrown and a crashdump.
Although I am not sure if you will make CefSharp work as intended under LinqPad, maybe this can get you a bit further.
Found the answer with motivation from #Sasha's post and #amaitland's note about BadImageFormatException's being more than just incorrect architectures.
The below is all in reference to LP6 and CefSharp.Offscreen.NetCore. I have not pushed the efforts into LP5 but the process should be similar.
After some trial and error I narrowed down all of the necessary dependencies and worked out why CefSharp would not run in LinqPad.
Here are the steps to make it run -
Add CefSharp.Offscreen.NetCore package as normal to query
Enable Copy all NuGet assemblies into a single local folder (F4->Advanced)
Add the OnInit() and queryPath code as below to the query
Ensure the BrowserSubprocessPath is set before Initializing Cef
Here is the code.
async Task Main()
{
var are = new AutoResetEvent(false);//my technique for waiting for the browser
var sett = new CefSettings();
sett.BrowserSubprocessPath = this.queryPath + #"\CefSharp.BrowserSubprocess.exe"; //CefSharp will complain it cant find it
if (!Cef.IsInitialized)
Cef.Initialize(sett);
var browser = new ChromiumWebBrowser("http://www.google.com");
browser.LoadingStateChanged += (sender, args) => { if (!args.IsLoading) are.Set(); };
are.WaitOne();
await browser.WaitForInitialLoadAsync();
var html = await browser.GetBrowser().MainFrame.GetSourceAsync();
html.Dump("winner winner chicken dinner");
}
//this is the location of the queries shaddow folder
string queryPath = Path.GetDirectoryName(typeof(CefSettings).Assembly.Location);
void OnInit() // Executes when the query process first loads
{
if (!Directory.Exists(queryPath + #"\locales")) //subdirectory of cef.redist
{
var nugetPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var sources = new[] {
/*paths here are hardcoded version dependant. Can get cefsharp.common.netcore version
from Util.CurrentQuery.NuGetReferences, but cef.redist not available via that method. */
#"cef.redist.x64\95.7.14\CEF", //contans all the Cef dependencies needed
#"cefsharp.common.netcore\95.7.141\runtimes\win-x64\lib\netcoreapp3.1", //mainly for ijwhost.dll
#"cefsharp.common.netcore\95.7.141\runtimes\win-x64\native"}; //contains BrowserSubprocess & friends
var dst = new DirectoryInfo(queryPath);
foreach (var path in sources)
{
var src = new DirectoryInfo($#"{nugetPath}\.nuget\packages\{path}");
CopyFilesRecursively(src, dst);
}
}
}
//curtesy of https://stackoverflow.com/a/58779/2738249 with slight mod
public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
{
foreach (DirectoryInfo dir in source.GetDirectories())
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
foreach (FileInfo file in source.GetFiles())
{
var dst = Path.Combine(target.FullName, file.Name);
if (!File.Exists(dst))
file.CopyTo(dst);
}
}
The why for those interested -
CefSharp needs every dependency to be in the same directory so they can be resolved at runtime, but Linqpad only copies a few key dll's from the NuGet package. None of the cef.redist files, ijwhost.dll or BrowserSubprocess.exe et al. come across. Dependencies are scattered between NuGet packages and trying to resolve them directly from the .nuget cache just does not work. So all these need to be brought in manually to the running query shadow path.
I did initially copy all files into the Assembly.GetExecutingAssembly().Location path, but this approach requires adding the assembly directory to the "path" environment variable.
Internally Linqpad seems to have the shadow path set, so copying the dependencies to the shadow folder skips the need to set the environment variable.

Relocating dependencies for my application throws System.IO.FileNotFoundException

I'm trying to clean up my Release folder so dependency files have some type of organization to them. My goal in this case is to contain all the dependencies I need for my compression module into a single folder. For this first part of my new class library I'm just trying to support 7zip files. I do this using the SevenZipSharp.Interop NuGet which depends on the SevenZipSharp NuGet.
By default, the above NuGets build results in my Release/Debug folders and look like the following...
Release
|- x64
| `- 7z.dll
|- x86
| `- 7z.dll
|- SevenZipSharp.dll
`- compressionmodule.dll
As I add more support to my class library I want to make sure everything has it's own folder etc. So, I want to organize it in the following way...
Release
|- compressionmodule
| `- 7zip
| |- x64
| | `- 7z.dll
| |- x86
| | `- 7z.dll
| `- SevenZipSharp.dll
`- compressionmodule.dll
I do this by running a few POST BUILD commands and once the project builds I get the structure I want above (the latter). However, when debugging, after I compile and the POST BUILD commands occur, the application tries to run and I get a System.IO.FileNotFoundException error telling me it can't find SevenZipSharp.dll. I knew this would happen in the back of my mind but was hoping there was a way to let the application know the new location of the dependencies.
Is it possible to relocate the files and then let my application know they are in a different place somehow?
I ended up using the NuGet package Costura.Fody (repository found here). The way it wraps up dependencies in your class module (or program) is nothing short of magic. When using Costura.Fody it will look for any dependencies and automatically compress them into you class library dll making a single dll. It will then make all references to the resource point to the embedded resources in your dll. All you need to do is add the two NuGet packages and it will do all the work when you build the project. It's really slick!
Important Note: Fody's (a dependency of Costura.Fody needed to
run Costura.Fody) current version is only compatible with Visual
Studio 2019 (using MSBuild 16). In my case I have Visual Studio 2017
(which uses MSBuild 15). I had to use an old version of
Costura.Fody that still used the old MSBuild. That also required me
to downgrade Fody as well. So I ended up using the latest versions
that would work with MSBuild 15. Fody version 4.2.1 and Costura.Fody
version 3.3.3 works great with Visual Studio 2017.
It even supports extracting the resource if needed. For example, I was looking to use this on the SevenZipSharp library. It requires a path to the 7zip.dll file to be used. So I used the following in the FodyWeavers.xml file that is located in the same directory as your solution.
<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Costura CreateTemporaryAssemblies='true' IncludeDebugSymbols='false'>
<Unmanaged32Assemblies>
7z
</Unmanaged32Assemblies>
<Unmanaged64Assemblies>
7z
</Unmanaged64Assemblies>
</Costura>
</Weavers>
You need to also include the 7zip.dll in your project so it compiles the dlls as they are not used like resources (like the SevenZipSharp library is). You do this by adding the following folders in the root of your project as well as the appropriate architectures files. Then add them as Build Action: Embedded Resource.
This would extract the 7zip.dll dependency at run-time when needed so I could reference the path when using the SevenZipSharp library. At this point I have not found out how to get a reference to the (C83815D61CE49B2E8D23145A1B95A956 maybe a checksum of all the files in the directory itself?) directory it uses but in my case this is where is was extracting it to...
%tmp%\Costura\C83815D61CE49B2E8D23145A1B95A956\32\
Update: I found a workaround to getting the path of that checksum like directory using Assembly information. I created the following function to try and set the 7Zip library path. It might be helpful to some so I'm including it here.
private bool SetupSevenZipLibrary()
{
string costuraExtractionPath = null;
try
{
//This will use the embeded resource to try and set a base path to the extraction
//location created by by Costura.Fody
string sevenZipAssembly = typeof(SevenZip.ArchiveFileInfo).Assembly.Location;
if (File.Exists(sevenZipAssembly))
costuraExtractionPath = Path.GetDirectoryName(sevenZipAssembly);
}
catch
{
//Issue trying to grab the extraction path from the Assembly information so try
//to use the first path found in the Costura directory as a last ditch effort
DirectoryInfo di = null;
string costuraTempPath = Path.Combine(
Path.GetTempPath(), //ex: C:\Users\username\AppData\Local\Temp
"Costura" //ex: Costura
);
di = new DirectoryInfo(costuraTempPath);
if (!di.Exists)
return false;
//ex: C:\Users\username\AppData\Local\Temp\Costura\C83815D61CE49B2E8D23145A1B95A956\
costuraExtractionPath = di.GetDirectories().First().FullName;
}
try
{
if (!Directory.Exists(costuraExtractionPath))
throw new Exception();
string sevenZipPath = Path.Combine(
costuraExtractionPath, //ex: C:\Users\username\AppData\Local\Temp\Costura\C83815D61CE49B2E8D23145A1B95A956
Environment.Is64BitProcess ? "64" : "32", //ex: 32
"7z.dll" //ex: 7z.dll
);
if (!File.Exists(sevenZipPath))
throw new Exception();
SevenZipBase.SetLibraryPath(sevenZipPath);
return true;
}
catch { return false; }
}
It's usage...
if (!SetupSevenZipLibrary())
throw new Exception("Error setting the path of the 7zip library.");

Opening a solution with msbuildworkspace gives diagnostics errors without details

I am trying to analyse a solution with Roslyn, with MSBuildWorkspace.
The solution is a new solution, with 2 class library projects in them, one referencing the other.
They are created in Visual Studio 2017, .Net 4.6.2.
When I open the solution, I receive two generic errors in workspace.Diagnostics, both are :
Msbuild failed when processing the file 'PathToProject'
There is nothing more in the diagnostics or output window, to indicate WHY it failed to process the project file.
The code for opening the solution:
namespace RoslynAnalyse
{
class Program
{
static void Main(string[] args)
{
LocalAnalysis();
}
private static void LocalAnalysis()
{
var workspace = MSBuildWorkspace.Create();
var solution = workspace.OpenSolutionAsync(#"D:\Code\Roslyn\RoslynAnalyse\SolutionToAnalyse\SolutionToAnalyse.sln").Result;
var workspaceDiagnostics = workspace.Diagnostics;
}
}
}
The version of Microsoft.CodeAnalysis is 2.0.0.0.
Does anybody have any idea why MSBuild failed, how I can get more information ?
When MSBuildWorkspace fails to open a project or solution this way, it is almost always because the application using MSBuildWorkspace does not include the same binding redirects that msbuild.exe.config has in it.
MSBuild uses binding redirects to allow tasks (typically already compiled C# code using possibly different versions of msbuild API libraries) to all use the current msbuild API's. Otherwise, msbuild gets runtime load failures.
The solution is to add an app.config file to your project and copy the binding redirects (the assemblyBinding section of the msbuild.exe.config file) into your file.

How to zip files/directories in VB.NET?

I would like to zip a folder containing files and subfolders in VB.NET. My solution targets .NET 4.0 Client Profile.
I see that there is a ZipFile class for .NET 4.5, and System.IO.Packing for .NET 4.0 (but not Client Profile). So, those won't help. I note that there is also a GZipStream class, but I never see .gz files floating around, so not sure if that's a good approach. I would prefer a basic .zip file that I know my users can work with.
There are a handful of third-party solutions, such as http://icsharpcode.github.io/SharpZipLib/, but I assume they are far more bloated than the 10-20 lines of code I am looking for. Maybe that's the right answer...I don't know.
Just hoping for a few lines of VB.NET code to zip some files in a solution targeting .NET 4.0 CP.
Create ZIP from "source" folder.
Imports System.IO.Compression
ZipFile.CreateFromDirectory("source","destination.zip",CompressionLevel.Optimal,False)
Extract ZIP to "destination" folder.
ZipFile.ExtractToDirectory("destination.zip","destination")
Unable to comment in the comments section due to lack of reputation (due to being new here).
On the two answers indicating that using System.IO.Compression can be used that were commented as not working in NET 4.0 CP, this should be noted, as the original answers did not include an often overlooked fact.
A reference needs to be added to the project in order to activate "System.IO.Compression". This is not obvious to many new VS users, and unless the refrence is added, it indeed seems like "System.IO.Compression" does not work in NET 4.0.
In this case, the reference that needs to be added is System.IO.Compression.Filesystem (v4.0.0.0). One may also wish to add the reference: System.IO.Compression.
The "Add Reference..." dialog is under the "Project" menu in VS.
The MSDN Website has a class that can help you out here called ZipFile
using System;
using System.IO;
using System.IO.Compression;
namespace ConsoleApplication {
class Program {
static void Main(string[] args) {
string startPath = #"c:\example\start";
string zipPath = #"c:\example\result.zip";
string extractPath = #"c:\example\extract";
'Creates Zip File to directory as specified (startPath). And puts it in a specifed folder (zipPath)
ZipFile.CreateFromDirectory(startPath, zipPath);
'
Extracts Zip File to directory as specified(extractpath)
ZipFile.ExtractToDirectory(zipPath, extractPath);
}
}
}
Hope this helps!
Simply you can do it using Ionic.Zip.Dll
Here is the function for this,accepting two variables source path and destination path
public static void ExecuteBackupAsZip(string SourcePath, string DestinationPath)
{
using (var zip = new Ionic.Zip.ZipFile())
{
try
{
zip.AddDirectory(SourcePath);
zip.Save(DestinationPath);
}
catch { Console.WriteLine("Failed to execute backup"); }
}
}
Same challenges as "an odder guest". That is can't commment due to low rep as new user and have to add a new answer. (Frequent reader though). Running on vb.Net 4.6 and got no errors on the suggestions above, but no output zip file was generated. Then after some googling added references System.IO.Compression.Filesystem and System.IO.Compression and ended up with the following one-liner:
System.IO.Compression.ZipFile.CreateFromDirectory(FilePath1, FilePath3_ZippedFile)
Where FilePath1 is the path to the source folder (c:\whateverfolder) and FilePath3_ZippedFile is the path to the resulting output zip-file(c:\anotherfolder\MyZipFile.zip).
Sorry if you think my input is redundant, but I know for newcommers like my self we need the answers feed with tee spoons.
Word of caution: If you try to create the zip-file in the source folder you might have issues. My code then only included one of the files from the source folder in the zip-file. (No error messages). Changing the target location to another folder solved this without other edits to the code.

Can't embed EPPlus.dll into my exe

I need to embed EPPlus.dll to my standalone exe app. I don't want it to be copied along the exe. Apparently icluding it as an assembly resource would solve my problem. I found many decriptions on how to do it. For e.g. I performed all the below:
https://www.youtube.com/watch?v=x-KK7bmo1AM
http://adamthetech.com/2011/06/embed-dll-files-within-an-exe-c-sharp-winforms/
Embedding DLL's into .exe in in Visual C# 2010
Still I get Could not load file or assembly 'EPPlus.dll'...
Can you please give me some idea as I have never done something like this before?
P.S.: I use VS C# 2010 Express
Try having a look at this in your application:
In the program.cs main method:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
Create the event:
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.Name.StartsWith("MyAssembly,"))
{
return Assembly.Load(Properties.Resources.MyAssembly_Dll);
}
return null;
}
I am using something similar for dynamically loading an assembly that requires other assemblies that have not yet been loaded and so I load them as required.
Comments welcome.
Download ILMerge from here then install ILMerge.
After that you should have the following file C:\Program Files (x86)\Microsoft\ILMerge\ILMerge.exe or C:\Program Files\Microsoft\ILMerge\ILMerge.exe if you are on a 32bit system.
Then you execute ILMerge.exe with the following parameters /out:OutputExeFile.exe InputExeFile.exe dllfile.dll
After that ILMerge should have merged InputExeFile.exe and dllfile.dll and stored the result in OutputExeFile.exe
if you just run the ilmerge you will get the following help
Usage: ilmerge [/lib:directory]* [/log[:filename]] [/keyfile:filename
[/delaysign]] [/internalize[:filename]]
[/t[arget]:(library|exe|winexe)] [/closed] [/ndebug] [/ver:version]
[/copyattrs [/allowMultiple] [/keepFirst]] [/xmldocs] [/attr:filename]
[/targetplatform:[,] | /v1 | /v1.1 | /v2 | /v4]
[/useFullPublicKeyForReferences] [/wildcards] [/zeroPeKind]
[/allowDup:type]* [/union] [/align:n] /out:filename
[...]

Categories