닷넷은 Win32 API와 같은 Native DLL을 using System.Runtime.InteropServices
네임스페이스를 통해 호출하는 방법을 제공한다. C++1, Delphi2에서 작성한 DLL 파일을 호출하는 방법은 이미 살펴보았다.
이번 글에서 살펴볼 것은 .NET 7에서 제공하는 Native AOT 컴파일을 사용하여 C#으로 Native DLL을 만들고 이를 불러와 사용하는 간단한 예제이다3. 아래의 소스처럼 ‘NativeLibrary’ 이름으로 프로젝트(DLL, .NET7)를 만든다. 특히 UnmanagedCallersOnly
는 바로 해당 함수를 export 하는 용도로 사용할 수 있게 해준다.
NativeLibrary 예제
using System.Runtime.InteropServices;
namespace NativeLibrary;
public class Class1
{
[UnmanagedCallersOnly(EntryPoint = "add")]
public static int Add(int a, int b)
{
return a + b;
}
[UnmanagedCallersOnly(EntryPoint = "sumstring")]
public static nint SumString(nint first, nint second)
{
string? my1String = Marshal.PtrToStringAuto(first);
string? my2String = Marshal.PtrToStringAuto(second);
string sum = my1String + my2String;
nint sumPointer = Marshal.StringToHGlobalAuto(sum);
return sumPointer;
}
}
nint
타입은 Native int를 말하는데 플랫폼(32/64bit)에 따라 다른 크기를 갖는 정수이다. 위의 소스를 아래의 프로젝트 컴파일 옵션을 사용하여 dll 파일을 생성한다. 자세한 내용은 Building Native Libraries with NativeAOT를 참고한다.
AOT 컴파일 옵션
프로젝트 옵션에 다음을 추가하여 컴파일 하거나
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
아래와 같이 빌드 옵션을 사용한다.
dotnet publish /p:NativeLib=Static /p:SelfContained=true -r <RID> -c <Configuration>
dotnet publish /p:NativeLib=Shared /p:SelfContained=true -r win-x64 -c release
C#에서 Native dll 활용
using System;
using System.Runtime.InteropServices;
using System.Windows;
namespace WpfApp;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
[DllImport(@"NativeLibrary.dll", EntryPoint = "sumstring", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.SysInt)]
private static extern IntPtr LibPingTest([MarshalAs(UnmanagedType.LPWStr)] string inputString1, [MarshalAs(UnmanagedType.LPWStr)] string inputString2);
public static string? GetLibPingTest(string inputString1, string inputString2)
{
return Marshal.PtrToStringAuto(LibPingTest(inputString1, inputString2));
}
private void ButtonTest_OnClick(object sender, RoutedEventArgs e)
{
string? str = GetLibPingTest("가나닭", "마바삵");
TextBlockTest.Text = str;
}
}
위 소스는 WPF 기본 생성 예제에서 버튼 클릭으로 ‘NativeLibrary.dll’ 파일을 호출하여 사용하는 예제이다.
Lazarus(free pascal) dll
Library NativeLibrary;
{$mode objfpc}{$H+}
Uses
Classes,
SysUtils;
function LibTest(inputString: PUnicodeChar): PUnicodeChar; stdcall; export;
var
buffer: UnicodeString;
begin
try
buffer := inputString;
result := PUnicodeChar(UTF8ToString('abc가나닭') + buffer);
except
on e: Exception do
begin
result := PUnicodeChar(e.Message);
end;
end;
end;
exports LibTest;
begin
end.
참고로 위 소스는 Lazarus(free pascal)에서 ‘NativeLibrary.dll’ 파일을 작성하는 예제이다.