본문 바로가기
기타

C#에서 DLL 내보내기 직접 호출

by leo21c 2011. 8. 11.

출처:http://msdn.microsoft.com/ko-kr/library/aa288468(v=vs.71).aspx

C#에서 DLL 내보내기 직접 호출

DLL 내보내기에서 구현하는 메서드를 선언하려면 다음 작업을 수행합니다.

  • C# 키워드staticextern을 사용하여 메서드를 선언합니다.
  • 메서드에DllImport특성을 추가합니다.DllImport특성을 사용하면 메서드를 포함하는 DLL의 이름을 지정할 수 있습니다. C# 메서드 이름은 내보낸 메서드와 같게 지정하는 것이 일반적이지만 C# 메서드에 다른 이름을 사용할 수도 있습니다.
  • 선택적으로, 메서드의 매개 변수 및 반환 값에 대한 사용자 지정 마샬링 정보를 지정하여 .NET Framework 기본 마샬링을 재정의합니다.

예제 1

이 예제에서는DllImport특성을 사용하여msvcrt.dll에서puts를 호출함으로써 메시지를 출력하는 방법을 보여 줍니다.

 
// PInvokeTest.cs
using System;
using System.Runtime.InteropServices;
class PlatformInvokeTest
{    
	[DllImport("msvcrt.dll")]    
	public static extern int puts(string c);    
	[DllImport("msvcrt.dll")]    
	internal static extern int _flushall();   
	public static void Main()    
	{       
		puts("Test");        
		_flushall();   
	}
}

출력

 
Test

코드 설명

앞의 예제에서는 관리되지 않는 DLL에 구현되는 C# 메서드를 선언하는 데 필요한 최소한의 작업을 보여 줍니다.PlatformInvokeTest.puts메서드는staticextern한정자로 선언되며,msvcrt.dll에서puts라는 기본 이름을 사용하여 구현하도록 컴파일러에게 알려 주는DllImport특성을 갖습니다. C# 메서드에 대해putstring등의 다른 이름을 사용하려면 다음과 같이DllImport특성에서EntryPoint옵션을 사용해야 합니다.

 
[DllImport("msvcrt.dll", EntryPoint="puts")]

DllImport특성의 구문에 대한 자세한 내용은DllImportAttribute 클래스를 참조하십시오.

관리되지 않는 메서드에 대한 매개 변수의 기본 마샬링 및 사용자 지정 마샬링

C# 코드에서 관리되지 않는 함수를 호출할 때는 공용 언어 런타임에서 매개 변수 및 반환 값을 마샬링해야 합니다.

모든 .NET Framework 형식에는 관리되지 않는 기본 형식이 있으며 공용 언어 런타임에서는 이 형식을 사용하여 관리되는 함수 호출에서 관리되지 않는 함수 호출로 데이터를 마샬링합니다. 예를 들어, C# 문자열 값에 대한 기본 마샬링 형식은 LPTSTR 형식(TCHAR 문자 버퍼에 대한 포인터)입니다. 관리되지 않는 함수의 C# 선언에MarshalAs특성을 사용하면 기본 마샬링을 재정의할 수 있습니다.

예제 2

이 예제에서는DllImport특성을 사용하여 문자열을 출력합니다. 또한MarshalAs특성을 사용하여 함수 매개 변수의 기본 마샬링을 재정의하는 방법도 보여 줍니다.

 
// Marshal.cs
using System;
using System.Runtime.InteropServices;
class PlatformInvokeTest
{    
    [DllImport("msvcrt.dll")]    
    public static extern int puts([MarshalAs(UnmanagedType.LPStr)] string m);
    [DllImport("msvcrt.dll")]    
    internal static extern int _flushall();    
    
    public static void Main()     
    {        
    	puts("Hello World!");
        _flushall();
    }
}

출력

이 예제를 실행하면

 
Hello World!

라는 문자열이 콘솔에 표시됩니다.

코드 설명

앞의 예제에서는puts함수에 대한 매개 변수의 기본 마샬링이 LPTSTR 기본값에서 LPSTR로 재정의되었습니다.

