티스토리로 이사했습니다. lazypaul.tistory.com

다시 정리 시작할겸..

이글루는 코드쓰기가 너무 불편해서 이사했습니다.

lazypaul.tistory.com

API :: C/C++ DLL을 만들어보자 API

본 강좌는 Visual C++ 6.0, 디버그 모드를 기준으로 작성되었습니다. Visual C++ 7.0, 8.0, 9.0 등에서도 기본적인 작성 방법은 같습니다. 어디까지나 예제이므로 릴리즈 모드는 포함하지 않았습니다. 릴리즈 모드를 원하시면 릴리즈로 컴파일하신 후 그 파일을 사용하면 됩니다.




1. DLL이란?
2. DLL 만드는 방법
3. DEF - 모듈 정의 파일
4. DllMain
5. 묵시적 연결과 명시적 연결
6. 다른 언어와의 연결





1. DLL이란?
DLL을 알기 전에 먼저 “라이브러리”라는 개념을 알아야 합니다.
라이브러리란 프로그램 개발 시 자주 필요한 함수 등을 미리 컴파일해 놓은, 말하자면 함수 모음집과 비슷합니다.
쉬 운 예로 CRT 함수들이 있죠. printf, scanf, fopen 등의 함수들은 각 컴파일러 제작사들이 라이브러리에 미리 포함시켜 놓고, 링크시에 그 코드를 가져와서 실행 파일에 합치는 것입니다. 따라서 개발자들은 printf, scanf 등의 함수를 프로젝트 하나하나마다 만들 필요가 없죠.






이렇게 실행 파일에 그 코드가 직접 삽입되는 것을 정적 연결 라이브러리라고 합니다. 정적 연결 라이브러리는 부속 파일이 필요없다는 장점이 있으나 다음과 같은 단점도 있습니다.

1. 각각의 실행 파일에 코드가 포함되므로 전체적인 시스템의 자원이 낭비됩니다.
2. 라이브러리 제작사가 라이브러리를 업그레이드하면 실행 파일을 다시 컴파일해야 합니다. 예를 들어서 printf 함수를 개선한 새로운 CRT.lib이 나왔다면 다시 컴파일하기 전까지는 그 혜택을 받을 수 없습니다.

그래서 나온 것이 바로 DLL-동적 연결 라이브러리입니다. DLL은 기본적으로 함수와 코드 등을 모아놨다는 점에서는 정적 연결 라이브러리와 동일하지만 실행 파일 안에 코드가 포함되지 않고 프로그램이 실행될 때 코드가 연결됩니다.




각 각의 실행 파일에는 CRT.DLL에 printf, scanf, fopen 등의 함수가 있다는 정보만 있고, 윈도우즈는 실행 파일을 실행할 때 CRT.DLL도 같이 읽어들여서 각각의 실행 파일과 연결시켜줍니다. 이러한 DLL에는 다음과 같은 특징이 있습니다.

1. 코드가 DLL 하나에만 들어있으므로 전체적인 시스템 자원이 절약됩니다.
2. DLL의 버전이 바뀌면 DLL만 새로 교체하면 됩니다. CRT.DLL의 printf 함수가 개선되었다면 CRT.DLL만 교체하면 그 이득을 볼 수 있다는 뜻이죠.
3. DLL에 필요한 코드가 있으므로 DLL이 없으면 실행 파일을 실행할 수 없습니다.

실 제로 DLL은 아주 많은 곳에서 사용되고 있으며 윈도우즈도 KERNEL32, USER32, GDI32 이 세 개의 핵심 DLL로 돌아가고 있습니다. 아무 생각 없이 호출했던 MessageBox, CreateWindow 등의 함수도 다 저 DLL 속에 들어있는 것들이고 그것들을 호출하는 것입니다.


2. DLL 만드는 방법
DLL을 만들기 위해서는 C/C++ 컴파일러, 키보드, 마우스, 손가락(가능하다면 발가락도 괜찮음), 생각을 위한 약간의 뇌가 필요합니다. 여기서는 Microsoft Visual C++ 6.0을 사용하도록 하겠습니다.

