P/Invoke to dynamically loaded library on Mono(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 上动态加载库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:P/Invoke 在 Mono 上动态加载库
基础教程推荐
- 将 XML 转换为通用列表 2022-01-01
- rabbitmq 的 REST API 2022-01-01
- SSE 浮点算术是否可重现? 2022-01-01
- 如何在 IDE 中获取 Xamarin Studio C# 输出? 2022-01-01
- 为什么Flurl.Http DownloadFileAsync/Http客户端GetAsync需要 2022-09-30
- c# Math.Sqrt 实现 2022-01-01
- 有没有办法忽略 2GB 文件上传的 maxRequestLength 限制? 2022-01-01
- MS Visual Studio .NET 的替代品 2022-01-01
- 如何激活MC67中的红灯 2022-01-01
- 将 Office 安装到 Windows 容器 (servercore:ltsc2019) 失败,错误代码为 17002 2022-01-01