MarshalAs특성은 메서드 매개 변수, 메서드 반환 값과, 구조체 및 클래스의 필드에 배치할 수 있습니다. 메서드 반환 값 마샬링을 설정하려면 반환 특성 위치가 재정의된 메서드의 특성 블록에MarshalAs특성을 배치합니다. 예를 들어,puts메서드의 반환 값에 대한 마샬링을 명시적으로 설정하려면 다음과 같이 합니다.

 
...[DllImport("msvcrt.dll")] 
[return : MarshalAs(UnmanagedType.I4)]
public static extern int puts
( ...

MarshalAs특성의 구문에 대한 자세한 내용은MarshalAsAttribute 클래스를 참조하십시오.

참고InOut특성을 사용하여 관리되지 않는 메서드의 매개 변수에 주석을 달 수 있습니다.InOut특성은 MIDL 소스 파일의inout한정자와 유사한 역할을 합니다.Out특성은 C# 매개 변수 한정자인out과 다르다는 점에 주의하십시오.InOut특성에 대한 자세한 내용은InAttribute 클래스OutAttribute 클래스를 참조하십시오.

사용자 정의 구조체에 대해 사용자 지정 마샬링 지정

관리되지 않는 함수로 전달되거나 이 함수에서 전달되는 구조체 및 클래스의 필드에 대해 사용자 지정 마샬링 특성을 지정할 수 있습니다. 구조체 또는 클래스의 필드에MarshalAs특성을 추가하면 됩니다. 또한 구조체의 레이아웃을 설정하고 문자열 멤버의 기본 마샬링을 제어하고(선택적) 기본 압축 크기를 설정하려면StructLayout특성을 사용해야 합니다.

예제 3

이 예제에서는 구조체에 대한 사용자 지정 마샬링 특성을 지정하는 방법을 보여 줍니다.

다음의 C 구조체를 참조하십시오.

 
typedef struct tagLOGFONT 
{    
    LONG lfHeight;    
    LONG lfWidth;
    LONG lfEscapement;
    LONG lfOrientation;
    LONG lfWeight; 
    BYTE lfItalic;
    BYTE lfUnderline; 
    BYTE lfStrikeOut;
    BYTE lfCharSet;
    BYTE lfOutPrecision;
    BYTE lfClipPrecision;
    BYTE lfQuality;
    BYTE lfPitchAndFamily;
    TCHAR lfFaceName[LF_FACESIZE]; 
} LOGFONT;

C#에서는 다음과 같이StructLayoutMarshalAs특성을 사용하여 앞의 구조체를 기술할 수 있습니다.

 
// logfont.cs
// compile with: /target:moduleusing System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public class LOGFONT 
{     
    public const int LF_FACESIZE = 32;
    public int lfHeight;
    public int lfWidth;
    public int lfEscapement; 
    public int lfOrientation;
    public int lfWeight;  
    public byte lfItalic;  
    public byte lfUnderline; 
    public byte lfStrikeOut;  
    public byte lfCharSet;    
    public byte lfOutPrecision;
    public byte lfClipPrecision;
    public byte lfQuality;   
    public byte lfPitchAndFamily;  
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=LF_FACESIZE)] 
    public string lfFaceName; 
}

StructLayout특성의 구문에 대한 자세한 내용은StructLayoutAttribute 클래스를 참조하십시오.

이제 이 구조체를 다음과 같이 C# 코드에서 사용할 수 있습니다.

 
// pinvoke.cs// compile with: /addmodule:logfont.netmodule
using System;
using System.Runtime.InteropServices; 
class PlatformInvokeTest
{         
	[DllImport("gdi32.dll", CharSet=CharSet.Auto)]      
	public static extern IntPtr CreateFontIndirect(            
		[In, MarshalAs(UnmanagedType.LPStruct)] LOGFONT lplf   // characteristics         
    );       
	[DllImport("gdi32.dll")]      
	public static extern bool DeleteObject(IntPtr handle); 

	public static void Main()
	{            
		LOGFONT lf = new LOGFONT();          
		lf.lfHeight = 9;    
		lf.lfFaceName = "Arial"; 
		IntPtr handle = CreateFontIndirect(lf);    
		if (IntPtr.Zero == handle)          
		{              
			Console.WriteLine("Can't creates a logical font."); 
		}          
		else         
		{              
			if (IntPtr.Size == 4)  
				Console.WriteLine("{0:X}", handle.ToInt32());  
			else                      
				Console.WriteLine("{0:X}", handle.ToInt64());   
			
            // Delete the logical font created.      
			if (!DeleteObject(handle))              
				Console.WriteLine("Can't delete the logical font");       
		}    
	}
}

샘플 실행

 
C30A0AE5

코드 설명

앞의 예제에서CreateFontIndirect메서드는 LOGFONT 형식의 매개 변수를 사용하고 있습니다.MarshalAsIn특성은 매개 변수를 한정하는 데 사용됩니다. 프로그램에서는 메서드에서 반환된 숫자 값을 16진법 대문자 문자열로 표시합니다.

콜백 메서드 등록

관리되지 않는 함수를 호출하는 관리되는 콜백을 등록하려면 동일한 인수 목록을 사용하여 대리자를 선언하고 PInvoke를 통해 대리자 인스턴스를 전달합니다. 관리되지 않는 쪽에서 콜백은 함수 포인터로 나타납니다. PInvoke 및 콜백에 대한 자세한 내용은플랫폼 호출을 참조하십시오.

예를 들어, 콜백을 인수로 요구하는 다음과 같은 관리되지 않는 함수MyFunction을 참조하십시오.

 
typedef void (__stdcall *PFN_MYCALLBACK)();int __stdcall MyFunction(PFN_ MYCALLBACK callback);

관리되는 코드에서MyFunction을 호출하려면 함수 선언에DllImport를 추가하고 매개 변수 또는 반환 값을 선택적으로 마샬링할 수도 있습니다.

 
public delegate void MyCallback();
[DllImport("MYDLL.DLL")]
public static extern void MyFunction(MyCallback callback);

또한 대리자 인스턴스의 수명이 관리되지 않는 코드의 수명 이상이어야 합니다. 그렇지 않으면 대리자가 가비지가 수집된 후에는 사용할 수 없게 됩니다.