P/Invoke to dynamically loaded library on Mono - c#

I'm writing a cross-platform .NET library that uses some unmanaged code. In the static constructor of my class, the platform is detected and the appropriate unmanaged library is extracted from an embedded resource and saved to a temp directory, similar to the code given in another stackoverflow answer.
So that the library can be found when it isn't in the PATH, I explicitly load it after it is saved to the temp file. On windows, this works fine with LoadLibrary from kernel32.dll. I'm trying to do the same with dlopen on Linux, but I get a DllNotFoundException when it comes to loading the P/Invoke methods later on.
I have verified that the library "libindexfile.so" is successfully saved to the temp directory and that the call to dlopen succeeds. I delved into the mono source to try figure out what is going on, and I think it might boil down to whether or not a subsequent call to dlopen will just reuse a previously loaded library. (Of course assuming that my naïve swoop through the mono source drew the correct conclusions).
Here is the shape of what I'm trying to do:
// actual function that we're going to p/invoke to
[DllImport("indexfile")]
private static extern IntPtr openIndex(string pathname);
const int RTLD_NOW = 2; // for dlopen's flags
const int RTLD_GLOBAL = 8;
// its okay to have imports for the wrong platforms here
// because nothing will complain until I try to use the
// function
[DllImport("libdl.so")]
static extern IntPtr dlopen(string filename, int flags);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string filename);
static IndexFile()
{
string libName = "";
if (IsLinux)
libName += "libindexfile.so";
else
libName += "indexfile.dll";
// [snip] -- save embedded resource to temp dir
IntPtr handle = IntPtr.Zero;
if (IsLinux)
handle = dlopen(libPath, RTLD_NOW|RTLD_GLOBAL);
else
handle = LoadLibrary(libPath);
if (handle == IntPtr.Zero)
throw new InvalidOperationException("Couldn't load the unmanaged library");
}
public IndexFile(String path)
{
// P/Invoke to the unmanaged function
// currently on Linux this throws a DllNotFoundException
// works on Windows
IntPtr ptr = openIndex(path);
}
Update:
It would appear that subsequent calls to LoadLibrary on windows look to see if a dll of the same name has already been loaded, and then uses that path. For example, in the following code, both calls to LoadLibrary will return a valid handle:
int _tmain(int argc, _TCHAR* argv[])
{
LPCTSTR libpath = L"D:\\some\\path\\to\\library.dll";
HMODULE handle1 = LoadLibrary(libpath);
printf("Handle: %x\n", handle1);
HMODULE handle2 = LoadLibrary(L"library.dll");
printf("Handle: %x\n", handle2);
return 0;
}
If the same is attempted with dlopen on Linux, the second call will fail, as it doesn't assume that a library with the same name will be at the same path. Is there any way round this?

After much searching and head-scratching, I've discovered a solution. Full control can be exercised over the P/Invoke process by using dynamic P/Invoke to tell the runtime exactly where to find the code.
Edit:
Windows solution
You need these imports:
[DllImport("kernel32.dll")]
protected static extern IntPtr LoadLibrary(string filename);
[DllImport("kernel32.dll")]
protected static extern IntPtr GetProcAddress(IntPtr hModule, string procname);
The unmanaged library should be loaded by calling LoadLibrary:
IntPtr moduleHandle = LoadLibrary("path/to/library.dll");
Get a pointer to a function in the dll by calling GetProcAddress:
IntPtr ptr = GetProcAddress(moduleHandle, methodName);
Cast this ptr to a delegate of type TDelegate:
TDelegate func = Marshal.GetDelegateForFunctionPointer(
ptr, typeof(TDelegate)) as TDelegate;
Linux Solution
Use these imports:
[DllImport("libdl.so")]
protected static extern IntPtr dlopen(string filename, int flags);
[DllImport("libdl.so")]
protected static extern IntPtr dlsym(IntPtr handle, string symbol);
const int RTLD_NOW = 2; // for dlopen's flags
Load the library:
IntPtr moduleHandle = dlopen(modulePath, RTLD_NOW);
Get the function pointer:
IntPtr ptr = dlsym(moduleHandle, methodName);
Cast it to a delegate as before:
TDelegate func = Marshal.GetDelegateForFunctionPointer(
ptr, typeof(TDelegate)) as TDelegate;
For a helper library that I wrote, see my GitHub.

Try running it like this from a terminal:
export MONO_LOG_LEVEL=debug
export MONO_LOG_MASK=dll
mono --debug yourapp.exe
Now every library lookup will be printed to the terminal, so you'll be able to find out what's going wrong.

