Windows API를 활용한 DLL 동적 로딩 및 함수 호출
Windows 프로그래밍에서 LoadLibrary와 GetProcAddress는 DLL(동적 링크 라이브러리)을 동적으로 로드하고 그 안의 함수를 실행하는 핵심 API입니다. 이를 통해 프로그램은 실행 중에 필요할 때마다 DLL을 메모리에 적재하고 원하는 함수를 호출할 수 있습니다. 이 기능은 플러그인 시스템 구현, 모듈화된 소프트웨어 설계, 혹은 비공개 API 호출 등 다양한 용도로 사용됩니다.
이 문서에서는 이러한 API들의 기본 개념과 실용적인 사용 방법을 설명합니다. 특히, 쉬운 언어(Easy Language) 환경에서 이를 어떻게 적용할 수 있는지 단계별로 다루겠습니다.
1. 핵심 개념
LoadLibrary함수: 지정된 DLL 파일을 프로세스 메모리에 적재하고, 해당 DLL의 모듈 핸들을 반환합니다. 적재 실패 시에는NULL을 반환합니다.
HMODULE LoadLibrary(LPCSTR lpFileName);
여기서 lpFileName은 DLL 파일 경로입니다(예: "kernel32.dll").
GetProcAddress함수: 이미 적재된 DLL에서 특정 함수의 주소를 얻습니다. 함수가 존재하지 않으면NULL을 반환합니다.
FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);
여기서 hModule은 LoadLibrary가 반환한 핸들이고, lpProcName은 함수 이름입니다(예: "MessageBoxA").
쉬운 언어에서는 자체적으로 이러한 저수준 호출을 지원하지 않으므로, 외부 DLL 명령을 선언하여 사용해야 합니다.
2. 쉬운 언어에서 API 선언하기
쉬운 언어에서는 "DLL 명령" 기능을 사용해 외부 API를 선언할 수 있습니다. 다음은 표준 선언 방식입니다:
- 쉬운 언어 IDE를 열고 새 프로그램을 생성합니다.
- "DLL 명령 정의 테이블"에 아래 선언을 추가합니다:
.DLL명령 LoadLibrary, 숫자형, "kernel32.dll", "LoadLibraryA"
.매개변수 lpFileName, 문자열형
.DLL명령 GetProcAddress, 숫자형, "kernel32.dll", "GetProcAddress"
.매개변수 hModule, 숫자형
.매개변수 lpProcName, 문자열형
.DLL명령 FreeLibrary, 논리형, "kernel32.dll", "FreeLibrary"
.매개변수 hModule, 숫자형
참고:
LoadLibraryA는 ANSI 문자열을 처리하는 버전입니다(쉬운 언어의 문자열은 기본적으로 ANSI로 간주됨).- 리턴 타입은
숫자형으로 설정됩니다(윈도우에서 핸들과 함수 포인터는 기본적으로 숫자로 처리됨). - 마지막에 반드시
FreeLibrary를 호출하여 리소스 누수를 방지해야 합니다.
3. DLL 함수 동적 로딩 및 호출 과정
다음은 user32.dll의 MessageBoxA 함수를 호출하는 예제입니다.
단계 1: DLL 로딩
DLL을 로드하고 핸들을 얻습니다. 이후 반환값을 확인하여 성공 여부를 판단합니다.
.지역변수 hModule, 숫자형
hModule = LoadLibrary("user32.dll")
.만약 (hModule == 0)
알림창("DLL 로딩 실패!", , )
돌아가기
.끝만약
단계 2: 함수 주소 획득
GetProcAddress를 사용해 함수 주소를 가져옵니다.
.지역변수 funcAddr, 숫자형
funcAddr = GetProcAddress(hModule, "MessageBoxA")
.만약 (funcAddr == 0)
알림창("함수 찾기 실패!", , )
FreeLibrary(hModule)
돌아가기
.끝만약
단계 3: 함수 호출
함수 포인터를 실제로 호출 가능한 형태로 변환합니다. 이를 위해 함수 원형을 선언해야 합니다.
.DLL명령 MessageBoxA, 숫자형, "user32.dll", "MessageBoxA"
.매개변수 hwnd, 숫자형
.매개변수 text, 문자열형
.매개변수 caption, 문자열형
.매개변수 type, 숫자형
.지역변수 result, 숫자형
result = 함수호출(funcAddr, 0, "DLL로부터의 메시지!", "쉬운 언어 예제", 0)
단계 4: 리소스 해제
더 이상 DLL이 필요 없을 때 FreeLibrary를 호출하여 핸들을 해제합니다.
FreeLibrary(hModule)
4. 완전한 코드 예제
아래는 쉬운 언어로 작성된 전체 코드입니다. user32.dll을 동적으로 로드하고 MessageBoxA를 호출하는 과정을 보여줍니다.
.프로그램집합 시작프로그램
.프로그램집합변수 dllHandle, 숫자형
.프로그램집합변수 funcPointer, 숫자형
.DLL명령 LoadLibrary, 숫자형, "kernel32.dll", "LoadLibraryA"
.매개변수 fileName, 문자열형
.DLL명령 GetProcAddress, 숫자형, "kernel32.dll", "GetProcAddress"
.매개변수 moduleHandle, 숫자형
.매개변수 funcName, 문자열형
.DLL명령 FreeLibrary, 논리형, "kernel32.dll", "FreeLibrary"
.매개변수 moduleHandle, 숫자형
.서브루틴 초기화
dllHandle = LoadLibrary("user32.dll")
.만약 (dllHandle == 0)
알림창("user32.dll 로딩 실패!", , )
돌아가기
.끝만약
funcPointer = GetProcAddress(dllHandle, "MessageBoxA")
.만약 (funcPointer == 0)
알림창("MessageBoxA 함수 찾기 실패!", , )
FreeLibrary(dllHandle)
돌아가기
.끝만약
' 함수 원형 선언
.DLL명령 MessageBoxA, 숫자형, ,
.매개변수 handle, 숫자형
.매개변수 message, 문자열형
.매개변수 title, 문자열형
.매개변수 flags, 숫자형
' 함수 호출
함수호출(funcPointer, 0, "동적 로딩 성공!", "쉬운 언어 예제", 0)
' 리소스 해제
FreeLibrary(dllHandle)
5. 주의사항 및 최상의 관행
- 오류 처리: 항상
LoadLibrary와GetProcAddress의 반환 값을 검사하세요. 실패 시GetLastError를 사용해 오류 코드를 확인할 수 있습니다. - 리소스 해제: 더 이상 DLL이 필요 없을 때 반드시
FreeLibrary를 호출하세요. 그렇지 않으면 메모리 누수가 발생할 수 있습니다. - 함수 서명 일치:
GetProcAddress로 얻은 함수 포인터의 매개변수와 리턴 값은 목표 함수와 반드시 일치해야 합니다. 불일치 시 프로그램이 충돌할 수 있습니다. - 보안 문제: 신뢰할 수 없는 경로의 DLL을 적재하지 마세요. 절대 경로를 사용하는 것이 권장됩니다. 또한 쉬운 언어는 기본적으로 32비트 응용 프로그램을 생성하므로, 호환성을 고려해 적절한 아키텍처의 DLL을 선택해야 합니다.