Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)
(시리즈 글이 7개 있습니다.)
.NET Framework: 969. .NET Framework 및 .NET 5 - UnmanagedCallersOnly 특성 사용
; https://www.sysnet.pe.kr/2/0/12412

.NET Framework: 970. .NET 5 / .NET Core - UnmanagedCallersOnly 특성을 사용한 함수 내보내기
; https://www.sysnet.pe.kr/2/0/12413

.NET Framework: 971. UnmanagedCallersOnly 특성과 DNNE 사용
; https://www.sysnet.pe.kr/2/0/12415

.NET Framework: 972. DNNE가 출력한 NE DLL을 직접 생성하는 방법
; https://www.sysnet.pe.kr/2/0/12421

.NET Framework: 973. .NET 5, .NET Framework에서만 허용하는 UnmanagedCallersOnly 사용예
; https://www.sysnet.pe.kr/2/0/12422

.NET Framework: 976. UnmanagedCallersOnly + C# 9.0 함수 포인터 사용 시 x86 빌드에서 오동작하는 문제
; https://www.sysnet.pe.kr/2/0/12431

닷넷: 2174. C# - .NET 7부터 UnmanagedCallersOnly 함수 export 기능을 AOT 빌드에 통합
; https://www.sysnet.pe.kr/2/0/13464




.NET 5 / .NET Core - UnmanagedCallersOnly 특성을 사용한 함수 내보내기

지난 글에서,

.NET Framework 및 .NET Core 3.1 이하 - UnmanagedCallersOnly 특성 사용
.NET Framework 및 .NET 5 - UnmanagedCallersOnly 특성 사용
; https://www.sysnet.pe.kr/2/0/12412

다룬 UnmanagedCallersOnly 특성은, .NET Core와 .NET 5 환경에서 추가적인 의미를 갖는데, 바로 해당 함수를 export하는 용도로도 사용할 수 있다는 점입니다. 사실 이에 대해서는 약간의 이력이 있는데요, 원래는 그런 용도로 예전에 NativeCallable 특성이 있었습니다.

C# DLL에서 Win32 C/C++처럼 dllexport 함수를 제공하는 방법 - 두 번째 이야기
; https://www.sysnet.pe.kr/2/0/11884

당시, myget.org에서 배포하던 Microsoft.DotNet.ILCompiler 구성 요소는 NativeCallable 특성을 이용해 함수 내보내기를 구현했는데 이후 CoreRT 측으로 넘어가면서,

dotnet / corert
; https://github.com/dotnet/corert

배포 피드도 "https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json"로 변경됩니다. 그리고 다시 한번 해당 팀이 NativeAOT로 바뀌면서,

dotnet / runtimelab
; https://github.com/dotnet/runtimelab/tree/feature/NativeAOT

최종 배포 피드는 "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json"로 바뀌고 버전도 6.0.0-*으로 바뀌었습니다.

새롭게 바뀌었으니, 예제도 새롭게 ^^ 실습을 해보겠습니다.




프로젝트는 .NET Core, .NET Standard, 또는 .NET 5 대상으로 생성합니다. 이 글에서는 .NET Standard 1.1로 실습을 하겠습니다. 그런 다음, Microsoft.DotNet.ILCompiler 참조를 6.0 버전으로 추가합니다.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard1.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="6.0.0-*" />
  </ItemGroup>
</Project>

그리고, (명령행에서 "dotnet new nuget" 등으로 만들거나, 직접 메모장을 이용해 생성한) nuget.config 파일에 Microsoft.DotNet.ILCompiler 구성 요소를 내려받을 수 있는 피드를 추가합니다.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <packageSources>
        <!--To inherit the global NuGet package sources remove the <clear/> line below -->
        <clear />
        <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
        <add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
    </packageSources>
</configuration>

환경 준비는 끝났고, 이제 다음과 같이 UnmanagedCallersOnly 특성을 이용해 코드를 작성하시면 됩니다.

using System;
using System.Runtime.InteropServices;

public class Class1
{
    // 반드시 EntryPoint 속성에 값을 설정!
    [UnmanagedCallersOnly(EntryPoint = "mymethod")]
    public static void MyMethod(IntPtr ptrText)
    {
        string text = Marshal.PtrToStringUni(ptrText);
        // Console.WriteLine($"{DateTime.Now} {text}");
    }
}

namespace System.Runtime.InteropServices
{
#if !NET5_0
    [AttributeUsage(AttributeTargets.Method)]
    public sealed class UnmanagedCallersOnlyAttribute : Attribute
    {
        public string EntryPoint;
        public CallingConvention CallingConvention;
        public UnmanagedCallersOnlyAttribute() { }
    }
#endif
}

남은 작업은, "dotnet publish" 명령어로 바이너리를 생성하는 것으로 끝!

dotnet publish /p:NativeLib=Shared -r win-x64 -c Release

이후 depends.exe 또는 dumpbin.exe 등의 도구로 export시킨 함수가 잘 나열되어 있는지 확인하시면 됩니다.




