파일 경로를 입력하면 해당 파일의 MD5 Hash를 스트링으로 반환해준다.


스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Imports System.Security.Cryptography
Public Function GetMD5Hash(ByVal sFilePath As String) As String
     Dim RD As FileStream = New FileStream(sFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)
     RD = New FileStream(sFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)
     Dim md5 As MD5CryptoServiceProvider = New MD5CryptoServiceProvider
     md5.ComputeHash(RD)
     RD.Close()
     Dim hash As Byte() = md5.Hash
     Dim SB As StringBuilder = New StringBuilder
     Dim HB As Byte
     For Each HB In hash
          SB.Append(String.Format("{0:X1}", HB))
     Next
     Return SB.ToString
End Function


사용예제

1
2
Dim sHash as String
sHash = GetMD5Hash("C:\Test.txt")


컨트롤 이름으로 컨트롤 객체를 반환 받을 수 있다.


소스코드

1
2
3
4
5
6
7
8
9
10
11
12
13
Public Module ControlSelectorExtension
    <System.Runtime.CompilerServices.Extension()>
    Public Function FindByName(Of T)(targetClass As Object, name As String) As T
        Dim info As Reflection.PropertyInfo = targetClass.GetType().GetProperty(name, Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)
        Return DirectCast(info.GetValue(targetClass, Nothing), T)
    End Function
    <System.Runtime.CompilerServices.Extension()>
    Public Function FindByName(Of T)(name As String, targetClass As Object) As T
        Dim info As Reflection.PropertyInfo = targetClass.GetType().GetProperty(name, Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)
        Return DirectCast(info.GetValue(targetClass, Nothing), T)
    End Function
End Module

출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=45&MAEULNO=18&no=509&page=1

'프로그래밍 > VB.net' 카테고리의 다른 글

VB.net - 파일의 MD5 Hash 값 추출  (0) 2015.05.11
VB.net - 문자열로 Form 객체 반환 받기  (0) 2015.05.11

문자열로 해당 문자열을 name으로 가지고 있는 FORM 객체를 반환해준다.


소스코드

1
2
3
4
5
6
7
8
9
Public Class ObjectFinder
    Public Shared Function GetFormByName(ByVal formName As String) As Form
            Dim assemblyName As String = _
            [Assembly].GetEntryAssembly().GetName.Name.Replace(" ", "_")
            Dim myType As Type = _
                Type.GetType(assemblyName & "." & formName)
            Return CType(Activator.CreateInstance(myType), Form)
    End Function
End Class


사용예제 (Form1의 객체를 받아오기)

1
2
dim frmTEST as Form
frmTEST = ObjectFinder.GetFormByName("Form1")



'프로그래밍 > VB.net' 카테고리의 다른 글

VB.net - 파일의 MD5 Hash 값 추출  (0) 2015.05.11
VB.net - 컨트롤 이름으로 컨트롤 접근  (0) 2015.05.11

출처 : MSDN '97

DLLs for Beginners(초심자를 위한 DLLs)
Debabrata Sarma
Microsoft Developer Support
November 1996
Contents(목차, 내용)
Introduction
Basic Concepts
Advantages of Using a DLL
Any Disadvantages?
Implementation of DLLs
Applications Sharing Data in DLL
Mutual Imports, DLLs and EXE
DLL Base Address Conflict
Conclusion
References
Appendix A
Introduction(소개)
이 기사의 목적은 동적 링크 라이브러리(DLL, Dynamic Link Library)의 개념과 Microsoft Windows 응용프로그램을 위한 DLL을 작성하는 메커니즘을 소개하는 것이다. 이 논의는 version 4.0 이나 그 이후의 버전, 그리고 version 2.x 대의 Microsoft Visual C++에 국한되어 있다. 부록 A(Appendix A)는 16비트 DLL 구현과 32비트 DLL로의 포팅(porting, 역자주 : 호환성 있도록 프로그램을 변형하는 것)에 대한 정보를 포함한다.
이 기사에 사용된 대부분의 레퍼런스는 마이크로 소프트 개발자 네트웤 라이브러리(Microsoft Developer Network Library, MSDN Library)에서 볼 수 있다. 이 기사는 MSDN 라이브러리나 http://www.microsoft.com/kb/default.asp의 Knowledge Base, 아무 곳에서나 "Q"로 검색되는 텍스트 중에 열거되어 있다.
Basic Concepts(기본개념)
윈도우 프로그램은 실행 가능한 파일인데, 일반적으로 하나 이상의 윈도우를 생성하고 사용자의 입력을 받아들이기 위한 메시지 루프를 사용한다. 동적 링크 라이브러리는 일반적으로 직접 실행되지 않으며, 그것들은 메시지를 받지 않는다. 그것들은 프로그램이나 다른 DLL에 의해 호출되어 특정 함수나 계산을 수행하기 위한 함수들을 포함하고 있는 개별 파일이다.
Static Linking(정적 링크)
C, Pascal, FORTRAN와 같은 고 수준의 프로그래밍 언어들에서는, 실행가능한 파일을 생성하기 위해서 응용프로그램의 소스 코드를 컴파일하고 다양한 라이브러리에 링크한다. 이러한 라이브러리들은 메모리를 할당하거나 숫자의 제곱근을 계산하는 것과 같은 일반적인 작업을 수행하기 위해서 호출되는 미리 컴파일된(precompiled) 함수들의 개체(object) 파일을 포함한다. 이러한 라이브러리 함수들이 응용프로그램에 링크될 때, 그것들은 응용프로그램 실행파일에 영구적으로 속하는(permanent) 부분이 된다. 그 라이브러리 함수에 대한 모든 호출은 링크 시간에 결정된다(resolved) - 즉 그 이름은 정적 링크(static linking)이다.
Dynamic Linking(동적 링크)
동적 링크는 실행시간에 응용프로그램이 라이브러리를 링크하는 메커니즘을 제공한다. 그 라이브러리들은 자체의 실행 파일에 존재하며, 응용프로그램의 실행 파일에 정적 링크처럼 복사되지 않는다. 이들 라이브러리들은 동적 링크 라이브러리(DLL)라고 불리우는데, 그것들이 로드되고 실행될 때가 그것이 링크되었을 때가 아니라 응용프로그램에 링크되었을 때임을 강조한 것이다. 응용프로그램이 DLL을 사용할 때, 운영 체제는 DLL을 메모리로 로드하고, DLL 안의 함수들에 대한 참조를 결정한다. 그래서 그것들은 응용프로그램에 의해 호출될 수 있으며, 더 이상 필요하지 않을 때 DLL은 언로드될 수 있는 것이다. 이 동적 링크 메커니즘은 응용프로그램이나 운영체제에 의해서 명시적으로 수행될 수 있다.
Differences Between Static-Link Libraries and Windows DLLs(정적 링크 라이브러리와 윈도우 DLL의 차이)
Windows DLLs 은 정적 링크 라이브러리와 상당히 다르다. 두 가지의 기본 차이는 다음과 같다 :
.LIB 파일에 존재하는 정적 링크라이브러리는 기본적으로 개체 파일의 집합인 반면에, 개별적인 실행가능 파일에 존재하는 동적 링크 라이브러리는 그것들이 필요할 때 Windows에 의해 메모리에 로드된다.
정적 링크 라이브러리를 사용하는 각 응용프로그램은 자신만의 라이브러리 복사본을 소유한다. 그러나 Windows는 같은 DLL에 대한 하나의 복사본을 동시에 사용하는 다중 응용프로그램을 지원한다.
정적 링크 라이브러리는 단지 코드와 데이터만을 포함한다. 왜냐하면 그것들은 개체 파일의 집합으로써 저장되기 때문이다. 반면에 Windows DLL은 코드, 데이터, 그리고 비트맵, 아이콘, 커서와 같은 리소스들을 포함한다. 왜냐하면 그것들은 실행 가능한 프로그램 파일로서 저장되기 때문이다.
정적 링크 라이브러리는 반드시 응용프로그램의 데이터 공간을 사용해야만 한다. 반면에 DLL은 프로세스의 주소 공간에 매핑된 자신만의 데이터 주소 공간을 가질 수 있다.
 Differences Between Windows-Based Applications and DLLs(윈도우 기반 응용프로그램과 DLL 의 차이)
이 섹션은 먼저 Windows 프로그래밍의 몇몇 중요 용어를 정의하고, 그들 용어 뒤에 있는 개념을 설명한다. 그리고 마지막으로 응용프로그램과 DLL 사이의 구체적인 차이를 설명한다.
Definitions(정의)
Let's review some basic terms that are thrown around quite a bit in programming for Windows.
executable은 .EXE나 .DLL 확장자를 가진 파일로 응용프로그램이나 DLL을 위한  실행 가능한 코드 그리고/또는 리소스를 포함한다.
 application은 윈도우 기반 프로그램으로 .EXE 파일에 존재한다.
DLL은 윈도우 동적 링크 라이브러리로 .DLL 파일에 존재한다. System DLL은 .EXE 확장자를 가지는데, 예를 들어 USER.EXE, GDI.EXE, KRNL286.EXE, KRNL386.EXE이다. 다양한 장치 드라이버는 .DRV 확장자를 가지는데, 예를 들어 MOUSE.DRV, KEYBOARD.DRV 이다. 단지 .DLL 확장자를 가진 동적 링크 라이브러리들만 윈도우 운영체제에 의해 자동으로 로드될 것이다. 만약 파일이 다른 확장자를 가진다면, 프로그램은 반드시 LoadLibrary 함수를 사용해 명시적으로 모듈을 로드해야만 한다.
더 진행하기 전에, DLL과 응용프로그램이 메모리에 어떻게 매핑되는지에 대해 어느 정도 이해할 필요가 있다.
Win32를 위한 DLL에 대한 중요한 변화 중 하나는 DLL의 코드와 데이터가 존재하는 메모리 상의 위치이다. Win32에서 각 응용프로그램은 자체의 32비트 선형 주소 공간의 문맥(context)안에서 실행된다. 이러한 방식으로 모든 응용프로그램은 자신만의 사적(private) 주소 공간을 가지는데, 이는 이 프로세스 안의 코드에 의해서만 어드레스(addressed, 역자주 : 주소를 배정하는 것?) 수 있다. (Win32에서 각 응용프로그램의 사적 주소 공간은 그 응용프로그램에 대한 process로서 참조된다.) 응용프로그램의 코드와 데이터, 리소스, 그리고 동적 메모리, 이 모두는 응용프로그램의 프로세스 안에 존재한다. 더우기 자신의 프로세스 바깥에 존재하는 데이터나 주소를 응용프로그램에 어드레스하는 것은 불가능하다. 왜냐하면 DLL이 로드될 때, 그것은 어떻게든 DLL을 로드한 응용프로그램의 프로세스 안에 존재해야만 하기 때문이다; 만약 하나 이상의 응용프로그램이 같은 DLL을 로드하면, 그것은 반드시 각 응용프로그램이 프로세스 안에 존재해야만 한다.
그래서 위의 요구를 만족시키기 위해 - 즉 재입력 가능하고(reentrant)(동시에 하나 이상의 스레드로부터 접근 가능한) 메모리에 물리적으로 로드된 단 하나의 DLL 복사본만을 가지기 위해 - Win32는 메모리 매핑(mapping)을 사용한다. 메모리 매핑을 통해 Win32는 DLL을 한 번만 전역 힙으로 로드하고, 그것을 로드하는 각 응용프로그램의 주소 공간으로 DLL의 주소 범위를 매핑한다. Figure 1은 DLL이 두개의 다른 응용프로그램에 동시에 매핑되는 방식을 표현한다(Process1과 Process2)

