1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Posted at

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

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


<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>

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


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


// 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)

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?