여기서 중요한 것은, 해당 DLL 파일이 "AOT(Ahead-of-time)" 빌드가 되었다는 점입니다. 따라서, 자체 실행 가능한 native 모듈이므로 이 DLL과 관련해서는 어떠한 부가 모듈도 필요가 없습니다. 덕분에, C++에서 사용할 때도 사용법이 다음과 같이 매우 간단합니다.

#include <iostream>
#include <windows.h>

typedef void(__stdcall* mymethod_proc)(intptr_t text);

static void* load_library(const char* path)
{
    HMODULE h = LoadLibraryA(path);
    return (void*)h;
}

static void* get_export(void* h, const char* name)
{
    void* f = GetProcAddress((HMODULE)h, name);
    return f;
}

int main()
{
    const wchar_t* pText = L"Hello World";

    void* mod = load_library("ClassLibrary.dll");
    mymethod_proc mymethod = (mymethod_proc)get_export(mod, "mymethod");
    mymethod((intptr_t)pText);
}

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 11/28/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 91  92  93  [94]  95  96  97  98  99  100  101  102  103  104  105  ...
NoWriterDateCnt.TitleFile(s)
11581정성태7/4/201821323Math: 34. GeoGebra 기하 (11) - 3대 작도 불능 문제의 하나인 임의 각의 3등분파일 다운로드1
11580정성태7/4/201818072Math: 33. GeoGebra 기하 (10) - 직각의 3등분파일 다운로드1
11579정성태7/4/201817200Math: 32. GeoGebra 기하 (9) - 임의의 선분을 한 변으로 갖는 정삼각형파일 다운로드1
11578정성태7/3/201817354Math: 31. GeoGebra 기하 (8) - 호(Arc)의 이등분파일 다운로드1
11577정성태7/3/201817278Math: 30. GeoGebra 기하 (7) - 각의 이등분파일 다운로드1
11576정성태7/3/201819453Math: 29. GeoGebra 기하 (6) - 대수의 4칙 연산파일 다운로드1
11575정성태7/2/201819870Math: 28. GeoGebra 기하 (5) - 선분을 n 등분하는 방법파일 다운로드1
11574정성태7/2/201818352Math: 27. GeoGebra 기하 (4) - 선분을 n 배 늘이는 방법파일 다운로드1
11573정성태7/2/201817727Math: 26. GeoGebra 기하 (3) - 평행선
11572정성태7/1/201817079.NET Framework: 783. C# 컴파일러가 허용하지 않는 (유효한) 코드를 컴파일해 테스트하는 방법
11571정성태7/1/201818530.NET Framework: 782. C# - JIRA에 등록된 Project의 Version 항목 추가하는 방법파일 다운로드1
11570정성태7/1/201818676Math: 25. GeoGebra 기하 (2) - 임의의 선분과 특정 점을 지나는 수직선파일 다운로드1
11569정성태7/1/201817876Math: 24. GeoGebra 기하 (1) - 수직 이등분선파일 다운로드1
11568정성태7/1/201830114Math: 23. GeoGebra 기하 - 컴퍼스와 자를 이용한 작도 프로그램 [1]
11567정성태6/28/201819387.NET Framework: 781. C# - OpenCvSharp 사용 시 포인터를 이용한 속도 향상파일 다운로드1
11566정성태6/28/201825105.NET Framework: 780. C# - JIRA REST API 사용 정리 (1) Basic 인증 [4]파일 다운로드1
11565정성태6/28/201821953.NET Framework: 779. C# 7.3에서 enum을 boxing 없이 int로 변환하기 - 세 번째 이야기파일 다운로드1
11564정성태6/27/201820387.NET Framework: 778. (Unity가 사용하는) 모노 런타임의 __makeref 오류
11563정성태6/27/201819232개발 환경 구성: 386. .NET Framework Native compiler 프리뷰 버전 사용법 [2]
11562정성태6/26/201818711개발 환경 구성: 385. 레지스트리에 등록된 원격지 스크립트 COM 객체 실행 방법
11561정성태6/26/201830084.NET Framework: 777. UI 요소의 접근은 반드시 그 UI를 만든 스레드에서! [8]파일 다운로드1
11560정성태6/25/201821342.NET Framework: 776. C# 7.3 - 초기화 식에서 변수 사용 가능(expression variables in initializers)파일 다운로드1
11559정성태6/25/201828503개발 환경 구성: 384. 영문 설정의 Windows 10 명령행 창(cmd.exe)의 한글 지원 [6]
11558정성태6/24/201822090.NET Framework: 775. C# 7.3 - unmanaged(blittable) 제네릭 제약파일 다운로드1
11557정성태6/22/201821615.NET Framework: 774. C# - blittable 타입이란?파일 다운로드1
11556정성태6/19/201828506.NET Framework: 773. C# 7.3 - 구조체의 고정 크기를 갖는 fixed 배열 필드에 대한 직접 접근 가능 [1]파일 다운로드1
... 91  92  93  [94]  95  96  97  98  99  100  101  102  103  104  105  ...