Figure 1. A DLL mapped to the address space of two different applications simultaneously
DLL이 물리적으로 전역 힙에 한 번만 로드되었음에 주목하라, 이제 양쪽 응용프로그램들은 DLL의 코드와 리소스를 공유한다.
Microsoft Windows 버전 3.1 이전에서는, DLL이 공통적인 데이터의 집합을 가졌는데, 그것들은 DLL을 로드한 모든 응용프로그램 사이에서 공유되었따. 이 행위는 DLL의 소스 코드 작성에 있어 매우 어려운 장애중 하나이다. Win32는 DLL이 그것을 로드하는 응용프로그램 각각에 대해서 하나의 개별적인 데이터 집합을 가지도록 허용함으로써 이 장애를 극복했다. 이들 데이터 집합 또한 전역 힙으로부터 할당되지만, 그것들은 응용프로그램 사이에서 공유되지 않는다. Figure 1 은 각 DLL 데이터 집합을 표현하고, 그것이 속한 응용프로그램에 매핑되는 방식을 설명한다. 이 새로운 기능은 DLL에 대한 코드 작성을 더욱 용이하게 만들었는데, 이는 DLL이 같은 전역 변수에 접근하는 두 개의 응용프로그램에 대해서 보호받을 필요가 없어졌기 때문이다.
다른 응용프로그램 사이에서 공유된 DLL에 대해 코드를 작성하는 것은 쉬워진 반면, 아직까지 잠재적인 경쟁(conflict)이 존재한다. DLL에서 어떻게 전역변수가 사용되는가 하는 것이다. DLL 데이터의 범위는 DLL을 로드한  응용프로그램의 전체 프로세스이다. 그래서 다중 스레드를 가지고 있는 응용프로그램의 각 스레드는 DLL 데이터 집합의 모든 전역 변수들에 접근할 수 있다. 각 스레드가 서로에 대해 독립적으로 실행되기 때문에, 만약 같은 전역 변수를 사용하는 하나 이상의 스레드가 있다면 잠재적인 경쟁이 존재한다. 응용프로그램에서 다중 스레드에 의해서 접근될 수 있는 DLL의 전역변수를 사용할 때 조심하고, 세마포어, 뮤텍스(상호 배제), 대기 이벤트, 임계영역같은 것을 필요할 때 사용하라.
DLL은 그것을 로드하는 응용프로그램의 프로세스로 매핑되기 때문에, DLL의 코드가 실행될 때 누가 현재 프로세스를 소유하고 있는지에 대한 혼란이 존재할 수 있다. DLL의 코드로부터 GetModuleHandle 함수를 모듈 이름 인자를 null로 해서 호출하면, DLL이 아니라 응용프로그램에 대한 모듈 핸들을 반환한다. DLL의 리소스를 접근할 때 나중의 참조를 위해 정적 변수에서 DLL의 모듈 핸들의 복사본을 유지하는 것은 좋은 생각이다(모듈 핸들은 DllEntryPoint 함수로 넘겨진다). 핸들을 다시 획득하는 다른 유일한 방법은 GetModuleHandle 함수를 DLL의 파일이름과 경로를 지정하는 유효한 문자열과 함께 사용하는 것이다.
How applications and DLLs differ(응용프로그램과 DLL은 어떻게 다른가)
DLL과 응용프로그램이 모두 실행 가능한 프로그램 모듈이기는 하지만, 그것들은 몇 가지 점에서 차이가 있다. 최종 사용자(end user)에게, 가장 명백한 차이는 DLL은 Program Manager나 다른 shell 프로그램으로부터 직접 실행되는 프로그램이 아니라는 것이다. 시스템의 관점에서 보면, 응용프로그램과 DLL에는 두 가지 기본적인 차이가 있다:
응용프로그램은 시스템 상에서 동시에 실행되는 자신의 다중 인스턴스를 가질 수 있다. 반면에 DLL은 단지 하나의 인스턴스만을 가진다. 응용프로그램의 각 인스턴스는 자신만의 자동적인 데이터 공간을 가지지만, 응용프로그램의 모든 인스턴스들은 실행가능한 코드와 리소스의 단일 복사본을 공유한다. 다시 말하면 DLL이 몇 번이나 로드되는지는 대수로운 문제가 아니며, 그것은 정확히 하나의 인스턴스만을 가진다. 32비트 운영체제에 대해, DLL에 대한 각 로딩은 자신만의 인스턴스를 가질 것이다; 결국 다중 프로세스들 사이에서의 데이터 세그먼트 공유는 직접적이지도 않고32비트 DLL에 대해 맡겨져 있지도 않다(원문 : therefore, sharing of data segment among multiple processes is not straightforward and not recommended for 32-bit DLLs).
응용 프로그램은 "자신만의" 것을 가질 수 있지만, DLL은 그렇지 않다. 단지 프로세스들만이 "소유권"을 가질 수 있으며, 단지 응용프로그램 인스턴스들 만이 프로세스를 가질 수 있다. 32비트 운영 체제에서, DLL은 그들 자신을 프로세스에 연결하며, 메모리는 단지 개별 프로세스에 의해서만 소유될 수 있다. DLL은 그 자체로는 실체가 없다.
DLL은 응용프로그램과 분리된 프로그램 모듈이다. Disk상에서 그것들은 자신만의 특별한 실행 가능한 파일에 존재하는데, 그 파일은 코드와 데이터, 그리고 비트맵이나 커서와 같은 (읽기 전용) 리소스들을 포함할 수 있다. 프로세스가 DLL을 로드할 때, 시스템은 DLL에 대한 코드와 데이터를 프로세스의 주소 공간에 매핑한다. 32비트 운영 체제에서, DLL은 운영체제의 일부가 아니게 되었다. 대신에 그것들은 DLL을 로드하는 프로세스의 일부가 되었다. DLL의 함수에 의해 만들어지는 모든 메모리 할당 호출은 메모리가 프로세스의 주소 공간으로부터 할당되도록 한다; 어떠한 프로세스도 할당된 이 메모리에 접근할 수 없다. DLL에 의해서 할당된 전역, 그리고 정적 변수 또한 DLL의 다중 매핑 사이에서 공유되지 않는다.
프로세스가 DLL을 처음으로 로드할 때, DLL에 대한 사용 카운트가 1이 된다. 만약 그 프로세스가 같은 DLL을 두 번째로 로드하기 위해 LoadLibrary를 호출하면, 그 프로세스와 관련된 라이브러리에 대한 사용 카운트는 2가 된다. 만약 다른 프로세스가 다른 프로세스에 의해 사용되고 있는 DLL을 로드하기 위해서 LoadLibrary를 호출하면, 시스템은 그 DLL에 대한 코드와 데이터를 호출한 프로세스의 주소 공간에 매핑하고, (이 프로세스와 관련한) DLL 사용 카운트를 1로 만든다. FreeLibrary 함수는 DLL의 사용 카운트를 감소시킨다; 만약 사용 카운트가 0에 도달하면, DLL은 프로세스의 주소 공간으로부터 매핑 해제된다.
Advantages of Using a DLL(DLL 사용의 이점)
DLL은 그것들을 사용하는 응용프로그램과는 독립적으로 컴파일되고 링크된다; 그것들은 응용프로그램이 재컴파일되거나 재링크될 것을 요청하지 않고도 갱신될 수 있다.
만약 몇몇 응용프로그램들이 시스템과 함께 작동하고, 모두 공통 DLL을 공유한다면, 전체 시스템은 강화된 버전의 공통 DLL을 대체함으로써 성능향상을 볼 수 있다. DLL 중 하나에서 버그를 수정하는 것은 그것을 사용하는 모든 응용프로그램에서의 버그를 수정한다. 비슷하게 속도 증가나 새로운 기능은 그 DLL을 사용하는 모든 응용프로그램에 좋은 영향을 끼친다.
DLL은 다중 응용프로그램 사이에서의 공통적인 코드와 리소스들에 대한 단일 카피를 공유함으로써 요구되는 메모리와 디스크 공간을 절약할 수 있다; 절약된 메모리와 디스크 공간은 DLL을 사용하는 응용프로그램이 많아지도록 했다. 만약 다중 응용프로그램이 정적 링크 라이브러리를 사용한다면, 디스크 상에 라이브러리의 동일한 복사본이 여러개 존재할 것이다. 만약 응용프로그램이 모두 유사하게 작동되었다면, 메모리에는 동일한 복사본이 여러개 존재했을 것이다. 응용프로그램의 개수가 늘어날 수록, 동일한 복사본의 개수도 즈가할 것이다. 이러한 동일한 복사본은 중복된 것이며 공간을 낭비한다. 만약 정적 링크 라이브러리 대신에 DLL이 사용되었다면, 코드와 리소스에 대한 하나의 복사본만이 필요할 것이며, 얼마나 많은 응용프로그램이 그것을 사용하는지는 관계없다.
Any Disadvantages?(단점도 있을까?)
정적 링크 라이브러리 대신에 DLL을 사용하는 것의 주된 단점은  DLL은 개발하기가 더 어려울 수도 있다는 것이다.
종종 DLL을 사용하는 것은 실제적으로 메모리와 디스크 공간 사용을 증가시킨다. 특별히 단지 하나의 응용프로그램만 DLL을 사용하는 경우에 그렇다. 이것은 응용프로그램과 DLL이 C run-time 라이브러리 함수의 경우와 같이 모두 같은 정적 링크 라이브러리 함수를 사용할 때 일어난다. 만약 DLL과 응용프로그램이 그것에 링크된 정적 링크 라이브러리 함수의 복사본을 각각 가지고 있다면, 메모리와 디스크에 라이브러리 함수의 복사본이 두 개 존재할 것이며, 공간을 낭비하게 될 것이다. 이 문제를 해결하기 위해서는, 함수의 분포와 함수의 호출이 반드시 적절하게 모듈화되거나 C run-time 라이브러리의 DLL버전을 사용해야만 한다.
Figure 2에서, 우리는 두 개의 응용프로그램과 정적 링크 라이브러리 함수를 사용하는 DLL을 가지고 있다, strlen; 부가적으로 두 응용프로그램은 동적 링크 라이브러리 함수 Foo를 사용하는데, 이 또한 strlen을 사용한다. 실행 가능한 그 세 모듈 다 자신의 strlen 코드의 복사본(도합 세 개의 정적 라이브러리)을 가지고 있으며, 반면에 Foo는 두 개의 응용프로그램에 의해 재사용된다.

