프로그램 언어들은 예외 핸들링(exception handling) 또는 반환 값(return value) 이라는 두 가지 에러 핸들링 접근 방식 중 한 가지를 사용한다. Rust는 후자를 사용한다.1 이전 글 Rust-004, 러스트 예외/에러 처리에서 복구 가능한 에러를 위한 Result<T, E>
사용법을 살펴봤다.
Rust의 Result는 한가지 에러 타입만 처리가 기본적으로 가능하다. 두 가지 이상의 다른 에러 타입은 처리가 불가능할 때 사용할 수 있는 Custom Error Handling 방법을 이번 글에서 살펴본다. 에러처리 간소화를 위한 thiserror, anyhow 크레이트 예제를 소개하였다.
use std::fs::File;
use std::io::Write;
use std::num::ParseIntError;
fn main() {
println!("{:?}", square("2"));
println!("{:?}", square("invalid"));
}
fn square(val: &str) -> Result<i32, ParseIntError> {
let num = val.parse::<i32>()?;
let mut f = File::open("file.txt")?;
let string_to_write = format!("Square of {} is {}", num, i32::pow(num, 2));
f.write(string_to_write.as_bytes())?;
Ok(i32::pow(num, 2))
}
/*
the trait `From<std::io::Error>` is not implemented for `ParseIntError`
ParseIntError, Error : 에러 타입이 두 가지지만 하나의 에러 타입만 지정
*/
닷넷 7.0 이전 버전에서는 대부분 외부 DLL 파일을 Import 하거나 P/Invoke1를 활용하여 Native DLL(Unmanaged DLL)에 있는 함수를 호출하기 위해 DllImport
를 사용해 왔다. 8.0버전부터는 LibraryImport
의 사용을 권장한다.
LibraryImport가 새로 생겨난 이유는 DllImport가 마샬링을 런타임에서 수행해서 IL 코드를 emit 한다고 하는데 NativeAOT 등 동적으로 IL 코드를 생성할 수 없는 환경에서 쓸 수 없으므로 LibraryImport의 소스 생성기 기능을 이용해 컴파일 시점에서 마샬링 코드를 삽입한다.2
기존에 작성했던 DllImport에서 LibraryImport 스타일로 변경할 때 몇 가지 변경 사항이 있는데 이 부분을 간단한 예제를 통하여 정리하였다. ~.csproj
를 열어 <PropertyGroup>
사이에<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
를 추가한다.
[LibraryImport("user32.DLL", EntryPoint = "ReleaseCapture", StringMarshalling = StringMarshalling.Utf16)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvStdcall)])]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool ReleaseCapture();
[LibraryImport("user32.DLL", EntryPoint = "SendMessageW", StringMarshalling = StringMarshalling.Utf16)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvStdcall)])]
[return: MarshalAs(UnmanagedType.SysInt)]
private static partial IntPtr SendMessageW(IntPtr hWnd, uint wMsg, IntPtr wParam, IntPtr lParam);
[LibraryImport("USER32.DLL", EntryPoint = "FindWindowW", StringMarshalling = StringMarshalling.Utf16)]
[UnmanagedCallConv(CallConvs = [typeof(CallConvStdcall)])]
[return: MarshalAs(UnmanagedType.SysInt)]
private static partial IntPtr FindWindowW(string? lpClassName, string? lpWindowName);
public static void DragMoveWindow(string windowTitle)
{
// MainWindow? mainWindow = Application.Current.MainWindow as MainWindow;
// _ = ReleaseCapture();
// _ = SendMessageW(mainWindow == null ? Process.GetCurrentProcess().MainWindowHandle : FindWindowW(null, mainWindow.Title),
// 0x112, 0xf012, 0);
_ = ReleaseCapture();
_ = SendMessageW(FindWindowW(null, windowTitle), 0x112, 0xf012, 0);
}
C#(.NET)으로 만든 앱을 네이티브 AOT로 게시하면 자체 포함 배포처럼 네이티브 코드로 AOT(ahead-of-time) 컴파일된 앱이 생성된다. 즉 IL(Intermediate language)을 네이티브 코드로 컴파일한다.1
System.Text.Json에서 원본을 생성하는 방법은 몇 가지 추가 및 제약 사항이 있는데 아래의 예제는 이를 설명하는 소스이다.2 테스트를 위해 생성한 파일은 Program.cs, TestModel.cs, JsonHelper.cs
이다.
테스트를 위하여 콘솔 프로젝트를 만들고 프로젝트 설정(~.csproj)을 아래와 같이 변경하고 터미널에서 dotnet publish -c Release -r win-x64
빌드한다. 정상적으로 빌드가 완료되면 하위 폴더 Native
에 실행파일이 생성된다.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<PublishAot>true</PublishAot>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>
</Project>