C#
P/Invoke 개요

C# / .NET 에서 Native DLL (Unmanaged DLL)에 있는 함수를 호출하는 것을 Platform Invoke 혹은 줄여서 P/Invoke 라 부른다. 예를 들어, C#에서 윈도우즈 운영체제의 Win32 API 를 호출한다거나, 기존 C/C++로 작성된 Native DLL의 함수를 호출할 필요가 있을 때, P/Invoke 방식을 사용한다.

아래 예제는 Win32 API중 Kernel32.dll 에 있는 Beep()라는 함수를 C#에서 사용하는 코드이다. Beep(주파수, 길이) 함수는 주파수와 길이를 입력받아 간단한 소리를 발생시키는 함수이다.

P/Invoke를 사용하기 위해서는 먼저 System.Runtime.InteropServices 네임스페이스에 있는 DllImport를 사용하여 어떤 DLL(여기서는 kernel32.dll)에서 함수를 가져올지를 지정하고, 해당 함수의 원형을 C# 메서드 원형으로 정의해 준다. 이때 C# 메서드는 static extern 으로 지정한다.

Win32 에 있는 함수들을 C# 메서드 원형으로 변환하기 위해서는 pinvoke.net 사이트를 이용하면 편리하다. 이 사이트는 Win32 API 함수들을 C#에서 어떻게 표현하는지를 쉽게 알려준다.


예제

using System;
using System.Runtime.InteropServices; // for DllImport

namespace PInvokeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Beep(100, 1000);
            Beep(200, 1000);
            Beep(300, 1000);
            Console.ReadKey();
        }

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



C#에서 C/C++ 함수를 호출하는 방법

C#에서 Win32 API를 호출하는 방법과 비슷하게, 자신이 만든 (혹은 3rd Party가 만든) C/C++ Native DLL 안의 함수를 호출할 수 있다. 이를 위해 먼저 (1) C/C++ 코드에서 해당 함수를 export 해야 하는데, 아래 예제에서 extern C 블럭이 해당 함수를 외부에서 사용할 수 있도록 export 하는 코드이다. 이러한 C/C++ 코드를 컴파일하여 DLL 로 만들어 두고, (2) 다음 C# 프로젝트에서 이 C/C++ DLL을 복사한 후, DllImport를 정의하여 사용하면 된다. (주: 통상 C/C++ DLL은 C# 실행파일과 같은 폴더에 두어야 한다.)

아래 예제는 ConvertFahrenheitToCelsius() 라는 C++ 함수를 C#에서 호출하여 사용하는 샘플이다.


예제

// C++ 코드 (MyLib.dll)

#include "stdafx.h"

extern "C" {
    __declspec(dllexport) float WINAPI ConvertFahrenheitToCelsius(float f);    
}

float WINAPI ConvertFahrenheitToCelsius(float f)
{
    float c = (f - 32) * 5.0 / 9.0;
    return c;
}

// C# 코드 
using System;
using System.Runtime.InteropServices;

namespace UsePInvoke
{
    class Program
    {
        static void Main(string[] args)
        {
            float f = 82.50F;
            float c = ConvertFahrenheitToCelsius(f);

            Console.WriteLine("{0} C", c);
        }

        [DllImport("MyLib.dll")]
        public static extern float ConvertFahrenheitToCelsius(float f);
    }
}




Cross Platform에서 P/Invoke 사용

C#의 P/Invoke 방식은 런타임시에 동적으로 함수를 호출하는 방식이다. 즉, 컴파일시 해당 함수를 갖는 DLL을 발견하지 못할지라도 컴파일은 성공적으로 진행된다. P/Invoke의 외부 함수는 프로그램 실행시 해당 함수를 갖는 DLL을 동적으로 로딩하기 때문에, 여러 Cross Platform 환경에서 다른 DLL 함수를 호출하는 것이 가능하다.

아래 코드는 윈도우즈 OS와 리눅스, Mac OSX 등에서 다른 P/Invoke 함수를 호출하는 예제인데, 윈도우즈의 경우 user32.dll의 MessageBox() 함수를 사용하고, Unix/리눅스 계열의 경우 libc 라이브러리의 printf() 함수를 사용하게 된다. 코드 아래 그림은 (Docker를 사용한) 리눅스에서 Mono를 사용하여 C# 프로그램을 컴파일하고 실행한 예인데, 이때 리눅스에서는 libc 라이브러리를 사용하게 된다.


예제

using System;
using System.Runtime.InteropServices;

namespace PInvokeCross
{
    class Program
    {
        static void Main(string[] args)
        {
            OperatingSystem os = Environment.OSVersion;
            if (os.Platform == PlatformID.Win32Windows || os.Platform == PlatformID.Win32NT)
            {
                MessageBox(IntPtr.Zero, "Hello", "Windows", 0);
            }
            else  // Unix, Linux, Mac
            {
                printf("Hello\n"); 
            }
        }

        [DllImport("user32", CharSet = CharSet.Auto)]
        public static extern int MessageBox(IntPtr hwnd, string text, string caption, int option);

        [DllImport("libc")]
        public static extern void printf(string text);
    }
}






본 웹사이트는 광고를 포함하고 있습니다. 광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.