Figure 2. Two applications and a DLL using a static-link library function and a dynamic-link library function
Implementation of DLLs(DLL의 구현)
기본 개념과 함께, 이제는 DLL을 작성하는 작업을 할 차례이다. DLL은 운영체제에 의해 명시적으로 로드되거나 응용프로그램에 의해 명시적으로 로드될 수 있다.
묵시적인 동적 링크는 운영 체제에 의해 로드되는 시간에 수행된다. 운영 체제가 응용프로그램이나 DLL을 로드할 때, 먼저 의존적인(dependent)DLL을 로드한다(즉, 응용프로그램이나 DLL에 의해 사용되는 DLL)
명시적인 동적 링크는 LoadLibrary 함수를 사용하여 운영체제가 DLL을 로드하도록 하는 호출을 생성함으로써 응용프로그램이나 DLL에 의해 실행시간에 수행된다.
Implementing a 32-bit DLL(32비트 DLL 구현)
먼저 정적 링크를 생성하고 그것을 DLL로 바꾸어 보자. 만약 정적 링크를 전에 구현해 보지 않았다면, 이제 연습을 할 수 있을 것이다. 또한 구현 파일(.C 나 .CPP)에서 원형을 선언하는 대신에 함수 원형에 대한 헤더 파일을 작성하는 버릇을 기르자. Third-party 업체들은 그들의 정적 혹은 동적 링크와 함께 헤더 파일을 제공하여 그들의 라이브러리를 사용하는 응용프로그램에 삽입할 수 있게 한다.
Building a Static Library(정적 라이브러리 빌드)
우리가 생성하고자 하는 정적 라이브러리 예제는 전역 변수, 지역변수, 함수를 가지고 응용프로그램과 응용프로그램의 지역 함수, 그리고 콜백 함수에 의해 호출되도록 한다. 응용프로그램은 Console 응용프로그램이다. 이 라이브러리는 지역 함수 원형(prototype), 그리고 다른 공유된 함수 및 변수들을 위해서 하나의 헤더 파일을 사용한다(this header file may also be used as is by the application). 이 헤더파일과 소스 파일은 아래에 나와 있다. 이 파일을 당신이 원하는 디렉토리에 생성하라.
Visual Workbench(Visual C++ 화면)에서, 파일 메뉴로부터 New를 선택하라. Project를 선택하고, project type "Static Library"의 project name을 설정하라(일반적으로 이름과 같은 디렉토리가 생성된다). Create를 선택하라.
--------------------------------------------------------------------
// Library header  file  lib1.h
int libglobal;
int callappfunc(int * ptrlib);
void libfuncA(char * buffer);
int  libfuncB(int const * mainint);
--------------------------------------------------------------------
// 이것은 라이브러리 지역 함수의 헤더 파일이다 -- lib2.h
 int liblocalA(int x, int y);
--------------------------------------------------------------------
// 이것은 라이브러리 소스 파일 lib1.c이다
#include <stdio.h>
#include "lib1.h"
#include "lib2.h"
libglobal = 20;
void libfuncA(char * buffer)
 {
    int i =0;
    printf("Printing in Library \n%s\n\n", buffer);
    printf("Value of i: %d\n", i);
 }
                               
int libfuncB(int const * mainint)
 {   
    int returnvalue;
    int  localint;
    int libvalue = 3;
    localint = *mainint;
    returnvalue = liblocalA(localint, libglobal);
   
    libvalue = callappfunc(&libvalue);
    printf("Print return value from app callback :%d\n", libvalue);
    return returnvalue;
 
 }
int liblocalA(int x, int y)
 {
       return (x + y);
 }
--------------------------------------------------------------------
이제 디버그 버전으로 라이브러리를 생성한다. 하위 디렉토리인 Debug가 프로젝트 디렉토리에 생성되어 있음을 발견할 수 있을 것이다(만약 Visual C++ 버전 2.x라면 Windebug 서브디렉토리가 사용된다). 그 디렉토리에서 당신은 Libname.lib라는 이름을 가지고 막 생성된 정적 라이브러리를 찾을 수 있을 것이다. libname은 당신이 프로젝트를 위해서 전에 설정한 이름이다. 이 라이브러리는 "Object Library"라고 알려져 있는데, 이 라이브러리는 라이브러리에 포함된 함수의 개체 코드를 포함한다. 만약 당신이 라이브러리 함수와 변수가 그 라이브러리 안에서 어떻게 열거되는지를 알고자 한다면, Dumpbin 유틸리티를 DOS 명령줄에서 다음과 같이 사용할 수 있다 :
DUMPBIN  /ALL  libname.lib
위 명령어의 출력은 당신에게 함수의 이름이 어떻게 꾸며졌는지(listed or decorated) 알려주는데, 이는 LNK2001 에러(Unresolved external)같은 링커 문제를 해결하는데 유용하다. 또한 사용되는 기본적인 라이브러리의 정보를 얻을 수도 있다. 이는 LNK2005 에러(Symbol alread defined in another library)를 해결하는데 유용하다. 위의 명령어를 막 생성된 라이브러리에 실행하고 사용된 기본 라이브러리가 무엇인지, 어떻게 함수와 변수가 열거되어 있는지 알아보라. 다른 DUMPBIN swithces는 /IMPORTS 와 /EXPORTS이다. 당신은 더 많은 정보를 위해서 다음 knowledge base 기사들을 참조할 수 있다:
"The /Mx Compiler Options and the LIBC, LIBCMT, MSVCRT Libs" (Q128641)
"Troubleshooting LNK2001 or L2029 Unresolved External Errors" (Q138400)
"How to Link with the Correct C Run-Time (CRT) Library" (Q140584)
"PRB: Error Msg: LNK2001 on _beginthreadex and _endthreadex" (Q126646)
"How to Determine Default Libraries for an .OBJ Object File" (Q143072)
이제 우리는 응용프로그램을 생성할 준비가 되었다. 이 응용프로그램은 방금 생성한 라이브러리를 사용할 것이다. 다음 헤더와 소스 파일이 사용된다. 우리는 같은 라이브러리에 의해 사용된 같은 헤더 파일을 사용하고 있다는 점에 주의하라. 원하는 디렉토리에 소스 파일을 생성하고(라이브러리를 위해 생성한 것과는 다른 디렉토리에다가 생성하라), 이 디렉토리의 Lib1.h 헤더파일을 복사하라. 새로운 "Console Application" 유형의 프로젝트를 생성하라. 이 프로젝트에 막 생성한 소스 파일을 추가하라. 프로젝트에 라이브러리를 삽입하기 위해서, 프로젝트에서 Insert -Files into Project menu 나 Build-Settings-Link-Input-Object/Library Modules 에디트 박스에서 추가하라. 라이브러리의 완전한 경로를 제공하라. 디버그 모드로 프로젝트를 빌드한다.
--------------------------------------------------------------------
// This is the application source file -- appsource.c
#include "lib1.h"
#include <stdio.h>
int mainglobal;
int callappfunc(int * ptrlib)
 {
    int m =  *ptrlib;
    return m = m * m - libglobal;
  
 }
 void main(void)
  {
      int libreturn;
    char  buf[] = "Let us print this line in library";
    printf ("We are starting in the application\n");
    libfuncA(buf);
    printf("Let us increment the library global and print in app :%d\n",
               ++libglobal);
   
    mainglobal = 10;
    libreturn = libfuncB( &mainglobal);
    printf("Print the library returned value :%d\n", libreturn);
    printf("Demonstration of library use is complete\n");
  }
