First, I know that it doesn't make sense to compare the dllimport attribute and the getProcAddress function directly. Rather, I am interested in comparing two pieces of code, that achieve basically the same thing - calling a function in a dll - by either importing the function with the dllimport attribute or with the getProcAddress function. Specifically, I am writing a C# application that uses some function in a dll that I have written. At first I accessed my dll function with the following piece of code:
class DllAccess
{
[DllImport("kernel32.dll", SetLastError = true)]
private extern IntPtr LoadLibrary(String DllName);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate Bool BarType(Byte arg); // return value indicates whether function call went well or not.
Bool Bar(Byte arg)
{
Bool ok = false;
IntPtr pDll= LoadLibrary("foo.dll");
if (pDll != IntPtr.Zero)
{
IntPtr pfunc = GetProcAddress(pDll, "bar");
if (pFunc != IntPtr.Zero)
{
BarType bar = (BarType)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(BarType));
ok = bar(arg);
}
FreeLibrary(pDll);
}
return ok;
}
}
However, I later needed to get at the lastError value, if it had been set during the dll call, so I changed my code into this:
class DllAccess
{
[DllImport("foo.dll", EntryPoint = "bar", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
private extern Bool DllBar(Byte arg); // return value indicates whether function call went well or not.
Bool Bar(Byte arg)
{
return DllBar(arg);
}
}
This is of course much tidier, and as mentioned, it sets the lastError code. Obviously, my first piece of code gives me the possibility of changing dll and function call at runtime, but at the moment this is not required. So my question is: Are there any reasons for using the first formulation, if I am certain, that I will not be using another dll or another function?
The only real advantages of using GetProcAddress are that you can unload the DLL manually as well as call a function, and that you can change the naming easily at runtime.
However, the second option provides you with a huge number of benefits. In addition to being "tidier", it also handles much of the marshaling of data types for you - which becomes very important with certain APIs.
That being said, if you do the method you have listed as first, you should make sure to unload everything, as well. Right now, you're basically leaking addresses each time you call Bar()... For details, look at FreeLibrary.
Probably the biggest advantage of GetProcAddress is that it lets you control the search path of the DLL. For example, you could load either 32-bit or 64-bit version of a native DLL automatically. With DllImportAttribute, this isn't possible.
Related
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.
have been working on this for hours, couldn't get it work :(
below code gives exception "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." this is fixed, see below update
but when i change the return type to int and return a int value, it works. how to make it to return a float value? thanks.
vs2012
c++ code
extern "C" __declspec(dllexport)
float compute_similarity()
{
return 1.01;
}
c# code
[DllImport("demo.exe", EntryPoint = "compute_similarity", CallingConvention = CallingConvention.Cdecl)]
public static extern float compute_similarity();
public void Get()
{
float x = compute_similarity(); // here returns random value
}
=====================================
UPDATE
See comments from David below, the problem was the c# project is targeting x64 and c++ is targeting Win32. After changing the c# project to target to x86, the exception went away.
However the call from c# returns some random value instead of 1.01 expected
I think your problem is that your function is declared in an executable rather than a DLL. Convert your native code into a library project and compile a DLL.
Your code will result in a call to LoadLibrary passing the file name demo.exe. The documentation for LoadLibrary says:
LoadLibrary can also be used to load other executable modules. For example, the function can specify an .exe file to get a handle that can be used in FindResource or LoadResource. However, do not use LoadLibrary to run an .exe file. Instead, use the CreateProcess function.
And so your code is doing exactly what you are told not to do. The call to LoadLibrary will succeed. The subsequent calls to GetProcAddress will succeed. But the module that you loaded is not fit to execute code.
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 have an older app (ca. 2005) which accepts dll plugins. The app was originally designed for Win32 C plugins, but I have a working C# dll template. My problem: I need to do some one-time initialization, which in a Win32 C dll would be done in DllMain:
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
[one-time stuff here...]
}
Is there a C# equivalent of this? There is no "DllMain" in the C# template I have. I tried a literal C# interpretation, but no go: the dll works but it won't trigger the DllMain function.
public static bool DllMain(int hModule, int reason, IntPtr lpReserved) {
[one time stuff here...]
}
Give your class a static constructor and do your initialization there. It will run the first time anybody calls a static method or property of your class or constructs an instance of your class.
I've had to interact with a legacy application probably in the same situation as you have. I've found a hacky way to get DllMain functionality in a CLR assembly. Luckily it isn't too hard. It requires an additional DLL but it doesn't require you to deploy an additional DLL so you can still have the "put a DLL in that directory and the app will load it" paradigm.
First, you make a simple regular C++ DLL that looks like the following:
dllmain.cpp:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "resource.h"
extern void LaunchDll(
unsigned char *dll, size_t dllLength,
char const *className, char const *methodName);
static DWORD WINAPI launcher(void* h)
{
HRSRC res = ::FindResourceA(static_cast<HMODULE>(h),
MAKEINTRESOURCEA(IDR_DLLENCLOSED), "DLL");
if (res)
{
HGLOBAL dat = ::LoadResource(static_cast<HMODULE>(h), res);
if (dat)
{
unsigned char *dll =
static_cast<unsigned char*>(::LockResource(dat));
if (dll)
{
size_t len = SizeofResource(static_cast<HMODULE>(h), res);
LaunchDll(dll, len, "MyNamespace.MyClass", "DllMain");
}
}
}
return 0;
}
extern "C" BOOL APIENTRY DllMain(HMODULE h, DWORD reasonForCall, void* resv)
{
if (reasonForCall == DLL_PROCESS_ATTACH)
{
CreateThread(0, 0, launcher, h, 0, 0);
}
return TRUE;
}
Note the thread creation. This is to keep Windows happy because calling managed code within a DLL entrypoint is a no-no.
Next, you have to create that LaunchDll function the code above references. This goes in a separate file because it will be compiled as a managed C++ unit of code. To do this, first create the .cpp file (I called it LaunchDll.cpp). Then right click on that file in your project and in Configuration Properties-->C/C++-->General change the Common Language RunTime Support entry to Common Language RunTime Support (/clr). You can't have exceptions, minimal rebuild, runtime checks and probably some other things I forgot about but the compiler will tell you about. When the compiler complains, track down what settings you much change from the default and change them on the LaunchDll.cpp file only.
LaunchDll.cpp:
#using <mscorlib.dll>
// Load a managed DLL from a byte array and call a static method in the DLL.
// dll - the byte array containing the DLL
// dllLength - the length of 'dll'
// className - the name of the class with a static method to call.
// methodName - the static method to call. Must expect no parameters.
void LaunchDll(
unsigned char *dll, size_t dllLength,
char const *className, char const *methodName)
{
// convert passed in parameter to managed values
cli::array<unsigned char>^ mdll = gcnew cli::array<unsigned char>(dllLength);
System::Runtime::InteropServices::Marshal::Copy(
(System::IntPtr)dll, mdll, 0, mdll->Length);
System::String^ cn =
System::Runtime::InteropServices::Marshal::PtrToStringAnsi(
(System::IntPtr)(char*)className);
System::String^ mn =
System::Runtime::InteropServices::Marshal::PtrToStringAnsi(
(System::IntPtr)(char*)methodName);
// used the converted parameters to load the DLL, find, and call the method.
System::Reflection::Assembly^ a = System::Reflection::Assembly::Load(mdll);
a->GetType(cn)->GetMethod(mn)->Invoke(nullptr, nullptr);
}
Now for the really tricky part. You probably noticed the resource loading in dllmain.cpp:launcher(). What this does is retrieve a second DLL that has been inserted as a resource into the DLL getting created here. To do this, create a resource file by doing the
right click-->Add-->New Item-->Visual C++-->Resource-->Resource File (.rc) thing. Then, you need to make sure there is a line like:
resource.rc:
IDR_DLLENCLOSED DLL "C:\\Path\\to\\Inner.dll"
in the file. (Tricky, huh?)
The only thing left to do is to create that Inner.dll assembly. But, you already have it! This is what you were trying to launch with your legacy app in the first place. Just make sure to include a MyNamespace.MyClass class with a public void DllMain() method (of course you can call these functions whatever you want to, these are just the values hardcoded into dllmain.cpp:launcher() above.
So, in conclusion, the code above takes an existing managed DLL, inserts it into a resource of an unmanaged DLL which, upon getting attached to a process, will load the managed DLL from the resource and call a method in it.
Left as an exercise to the reader is better error checking, loading different DLLs for Debug and Release, etc. mode, calling the DllMain substitute with the same arguments passed to the real DllMain (the example only does it for DLL_PROCESS_ATTACH), and hardcoding other methods of the inner DLL in the outer DLL as pass through methods.
Also not easy to do from C# you can have a per module initializers
Modules may contain special methods called module initializers to initialize the module itself.
All modules may have a module initializer. This method shall be static, a member of the module, take no parameters, return no value, be marked with rtspecialname and specialname, and be named .cctor.
There are no limitations on what code is permitted in a module initializer. Module initializers are permitted to run and call both managed and unmanaged code.
Even though C# doesn't directly support module initialization we can implement it using reflection and static constructors. To do this we can define a custom attribute and use it find classes that need to be initialized on module loading:
public class InitOnLoadAttribute : Attribute {}
private void InitAssembly(Assembly assembly)
{
foreach (var type in GetLoadOnInitTypes(assembly)){
var prop = type.GetProperty("loaded", BindingFlags.Static | BindingFlags.NonPublic); //note that this only exists by convention
if(prop != null){
prop.GetValue(null, null); //causes the static ctor to be called if it hasn't already
}
}
}
static IEnumerable<Type> GetLoadOnInitTypes(Assembly assembly)
{
foreach (Type type in assembly.GetTypes())
{
if (type.GetCustomAttributes(typeof(InitOnLoadAttribute), true).Length > 0){
yield return type;
}
}
}
public MyMainClass()
{
//init newly loaded assemblies
AppDomain.CurrentDomain.AssemblyLoad += (s, o) => InitAssembly(o.LoadedAssembly);
//and all the ones we currently have loaded
foreach(var assembly in AppDomain.CurrentDomain.GetAssemblies()){
InitAssembly(assembly);
}
}
on classes that we need to initialize immediately we add that code to their static constructor (which will be run once even if the property getter is accessed multiple times) and add the custom attribute we added to expose this functionality.
[InitOnLoad]
class foo
{
private static bool loaded { get { return true; } }
static foo()
{
int i = 42;
}
}
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.