'DLL'에 해당되는 글 6건

  1. 2010.02.11 DLL에서 WndProc 메시지 처리하는 방법
  2. 2009.04.27 DllMain
  3. 2009.04.24 DLL 제작 (Explicit)
  4. 2009.04.23 DLL 제작 (Implicit 연결)
  5. 2009.04.23 DLL 이란...
  6. 2008.02.01 CVI에서 Dynamic Link Library(DLL) 만들기
2010. 2. 11. 18:04

DLL에서 WndProc 메시지 처리하는 방법



응용 프로그램에서 특정 DLL을 사용할 때, 해당하는 DLL에서 윈도우즈 메시지를 처리해야 할 경우가 있다.
한참을 고민했네.. 메시지를 후킹해서 필요한 녀석만 처리를 하게끔 해야하나?? -_-;

의외로 간단한 방법이 있었다.

그것을 '서브클래싱(SubClassing)' 이라고 부르는 것 같다.

DLL의 초기화 하는 적절한 부분에 다음과 같이 서브클래싱을 해준다.
본인의 경우 다음과 같이...

WNDPROC oldProc;
...

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
...
switch(msg)
{
...
}
return CallWindowProc(oldProc, hwnd, msg, wp, lp);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
...
switch(fdwReason)
{
case DLL_PROCESS_ATTACH :
...
oldProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG)WndProc);
}
}

SetWindowLongPtr() 함수를 통해 메시지를 처리할 윈도우 프로시저를 기존의 응용프로그램의 것에서 DLL의
것으로 옮긴다.
DLL 내부에 만들어 놓은 윈도우 프로시저(WndProc)에서 처리하고자 하는 메시지만 처리하도록 하고 그 외의 것은
CallWindowProc()함수를 통해 기존의 응용프로그램의 윈도우 프로시저가 처리하도록 한다.



2009. 4. 27. 11:43

DllMain



DLL은 함수들의 집합이므로 함수에 대한 정의만 있으면 되고 일반적인 실행파일의 WinMain과 같은 메인함수가 꼭 있어야 하는것은 아니다. 엄밀히 말하자면 DLL도 반드시 Main이라는 엔트리포인트가 필요하지만, VC++를 사용할 경우 C런타임이 엔트리 포인트를 대신 제공하기 때문에 없어도 상관이 없다는 것이다.

DLL이 단순한 함수의 집합이 아니라면 DLL의 엔트리 포인트 DllMain이 필요하다. 전역변수가 있다거나 동적으로 메모리를 할당해 사용한다거나 할 때 초기화와 종료 처리를 위해 필요하다.

BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID lpRes);

hInst는 DLL의 인스턴스 핸들이다.
fdwReason은 이 함수가 호출된 이유를 지정한다.(일종의 통지 메시지)
  DLL_PROCESS_ATTACH
  - DLL이 프로세스의 주소공간에 맵핑될 때 호출된다.
    주로 메모리를 할당하거나 시스템 전역 핸들을 초기화하는 용도로 사용된다.
  DLL_PROCESS_DETACH
  - DLL이 프로세스의 주소 공간에서 분리될 때 호출된다.
    할당한 메모리를 해제하거나 시스템 전역 핸들을 파괴하는 용도로 사용된다.
  DLL_THREAD_ATTACH
  - DLL을 사용하는 프로세스에서 스레드를 생성할 때마다 이 값과 함께 DllMain함수가 호출된다.
    DLL에서는 이 값을 받았을 때 스레드별 초기화를 수행한다.
  DLL_THREAD_DETACH
  - DLL을 사용하는 프로세스에서 스레드가 종료될 때마다 이 값과 함께 DllMain 함수가 호출된다.
    DLL에서는 이 값을 받았을 때 스레드별 종료 처리를 한다.
lpRes는 DLL의 연결 방식을 지정한다.
  TRUE - 암시적 연결
  FALSE - 명시적 연결

BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID lpRes)
{
    switch(fdwReason) {
        case DLL_PROCESS_ATTACH:
            ...
            break;

        case DLL_PROCESS_DETACH:
            ...
            break;

        case DLL_THREAD_ATTACH:           
            ...
            break;

        case DLL_THREAD_DETACH:
            ...
            break;
    }
    return TRUE;
}

이런 식으로 구현을 해주면 된다.
해당하는 통지 메시지가 전달 될 때 필요한 작업을 넣어준다. 가만히 보고 있자면 하는 일이 WndProc와 비슷한것 같다면서.. ^^;


'Programmings > Windows Programming' 카테고리의 다른 글

