Load mixed assembly at runtime - c#

I have a project uses Sqlite (System.Data.SQLite.DLL) it is mixed assembly. I need to load it at runtime. As I know it impossible using AssemblyResolve event. So the target is to unpack assembly to Temp directory and show to application where to find it.
The code is:
public static void SaveSqlite()
{
byte[] sqliteAsm = EmbedAssembly.System_Data_SQLite;
string tempFile = Path.GetTempPath();
File.WriteAllBytes(tempFile + "System.Data.SQLite.DLL", sqliteAsm);
}
// and the setup int start method
SaveSqlite();
AppDomain.CurrentDomain.SetupInformation.PrivateBinPath = Path.GetTempPath();
So it not works. Application cant find sqlite assembly but it successfuly saved to Temp dir. How to solve this problem? Thanks.

The solution has founded.
Not using mixed assembly but managed for sqlite interop Using
SQLite.
SQLite.Interop.dll - native dll (unpack to any directory)
Call SetDllDirectory WinAPI to set dll search directory
code for extract sqlite native dll from resource looks like that:
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetDllDirectory(string lpPathName);
//...
string text4 = Path.Combine(text, "SQLite.Interop.dll");
if (!File.Exists(text4))
{
byte[] array3 = LoadCompressedDllFromResource("nativedll.SQLite.Interop.z");
array3 = DecompressBytes(array3);
SaveToFile(array3, text4);
}
//...
pack all over managed assemblies using libz application

Related

Why does LoadLibrary fail while DllImportAttribute works?

I'm creating a .NET application for a client that performs I/O with one of their third-party systems. As they regularly change the password of this system, I should retrieve it dynamically by calling a native DLL that they provide in a dedicated directory (not besides my EXE file).
However, I have trouble loading the DLL dynamically using LoadLibraryEx. The weird thing is that I can call the library using the DllImportAttribute.
This is what I have done so far:
According to this SO answer, I use the following code (in a constructor) to try to load the DLL dynamically:
public PasswordProvider(string dllPath)
{
if (!File.Exists(dllPath))
throw new FileNotFoundException($"The DLL \"{dllPath}\" does not exist.");
_dllHandle = NativeMethods.LoadLibraryEx(dllPath, IntPtr.Zero, LoadLibraryFlags.None);
if (_dllHandle == IntPtr.Zero)
throw CreateWin32Exception($"Could not load DLL from \"{dllPath}\".");
var procedureHandle = NativeMethods.GetProcAddress(_dllHandle, GetPasswordEntryPoint);
if (procedureHandle == IntPtr.Zero)
throw CreateWin32Exception("Could not retrieve GetPassword function from DLL.");
_getPassword = Marshal.GetDelegateForFunctionPointer<GetPasswordDelegate>(procedureHandle);
}
When LoadLibraryEx is called, the resulting handle is null, the error code is 126 which usually means that the DLL or one of its dependencies could not be found.
When I call LoadLibraryEx with DoNotResolveDllReferences, then I get a working handle but afterwards, I cannot call GetProcAddress (error code 127) - I suspect that I have to fully load the DLL for this.
When I open the native DLL in Dependencies (which essentially is Dependency Walker for Win10), I can clearly see that one of the statically linked DLLs is missing
However, if I copy the DLL besides my EXE file and use the DllImportAttribute, I can call into the DLL
[DllImport(DllPath, EntryPoint = GetPasswordEntryPoint, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private static extern long GetPassword(long systemId, string user, byte[] password);
How is this possible? I thought that the mechanism behind DllImportAttribute uses LoadLibary internally, too. Where does my code differ? Am I missing something obvious?
Just some notes:
I can't just use DllImportAttribute as I cannot specify searching in a dedicated directory this way (the DLL must lie beside my EXE file or in a common Windows location for this to work).
I also tried LoadLibrary instead of LoadLibraryEx but with the same results.
EDIT after Simons comment:
NativeMethods is defined as followed:
private static class NativeMethods
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr LoadLibrary(string dllName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr LoadLibraryEx(string dllFileName, IntPtr reservedNull, LoadLibraryFlags flags);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr moduleHandle, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr moduleHandle);
}
[Flags]
private enum LoadLibraryFlags : uint
{
None = 0,
DoNotResolveDllReferences = 0x00000001,
LoadIgnoreCodeAuthorizationLevel = 0x00000010,
LoadLibraryAsDatafile = 0x00000002,
LoadLibraryAsDatafileExclusive = 0x00000040,
LoadLibraryAsImageResource = 0x00000020,
LoadLibrarySearchApplicationDir = 0x00000200,
LoadLibrarySearchDefaultDirs = 0x00001000,
LoadLibrarySearchDllLoadDir = 0x00000100,
LoadLibrarySearchSystem32 = 0x00000800,
LoadLibrarySearchUserDirs = 0x00000400,
LoadWithAlteredSearchPath = 0x00000008
}
EDIT after Hans Passant's comment:
The overall goal is the ability to replace / update the native DLL while my application (a Windows Service) is running. I detect a file change and then reload the DLL. I am not quite sure if this is possible with DllImportAttribute without restarting the service.
And I should be more specific on the actual problem: I couldn't load the native DLL using LoadLibraryEx, no matter if it was placed next to my EXE, or in another random folder, or in SysWow64. Why does it work with DllImportAttribute? I'm pretty sure that the missing FastMM subdependency DLL is not present on my system (neither next to the actual DLL, nor in any Windows directory).
It's because the DLL search order path. In windows when application try to load a DLL the underlying system automatically search some path for the DLL ,So let's pretend Windows's DLL search path looks something like this:
A) . <-- current working directory of the executable, highest priority, first check
B) \Windows
C) \Windows\system32
D) \Windows\syswow64 <-- lowest priority, last check
You can read more about the underlying mechanism in this Microsoft documentation.
Search for DLL which your main DLL has dependency to it and find where it store on system, add the directory of it to DLL search path of Windows using AddDllDirectory or SetDllDirectory.
If the dll already loaded into memory by any of running process Windows automatically use it instead of searching, so you can load FastMM DLL into memory using LoadLibrary manually and then try to load the main DLL and it should solve the problem too.
#HansPassant and #David Heffernan are right: I actually tried to load two different versions of the DLL (one of them had the FastMM subdependency, one did not). Thanks for your help and sorry for the inconvenience.