Microsoft Visual C++ 6.0을 켜고, File - New - Projects - Win32 Dynamic Link Library을 선택하고 적절한 이름을 지은 뒤
 OK!를 누릅니다.




DLL 예제 첫번째이므로 DLLEXAM1이라는 이름을 붙였습니다.

OK를 누르면 뭔가 쎈 척 하는 창이 뜨는데 empty 들어간 걸 선택해서 Finish, OK를 차례대로 누릅니다. 그러면 프로젝트가 만들어집니다.
첫번째 예제이므로 두 정수를 입력받아 그 합을 리턴하는 함수를 포함하는 DLL을 만들어봅시다.
프로젝트에 cpp 파일을 하나 추가하고 다음과 같이 입력합니다.




혹시 여기까지 작성하실 수 없는 분이라면 지금 전혀 엉뚱한 강좌를 잘못 보고 있는 것이므로 C에 대한 기본적인 것을 먼저 마스터하고 오실 것을 권하겠습니다.

어쨌든 여기까지 작성하고 빌드를 하면 성공일겁니다.



프로젝트 폴더의 debug 폴더에 보면 DLLEXAM1.dll이라는 파일이 만들어져 있을 겁니다.
근데 이 파일을 수동으로 시스템 폴더에 옮기기엔 너무 귀찮습니다. 그래서 컴파일 시 자동으로 c:\windows\system32에 dll이 복사되도록 해 보겠습니다.

메뉴의 Project - Settings - Post-build step(화살표를 눌러 탭을 옮기다 보면 나옵니다.)에 다음과 같은 문을 추가합니다.
copy debug\dllexam1.dll c:\windows\system32



다시 컴파일해보시면 출력창에 다음과 같이 1개 파일이 복사되었습니다. 라고 나올 겁니다.



여기까지 따라하셨다면 여러분은 첫 번째 DLL을 성공적으로 작성한 것입니다. 축하합니다!
(라고 썼지만 실제로 따라하는 사람이 없을 거라는 걸 잘 알죠.)

ps. 아까 DLL은 함수의 집합이라고 했지만 꼭 함수만을 작성할 필요는 없고 전역변수, 클래스, C/C++ 런타임 함수, API 함수등을 마음대로 사용해도 괜찮습니다. 결국엔 DLL이나 실행파일이나 코드와 데이터로 이루어져 있는 건 동일하니까요.

그 러면 이제 이 DLL을 사용하는 간단한 프로그램을 만들어보겠습니다. 콘솔 프로젝트를 작성하고 DLLEXAM1의 debug 폴더에 있는 DLLEXAM1.lib을 해당 프로젝트 폴더에 복사합니다(이게 무엇인지는 잠시 후에 설명합니다) 코드는 다음과 같습니다.




실행 결과는 다음과 같습니다.



DLLEXAM1.dll에 있는 addinteger 함수를 호출해서 덧셈을 하고 있습니다.
그러면 어떻게 이런 결과가 나온 건지 분석해보죠.

다음은 DLLEXAM1의 소스입니다.

extern "C" __declspec(dllexport) int addinteger(int a,int b)
{
 return a+b;
}

extern "C"는 함수 이름을 C 형식으로 하라는 지시자입니다. DLL에는 하나의 이름에 하나의 함수만 대응될 수 있는데, C++은 같은 이름을 가지는 함수를 여러 벌 작성하는 오버로딩이 가능합니다. 따라서 C++로 DLL을 작성하면 실제 DLL에 기록되는 이름에 여러 장식이 붙습니다. 이것은 인수 정보 등의 정보를 담고 있는 표식입니다. 저 extern "C" 문을 지우고 컴파일한 후, Visual Studio 도구 중 하나인 Depends로 DLL을 열어 보면 그 실체를 알 수 있습니다. 여기서는 생략하겠습니다. 그냥 붙이면 좋다-고 알아두세요.

__declspec(dllexport)는 컴파일러에게 이 함수를 export하겠다고 알려줍니다.


그리고 DLLEXAM2의 소스입니다.