--------------------------------------------------------------------
프로젝트가 빌드되면, 그것을 디버그하고 라이브러리와 응용프로그램의 모든 함수를 통해 단계를 밟아가는지 본다. 코드 안에서 breakpoint를 설정하고, F5(Debug Go) 키를 누르는 대신에, 이런 작은 응용프로그램에서는 F11(Step Into) 키(만약 Visual C++ 버전 2.x라면 F8키)를 사용한다. 이는 우리를 메인 함수의 중괄호 부분으로 인도한다. 그 지점으로부터 우리는 F11을 누르고 라이브러리 함수를 포함한 모든 코드를 통해 단계를 밟아갈 수 있다. ALT + TAB을 사용해 콘솔의 명령줄 윈도우와 Visual C Workbench 소스 디버그 윈도우 사이를 이동함으로써 출력을 확인할 수 있다. 일단 메인 함수의 마지막 중괄호 부분에 도착하면, F5 누르거나 debug 메뉴로부터 Stop Debutg(혹은 ALT + F5)를 눌러 디버깅을 종료할 수 있다. breakpoint는 소스 파일의 코드 줄에 커서를 위치하고 툴바의 손 모양 심볼을 누름으로써 설정될 수 있다 - 빨간 점은 breakpoint를 지정한 라인에 나타난다. F5를 누르면 이 breakpoint에서 프로그램의 실행을 멈추게 될 것이다.
응용프로그램은 릴리즈 버전의 라이브러리를 사용해 생성될 수 있다는 점에 주목하라. 릴리즈 모드로 라이브러리를 생성하고, 이 라이브러리를 디버깅모드로 생성된 응용프로그램에 추가함으로써 이를 확인할 수 있다. 만약 지금 코드를 디버그한다면, 라이브러리가 호출되는 시점을 찾을 수 있을 수 있지만, 코드를 통해 단계를 밟아들어갈 수는 없다; 대신 라이브러리 함수는 적절히 실행되며, 코드에서 기대되는 행동을 얻을 수 있을 것이다. 비록 libglobal 변수 선언을 위해 extern 키워드를 사용하지 않았지만, 모듈의 바깥에 선언되는 변수를 위해 extern을 사용하는 프로그래밍 습관은 좋은 것이다.
C에서 작성된 정적 라이브러리를 구현하였고, C 응용프로그램으로 사용했다. 같은 정적 라이브러리는 C++응용프로그램에서도 이용 가능해야 한다. 그러나 만약 지금 당신의 Appsource.c 파일을 재명명해 appsource.cpp로 바꾸고 이 파일을 appsource.c 파일을 제거한후 프로젝트에 추가하고, 빌드한다고 하면, 다음과 링커 에러를 만나게 될 것이다. C++은 변수와 함수의 이름을 꾸미기(decorates) 때문이다.
appsource.obj : error LNK2001: unresolved external symbol "?libfuncA@@YAXPAD@Z (void  __cdecl                         libfuncA(char *))"
appsource.obj : error LNK2001: unresolved external symbol "?libfuncB@@YAHPBH@Z (int  __cdecl libfuncB(int const *))"
우리는 다음과 같은 방식으로 C에 의해 사용된 함수와 변수를 사용하여 이름 꾸미기(decoration)이 유지되도록 컴파일러에 명령을 수행할 수 있다.
extern "C"  functionname /variablename ;
또한 몇 개의 선언들을 함께 그룹화할 수도 있다. 수정된 헤더 파일은 다음과 같다. 응용프로그램을 생성하고 테스트할 수 있다.
--------------------------------------------------------------------
// This is the header file for the application for C++ -- applib.h
#ifdef __cplusplus
extern "C" {
#endif
extern int libglobal;
int callappfunc(int * ptrlib);
void libfuncA(char * buffer);
int  libfuncB(int const * mainint);
#ifdef __cplusplus
}
#endif
--------------------------------------------------------------------
Building a DLL(DLL 빌드)
Implicit dynamic linking(묵시적인 동적 링크)
Using the __declspec attribute(__declspec 속성 사용하기)
이제 우리가 생성한 정적 라이브러리를 동적 링크 라이브러리(DLL)로 변환해 보자. dllexport 속성을 사용하여 DLL을 생성하거나 개별 모듈 정의 파일(module-definition-file, .DEF 파일)을 사용할 수 있다. 우리는 두 가지 기법을 모두 논의할 것이다. 먼저 우리는 dlexport 속성을 사용한 DLL 구현을 논의할 것이다. dllimport 속성은 효율성을 개선하고 데이터와 개체를 코드와 같이 임포트할 수 있도록 해 준다. 그것들은 속성이고 키워드가 아니기 때문에, dllexport와 dllimport는 반드시 __declspec 키워드와 함께 결합해 사용해야만 한다. __declspec에 대한 문법은 다음과 같다 :
__declspec(attribute) variable-declaration
예를 들어서 다음 선언은 정수를 정의하는데, 속성으로 dllexport를 사용하였다 : 
__declspec(dllexport) int Sum = 0;
위의 문맥을 사용하여, 이제 정적 라이브러리에서 사용하였던 헤더 파일의 함수 원형 선언을 바꾸어 보자. DLL을 위한 헤더파일은 이제 아래와 같다. 당분간은 DLL의 콜백 함수에 대한 모든 참조들에 대해 언급할 것이다; 즉 응용프로그램에서 DLL로부터의 함수를 호출하지 않을 것이다. lib1.c에서 callappfunc()에 관련된 호출은 결국 제거되었다.
--------------------------------------------------------------------
// Library header  lib1.h
__declspec(dllexport) int libglobal;
__declspec(dllexport) void libfuncA(char * buffer);
__declspec(dllexport) int  libfuncB(int const * mainint);
--------------------------------------------------------------------
이제 새로운 프로젝트를 생성하고, 그것의 이름을 Libname으로 하고, 그것의 유형을 "Dynamic-LinkLibrary"로 한다. 수정된 파일 Lib1.c를 프로젝트에 삽입하고, 디버그 모드로 프로젝트를 빌드한다. 이제 Debug 서브 디렉토리에 두 개의 파일이 생겼는데, Libname.lib와 LIBNAME.DLL이다. Libname은 프로젝트의 이름이다. Libname.lib는 막 생성한 DLL을 위한 "Import Library"로 알려져 있다. 이 파일은 어떠한 코드도 포함하고 있지 않다. 그것은 응용프로그램에 링크하기 위해 링커가 필요로 하는 함수에 대한 모든 참조와 다른 선언들을 포함하고 있다. LIBNAME.DLL 파일에는 함수의 개체 코드가 존재한다.
이제 응용프로그램을 빌드할 준비가 되었따. 정적 라이브러리를 빌드하는 동안 사용했던 같은 소스파일과 헤더파일을 사용할 것이다. 이번에는 헤더파일이 DLL에서 사용되었던 그것과 같지 않다. 왜냐하면 정적 라이브러리에서 사용하였던 헤더파일을 사용하고 있었기 때문이다. 그 차이를 지정하기 위해서 이 헤더파일에 다른 이름을 부여해야 한다; 이 응용프로그램에 대해서는 그것을 Applib.h라고 부른다.
--------------------------------------------------------------
// This is the header file for the application when using the  DLL-- applib.h
int libglobal;
void libfuncA(char * buffer);
int  libfuncB(int const * mainint);
-------------------------------------------------------------------
다시 프로젝트를 Console Project로 생성하고 소스파일 Appsource2.c를 프로젝트에 추가한다. 그 전에 우리는 이 프로젝트에 우리가 막 생성한 임포트 라이브러리를 전체 경로로 추가하는데, Insert -Files into Project menu나 Build-Settings-Link-Input-Object/Library Modules 에디트 박스에서 추가할 수 있다.
-------------------------------------------------------------------//This is the application source file -- appsource2.c
#include "applib.h"
#include <stdio.h>
int mainglobal;
void main(void)
  {
      int libreturn;
    char  buf[] = "Let us print this line in library";
    printf ("We are starting in the application\n");
    libfuncA(buf);
    printf("Let us increment the library global and print in app :%d\n",
               ++libglobal);
   
    mainglobal = 10;
    libreturn = libfuncB( &mainglobal);
    printf("Print the library returned value :%d\n", libreturn);
    printf("Demonstration of library use is complete\n");
  }
--------------------------------------------------------------------
Debugging(디버깅)
프로젝트가 빌드되는 동안에, F11키를 누름으로써 디버깅을 시작할 수 있다. 그러나 이번에는 동적 링크 라이브러리 LIBNAME.DLL이 지정된 경로에서 발견되지 않는다는 것을 지시하는 메시지 박스의 에러메시지를 얻는다. 그리고 그것이 DLL을 찾기 위해 검색한 디렉토리를 열거한다. 이것은 운영 체제가 지정된 디렉토리로부터 DLL을 로드하려고 시도하고 있으며, 그것이 검색한 장소가 현재 디렉토리, 실행 디렉토리, 윈도우 system 디렉토리, windows 디렉토리, 그리고 호나경 변수 경로로 지정된 모든 디렉토리임을 의미한다. 그것은 생성한 DLL을 이러한 디렉토리 중 하나로 옮겨야 함을 의미한다. 16비트 Visual C 환경에서, 이 메시지는 위와 같이 명확하지 않다; " PRB: Error 'Could not load debuggee. File not found (2)' "(Q123616) 기사를 참조하라
LIBNAME.DLL 파일을 응용프로그램 프로젝트의 Debug 서브 디렉토리로 옮겨보자. 이제 F11을 다시 눌러 디버거를 시작하고 응용프로그램의 단계를 밟아가보자. DLL에 있는 함수를 통해 단계를 밟아갈 수 있음을 알 수 있을 것이고, 응용프로그램의 libglobal 변수를 출력할때, 1이라는 값을 얻을려고 하지 않는다면 모든 것이 올바로 작동한다. 이 행동은 정적 라이브러리 예제와 비교했을 때 다른 것이다. 이것은 정적 라이브러리와 응용프로그램은 라이브러리와 응용프로그램에 대한 하나의 데이터 세그먼트를 가지지만, 이제 응용프로그램은 자신만의 데이터 세그먼트를 가지고 DLL도 자신만의 데이터 세그먼트를 가지기 때문이다. 그래서 libglobal은 응용프로그램에 의해 자신의 전역 변수로서 취급되며, 비슷하게 DLL은 그것을 자신의 전역 변수로 취급하고 있는 것이다. 그래서 DLL로부터 응용프로그램으로 libglobal을 익스포트하는 시도는 작동하지 않는 것이다. 그것이 작동하게 하려면, 응용프로그램 헤더 파일에서 libglobal의 선언을 수정할 필요가 있다. 그래서 수정된 응용프로그램 파일은 다음과 같다 :
-------------------------------------------------------------------
// This is the header file for the application when using the  DLL-- applib.h
__declspec( dllimport) int libglobal;
void libfuncA(char * buffer);
int  libfuncB(int const * mainint);
--------------------------------------------------------------------
이것은 어떤 데이터 유형이나 개체가 DLL로부터 익스포트될 필요가 있을 때마다, 응용프로그램은 __declspec(dllimport) 속성을 위와 같이 사용할 필요가 있다는 것을 의미한다. 응용프로그램을 다시 빌드하고, 이전처럼 디버깅을 시도하라. 이번에는 모든 것이 올바로 작동할 것이다.
위의 디버깅 세션은 응용프로그램 프로젝트의 작업공간에서 시작했다. 또한 DLL의 프로젝트 작업공간으로부터 DLL을 디버그하는 것도 가능하다. 이렇게 하기 위해, DLL 함수 안에 breakpoint를 설정하라. Build—Settings—Debug—General—Executable For Debug Session 에디트 박스 메뉴에 이 DLL을 사용하는 응용프로그램의 전체 경로를 입력하라. F5 키를 누름으로써 디버거를 시작하라; 디버거는 DLL에 설정된 breakpoint에서 정지할 것이다. 이제 DLL 코드를 통해 단계를 밟아 들어갈 수 있다. 심지어 응용프로그램이 debug information함께 빌드되지 않더라도 DLL을 디버그할 수 있다. 즉 만약 Word나 Microsoft Excel과 같은 응용프로그램이 사용자가 제공한 DLL(debug information과 함께 빌드된)을 호출한다고 해도, 이러한 기법으로 DLL을 디버그할 수 있다. 응용프로그램의 이름을 입력하는 것을 잊어버렸다면, F5를 눌러라. 응용프로그램의 전체 경로를 입력할 것을 요청하는 메시지 박스가 나타날 것이다.
Use __declspec(dllimport)(__declspec(dllimport)사용하기)
비록 DLL에 의해 익스포트되는 함수에서 __declspec(dllimport)를 사용하지 않았지만, 그렇게 하는 것은 코드의 효율성을 향상시킬 것이다. Microsoft Systems Jounal 에서 1995년 11월에 Matt Pietrek이 기고한 "Under the Hood"라는 기사에서는 이에 대해 세부적인 설명을 하고 있다. 만약 그렇게 한다면, 헤더 파일은 다음과 같이 될 것이다. 한 번 해 보라.
--------------------------------------------------------------------
// This is the header file for the application when building a DLL-- applib.h
__declspec( dllimport) int libglobal;
__declspec( dllimport) void libfuncA(char * buffer);
__declspec( dllimport) int  libfuncB(int const * mainint);
--------------------------------------------------------------------
Use the Same Header File(같은 헤더 파일 사용하라)
응용프로그램뿐만 아니라 DLL에 대해서도 같은 헤더파일을 사용하는 것은 어떨까? DLL 소스 파일의 위쪽에 #define MAKE_A_DLL을 입력함으로써 헤더파일을 다음과 같이 해 보라. 이 헤더파일을 DLL과 응용프로그램에 모두 삽입하라. 이것은 응용프로그램의 소스 파일에서 이 Lib1.h 헤더 파일로 Applib.h 헤더파일을 대체할 수 있음을 의미한다.
--------------------------------------------------------------------
// Common Library header for DLL and application-- lib1.h
#ifdef MAKE_A_DLL
#define LINKDLL __declspec( dllexport)
#else
#define LINKDLL __declspec( dllimport)
#endif
LINKDLL int libglobal;
LINKDLL void libfuncA(char * buffer);
LINKDLL int  libfuncB(int const * mainint);
--------------------------------------------------------------------
Using the DEF File(DEF 파일 사용하기)
우리는 이미 DLL이 모듈 정의 파일(module definition file, .DEF 확장자)을 사용하여 __declspec(dllexport)를 사용하지 않고 구현될 수 있다고 언급하였다. 모듈 정의 파일을 사용하여 DLL을 빌드해야만 하는 특정한 상황이 있다. 그 상황에 대해서는 나중에 언급하겠다. 그러나 클라이언트 프로그램은 DLL로부터 데이터 아이템을 임포트할 때 반드시 __declspec(dllimport)를 사용해야만 한다는 것을 기억하라.
먼저 .DEF 파일을 사용함으로써 DLL을 빌드해 보자. 헤더 파일로부터 declspec 지시자를 제고할 필요가 있다. 그리고 모듈 정의 파일을 추가한다. 이제 두 개의 파일이 다음과 같이 생성되었다 :
--------------------------------------------------------------------
// Header for DLL using .DEF-- lib1.h
int libglobal;
void libfuncA(char * buffer);
int  libfuncB(int const * mainint);
--------------------------------------------------------------------
;lib1.def
; The LIBRARY entry must be same as the name of your DLL, the name of
; our DLL is lib1.dll
LIBRARY lib1
EXPORTS
  
       libglobal 
       libfuncA
       libfuncB
