最終更新日 2025-04-27

Native AOT による手法 (マイクロソフト公式)

概要

Native AOTとは、C#をネイティブにコンパイル出来るものです。
その一貫として、ネイティブのdllも作成できます。
.NET 9.0 以降は、「x64のdll」と「x86のdll」の両方に対応しました。
(.NET 8.0までは x64のみの対応でした)

ClassLibrary36.cs
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

internal sealed class StaticWStrPtrHandle : SafeHandle
{
    private StaticWStrPtrHandle() : base(IntPtr.Zero, true) { }
    public StaticWStrPtrHandle(String managedString) : base(IntPtr.Zero, true)
    {
        handle = Marshal.StringToHGlobalUni(managedString);
    }

    public static implicit operator String(StaticWStrPtrHandle managedString)
    {
        return managedString.ToString();
    }

    protected override bool ReleaseHandle()
    {
        try
        {
            Marshal.FreeHGlobal(handle);
        }
        catch (Exception ex)
        {
            System.Diagnostics.Trace.WriteLine("文字列解放エラー:\n" + ex.Message);
            return false;
        }

        return true;
    }

    public override bool IsInvalid
    {
        get { return (IntPtr.Zero == handle); }
    }

    public override string ToString()
    {
        if (this.IsInvalid)
        {
            return String.Empty;
        }
        else
        {
            return Marshal.PtrToStringUni(handle) ?? String.Empty;
        }
    }
}


 
namespace ClassLibrary3
{
    public class Class1
    {
        // 文字列を引数とする関数
        [UnmanagedCallersOnly(EntryPoint = "my_func1", CallConvs = new[] { typeof(CallConvCdecl) })]
        public static IntPtr my_func1(IntPtr wStringPointer)
        {
            String? str = Marshal.PtrToStringUni(wStringPointer);
            if (str == null)
            {
                return IntPtr.Zero;
            }

            return (IntPtr)str.Length;
        }

        // static な静的フィールドのクラス変数にしておくことが重要。
        // 関数と1対1にしておく。
        static StaticWStrPtrHandle hReturnWStringPointer = new StaticWStrPtrHandle("");

        [UnmanagedCallersOnly(EntryPoint = "my_func2", CallConvs = new[] { typeof(CallConvCdecl) })]
        // 文字列を返す関数
        public static IntPtr my_func2()
        {
            // 先にDispose()をする。1つ前の文字列を解放するため。
            hReturnWStringPointer.Dispose();

            // 文字列を StaticWStrPtrHandle 型として確保する。
            hReturnWStringPointer = new StaticWStrPtrHandle("★🎶👨‍👦‍👦");

            // 文字列が格納されているメモリアドレスを返す。
            return hReturnWStringPointer.DangerousGetHandle();
        }
    }
}

dllをネイティブAOTに対応するには、

プロジェクトファイル
<Project Sdk="Microsoft.NET.Sdk">

	<PropertyGroup>
		<TargetFramework>net9.0-windows</TargetFramework>
		<ImplicitUsings>enable</ImplicitUsings>
		<Nullable>enable</Nullable>
		<PublishAot>true</PublishAot>
		<IsAotCompatible>true</IsAotCompatible>
	</PropertyGroup>        

といったように設定しましょう。

通常のビルドではなく、発行する

これで、ネイティブのdll (C++で作成したものに近いdll)ができました。
実際には純粋なネイティブというわけではなく、
C++/CLIで作成したものに少し近い混成アセンブリの性質を帯びています。
(このため、freedllが効きません)

マクロ側でからの呼び出し

test.mac
jsmode "WebView2\\" + currentmacrofilename;

js {
    debuginfo(2);
    let dll = loaddll(currentmacrodirectory() + "\\ClassLibrary3.dll");
    console.log(dll);
    let len = dll.dllFuncW.my_func1("あいうえお");
    console.log(len);
    let sss = dll.dllFuncStrW.my_func2();
    console.log(sss);
}