#pragma comment(lib, "dllexam1.lib")
extern "C" __declspec(dllimport) int addinteger(int,int);

extern "C"는 위에서와 같고, __declspec(dllimport)는 컴파일러에게 이 함수는 DLL에서 import되는 함수라고 알려줍니다.
그렇다면 위의 #pragma comment(lib, "dllexam1.lib")문은 무엇일까요???

그 답은 바로 이렇습니다..
저 addinteger라는 함수는 dll에서 import한다고만 나와 있지 어떤 dll인지는 알려주지 않습니다. KERNEL32인지 USER32인지 GDI32인지 ADVAPI32인지 DLLEXAM1인지 어떤 dll이 저 함수를 가지는지 컴파일러는 알지 못하죠. 저 dllexam1.lib이라는 파일 안에는 dllexam1이 export하는 함수들에 대한 정보가 들어있습니다. 컴파일러는 lib파일들을 읽고 각 함수와 비교해서 맞는 함수를 골라주는거죠. 즉 kernel32.lib, gdi32.lib 따위도 있다는 말이 되겠군요. 이것들은 각각 kernel32.dll, gdi32.dll의 함수들에 대한 정보가 들어있겠고요. 이것은 후에 나올 묵시적 연결과 연결되는 내용(?)입니다.
그럼 저 문이 뭔지 아실 수 있겠죠? dllexam1.lib을 참조하라는 문입니다.


3. DEF - 모듈 정의 파일
옛날에는 DLL을 만들 때 DEF파일이 반드시 필요했습니다. 그러나 요즘에는 링커의 스위치로 대부분 대체되어서 별 실용성이 없습니다. 그래서 제가 DEF에 대해 쓸 내용은 한 가지밖에 없습니다.

위에서 extern "C"가 함수 이름의 장식을 없애준다고 썼었습니다. 실제로 위 코드의 DLLEXAM1.dll을 depends로 열어 보면 다음과 같이 보입니다.



함수 이름이 addinteger로 깔끔하네요.
그런데 저 코드를 다시 한번 살펴보죠.

extern "C" __declspec(dllexport) int addinteger(int a,int b)

