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

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

问题描述

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

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"已成功保存到临时目录,并且对 dlopen 的调用成功.我深入研究了 mono source 来尝试弄清楚发生了什么,我认为这可能归结为对 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).

这是我想要做的形状:

// 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
", handle1);

    HMODULE handle2 = LoadLibrary(L"library.dll");
    printf("Handle: %x
", 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 解决方案

使用这些导入:

[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/Invoke 在 Mono 上动态加载库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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