How can I merge System.Data.SQLite into a single-executable program? - c#

I'm trying to create a single-executable application in C#, which includes SQLite. System.Data.SQLite depends on one unmanaged DLL (SQLite.Interop.dll), so I can't merge it with ILMerge.
How can I bundle System.Data.SQLite into my project so I can produce a single-executable application with no tag-along DLLs?

You can include the dll as an embedded resource in the executable, then at runtime extract it (this assumes the program has permissions to write to whatever directory you are extracting the dll to).
Something like
string sqllitefile = "sqllite.dll";
Assembly currentAssembly = Assembly.GetExecutingAssembly();
using (FileStream fs = fileInfoOutputFile.OpenWrite())
using (Stream resourceStream =currentAssembly.GetManifestResourceStream(sqllitefile))
{
const int size = 4096;
byte[] bytes = new byte[4096];
int numBytes;
while ((numBytes = resourceStream.Read(bytes, 0, size)) > 0)
{
fs.Write(bytes, 0, numBytes);
}
fs.Flush();
fs.Close();
resourceStream.Close();
}

Related

Unity. Unload DLL at runtime

So, currently I have a dll that is loaded when the application starts. I have both used
using (FileStream fileStream = File.Open(DLLData.LocalDLLFolder() + "DLLSupport.dll", FileMode.Open))
{
using (MemoryStream memoryStream = new MemoryStream())
{
byte[] buffer = new byte[1024];
int read = 0;
while ((read = fileStream.Read(buffer, 0, 1024)) > 0)
{
memoryStream.Write(buffer, 0, read);
}
Assembly assembly = Assembly.Load(memoryStream.ToArray());
Type _main = assembly.GetType("DLLSupport.DebugStart");
dllSupport = new GameObject();
dllSupport.AddComponent(_main);
memoryStream.Close();
fileStream.Close();
}
}
and Assembly.LoadFrom
The thing is that when is running I will download this same DLL again if its updated.
If I try to download it with Assembly.LoadFrom It wont allow me since the file is being used by Unity.
With the other method I can download or delete it when I want, but the thing is that even if I call that method again it will work like the previous one until I restart the application. (Maybe there is any way to unload that that I didnt found?)
I tried it too with AppDomain but I will get FileNotFoundException: Could not load file or assembly or one of its dependencies
Any idea?
You cannot unload an assembly from the curren AppDomain, but you can create an AppDomain, and load assembly into it, execute some codes on that AppDomain, and then Unload the AppDomain when necessary, this will also Unload the assembly on that domain.
Reference : https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2008/ms173101(v=vs.90)?redirectedfrom=MSDN

Writing the Assembly version to a string and saving this into a textfile C#

Hi I am trying to write the assembly version to a textfile for use with an autoupdater program.
This is what I have for getting the current assembly version to a string.
// Get assembly info to string
string assemblyVersion = AssemblyName.GetAssemblyName("MainApplication.exe").Version.ToString();
This is then being written to a textfile using filestream.
private void SaveVersion()
{
// creating filestream that can write a file
FileStream fs = new FileStream("Version.txt", FileMode.Create, FileAccess.Write);
// if we don't have permission to write we exit function
if (!fs.CanWrite)
return;
byte[] buffer = Encoding.ASCII.GetBytes(assemblyVersion);
// writing whole buffer array
fs.Write(buffer, 0, buffer.Length);
// closing filestream
fs.Flush();
fs.Close();
}
However for some reason the Version.txt file is never being populated.
What am I missing here. Thanks.
You can do this with a single line of code
System.IO.File.WriteAllText("Version.txt", assemblyVersion);

Unzip .zip file from Assets folder to Internal Storage (Xamarin.Android)