I needed to load a native library extracted to a temporary location, and I almost found a solution. I've checked Mono's source code and figured out a way:
[DllImport("__Internal", CharSet = CharSet.Ansi)]
private static extern void mono_dllmap_insert(IntPtr assembly, string dll, string func, string tdll, string tfunc);
// and then somewhere:
mono_dllmap_insert(IntPtr.Zero, "somelib", null, "/path/to/libsomelib.so", null);
This kind of works. The problem is, you cannot allow Mono's stupid JIT compiler to catch a whiff of any DllImported method referring this library before calling mono_dllmap_insert().
Because if it does, strange things will happen:
Mono: DllImport searching in: '/tmp/yc1ja5g7.emu/libsomelib.so' ('/tmp/yc1ja5g7.emu/libsomelib.so').
Mono: Searching for 'someGreatFunc'.
Mono: Probing 'someGreatFunc'.
Mono: Found as 'someGreatFunc'.
Error. ex=System.DllNotFoundException: somelib
So now that I'm calling my native someGreatFunc(), Mono is able to find the library and load it (I checked), it is able to find the symbol (I checked), but because somewhen in the past when it was doing JIT it was not able to load that library, it decides to throw DllNotFoundException anyway. I guess the generated code contains a hardcoded throw statement or something :-O
When you call another native function from the same library that happens not to have been JITted before you called mono_dllmap_insert(), it will work.
So you can either use the manual solution added by #gordonmleigh or you must tell Mono where the library is BEFORE it JITs any of these imports. Reflection may help there.

