LoginSignup
2
0

More than 3 years have passed since last update.

[.NET Core 3]ライブラリの動的ロード(NativeLibrary)を使ってみた

Last updated at Posted at 2020-07-11

.NET Core 3から、標準(System.Runtime.InteropServices.NativeLibrary クラス)でライブラリの動的ロード、およびエクスポートされた関数のアドレスを取得できるようになったらしいです。

このツイートで知りました。

つまり、WindowsでいうとLoadLibraryGetProcAddressのP/Invokeを書く必要がなくなります。
(Linuxでのdlopendlsymは試してません)

動的ロード以外にも、DLLImportしたメソッドのライブラリの解決も制御できるようになっているようですが、この記事では動的ロードしたライブラリの関数を呼び出すだけです。

動的ロードと関数の呼び出し例

using System;
using System.Runtime.InteropServices;

class Program
{
    [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
    delegate int MessageBoxW(IntPtr hWnd, string lpText, string lpCaption, uint uType);

    static void Main(string[] args)
    {
        IntPtr user32 = NativeLibrary.Load("User32.dll");
        var fpMessageBox = NativeLibrary.GetExport(user32, "MessageBoxW");
        var messagebox = Marshal.GetDelegateForFunctionPointer<MessageBoxW>(fpMessageBox);
        messagebox(default, "NativeLibrarySample", "Caption", 0);
        NativeLibrary.Free(user32);
    }
}

なんというか、LoadLibraryGetProcAddressそのままですね。
P/Invokeの様に末尾のA/Wは自動で検索してくれません。

ポインターが得られるので、GetDelegateForFunctionPointerでデリゲート経由で呼び出す必要があります。

NativeLibrary.Loadの戻り値はせめてSafeLibraryHandleとかにくるんで欲しかった。
NativeLibrary.GetExportも型引数でデリゲートを指定させて欲しい。

C# 9.0 Preview

C# 9.0には関数ポインターの構文が入るので、同様のことをしてみました。

//    <LangVersion>preview</LangVersion>
//    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
using System;
using System.Runtime.InteropServices;

unsafe class Program
{
    static void Main(string[] args)
    {
        IntPtr user32 = NativeLibrary.Load("User32.dll");
        var fpMessageBox = NativeLibrary.GetExport(user32, "MessageBoxW");
        var messagebox = (delegate* stdcall<IntPtr, char*, char*, uint, int>)fpMessageBox;
        fixed (char* text = "NativeLibrarySample")
        fixed (char* caption = "Caption")
            messagebox(default, text, caption, 0);
        NativeLibrary.Free(user32);
    }
}

マーシャリングされないので、文字列も固定しないと行けないのが面倒ですね。
(正式版では関数ポインターの構文は少し変わるようですが。)

文字列の渡し方

文字列の渡し方が煩わしいので、下記のようのな方法を考えたが、(「安全」ではなく)動作として保証できるのかちょっとわからない…


static class Extention
{
    public static StringHolder Hold(this string s) => new StringHolder(s);

}

unsafe readonly ref struct StringHolder
{
    readonly ReadOnlySpan<char> span;
    public StringHolder(string s) => span = s;

    public char* Pointer => (char*)Unsafe.AsPointer(ref Unsafe.AsRef(in MemoryMarshal.GetReference(span)));
}

...
    messagebox(default, "NativeLibrarySample".Hold().Pointer, caption, 0);
...

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0