--------------------------------------------------------------------
EXPORTS 섹션은 익스포드된 모든 정의를 열거한다.
LIBRARY 문은 모듈 정의 파일이 DLL에 속한다는 것을 식별한다. 이것은 반드시 파일에서 가장 먼저 기술되어야만 한다. LIBRARY문에서 지정된 이름은 라이브러리가 DLL의 임포트 라이브러리임을 식별한다.
Lib.def 파일을 DLL 프로젝트에 추가하고, DLL을 빌드한다. 다음 헤더 파일이 사용되었다. 그 외에 모든 것은 이전과 같이 남겨둔다. 데이터 멤버를 위해서 declspec 지시자를 사용했음에 주의하라.
The following header file is used. Everything else remains same as before. Note the use of the declspec specifier for the data member (which is a must).
--------------------------------------------------------------------
//  Library (DLL) header for application-- applib.h
__declspec( dllimport) int libglobal;
void libfuncA(char * buffer);
int  libfuncB(int const * mainint);
--------------------------------------------------------------------
응용프로그램을 빌드하고, 실행하고 디버그하라. 원화는 결과를 얻을 것이다.
32비트 버전의 Visual C++ 컴파일러는 Implib 유틸리티를 가지고 있지 않다; 왜냐하면 만약 제공되지 않으면 DLL로부터 임포트 라이브러리를 빌드하는 것은 불가능하기 때문이다. "How to Create 32-bit Import Libraries Without .OBJs or Source" (Q131313)의 기사는 어떤 임포트 라이브러리가 DLL로부터 빌드될 수 있는지에 대한 기법을 기술한다.
DEF 파일은 다른 이름으로 익스포트된 함수를 호출하는데 사용될 수도 있다. 즉 DLL에서의 원래 이름이나 다른 이름으로 함수를 호출할 수 있는 DLL을 구현할 수 있다는 것이다. 예를 들기 전에, 함수 호출에서 사용되는 __cdecl과 __stdcall 호출 규약의 의미에 대해 설명하고자 한다.
__cdecl은 C와 C++ 프로그램에 대한 기본 호출 규약이다. 스택이 호출자에 의해서 제거되기 때문에, 그것은 vararg 함수를 수행할 수 있다. __cdecl 호출 규약은 __stdcall보다 더 큰 실행 생성한다. 왜냐하면 그것은 각 함수 호출에 대해 스택 제거 코드를 요청하기 때문이다. C 이름 꾸미기 방식(C name decoration scheme)은 함수 이름을 언더스코어(_)로 꾸민다. 다음 함수 선언에 대해, 꾸며진 이름은 _Myfunc이다. __cdecl의 사용은 선택적이다.
    int  __cdecl Myfunc (int a, double  b)
__stdcall 호출 규약은 Win32 API 함수를 호출하는데 사용된다. 호출자가 스택을 제거한다. 그래서 컴파일러는 자동으로 변수의 인자(vararg) 함수를 __cdecl로 바꾼다. C 이름 꾸미기 방식은 그 함수 이름을 언더스코어로 꾸미는데 그 뒤에 @#nn가 붙는다. #nn은 인자의 전체 바이트 수(4의 배수, 10진수)이거나 스택에서 인자에 의해 필요로 하는 바이트 수이다. 다음 함수 선언에 대해서 꾸며진 이름은 Myfunc@12이다.
    int  __stdcall Myfunc (int a, double  b)
__cdecl이나 __stdcall 모두, 인자들은 스택으로 오른쪽에서 왼쪽의 순으로 넘겨진다. WINDOWS.H 헤더파일에서, WINAPI, PASCAL, 그리고 CALLBACK은 __stdcall로 정의되어 있다. PASCAL이나 __far __pascal 을 이전에 사용했던 WINAPI를 사용하라(원문 : Use WINAPI where you previously used PASCAL or __far __pascal.)
함수에 대한 PASCAL 이름 꾸미기는 함수 이름을 모두 대문자로 바꾼다. Visual Basic과 같은 언어는 PASCAL 규약을 따른다. 즉 위에 있는 MyFunc 함수는 PASCAL 이름 꾸미기에 의해 MYFUNC가 된다. Visual Basic 프로그램에서 C DLL을 호출하기 위해, DEF 파일을 사용해 다른 (꾸며진) 이름을 가진 같은 함수를 호출할 수 있다. 다음 예제는 DEF파일을 사용함으로써 이것을 달성하는 방식을 보여준다. 이 예제에서 호출하는 응용프로그램은 C 프로그램이다.
Developer Studio에서 Dynamic Link Library로 해서 Pascaldll이라는 프로젝트를 생성하라. .CPP를 추가하고, .DEF를 추가한다. 빌드했을 때, 이 프로젝트는 PASCALDLL.DLL이라는 DLL을 생성할 것이다. 임포트 라이브러리는 Pascaldll.lib이다.
--------------------------------------------------------------------
// DLL source file pascal.cpp
#include"pascal.h"
double __stdcall MyFunc(int a, double b)
{
    return a*b;
}
int CdeclFunc(int  a)
{
    return  2*a;
}
--------------------------------------------------------------------
//Header file  pascal.h
extern "C" double __stdcall MyFunc(int a, double b);
extern "C" int  CdeclFunc(int a);   //By default this is a __cdecl convetion
--------------------------------------------------------------------
; DEF file for the  DLL
EXPORTS
   MYFUNC =_MyFunc@12 ; Alias for function call as MYFUNC for pascal calls
   _MyFunc@12                  ;  To call as MyFunc in C in __stdcall
  CdeclFunc                    ;   Called from C program in __cdecl
  CMyFunc = CdeclFunc          ;  Calling CdeclFunc with different name
--------------------------------------------------------------------
이 DLL을 빌드한 후에, DUMPBIN /EXPORTS 유틸리티를 사용하면, 다음과 같이 익스포트된 함수를 확인할 수 있을 것이다.
    CMyFunc   
             CdeclFunc
             MYFUNC
             _MyFunc@12
이제 Pascalmain이라는 console 응용프로그램을 빌드하고, .CPP파일을 프로젝트에 삽입하고, 위에서 빌드된 DLL을 획득하기 위해서 Pascaldll.lib 임포트 라이브러리를 삽입하자.
--------------------------------------------------------------------
// Application .CPP file pascalmain.cpp
#include "pascal.h"
#include "pascal2.h"
#include <iostream.h>
voidmain(void)
{
    int x = 3;
    double y = 2.3;
        int a;
    double b;
    b = MyFunc(x,y);
    cout<< "b=" <<b <<endl;
        a = CdeclFunc(x);
        cout << a <<endl;
        a = CMyFunc(a*x);       // Calling with a different name
        cout << a <<endl;
}
--------------------------------------------------------------------
// Header file pascal2.h, needed to resolve CMyFunc
extern "C"  int  CMyFunc(int a);
--------------------------------------------------------------------
이 응용프로그램을 빌드하고, 실행하자. 기대한 대로 작동할 것이다.
C DLL을 Visual Basic으로부터 호출하는데 대한 좋은 논의는 VB 디렉토리의 VB4DLL.TXT 파일에서 찾을 수 있다.
DEF파일의 다른 유용한 기능은 서수와 NONAME 플래그의 사용에 있다. DEF 파일에서 다음과 같이 수정하고, 다시 DLL을 빌드하라. 보통 10진수인 서수는 DLL에서 함수의 번호를 식별하기 위해 설정한다. GetProcAddress함수가 사용될 때(명시적 링크를 다루는 다음 섹션에서 논의된다), DLL에서의 함수 주소를 얻기 위한 빠른 기법을 제공한다(원문 : The ordinal number can be any decimal number usually set to indicate the number of functions in the DLL, which in turn provides a shorthand method for getting the address of the function in the DLL when GetProcAddress function is used (discussed in the following section on explicit linking)).
--------------------------------------------------------------------
; DEF file for the  DLL
EXPORTS
   MYFUNC =_MyFunc@12
  _MyFunc@12                           @1
  CdeclFunc                            @2  NONAME
  CMyFunc = CdeclFunc
--------------------------------------------------------------------
NONAME 플래그는 익스포트되기 위해 함수 이름을 필요로 하지 않음을 지정하는데, 서수값일 뿐이다. 링커는 함수 이름을 DLL 내부의 함수 테이블에 추가하지 않을 것이다. 이것은 응용프로그램이 함수의 이름에 GetProcAddress를 넘기는 호출을 할 수 없음을 의미한다.
NONAME 플래그를 지정하는 것은 DLL의 함수 이름이 임포트 라이브러리에서 제거되게 하지는 않는다. 그렇지 않으면 응용프로그램을 명시적으로 함수 이름을 사용하는 DLL에 링크할 수 없을 것이다.
DLL을 빌드한 이후에, DUMPBIN/EXPORTS 유틸리티를 사용하면, 익스포트된 함수를 다음과 같이 확인 할 수 있다. CdeclFunc의 함수 이름이 없음에 주의하라.
ordinal    hint      name
   4      0    CMyFunc 
   3          1    MYFUNC
   1     2    _MyFunc@12
