링커의 지연 로드된 DLL 지원

MSVC 링커는 DLL의 지연된 로드를 지원합니다. 이 기능을 사용하면 Windows SDK 함수 LoadLibraryGetProcAddress 를 사용하고 DLL 지연 로드를 구현할 필요가 없습니다.

지연된 로드가 없으면 런타임에 DLL을 로드하는 유일한 방법은 DLL을 사용하는 LoadLibrary 것입니다. GetProcAddressDLL을 사용하는 실행 파일 또는 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 가져오기의 지연 로드에는 몇 가지 제약 조건이 있습니다.

  • 데이터 가져오기는 지원되지 않습니다. 해결 방법은 지연 로드 도우미가 DLLGetProcAddress을 로드한 것을 알고 나면 사용 LoadLibrary 하거나 사용하여 GetModuleHandle 데이터 가져오기를 명시적으로 처리하는 것입니다.

  • 지연 로드 Kernel32.dll 는 지원되지 않습니다. 지연 로드 도우미 루틴이 작동하려면 이 DLL을 로드해야 합니다.

  • 전달된 진입점의 바인딩 은 지원되지 않습니다.

  • DLL이 시작 시 로드되지 않고 지연 로드되는 경우 프로세스의 동작이 다를 수 있습니다. 지연 로드된 DLL의 진입점에서 발생하는 프로세스별 초기화가 있는지 확인할 수 있습니다. 다른 경우로는 DLL을 통해 LoadLibrary로드될 때 처리되지 않는 정적 TLS(스레드 로컬 스토리지)를 사용하여 __declspec(thread)선언됩니다. TlsAlloc, TlsFree, TlsGetValueTlsSetValue를 사용하는 동적 TLS는 정적 또는 지연 로드된 DLL에 계속해서 사용할 수 있습니다.

  • 각 함수의 첫 번째 호출 후 가져온 함수에 정적 전역 함수 포인터를 다시 초기화합니다. 함수 포인터의 첫 번째 사용은 로드된 함수가 아니라 thunk를 가리키기 때문에 필요합니다.

  • 현재 일반 가져오기 메커니즘을 사용하는 동안 DLL에서 특정 프로시저의 로드만 지연할 수 있는 방법은 없습니다.

  • 사용자 지정 호출 규칙(예: x86 아키텍처에서 조건 코드 사용)은 지원되지 않습니다. 또한 부동 소수점 레지스터는 플랫폼에 저장되지 않습니다. 사용자 지정 도우미 루틴 또는 후크 루틴이 부동 소수점 형식을 사용하는 경우 주의하세요. 루틴은 부동 소수점 매개 변수를 사용하여 등록 호출 규칙을 사용하는 컴퓨터에서 전체 부동 소수점 상태를 저장하고 복원해야 합니다. 특히 도움말 함수의 NDP(숫자 데이터 프로세서) 스택에서 부동 소수점 매개 변수를 사용하는 CRT 함수를 호출하는 경우 CRT DLL의 지연 로드에 주의해야 합니다.

지연 로드 도우미 함수 이해

링커 지원 지연 로드에 대한 도우미 함수는 실제로 런타임에 DLL을 로드합니다. 도우미 함수를 수정하여 동작을 사용자 지정할 수 있습니다. 제공된 도우미 함수 delayimp.lib를 사용하는 대신 사용자 고유의 함수를 작성하고 프로그램에 연결합니다. 하나의 도우미 함수는 로드된 모든 지연 DLL을 제공합니다. 자세한 내용은 지연 로드 도우미 함수 이해 및 사용자 고유의 도우미 함수 개발을 참조하세요.

참고 항목

Visual Studio에서 C/C++ DLL 만들기
MSVC 링커 참조