SevenZipSharp library not working on Xamarin forms - c#

We are working on a Xamarin project that needs to split a large file into smaller files with specified file sizes. we tried to use the SeveZipSharp library by installing the squid-box-sevenzipsharp NuGet package which supports xamarin forms for compressing and decompressing large files.
But while we compress the large files, we got an exception: kernel32.dll assembly: type: member:(null)
For your reference, this is our code,
SevenZipCompressor.SetLibraryPath(#"/Users/macmini3/Documents/7z/7z.dll");
SevenZipCompressor compressor = new SevenZipCompressor();
compressor.ArchiveFormat = OutArchiveFormat.SevenZip;
compressor.TempFolderPath = Path.GetTempPath();
compressor.CompressionMode = CompressionMode.Create;
// compressor.CompressionLevel = CompressionLevel.Fast;
//compressor.CompressionMethod = CompressionMethod.Lzma2;
//compressor.ZipEncryptionMethod = ZipEncryptionMethod.Aes256;
compressor.VolumeSize = 5000000;
var assemblyDllPath = compressor.TempFolderPath + "32-7z.dll";
//var assemblyDllPath = compressor.TempFolderPath + "64-7z.dll";
File.Copy(dll, assemblyDllPath, overwrite: true);
// SevenZipExtractor.SetLibraryPath(dll);
compressor.CompressDirectory(filename, output);
}
catch (Exception ex)
{
// throws an exception, Kernel32.dll assembly:<unknown assembly> type:<unknown type> member:(null)
}
We are using Visual Studio Community 2019 for mac (Version: 8.10.25) and Xamarin Forms (Version: 4.5.0.356)
Can anyone please help us to resolve this issue?
Does the SevenZipSharp library support the Xamarin Forms project?
Can anyone suggest any other thirty-party library (Paid/Free) to split large files into smaller files that support Xamarin Forms?

Related

A problem with System.DllNotFoundException in the dll file created by including ippiFFTGetSize_C_32fc ().(intel IPP)

