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.
Related
I write the .net-extension which can be loaded into different versions of some unmanaged application.
Below I imported the some_func_v01, some_func_v02, and some_func_v03 functions:
[DllImport("some_library_v1.0.dll", CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Unicode, EntryPoint = "func_name")]
extern static private void some_func_v01(string msg);
[DllImport("some_library_v2.0.dll", CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Unicode, EntryPoint = "func_name")]
extern static private void some_func_v02(string msg);
[DllImport("some_library_v3.0.dll", CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Unicode, EntryPoint = "func_name")]
extern static private void some_func_v03(string msg);
...
public void some_func(string msg)
{
switch (Application.Version.Major)
{
case 1: some_func_v01(msg); break;
case 2: some_func_v02(msg); break;
case 3: some_func_v03(msg); break;
}
}
The some_library library is the part of the target application and has the same version like the application.
The problem is that I am to edit the code of my extension when the new versions of application will appear. I would like to dynamically generate code depending of application version. For example, for application version 1:
[DllImport("some_library_v1.0.dll", CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Unicode, EntryPoint = "func_name")]
extern static private void some_func(string msg);
I can to do it through the PowerShell hosting using, but maybe more simple way exists... I wouldn't want to create PowerShell hosting only to carry out this task.
Is exist the simple way to do it?
I think that the best solution will be to load DLLs dynamically.
Use WINAPI LoadLibrary and GetProcAddress to get address of the function. Then use Marshal.GetDelegateForFunctionPointer to get .net delegate.
Example:
//skipping WINAPI native definitions
//you need to define delegate type:
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
delegate void MyFuncType(string msg);
//how to get delegate object:
var library = LoadLibrary(libraryNameChosenAtRuntime);
var funcPtr = GetProcAddress(library, funcName);
var func = Marshal.GetDelegateForFunctionPointer<MyFuncType>(funcPtr);
// now you can use func as delegate
// you can store this delegate object somewhere and reuse.
func("msg");
Since you know the version of the external application then you also know its directory and hence the location of of the external dll which resides somewhere in the directory of the external application. What you would like to ignore is the suffix name of the dll (the version v_1 part given in your example). I believe that there is only one version of that dll in each version of the external application.
If my assumption is correct then you could achieve all this like so:
Choose a prefix of the dll that is always in its filename no matter the version ("some_library" in your example)
Enumerate all files in the location of the dll in the external application's directory or subdirectory (Directory.EnumerateFiles or .GetFiles if using older .NET)
Locate the filename that matches the prefix ("some_library_*.dll" as given in your example)
Load the dll dynamically and call the function (Oleh gave you a good example in his answer).
I will completely describe you my problem and process. I was making an editor for an other's game and I have one wrapper DLL written in C language which I am communicating with.
At first I had list of methods with DllImport calling functions from the DLL. First method was CSharp_new_CEditorInterface which returned an IntPtr. Then CSharp_CEditorInterface_CreateApp which toke ulong handle to window control where it will draw graphics. At the end I should call CSharp_CEditorInterface_CloseApp and CSharp_delete_CEditorInterface. These methods take a HandleRef with pointer returned from CSharp_new_CEditorInterface.
However, I needed to call creating and deleting methods multiple times and when calling CSharp_CEditorInterface_CreateApp for the second time, it threw System.AccessViolationException. So I decided to load and unload DLL dynamically with LoadLibrary and FreeLibrary. I wrote an application that with reflection browsed all p/invoke methods and generated code consisting of delegates, readonly fields and GetProcAddress-es. However, as I found out, the entry points were only partial. CSharp_new_CEditorInterface was _CSharp_new_CEditorInterface#0. With my DLL export viewer, I saved all complete function names and then searched within. In constructor, I call LoadLibrary and appropriate function loads. In Dispose, there was FreeLibrary.
This solution worked fine, functions were called OK, until I discovered that some functions which return string are throwing AccessViolationException. They work fine when using DllImport method. I have also discovered that when calling ANY function from static class, thus loading another module, calling problematic functions is now OK and they return appropriate values. However, after unloading DLL dynamically and reloading, it does not work again and guess which exception is thrown.
Now which function I call and in what order:
--When initializing--
LoadLibrary(string) (winapi)
--bunch of GetProcAddress, Marshal.GetDelegateForFunctionPointer--
new_CEditorInterface() (from DLL)
CreateApp(HandleRef, ulong) (from DLL)
--When closing in Dispose--
CloseApp(HandleRef) (from DLL)
delete_CEditorInterface(HandleRef) (from DLL)
FreeLibrary(IntPtr) (winapi)
I should note that the DLL was not created to be loaded more than one at a time.
Can somebody help me, please?
Try this I hope that this helps you
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string libname);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool FreeLibrary(IntPtr hModule);
//Load
IntPtr Handle = LoadLibrary(fileName);
if (Handle == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Exception(string.Format("Failed to load library (ErrorCode: {0})",errorCode));
}
//Free
if(Handle != IntPtr.Zero)
FreeLibrary(Handle);
If you want to call functions first you must create delegeate that matches this function
and then use WinApi GetProcAddress
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
IntPtr funcaddr = GetProcAddress(Handle,functionName);
YourFunctionDelegate function = Marshal.GetDelegateForFunctionPointer(funcaddr,typeof(YourFunctionDelegate )) as YourFunctionDelegate ;
function.Invoke(pass here your parameters);
I'm trying to open an .evtx file on a Windows 7 x64 machine using the OpenBackupEventLog function however I keep on getting ERROR_FILE_NOT_FOUND (error code 2) even though the file does exist.
My P/Invoke declaration / point where I call the file is:
[DllImport("advapi32.dll", SetLastError = true, ExactSpelling = false, EntryPoint = "OpenBackupEventLog")]
public static extern IntPtr OpenBackupEventLog(
[MarshalAs(UnmanagedType.LPTStr)]string uncServerName,
[MarshalAs(UnmanagedType.LPTStr)]string fileName);
IntPtr ptr = NativeMethods.OpenBackupEventLog(null, filename);
if (ptr == IntPtr.Zero && File.Exists(filename))
{
// This exception is thrown and so the file does exist
throw new Win32Exception(string.Format("Failed to open event log archive '{0}'", filename));
}
Note that this is inside an x86 process.
The only thing that I can think of is that the problem is down to Unicode / ANSI marshalling (previously I recall getting ERROR_INVALID_PARAMETER instead), however I've double checked and playing around with the marshalling has no effect.
Why is this failing to open the file / how can I diagnose this?
[DllImport("advapi32.dll", ..., EntryPoint = "OpenBackupEventLog")]
The EntryPoint property is the source of your problem here. The exported function names are OpenBackupEventLogA and OpenBackupEventLogW. Respectively the ANSI and the Unicode versions of this function. Your declaration would use the ANSI version since you didn't specify the CharSet property.
The pinvoke marshaller can find the A and W versions automatically when ExactSpelling = false (the default). But not when you specify the name explicitly.
There's no point in using the ANSI version, use CharSet.Auto and omit EntryPoint. MarshalAs is unnecessary as well, strings already are marshaled as LPTStr. Thus:
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr OpenBackupEventLog(string uncServerName, string fileName);
Since this is a 64-bit OS and you are accessing the files using a 32-bit app, the most likely cause of this exception is that the OS very "helpfully" automatically redirects requests for the System directory (C:\Windows\system, e.g.) to the SysWOW64 directory C:\Windows\SysWOW64.
Fortunately, there is a builtin workaround: the sysnative directory.
When you generate the path to the file name, use something similar to the following:
string filePath = #"%WINDIR%\sysnative\winevt\logs\mylog.evtx";
However, only do this if you retain the app as a 32-bit app. It will not resolve correctly as a 64-bit app.
This was a encoding Unicode vs ANSI issue - I seemed to manage to get this to start working again somehow, but later realised that it was returning ANSI strings instead of UNICODE strings - obviously I had accidentally started using the ANSI versions of these functions which is no good.
Its now working with the following PInvoke declaration
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "OpenBackupEventLogW")]
public static extern EventLogHandle OpenBackupEventLog(
string uncServerName,
string fileName);
The key points being that I:
Removed the MarshalAs attributes
Added the CharSet = CharSet.Unicdoe paramter to my DllImport declaration
Added W to the end of the entry point name
Note that it also seems to work if I remove the W from the end of the entry point name as long s the CharSet = CharSet.Unicdoe paramter is there.
Hey guys and girls :) ok so i ran this project ->
http://www.helyar.net/2009/libvlc-media-player-in-c-part-2/ and it worked perfectly (he was using .net 2.0) however when i try anything above 3.5 it gives ->
Unable to load DLL ‘libvlc’: The specified module could not be found. (Exception from HRESULT: 0x8007007E)
is there any workaround someone has done that sorts this out? MANY thanks ppl :D:D:D:D
There are two things that must be done when using that example with the new 2.0.x VLC releases. First, you have to somehow add the libvlc DLL to the search path. I used a call to SetDllDirectory to do the trick. You declare it as:
static class LibVlc
{
. . .
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetDllDirectory(string lpPathName);
. . .
}
Then you can call this method with the root folder of the VLC installation. On my PC, I called it as follows:
LibVlc.SetDllDirectory(#"C:\Program Files (x86)\VideoLAN\VLC");
Obviously, for a program being distributed this parameter should be configurable.
Next, the VLC API's have apparently changed because none of the methods require an exception object to be passed in anymore. It looks like return values from the methods should be checked (for example, libvlc_new() returns NULL if there was an error). I haven't tried passing in the exception object by reference like he does but the calls all work fine without it (and my interfaces now match the VLC API exactly). I also specify the calling convention to use when doing interop, just to be clear to the runtime what I expect for parameter passing order and such. For example, here are my defines for libvlc_new and libvlc_release:
[DllImport("libvlc", CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr libvlc_new(int argc,
[MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.LPStr)] string[] argv);
[DllImport("libvlc", CallingConvention=CallingConvention.Cdecl)]
public static extern void libvlc_release(IntPtr instance);
I hope this helps!
You must copy libvlc.dll to your bin/debug folder. It must be the one from your VLC installation folder (C:\program files\videolan\vlc)
I have a window to use for editing. The editor should load a dll (which I have full control of) in response to the user's selection to know how to display the information visually.
(They're dll's, as a user will not necessarily want or need every single display model, and also allow new ones to be added without messing around with the main project)
They will all simply be stored in a subdirectory (for now anyway)
I'm pretty sure I can enumerate the available dlls but I need to do 2 more things that I'm not sure on
1) Some way to get metadata from\on the dll, so I can build the lists of possible display selections...
2) Load the selected dll, and unload it as necessary
Any suggestions would be greatly appreciated.
If you are using raw dll's and not .NET assemblies then here are some handy P/Invokes for you:
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static extern void SetDllDirectory(string lpPathName);
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
privatestatic extern int GetModuleFileName(IntPtr module, [Out] StringBuilder fileName, int size);
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private static bool FreeLibrary(IntPtr module);
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
private IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
Note that SetDllDirectory may need some protection as it is not available on all versions of windows (Windows 2000, in particular doesn't have it).
And in use:
SetDllDirectory(candidateFolder);
IntPtr dllHandle = LoadLibrary(dllName);
if (dllHandle != IntPtr.Zero)
{
_dllHandle = dllHandle;
_location = candidateFolder;
_fullPath = Path.Combine(candidateFolder, dllName);
IntPtr p = GetProcAddress(_dllHandle, procName);
if (p == IntPtr.Zero)
throw new ArgumentException("procName");
SomeDelegateType d = (SomeDelegateType)Marshal.GetDelegateForFunctionPointer(p, typeof(SomeDelegateType));
d(/* args */);
}
otherwise, you will be using Assembly methods. Looking at assembly level attributes or object level attributes is a good way to get extra information, although if what you want is a plug-in system, you should use a plug-in system, like the Managed Add-In Framework at CodePlex. See also this SO question and answer.
Take a look at the Castle Windsor framework. It is designed to handle all of your requirements including DLL unloading. It's also free and open source.
I don't know if changing how your program works is an option, but, you could use dependency injection for this, as long as they adhere to a certain interface.
The user selects, you dynamically set class to be loaded, and then just get an instance of the class.
I am not dealing with the unloading, I am just thinking about how you could possibly get classes, and since plinth already gave links to the functions for actually handling the dll, I think I will just end here.
For a native module, the simplest way to get "metadata" would be to define some C-exported (non-name-mangled) functions that return the information you want. At their simplest, these would return pointers to static data within the modules, e.g.:
extern "C" const char* GetModuleDescription();
...
const char* GetModuleDescription() { return "Dummy Module"; }
You would then load each ".dll" file in the directory using LoadLibrary, load and call your known exports from it using GetProcAddress. If you can't load a file or find the exports, then it's not a valid plugin module, so skip it.
Once you're done with a module, you can call FreeLibrary. Windows will then unload the module from your address space.
OK, I;ve figured out I need to use a second AppDomain, load the dll into that, and then I can unload the AppDomain as required.
string SignalSystemDLLPath = AppDomain.CurrentDomain.BaseDirectory + MyApp.Properties.Resources.SystemModuleFolder;
AppDomainSetup info = new AppDomainSetup();
info.ApplicationBase = DLLPath;
DLLDomain = AppDomain.CreateDomain("EditorDomain", null, info);
DLLPath is set to the subdir that holds the dll's.
I then foreach on all the dll's to get the AssemblyName, then later
I use
DLLDomain.Load(SelectedAssemblyName)
to load the DLL. I keep getting FileNotFound exceptions though.
After much googling I've decided its to much work at the moment, and I can refactor it later If I really need to do it...
Thank you for your replies though!
Found out how to do this very easy using MEF, simply use a DirectoryCatalog pointed at your plugin dir, and as long as you have matching [Export]s and [Import]s it works great.