GetWindowThreadProcessId()  (0) 2009.05.18
GetModuleFileNameEx()  (0) 2009.05.15
DLL 제작 (Explicit)  (0) 2009.04.24
DLL 제작 (Implicit 연결)  (0) 2009.04.23
DLL 이란...  (0) 2009.04.23
2009. 4. 24. 15:02

DLL 제작 (Explicit)



DLL을 명시적(Explicit)으로 연결하기 위한 방법이다.
명시적(Explicit) 연결이라는 부분에 대해서는 프로그램이 실행되고 있는 도중에 필요한 함수가 포함된 DLL을 로드하는 방식이라고 보면 될 듯하다.

명시적인 연결을 위한 DLL을 제작할 때 중요한 함수들이 있다.
HINSTANCE LoadLibrary(LPCTSTR lpLibFileName);
지정한 DLL을 메모리로 읽어와 현재 프로세스의 주소공간에 맵핑시켜 사용할 수 있도록 한다. 하지만, 이미 다른 프로세스에서 해당 DLL을 사용하고 있다면, DLL을 메모리로 또다시 올리는 것이 아니고 DLL 의 사용 카운트만 1 증가시킨다.
인수는 읽고자 하는 DLL 의 파일 이름이다. 파일의 경로를 지정할 수도 있고, 지정하지 않을 수도 있다. 파일의 경로를 지정하지 않으면 DLL을 찾는 순서에 따라 경로를 뒤져가며 해당 DLL을 찾게 된다.

FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);
DLL에서 엑스포트한 함수의 번지를 찾아 그 함수의 함수 포인터를 리턴한다.
첫 번째 인수는 함수가 포함된 DLL의 모듈 핸들이며 LoadLibrary() 가 리턴한 값이다.
두 번째 인수는 찾고자 하는 함수 이름이다.
이 함수가 리턴한 함수포인터를 사용하여 DLL에 있는 함수를 호출한다.

BOOL FreeLibrary(HMODULE hLibModule);
DLL의 사용 카운트를 1 감소시키며 사용 카운트가 0이 되었을 경우 메모리에서 DLL을 삭제한다.
첫 번째 인수는 LoadLibrary()가 리턴한 DLL의 모듈 핸들이다. LoadLibrary()로 DLL을 읽어왔다면 반드시 프로세스를 종료하기 전에 FreeLibrary()를 호출하여 DLL을 해제해야 한다.


DLL 제작
DLL 제작 방법은 아래를 참조하면 될듯.. -_-;;
http://todayis.tistory.com/206