Unable to find entry point in DLL

I have a C# application from which I am trying to send a parameter to a C++ function. However, I am getting the error (mentioned in the subject)
C# application:
static class SegmentationFunctions
{
[DllImport("MyApplication.dll", EntryPoint = "fnmain", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int fnmain(string search);
}
}
public partial class MainWindow:Window
{
public MainWindow()
{
InitializeComponent();
string search = "test string here";
int scommand = SegmentationFunctions.fnmain(search);
}
C++ file.h
extern "C" QUERYSEGMENTATION_API int fnmain(char query[MAX_Q_LEN]);
C++ file .cpp
extern "C" QUERYSEGMENTATION_API int fnmain(char searchc[MAX_LEN_Q])
{
do something...
}
Dependency Walker can show you what functions are effectively exported from the DLL. You will be able to see if your fnmain is there at all, or it is _fnmain instead , or has a C++ decoration in its name.
Note that by default visual studio will not copy your native output to the same folder as your managed output.
manually copy native output to your managed build folder and try again - if that is your problem then you need to change the C++ build settings to put the destination folder the same as your managed app folder.
Your code is correct - so long as the QUERYSEGMENTATION_API macro is defined correctly and your dll is in fact built as "MyApplication.dll"
I would manually run the executable from the file system - making sure that the latest exe and dll are in the same folder, and if it fails run depends.exe to figure it out.

Providing path to externals assembly native dll dependecy

I have C# application which loads set of managed assemblies. One of this assemblies loads two native dlls (each of them in different location) if they are avaiable. Iam trying to find way to provide search path to those native dlls.
Are there other options? I really dont want to provide those dlls with my software - copying them to programs directory of course solves the problem.
I've tried using SetDllDirectory system function but it is possible to provide only one path using it. Each call to this function resets path.
Setting PATH enviroment variable does not solve the problem too :/
I know this was an old post, but just so there's an answer: Using the LoadLibary function you can force load a native DLL:
public static class Loader
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string fileName);
}
You must call this before any other DLL does - I usually call it in a static constructor of my main program. I had to do this for DllImport(), and static constructors were always executed before the native DLLs were loaded - they only load actually the first time an imported function is called.
Example:
class Program
{
static Program()
{
Loader.LoadLibrary("path\to\native1.dll");
Loader.LoadLibrary("otherpath\to\native2.dll");
}
}
Once the library is loaded it should satisfy the DllImports() of the other managed assemblies you are loading. If not, they might be loaded using some other method, and you may have no other option but to copy them locally.
Note: This is a Windows solution only. To make this more cross-platform you'd have to detect operating systems yourself and use the proper import; for example:
[DllImport("libdl")]
public static extern IntPtr DLOpen(string fileName, int flags);
[DllImport("libdl.so.2")]
public static extern IntPtr DLOpen2(string fileName, int flags);
// (could be "libdl.so.2" also: https://github.com/mellinoe/nativelibraryloader/issues/2#issuecomment-414476716)
// ... etc ...
This could help:
private void Form1_Load(object sender, EventArgs e)
{
//The AssemblyResolve event is called when the common language runtime tries to bind to the assembly and fails.
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(currentDomain_AssemblyResolve);
}
//This handler is called only when the common language runtime tries to bind to the assembly and fails.
Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string dllPath = Path.Combine(YourPath, new AssemblyName(args.Name).Name) + ".dll";
return (File.Exists(dllPath))
? Assembly.Load(dllPath)
: null;
}
Register yours dlls to GAC. More here.

