P/在Mono上调用动态加载的库 [英] P/Invoke to dynamically loaded library on Mono

查看:107
本文介绍了P/在Mono上调用动态加载的库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个使用一些非托管代码的跨平台.NET库.在我的类的静态构造函数中,将检测平台,并从嵌入式资源中提取适当的非托管库,并将其保存到temp目录中,类似于

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.

为使该库不在PATH中也能找到它,我将其保存到临时文件后显式加载.在Windows上,它可以与kernel32.dll中的LoadLibrary一起正常工作.我试图在Linux上对dlopen做同样的事情,但是稍后在加载P/Invoke方法时却得到了DllNotFoundException.

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.

我已经验证了库"libindexfile.so"已成功保存到temp目录,并且对dlopen的调用成功.我深入研究了单源尝试弄清楚发生了什么,我认为这可能归结为后续对dlopen的调用是否只会重用以前加载的库. (当然,假设我天真地掌握了单声道源得出了正确的结论).

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);
}


更新:

似乎随后在Windows上对LoadLibrary的调用看起来是否已经加载了同名的dll,然后使用该路径.例如,在以下代码中,对LoadLibrary的两个调用都将返回有效的句柄:

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;
}

如果在Linux上对dlopen尝试相同的操作,则第二次调用将失败,因为它不假定具有相同名称的库将位于相同的路径.有什么办法解决这个问题吗?

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?

推荐答案

经过大量的搜索和从头开始,我发现了一个解决方案.通过使用动态P/Invoke <,P/Invoke进程可以完全控制. /a>告诉运行时确切的代码位置.

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.

您需要这些导入:

[DllImport("kernel32.dll")]
protected static extern IntPtr LoadLibrary(string filename);

[DllImport("kernel32.dll")]
protected static extern IntPtr GetProcAddress(IntPtr hModule, string procname);

应通过调用LoadLibrary来加载非托管库:

The unmanaged library should be loaded by calling LoadLibrary:

IntPtr moduleHandle = LoadLibrary("path/to/library.dll");

通过调用GetProcAddress获取指向dll中函数的指针:

Get a pointer to a function in the dll by calling GetProcAddress:

IntPtr ptr = GetProcAddress(moduleHandle, methodName);

将此ptr投射为TDelegate类型的委托:

Cast this ptr to a delegate of type TDelegate:

TDelegate func = Marshal.GetDelegateForFunctionPointer(
    ptr, typeof(TDelegate)) as TDelegate;

Linux解决方案

使用这些导入:

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 

加载库:

IntPtr moduleHandle = dlopen(modulePath, RTLD_NOW);

获取函数指针:

IntPtr ptr = dlsym(moduleHandle, methodName);

像以前一样将其广播给委托人:

Cast it to a delegate as before:

TDelegate func = Marshal.GetDelegateForFunctionPointer(
    ptr, typeof(TDelegate)) as TDelegate;


有关我编写的帮助程序库,请参见我的GitHub .

这篇关于P/在Mono上调用动态加载的库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