Not sure why you think this is related to mono, since the issue you're having is not about mono's dynamic loading facilities.
If your updated sample works, it just means that LoadLibrary() on windows has different semantics than dlopen() on Linux: as such you either have to live with the difference or implement your own abstraction that deals with the directory issue (my hunch is that it's not the directory that is retained, but windows simply looks to see if a library with the same name was already loaded and it reuses that).

Related

DllImport with non-exported functions. Why is it working?

I just encountered with strange behaviour of DllImport in C#, which I can't explain. I want to know how It is possible and where I can read about It. Case is that via DllImport one can call function that doesn't really exported form dll. In my case It is kernel32.dll and function ZeroMemory (but with Copy/Move/Fill memory such behavior). So, my code:
[DllImport("kernel32", EntryPoint = "LoadLibraryW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr LoadLibrary(string libName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr module, string procName);
//WTF???
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ZeroMemory(IntPtr address, int size);
static void TestMemory()
{
IntPtr mem = Marshal.AllocHGlobal(100); //Allocate memory block of 100 bytes size
Marshal.WriteByte(mem, 55); //Write some value in the first byte
ZeroMemory(mem, 100); //Clearing block of memory
byte firstByte = Marshal.ReadByte(mem); //Read the first byte of memory block
Console.WriteLine(firstByte); //Output 0 (not 55) - ZeroMemory is working
//Getting address of ZeroMemory from kernel32.dll
IntPtr kernelHandle = LoadLibrary("kernel32.dll");
IntPtr address = GetProcAddress(kernelHandle, "ZeroMemory");
Console.WriteLine(address.ToString("X")); //Output 0 - Library kernel32.dll DOESN'T export function ZeroMemory!!!
//Testing GetProcAddress via getting address of some exported function
Console.WriteLine(GetProcAddress(kernelHandle, "AllocConsole").ToString("X")); //Output some address value - all is OK.
}
No EntryPointNotFoundException is thrown - code works fine. If change name of ZeroMemory to ZeroMemory1 or something like that - exception will be thrown. But in export table of kernel32.dll we see:
There is NO ZeroMemory function at all!
If we look in msdn, we read that ZeroMemory is just a macro in WinBase.h header file for C++. Inside that we see:
#define RtlMoveMemory memmove
#define RtlCopyMemory memcpy
#define RtlFillMemory(d,l,f) memset((d), (f), (l))
#define RtlZeroMemory(d,l) RtlFillMemory((d),(l),0)
#define MoveMemory RtlMoveMemory
#define CopyMemory RtlCopyMemory
#define FillMemory RtlFillMemory
#define ZeroMemory RtlZeroMemory
Obviously, that in C++ ZeroMemory actually works through RtlFillMemory from ntdll.dll. But it is in C++!!! Why is it work in C#?? On official documentation for DllImport attribute here we can read the next:
As a minimum requirement, you must supply the name of the DLL
containing the entry point.
But in that case kernel32.dll CANNOT contating entry point for ZeroMemory. What is going on?? Help, please.
The OP is correct in that kernel32.dll has no export ZeroMemory export, yet the C# DllImport somehow succeeds to magically resolve the ZeroMemory reference to the correct RtlZeroMemory export in .NET apps targeted at the Framework (but not at Core).
Turns out that a handful of Win32 APIs documented as inlines/macros (MoveMemory, CopyMemory FillMemory, ZeroMemory) are specifically checked by the Framework code and internally rerouted to the correct exports. While not formally documented, this was acknowledged in a MS-sanctioned comment under a .NET Runtime issue.
As an FYI, there were a few special cased P/Invoke names in .NET Framework: MoveMemory, CopyMemory, FillMemory, and ZeroMemory. All of these will work when pointing at kernel32 on .NET Framework, but will fail on .NET Core. Please use the EntryPoint property in the DllImport attribute to get the desired behavior. The proper export names can be found using dumpbin /exports kernel32.dll from a Visual Studio command prompt.
The above suggests adding an explicit EntryPoint for the declaration to work in both Framework and Core, for example:
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
public static extern void ZeroMemory(IntPtr address, IntPtr count);
The magic name remapping happens outside the open-sourced .NET code, but can be clearly seen in the disassembly contributed by Simon Mourier in a comment.

Can i use unmanaged DLL without its pdb file? C#

i have a project in c# that use a unmanaged dll from another project, i have try to import that dll file, but i cannot use that dll file because Visual Studio cannot find its .pdb file, me neither.
i have tried to dll import
[DllImport("unmanaged.dll", EntryPoint= "Analyse", CallingConvention = CallingConvention.Cdecl)]
private static extern unsafe long* Analyse(byte[] bImgData, uint nLength, ushort nWidth, ushort nHeigth, uint nMaxCodeCount, short nAnalyseLevel);
but the method just doesnt return any value.
and in Output List there are:
'CCan.exe' (Win32): Loaded 'C:\Users\Masbro\Documents\Visual Studio 2013\Projects\CCan\CCan\bin\x86\Debug\unmanaged.dll'. Cannot find or open the PDB file.
can i use unmanaged DLL without its pdb file?or can i generate its .pdb file?
You don't need the PDB. However, you do need to know the entry point and function signature of the API you're calling in the DLL; Visual Studio won't figure those out for you. However, if your DLL exports its function names, you can find them using various tools. Some examples here, on MSDN: http://msdn.microsoft.com/en-us/library/vstudio/31d242h4(v=vs.100).aspx
For example, let's say your library, unmanaged.dll, has the following API:
void* DoStuff (DWORD number, LPWSTR str, PDWORD outval);
To call that from C# code, you would need to define the following extern function in in one of your classes:
using System.Runtime.InteropServices
[DllImport("unmanaged.dll", CharSet=Unicode)]
public static extern IntPtr DoStuff (UInt32 number, String str, out UInt32 outval);
The visibility modifier (public) isn't important, except that you need to be able to see the function from wherever you call it. The DllImport attribute is defined in System.Runtime.InteropServices, hence the using statement.
DllImport requires the first value, the string giving the DLL name, but it also has a ton of other parameters which are sometimes helpful or even required (such as CharSet). In particular, if your unmanaged library doesn't export names (or you want to use a different name in C# than the exported unmanaged name) then you need to specify the EntryPoint field of the DllImport attribute. An example of accessing a function without an exported name if you know the function's ordinal:
[DllImport("unmanaged.dll", CharSet=Unicode, EntryPoint="#1")]
public static extern IntPtr DoStuff (UInt32 number, String str, out UInt32 outval);
Take a look at the MSDN documentation for more info.

Call native method from dll

I am trying to use MimeTex.dll in Mixed Mode of C++/CLI project. I include the dll by:
#pragma comment(lib,"MimeTex.dll")
and I tried to call this method:
CreateGifFromEq("expression","path");
but the compiler inform that it doesn't know CreateGifFromEq() method.
I didn't find resources in the web in how to use MimeTex.dll in C++. I just find how to use it with C# in this link by Pinvok like:
[System.Security.SuppressUnmanagedCodeSecurity()]
internal class NativeMethods
{
private NativeMethods()
{ //all methods in this class would be static
}
[System.Runtime.InteropServices.DllImport("MimeTex.dll")]
internal static extern int CreateGifFromEq(string expr, string fileName);
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
internal extern static IntPtr GetModuleHandle(string lpModuleName);
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
internal extern static bool FreeLibrary(IntPtr hLibModule);
}
and then call it like:
NativeMethods.CreateGifFromEq(equation, tempGifFilePath);
How I can call it without Pinvok in mixed mode of C++/CLI?
Surely you meant to write:
#pragma comment(lib,"MimeTex.lib")
In other words, you pass the .lib file to the linker rather than the .dll. When you compiled the DLL, a .lib file will have been generated.
But that's not your immediate problem. The compiler has no declaration for CreateGifFromEq. That's because you have not included the library's header file in your C++ code. Doing that should resolve the issue.
If all you need is that one function then it should be trivial to declare it in your C++ code.
__declspec(dllimport) extern int CreateGifFromEq(char *expr, char *fileName);
You probably will need to wrap that in an extern "C" block too.
Actually, having looked at the library, the header file that is supplied with the source does not declare that function, even though it's present in the library.
In C++/CLI you can use P/Invoke as in C#.
If you don't want to use P/Invoke, the other way is to load the DLL with LoadLibrary and get pointers to the functions with GetProcAddress.
Here's the equivalent of the C# code in C++/CLI:
using namespace System;
using namespace System::Runtime::InteropServices;
private ref class NativeMethods abstract sealed
{
internal:
[DllImport("MimeTex.dll")]
static int CreateGifFromEq(String^ expr, String^ fileName);
[DllImport("kernel32.dll")]
static IntPtr GetModuleHandle(String^ lpModuleName);
[DllImport("kernel32.dll")]
[returnvalue: MarshalAs(UnmanagedType::Bool)]
static bool FreeLibrary(IntPtr hLibModule);
};
As you can see it's almost identical to the C# code except for some minor syntax changes.
Here's how you would use LoadLibrary and GetProcAddress (which has the advantage of not requiring marshaling, which is unnecessary in C++ anyway):
HMODULE hModule = LoadLibrary(L"MimeTex.dll");
int (*fpCreateGifFromEq)(WCHAR*, WCHAR*) = GetProcAddress(hModule, "CreateGifFromEq");
(*fpCreateGifFromEq)(L"expression", L"filename");
You don't need to do that for GetModuleHandle and FreeLibrary, because they're from kernel32.dll so you can just include Windows.h and call them normally.
There's a cleaner way of doing things:
Create a C++/CLI project. It will be your interface between C# and C++ (this is what C++/CLI is aimed at).
Make your C# project have a reference to this new assembly.
Inside your new C++/CLI project, configure project options so as you point to the directory with mimetex .h files for the compiler to compile your project, and the directory with .lib/.dll for the linker.
Inside your C++/CLI part, you just write C++ inside the functions. They are exposed to your C# assembly, but in the inside it's just calls to C++ functions as usual.
(You should check a C++CLI tutorial to know what to do with strings to convert them to native char or std::string. I remember I had to look at that.)*
In your C# part, you just call the C++CLI project assembly exposed functions like normal C# functions, without noticing it's native code called behind.

Relative path to unmanaged DLL [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Specify the search path for DllImport in .NET
I have an unmanaged DLL. For clearance, its a C++ dll that I want to use in my c# code.
The problem is that its a windows app and user can install it in any directory of their choice. So I can't reference it with a static path and I couldn't find a way to give a relative path.
Here is the code I tried :
[DllImport("mydll.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);
public void SetDllDirectory(string lpPathName)
{
try
{
bool r = SetDllDirectory(lpPathName);
}
catch (Exception ex)
{
throw ex;
}
}
public void SetDirectoryPath()
{
try
{
DirectoryInfo directory = new DirectoryInfo(System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath));
if (directory.Exists)
SetDllDirectory(directory.ToString() + "\\mydll.dll");
}
catch (Exception ex)
{
throw ex;
}
}
And below is the error I am receiving.
Unable to find an entry point named 'SetDllDirectory' in DLL 'mydll.dll'.
This question could be a replica of
"C++ unmanaged DLL in c#"
"Relative Path to DLL in Platform Invoke Statement"
Sorry for that but I didn't find any solution in these references.
If you are trying to pinvoke into an unmanaged library that is in a non-standard location you need to add this location to the dll search path that windows uses to look for dll files.
If your users can tell the program where the c/c++ library file is, you can load it manually when they choose it.
public class UnsafeNativeMethods {
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetDllDirectory(string lpPathName);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int GetDllDirectory(int bufsize, StringBuilder buf);
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr LoadLibrary(string librayName);
[DllImport("mylibrary")]
public static extern void InitMyLibrary();
}
You can call the above in some form of preload routine like so:
public void LoadDllFile( string dllfolder, string libname ) {
var currentpath = new StringBuilder(255);
UnsafeNativeMethods.GetDllDirectory( currentpath.Length, currentpath );
// use new path
UnsafeNativeMethods.SetDllDirectory( dllfolder );
UnsafeNativeMethods.LoadLibrary( libname );
// restore old path
UnsafeNativeMethods.SetDllDirectory( currentpath );
}
You might then call it like so:
LoadDllFile( "c:\whatever", "mylibrary.dll" );
This error message:
Unable to find an entry point named 'SetDllDirectory' in DLL 'mydll.dll'.
says nothing about not being able to find the DLL. It says that your P/Invoke definition is incorrect. There is no function named SetDllDirectory in your DLL (mydll.dll). And why should there be? The only functions that are exported from DLLs are those that you explicitly code and export. Since you didn't write code for a SetDllDirectory and indicate that it should be exported from mydll.dll, it doesn't exist in the DLL.
Beyond that, this question is unanswerable unless you update your question with real code. You need to post, at minimum, the signature for the function you're trying to call from the unmanaged DLL, along with the P/Invoke code that you've tried on the C# (managed) side.
The problem is that its a windows app and user can install it in any directory of their choice. So I can't reference it with a static path and I couldn't find a way to give a relative path.
This is not a real problem. Relative paths work just fine, and they work in exactly the way that your question indicates that you've already tried. You just include the name of the DLL in the P/Invoke definition. The default DLL search order takes care of everything else. If the managed EXE is in the same directory as the unmanaged DLL that you want to P/Invoke, everything will work seamlessly.
It doesn't matter where the user chooses to install the app, so long as the DLL and the EXE are located in the same folder. And that's the job of your installer.
And so long as you use relative paths, you absolutely do not need to use the SetDllDirectory function from the Win32 API (which is actually defined in kernel32.dll).

c#: how to know the full path of dll used in DllImport?

I've import dll in my code like that:
[DllImport("dll.dll", CharSet = CharSet.Ansi)]
private static extern int function(String pars, StringBuilder err);
I was wondered that function works, but it is not inside project and not inside Debug or Release folders. I.e. "dll.dll" should not be availabe because it is not in the current project folder, however it IS available. Now I want to know the exact full path of the dll used at runtime, but don't know how to get it.
You're going to have to use the win32 API.
First use GetModuleHandle passing "dll.dll" to it. Then pass that handle to GetModuleFileName.
string GetDllPath()
{
const int MAX_PATH = 260;
StringBuilder builder = new StringBuilder(MAX_PATH);
IntPtr hModule = GetModuleHandle("dll.dll"); // might return IntPtr.Zero until
// you call a method in
// dll.dll causing it to be
// loaded by LoadLibrary
Debug.Assert(hModule != IntPtr.Zero);
uint size = GetModuleFileName(hModule, builder, builder.Capacity);
Debug.Assert(size > 0);
return builder.ToString(); // might need to truncate nulls
}
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32.dll", SetLastError=true)]
[PreserveSig]
public static extern uint GetModuleFileName
(
[In] IntPtr hModule,
[Out] StringBuilder lpFilename,
[In][MarshalAs(UnmanagedType.U4)] int nSize
);
See Dynamic-Link Library Search Order -- this should hold true with P/Invoke, but the behavior is slightly alterable depending upon load method. However it doesn't say how to determine the filename of a loaded DLL ;-)
A completely un-tested and perhaps ill-advised solution to find the DLL path at run-time. This assumes that P/Invoke and LoadLibrary use the same resolution (e.g. P/Invoke doesn't use LoadLibraryEx with LOAD_WITH_ALTERED_SEARCH_PATH) and that there are no awful conflicts with this approach.
Load the DLL with LoadLibrary or LoadLibaryEx.
Find the filename of the module using GetModuleFileName and the handle obtained in step #1.
Unload the module with FreeLibrary. P/Invoke should keep the module reference-count > 0, but again, this is untested ;-)
(There is no guarantee about the correctness or validity of the above solution. Suggestions, warnings and/or corrections welcome. YMMV.)
Happy coding.
If you really want to find out, use dependency-walker
It can even 'monitor' your application in runtime and detect dynamic loads so nothing remains hidden

Categories

Resources