13

アンマネージ コードを使用するクロスプラットフォーム .NET ライブラリを作成しています。私のクラスの静的コンストラクターでは、プラットフォームが検出され、適切なアンマネージ ライブラリが埋め込みリソースから抽出され、temp ディレクトリに保存されます

ライブラリが PATH にないときに見つけられるように、一時ファイルに保存した後に明示的にロードします。Windows では、これLoadLibraryは kernel32.dll から正常に動作します。私はdlopenLinux で同じことをしようとしていますが、DllNotFoundException後で P/Invoke メソッドをロードするときにエラーが発生します。

ライブラリ「libindexfile.so」が一時ディレクトリに正常に保存され、呼び出しがdlopen成功することを確認しました。私は何が起こっているのかを理解するためにmono ソースを掘り下げました。それは、次の への呼び出し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);
}

アップデート:

on windowsへの後続の呼び出しLoadLibraryは、同じ名前の dll が既にロードされているかどうかを確認し、そのパスを使用するように見えます。たとえば、次のコードでは、両方の呼び出しがLoadLibrary有効なハンドルを返します。

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、2 回目の呼び出しは失敗します。これは、同じ名前のライブラリが同じパスにあると想定していないためです。これを回避する方法はありますか?

4

4 に答える 4

1

ターミナルから次のように実行してみてください。

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

これで、すべてのライブラリルックアップが端末に出力されるため、何が問題になっているのかを見つけることができます。

于 2012-11-19T22:22:37.867 に答える
1

一時的な場所に抽出されたネイティブ ライブラリをロードする必要がありましたが、ほぼ解決策が見つかりました。私はMonoのソースコードをチェックして、方法を見つけました:

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

この種の作品。問題は、Mono のばかげた JIT コンパイラが、このライブラリを参照する DllImported メソッドの気配を呼び出してから呼び出すことを許可できないことmono_dllmap_insert()です。

もしそうなら、奇妙なことが起こるからです:

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

だから私は私のネイティブを呼び出しているのでsomeGreatFunc()、Monoはライブラリを見つけてロードすることができます(チェックしました)、シンボルを見つけることができます(チェックしました)が、過去にJITを行っていたときはそのライブラリをロードできないため、DllNotFoundExceptionとにかくスローすることにします。生成されたコードには、ハードコードされた throw ステートメントか何かが含まれていると思います:-O

呼び出す前にJITされていなかった同じライブラリから別のネイティブ関数を呼び出すとmono_dllmap_insert()、それは機能します。

したがって、@gordonmleigh によって追加された手動のソリューションを使用するか、これらのインポートのいずれかを JIT する前にライブラリの場所を Mono に伝える必要があります。そこでリフレクションが役立つかもしれません。

于 2018-05-09T15:03:25.400 に答える
0

あなたが抱えている問題はモノの動的読み込み機能に関するものではないため、これがモノに関連していると考える理由がわかりません。

更新されたサンプルが機能する場合は、Windows の LoadLibrary() が Linux の dlopen() とは異なるセマンティクスを持っていることを意味します。そのため、違いを受け入れるか、ディレクトリの問題を処理する独自の抽象化を実装する必要があります (私の勘は保持されているのはディレクトリではありませんが、Windows は単に同じ名前のライブラリが既に読み込まれているかどうかを確認し、それを再利用します)。

于 2012-11-20T09:48:29.773 に答える