DllImport ๋Œ€์‹  LibraryImport ์‚ฌ์šฉ

.NET 7์— LibraryImport ํŠน์„ฑ์ด ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด DllImport ํŠน์„ฑ์œผ๋กœ ๋‹ค์Œ์˜ DllImport ์žฅ์‹์„

    [DllImport("Kernel32.dll")]
    public extern static bool Beep(uint freq, uint duration);

๋‹ค์Œ์˜ LibraryImport ํŠน์„ฑ์œผ๋กœ ์žฅ์‹ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    [LibraryImport("Kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static partial bool Beep(uint freq, uint duration);

LibraryImport๊ฐ€ ์ƒˆ๋กœ ์ƒ๊ฒจ๋‚œ ์ด์œ ๋Š” DllImport๊ฐ€ ๋งˆ์ƒฌ๋ง์„ ๋Ÿฐํƒ€์ž„์—์„œ ์ˆ˜ํ–‰ํ•ด์„œ IL ์ฝ”๋“œ๋ฅผ emitํ•œ๋‹ค๊ณ  ํ•˜๋Š”๋ฐ NativeAOT ๋“ฑ ๋™์ ์œผ๋กœ IL ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์—†๋Š” ํ™˜๊ฒฝ์—์„œ ์“ธ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

LibraryImport๋Š” ์†Œ์Šค ์ƒ์„ฑ๊ธฐ ๊ธฐ๋Šฅ์„ ์ด์šฉํ•ด ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ๋งˆ์ƒฌ๋ง ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•ฉ๋‹ˆ๋‹ค.

obj/Debug/net7.0/generated/Microsoft.Interop.LibraryImportGenerator/Microsoft.Interop.LibraryImportGenerator/LibraryImports.g.cs
(csproj์— EmitCompilerGeneratedFiles ์„ค์ •์„ true๋กœ ์ค˜์•ผ ํ•ด๋‹น ๊ฒฝ๋กœ์— ํŒŒ์ผ๋กœ ์ƒ์„ฑ๋˜์–ด ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

// <auto-generated/>
static unsafe partial class ExternDll
{
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "7.0.6.42610")]
    [System.Runtime.CompilerServices.SkipLocalsInitAttribute]
    public static partial bool Beep(uint freq, uint duration)
    {
        bool __retVal;
        int __retVal_native;
        {
            __retVal_native = __PInvoke(freq, duration);
        }

        // Unmarshal - Convert native data to managed data.
        __retVal = __retVal_native != 0;
        return __retVal;
        // Local P/Invoke
        [System.Runtime.InteropServices.DllImportAttribute("Kernel32.dll", EntryPoint = "Beep", ExactSpelling = true)]
        static extern unsafe int __PInvoke(uint freq, uint duration);
    }
}

๋ญ”๊ฐ€ ์ฝ”๋“œ๊ฐ€ ์‚ฝ์ž…๋๊ณ  ์‹ฌ์ง€์–ด ์ตœ์ข… ๋‹ค์‹œ DllImport๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ตฐ์š”? ๊ฒฐ๊ตญ์— LibraryImport ํŠน์„ฑ์œผ๋กœ ์ธํ•ด ์ƒ์„ฑ๋œ ์ฝ”๋“œ๋Š” ๋งˆ์ƒฌ๋ง ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•˜๋Š” ์—ญํ• ๋งŒ ๋”ฑ ํ•˜๊ณ  ๊ธฐ์กด DllImport๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

        // Unmarshal - Convert native data to managed data.
        __retVal = __retVal_native != 0;

์ •๋ง๋กœ ๋งˆ์ƒฌ๋ง ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๋Š”์ง€ ๋‹ค๋ฅธ ์˜ˆ์ œ๋กœ ํ™•์ธํ•ด ๋ดค์Šต๋‹ˆ๋‹ค. LZ4 DLL์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด LibraryImport ํŠน์„ฑ์„ ์‚ฌ์šฉํ•ด

static partial class ExternDll
{
    [LibraryImport("msys-lz4-1.dll")]
    public static partial int LZ4_compressBound(int inputSize);

    [LibraryImport("msys-lz4-1.dll")]
    public static partial int LZ4_compress_default(byte[] src, byte[] dst, int srcSize, int dstCapacity);

    [LibraryImport("msys-lz4-1.dll")]
    public static partial int LZ4_decompress_safe(byte[] src, byte[] dst, int compressedSize, int dstCapacity);
}

byte[] ์€ ๊ด€๋ฆฌ ํž™ ๋ฉ”๋ชจ๋ฆฌ์— ์œ„์น˜ํ•˜๋ฏ€๋กœ ๋น„๊ด€๋ฆฌ ๋ชจ๋“ˆ์— ๊ทธ๋Œ€๋กœ ์ „๋‹ฌํ•˜๋ฉด ์•ˆ๋ฉ๋‹ˆ๋‹ค. LibraryImport ์žฅ์‹์— ์˜ํ•ด ์ƒ์„ฑ๋œ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด,

static unsafe partial class ExternDll
{
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Interop.LibraryImportGenerator", "7.0.6.42610")]
    [System.Runtime.CompilerServices.SkipLocalsInitAttribute]
    public static partial int LZ4_compress_default(byte[] src, byte[] dst, int srcSize, int dstCapacity)
    {
        int __retVal;
        // Pin - Pin data in preparation for calling the P/Invoke.
        fixed (void* __src_native = &global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<byte, byte>.ManagedToUnmanagedIn.GetPinnableReference(src))
        fixed (void* __dst_native = &global::System.Runtime.InteropServices.Marshalling.ArrayMarshaller<byte, byte>.ManagedToUnmanagedIn.GetPinnableReference(dst))
        {
            __retVal = __PInvoke((byte*)__src_native, (byte*)__dst_native, srcSize, dstCapacity);
        }

        return __retVal;
        // Local P/Invoke
        [System.Runtime.InteropServices.DllImportAttribute("msys-lz4-1.dll", EntryPoint = "LZ4_compress_default", ExactSpelling = true)]
        static extern unsafe int __PInvoke(byte* src, byte* dst, int srcSize, int dstCapacity);
    }
}

์œ„์™€ ๊ฐ™์ด ๋งˆ์ƒฌ๋ง์„ ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์‚ฝ์ž…๋จ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•˜๊ฒŒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“  ํ›„,

using System.Runtime.InteropServices;
using System.Text;

// TestBeep()
TestLZ4Compress();


static void TestBeep()
{
    for (uint i = 0; i < 100; i++)
    {
        ExternDll.Beep(i * 100, 50);
    }
}

static void TestLZ4Compress()
{
    var sourceText = """
        ๋‹ท๋„ท ์ฝ”์–ด๋Š” ASP.NET Core ์›น ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ, ๋ช…๋ น์ค„ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฐ ์œ ๋‹ˆ๋ฒ„์…œ ์œˆ๋„์šฐ ํ”Œ๋žซํผ ์•ฑ, ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ๋“ฑ ์ด 4๊ฐ€์ง€๋กœ ํฌ๋กœ์Šค ํ”Œ๋žซํผ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ง€์›ํ•œ๋‹ค. ๋‹ค๋งŒ, ํ˜„์žฌ ์œˆ๋„์šฐ์˜ ๋ฐ์Šคํฌํ†ฑ ์†Œํ”„ํŠธ์›จ์–ด์šฉ ํ‘œ์ค€ GUI๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ์œˆ๋„์šฐ ํผ ๋˜๋Š” WPF๋Š” ๊ตฌํ˜„๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค.[3][4] ์ด์— ๋งˆ์ดํฌ๋กœ์†Œํ”„ํŠธ๋Š” ๋‹ท๋„ท ์ฝ”์–ด3์—์„œ ์œˆ๋„์šฐ ํผ, WPF์„ ์œ ๋‹ˆ๋ฒ„์…œ ์œˆ๋„์šฐ ํ”Œ๋žซํผ ์•ฑ๊ณผ ํ•จ๊ป˜ ์ง€์›ํ•  ๋ฐฉ์นจ์ด๋‹ค.[5] ์—ฌ๊ธฐ์— ๋‹ท๋„ท ์ฝ”์–ด๋Š” NuGet ํŒจํ‚ค์ง€์˜ ์‚ฌ์šฉ์„ ์ง€์›ํ•œ๋‹ค. ์œˆ๋„์šฐ ๋ฒ„์ „์˜ ๋‹ท๋„ท ํ”„๋ ˆ์ž„์›Œํฌ์™€๋Š” ๋‹ฌ๋ฆฌ ์—…๋ฐ์ดํŠธ๋Š” ์œˆ๋„์šฐ ์—…๋ฐ์ดํŠธ์—์„œ๋งŒ ์ฃผ๋กœ ์ด๋ฃจ์–ด์ง€๋งŒ, ๋‹ท๋„ท ์ฝ”์–ด๋Š” ์—…๋ฐ์ดํŠธ๋ฅผ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์ž ํ˜•์‹์œผ๋กœ ์—…๋ฐ์ดํŠธ๋ฅผ ํ•˜๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.[3][4]

        ๋‹ท๋„ท ์ฝ”์–ด๋Š” ๊ณตํ†ต ์–ธ์–ด ๋Ÿฐํƒ€์ž„(CLR)์˜ ์™„์ „ํ•œ ๋Ÿฐํƒ€์ž„ํ™˜๊ฒฝ์„ ๊ตฌํ˜„์‹œํ‚จ CoreCLR๋กœ ๊ตฌ์„ฑ๋˜์–ด์žˆ๋‹ค. ์ด ๋Ÿฐํƒ€์ž„์€ ๋‹ท๋„ท ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ๊ฐ€์ƒ ์ปดํ“จํ„ฐ๋กœ ๋งˆ์ดํฌ๋กœ์†Œํ”„ํŠธ์—์„œ ์‹œ์ž‘ํ•˜์—ฌ, RyuJIT๋ผ๋Š” JIT ์ปดํŒŒ์ผ์„ ํฌํ•จํ•œ๋‹ค.[6] ๋˜ํ•œ, AOT ์ปดํŒŒ์ผ ๋œ ์›์‹œ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ํ†ตํ•ฉ๋˜๋„๋ก ์ตœ์ ํ™” ๋œ ๋‹ท๋„ท ์›์‹œ ๋Ÿฐํƒ€์ž„์ธ CoreRT๋ฅผ ํฌํ•จํ•œ๋‹ค.

        ๋‹ท๋„ท ์ฝ”์–ด๋Š” ๋‹ท๋„ท ํ”„๋ ˆ์ž„์›Œํฌ์˜ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ผ๋ถ€ ํฌํฌ์ธ CoreFX๋„ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉฐ,[7] ๋‹ท๋„ท ์ฝ”์–ด์˜ API์˜ ์ผ๋ถ€๋ถ„์€ ๋‹ท๋„ท ํ”„๋ ˆ์ž„์›Œํฌ์˜ API๊ณผ ๋™์ผํ•œ ๋ถ€๋ถ„๋„ ์žˆ์œผ๋‚˜, ๋‹ท๋„ท ํ”„๋ ˆ์ž„์›Œํฌ์™€๋Š” ์ „ํ˜€ ๋‹ค๋ฅธ ์ „์šฉ API์„ ์‚ฌ์šฉํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‹ท๋„ท ์ฝ”์–ด์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋ณ€ํ˜•์‹œ์ผœ UWP์˜ ๊ฐœ๋ฐœ์— ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.[8]

        ๋‹ท๋„ท ์ฝ”์–ด์˜ ๋ช…๋ น ์ค„ ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์šด์˜ ์ฒด์ œ์— ๋Œ€ํ•œ ์‹คํ–‰ ์ง„์ž… ์ ์„ ์ œ๊ณตํ•˜๊ณ  ์ปดํŒŒ์ผ ๋ฐ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์™€ ๊ฐ™์€ ๊ฐœ๋ฐœ์ž ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.[9]
        """;

    Console.WriteLine(sourceText);
    Console.WriteLine("------");

    var source = Encoding.Default.GetBytes(sourceText);

    var maxDstSize = ExternDll.LZ4_compressBound(source.Length);
    //Console.WriteLine(maxDstSize);
    var target = new byte[maxDstSize];

    var compressedSize = ExternDll.LZ4_compress_default(source, target, source.Length, maxDstSize);
    Console.WriteLine($"์••์ถ• (LZ4 ๊ธฐ๋ณธ)");
    Console.WriteLine($"์†Œ์Šค ํฌ๊ธฐ = {source.Length}");
    Console.WriteLine($"์••์ถ•๋œ ํฌ๊ธฐ = {compressedSize}");
    Console.WriteLine($"์••์ถ•(%) = {(1 - (float)compressedSize / source.Length) * 100} %");
    Console.WriteLine("------");

    var decompressedTarget = new byte[source.Length];
    var decompressedSize = ExternDll.LZ4_decompress_safe(target, decompressedTarget, compressedSize, decompressedTarget.Length);

    Console.WriteLine($"์••์ถ• ํ•ด์ œ (LZ4 ๊ธฐ๋ณธ)");
    Console.WriteLine($"์••์ถ• ํ•ด์ œ ํฌ๊ธฐ = {decompressedSize}");

    Console.WriteLine("------");

    var decompressedText = Encoding.Default.GetString(decompressedTarget);
    Console.WriteLine(decompressedText);
}

NativeAOT๋กœ ๋‹ค์Œ์˜ ํ”„๋กœ์ ํŠธ ์„ค์ •์œผ๋กœ ๊ฒŒ์‹œํ•˜์—ฌ,

...
	  <PublishAot>true</PublishAot>
	  <InvariantGlobalization>true</InvariantGlobalization>
	  <UseSystemResourceKeys>true</UseSystemResourceKeys>

	  <IlcOptimizationPreference>Size</IlcOptimizationPreference>
	  <IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>

	  <DebuggerSupport>false</DebuggerSupport>
	  <EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
	  <EventSourceSupport>false</EventSourceSupport>
	  <HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
	  <MetadataUpdaterSupport>false</MetadataUpdaterSupport>

	  <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
	  <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>     
...

2M ์‚ฌ์ด์ฆˆ .NET ๋Ÿฐํƒ€์ž„ ์˜์กด์„ฑ ์—†๋Š” ์‹คํ–‰ํŒŒ์ผ๊ณผ 500K ์‚ฌ์ด์ฆˆ์˜ LZ4 DLL์„ ๋ณผ ์ˆ˜ ์žˆ๊ณ ,

$ .\No19.LibraryImportAttributeTest.exe
๋‹ท๋„ท ์ฝ”์–ด๋Š” ASP.NET Core ์›น ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ, ๋ช…๋ น์ค„ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ...
------
์••์ถ• (LZ4 ๊ธฐ๋ณธ)
์†Œ์Šค ํฌ๊ธฐ = 1857
์••์ถ•๋œ ํฌ๊ธฐ = 1216
์••์ถ•(%) = 34.51804 %
------
์••์ถ• ํ•ด์ œ (LZ4 ๊ธฐ๋ณธ)
์••์ถ• ํ•ด์ œ ํฌ๊ธฐ = 1857
------
๋‹ท๋„ท ์ฝ”์–ด๋Š” ASP.NET Core ์›น ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ, ๋ช…๋ น์ค„ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ...

์ž˜ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

https://github.com/dimohy/csharp-check/tree/main/No19.LibraryImportAttributeTest

8๊ฐœ์˜ ์ข‹์•„์š”