2014년 3월 23일 일요일


DLL(Dynamic-link library) MS에서 구현된 동적 라이브러리. 내부 프로그램이 불러서 쓸 수 있는 다양함 함수 제공



DLL 호출의 2가지 방식

묵시적 연결(Implicit linking) : proj에 import lib를 포함해야 하며, DLL을 로드하고 함수를 찾는다. 프로그램이 실행시 연결. dll이 이미 로드되었다면 사용 카운트를 1 증가시킨다. 자주 이용할 때 좋다.(=load time linking)
#pragma comment(lib."dll.lib");
extern "C" __declspec(dllimport) int add(int a, int b);



명시적 연결(Explicit linking) : 실행중 DLL호출. 필요시 DLL 함수를 명시하여 사용. 선택적 DLL호출로 상황에 따라 리소스 교체가 가능하다.프로그램에 특화 시 사용(=run time linking)
HMODULE hDll;
hDll = LoadLibrary("testDll.dll");

typedef int (DLL)(int, int);
DLL* funcDll = (Dll*)GetProcAddress(hDll, "plus");
int ret = (*funcDll)(1,5);


지연 로딩
함수를 실제 호출하기 전까지 dll을 로드하지 않는 기법. 암시적과 명시적의 장점을 둘다 가지고 있지만 첫 함수 호출시 조금 느리다.
프로젝트 속성 - 구성속성 - 링커 - 입력 - 지연 로드된 DLL



DLL에서 제공하는 함수를 선언하는 것을 __declspec()가 한다.

__declspec(extended-attribute) declarator

extended-attribute에 들어갈 인수
thread : TLS(Thread Local Storage)로 저장. 이 지정자가 붙은 변수는 해당 스레드에서만 사용 가능.
naked : assem을 사용하여 직접 prolog, epilog를 달고자 할 때 사용
dllimport : dll에 있는 데이터,오브젝트 함수를 임포트. 함수 사용을 선언
dllexport : dll에 있는 데이터, 오브젝트 함수를 익스포트, dll의 정보를 명시적으로 제공하는 역활



명시적 연결시 에러 처리

dll은 제대로 읽혀지지 않을 불확실성을 가지고 있다. 그러므로 dll을 확인하는 것이 중요
PetProcAddress, LoadLibary로 확인, 에러가 발생하면 GetLastError을 이용하여, 에러 값 확인



명시적 연결의 장점

1. 필요시 dll을 호출하여 사용하기 때문에 메모리와 리소스가 절약된다.
2. 경우에 따라 dll을 교체할 수 있으며, 호출할 함수도 문자열로 지정하여 선택할 수 있다.
3. 필요한 dll이 없는 경우에도 실행할 수 있다.
4. 클라이언트 프로그램의 시작이 빠르다.

단점 : dll사용전 메모리로 읽어와야 하므로 함수 호출이 느리며, 반복적인 루프에 명시적 연결을 사용하는 것은 바람직하지 않아. 암시적 연결을 사용한다.






DllMain() 메모리 처음 올라갈때와 종료시 호출된다.
int dllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
dwReason(함수호출 이유)
DLL_PROCESS_ATTACH : implicit의 경우 dll 사용 프로그램 실행시를 의미, explicit의 경우 load library실행시를 의미
DLL_THREAD_ATTACH : 스레드 생성시마다 호출 스레드별 초기화 수행
DLL_THREAD_DETACH : 스레드 종료시마다 호출 스레드별 종료 처리
DLL_PROCESS_DETACH : implicit의 경우 종료시의미, explicit의 경우 free library실행시 의미
lpReserved true(implicit) false(explicit)



DllMain에서 하지말아야 할 작업

1. LoadLibrary, LoadLibraryEx 호출. 데드락이나 크래시를 유발한다.
2. 다른 스레드와 동기화. 데드락을 유발한다.
3. 로더 락을 획득하려는 코드가 가지고 있는 동기화 오브젝트를 획득하려는 시도. 데드락을 유발한다.
4. CoInitializeEx를 사용한 COM 스레드 초기화. 특정 조건이 충족될 경우 이 함수는 LoadLibraryEx를 호출한다.
5. 레지스트리 함수들. 이 함수들은 advapi32.dll에 구현되어 있다. advapi32.dll이 초기화 되지 않았다면 크래시가 발생할 수 있다.
6. CreateProcess 호출. 프로세스 생성은 다른 DLL을 로드할 수 있다.
7. ExitThread 호출. DLL 디태치(detach) 과정 중에 스레드를 종료하면 로더 락을 다시 획득하도록 만들 수 있다. 이는 데드락이나 크래시가 유발된다.
8. CreateThread 호출. 동기화만 하지 않는다면 스레드 생성은 괜찮을 수 있다. 하지만 위험하다.
9. 네임드 파이프나 네임드 오브젝트 생성 (2000만 해당한다). 윈도우 2000에서 네임드 오브젝트 생성은 터미널 서비스 DLL에서 구현되어 있다. 해당 DLL이 초기화되어 있지 않다면 크래시.
10. CRT에 포함된 메모리 관리 함수들. CRT DLL이 초기화되어 있지 않다면 크래시.
11. user32.dll이나 gdi32.dll에 포함된 함수 호출. 일부 함수들은 다른 DLL을 로드하고, 이 사실은 크래시가 발생할 수 있다는 것을 의미한다.
12. 관리된 코드 사용.

즉, DllMain은 존재하지 않게~, 그래도 사용해야 한다면 Kernel32.dll에 포함된 함수중 위에 언급안된 것만 쓰자. 글로벌 오브젝트의 생성자 내지는 초기화 함수 부분에 위에 언급한 내용이 있어서는 안된다. 초기화를 지연시키자



cf) DLL_PROCESS_DEATCH 호출시점

호출시점은 프로세스 종료 시점, FreeLibrary호출하여 카운트 0 시점이 있다.
프로세스종료를 위해 TerminateProcess, ExitProcess를 사용할 수 도 있는데, 둘 다 NtTerminateProcess를 사용한다. 둘의 차이점은 ExitProcess는 NtTerminateProcess이후에 shutdown process작업을 수행한다.
TerminateProcess는 shutdownprocess가 존재 하지 않아 DLL_PROCESS_DETACH를 호출 받을 수 없다.
결론, DLL_PROCESS_DETACH호출 시점에는 Thread가 모두 죽은 시점, DLL_PROCESS_DETACH에서 복잡한 CleanUp작업은 나쁘다.

0 개의 댓글:

댓글 쓰기