호출 규약에 대한 정의가 없고, 따라서 호출 규약은 C++ 기본값인 cdecl입니다. (호출 규약에 대한 자세한 내용은http://www.winapi.co.kr/clec/cpp2/16-1-3.htm에 가면 보실 수 있습니다.)
그런데 윈도우즈의 기본 호출 규약은 stdcall이고, VB에서도 stdcall을 사용합니다. cdecl보다는 stdcall을 사용하는 게 더 좋아 보입니다. 그래서 호출 규약을 stdcall로 바꿔보고 컴파일해보겠습니다.



컴파일한 결과를 depends로 열어보았더니, 앗!



대체 저 _, @8은 뭘까여... 이대로 가다가는 addinteger라는 이름만으로는 저 함수를 찾기 힘들 텐데...
저 장식까지도 완벽하게 지우기 위해서 DEF파일이 필요한겁니다.
적절한 이름으로 DEF파일을 만들어 프로젝트에 추가하고 다음과 같이 넣으세요.



DLLEXAM1 라이브러리에서 addinteger라는 이름을 가지는 함수를 export하겠다... 라는 뜻입니다.
이제 다시 depends로 보면 장식이 없어져 있을 겁니다.. 그건 직접 확인해보세요.


4. DllMain

보통 프로그램은 main이나 WinMain 함수를 진입점으로 가집니다. DLL도 진입점을 가질 수 있고 그것이 바로 DllMain 함수입니다.
DllMain함수는 다음과 같은 원형을 가집니다.
BOOL WINAPI DllMain(HINSTANCE hInst,DWORD dwReason,LPVOID lpRes);

hInst: DLL의 인스턴스 핸들
dwReason: 함수가 호출된 이유
lpRes는 예약되어 있고 사용되지 않습니다.

여기서 가장 중요한 인수는 dwReason으로 다음 4가지 값을 가집니다.
DLL_PROCESS_ATTACH: (프로세스 단위로) DLL이 로드될 때
DLL_PROCESS_DETACH: (프로세스 단위로) DLL이 언로드될 때
DLL_THREAD_ATTACH: (스레드 단위로) DLL이 로드될 때
DLL_THREAD_DETACH: (스레드 단위로) DLL이 언로드될 때

DllMain 함수는 대체로 다음과 같은 모양을 가집니다.

switch (dwReason)
{
case DLL_PROCESS_ATTACH:
    처리;
case DLL_PROCESS_DETACH:
    처리;
case DLL_THREAD_ATTACH:
    처리;
case DLL_THREAD_DETACH:
    처리;
}

초기화에 성공했으면 TRUE를 리턴하고, 실패했으면 FALSE를 리턴하면 되는데, 리턴값은 dwReason이 DLL_PROCESS_ATTACH일 때만 유효하며 다른 때는 무시됩니다.
일반적으로 DLL_PROCESS_ATTACH에 메모리 할당 같은 초기화 코드를, DLL_PROCESS_DETACH에 메모리 해제 같은 뒤처리 코드를 넣습니다.

다음은 DllMain에서 메모리를 할당하고 해제하는 예제입니다.




5. 묵시적 연결과 명시적 연결

DLL의 함수를 연결하는 방법에는 2가지가 있는데 아까 lib파일을 설명하는 곳에서 나왔던 묵시적 연결이라는 방법과 명시적 연결이라는 방법이 있습니다.
묵시적 연결은 위에서처럼 함수가 어떤 DLL에 있는지 밝히지 않고 쓰는 방법이고, 명시적 연결은 함수를 직접 로드하고 함수의 주소를 직접 얻어오는 방법입니다.
명시적 연결은 함수 포인터에 대한 개념도 필요하고, 코딩이 좀 귀찮지만 여러 유연한 처리가 가능하다는 장점이 있습니다.(dll이 없으면 받아온다던지 하는)

명시적 연결을 하는 절차는 다음과 같습니다.

1. LoadLibrary(혹은 이미 로드되어있다면 GetModuleHandle) 함수를 사용하여 DLL을 로드합니다.
2. GetProcAddress 함수로 원하는 함수의 주소를 얻습니다.
3. 얻어온 함수의 주소를 지져먹고 쪄먹고 구워먹고 삶아먹습니다.

다음은 예제입니다.



LoadLibrary 함수의 원형은 다음과 같습니다.
HMODULE LoadLibrary(LPCTSTR lpFileName);
HMODULE은 HINSTANCE와 동일한 자료형입니다. lpFileName이 지정하는 DLL을 로드해 성공하면 핸들을, 실패하면 NULL을 리턴합니다.

GetProcAddress 함수의 원형은 다음과 같습니다.
FARPROC GetProcAddress(HMODULE hModule,LPCSTR lpProcName);
hModule이 지정하는 DLL의 함수 중 lpProcName의 이름을 가진 것을 찾아서 그 포인터를 돌려줍니다.

즉, 다음과 같은 에러 처리가 가능합니다.

if (!(hMod=LoadLibrary(DLL)))
{
printf("DLL이 없는데요?\n");
}
if (!(proc=GetProcAddress(DLL,Name)))
{
printf("함수를 찾을 수 없다는데?\n");
}

이런 것은 묵시적 연결에서는 불가능합니다. 그렇다고 묵시적 연결이 안 좋은 것은 아닙니다. 특수한 경우를 제외하고는 묵시적 연결로 사용하는 것이 편하고 좋습니다.


6. 다른 언어와의 연결

DLL 을 사용할 만한 가장 현실적인 이유가 바로 이것입니다. 분명 C++는 강력하고 빠르지만 개발 기간이 너무 오래 걸립니다. 그래서 프로그램의 비주얼 부분은 VB 등의 언어로 만들고, 성능과 속도가 중요한 부분만 C++ DLL로 만들어 합치는 것입니다. 그러면 프로그램의 성능과 개발자의 편의가 동시에 만족될 수 있습니다.

여기서는 VB 6.0과 C#에 각각 연결시켜보겠습니다.


-VB-

프로젝트를 만들고 폼에 텍스트박스 2개와 버튼을 올려놓습니다. 모듈을 하나 만들어 다음 코드를 써넣습니다.

Declare Function addinteger Lib "dllexam1" (ByVal a As Long, ByVal b As Long) As Long

폼의 버튼 클릭 이벤트에는 다음 코드를 넣습니다.

Private Sub Command1_Click()
MsgBox addinteger(CLng(Text1.Text), CLng(Text2.Text))
End Sub

다음은 실행 결과입니다.



-C#-

프로젝트를 만들고 폼에 텍스트박스 2개와 버튼 하나를 올려놓고 폼에 다음과 같은 코드를 추가합니다. (System.Runtime.InteropServices 네임스페이스를 미리 using 선언해놓아야 합니다.)

[DllImport("dllexam1")]
public static extern int addinteger(int a, int b);

버튼 클릭 이벤트에 다음 코드를 추가합니다.

private void button1_Click(object sender, EventArgs e)
{
    MessageBox.Show(addinteger(int.Parse(textBox1.Text), int.Parse(textBox2.Text)).ToString());
}

실행 결과는 다음과 같습니다.





Visual C++ DLL (Dynamic Link Library) MFC

Visual C++ DLL (Dynamic Link Library) 사용 방법 정리



차  례

DLL (Dynamic Link Library)
DLL 종류
DLL 링크 방법
선택의 길
Regular DLL(일반 DLL) 작성
Extention DLL(확장 DLL) 작성
Implicit Linkage (암시적 연결) 사용
Delay Loading(지연 로딩)
Explicit Linkage(명시적 연결) 사용
DEF 파일(.def) - Module Definition File
DllMain() 함수
DLL 디버깅
DLL과 EXE project를 한 곳에서 작업






DLL (Dynamic Link Library)
DLL은 코드만 공유될 뿐 데이터는 독립적으로 관리 됨.
DLL은 자체 스택이 없기 때문에 호출한 프로그램의 스택 사용.
DLL에서 여러 프로세스가 메모리 공유하려면 Memory Mapped File 사용해야 함.





DLL 종류
1. Regular   DLL(일반 DLL) : C함수 형태로 작성되어 C++, 다른 언어 에서도 사용할 수 있는 범용 DLL
2. Extention DLL(확장 DLL) : C++로 작성하며 Visual C++에서만 사용 가능. (Class Export)





DLL 링크 방법
1. implicit linkage(암시적 연결)
.dll, .lib 파일 필요.
project에 lib 추가 후 header(.h) 정의하여 호출.

2. Delay Load (지연 로딩)
implicit linkage 와 explicit linkage의 혼합 방식.
특별한 경우에 사용될 수 있음.

3. explicit linkage(명시적 연결)
.dll 파일 경로 지정하여 사용.
LoadLibrary(), GetProcAddress(), FreeLibrary() 로 호출.

**.참고 : MFC DLL 링크 설정
MFC도 DLL 이므로 linkage  방식 설정 가능.
MFC project property의 "Configuration Properties > General > Use of MFC"에서
Use MFC in a Shared DLL 혹은 Use MFC in a Static Library 로 동적 링크 사용 여부 선택.  

**.참고 : .dll 파일 찾는 순서
1).dll을 사용하는 .exe 파일의 디렉토리
2)process의 현재 디렉토리
3)윈도우 시스템 디렉토리(C:\WINDOWS\system32)
4)윈도우 디렉토리(C:\WINDOWS)
5)환경변수 PATH에 지정된 디렉토리 