응용프로그램을 다시 빌드(혹은 링크)해서 임포트 라이브러리 내의 변화를 반영해야(? account for) 한다. 응용프로그램은 평상시처럼 작동할 것이다.
NONAME 플래그를 DLL에서 사용하는 것의 이점은 함수 이름을 DLL에서 문자열로 지정하는 대신에, 단지 서수만을 사용할 수 있다는 것이다. 이것은 550개의 함수를 익스포트하는 USER32.DLL처럼 수백개의 함수를 포함하는 DLL이 있다면, 많은 공간을 절약할 수 있게 해 준다.
Exporting Classes(클래스 익스포트하기)
C++을 익스포트하는 것은 위에서 보았던 함수나 데이터 멤버를 익스포트하는 것 만큼 간단하다. declspec 속성이나 DEF 파일을 사용할 수 있다. "__declspec(dllexport) Replaces __export in 32-bit Visual C++ " (Q107501) 기사는 declspec 속성을 클래스를 익스포트하기 위해서 사용하는 방법에 대해 설명하고 있다. DEF 파일 접근을 사용하기 위해, .map파일로부터 익스포트하고자 하는 아이템의 꾸며진 C++이름을 복사하라. .map파일은 Developer Studio의 General category에 대한 link settings로부터 선택하거나 관련한 link option을 선택함으로써 생성할 수 있다. DEF 파일에 이러한 꾸며진 이름들을 사용하라.
만약 상속된 클래스를 익스포트하고 있다면, 그것의 기저(base) 클래스도 익스포트할 필요가 있다. 같은 것이 내포된(embedded)에도 적용된다. "Exporting with Class" by Dale Rogerson (MSDN Library Archive Edition)의 기사를 읽는 것도 좋다.
EXE와 DLL을 넘나드는 클래스를 생성하고 제거하는 동안 new/delete가 맞지 않는 문제가 있는데, 그것은 run-time 에러를 유발할 수 있다. 이 상황을 피하기 위해서는 "BUG: Wrong Operator Delete Called for Exported Class" (Q122675) 기사에 제공되는 제안을 따르는 것이 좋다.
Exporting Template Class and Functions(템플릿 클래스와 템플릿 함수 익스포트하기)
Visual C++ 은 템플릿 클래스와 템플릿 함수에 대한 익스포트를 지원하지 않는다. 그 이유 중 하나는 템플릿 자체의 개념에서 발생한다. 클래스 템플릿은 유사한, 그러나 다른 타입들에 대한 추상적인 표현이다. 클래스 템플릿의 인스턴스가 생성되기 전까지는 어떠한 코드나 데이터도 존재하지 않는다. workaround(추천하지 않음, 역자주 :  언어인가...)는 DLL에서 declspec 속성을 사용하여 템플릿 함수와 템플릿 클래스를 선언한다. 그리고 모든 가능한 유형을 DLL 소스에서 인스턴스화(instantiate)한다. 알겠지만, 이것은 템플릿을 사용하기 위한 목적을 망가뜨리는 것이다.
어떤 이유로도 Standard Template Library(STL) 클래스나 함수를 익스포트할 수 없다.
Explicit Dynamic Linking(명시적 동적 링크)
명시적인 동적 링크는 응용프로그램 내에서 DLL의 함수를 호출할 준비가 되었을 때만 DLL을 호출하고자 할 때 사용된다. 함수의 사용을 마쳤으면, DLL을 메모리에서 언로드할 수 있다. 즉 응용프로그램이 실행되는 동안 메모리 공간을 절약할 수 있는 것이다. 그리고 다른 DLL을 다른 함수 호출을 위해 로드할 수 있다. 이를 달성하기 위한 두 함수는 LoadLibrary와 FreeLibrary이다. 함수의 주소를 얻기 위해서는 GetProcAddress가 사용된다. GetProcAddress는 실제 함수 이름이나 전에 언급한 DEF 파일과 관련된 함수의 서수를 얻을 수 있다. 서수의 사용은 GetProcAddress가 함수를 DLL 함수 테이블에 빨리 위치시킬 수 있게 하는데, 함수 이름 문자열로 테이블을 검색할 필요가 없게 된다. NONAME 플래그가 DEF파일에서 사용되면, GetProcAddress는 반드시 서수를 사용해야 한다. 서수를 통해 함수 주소를 얻기 위해서, MAKEINTRESOURCE 매크로가 사용된다. 다음 예제는 이것을 수행하기 위해서 응용프로그램 측에서 작성하는 코드를 보여준다. Pascal.h 와 Pascal2.h 헤더파일이 없다는 점에 주의하라. DLL이 명시적으로 로드되었기 때문에, 응용프로그램은 DLL 임포트 라이브러리를 링크하지 않는다. 그러나 DLL은 LoadLibrary가 성공하기 위해 지정된 디렉토리에 존재해야만 한다. 아래의 예제에서 우리는 pascaldll.dll을 응용프로그램의 EXE 디렉토리에 넣었다. 다음 소스 파일을 사용하라. 응용프로그래을 pascaldll.lib 임포트 라이브러리 없이 빌드한다.
--------------------------------------------------------------------
// Application .CPP file  explicitmain.cpp
#include <windows.h>
#include <iostream.h>
void main(void)
{
        typedef int ( * lpFunc1)(int);
    typedef double ( * lpFunc2)(int, double);
    HINSTANCE  hLibrary;
    lpFunc1 Func1, Func2;
    lpFunc2 Func3;
       
    int x = 3;
    double y = 2.3;
        int a,c;
    double b;
   
    hLibrary = LoadLibrary("pascaldll.dll"); // Load the DLL now
   
    if (hLibrary != NULL)
    {
        Func1 = (lpFunc1) GetProcAddress(hLibrary, "CMyFunc");
        if (Func1 != NULL)
            a = ((Func1)(x ));
        else cout << "Error in Func1 call" << endl;
        Func2 = (lpFunc1) GetProcAddress(hLibrary,
                      MAKEINTRESOURCE(2));
        if (Func2 != NULL)
            c = ((Func2)(a*x ));
        else cout << "Error in Func2 call" << endl;
       
        Func3 = (lpFunc2) GetProcAddress(hLibrary,
                      MAKEINTRESOURCE(1));
        if (Func3 != NULL)
            b = ((Func3)( x, y ));
        else cout << "Error in Func3 call" << endl;
    }
    else cout << "Error in Load Library" << endl;
    cout << "b=" << b << endl;
        cout << "a=" << a << endl;
            cout << "c=" << c << endl;
    FreeLibrary(hLibrary);         // Unload DLL from memory
}
--------------------------------------------------------------------
응용프로그램을 실행해보자. 그것은 원하는 출력을 보일 것이다.
디버깅하는 동안에, LoadLibrary가 DLL을 로드하지 않는한 어떤 DLL함수에도 breakpoint를 설정할 수 없다. 이 상황을 피하기 위해서, Build 메뉴의 Settings 다이얼로그 박스를 열고, Debug 눌러, Category 드롭다운 리스트 박스에서 Additional DLLs를 선택하고, Local Name 항목에 동적으로 로드된 DLL의 이름을 입력한다. 이제 F11이나 F5를 눌러 디버거를 시작했을 때 디버그 출력 윈도우에서 "Loaded symbols for mydllname.dll"이라는 메시지를 볼 수 있을 것이다. LoadLibrary호출이 아직 만들어지지 않았더라도 DLL 소스 파일을 열어 breakpoint를 설정할 수 있다.
만약 응용프로그램을 Windows NT 운영체제에서 돌리고 있다면, PVIEW 유틸리티를 사용해 응용프로그램의 실행 시간 동안에 로드되는 DLL이 어떤 것인지 찾을 수 있다. PVIEW에서 응용프로그램을 선택하고 Memory Detail 버튼을 클릭한다. 그리고 나서 Total Commit를 보여주는 드롭다운 리스트 박스에서 화살표를 클릭한다. 응용프로그램을 위해 로드된 DLL의 리스트를 볼 수 있을 것이다.
The DLL Entry Point (DLL 시작점)
이전에 DLL 시작점인 함수를 언급했다. 아직까지 예제 DLL의 구현에서 이 함수를 삽입하지 않았지만, DLL은 작동했다. 왜냐면 C/C++ run-time 라이브러리가 자동으로 DLLMain이라는 DLL 시작점 함수를 제공하기 때문이다. 이 기본 DllMain 함수를 찾지 못하면, 컴파일러는 DLL 코드에서 이 함수를 검색한다. 이 함수는 DLL의 초기화와 종료를 수행한다. C/C++ run-time 라이브러리 DLL 시작점 함수의 실제 이름은 _DLLMainCRTStratup인데, 이것은 (_CRT_INT 함수를 호출하는) C/C++ run-time 라이브러리를 초기화하고, DLLMain을 호출한다. 자신만의 DllMain을 DLL 코드에 제공할 수도 있다. 자신만의 시작점 함수를 가지도록 선택할 수도 있다. 이 경우에는 _DllMainCRTStartup 함수에 의해 수행되는 작업(적절하게 C/C++ run-time 라이브러리를 초기화하는 것)을 해야하고, /ENTRY:FunctionName 링커 스위치를 사용해야한다(Visual C++ Developer Studio를 사용하고 있따면, Build 메뉴의 Settings 다이얼로그 박스를 열고, Link 탭을 눌러, Category 드롭다운 리스트에서 Output을 선택하고, Entry-point symbol 텍스트 박스에 함수 이름을 입력해도 된다). 시작점 함수는 세 개의 인자인, 모듈핸들, 호출이유(네 가지 이유가 가능), 예약 인자를 가지고 있다. 그것은 성공에 대해 TRUE(1)를 반환한다. 다음에 오는 코드는 DllMain의 정의가 어떤 식인지에 대한 기본 구조를 보여준다. 이 코드를 이전에 만들었떤 모든 DLL에 추가할 수 있다. 그러나 실행에 있어서는 차이를 느낄 수 없을 것이다. 왜냐하면 이것은 기본 DllMain과 다른 행동을 취하지 않기 때문이다.
#include <windows.h>
BOOL WINAPI DllMain( HANDLE hModule,
                        DWORD fdwreason,  LPVOID lpReserved )
{
    switch(fdwreason) {
    case DLL_PROCESS_ATTACH:
    // The DLL is being mapped into process's address space
    //  Do any required initialization on a per application basis, return FALSE if failed
    break;
    case DLL_THREAD_ATTACH:
    // A thread is created. Do any required initialization on a per thread basis
    break;
    case DLL_THREAD_DETACH:
    // Thread exits with  cleanup
    break;
    case DLL_PROCESS_DETACH:
    // The DLL unmapped from process's address space. Do necessary cleanup
    break;
    }
    return TRUE;
}
hModule 인자는 DLL의 인스턴스 핸들을 포함하고 있따. 이 인자를 전역 변수에 저장해서 LoadString과 같은 리소스를 로드하는 호출에서 이를 사용할 수도 있다.
fdwreason 인자는 왜 운영체제가 DllMain을 호출했는지에 대한 이유를 기술한다. 네 가지 이유인 DLL_PROCESS_ATTACH, DLL_TREAD_ATTACH, DLL_THREAD_DETACH, DLL_PROCESS_DETACH가 있다. Dll이 새로운 프로세스에 의해 로드될 때마다 DllMain은 DLL_PROCESS_ATTACH와 함께 호출된다. 만약 이 프로세스의 스레드가 이 DLL상에서 LoadLibrary를 호출하면 DllMain이 DLL_THREAD_ATTACH와 함께 호출된다. 이 스레드에서 FreeLibrary 호출은 DLL_THREAD_ATTACH와 함께 DllMain을 호출한다. 응용프로그램이 DLL을 해제할 때, DllMain은 DLL_PROCESS_DETACH와 함께 호출된다. "DLLs in Win32" (MSDN Library)와 온라인 도움말을 참조하라.
lpReserved 인자는 예약되었으며, ExitProcess가 호출되지 않는 한, FreeLibrary 호출과 같은 일반적 프로세스 퇴장(exit)에 대해 일반적으로 NULL값을 넘긴다.
DllMain 함수의 사용을 설명하는 코드를 작성해 보자. pascal.cpp 파일을 수정하고, DllMain 함수를 가진 pascal.dll을 빌드하려고 한다. 이 파일은 다음과 같이 수정된다.
--------------------------------------------------------------------
// File showing use of DllMain function
// DLL source file pascal.cpp
#include <windows.h>
#include <iostream.h>
#include "pascal.h"
HANDLE dllHandle;
BOOL WINAPI DllMain( HANDLE hModule,
                        DWORD fdwreason,  LPVOID lpReserved )
{
    dllHandle = hModule; // Saved for later use
   switch(fdwreason) {
    case DLL_PROCESS_ATTACH:
    // The DLL is being mapped into process's address space
    //  Do any required initialization on a per application basis,
    //  return FALSE if failed
    MessageBox(NULL, "DLL Process Attach", "DLL Message 1", MB_OK);
    break;
    case DLL_THREAD_ATTACH:
    // A thread is created. Do any required initialization on a per
    // thread basis
    MessageBox(NULL, "DLL Thread Attach", "DLL Message 2", MB_OK);
    break;
    case DLL_THREAD_DETACH:
    // Thread exits with  cleanup
    MessageBox(NULL, "DLL Thread Detach", "DLL Message 3", MB_OK);
    break;
    case DLL_PROCESS_DETACH:
    // The DLL unmapped from process's address space. Do necessary
    // cleanup
    MessageBox(NULL, "DLL Process Detach", "DLL Message 4", MB_OK);
    break;
    }
    return TRUE;
}
double __stdcall MyFunc(int a, double b){
return a*b;
}
int CdeclFunc(int a){
return 2*a;
}
--------------------------------------------------------------------
DLL을 빌드하자. 이제 위의 DLL을 다음 소스 파일과 함께 사용하는 스레드를 가진 콘솔 응용프로그램을 구현해 보자. 간단하게 스레드에서 할 일은 메인 프로세스에서와 거의 같다. _beginthread 함수를 사용하고 있기 때문에, 다중 스레드를 가진 C run-time 라이브러리(LIBCMT[D].LIB)나 다중 스레드를 가진 DLL 버전의 C run-time library(MSVCRT[D].LIB)와 함께 이 응용프로그램을 빌드한다. [D]는 디버그 버전의 라이브러리를 위한 것이다. project settings for the C/C++ Code Generation category에서 이 옵션을 선택할 수 있다. 이 응용프로그램을 실행할 때, 프로세스와 스레드가 DLL을 로드하고 있다는 메시지 박스가 뜨는 것을 볼 수 있을 것이다.
--------------------------------------------------------------------
// File processattach.cpp
#include <windows.h>
#include <process.h>
#include <iostream.h>
void firstthread(void* dummy);
void main(void)
{
    typedef int ( * lpFunc1)(int);
    typedef double ( * lpFunc2)(int, double);
    HINSTANCE  hLibrary;
    lpFunc1 Func1, Func2;
    lpFunc2 Func3;
   
    int x=3;
    double y=2.3;
    int a,c;
    double b;
   
    hLibrary = LoadLibrary("pascaldll.dll"); //Load DLL in main
   
    if (hLibrary != NULL)
    {
        Func1 =(lpFunc1) GetProcAddress(hLibrary, "CMyFunc");
        if (Func1 != NULL)
            a = ((Func1)(x ));
        else cout << "Error in Func1 call" << endl;
        Func2 =(lpFunc1) GetProcAddress(hLibrary,
                     MAKEINTRESOURCE(2));
        if (Func2 != NULL)
            c = ((Func2)(a*x ));
        else cout << "Error in Func2 call" << endl;
       
        Func3 =(lpFunc2) GetProcAddress(hLibrary,
                     MAKEINTRESOURCE(1));
        if (Func3 != NULL)
            b = ((Func3)( x, y ));
        else cout << "Error in Func3 call" << endl;
    }
    else cout << "Error in Load Library" << endl;
    cout << "b=" << b << endl;
        cout << "a=" << a << endl;
              cout << "c=" << c << endl;
   
    _beginthread(firstthread,0,NULL); // Start a thread
    Sleep(10000L);
    cout << "Exit Main Process" << endl;
    FreeLibrary(hLibrary); //Free Library in main
}
void firstthread(void* dummy)
{
        typedef int ( * lpFunc1)(int);
    typedef double ( * lpFunc2)(int, double);
    HINSTANCE  hLibrary;
    lpFunc1 Func1, Func2;
    char buf[10];
   
    int x=10;
    int a,c;
   
    hLibrary = LoadLibrary("pascaldll.dll"); //Load DLL in thread
   
    if (hLibrary != NULL)
    {
        Func1 =(lpFunc1) GetProcAddress(hLibrary, "CMyFunc");
        if (Func1 != NULL)
            a = ((Func1)(x ));
        else cout << "Error in Func1 call" << endl;
        Func2 =(lpFunc1) GetProcAddress(hLibrary,
                     MAKEINTRESOURCE(2));
        if (Func2 != NULL)
            c = ((Func2)(a*x ));
        else cout << "Error in Func2 call" << endl;
       
    }
    else cout << "Error in Load Library" << endl;
   
    _itoa( c, buf, 10 );
    MessageBox(NULL, buf, "Threadtest Result", MB_OK);
   
    cout << "Now output other values" << endl;
    cout << "a=" <<  a  << endl;
              cout << "Leaving firstthread" << endl;
   
    FreeLibrary(hLibrary); //Free Library in thread
}

