-
-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
function pointers #347
Comments
https://github.com/dotnet/runtime/blob/e05c631ee62b04d93dfed778abfdada48517ddd7/docs/design/libraries/DllImportGenerator/Compatibility.md |
using System.Runtime.InteropServices;
var libraryHandle = NativeLibrary.Load("user32.dll");
var functionPtr = NativeLibrary.GetExport(libraryHandle, "MessageBoxA");
unsafe
{
// Define a delegate for the MessageBoxW function
var messageBox = (delegate* unmanaged[Cdecl]<nint, byte*, byte*, uint, int>)functionPtr;
fixed (byte* p1 = "Hello World!"u8)
fixed (byte* p2 = "My Caption"u8)
{
// Call the MessageBoxW function
var result = messageBox(0, p1, p2, 1);
Console.WriteLine(result == 1 ? "OK" : "Cancel");
}
}
static partial class Lib
{
[LibraryImport("user32.dll")]
public static partial int MessageBoxA(nint hWnd, ReadOnlySpan<byte> lpText, ReadOnlySpan<byte> lpCaption, uint uType);
} |
win32 API を bind して、primitive な型しか使わない限りは別に DllImport, LibraryImport よりも速いとかもなさげ。 速度を理由には使わなさそう。 using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System.Runtime.InteropServices;
BenchmarkRunner.Run<PInvokeBenchmark>();
public class PInvokeBenchmark
{
[Benchmark]
public DateOnly UseNativeLibrary()
{
ByNativeLibrary.GetSystemTime(out var t);
return new(t.Year, t.Month, t.Day);
}
[Benchmark]
public unsafe DateOnly UseLibraryImport()
{
ByLibraryImport.GetSystemTime(out var t);
return new(t.Year, t.Month, t.Day);
}
[Benchmark]
public unsafe DateOnly UseDllImport()
{
ByDllImport.GetSystemTime(out var t);
return new(t.Year, t.Month, t.Day);
}
}
unsafe static class ByNativeLibrary
{
private static nint _ptr = NativeLibrary.GetExport(NativeLibrary.Load("kernel32.dll"), "GetSystemTime");
public static void GetSystemTime(out SystemTime systemTime)
{
var getSystemTime = (delegate* unmanaged[Cdecl]<out SystemTime, void>)_ptr;
getSystemTime(out systemTime);
}
}
static partial class ByLibraryImport
{
[LibraryImport("kernel32.dll")]
public static partial void GetSystemTime(out SystemTime systemTime);
}
static partial class ByDllImport
{
[DllImport("kernel32.dll")]
public static extern void GetSystemTime(out SystemTime systemTime);
}
struct SystemTime
{
public ushort Year;
public ushort Month;
public ushort DayOfWeek;
public ushort Day;
public ushort Hour;
public ushort Minute;
public ushort Second;
public ushort Milliseconds;
} |
dotnet/runtime 内、ちらほら
if (!NativeLibrary.TryLoad($"{Interop.Libraries.MsQuic}.{s_minMsQuicVersion.Major}", typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out IntPtr msQuicHandle) &&
!NativeLibrary.TryLoad(Interop.Libraries.MsQuic, typeof(MsQuicApi).Assembly, DllImportSearchPath.AssemblyDirectory, out msQuicHandle)) |
.NET IL の仕様的には .NET Framework 1.0 の頃から関数ポインターの仕様持ってる。 関数ポインターのアドレスを IntPtr で取る手段も同じく 1.0 の頃から、 問題はそれを C# から効率よく invoke する手段がなかったこと。 「卵が先か」問題になるけども、効率よく invoke できないから関数ポインターの使い道限られてた。
|
https://github.com/dotnet/csharplang/blob/main/proposals/csharp-9.0/function-pointers.md
元々 static delegate (静的メソッドならアロケーションなしで間接参照できないか?という話)だったけど、どうも unsafe 必須になったみたい。
「アロケーションなしで assembly unload を安全にトラッキングする手段がないから」とのこと。
unsafe 必須となると、ちょっとしたパフォーマンス改善のために頑張って使うというほどじゃなくなる。
なので、interop シナリオ以外での用途があんまりなく、実例込みで説明した方がよさそう。
ufcpp-live/UfcppLiveAgenda#33 (comment)
The text was updated successfully, but these errors were encountered: