P /在Mono上调用动态加载的库

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

所以当它不在PATH中时可以找到这个库,我将它保存到临时文件后明确加载它。 在Windows上,这与来自kernel32.dll的LoadLibrary正常工作。 我正在尝试在Linux上对dlopen进行相同的操作,但是在DllNotFoundException加载P / Invoke方法时,我得到了一个DllNotFoundException

我已验证库“libindexfile.so”已成功保存到临时目录,并且对dlopen的调用成功完成。 我深入研究了单声道源代码,试图弄清楚发生了什么,我认为这可能归结为后续的dlopen调用是否会重用先前加载的库。 (当然,假设我通过单声道来源的天真飞翔得出了正确的结论)。

这是我想要做的事情的形状:

// 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调用都会返回一个有效的句柄:

int _tmain(int argc, _TCHAR* argv[])
{
    LPCTSTR libpath = L"D:somepathtolibrary.dll";

    HMODULE handle1 = LoadLibrary(libpath);
    printf("Handle: %xn", handle1);

    HMODULE handle2 = LoadLibrary(L"library.dll");
    printf("Handle: %xn", handle2);

    return 0;
}

如果在Linux上使用dlopen尝试了相同的操作,则第二次调用将失败,因为它不会假定具有相同名称的库将位于相同的路径中。 这有什么办法吗?


经过多次搜索和头部划伤,我发现了一个解决方案。 可以通过使用动态P / Invoke来完全控制P / Invoke过程,以告知运行时确切地在哪里找到代码。


编辑:

Windows解决方案

您需要这些导入:

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

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

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

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

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

IntPtr ptr = GetProcAddress(moduleHandle, methodName);

将此ptr转换为TDelegate类型的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);

像以前一样将其转换为委托:

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

对于我编写的帮助程序库,请参阅我的GitHub。


尝试从终端上像这样运行它:

export MONO_LOG_LEVEL=debug
export MONO_LOG_MASK=dll
mono --debug yourapp.exe

现在,每个库查询都将打印到终端,以便您能够找出发生了什么问题。


不知道你为什么认为这与mono有关,因为你所遇到的问题不是关于mono的动态加载工具。

如果你的更新后的示例有效,它只是意味着Windows上的LoadLibrary()与Linux上的dlopen()有不同的语义:因此,你必须忍受差异或实现自己的抽象处理目录问题(我的预感是它不是保留的目录,但是Windows只是查看是否已经加载了一个名称相同的库并重新使用它)。

链接地址: http://www.djcxy.com/p/44583.html

上一篇: P/Invoke to dynamically loaded library on Mono

下一篇: How to adjust %PATH% for dynamically loaded native dlls?