**. 참고 : extention DLL(확장 DLL) 과 explicit linkage(명시적 연결) 사용
별로 안 좋은 방법인가? 거의 안쓰이는 듯 하다. (시도 해봤는데 소스만 더럽히는 기분)

댓글에서 본 방법은
DLL의 class 안에 해당 클래스를 넘겨주는 static 팩토리 메소드 패턴을 구현하고 이 메소드를 노출시킨다.
EXE project 에 .dll을 배포하고 DLL class의 헤더를 include 한다.
LoadLibrary() 로 explicit linkage 한 후 GetProcAddress() 로 해당 메소드를 호출하여
DLL에 정의된 class를 리턴 받는다.

서핑으로 찾은 자세한 설명은 http://dolphin.ivyro.net/file/windows_api/dll_class.html 
<내용이 사라질까봐 저장 해둠>





선택의 길
언어에 상관 없이 하려면 (C/C++/Delphi/VB) Regular DLL(일반 DLL)
clss 사용 및 VC++ 에서만 사용할거라면 Extention DLL(확장 DLL)
많이 이용될 거라면 implicit linkage(암시적 연결)
프로그램에 특화된거라면 explicit linkage(명시적 연결)
특수한 경우 Delay Load(지연 로딩)
Extention DLL과 explicit linkage의 조합은 최악이다.
(개인적인 생각 : 너무 지저분해 보인다.)