So the question is pretty simple. I'm using Xamarin.Android and I have a zip file in the Assets folder named "MyZipFile.zip", which I want extracted to the following path: System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
It sounds simple enough, but I cannot figure out how to read the Asset into memory through the AssetManager and then unzip it at the targeted location.
Is there a simple way to do this?
The Android Java framework includes a Java.Util.Zip package, so without adding any additional app libraries, I directly use it instead of using C# framework code, thus no bloat that linking can not remove.
So basically you are creating an asset stream and feeding that to a ZipInputStream and iterating over each ZipEntry in that zip stream to either create directories or files to your destination path.
UnZipAssets
public void UnZipAssets(string assetName, string destPath)
{
byte[] buffer = new byte[1024];
int byteCount;
var destPathDir = new Java.IO.File(destPath);
destPathDir.Mkdirs();
using (var assetStream = Assets.Open(assetName, Android.Content.Res.Access.Streaming))
using (var zipStream = new ZipInputStream(assetStream))
{
ZipEntry zipEntry;
while ((zipEntry = zipStream.NextEntry) != null)
{
if (zipEntry.IsDirectory)
{
var zipDir = new Java.IO.File(Path.Combine(destPath, zipEntry.Name));
zipDir.Mkdirs();
continue;
}
// Note: This is deleting existing entries(!!!) for debug purposes only...
#if DEBUG
if (File.Exists(Path.Combine(destPath, zipEntry.Name)))
File.Delete(Path.Combine(destPath, zipEntry.Name));
#endif
using (var fileStream = new FileStream(Path.Combine(destPath, zipEntry.Name), FileMode.CreateNew))
{
while ((byteCount = zipStream.Read(buffer)) != -1)
{
fileStream.Write(buffer, 0, byteCount);
}
}
Log.Debug("UnZipAssets", zipEntry.Name);
zipEntry.Dispose();
}
}
}
Usage:
UnZipAssets("gameModLevels.zip", Path.Combine(Application.Context.CacheDir.AbsolutePath, "assets"));
Note: Even through the asset/zip steam is fast, depending upon number/size of the zip entries and the speed of the flash the entry is being written to, this should be done on a background thread as not to block UI thread and cause an ANR

How to retrieve .dotx and .csv files from Resources Folder in C#?

I have an application that uses a template file and a CSV file. It works perfectly fine since I do have the said files on my computer and I am referencing their paths. The only thing I want to do is that when the software is published and installed (and the said files are in the Resources folder so it would make them an embedded resource), the csv and template files would be copied to a directory folder that my program would use. Preferably the paths it would be copied to would be something like this : "C:\FILES" + template.dotx .
Now how do I get/retrieve the said files from the Resources Folder in my software into a new folder?
You could call
System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
And inspect which embedded resources are accessible. Then you can compare that against what you are passing in to see if you are indeed accomplishing what you expected to.
string FileExtractTo = "C:\FILES";
DirectoryInfo dirInfo = new DirectoryInfo(FileExtractTo);
if (!dirInfo.Exists())
dirInfo.Create();
using (Stream input = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
using (Stream output = File.Create(FileExtractTo + "\template.dotx"))
{
CopyStream(input, output);
}
CopyStream Method:
public static void CopyStream(Stream input, Stream output)
{
// Insert null checking here for production
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, bytesRead);
}
}

Embedding one dll inside another as an embedded resource and then calling it from my code