I created a dll created using the IPP function ippiFFTGetSize_C_32fc ().
Referencing this dll file from wpf project (c #) results in System.DllNotFoundException: Unable to load DLL 'TEST.dll' error.
int HioTest(Ipp32fc* csSrcImg, Ipp32fc* csDstImg)
{
// FFT Init
IppiFFTSpec_C_32fc *pSpec = NULL; /* Pointer to FFT spec structure */
Ipp8u *pMemInit = NULL, *pBuffer = NULL; /* Pointer to the work buffers */
int sizeSpec = 0, sizeInit = 0, sizeBuf = 0; /* Size of FFT spec structure, init and work buffers */
int order = 9;
ippiFFTGetSize_C_32fc(order, order, IPP_FFT_DIV_INV_BY_N, ippAlgHintAccurate, &sizeSpec, &sizeInit, &sizeBuf);
return 5;
}
I created a TEST.dll (C language) using IPP for External library. I want to refer to TEST.dll in my wpf project and use it.
The tool used is "visual studio 2017" and the IPP version uses the latest version of compilers_and_libraries_2019.1.144.
See the IPP documentation. The settings of the dll project,
Debug / x64
Add ippsmt.lib, ippcoremt.lib for Additional Dependencies.
Automatic "single-threaded DLL" setting.
export .def created.
Add ippInit () to dllmain.cpp
Referencing from C # . /clr setting
C # project
Debug / x64
unlock the loader
Increase the reserve stack size
I set it up like this.
Other ippMalloc (), ippsAdddC_32f_I ()
The dll file I created using the above worked properly in my wpf project. However, TEST.dll containing ippiFFTGetSize_C_32fc () will result in an error System.DllNotFoundException: Unable to load DLL 'TEST.dll'.
I want to know the cause or solution.
In case you are using Microsoft Visual Studio, once try cleaning and the rebuilding it.

AWS Lambda access local resource or storage C#

How can I store and access a file in Lambda using C# I used the tmp folder that is available for the lambda but I get an error of could not load file or assembly. How can I solve the error? I used the ADP nuget.
using (WebClient webClient = new WebClient())
{
webClient.DownloadFile(reportLine, Path.GetTempPath() +
"sample_auth.key");
}
I used this to download the file into the tmp folder of the lambda. I did not include the other config in the string confidential but you can check the github below for the exact same sample.
string config = #"{
""sslCertPath"": ""/tmp/sample.pfx"",
""sslKeyPath"": ""/tmp/sample_auth.key"",
}";
ADPAccessToken token = null;
if (String.IsNullOrEmpty(clientconfig))
{
Console.WriteLine("Settings file or default options not available.");
}
else
{
ClientCredentialConfiguration connectionCfg = JSONUtil.Deserialize<ClientCredentialConfiguration>(clientconfig);
ClientCredentialConnection connection = (ClientCredentialConnection)ADPApiConnectionFactory.createConnection(connectionCfg);
//context.Logger.Log(ADPApiConnection.certificatepath);
//context.Logger.Log(clientconfig);
try
{
connection.connect();
if (connection.isConnectedIndicator())
{
token = connection.accessToken;
// context.Logger.Log("Connected to API end point");
// //Console.WriteLine("Token: ");
// //Console.WriteLine(" AccessToken: {0} ", token.AccessToken);
// //Console.WriteLine(" TokenType: {0} ", token.TokenType);
// //Console.WriteLine(" ExpiresIn: {0} ", token.ExpiresIn);
// //Console.WriteLine(" Scope: {0} ", token.Scope);
// //Console.WriteLine(" ExpiresOn: {0} ", token.ExpiresOn);
// //Console.ReadLine();
}
}
catch (ADPConnectionException e)
{
context.Logger.Log(e.Message);
}
//catch (Exception e)
//{
// context.Logger.Log(e.Message);
//}
//Console.Read();
}
return "Ok";
}
I get an error of I think lambda check the /var/task folder
errorMessage": "One or more errors occurred. (Could not load file or
assembly 'System.Net.Http.WebRequest, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file
specified.\n)",
"cause": {
"errorType": "FileNotFoundException",
"errorMessage": "Could not load file or assembly
'System.Net.Http.WebRequest, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file
specified.\n",
Here is the sample program: https://github.com/adplabs/adp-connection-NET/blob/master/ADPClientDemo/Program.cs
I can run the program on console but when I try to do it in lambda I get an error. Is it because of the NuGet from AWS?
I have the following NuGet
Amazon Lambda Core
Amazon Lambda S3 Events
Amazon lambda Serialization json
AWS SDK Core
Microsoft Asp Net Web Api Client
ADP library connection NET
Could not load file or assembly: System.Net.Http.WebRequest
The error seems to be caused by a versioning issue, I think you need to use a .Net core version of System.Net.Http.WebRequest dll or a later version than .Net 4.0 to work with .NET Core 2.0.
Actually please see this answer you might be out of luck: The libraries you use need to ship targeting .NET Core: https://github.com/dotnet/corefx/issues/28267#issuecomment-396349873
Also see https://stackoverflow.com/a/41683787/495455 and Could not load file or assembly "System.Net.Http.Webrequest" in .NET AWSSDK for mono for similar versioning issues and fixes.
If that doesn't fix it, please consider using the AWS API. You could put your sample_auth.key file on an S3 bucket and read it, eg
https://docs.aws.amazon.com/lambda/latest/dg/with-s3.html
Or as per the example you linked to they package the json file with the Lambda:
https://github.com/adplabs/adp-connection-NET/tree/master/ADPClientDemo/Content/config
And they read it using StreamReader, perhaps this will work using the System.IO dll instead of trying to find the System.Net.Http.WebRequest dll:
string configFileName = "default.json";
StreamReader sr = new StreamReader("..\\..\\Content\\config\\" + configFileName);
string clientconfig = sr.ReadToEnd();

Compile Brotli into a DLL .NET can reference

So I'd like to take advantage of Brotli but I am not familiar with Python and C++..
I know someone had compiled it into a Windows .exe. But how do I wrap it into a DLL or something that a .NET app can reference? I know there's IronPython, do I just bring in all the source files into an IronPython project and write a .NET adapter that calls into the Brotli API and exposes them? But actually, I'm not even sure if the Brotli API is Python or C++..
Looking at tools/bro.cc, it looks like the "entry" methods are defined in encode.c and decode.c as BrotliCompress(), BrotliDecompressBuffer(), BrotliDecompressStream() methods. So I suppose a DLL can be compiled from the C++ classes.
To avoid the need for Python, I have forked the original brotli source here https://github.com/smourier/brotli and created a Windows DLL version of it that you can use with .NET.
I've added a directory that contains a "WinBrotli" Visual Studio 2015 solution with two projects:
WinBrotli: a Windows DLL (x86 and x64) that contains original unchanged C/C++ brotli code.
Brotli: a Windows Console Application (Any Cpu) written in C# that contains P/Invoke interop code for WinBrotli.
To reuse the Winbrotli DLL, just copy WinBrotli.x64.dll and WinBrotli.x86.dll (you can find already built release versions in the WinBrotli/binaries folder) aside your .NET application, and incorporate the BrotliCompression.cs file in your C# project (or port it to VB or another language if C# is not your favorite language). The interop code will automatically pick the right DLL that correspond to the current process' bitness (X86 or X64).
Once you've done that, using it is fairly simple (input and output can be file paths or standard .NET Streams):
// compress
BrotliCompression.Compress(input, output);
// decompress
BrotliCompression.Decompress(input, output);
To create WinBrotli, here's what I've done (for others that would want to use other Visual Studio versions)
Created a standard DLL project, removed the precompiled header
Included all encoder and decoder original brotli C/C++ files (never changed anything in there, so we can update the original files when needed)
Configured the project to remove dependencies on MSVCRT (so we don't need to deploy other DLL)
Disabled the 4146 warning (otherwise we just can't compile)
Added a very standard dllmain.cpp file that does nothing special
Added a WinBrotli.cpp file that exposes brotli compression and decompression code to the outside Windows world (with a very thin adaptation layer, so it's easier to interop in .NET)
Added a WinBrotli.def file that exports 4 functions
I'll show one way to do that via calling python native library from .NET code. What you need:
You need to intall python 2.7 (hope that is obvious)
You need to compile brotli from source. Hopefully that is easy. First install Microsoft Visual C++ compiler for Python 2.7. Then clone brotli repository via git clone https://github.com/google/brotli.git and compile using python setup.py build_ext. When it's done, in build\lib.win32-2.7 directory you will find brotli.pyd file. This is python c++ module - we will need it later.
You need to either download pythonnet binaries or compile it from source. The reason we use pythonnet here, and not for example Iron Python is because Iron Python does not support native (C\C++) python modules, and that is what we need here. So, to compile from source, clone via git clone https://github.com/pythonnet/pythonnet.git then compile via python setup.py build. In result you will get Python.Runtime.dll (in build\lib.win32-2.7 directory), which is what we need.
When you have all that in place, create console project, reference Python.Runtime.dll and then:
public static void Main()
{
PythonEngine.Initialize();
var gs = PythonEngine.AcquireLock();
try {
// import brotli module
dynamic brotli = PythonEngine.ImportModule(#"brotli");
// this is a string we will compress
string original = "XXXXXXXXXXYYYYYYYYYY";
// compress and interpret as byte array. This array you can save to file for example
var compressed = (byte[]) brotli.compress(original);
// little trick to pass byte array as python string
dynamic base64Encoded = new PyString(Convert.ToBase64String(compressed));
// decompress and interpret as string
var decompressed = (string) brotli.decompress(base64Encoded.decode("base64"));
// works
Debug.Assert(decompressed == original);
}
finally {
PythonEngine.ReleaseLock(gs);
PythonEngine.Shutdown();
}
Console.ReadKey();
}
Then build that and put brotli.pyc you get above in the same directory with your .exe file. After all that manipulations you will be able to compress and decompress from .NET code, as you see above.
You may use Brotli.NET which provides full stream support.
github: https://github.com/XieJJ99/brotli.net/.
Nuget: https://www.nuget.org/packages/Brotli.NET/.
To compress a stream to brotli data:
public Byte[] Encode(Byte[] input)
{
Byte[] output = null;
using (System.IO.MemoryStream msInput = new System.IO.MemoryStream(input))
using (System.IO.MemoryStream msOutput = new System.IO.MemoryStream())
using (BrotliStream bs = new BrotliStream(msOutput, System.IO.Compression.CompressionMode.Compress))
{
bs.SetQuality(11);
bs.SetWindow(22);
msInput.CopyTo(bs);
bs.Close();
output = msOutput.ToArray();
return output;
}
}
To decompress a brotli stream:
public Byte[] Decode(Byte[] input)
{
using (System.IO.MemoryStream msInput = new System.IO.MemoryStream(input))
using (BrotliStream bs = new BrotliStream(msInput, System.IO.Compression.CompressionMode.Decompress))
using (System.IO.MemoryStream msOutput = new System.IO.MemoryStream())
{
bs.CopyTo(msOutput);
msOutput.Seek(0, System.IO.SeekOrigin.Begin);
output = msOutput.ToArray();
return output;
}
}
To support dynamic compress in web applications,add the code like this in the Global.asax.cs:
protected void Application_PostAcquireRequestState(object sender, EventArgs e)
{
var app = Context.ApplicationInstance;
String acceptEncodings = app.Request.Headers.Get("Accept-Encoding");
if (!String.IsNullOrEmpty(acceptEncodings))
{
System.IO.Stream baseStream = app.Response.Filter;
acceptEncodings = acceptEncodings.ToLower();
if (acceptEncodings.Contains("br") || acceptEncodings.Contains("brotli"))
{
app.Response.Filter = new Brotli.BrotliStream(baseStream, System.IO.Compression.CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "br");
}
else
if (acceptEncodings.Contains("deflate"))
{
app.Response.Filter = new System.IO.Compression.DeflateStream(baseStream, System.IO.Compression.CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "deflate");
}
else if (acceptEncodings.Contains("gzip"))
{
app.Response.Filter = new System.IO.Compression.GZipStream(baseStream, System.IO.Compression.CompressionMode.Compress);
app.Response.AppendHeader("Content-Encoding", "gzip");
}
}
}

Creating one exe file out of visual studio project with external DLL files [duplicate]

Is it possible to embed a pre-existing DLL into a compiled C# executable (so that you only have one file to distribute)? If it is possible, how would one go about doing it?
Normally, I'm cool with just leaving the DLLs outside and having the setup program handle everything, but there have been a couple of people at work who have asked me this and I honestly don't know.
I highly recommend to use Costura.Fody - by far the best and easiest way to embed resources in your assembly. It's available as NuGet package.
Install-Package Costura.Fody
After adding it to the project, it will automatically embed all references that are copied to the output directory into your main assembly. You might want to clean the embedded files by adding a target to your project:
Install-CleanReferencesTarget
You'll also be able to specify whether to include the pdb's, exclude certain assemblies, or extracting the assemblies on the fly. As far as I know, also unmanaged assemblies are supported.
Update
Currently, some people are trying to add support for DNX.
Update 2
For the lastest Fody version, you will need to have MSBuild 16 (so Visual Studio 2019). Fody version 4.2.1 will do MSBuild 15. (reference: Fody is only supported on MSBuild 16 and above. Current version: 15)
Just right-click your project in Visual Studio, choose Project Properties -> Resources -> Add Resource -> Add Existing Fileā€¦
And include the code below to your App.xaml.cs or equivalent.
public App()
{
AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
return System.Reflection.Assembly.Load(bytes);
}
Here's my original blog post:
http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/
If they're actually managed assemblies, you can use ILMerge. For native DLLs, you'll have a bit more work to do.
See also: How can a C++ windows dll be merged into a C# application exe?
Yes, it is possible to merge .NET executables with libraries. There are multiple tools available to get the job done:
ILMerge is a utility that can be used to merge multiple .NET assemblies into a single assembly.
Mono mkbundle, packages an exe and all assemblies with libmono into a single binary package.
IL-Repack is a FLOSS alterantive to ILMerge, with some additional features.
In addition this can be combined with the Mono Linker, which does remove unused code and therefor makes the resulting assembly smaller.
Another possibility is to use .NETZ, which does not only allow compressing of an assembly, but also can pack the dlls straight into the exe. The difference to the above mentioned solutions is that .NETZ does not merge them, they stay separate assemblies but are packed into one package.
.NETZ is a open source tool that compresses and packs the Microsoft .NET Framework executable (EXE, DLL) files in order to make them smaller.
ILMerge can combine assemblies to one single assembly provided the assembly has only managed code. You can use the commandline app, or add reference to the exe and programmatically merge. For a GUI version there is Eazfuscator, and also .Netz both of which are free. Paid apps include BoxedApp and SmartAssembly.
If you have to merge assemblies with unmanaged code, I would suggest SmartAssembly. I never had hiccups with SmartAssembly but with all others. Here, it can embed the required dependencies as resources to your main exe.
You can do all this manually not needing to worry if assembly is managed or in mixed mode by embedding dll to your resources and then relying on AppDomain's Assembly ResolveHandler. This is a one stop solution by adopting the worst case, ie assemblies with unmanaged code.
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string assemblyName = new AssemblyName(args.Name).Name;
if (assemblyName.EndsWith(".resources"))
return null;
string dllName = assemblyName + ".dll";
string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
//or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);
File.WriteAllBytes(dllFullPath, data);
}
return Assembly.LoadFrom(dllFullPath);
};
}
The key here is to write the bytes to a file and load from its location. To avoid chicken and egg problem, you have to ensure you declare the handler before accessing assembly and that you do not access the assembly members (or instantiate anything that has to deal with the assembly) inside the loading (assembly resolving) part. Also take care to ensure GetMyApplicationSpecificPath() is not any temp directory since temp files could be attempted to get erased by other programs or by yourself (not that it will get deleted while your program is accessing the dll, but at least its a nuisance. AppData is good location). Also note that you have to write the bytes each time, you cant load from location just 'cos the dll already resides there.
For managed dlls, you need not write bytes, but directly load from the location of the dll, or just read the bytes and load the assembly from memory. Like this or so:
using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
{
byte[] data = new byte[stream.Length];
s.Read(data, 0, data.Length);
return Assembly.Load(data);
}
//or just
return Assembly.LoadFrom(dllFullPath); //if location is known.
If the assembly is fully unmanaged, you can see this link or this as to how to load such dlls.
.NET Core 3.0 natively supports compiling to a single .exe
The feature is enabled by the usage of the following property in your project file (.csproj):
<PropertyGroup>
<PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>
This is done without any external tool.
See my answer for this question for further details.
The excerpt by Jeffrey Richter is very good. In short, add the libraries as embedded resources and add a callback before anything else. Here is a version of the code (found in the comments of his page) that I put at the start of Main method for a console app (just make sure that any calls that use the libraries are in a different method to Main).
AppDomain.CurrentDomain.AssemblyResolve += (sender, bargs) =>
{
String dllName = new AssemblyName(bargs.Name).Name + ".dll";
var assem = Assembly.GetExecutingAssembly();
String resourceName = assem.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
if (resourceName == null) return null; // Not found, maybe another handler will find it
using (var stream = assem.GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
To expand on #Bobby's asnwer above. You can edit your .csproj to use IL-Repack to automatically package all files into a single assembly when you build.
Install the nuget ILRepack.MSBuild.Task package with Install-Package ILRepack.MSBuild.Task
Edit the AfterBuild section of your .csproj
Here is a simple sample that merges ExampleAssemblyToMerge.dll into your project output.
<!-- ILRepack -->
<Target Name="AfterBuild" Condition="'$(Configuration)' == 'Release'">
<ItemGroup>
<InputAssemblies Include="$(OutputPath)\$(AssemblyName).exe" />
<InputAssemblies Include="$(OutputPath)\ExampleAssemblyToMerge.dll" />
</ItemGroup>
<ILRepack
Parallel="true"
Internalize="true"
InputAssemblies="#(InputAssemblies)"
TargetKind="Exe"
OutputFile="$(OutputPath)\$(AssemblyName).exe"
/>
</Target>
The following method DO NOT use external tools and AUTOMATICALLY include all needed DLL (no manual action required, everything done at compilation)
I read a lot of answer here saying to use ILMerge, ILRepack or Jeffrey Ritcher method but none of that worked with WPF applications nor was easy to use.
When you have a lot of DLL it can be hard to manually include the one you need in your exe. The best method i found was explained by Wegged here on StackOverflow
Copy pasted his answer here for clarity (all credit to Wegged)
1) Add this to your .csproj file:
<Target Name="AfterResolveReferences">
<ItemGroup>
<EmbeddedResource Include="#(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
<LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Target>
2) Make your Main Program.cs look like this:
[STAThreadAttribute]
public static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
App.Main();
}
3) Add the OnResolveAssembly method:
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
AssemblyName assemblyName = new AssemblyName(args.Name);
var 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);
}
}
You could add the DLLs as embedded resources, and then have your program unpack them into the application directory on startup (after checking to see if they're there already).
Setup files are so easy to make, though, that I don't think this would be worth it.
EDIT: This technique would be easy with .NET assemblies. With non-.NET DLLs it would be a lot more work (you'd have to figure out where to unpack the files and register them and so on).
Another product that can handle this elegantly is SmartAssembly, at SmartAssembly.com. This product will, in addition to merging all dependencies into a single DLL, (optionally) obfuscate your code, remove extra meta-data to reduce the resulting file size, and can also actually optimize the IL to increase runtime performance.
There is also some kind of global exception handling/reporting feature it adds to your software (if desired) that could be useful. I believe it also has a command-line API so you can make it part of your build process.
Neither the ILMerge approach nor Lars Holm Jensen's handling the AssemblyResolve event will work for a plugin host. Say executable H loads assembly P dynamically and accesses it via interface IP defined in an separate assembly. To embed IP into H one shall need a little modification to Lars's code:
Dictionary<string, Assembly> loaded = new Dictionary<string,Assembly>();
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{ Assembly resAssembly;
string dllName = args.Name.Contains(",") ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
dllName = dllName.Replace(".", "_");
if ( !loaded.ContainsKey( dllName ) )
{ if (dllName.EndsWith("_resources")) return null;
System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);
resAssembly = System.Reflection.Assembly.Load(bytes);
loaded.Add(dllName, resAssembly);
}
else
{ resAssembly = loaded[dllName]; }
return resAssembly;
};
The trick to handle repeated attempts to resolve the same assembly and return the existing one instead of creating a new instance.
EDIT:
Lest it spoil .NET's serialization, make sure to return null for all assemblies not embedded in yours, thereby defaulting to the standard behaviour. You can get a list of these libraries by:
static HashSet<string> IncludedAssemblies = new HashSet<string>();
string[] resources = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
for(int i = 0; i < resources.Length; i++)
{ IncludedAssemblies.Add(resources[i]); }
and just return null if the passed assembly does not belong to IncludedAssemblies .
It may sound simplistic, but WinRar gives the option to compress a bunch of files to a self-extracting executable.
It has lots of configurable options: final icon, extract files to given path, file to execute after extraction, custom logo/texts for popup shown during extraction, no popup window at all, license agreement text, etc.
May be useful in some cases.
I use the csc.exe compiler called from a .vbs script.
In your xyz.cs script, add the following lines after the directives (my example is for the Renci SSH):
using System;
using Renci;//FOR THE SSH
using System.Net;//FOR THE ADDRESS TRANSLATION
using System.Reflection;//FOR THE Assembly
//+ref>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+res>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+ico>"C:\Program Files (x86)\Microsoft CAPICOM 2.1.0.2 SDK\Samples\c_sharp\xmldsig\resources\Traffic.ico"
The ref, res and ico tags will be picked up by the .vbs script below to form the csc command.
Then add the assembly resolver caller in the Main:
public static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
.
...and add the resolver itself somewhere in the class:
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
String resourceName = new AssemblyName(args.Name).Name + ".dll";
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
I name the vbs script to match the .cs filename (e.g. ssh.vbs looks for ssh.cs); this makes running the script numerous times a lot easier, but if you aren't an idiot like me then a generic script could pick up the target .cs file from a drag-and-drop:
Dim name_,oShell,fso
Set oShell = CreateObject("Shell.Application")
Set fso = CreateObject("Scripting.fileSystemObject")
'TAKE THE VBS SCRIPT NAME AS THE TARGET FILE NAME
'################################################
name_ = Split(wscript.ScriptName, ".")(0)
'GET THE EXTERNAL DLL's AND ICON NAMES FROM THE .CS FILE
'#######################################################
Const OPEN_FILE_FOR_READING = 1
Set objInputFile = fso.OpenTextFile(name_ & ".cs", 1)
'READ EVERYTHING INTO AN ARRAY
'#############################
inputData = Split(objInputFile.ReadAll, vbNewline)
For each strData In inputData
if left(strData,7)="//+ref>" then
csc_references = csc_references & " /reference:" & trim(replace(strData,"//+ref>","")) & " "
end if
if left(strData,7)="//+res>" then
csc_resources = csc_resources & " /resource:" & trim(replace(strData,"//+res>","")) & " "
end if
if left(strData,7)="//+ico>" then
csc_icon = " /win32icon:" & trim(replace(strData,"//+ico>","")) & " "
end if
Next
objInputFile.Close
'COMPILE THE FILE
'################
oShell.ShellExecute "c:\windows\microsoft.net\framework\v3.5\csc.exe", "/warn:1 /target:exe " & csc_references & csc_resources & csc_icon & " " & name_ & ".cs", "", "runas", 2
WScript.Quit(0)
If you are using .NET Core 3.0
You can do this with the dotnet publish command with PublishSingleFile property:
dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true
The only downside is you end up with a single EXE file with a huge size.
It's possible but not all that easy, to create a hybrid native/managed assembly in C#. Were you using C++ instead it'd be a lot easier, as the Visual C++ compiler can create hybrid assemblies as easily as anything else.
Unless you have a strict requirement to produce a hybrid assembly, I'd agree with MusiGenesis that this isn't really worth the trouble to do with C#. If you need to do it, perhaps look at moving to C++/CLI instead.
Generally you would need some form of post build tool to perform an assembly merge like you are describing. There is a free tool called Eazfuscator (eazfuscator.blogspot.com/) which is designed for bytecode mangling that also handles assembly merging. You can add this into a post build command line with Visual Studio to merge your assemblies, but your mileage will vary due to issues that will arise in any non trival assembly merging scenarios.
You could also check to see if the build make untility NANT has the ability to merge assemblies after building, but I am not familiar enough with NANT myself to say whether the functionality is built in or not.
There are also many many Visual Studio plugins that will perform assembly merging as part of building the application.
Alternatively if you don't need this to be done automatically, there are a number of tools like ILMerge that will merge .net assemblies into a single file.
The biggest issue I've had with merging assemblies is if they use any similar namespaces. Or worse, reference different versions of the same dll (my problems were generally with the NUnit dll files).
Try this:
https://github.com/ytk2128/dll-merger
here you can merge all 32 bit dlls/exe - even its not ".net" dlls - so for me better then ilmerge for example ...

Is ExifLib usable in a WPF / XAML app?

I want to extract exif data from jpg images. ExifLib seemed like a good choice to simplify this chore, and so I installed it via NuGet.
I then tried to get started using the sample code from here (commenting out the MessageBox code for now):
using (ExifReader reader = new ExifReader(#"C:\temp\testImage.jpg"))
{
// Extract the tag data using the ExifTags enumeration
DateTime datePictureTaken;
if (reader.GetTagValue<DateTime>(ExifTags.DateTimeDigitized, out datePictureTaken))
{
// Do whatever is required with the extracted information
//System.Windows.MessageBox.Show(this, string.Format("The picture was taken on {0}",
// datePictureTaken), "Image information"); //, MessageBoxButtons.OK);
}
}
but get an error:
The best overloaded method match for 'ExifLib.ExifReader.ExifReader(System.IO.Stream)' has some invalid arguments
and
Argument 1: cannot convert from 'string' to 'System.IO.Stream'
both on this line:
using (ExifReader reader = new ExifReader(#"C:\temp\testImage.jpg"))
Is this fixable, or is ExifLib not usable from a WPF / XAML app?
If ExifLib is not a viable solution for a WPF / XAML app, what alternatives exist?
Update:
With this code, from Simon McKenzie's answer:
private void btnLoadNewPhotoset_Click(object sender, RoutedEventArgs e)
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
using (var stream = store.OpenFile("testImage.jpg", FileMode.Open))
using (var reader = new ExifReader(stream))
{
// ...
}
}
I still get an error:
The type or namespace name 'IsolatedStorage' does not exist in the namespace 'System.IO' (are you missing an assembly reference?)
This is a Windows Store (C#) app created in Visual Studio 2013. The project's properties shows that it targets Windows 8.1, and Configuration Manager shows configuration == debug, platform = x64)
My project's displayed References are:
.NET for Windows Store apps
Bing.Maps.Xaml
ExifLib
Microsoft Visual C++ Runtime Package
Windows 8.1
What am I missing?
Update 2:
When I look in Reference Manager at Assemblies.Framework, it says, "All of the Framework assembles are already referenced..." I assume mscorlib.dll is supposed to be one of these (it doesn't list them)?
I searched my hard drive for "mscorlib.dll" and I've got a million of them, all different sizes and dates. Which one should I try to add as a reference? I've got everything from one in C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.5 dated 7/9/2012 with file size of 2,564,528 to one in C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5.1 to...you name it.
Thinking "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5.1" seemed the best bet, I tried to reference it via the Browse button, but when I did, I got scolded with:
In the interests of full disclosure, in Reference Manager for Windows 8.1, it says, "The Windows 8.1. SDK is already referenced."
For Windows 8.1.Extensions, it shows me:
Microsoft Visual C++ 2013 Runtime Package for Windows 12.0 (unchecked)
Microsoft Visual C++ 2013 Runtime Package 11.0 (checked)
Since this seems to be the cause of one of the warnings, I reversed their checkedness (checked 2013,
unchecked the other).
I also checked:
Behaviors SDK (XAML) 12.0
SQLite for Windows Runtime 3.8.6 (because I will eventually be using SQLite in this project)
Update 3:
I just found this: "Isolated storage is not available for Windows Store apps. Instead, use the application data classes in the Windows.Storage namespaces included in the Windows Runtime API to store local data and files." here.
Update 4:
I'm waiting for Simon's example, but I'm thinking it might be something like this:
using Windows.Storage;
using ExifLib;
. . .
private async void btnOpenImgFiles_Click(object sender, RoutedEventArgs e)
{
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".png");
IReadOnlyList<StorageFile> files = await openPicker.PickMultipleFilesAsync();
for (int i = 0; i < files.Count; i++)
{
using (var randomAccessStream = await files[i].OpenAsync(FileAccessMode.Read))
using (var stream = randomAccessStream.AsStream())
using (var exfrdr = new ExifReader(stream))
{
// ...exfrdr
}
}
}
this has nothing to do with WPF
try something like this
using (ExifReader reader = new ExifReader(File.Open(#"C:\temp\testImage.jpg",FileMode.Open)))
The string constructor is not available in Windows Phone/Windows Store apps because they aren't allowed direct filesystem access. You will instead need to pass in a stream containing your image. Here's an example using a FileOpenPicker. Note the use of AsStream(...) to convert the IRandomAccessStream into a Stream for use with an ExifReader.
using System;
using System.IO;
using Windows.Storage;
using Windows.Storage.Pickers;
// ...
var picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".jpg");
var file = await picker.PickSingleFileAsync();
using (var randomAccessStream = await file.OpenAsync(FileAccessMode.Read))
{
using (var stream = randomAccessStream.AsStream())
{
using (var reader = new ExifReader(stream))
{
string model;
if (reader.GetTagValue(ExifTags.Model, out model))
{
var dialog = new MessageDialog(model, "Camera Model");
dialog.ShowAsync();
}
}
}
}

Categories