Adding a directory temporarily to Windows 7's DLL search paths

I want to temporarily add a directory to the DLL search paths - is there a correct way to do this under Windows 7?
Scenario
I've got a C# application, let's call it WonderApp.
WonderApp needs to call a C++ DLL, located in C:\MyPath. So as part of WonderApp's Program.Main(), I added the following command:
Environment.SetEnvironmentVariable("PATH",
"C:\\MyPath;" + Environment.GetEnvironmentVariable("PATH"));
According to this article, adding a directory to the PATH should also add it to the directories search for DLLs.
The solution works fine in Windows XP: if I add the directory to the PATH, the DLL loads and the program works just fine. If I don't add the directory, the DLL doesn't load, failing with a "not found" error.
However, this doesn't work for Windows 7.
So I figured, let's try using SetDllDirectory(). Like this:
[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetDllDirectory(string lpPathName);
And, later on:
bool success = SetDllDirectory(Util.Paths.GetApplicationDataDir());
The value of success is true, but the DLL still fails to load.
Finally, if I set the PATH to include C:\MyPath manually, before running the application - it all works! The DLL loads, and runs just fine.
So, to re-iterate:
Is there a correct way to temporarily add a directory to the DLL search paths under Windows 7?
UPDATE: Using Process Explorer, I checked the application's run-time Environment, and "C:\MyPath" was indeed in the PATH! Furthermore, I saw that Helper.dll was in the list of open handles (as a DLL, not just a file) - and it still claimed not to find it.
You can add custom DLL loading logic to a C# app using the 'AssemblyResolve' event.
This page has a good summary, with code samples: http://support.microsoft.com/kb/837908
Just as you did, I have found that changing the PATH environment variable of a running C# app does not affect the DLL search behaviour. Perhaps the AppDomain caches the PATH value at startup? You can use the AssemblyResolve event to work around this.
See also How to add folder to assembly search path at runtime in .NET?
I am thinking it has to do with permission problems.
Try turning off UAC and running your code again. Check to see if updating the path worked.
If it did, at least you know where to start...
My solution is simple, but I feel ridiculous resorting to it.
I've written another assembly, "Shell", that modifies the Environment, runs WonderApp, and exits.
By modifying the PATH before running the main application (WonderApp), the main application's DLL search-path includes the directories added to the modified PATH.
It looks like this:
namespace shell
{
static class program
{
[dllimport("kernel32.dll", charset = charset.auto, setlasterror = true)]
public static extern bool setenvironmentvariable(string lpname, string lpvalue);
private static string joinargstosinglestring(string[] args)
{
string s = string.empty;
for (int i = 0; i < args.length; ++i)
{
if (!string.isnullorempty(s))
{
s += " ";
}
s += "\"" + args[i] + "\"";
}
return s;
}
[stathread]
static void main(string[] args)
{
string pathbefore = environment.getenvironmentvariable("path");
string wewant = util.paths.getapplicationdatadir() + ";" + pathbefore;
setenvironmentvariable("path", wewant);
Process process = Process.Start(".\\WonderApp.exe", joinArgsToSingleString(args));
}
}
}
I wish I could find a better solution!

Embedding unmanaged dll into a managed C# dll

I have a managed C# dll that uses an unmanaged C++ dll using DLLImport. All is working great.
However, I want to embed that unmanaged DLL inside my managed DLL as explain by Microsoft there:
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute.dllimportattribute.aspx
So I added the unmanaged dll file to my managed dll project, set the property to 'Embedded Resource' and modify the DLLImport to something like:
[DllImport("Unmanaged Driver.dll, Wrapper Engine, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null",
CallingConvention = CallingConvention.Winapi)]
where
'Wrapper Engine' is the assembly name of my managed DLL
'Unmanaged Driver.dll' is the unmanaged DLL
When I run, I get:
Access is denied. (Exception from HRESULT: 0x80070005
(E_ACCESSDENIED))
I saw from MSDN and from http://blogs.msdn.com/suzcook/ that's supposed to be possible...
You can embed the unmanaged DLL as a resource if you extract it yourself to a temporary directory during initialization, and load it explicitly with LoadLibrary before using P/Invoke. I have used this technique and it works well. You may prefer to just link it to the assembly as a separate file as Michael noted, but having it all in one file has its advantages. Here's the approach I used:
// Get a temporary directory in which we can store the unmanaged DLL, with
// this assembly's version number in the path in order to avoid version
// conflicts in case two applications are running at once with different versions
string dirName = Path.Combine(Path.GetTempPath(), "MyAssembly." +
Assembly.GetExecutingAssembly().GetName().Version.ToString());
if (!Directory.Exists(dirName))
Directory.CreateDirectory(dirName);
string dllPath = Path.Combine(dirName, "MyAssembly.Unmanaged.dll");
// Get the embedded resource stream that holds the Internal DLL in this assembly.
// The name looks funny because it must be the default namespace of this project
// (MyAssembly.) plus the name of the Properties subdirectory where the
// embedded resource resides (Properties.) plus the name of the file.
using (Stream stm = Assembly.GetExecutingAssembly().GetManifestResourceStream(
"MyAssembly.Properties.MyAssembly.Unmanaged.dll"))
{
// Copy the assembly to the temporary file
try
{
using (Stream outFile = File.Create(dllPath))
{
const int sz = 4096;
byte[] buf = new byte[sz];
while (true)
{
int nRead = stm.Read(buf, 0, sz);
if (nRead < 1)
break;
outFile.Write(buf, 0, nRead);
}
}
}
catch
{
// This may happen if another process has already created and loaded the file.
// Since the directory includes the version number of this assembly we can
// assume that it's the same bits, so we just ignore the excecption here and
// load the DLL.
}
}
// We must explicitly load the DLL here because the temporary directory
// is not in the PATH.
// Once it is loaded, the DllImport directives that use the DLL will use
// the one that is already loaded into the process.
IntPtr h = LoadLibrary(dllPath);
Debug.Assert(h != IntPtr.Zero, "Unable to load library " + dllPath);
Here is my solution, which is a modified version of JayMcClellan's answer. Save the file below into a class.cs file.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using System.ComponentModel;
namespace Qromodyn
{
/// <summary>
/// A class used by managed classes to managed unmanaged DLLs.
/// This will extract and load DLLs from embedded binary resources.
///
/// This can be used with pinvoke, as well as manually loading DLLs your own way. If you use pinvoke, you don't need to load the DLLs, just
/// extract them. When the DLLs are extracted, the %PATH% environment variable is updated to point to the temporary folder.
///
/// To Use
/// <list type="">
/// <item>Add all of the DLLs as binary file resources to the project Propeties. Double click Properties/Resources.resx,
/// Add Resource, Add Existing File. The resource name will be similar but not exactly the same as the DLL file name.</item>
/// <item>In a static constructor of your application, call EmbeddedDllClass.ExtractEmbeddedDlls() for each DLL that is needed</item>
/// <example>
/// EmbeddedDllClass.ExtractEmbeddedDlls("libFrontPanel-pinv.dll", Properties.Resources.libFrontPanel_pinv);
/// </example>
/// <item>Optional: In a static constructor of your application, call EmbeddedDllClass.LoadDll() to load the DLLs you have extracted. This is not necessary for pinvoke</item>
/// <example>
/// EmbeddedDllClass.LoadDll("myscrewball.dll");
/// </example>
/// <item>Continue using standard Pinvoke methods for the desired functions in the DLL</item>
/// </list>
/// </summary>
public class EmbeddedDllClass
{
private static string tempFolder = "";
/// <summary>
/// Extract DLLs from resources to temporary folder
/// </summary>
/// <param name="dllName">name of DLL file to create (including dll suffix)</param>
/// <param name="resourceBytes">The resource name (fully qualified)</param>
public static void ExtractEmbeddedDlls(string dllName, byte[] resourceBytes)
{
Assembly assem = Assembly.GetExecutingAssembly();
string[] names = assem.GetManifestResourceNames();
AssemblyName an = assem.GetName();
// The temporary folder holds one or more of the temporary DLLs
// It is made "unique" to avoid different versions of the DLL or architectures.
tempFolder = String.Format("{0}.{1}.{2}", an.Name, an.ProcessorArchitecture, an.Version);
string dirName = Path.Combine(Path.GetTempPath(), tempFolder);
if (!Directory.Exists(dirName))
{
Directory.CreateDirectory(dirName);
}
// Add the temporary dirName to the PATH environment variable (at the head!)
string path = Environment.GetEnvironmentVariable("PATH");
string[] pathPieces = path.Split(';');
bool found = false;
foreach (string pathPiece in pathPieces)
{
if (pathPiece == dirName)
{
found = true;
break;
}
}
if (!found)
{
Environment.SetEnvironmentVariable("PATH", dirName + ";" + path);
}
// See if the file exists, avoid rewriting it if not necessary
string dllPath = Path.Combine(dirName, dllName);
bool rewrite = true;
if (File.Exists(dllPath)) {
byte[] existing = File.ReadAllBytes(dllPath);
if (resourceBytes.SequenceEqual(existing))
{
rewrite = false;
}
}
if (rewrite)
{
File.WriteAllBytes(dllPath, resourceBytes);
}
}
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
static extern IntPtr LoadLibrary(string lpFileName);
/// <summary>
/// managed wrapper around LoadLibrary
/// </summary>
/// <param name="dllName"></param>
static public void LoadDll(string dllName)
{
if (tempFolder == "")
{
throw new Exception("Please call ExtractEmbeddedDlls before LoadDll");
}
IntPtr h = LoadLibrary(dllName);
if (h == IntPtr.Zero)
{
Exception e = new Win32Exception();
throw new DllNotFoundException("Unable to load library: " + dllName + " from " + tempFolder, e);
}
}
}
}
I wasn't aware this is possible - I'd guess that the CLR needs to extract the embedded native DLL somewhere (Windows needs to have a file for the DLL to load it - it cannot load an image from raw memory), and wherever it's trying to do that the process does not have permission.
Something like Process Monitor from SysInternals might give you a clue if the pronblem is that creating the DLL file is failing...
Update:
Ah... now that I've been able to read Suzanne Cook's article (the page didn't come up for me before), note that she is not talking about embedding the native DLL as a resource inside the managed DLL, but rather as a linked resource - the native DLL still needs to be its own file in the file system.
See http://msdn.microsoft.com/en-us/library/xawyf94k.aspx, where it says:
The resource file is not added to the output file. This differs from the /resource option which does embed a resource file in the output file.
What this seems to do is add metadata to the assembly that causes the native DLL to logically be part of the assembly (even though it's physically a separate file). So things like putting the managed assembly into the GAC will automatically include the native DLL, etc.
You can try Costura.Fody. Documentation says, that it's able to handle unmanaged files. I only used it for managed files, and it works like a charm :)
One could also just copy the DLLs to any folder, and then call SetDllDirectory to that folder. No call to LoadLibrary is needed then.
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetDllDirectory(string lpPathName);

Categories