Regular DLL(일반 DLL) 작성
1. DLL project 생성
MFC 의 경우 project 생성
"new project > MFC > MFC DLL" 로 project 생성.
"Application Settings" 에서 
"Regular DLL using shared MFC DLL" 혹은 "Regular DLL with MFC statically linked" 선택
Win32 의 경우 project 생성
"new project > Win32 > Win32 project" 로 project 생성.
"Application Settings" 에서
"DLL" 선택
("Static Library"는 컴파일시 exe 파일에 포함됨)
**. 참고 : Automation 설정
MFC DLL project 생성시
"Application Settings" 에서
"Additional features"의 Automation 을 체크할 경우
IDispatch 인터페이스를 구현하여 pointer 미지원 언어에서도 사용할 수 있게 한 기능.
Dispatch ID를 이용하여 함수 호출. 

2. DLL의 C 함수 작성
extern "C" __declspec(dllexport) int plus(int a, int b)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState()); //MFC에서 호출 가능하도록 설정
return a+b;
} 

보통 해더를 DLL 및 EXE(DLL 사용 프로그램) 양쪽에서 모두 사용하기 위해 다음과 같이 정의 한다. (관례적이 된듯)
//XXXX.h
#ifdef DLLBUILD
#define DLLEXPORT extern "C" __declspec(dllexport)
#else
#define DLLEXPORT extern "C" __declspec(dllimport)
#endif
DLLEXPORT int plus(int a, int b);
 

//XXXX.cpp
#define DLLEXPORT
#include "XXXX.h"
..





Extention DLL(확장 DLL) 작성
DLL에서 class 사용시 extention dll 임.

1. MFC의 경우
MFC dll은 MFC application에서만 사용 가능.
project 생성
"new project > MFC > MFC DLL" 로 project 생성.
"Application Settings" 에서 
"MFC extension DLL" 선택
DLL 작성
class 생성 및 
class 헤더에 AFX_EXT_CLASS 추가.
Test.h
#pragma once
class AFX_EXT_CLASS CTest
{
public:
CTest(void);
~CTest(void);
int plus(int a, int b);
};
Test.cpp
#include "StdAfx.h"
#include "Test.h"
CTest::CTest(void) {}
CTest::~CTest(void){}
int CTest::plus(int a, int b)
{
return a+b;
} 
implecit linkage (암시적 연결) 사용
.h, .dll, .lib 을 읽을 수 있게 위치 시킴.
#include "Test1.h"
#pragma comment(lib, "XXXXX.lib")

CTest test;
int ret = test.plus(1, 2) ;


2. Win32 의 경우
win32/MFC application 에서 사용 가능.
project 생성
"new project > Win32 > Win32 project" 로 project 생성.
"Application Settings" 에서
"DLL" 선택
("Static Library"는 컴파일시 exe 파일에 포함됨)
class 생성
Test1.h
#pragma once
#ifdef DLLEXPORT
#define CINTDLL __declspec(dllexport)
#else
#define CINTDLL __declspec(dllimport)
#endif
class CINTDLL CTest1
{
public:
CTest1(void);
~CTest1(void);
int plus(int a, int b);
};


