플랫폼 호출에 대한 원본 생성

.NET 7에는 C# 코드에서 LibraryImportAttribute를 인식하는 P/Invoke용 원본 생성기가 도입되었습니다.

원본 생성을 사용하지 않는 경우 .NET 런타임의 기본 제공 Interop 시스템은 런타임에 JIT 처리된 IL 명령 스트림인 IL 스텁을 생성하여 관리에서 비관리로의 전환을 용이하게 합니다. 다음 코드는 이 메커니즘을 사용하는 P/Invoke를 정의한 후 호출하는 방법을 보여 줍니다.

[DllImport(
    "nativelib",
    EntryPoint = "to_lower",
    CharSet = CharSet.Unicode)]
internal static extern string ToLower(string str);

// string lower = ToLower("StringToConvert");

IL 스텁은 매개 변수 및 반환 값의 마샬링을 처리하고 비관리 코드 호출 방법에 영향을 미치는 DllImportAttribute의 설정을 준수하면서 비관리 코드 호출을 처리합니다(예: SetLastError). 이 IL 스텁은 런타임에 생성되므로 AOT(Ahead-of-Time) 컴파일러 또는 IL 트리밍 시나리오에는 사용할 수 없습니다. IL 생성은 마샬링을 위해 고려해야 할 중요한 비용을 나타냅니다. 이 비용은 동적 코드 생성을 허용하지 않을 수 있는 잠재적 대상 플랫폼에 대한 지원 및 애플리케이션 성능 측면에서 측정할 수 있습니다. 네이티브 AOT 애플리케이션 모델은 모든 코드를 미리 네이티브 코드로 직접 사전 컴파일하여 동적 코드 생성 문제를 해결합니다. DllImport 사용은 전체 네이티브 AOT 시나리오가 필요한 플랫폼에는 옵션이 아니므로 다른 방식(예: 원본 생성)을 사용하는 것이 더 적절합니다. DllImport 시나리오에서 마샬링 논리를 디버깅하는 것도 쉽지 않은 작업입니다.

.NET 7 SDK에 포함되어 있고 기본적으로 사용하도록 설정되어 있는 P/Invoke 원본 생성기는 staticpartial 메서드에서 LibraryImportAttribute를 찾아 마샬링 코드의 컴파일 시간 원본 생성을 트리거하므로 런타임 시 IL 스텁을 생성하고 P/Invoke가 인라인되도록 허용합니다. 기본 제공 시스템에서 원본 생성기로의 마이그레이션과 일반적인 사용을 돕기 위해 분석기와 코드 수정기도 포함되어 있습니다.

기본 사용법

LibraryImportAttribute는 사용법이 DllImportAttribute와 유사하도록 설계되었습니다. LibraryImportAttribute를 사용하고 메서드를 extern 대신 partial로 표시하여 P/Invoke 원본 생성을 사용하도록 이전 예를 변환할 수 있습니다.

[LibraryImport(
    "nativelib",
    EntryPoint = "to_lower",
    StringMarshalling = StringMarshalling.Utf16)]
internal static partial string ToLower(string str);

컴파일하는 동안 원본 생성기는 string 매개 변수의 마샬링과 반환 값을 UTF-16으로 처리하는 ToLower 메서드 구현을 생성하도록 트리거됩니다. 마샬링은 이제 생성된 소스 코드이므로 실제로 디버거에서 논리를 살펴보고 단계별로 실행할 수 있습니다.

MarshalAs

원본 생성기는 MarshalAsAttribute도 준수합니다. 앞의 코드는 다음과 같이 작성할 수도 있습니다.

[LibraryImport(
    "nativelib",
    EntryPoint = "to_lower")]
[return: MarshalAs(UnmanagedType.LPWStr)]
internal static partial string ToLower(
    [MarshalAs(UnmanagedType.LPWStr)] string str);

MarshalAsAttribute에 대한 일부 설정은 지원되지 않습니다. 지원되지 않는 설정을 사용하려고 하면 원본 생성기에서 오류가 발생합니다. 자세한 내용은 DllImport와의 차이점을 참조하세요.

호출 규칙

호출 규칙을 지정하려면 다음과 같이 UnmanagedCallConvAttribute를 사용합니다.

[LibraryImport(
    "nativelib",
    EntryPoint = "to_lower",
    StringMarshalling = StringMarshalling.Utf16)]
[UnmanagedCallConv(
    CallConvs = new[] { typeof(CallConvStdcall) })]
internal static partial string ToLower(string str);

DllImport와의 차이점:

LibraryImportAttribute는 대부분의 경우 DllImportAttribute에서 직접 변환하도록 의도되었지만 의도적인 변경 내용이 몇 가지 있습니다.

  • CallingConventionLibraryImportAttribute에 상응하는 항목이 없습니다. UnmanagedCallConvAttribute를 대신 사용해야 합니다.
  • CharSet(CharSet의 경우)이 StringMarshalling(StringMarshalling의 경우)로 바뀌었습니다. ANSI가 제거되었으며 이제 UTF-8이 최고 수준의 옵션으로 제공됩니다.
  • BestFitMappingThrowOnUnmappableChar에는 상응하는 항목이 없습니다. 이러한 필드는 Windows에서 ANSI 문자열을 마샬링하는 경우에만 관련이 있습니다. ANSI 문자열을 마샬링하기 위해 생성된 코드는 BestFitMapping=falseThrowOnUnmappableChar=false와 동등한 동작을 갖습니다.
  • ExactSpelling에는 상응하는 항목이 없습니다. 이 필드는 Windows 중심 설정이며 Windows가 아닌 운영 체제에는 영향을 미치지 않습니다. 메서드 이름 또는 EntryPoint는 진입점 이름의 정확한 맞춤법이어야 합니다. 이 필드에는 Win32 프로그래밍에 사용되는 AW 접미사와 관련된 역사적 용도가 있습니다.
  • PreserveSig에는 상응하는 항목이 없습니다. 이 필드는 Windows 중심 설정이었습니다. 생성된 코드는 항상 서명을 직접 번역합니다.
  • 프로젝트는 AllowUnsafeBlocks를 사용하여 안전하지 않은 것으로 표시되어야 합니다.

MarshalAsAttribute의 일부 설정, 특정 형식의 기본 마샬링 및 기타 상호 운용성 관련 특성에 대한 지원에도 차이가 있습니다. 자세한 내용은 호환성 차이점에 대한 설명서를 참조하세요.

참고 항목