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

비밀번호

댓글 작성자
 




... 181  182  183  184  185  186  187  188  189  190  191  [192]  193  194  195  ...
NoWriterDateCnt.TitleFile(s)
142정성태4/14/200517866    답변글 VS.NET IDE: 36.1. 메모리 구성에 대한 추가 내용
137정성태3/31/200522183.NET Framework: 35. XP SP2 팝업 뚫은 소스
135정성태3/31/200520926VS.NET IDE: 26. SQL Server 2000구성이 실패
134정성태3/31/200518135COM 개체 관련: 16. Microsoft.XMLHTTP 개체에서 Microsoft.XMLDOM 개체를 전송할 때 charset 지정 문제? [2]
128정성태3/30/200516480.NET Framework: 34. VC++에서 Managed 타입의 메서드에 BSTR을 넘기는 경우의 오류(!)
129정성태3/30/200518594    답변글 .NET Framework: 34.1. 위의 질문에 대한 답변으로 나온 것입니다.
130정성태3/30/200515911        답변글 .NET Framework: 34.2. 다시... 제가 질문한 내용입니다. ^^
131정성태3/30/200516421            답변글 .NET Framework: 34.3. 다시... 정봉겸님이 하신... 명확한 답변입니다.
126정성태3/26/200516275.NET Framework: 33. Proxy 환경에서의 Smart Client 업데이트 문제 [1]
133정성태3/31/200517418    답변글 .NET Framework: 33.1. [추가]: Proxy 환경에서의 Smart Client 업데이트 문제 [2]
125정성태3/26/200516390VC++: 15. VC++ Keyword
124정성태3/25/200516910.NET Framework: 32. 네트워크 공유 없이 상대 컴퓨터에 프로그램 설치
119정성태3/21/200516487.NET Framework: 31. 소스세이프 오류현상: 웹 프로젝트를 열수 없습니다.
120정성태3/21/200517805    답변글 .NET Framework: 31.1. 소스세이프 오류현상: PDB 파일이 잠기는 문제
121정성태3/21/200517859    답변글 .NET Framework: 31.2. 소스세이프 오류현상: VS.NET 2003 IDE 와 연동되는 소스세이프 버전 문제
122정성태3/21/200516543    답변글 .NET Framework: 31.3. 소스세이프 관련 사이트
160정성태11/14/200519434    답변글 VS.NET IDE: 31.4. [추가]: 웹 애플리케이션 로드시 "_1"을 붙여서 묻는 경우. [1]
196이문석12/23/200516255        답변글 .NET Framework: 31.8. [답변]: [추가]: 웹 애플리케이션 로드시 "_1" 을 붙여서 묻는 경우.
167정성태10/10/200515826    답변글 .NET Framework: 31.5. [추가]: 삭제한 웹 가상 디렉터리에 대해 동일한 이름으로 웹 공유를 설정할 때 - 이미 있다고 오류발생
190정성태12/11/200515122    답변글 VC++: 31.6. ASP.NET 소스세이프 오류현상: 다른 사람이 체크아웃 한 것을 또 다른 사람이 체크아웃 가능!
191정성태12/11/200517571    답변글 VC++: 31.7. 소스 세이프 사용 시, 특정 프로젝트의 빌드 체크가 솔루션 로드할 때마다 해제되는 경우
118정성태3/30/200623359VC++: 14. TCP through HTTP tunneling: 기업 내 Proxy 서버 제한에서 벗어나는 방법 [2]
117정성태3/19/200524480.NET Framework: 30. Process.Start에서의 인자 길이 제한 [4]
116정성태3/14/200516989.NET Framework: 29. [.NET WebService] 자동생성되는 WSDL 을 막는 방법.
115정성태3/13/200517529VS.NET IDE: 25. [IIS 서버] ODBC 로그 남기기 [1]
195정성태12/21/200516765    답변글 VC++: 25.1. ODBC 로그를 못 남길 때의 오류 화면
... 181  182  183  184  185  186  187  188  189  190  191  [192]  193  194  195  ...