닷넷은 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 하는 용도로 사용할 수 있게 해준다.
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를 참고한다.
Rust 프로젝트는 터미널에서 cargo
명령어를 통해서 생성 할 수 있다. 프로젝트 hello
를 생성한다고 가정할 때 임의의 디렉터리에서 cargo new hello --bin
을 실행하면 hello
디렉터리가 만들어지고 이 안에 기본 뼈대가 생성된다. 또는 직접 hello
디렉터리를 만들고 이 안에서 cargo init
명령어를 실행해도 똑같은 결과를 볼 수 있다.
hello\
src\
main.rs
Cargo.toml
main()
함수가 존재하는 기본 메인 파일[dependencies]
hello
디렉터리 안에서 cargo check
, cargo build
, cargo run
을 실행하면 결과를 볼 수 있는데 check은 문법 확인, build는 결과 생성, run은 결과를 실행한다. 바로 실행하고 싶다면 cargo run
을 실행한다.
위의 이미지는 Rust로 개발한 Unix 스타일의 Redox1 운영체제이다. 현재 개발 단계이며 글 쓴 시점에서 버전은 0.6.0이다. 첫 포스팅에 Redox 이미지를 사용한 이유는 Rust 언어의 성격을 잘 표현해주기 때문이다. 참고로 위의 이미지는 개인 노트북의 VirtualBox에서 실행한 모습이다.