링커의 지연 로드된 DLL 지원
MSVC 링커는 DLL의 지연된 로드를 지원합니다. 이 기능을 사용하면 Windows SDK 함수 LoadLibrary
GetProcAddress
를 사용하고 DLL 지연 로드를 구현할 필요가 없습니다.
지연된 로드가 없으면 런타임에 DLL을 로드하는 유일한 방법은 DLL을 사용하는 LoadLibrary
것입니다. GetProcAddress
DLL을 사용하는 실행 파일 또는 DLL이 로드될 때 운영 체제는 DLL을 로드합니다.
지연 로드를 사용하면 DLL을 암시적으로 연결할 때 링커는 프로그램이 해당 DLL의 함수를 호출할 때까지 DLL 로드를 지연하는 옵션을 제공합니다.
애플리케이션은 도우미 함수와 함께 (로드 가져오기 지연) 링커 옵션을 사용하여 /DELAYLOAD
DLL 로드를 지연시킬 수 있습니다. (기본 도우미 함수 구현은 Microsoft에서 제공합니다.) 도우미 함수는 런타임 시 요청 시 DLL을 호출 LoadLibrary
하여 로드합니다 GetProcAddress
.
다음과 같은 경우 DLL 로드를 지연하는 것이 좋습니다.
프로그램에서 DLL에서 함수를 호출하지 않을 수 있습니다.
DLL의 함수는 프로그램 실행이 늦게까지 호출되지 않을 수 있습니다.
EXE 또는 DLL 프로젝트를 빌드하는 동안 DLL의 지연된 로드를 지정할 수 있습니다. 하나 이상의 DLL의 로드를 지연하는 DLL 프로젝트는 지연 로드된 진입점을 DllMain
호출하면 안 됩니다.
지연 로드할 DLL 지정
링커 옵션을 사용하여 로드를 지연할 DLL을 /delayload:
dllname
지정할 수 있습니다. 사용자 고유의 도우미 함수 버전을 사용하지 않으려면 프로그램을 delayimp.lib
(데스크톱 애플리케이션용) 또는 dloadhelper.lib
(UWP 앱의 경우) 연결해야 합니다.
다음은 DLL 로드 지연의 간단한 예입니다.
// cl t.cpp user32.lib delayimp.lib /link /DELAYLOAD:user32.dll
#include <windows.h>
// uncomment these lines to remove .libs from command line
// #pragma comment(lib, "delayimp")
// #pragma comment(lib, "user32")
int main() {
// user32.dll will load at this point
MessageBox(NULL, "Hello", "Hello", MB_OK);
}
프로젝트의 디버그 버전을 빌드합니다. 디버거를 사용하여 코드를 단계별로 실행하면 user32.dll
호출 MessageBox
할 때만 로드됩니다.
지연 로드된 DLL의 명시적 언로드
/delay:unload
링커 옵션을 사용하면 코드가 로드 지연된 DLL을 명시적으로 언로드할 수 있습니다. 기본적으로 지연 로드된 가져오기는 IAT(가져오기 주소 테이블)에서 다시 기본. 그러나 링커 명령줄에서 사용하는 /delay:unload
경우 도우미 함수는 호출을 통해 __FUnloadDelayLoadedDLL2
DLL의 명시적 언로드를 지원하고 IAT를 원래 형식으로 다시 설정합니다. 이제 잘못된 포인터를 덮어씁니다. IAT는 원래 IAT의 복사본 주소가 포함된 구조체의 필드 ImgDelayDescr
입니다(있는 경우).
지연 로드된 DLL 언로드의 예
이 예제에서는 함수fnMyDll
를 포함하는 DLLMyDll.dll
을 명시적으로 언로드하는 방법을 보여 줍니다.
// link with /link /DELAYLOAD:MyDLL.dll /DELAY:UNLOAD
#include <windows.h>
#include <delayimp.h>
#include "MyDll.h"
#include <stdio.h>
#pragma comment(lib, "delayimp")
#pragma comment(lib, "MyDll")
int main()
{
BOOL TestReturn;
// MyDLL.DLL will load at this point
fnMyDll();
//MyDLL.dll will unload at this point
TestReturn = __FUnloadDelayLoadedDLL2("MyDll.dll");
if (TestReturn)
printf_s("\nDLL was unloaded");
else
printf_s("\nDLL was not unloaded");
}
지연 로드된 DLL 언로드에 대한 중요한 참고 사항:
MSVC
include
디렉터리에서 파일delayhlp.cpp
에서 함수의__FUnloadDelayLoadedDLL2
구현을 찾을 수 있습니다. 자세한 내용은 지연 로드 도우미 함수를 참조하세요.함수의 매개 변수는
name
가져오기 라이브러리에__FUnloadDelayLoadedDLL2
포함된 것과 정확히 일치해야 합니다(대/소문자 포함). (해당 문자열은 이미지의 가져오기 테이블에도 있습니다.) 를 사용하여DUMPBIN /DEPENDENTS
가져오기 라이브러리의 내용을 볼 수 있습니다. 대/소문자를 구분하지 않는 문자열 일치를 선호하는 경우 대/소문자를 구분하지 않는 CRT 문자열 함수 또는 Windows API 호출 중 하나를 사용하도록 업데이트__FUnloadDelayLoadedDLL2
할 수 있습니다.
지연 로드 가져오기 바인딩
기본 링커 동작은 지연 로드된 DLL에 대한 바인딩 가능한 IAT(가져오기 주소 테이블)를 만드는 것입니다. DLL이 바인딩된 경우 도우미 함수는 참조된 각 가져오기를 호출 GetProcAddress
하는 대신 바인딩된 정보를 사용하려고 시도합니다. 타임스탬프 또는 기본 설정 주소가 로드된 DLL의 주소와 일치하지 않는 경우 도우미 함수는 바인딩된 가져오기 주소 테이블이 만료된 것으로 가정합니다. IAT가 없는 것처럼 진행됩니다.
DLL의 지연 로드 가져오기를 바인딩하지 않으려는 경우 링커 명령줄에 지정 /delay:nobind
합니다. 링커는 바인딩된 가져오기 주소 테이블을 생성하지 않으므로 이미지 파일의 공간이 절약됩니다.
지연 로드된 DLL의 모든 가져오기 로드
정의된 delayhlp.cpp
함수는 __HrLoadAllImportsForDll
링커 옵션으로 지정된 DLL에서 모든 가져오기를 로드하도록 링커에 /delayload
지시합니다.
모든 가져오기를 한 번에 로드하면 오류 처리를 한 곳에서 중앙 집중화할 수 있습니다. 가져오기에 대한 모든 실제 호출을 중심으로 구조적 예외 처리를 방지할 수 있습니다. 또한 애플리케이션이 프로세스를 통해 부분적으로 실패하는 상황을 방지합니다. 예를 들어 도우미 코드가 가져오기를 로드하지 못하는 경우, 다른 사용자를 성공적으로 로드한 후입니다.
호출 __HrLoadAllImportsForDll
은 후크 및 오류 처리의 동작을 변경하지 않습니다. 자세한 내용은 오류 처리 및 알림을 참조하세요.
__HrLoadAllImportsForDll
는 DLL 자체 내에 저장된 이름과 대/소문자를 구분하여 비교합니다.
다음은 명명된 DLL을 로드하기 위해 호출된 TryDelayLoadAllImports
함수에서 사용하는 __HrLoadAllImportsForDll
예제입니다. 함수 CheckDelayException
를 사용하여 예외 동작을 확인합니다.
int CheckDelayException(int exception_value)
{
if (exception_value == VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND) ||
exception_value == VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND))
{
// This example just executes the handler.
return EXCEPTION_EXECUTE_HANDLER;
}
// Don't attempt to handle other errors
return EXCEPTION_CONTINUE_SEARCH;
}
bool TryDelayLoadAllImports(LPCSTR szDll)
{
__try
{
HRESULT hr = __HrLoadAllImportsForDll(szDll);
if (FAILED(hr))
{
// printf_s("Failed to delay load functions from %s\n", szDll);
return false;
}
}
__except (CheckDelayException(GetExceptionCode()))
{
// printf_s("Delay load exception for %s\n", szDll);
return false;
}
// printf_s("Delay load completed for %s\n", szDll);
return true;
}
결과를 TryDelayLoadAllImports
사용하여 가져오기 함수를 호출할지 여부를 제어할 수 있습니다.
오류 처리 및 알림
프로그램에서 지연 로드된 DLL을 사용하는 경우 오류를 강력하게 처리해야 합니다. 프로그램이 실행되는 동안 발생하는 오류로 인해 처리되지 않은 예외가 발생합니다. DLL 지연 로드 오류 처리 및 알림에 대한 자세한 내용은 오류 처리 및 알림을 참조하세요.
지연 로드된 가져오기 덤프
지연 로드 가져오기는 .를 사용하여 DUMPBIN /IMPORTS
덤프할 수 있습니다. 이러한 가져오기는 표준 가져오기와 약간 다른 정보로 표시됩니다. 목록의 /imports
자체 섹션으로 분리되며 지연 로드 가져오기로 명시적으로 레이블이 지정됩니다. 이미지에 언로드 정보가 있는 경우 기록됩니다. 바인딩 정보가 있는 경우 대상 DLL의 시간 및 날짜 스탬프는 가져오기의 바인딩된 주소와 함께 표시됩니다.
지연 로드 DLL에 대한 제약 조건
DLL 가져오기의 지연 로드에는 몇 가지 제약 조건이 있습니다.
데이터 가져오기는 지원되지 않습니다. 해결 방법은 지연 로드 도우미가 DLL
GetProcAddress
을 로드한 것을 알고 나면 사용LoadLibrary
하거나 사용하여GetModuleHandle
데이터 가져오기를 명시적으로 처리하는 것입니다.지연 로드
Kernel32.dll
는 지원되지 않습니다. 지연 로드 도우미 루틴이 작동하려면 이 DLL을 로드해야 합니다.전달된 진입점의 바인딩 은 지원되지 않습니다.
DLL이 시작 시 로드되지 않고 지연 로드되는 경우 프로세스의 동작이 다를 수 있습니다. 지연 로드된 DLL의 진입점에서 발생하는 프로세스별 초기화가 있는지 확인할 수 있습니다. 다른 경우로는 DLL을 통해
LoadLibrary
로드될 때 처리되지 않는 정적 TLS(스레드 로컬 스토리지)를 사용하여__declspec(thread)
선언됩니다.TlsAlloc
,TlsFree
,TlsGetValue
및TlsSetValue
를 사용하는 동적 TLS는 정적 또는 지연 로드된 DLL에 계속해서 사용할 수 있습니다.각 함수의 첫 번째 호출 후 가져온 함수에 정적 전역 함수 포인터를 다시 초기화합니다. 함수 포인터의 첫 번째 사용은 로드된 함수가 아니라 thunk를 가리키기 때문에 필요합니다.
현재 일반 가져오기 메커니즘을 사용하는 동안 DLL에서 특정 프로시저의 로드만 지연할 수 있는 방법은 없습니다.
사용자 지정 호출 규칙(예: x86 아키텍처에서 조건 코드 사용)은 지원되지 않습니다. 또한 부동 소수점 레지스터는 플랫폼에 저장되지 않습니다. 사용자 지정 도우미 루틴 또는 후크 루틴이 부동 소수점 형식을 사용하는 경우 주의하세요. 루틴은 부동 소수점 매개 변수를 사용하여 등록 호출 규칙을 사용하는 컴퓨터에서 전체 부동 소수점 상태를 저장하고 복원해야 합니다. 특히 도움말 함수의 NDP(숫자 데이터 프로세서) 스택에서 부동 소수점 매개 변수를 사용하는 CRT 함수를 호출하는 경우 CRT DLL의 지연 로드에 주의해야 합니다.
지연 로드 도우미 함수 이해
링커 지원 지연 로드에 대한 도우미 함수는 실제로 런타임에 DLL을 로드합니다. 도우미 함수를 수정하여 동작을 사용자 지정할 수 있습니다. 제공된 도우미 함수 delayimp.lib
를 사용하는 대신 사용자 고유의 함수를 작성하고 프로그램에 연결합니다. 하나의 도우미 함수는 로드된 모든 지연 DLL을 제공합니다. 자세한 내용은 지연 로드 도우미 함수 이해 및 사용자 고유의 도우미 함수 개발을 참조하세요.
참고 항목
피드백
https://aka.ms/ContentUserFeedback
출시 예정: 2024년 내내 콘텐츠에 대한 피드백 메커니즘으로 GitHub 문제를 단계적으로 폐지하고 이를 새로운 피드백 시스템으로 바꿀 예정입니다. 자세한 내용은 다음을 참조하세요.다음에 대한 사용자 의견 제출 및 보기