DLL Test 프로그램(명시적 연결) 제작
새로운 Win32 프로젝트를 생성하고, WndProc 소스 부분을 아래와 같이 굵은 글자로 된 부분을 추가한다.
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    HINSTANCE hInst;
    int(*pFunc)(int, int);
    TCHAR str[128];

    switch (iMessage) {
    case WM_CREATE:
        hWndMain=hWnd;
        return 0;
    case WM_PAINT:
        hdc=BeginPaint(hWnd, &ps);
        hInst = LoadLibrary(L"DLL_test.dll");
        pFunc = (int(*)(int, int))GetProcAddress(hInst, "AddInteger");
        wsprintf(str, L"1+2 = %d", (*pFunc)(1, 2));
        TextOut(hdc, 10, 10, str, lstrlen(str));
        FreeLibrary(hInst);
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

컴파일.. 하고 실행을 하면.. 전에 설명했던 암시적(Implicit) 연결과 동일한 결과임을 알 수 있다.

단지 명시적 연결의 경우 원하는 위치에서 DLL을 직접 호출하는 구문이 있다는 것이다. 위의 경우에는 WM_PAINT
DLL을 사용하는 프로그램이 실행될 때 DLL이 같이 로드되는 것이 아니기 때문에 Settings에 따로 XXX.lib를 지정할 필요가 없다. 또 함수포인터로 함수가 있는 번지를 직접 조사한 후 그 주소를 이용하기 때문에 함수의 원형같은 것도 필요가 없다.
어떻게 보면, 명시적인 연결을 사용하는 DLL 이 더 쉬운듯 하다.

추가로 에러를 위한 처리구문을 추가하자.
LoadLibrary() 그리고 GetProcAddress() 등의 함수를 사용하는 부분을 아래와 같이 처리해 주면 된다.
if((hInst = LoadLibrary(L"DLL_test.dll")) == NULL) {
    MessageBox("There are no DLLs!!!");
    // 추가적인 에러 처리
    return 0;
}
if((pFunc = (int(*)(int, int))GetProcAddress(hInst, "AddInteger")) == NULL) {
    MessageBox("Where is The Function what I want to?? ");
    // 추가적인 에러처리
    return 0;
}


명시적(Explicit) 연결의 장점
1. 필요할 때만 DLL을 읽어와 사용하므로 메모리와 리소스가 절약된다.
2. 상황에 따라 사용할 DLL을 교체할 수 있다.
3. 필요한 DLL이 없는 경우라도 프로그램을 실행할 수는 있다. DLL을 사용하는 부분에서는 정상적인 동작을 안하더라도...
4. DLL을 호출하는 프로그램의 시작 실행속도가 빠르다.

명시적(Explicit) 연결의 단점
1. 사용하기 전에 DLL을 메모리로 읽어오므로 함수 호출 속도가 느리다.

  • 윈도우즈 API 완전정복 참고...


'Programmings > Windows Programming' 카테고리의 다른 글

GetModuleFileNameEx()  (0) 2009.05.15
DllMain  (0) 2009.04.27
DLL 제작 (Implicit 연결)  (0) 2009.04.23
DLL 이란...  (0) 2009.04.23
아스키 코드표  (0) 2009.03.03
2009. 4. 23. 22:17

DLL 제작 (Implicit 연결)



DLL 제작
단순히 DLL 파일을 생성하는 방법이다. DLL은 단독으로 실행될 수 없으므로 이 파일을 가지고 테스트를 할 수는 없다.(테스트 프로그램을 따로 제작할 것이다.)

1. Visual Studio를 실행한다.(본인의 경우 Visual Studio 2005 임)
2. File -> New -> Project..
3. New Project 위자드
   - Visual C++ 탭에서 Win32를 선택하고, 템플릿으론 Win32 Project를 선택한다.
   - Name, Location, Solution Name는 알아서 대충 넣어준다..
4. 대충 Next 버튼을 클릭하며, Application Settings에서는  DLL을 선택한다.
5. Finish 버튼 클릭!!
6. 새프로젝트가 만들어지면, .cpp파일을 하나 생성하고, 테스트용으로 간단한 내용을 입력한다.
// 윈도우즈 API 완전정복의 코드임..
// a, b를 입력받아 a, b의 합을 리턴하는 코드임
// 다른 프로그램에서 이 함수를 사용할 수 있도록 extern "C"__declspec를 사용..
extern "C"__declspec(dllexport) int AddInteger(int a, int b)
{
    return a+b;
}

7. 이 프로젝트를 컴파일하면 해당하는 .dll과 .lib 파일이 생성된다.


DLL 테스트 프로그램 제작
Implicit(암시적), Explicit(묵시적) 연결을 사용할 수 있다.

Implicit(암시적) 연결
MyDllTest라는 API 프로젝트를 만들고 다래 코드를 추가한다.

// DLL  함수를 위해 추가해야 하는 부분..
extern "C"__declspec(dllimport) int AddInteger(int, int);
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    TCHAR str[128];

    switch (iMessage) {
    case WM_CREATE:
        hWndMain=hWnd;
        return 0;
    case WM_PAINT:
        hdc=BeginPaint(hWnd, &ps);
        wsprintf(str, "1+2 = %d", AddInteger(1, 2));
        TextOut(hdc, 10, 10, str, lstrlen(str));
        EndPaint(hWnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
       return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}


다음은 사용하고자 하는 DLL의 임포트 라이브러리를 프로젝트에 포함시키는 작업을 해주어야 한다.
메뉴 탭의 Project -> XXXProperties.. (이미지 클릭 하셈!!! ㅋ)


마지막으로 .DLL 파일과 .lib 파일을 MyDllTest 프로젝트 디렉토리로 복사한다.
참고로 .DLL과 .lib 파일은 위에서 DLL 제작의 컴파일 결과물로 생성된 녀석들이다.


이 방법 말고도, DLL을 사용하려는 소스의 윗부분에 #pragma 문을 사용할 수도 있다.
#pragma comment(lib,"XXXDLL.lib") 를 추가하면 된다.
#pragma 지시자를 사용하면 컴파일러의 라이브러리 관리 모듈에게 XXXDLL.lib를 검색하도록 하게 하므로 위의 과정에서 보인것과 같은 설정 대화상자나 파일(.dll, .lib) 추가 없이 소스에서 곧바로 임포트 라이브러리를 지정할 수 있다.


그리고 컴파일... 후 실행하면..



추가로  DLL의 헤더파일을 정의해서 깔끔하게 DLL을 작성하는 방법이다.

위와 같이 함수의 내용이 얼마 되지 않는다면 그냥 사용할 수 있겠지만, DLL에 추가되는 함수의 수가 많아지게 되면, 선언의 꺼리(extern "C"__declspec..와 같은)가 그만큼 많아질 것이다.
헤더파일을 하나 만들어 선언을 모두 옮겨 놓는 방법도 하나의 해결 방안이 될 듯..

DLL의 프로젝트에 헤더파일 하나를 만든다.
그리고 아래와 같은 내용을 추가한다.

//XXX.h

#ifdef DLLEXPORT
#define MYDLLTYPE __declspec(dllexport)
#else
#define MYDLLTYPE __declspec(dllimport)
#endif

extern "C" MYDLLTYPE int AddInteger(int a, int b);

추가로 DLL에 함수를 추가하고자 한다면, 함수의 선언 부분을 extern "C" .. 의 아래에 추가시켜주면 될 것이다.

DLL의 .cpp 파일을 열어서 다음의 두줄을 추가한다.

#define DLLEXPORT             // DLL Export임을 선언(??)
#include "XXX.h"

이 두줄을 추가시키므로 인해서, DLL은 export 하는 용도임을 정의한다.

테스트 프로그램은 단지 XXX.h파일을 추가시켜준다.
어떠한 정의가 따로 되어있지 않으므로, __declspec(dllimport)로 인식할 것이다.


여기까지!!

이게 DLL을 사용하는 가장 간편한 방법이라고 하는데..
다른 방법들은.. 다음 이시간에.. !!!


'Programmings > Windows Programming' 카테고리의 다른 글

DllMain  (0) 2009.04.27
DLL 제작 (Explicit)  (0) 2009.04.24
DLL 이란...  (0) 2009.04.23
아스키 코드표  (0) 2009.03.03
프로그램 실행 에러 - MFC71D.DLL을(를) 찾을 수 없으므로...  (5) 2009.02.05
2009. 4. 23. 18:30

DLL 이란...



DLL(Dynamic Link)라는 것.. 동적 링크라고도 하는 이넘은... 컴파일시 함수의 코드가 실행 파일에 그대로 복사되는 것이아니고, 실행되는 파일이 실행 될 때 필요로 하는 함수가 실행 파일에 연결되는 형식이다. 실행파일은 단지 호출할 함수의 정보만 포함되고 실제 함수코드는 복사되지 않기 때문에 실행 파일의 크기가 작아진다. 하지만 실행시킬 루틴이 필요하기 때문에 실행파일은 실행시킬 함수에 대한 정보를 가지고 있어야하고, 실행시킬 함수는 DLL 파일의 형태로 존재하게 된다.

장점
1. 한 코드를 여러 프로그램이 같이 사용하기 때문에 메모리가 절약된다.
2. DLL을 사용하는 프로그램은 실행파일 자체에 해당 함수가 포함되지 않기에 크기가 작다.
3. 함수의 루틴을 바꾸고자(업그레이드) 할 경우 해당하는 DLL만 수정하면 된다.
...
이외에도 많은 장점들이 있는듯함..

단점
1. DLL 없이 실행프로그램 자체만으론 동작을 안한다. -_-;
2. DLL 이 교체되거나 하면, 프로그램이 정상동작을 안할 수도 있다.
... 등등..

DLL 연결 방법
1. 암시적(Implicit) 연결

함수가 어느  DLL에 있는지 밝히지 않고 그냥 사용을 하는 방법이다. DLL을 사용하려는 프로젝트는 해당 dll과 lib를 포함하고 있어야 하고, 윈도우즈는 임포트 라이브러리의 정보를 참조해서 해당 DLL을 로드하고 함수를 찾게 된다.
프로그램이 실행될 때 DLL이 로드되므로 실행시 연결(Load time Linking)이라고 한다.
2. 명시적(Explicit) 연결
어느 DLL에 있는 함수인지를 밝히고 사용하는 방법이다. 프로그램이 실행될 때 DLL이 로드되는 것이 아니라 로드하라는 명령이 있을 때만 해당 DLL이 로드된다. 함수가 속해있는 DLL의 이름을 명시적으로 지정하여 호출하므로 임포트 라이브러리는 필요하지 않다.
프로그램이 실행 중에 DLL이 메모리로 읽혀지기 때문에 실행중 연결(Run Time Linking)이라고 한다.

2008. 2. 1. 17:23

CVI에서 Dynamic Link Library(DLL) 만들기



이번엔 LabWindows/CVI에서 Dynamic Link Labrary(DLL)을 만드는 방법이다!!
아무래도 Static Library보다는 Dynamic Link Labrary이 사용하기에도 더 편리하고 더 많이 사용하는 듯 하기에...

현재 만드는 DLL이니 LIB니 하는 것들은 다음번에 다룰 CVI로 작성하게 될 MP3 Player를 만들기 위해 사용되는 라이브러리이다. 뭐 직접 하지 않아도 상관은 없지만.. 하나하나 직접 따라 해보는 것이 좋지 않을까? 생각한다.

이번 예제도 앞서 이야기 했던 'Static Labrary'와 마찬가지로 'CVI 정보나눔'의 강좌를 토대로 작성을 했고, KD_MCI_sl_source.zip파일을 이용하여 DLL파일을 작성하게 된다.

DLL을 만들기 위해 하나의 프로젝트(KD_MCI.prj)를 생성한다. 물론 다른 이름으로 프로젝트를 생성해도 전혀 상관이 없다. 위의 첨부파일의 압축을 해제하고, 파일들을 프로젝트에 포함시킨다.
참고로, 편의를 위해서 파일명을 KD_MCI_sl.*에서 KD_MCI.*로 바꾸어 준다. 안해도 상관은 없음... -_-
사용자 삽입 이미지
Static Library를 만들 때와 다른 점이라면, 프로젝트에 winmm.lib가 포함된다는 점이다.

다음 해주어야 하는 일은 각각의 소스파일에 약간의 수정을 해주어야 하는 부분이 있다.
파일 이름을 수정해 주었으므로 include문도 수정을 해주고, Export할 변수 이름과 함수 앞에 DLLEXPORT라는 것을 추가시켜준다.
Export한다는 것은 해당 DLL을 이용하는 다른 프로그램이 해당 변수나 함수를 이용할 수 있도록 하겠다는 의미이다.
사용자 삽입 이미지
참고로... 위 그림에 나오는 부분만 바꾸는 것이 아니고 죄다~ 싹 다 바꾸는 거다!!

다음은... DLL중 가장 중요한 함수를 작성해야 한다.
DLL main이라고.. 다른 main함수와 마찬가지로 DLL의 시작점을 나타내는 함수이다.
'KD_MCIPLAY DLLEXPORT kmp; 다음에 커서를 위치시키고, Edit > Insert Construct > DLLMain을 선택하자.
사용자 삽입 이미지

그러면, 아래와 같이 DLLMain과 관련된 부분이 생성되는 것을 확인할 수 있다.
사용자 삽입 이미지
추가로 맨위의 #define KD_MCI_C는 KD_MCI.h 파일에서 전역변수 kmp에 대한 선언을 DLL만들 때와 DLL사용할 때를 구분하기 위해 정의한 매크로이다.

다음으로 KD_MCI.h파일을 수정하자.
별건 없고, 아래의 붉은 부분으로 된 부분을 수정해 주면 된다.
사용자 삽입 이미지
잘은 모르지만.. extern KD_MCIPLAY DLLIMPORT kmp;를 해주어야 한다고 한다. DLL을 만들 때는 선언되지 않도록 하고, 나중에 DLL을 사용하기 위한 프로그램의 프로젝트에서는 적용되어 사용을 할 수 있게 된다고 한다.
.c에서 define으로 정의해 두고, 정의가 되지 않으면 사용하도록 해서 그런가?? -_-
다음은 DLL Project 파일 속성을 설정하는 부분이다.
Build > Configuration > Release를 선택하고,
사용자 삽입 이미지

Build > Target Type > Dynamic Link Library를 선택,
사용자 삽입 이미지

Build > Target Settings... 에서 DLL Export Option을 설정한다.
사용자 삽입 이미지

Build > Create Release Dynamic Link Library 메뉴를 선택하여, DLL을 생성한다.
생성된 DLL은 프로젝트가 있는 디렉토리를 확인하면 생성된 파일을 볼 수 있을 것이다.
사용자 삽입 이미지

생성된 파일들은 Static Library와 비슷하다.

참고 : CVI 정보나눔

'Programmings > CVI' 카테고리의 다른 글

CVI 시리얼 통신 예제  (0) 2008.04.16
CVI Ring control의 간단한 예  (0) 2008.04.11
CVI에서 Static Library 만들기  (0) 2008.02.01
CVI에서 컨트롤을 동적으로 생성하기  (0) 2008.01.24
GetActiveCtrl() 함수  (0) 2008.01.24