Test1.cpp
#include "StdAfx.h"
#define DLLEXPORT
#include "Test1.h"
CTest1::CTest1(void) {}
CTest1::~CTest1(void){}
int CTest1::plus(int a, int b)
{
return a+b;
}
implecit linkage(암시적 연결) 사용
.dll, .lib 을 읽을 수 있게 위치 시킴.
#include "Test1.h"
#pragma comment(lib, "XXXXX.lib")

CTest test;
int ret = test.plus(1, 2); 


3. DLL 에서 CString 매개변수 사용의 문제
CString을 매개변수로 갖는 DLL의 method 사용시 해당 method를 찾지 못하는 컴파일 에러가 발생한다.
작성한 코드
//호출쪽 코드(MFC)CString str = _T("test");cls.configure(str);//DLL쪽 코드 (Extention DLL)void CTest::configure(CString& str){ //이런 저런 처리..
에러 내용
error LNK2001: unresolved external symbol "__declspec(dllimport) public: static void __cdecl CTest::configure(... 어쩌고 저쩌고..

원인은 CSttring의 실제 구현이 다르기 때문이다. (혹은 VC++ 버전에 따라 구현이 다를 수 있다.)
(MFC의 CString은 cstringt.h에 있고 Win32의 CString은 atlstr.h 이니 실제로 보면 다른듯..)

해결방법으론 CString 대신 LPCTSTR로 주고 받으면 되지만 가급적이면 char 같은 Native한 데이터형이나
STL을 사용하는게 나은 것 같다. (DLL에서 STL 사용은 Export 문제가 찝찝해질 수도 있지만 래핑한다면 뭐..)
어쩃든 꼭 CString을 써야 한다면 LPCTSTR 을 사용하는 방법이 있다.
수정된 코드
//호출쪽 코드(MFC)CString str = _T("test");cls.configure((LPCTSTR)str);//DLL쪽 코드 (Extention DLL)void CTest::configure(LPCTSTR str){ //이런 저런 처리..}  
다음은 동일한 현상을 겪은 분들의 포스팅
(난 이분들보다 3년이 늦었다.. 잃어버린 세월이여..)





Implicit Linkage (암시적 연결) 사용1. .dll, .lib 파일 복사 .dll : 글 상단이 "**.참고 : .dll 파일 찾는 순서" 참고.
.lib : "project property > Linker > General > Additional Library Directories" 에 해당 위치 지정
2. 라이브러리 추가
#pragma comment(lib, "XXXXX.lib")
혹은
"project property > Linker > Input > Additional Dependencies"에 .lib 파일 기술 
3. 해더 정의 및 사용
extern "C" __declspec(dllimport) int plus(int a, int b); //편의를 위해 .h 정의 및 배포...int ret = plus(1, 2); 



Delay Loading(지연 로딩)implicit linkage 와 explicit linkage의 혼합 방식으로실제 DLL 함수가 처음 호출될떄 DLL이 로딩된다. 사용방법은 implicit linkage 와 동일하며 project property 설정을 통해 Delay Loading을 설정한다. 설정 방법은 "exe project property > Link > Input > Additional Dependencies" 에 DelayImp.lib 추가. "exe project property > Link > Input > Delay Loaded DLLs" 에 .lib 파일명 추가.
**. 참고 : DLL Unload__FUnloadDelayLoadedDLL2() 함수 "exe project property > Linker > Advanced > Delay Loaded DLL" 에서 Support Unload(/DELAY:UNLOAD) 선택시 __FUnloadDelayLoadedDLL2("XXXXX.dll"); 를 통해 DLL Unload가 가능하다. 



Explicit Linkage(명시적 연결) 사용.dll 파일만 필요.
#include <iostream>#include <tchar.h>#include <windows.h>using namespace std;void printError(TCHAR *errMsg){TCHAR* lpOSMsg;FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpOSMsg, 0, NULL);_tprintf(_T("[ERROR] %s :: %s\n"), errMsg, lpOSMsg);LocalFree(lpOSMsg);}int main(){ setlocale(LC_ALL, ""); HMODULE hDLL; try { hDLL = LoadLibrary(_T("TestMFCDLL.dll")); if (hDLL == NULL) throw _T("LoadLibrary error"); typedef int (DLL)(int, int); DLL* funcDLL = (DLL*)GetProcAddress(hDLL, "plus"); //DLL* funcDLL = (int (*)(int, int))GetProcAddress(hDLL, "plus"); int ret = (*funcDLL)(1, 5); _tprintf(_T("ret = %d\n"), ret); } catch (TCHAR* errmsg) { printError(errmsg); } if (hDLL) FreeLibrary(hDLL);}    