-------------------------------------------------------------------
Applications Sharing Data in DLL(DLL의 데이터를 공유하는 응용프로그램)
16비트 윈도우 운영체제와는 달리, 32비트 윈도우 운영체제는 다른 응용프로그램으로부터 DLL의 데이터를 공유하는 것을 허용하지 않는다. 왜냐하면 응용프로그램들이 직접적으로 프로세스 간의 메모리를 공유하는 것은 금지되어 있기 때문이다. 프로세스간에 데이터를 공유하기 위해 사용되는 기법의 두 가지가 있다. 하나는 memory-mapped 파일을 사용하는 것이고, 다른 하나는 공유 데이터 세그먼트를 생성하는 것이다. memory-mapped 파일을 사용하는 방식을 추천한다. Advanced Windows NT, The Developer's Guide to the Win32 Application Programming Interface by J. Richter and "DLLs in Win32" by Randy Kath (MSDN Library) 을 보면 더 세부적인 사항을 알 수 있을 것이라고 추천하는 바이다. 다음 기사들도 유용할 것이다.
"How to Share Data Between Different Mappings of a DLL" (Q125677)
"Sharing All Data in a DLL" (Q109619)
"How to Specify Shared and Nonshared Data in a DLL" (Q89817)
Mutual Imports, DLLs and EXE(공동 임포트, DLL과 EXE)
DLL과 EXE 사이의 익스포트와 임포트는 약간의 복잡함을 제공한다. 왜냐하면 하나가 빌드되기 전에 다른 것의 임포트 라이브러리가 필요하기 때문이다. 그러나 다른 DLL이나 EXE로부터 임포트하는 DLL이나 EXE를 빌드하는 동안, 임포트 라이브러리와 .EXP 확장자를 가진 익스포트 파일은 dllexport나 DEF 파일이 사용될 때 생성된다. 그래서 DLL이나 EXE가 함수를 익스포트한다면, 링커가 unresolved symbol error를 생성하고, EXE와 DLL이 생성되지 않는다고 할지라도,  임포트 라이브러리와 .EXP파일을 획득할 수 있다. But, with the help of this import library you will be able to build the next DLL, which in turn will generate its import library and its DLL file. 이제 이 임포트 라이브러리를 .EXP 파일과 함께 사용하고, 이전의 DLL이나 EXE를 첫번째 것을 획득할 때까지 빌드할 수 있다. Developer Stuio에서 빌드한다면, .EXP 파일은 자동적으로 사용된다. 명령줄에서 빌드하고 있다면, 링커 입력에 .EXP 파일을 지정할 필요가 있다. 아래의 예제에서 응용프로그램을 먼저 빌드하는 것으로 시작하는데, 이 응용프로그램은 .EXP 파일과 임포트 라이브러리를 제공한다. 그러나 .EXE파일은 제공하지 않는다. 이 임포트 라이브러리로 DLL1을 빌드하는데, 이것은 임포트 라이브러리인 DLL1.LIB와 DLL1.EXP를 제공한다. 이제 DLL2를 빌드할 수 있다. 왜냐하면 DLL1.LIB를 가지고 있기 때문이다. 그래서 우리는 DLL2.DLL과 DLL2.LIB를 획득할 수 있다. DLL2.LIB와 DLL1.EXP를 가지고 이제 DLL1.DLL을 빌드한다. 이미 DLL.LIB를 가지고 있기 때문에, EXE도 빌드할 수 있다. 예제에서 응용프로그램은 DLL1을 호출하고, DLL1은 차례대로 DLL2와 응용프로그램을 호출한다. 그리고 DLL2는 DLL1을 호출한다. 아래에 프로젝트를 빌드하는데 사용된 파일이 있다. This is like going forward until success, and then coming back in reverse order until all modules are built.
--------------------------------------------------------------------
// This is the header file for the application when using DLL1--
// applib.h
// DLL1 calls callappfunc function from this application as a callback
// function.
__declspec(dllexport) int callappfunc(int * ptrlib);
__declspec(dllimport) int libglobal;
__declspec(dllimport) void libfuncA(char * buffer);
__declspec(dllimport) int  libfuncB(int const * mainint);
--------------------------------------------------------------------
// This is the application source file  -- appsource.c
// The callappfunc function will be called by DLL1.
// Application calls libfuncA and libfuncB ib DLL1.
#include "applib.h"
#include <stdio.h>
int mainglobal;
int callappfunc(int * ptrlib)
 { 
    static int x = 0;
    int m =  *ptrlib;
    printf ("We are starting in the application callappfunc round %d\n",
             ++x);
   
    m =  libglobal - m * m ;
    printf ("Application callappfunc value: %d\n", m);
    return m;
 }
 void main(void)
  {
        int libreturn;
    char  buf[] = "Let us print this line in DLL1";
    printf ("We are starting in the application\n");
    libfuncA(buf);
    printf("Let us increment the library global and print in app
              :%d\n", ++libglobal);
   
    mainglobal = 10;
    libreturn = libfuncB( &mainglobal);
    printf("\nPrint the DLL1 returned value in app :%d\n", libreturn);
    printf("Demonstration of Mutual DLL (import) use is complete\n");
  }
--------------------------------------------------------------------
// Library header for DLL1-- lib1.h
// DLL1 calls dllfuncA function in DLL2 and callappfunc in application
__declspec(dllexport) int libglobal;
__declspec(dllexport) void libfuncA(char * buffer);
__declspec(dllexport) int  libfuncB(int const * mainint);
__declspec(dllimport) int callappfunc(int * ptrlib);
__declspec(dllimport) void dllfuncA(char * buffer);
--------------------------------------------------------------------
// This is a header file of the DLL1 local functions-- lib2.h
 int liblocalA(int x, int y);
--------------------------------------------------------------------
// This is the DLL source file for DLL1 ---lib1.c
// DLL1 calls callappfunc function in application and
// dllfuncA function in DLL2.
#include <stdio.h>
#include "lib1.h"
#include "lib2.h"
libglobal = 20;
void libfuncA(char * buffer)
 {       
      int i =1;
        printf("Printing in DLL1 \n%s\n\n", buffer);
     printf("Value of i in DLL1: %d\n", i);
    buffer = "Changed value(in DLL1) of original content";
    dllfuncA(buffer);
 }
int libfuncB(int const * mainint)
 {   
        int returnvalue;
        int  localint;
        int libvalue = 3;
    localint = *mainint;
    returnvalue = liblocalA(localint, libglobal);
   
    libvalue = callappfunc(&libvalue);
    printf("\nrint returnvalue and libvalue in DLL1:%d, %d\n",
               returnvalue, libvalue);
    return returnvalue;
 
 }
int liblocalA(int x, int y)
 {
   return (x + y);
 }
--------------------------------------------------------------------
// Library header lib2A.h for DLL2
// DLL2 calls libfuncB function in DLL1.
__declspec(dllexport) int dllglobal;
__declspec(dllexport) void dllfuncA(char * buffer);
__declspec(dllimport) int  libfuncB(int const * mainint);
--------------------------------------------------------------------
// This is the  source file lib2.c for DLL2.
// Calls libfuncB function in DLL1.
#include <stdio.h>
#include "lib2A.h"
dllglobal = 35;
void dllfuncA(char * buffer)
 {       
        int i =2;
        printf ("We are starting in DLL2\n");
        printf("Printing in DLL2 \n%s\n\n", buffer);
        printf("Value of i in DLL2: %d\n dllglobal:%d\n", i,dllglobal);
       
        printf ("We are calling into DLL1 from DLL2\n");
        libfuncB(&dllglobal);
 }
----------------------------------------------------------------------
DLL Base Address Conflict(DLL 기본 주소 충돌)
모든 시스템 DLL은 보통 같은 가상 주소에 로드되는데, 프로세스를 고려하지 않는다. 종종 DLL 기본 주소에서 충돌이 일어날 수도 있는데, 로드되고 있는 응용프로그램에 의해 DLL이 사용될 때 다음과 같은 메시지를 만나게 될 것이다 :
     LDR: Dll xxxx.DLL base 10000000 relocated due to collision with
     yyyy.DLL
이 재배치는 응용프로그램에서 수행성 패널티(penalty)를 유발한다. 그러나 한 번만 일어나며, 패널티는 상관할 필요가 없다. DLL이 재배치될 때마다 DLL에 대한 fix-ups(조정?)가 재계산되어야 한다. 이것은 운영체제 내부적으로 수행되며, 이들 DLL을 사용하는 응용프로그래에 의해 제어될 수 없다. 당신이 생성하고 있는 새로운 DLL에 대해서, 10000000(hex)는 Visual C++에 의해 사용되는 디폴트 기본 주소이다.
다음 LINK 명령어를 DLL에 사용하고, DLL의 기본 주소를 찾기 위해 "image base" 시작점을 살펴보라:
     link -dump -headers  your.dll
기본 주소는 링커 스위치 /BASE:address를 사용함으로써 병경될 수 있다. DLL의 기본 주소를 그룹화해야만 한다. 0x10000000보다 크고 0x60000000보다 작은 번위의 주소를 사용해야만 한다. 또한 EDITBIN 유틸리티를 사용할 수도 있고, /REBASE 스위치를 사용할 수도 있다. 더 많은 정보를 위해서는 온라인 도움말에서 "base"로 검색하라. "Rebasing Win32 DLLs: The Whole Story" (MSDN Library) 와 "Dynamically Loading Dynamic-Link Libraries in Windows NT" (Q100635)라는 글을 참조해도 좋다.
Conclusion
이 기사에서는 쉽게 이해할 수 있도록 도움을 주는 예제와 함께 C/C++ DLL을 빌드하는 기본 개념이 설명되었다. 레퍼런스의 리스트는 더 많은 공부를 위해 제공되었다. 이 기사의 목적은 새로운 프로그래머들이 DLL을 구현하려고 하면서 직면하게 되는 처음의 벽을 무너뜨리는데 있다. 나는 이 기사가 이 목적에 적합하길 바란다.
References
Asche, Ruediger R. "Rebasing Win32 DLLs: The Whole Story." September 1995. (MSDN Library)
Dynamic Link Libraries (DLLs), Microsoft Visual C++ 4.0 Online Documentation.
Gellock, Scot. "Writing DLLs in Win32." (MSDN Library Archive)
Kath, Randy. "DLLs in Win32." September 1992. (MSDN Library)
Richter, J. Advanced Windows NT, The Developer's Guide to the Win32 Application Programming Interface. Redmond, Washington: Microsoft Press, 1994.
Richter, J. Advanced Windows, The Developer's Guide to the Win32 API for Windows NT 3.5 and Windows 95. Redmond, Washington: Microsoft Press, 1995.
Appendix A
16비트 DLL은 그것이 작동하는 방식에서 구현시 고려해야할 다른 점이 있다. 무엇보다 하나 이상의 응용프로그램이 DLL을 로드한다면, 하나의 데이터 세그먼트만이 존재할 것이라는 점이다. 그러한 이유로 DLL의 전역 변수들은 32비트 DLL과는 달리, 그 DLL을 사용하는 모든 응용프로그램에 의해 공유될 수 있다. DLL이 자신만의 스택 세그먼트를 가지지 않기 때문에, DLL을 생성하는 동안 프로그래머는 스텍 세그먼트가 데이터 세그먼트와 같지 않다(SS != DS)는 점을 기억해야 한다. 16비트 DLL을 구현하는 방법에 대해서는 "How to Export Functions from a 16-bit DLL" (Q148832) 기사를 참조하라.
두 기사는 16비트 DLL과 16비트 DLL을 32비트 DLL로 포팅하는 법에 대해서 많은 정보를 포함하고 있다 : "General Information Regarding Dynamic-Link Libraries" (Q76476) 와 "Portable DLL Example Using _declspec() and _export" (Q123870).
References
Gunderson, Bob. "Loading, Initializing, and Terminating a DLL." March 1992. (MSDN Library Archive)
Gunderson, Bob. "Modules, Instances, and Tasks." March 1992. (MSDN Library Archive)
Long, David. "Designing DLLs for Multiple Client Applications, Part 1: Strategy." April 1993. (MSDN Library Archive)
Long, David, and Dan Ruder. "Introduction to Microsoft Windows Dynamic-Link Libraries." August 1992. (MSDN Library Archive)
Long, David, and Dan Ruder. "Mechanics of Dynamic Linking." January 1993. (MSDN Library Archive)
Rogerson, Dale. "Exporting with Class." October 1992. (MSDN Library Archive)
Ruder, Dan. "DLL Anatomy—What's a DLL Made Of?" May 1993. (MSDN Library Archive)


'프로그래밍 > C C++' 카테고리의 다른 글

Process Kill with processname  (0) 2009.05.06
Always On Top  (0) 2009.05.06
#include "Tlhelp32.h"

bool ProcessKill(CString strProcessName)
{
    HANDLE         hProcessSnap = NULL; 
    BOOL           bRet      = FALSE; 
    PROCESSENTRY32 pe32      = {0}; 

    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 

    if (hProcessSnap == (HANDLE)-1) 
        return false;

    pe32.dwSize = sizeof(PROCESSENTRY32); 

    //프로세스가 메모리상에 있으면 첫번째 프로세스를 얻는다
    if (Process32First(hProcessSnap, &pe32)) 
    { 
        BOOL          bCurrent = FALSE; 
        MODULEENTRY32 me32       = {0}; 

        do 
        { 
            bCurrent = GetProcessModule(pe32.th32ProcessID,strProcessName);
            if(bCurrent) 
            { 
                HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID); 
                if(hProcess)
                {
                    if(TerminateProcess(hProcess, 0))
                    {
                        unsigned long nCode; //프로세스 종료 상태 
                        GetExitCodeProcess(hProcess, &nCode);
                    }
                    printf("process exit\n");
                    CloseHandle(hProcess);
                    return true;
                }
            } 
        } 
        while (Process32Next(hProcessSnap, &pe32)); //다음 프로세스의 정보를 구하여 있으면 루프를 돈다.
    }
    CloseHandle (hProcessSnap); 
    return true;
}



