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

비밀번호

댓글 작성자
 




... 106  [107]  108  109  110  111  112  113  114  115  116  117  118  119  120  ...
NoWriterDateCnt.TitleFile(s)
11280정성태8/24/201719970디버깅 기술: 94. windbg - 풀 덤프에 포함된 모든 모듈을 파일로 저장하는 방법
11279정성태8/23/201731328.NET Framework: 676. C# Thread가 Running 상태인지 아는 방법
11278정성태8/23/201719818오류 유형: 417. TFS - Warning - Unable to refresh ... because you have a pending edit. [1]
11277정성태8/23/201720963오류 유형: 416. msbuild - error MSB4062: The "TransformXml" task could not be loaded from the assembly
11276정성태8/23/201724809.NET Framework: 675. C# - (파일) 확장자와 연결된 실행 파일 경로 찾기 [2]파일 다운로드1
11275정성태8/23/201734171개발 환경 구성: 323. Visual Studio 설치 없이 빌드 환경 구성 - Visual Studio 2017용 Build Tools [1]
11274정성태8/22/201720839.NET Framework: 674. Thread 타입의 Suspend/Resume/Join 사용 관련 예외 처리
11273정성태8/22/201722314오류 유형: 415. 윈도우 업데이트 에러 Error 0x80070643
11272정성태8/21/201726123VS.NET IDE: 120. 비주얼 스튜디오 2017 버전 15.3.1 - C# 7.1 공개 [2]
11271정성태8/19/201720027VS.NET IDE: 119. Visual Studio 2017에서 .NET Core 2.0 프로젝트 환경 구성하는 방법
11270정성태8/17/201732230.NET Framework: 673. C#에서 enum을 boxing 없이 int로 변환하기 [2]
11269정성태8/17/201722363디버깅 기술: 93. windbg - 풀 덤프에서 .NET 스레드의 상태를 알아내는 방법
11268정성태8/14/201722396디버깅 기술: 92. windbg - C# Monitor Lock을 획득하고 있는 스레드 찾는 방법
11267정성태8/10/201726071.NET Framework: 672. 모노 개발 환경
11266정성태8/10/201726212.NET Framework: 671. C# 6.0 이상의 소스 코드를 Visual Studio 설치 없이 명령행에서 컴파일하는 방법
11265정성태8/10/201754121기타: 66. 도서: 시작하세요! C# 7.1 프로그래밍: 기본 문법부터 실전 예제까지 [11]
11264정성태8/9/201725523오류 유형: 414. UWP app을 signtool.exe로 서명 시 0x8007000b 오류 발생
11263정성태8/9/201720834오류 유형: 413. The C# project "..." is targeting ".NETFramework, Version=v4.0", which is not installed on this machine. [3]
11262정성태8/5/201719419오류 유형: 412. windbg - SOS does not support the current target architecture. [3]
11261정성태8/4/201721856디버깅 기술: 91. windbg - 풀 덤프 파일로부터 강력한 이름의 어셈블리 추출 후 사용하는 방법
11260정성태8/3/201720342.NET Framework: 670. C# - 실행 파일로부터 공개키를 추출하는 방법
11259정성태8/2/201718904.NET Framework: 669. 지연 서명된 어셈블리를 sn.exe -Vr 등록 없이 사용하는 방법
11258정성태8/1/201720212.NET Framework: 668. 지연 서명된 DLL과 서명된 DLL의 차이점파일 다운로드1
11257정성태7/31/201719849.NET Framework: 667. bypassTrustedAppStrongNames 옵션 설명파일 다운로드1
11256정성태7/25/201721759디버깅 기술: 90. windbg의 lm 명령으로 보이지 않는 .NET 4.0 ClassLibrary를 명시적으로 로드하는 방법 [1]
11255정성태7/18/201724279디버깅 기술: 89. Win32 Debug CRT Heap Internals의 0xBAADF00D 표시 재현 [1]파일 다운로드3
... 106  [107]  108  109  110  111  112  113  114  115  116  117  118  119  120  ...