DEF 파일(.def) - Module Definition File
.dll의 속성 정의 파일.
(.exe에선 더이상 사용되지 않으며 .dll 에서도 사라질 운명)
DLL프로젝트명.DEF 파일을 만들어 프로젝트에 포함시킨다.
ex]
LIBRARY DLLProject
EXPORTS
MyFunc1=fucntion1
MyFunc2=function2

LIBRARY는 프로젝트 명이며
EXPORTS 에서 function1 함수는 MyFunc1로 이름을 재정의 한다.

표현식]     EXPORTS 익스포트명[=내부함수명][@서수]
.def 파일 사용하지 않고 export 하는 방법
파일 맨 아래에 다음처럼 정의
#pragma comment(linker, "/export:함수명=_함수명@인자총바이트수")
ex)
DLL_METHOD DWORD WINAPI PerfOpen(LPCTSTR lpDevNames) {...}
DLL_METHOD DWORD WINAPI PerfClose(VOID) {...}

#pragma comment(linker, "/export:PerfOpen=_PerfOpen@4")
#pragma comment(linker, "/export:PerfClose=_PerfClose@0")


DllMain() 함수
DLL이 메모리에 처음 올라올때와 제거될때 호출됨.
int DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved);
hInstance : DLL의 인스턴스 핸들
dwReason  : 함수 호출 이유
DLL_PROCESS_ATTACH : implicit linkage인 경우 DLL 사용 프로그램 실행시를 의미한다.
      explicit linkage인 경우 LoadLibrary 실행시를 의미한다.
DLL_THREAD_ATTACH   : 스레드 생성시 마다 호출되어 스레드별 초기화를 수행할 수 있다.
                          첫 스레드는 DLL_PROCESS_ATTACH 로 전달된다.
DLL_THREAD_DETACH   : 스레드 종료시 마다 호출되어 스레드별 종료처리를 수행할 수 있다.
      DLL 로드 전에 생성된 스레드가 있을 경우 DLL_THREAD_DETACH 만
      전달 될 수 있으므로 초기화된 스레드인지 체크 필요.
DLL_PROCESS_DETACH : implicit linkage의 경우 DLL 사용 프로그램 종료시를 의미한다.
      explicit linkage의 경우 FreeLibrary 실행시를 의미한다.
lpReserved: TRUE면 implicit linkage, FALSE면 explicit linkage


DLL 디버깅
DLL project 디버깅(F5)시 Executable file name 에 DLL 호출 프로그램 지정.
또는
"project property > Debugging > Command" 에서 DLL 호출 프로그램 지정.

혹은 아래 "DLL과 EXE project를 한 곳에서 작업"의 방법으로 디버깅 한다.





DLL과 EXE project를 한 곳에서 작업
(여러개 project를 한곳에서 사용)
1. EXE project 에서 DLL project 추가 "File 메뉴 > Add > Existing Project..."
   (또는 "New Project..." 로 새로 생성)

2. Solution Explorer 에서 EXE project 이름 오른쪽 마우스 메뉴에서
   "Set as StartUp project" 선택. (지정된 project는 bold체로 표시됨)

3. 각 project property의 "General > Output Directory"를 같은 곳으로 맞추어 동일 폴더에
   .dll, .exe 생성하도록 설정.
   또는
   project property의 "Build Events > Post-Build Event" 에 직접 copy 명령등을 써서
    사용할 수 있다.
  


출처 : http://yamoe.tistory.com/160
출처 : 

1 2 3 4 5 6 7 8 9 10 다음


[위자드팩토리] XouClock

통계 위젯 (화이트)

9429
123
51743

[위자드팩토리] 블루씨