bool GetProcessModule(DWORD dwPID,CString sProcessName)

    HANDLE        hModuleSnap = NULL; 
    MODULEENTRY32 me32        = {0}; 
    hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID); 

    if (hModuleSnap == (HANDLE)-1) 
        return (FALSE); 

    me32.dwSize = sizeof(MODULEENTRY32); 

//해당 프로세스의 모듈리스트를 루프로 돌려서 프로세스이름과 동일하면 
//true를 리턴한다.

    if(Module32First(hModuleSnap, &me32)) 
    { 
        do 
        { 
            printf("process name : %s\n", me32.szModule);

            if(me32.szModule == sProcessName)
            { 
                CloseHandle (hModuleSnap); 
                return true;
            } 

        } 
        while(Module32Next(hModuleSnap, &me32)); 
    } 

    CloseHandle (hModuleSnap); 
    return false;
}



사용예)

ProcessKill(_T("iexplore.exe"));

'프로그래밍 > C C++' 카테고리의 다른 글

DLLs for Beginners  (0) 2009.05.06
Always On Top  (0) 2009.05.06

MFC 에서는 CWnd::SetWindowPos , API 에서는 SetWindowPos 함수를 사용

1. MFC 버젼

Always on Top 설정
SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);

Always on Top 해제
SetWindowPos(&wndNoTopMost, 0, 0, 0, 0, SWP_NOSIZE |SWP_NOMOVE);

SWP_NOSIZE, SWP_NOMOVE 는 윈도우의 크기와 이동이 되지 않게끔 하는 플래그

 

2. API 버젼

Always on Top 설정
SetWindowPos(YourHwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);

Always on Top 해제
SetWindowPos(YourHwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);


m_pMainWnd->SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOSIZE);

만약 wndTopMost 를 찾을수 없다고 하면

SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOSIZE);

=> SetWindowPos(&(Cwnd::wndTopMost), 0, 0, 0, 0, SWP_NOSIZE); 

'프로그래밍 > C C++' 카테고리의 다른 글

DLLs for Beginners  (0) 2009.05.06
Process Kill with processname  (0) 2009.05.06

+ Recent posts