I've got a situation where I have a DLL I'm creating that uses another third party DLL, but I would prefer to be able to build the third party DLL into my DLL instead of having to keep them both together if possible.
This with is C# and .NET 3.5.
The way I would like to do this is by storing the third party DLL as an embedded resource which I then place in the appropriate place during execution of the first DLL.
The way I originally planned to do this is by writing code to put the third party DLL in the location specified by System.Reflection.Assembly.GetExecutingAssembly().Location.ToString()
minus the last /nameOfMyAssembly.dll. I can successfully save the third party .DLL in this location (which ends up being
C:\Documents and Settings\myUserName\Local Settings\Application
Data\assembly\dl3\KXPPAX6Y.ZCY\A1MZ1499.1TR\e0115d44\91bb86eb_fe18c901
), but when I get to the part of my code requiring this DLL, it can't find it.
Does anybody have any idea as to what I need to be doing differently?
Once you've embedded the third-party assembly as a resource, add code to subscribe to the AppDomain.AssemblyResolve event of the current domain during application start-up. This event fires whenever the Fusion sub-system of the CLR fails to locate an assembly according to the probing (policies) in effect. In the event handler for AppDomain.AssemblyResolve, load the resource using Assembly.GetManifestResourceStream and feed its content as a byte array into the corresponding Assembly.Load overload. Below is how one such implementation could look like in C#:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var resName = args.Name + ".dll";
var thisAssembly = Assembly.GetExecutingAssembly();
using (var input = thisAssembly.GetManifestResourceStream(resName))
{
return input != null
? Assembly.Load(StreamToBytes(input))
: null;
}
};
where StreamToBytes could be defined as:
static byte[] StreamToBytes(Stream input)
{
var capacity = input.CanSeek ? (int) input.Length : 0;
using (var output = new MemoryStream(capacity))
{
int readLength;
var buffer = new byte[4096];
do
{
readLength = input.Read(buffer, 0, buffer.Length);
output.Write(buffer, 0, readLength);
}
while (readLength != 0);
return output.ToArray();
}
}
Finally, as a few have already mentioned, ILMerge may be another option to consider, albeit somewhat more involved.
In the end I did it almost exactly the way raboof suggested (and similar to what dgvid suggested), except with some minor changes and some omissions fixed. I chose this method because it was closest to what I was looking for in the first place and didn't require using any third party executables and such. It works great!
This is what my code ended up looking like:
EDIT: I decided to move this function to another assembly so I could reuse it in multiple files (I just pass in Assembly.GetExecutingAssembly()).
This is the updated version which allows you to pass in the assembly with the embedded dlls.
embeddedResourcePrefix is the string path to the embedded resource, it will usually be the name of the assembly followed by any folder structure containing the resource (e.g. "MyComapny.MyProduct.MyAssembly.Resources" if the dll is in a folder called Resources in the project). It also assumes that the dll has a .dll.resource extension.
public static void EnableDynamicLoadingForDlls(Assembly assemblyToLoadFrom, string embeddedResourcePrefix) {
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { // had to add =>
try {
string resName = embeddedResourcePrefix + "." + args.Name.Split(',')[0] + ".dll.resource";
using (Stream input = assemblyToLoadFrom.GetManifestResourceStream(resName)) {
return input != null
? Assembly.Load(StreamToBytes(input))
: null;
}
} catch (Exception ex) {
_log.Error("Error dynamically loading dll: " + args.Name, ex);
return null;
}
}; // Had to add colon
}
private static byte[] StreamToBytes(Stream input) {
int capacity = input.CanSeek ? (int)input.Length : 0;
using (MemoryStream output = new MemoryStream(capacity)) {
int readLength;
byte[] buffer = new byte[4096];
do {
readLength = input.Read(buffer, 0, buffer.Length); // had to change to buffer.Length
output.Write(buffer, 0, readLength);
}
while (readLength != 0);
return output.ToArray();
}
}
There's a tool called IlMerge that can accomplish this: http://research.microsoft.com/~mbarnett/ILMerge.aspx
Then you can just make a build event similar to the following.
Set Path="C:\Program Files\Microsoft\ILMerge"
ilmerge /out:$(ProjectDir)\Deploy\LevelEditor.exe $(ProjectDir)\bin\Release\release.exe $(ProjectDir)\bin\Release\InteractLib.dll $(ProjectDir)\bin\Release\SpriteLib.dll $(ProjectDir)\bin\Release\LevelLibrary.dll
I've had success doing what you are describing, but because the third-party DLL is also a .NET assembly, I never write it out to disk, I just load it from memory.
I get the embedded resource assembly as a byte array like so:
Assembly resAssembly = Assembly.LoadFile(assemblyPathName);
byte[] assemblyData;
using (Stream stream = resAssembly.GetManifestResourceStream(resourceName))
{
assemblyData = ReadBytesFromStream(stream);
stream.Close();
}
Then I load the data with Assembly.Load().
Finally, I add a handler to AppDomain.CurrentDomain.AssemblyResolve to return my loaded assembly when the type loader looks it.
See the .NET Fusion Workshop for additional details.
You can achieve this remarkably easily using Netz, a .net NET Executables Compressor & Packer.
Instead of writing the assembly to disk you can try to do Assembly.Load(byte[] rawAssembly) where you create rawAssembly from the embedded resource.

Categories