AOTを使用してネイティブdllをエクスポートする

以前、医療保険インターフェースを作成していた時、C++やDelphiで書かれたネイティブdll（医療保険カードの読み取りなどに使用されるもの）を頻繁に呼び出すことが多かったです。ここでは、C#を使用して、ネイティブで直接呼び出しが可能なdllを生成す る方法について簡単に紹介します。具体的には、UnmanagedCallersOnlyAttributeを介して行います。

dllプロジェクトファイルは以下の通りです。



```xml

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>

    <TargetFramework>net8.0</TargetFramework>

    <LangVersion>12</LangVersion>

    <PublishAot>true</PublishAot>

  </PropertyGroup>

  <ItemGroup>

    <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="8.0.0" />

  </ItemGroup>

</Project>

```



いくつかの簡単なメソッドを定義しました。以下の通りです：



```csharp

using System;

using System.Collections.Generic;

using System.Runtime.CompilerServices;

using System.Runtime.InteropServices;

using System.Text;



namespace classlib {

    // 発行コマンド dotnet publish /p:NativeLib=Shared --use-current-runtime

    public static class DllTest {

        [UnmanagedCallersOnly(EntryPoint = "int_add")]

        public static int Add(int a, int b) {

            return a + b;

        }



        [UnmanagedCallersOnly(EntryPoint = "string_add")]

        public static IntPtr StringTest(IntPtr ptr1, IntPtr ptr22) {

            var s = Marshal.PtrToStringUTF8(ptr1) + Marshal.PtrToStringUTF8(ptr22);

            return Marshal.StringToCoTaskMemUTF8(s);

        }



        [UnmanagedCallersOnly(EntryPoint = "get_doubles")]

        public static IntPtr StringTest() {

            return Marshal.UnsafeAddrOfPinnedArrayElement(new double[] { 1.1, 1.2 }, 0);

        }



        [UnmanagedCallersOnly(EntryPoint = "set_doubles", CallConvs = new[] { typeof(CallConvCdecl) })]

        public static double SetAll(IntPtr InItems, int InItemsLength) {

            var sum = 0d;

            for (int i = 0; i < InItemsLength; i++) {

                sum += Marshal.PtrToStructure<double>(InItems + i * Marshal.SizeOf<double>());

            }

            return sum;

        }



        [UnmanagedCallersOnly(EntryPoint = "get_order")]

        public static IntPtr get_order() {

            var order = new Order { ID = 1, Name = "注文1" };

            IntPtr structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(order));

            Marshal.StructureToPtr(order, structPtr, false);

            return structPtr;

        }

    }



    public struct Order {

        public int ID { get; set; }

        public string Name { get; set; }

    }

}

```



次に、コマンドラインでアプリケーションを公開します：



```

dotnet publish /p:NativeLib=Shared --use-current-runtime

```



公開されたdllを現在のプロジェクトにコピーし、「ファイルを出力ディレクトリに常にコピーする」属性を設定します。次のコード は、上記のメソッドの呼び出し方と使用するパラメータおよび型です。



```csharp

// See https://aka.ms/new-console-template for more information

using System.Reflection;

using System.Runtime.InteropServices;

using System.Text;



Console.WriteLine(MyClass.int_add(1, 2));

Console.WriteLine(MyClass.string_add("abcd", "efg"));



var resultPtr = MyClass.get_doubles();

var Items = new double[2];

for (int j = 0; j < 2; j++) {

    Items[j] = Marshal.PtrToStructure<double>(resultPtr + j * Marshal.SizeOf<double>());

    Console.WriteLine(Items[j]);

}



var sum = MyClass.set_doubles(new double[] { 5.6, 6.7 }, 2);

Console.WriteLine(sum);



var orderP = MyClass.get_order();

var order = Marshal.PtrToStructure<Order>(orderP);

Console.WriteLine(order);

Console.ReadLine();



public class MyClass {

    // 非托管関数の署名を定義

    [DllImport("classlib.dll")] // DLLファイル名を指定

    public static extern int int_add(int a, int b);



    [DllImport("classlib.dll")] // DLLファイル名を指定

    public static extern string string_add(string a, string b);



    [DllImport("classlib.dll")] // DLLファイル名を指定

    public static extern IntPtr get_doubles();



    [DllImport("classlib.dll")] // DLLファイル名を指定

    public static extern double set_doubles(double[] arr, int length);



    [DllImport("classlib.dll")] // DLLファイル名を指定

    public static extern IntPtr get_order();

}



public struct Order {

    public int ID { get; set; }

    public string Name { get; set; }

    public override string ToString() {

        return $"Order  ID:{ID},Name:{Name}";

    }

}

```



(Translated by GPT)