DLL이란?

이 문서에서는 DLL(동적 연결 라이브러리) 및 DLL 사용 시에 발생할 수 있는 여러 가지 문제에 대해 설명합니다. 그리고 DLL을 직접 개발할 때 고려해야 하는 몇 가지 고급 문제에 대해 설명합니다.

적용 대상: Windows 10 - 모든 버전
원본 KB 번호: 815065

요약

DLL을 설명하는 부분에서는 동적 연결 방법, DLL 종속성, DLL 진입점, DLL 함수 내보내기, DLL 문제 해결 도구에 대해서도 설명합니다.

그리고 마지막으로 DLL과 Microsoft .NET Framework 어셈블리를 대략적으로 비교합니다.

Windows 운영 체제의 경우 운영 체제의 많은 기능이 DLL에서 제공됩니다. 또한 이와 같은 Windows 운영 체제 중 하나에서 프로그램을 실행할 때는 대부분의 프로그램 기능이 DLL을 통해 제공될 수 있습니다. 예를 들어 일부 프로그램은 서로 다른 모듈을 많이 포함할 수 있으며, 프로그램의 각 모듈은 DLL에 포함되어 배포됩니다.

DLL을 사용하면 효율적으로 코드를 모듈화하고 재사용할 수 있으며, 메모리 사용 효율성을 높이고 사용되는 디스크 공간을 줄일 수 있습니다. 따라서 운영 체제와 프로그램이 더 빠르게 로드 및 실행되며 컴퓨터에서 더 적은 디스크 공간을 차지합니다.

프로그램이 DLL을 사용할 때는 종속성이라는 문제로 인해 프로그램이 실행되지 않을 수 있습니다. 프로그램이 DLL을 사용하는 경우 종속성이 작성됩니다. 다른 프로그램에서 이 종속성을 덮어써서 손상시키는 경우에는 원래 프로그램이 정상적으로 실행되지 않을 수 있습니다.

.NET Framework가 도입됨에 따라 어셈블리를 사용하게 되어 대부분의 종속성 문제는 해결되었습니다.

추가 정보

DLL은 여러 프로그램에서 동시에 사용할 수 있는 코드와 데이터를 포함하는 라이브러리입니다. 예를 들어 Windows 운영 체제에서 Comdlg32 DLL은 일반적인 대화 상자 관련 기능을 수행합니다. 각 프로그램은 이 DLL에 포함된 기능을 사용해 열기 대화 상자를 구현할 수 있습니다. 이를 통해 코드를 쉽게 재사용할 수 있으며 메모리 사용 효율성을 높일 수 있습니다.

DLL을 사용하면 프로그램을 여러 개별 구성 요소로 모듈화할 수 있습니다. 회계 프로그램이 모듈 단위로 판매되는 경우를 예로 들 수 있습니다. 각 모듈은 설치되어 있는 경우 런타임에 주 프로그램으로 로드할 수 있습니다. 모듈은 분리되어 있으므로 프로그램의 로드 시간이 더 빨라집니다. 그리고 모듈은 해당 기능이 요청될 때만 로드됩니다.

또한 프로그램의 다른 부분에 영향을 주지 않고 업데이트를 각 모듈에 더 쉽게 적용할 수 있습니다. 급여 프로그램을 사용하고 있는데 세율이 매년 변경되는 경우를 예로 들어 보겠습니다. 이러한 변경 내용이 DLL 하나에만 적용되는 경우 전체 프로그램을 다시 빌드하거나 설치할 필요 없이 업데이트를 적용할 수 있습니다.

아래 목록에서는 Windows 운영 체제에서 DLL로 구현되는 일부 파일에 대해 설명합니다.

  • ActiveX 컨트롤(.ocx) 파일

    ActiveX 컨트롤의 예로는 달력에서 날짜를 선택할 수 있는 달력 컨트롤이 있습니다.

  • 제어판(.cpl) 파일

    .cpl 파일의 예로는 제어판에 있는 항목이 있습니다. 각 항목은 특수 DLL입니다.

  • 장치 드라이버(.drv) 파일

    장치 드라이버의 예로는 프린터로 인쇄하는 작업을 제어하는 프린터 드라이버가 있습니다.

DLL의 이점

아래 목록에서는 프로그램에서 DLL을 사용할 때 제공되는 몇 가지 이점에 대해 설명합니다.

  • 더 적은 리소스 사용

    여러 프로그램이 같은 함수 라이브러리를 사용하는 경우 DLL을 사용하면 디스크와 실제 메모리에서 로드되는 중복 코드를 줄일 수 있습니다. 그러면 전경에서 실행되는 프로그램의 성능뿐 아니라 Windows 운영 체제에서 실행되는 다른 프로그램의 성능도 크게 향상될 수 있습니다.

  • 모듈식 아키텍처 활용

    DLL을 사용하면 모듈식 프로그램을 효율적으로 개발할 수 있습니다. 즉, 여러 언어 버전이 필요한 큰 프로그램이나 모듈식 아키텍처가 필요한 프로그램을 개발할 수 있습니다. 모듈식 프로그램의 예로는 런타임에 동적으로 로드할 수 있는 여러 모듈이 포함된 회계 프로그램이 있습니다.

  • 손쉬운 배포와 설치

    DLL 내의 함수를 업데이트하거나 수정해야 하는 경우 DLL을 배포하고 설치할 때 프로그램을 DLL과 다시 연결하지 않아도 됩니다. 또한 여러 프로그램이 같은 DLL을 사용하는 경우에는 모든 프로그램에 업데이트나 수정 내용이 적용됩니다. 이 문제는 정기적으로 업데이트되거나 수정되는 타사 DLL을 사용할 때 보다 자주 발생할 수 있습니다.

DLL 종속성

프로그램이나 DLL이 다른 DLL의 DLL 함수를 사용하는 경우 종속성이 작성됩니다. 이 경우 해당 프로그램은 더 이상 자체 포함 프로그램이 아니며, 종속성이 손상되면 프로그램에 문제가 발생할 수 있습니다. 예를 들어 다음 작업 중 하나를 수행하면 프로그램이 실행되지 않을 수 있습니다.

  • 종속 DLL을 새 버전으로 업그레이드하는 경우
  • 종속 DLL을 수정하는 경우
  • 종속 DLL을 이전 버전으로 덮어쓰는 경우
  • 종속 DLL을 컴퓨터에서 제거하는 경우

이러한 작업을 DLL 충돌이라고 합니다. 이전 버전과의 호환성이 적용되지 않으면 프로그램이 정상적으로 실행되지 않을 수 있습니다.

아래 목록에서는 종속성 문제를 최소화하기 위해 Windows 2000 및 이후 Windows 운영 체제에 도입된 변경 내용에 대해 설명합니다.

  • Windows 파일 보호

    Windows 파일 보호에서는 운영 체제가 권한이 없는 에이전트의 시스템 DLL 업데이트 또는 삭제를 차단합니다. 프로그램 설치 시 시스템 DLL로 정의된 DLL 제거 또는 업데이트를 시도하는 경우 Windows 파일 보호에서는 유효한 디지털 서명을 찾습니다.

  • 전용 DLL

    전용 DLL을 사용하면 공유 DLL에 적용된 변경 내용이 적용되지 않도록 프로그램을 격리할 수 있습니다. 프라이빗 DLL은 버전별 정보 또는 빈 .local 파일을 사용하여 프로그램에서 사용하는 DLL 버전을 적용합니다. 전용 DLL을 사용하려면 프로그램 루트 폴더에 DLL을 저장합니다. 그런 다음 새 프로그램에 대해 버전별 정보를 DLL에 추가합니다. 이전 프로그램의 경우에는 빈 .local 파일을 사용합니다. 각 메서드는 프로그램 루트 폴더에 있는 전용 DLL을 사용하도록 운영 체제에 지시합니다.

DLL 문제 해결 도구

여러 가지 도구를 사용하여 DLL 문제를 해결할 수 있습니다. 이러한 도구 중 몇 가지가 아래에 나와 있습니다.

Dependency Walker

Dependency Walker 도구는 프로그램에서 사용하는 모든 종속 DLL을 재귀적으로 검사할 수 있습니다. Dependency Walker에서 프로그램을 열면 다음 확인이 수행됩니다.

  • 누락된 DLL을 확인합니다.
  • 유효하지 않은 프로그램 파일이나 DLL을 확인합니다.
  • 가져오기 함수와 내보내기 함수가 일치하는지 확인합니다.
  • 순환 종속성 오류를 확인합니다.
  • 유효하지 않은 모듈(다른 운영 체제용 모듈)을 확인합니다.

Dependency Walker를 사용하면 프로그램이 사용하는 모든 DLL을 문서로 작성할 수 있습니다. 그러면 나중에 발생할 수 있는 DLL 문제를 예방하고 해결할 수 있습니다. Dependency Walker는 Visual Studio 6.0을 설치할 때 다음 디렉터리에 설치됩니다.

drive\Program Files\Microsoft Visual Studio\Common\Tools

DLL Universal Problem Solver

DLL Universal Problem Solver(DUPS)는 DLL 정보를 감사, 비교, 문서화 및 표시하는 데 사용됩니다. 아래 목록에서는 DUPS 도구를 구성하는 유틸리티에 대해 설명합니다.

  • Dlister.exe

    이 유틸리티는 컴퓨터의 모든 DLL을 열거하고 텍스트 파일이나 데이터베이스 파일에 정보를 기록합니다.

  • Dcomp.exe

    이 유틸리티는 두 텍스트 파일에 나와 있는 DLL을 비교하여 두 파일 간의 차이점을 포함하는 세 번째 텍스트 파일을 생성합니다.

  • Dtxt2DB.exe

    이 유틸리티는 Dlister.exe 유틸리티 및 Dcomp.exe 유틸리티를 사용하여 만든 텍스트 파일을 dllHell 데이터베이스에 로드합니다.

  • DlgDtxt2DB.exe

    이 유틸리티는 Dtxt2DB.exe 유틸리티의 GUI(그래픽 사용자 인터페이스) 버전을 제공합니다.

DLL Help 데이터베이스

DLL Help 데이터베이스를 사용하면 Microsoft 소프트웨어 제품에 의해 설치되는 DLL의 특정 버전을 찾을 수 있습니다.

DLL 개발

이 섹션에서는 DLL을 직접 개발할 때 고려해야 하는 문제와 요구 사항에 대해 설명합니다.

DLL의 유형

응용 프로그램에서 DLL을 로드할 때는 두 가지 연결 방법을 통해 내보낸 DLL 함수를 호출할 수 있습니다. 이 두 가지 연결 방법은 로드 시간 동적 연결 및 런타임 동적 연결입니다.

로드 시간 동적 연결

로드 시간 동적 연결 시에 응용 프로그램은 내보낸 DLL 함수를 로컬 함수처럼 명시적으로 호출합니다. 로드 시간 동적 연결을 사용하려면 응용 프로그램을 컴파일하고 연결할 때 헤더(.h) 파일과 가져오기 라이브러리(.lib) 파일을 제공합니다. 이렇게 하면 링커가 DLL을 로드하고 로드 시간에 내보낸 DLL 함수 위치를 확인하는 데 필요한 정보를 시스템에 제공합니다.

런타임 동적 연결

런타임 동적 연결에서는 응용 프로그램이 LoadLibrary 함수 또는 LoadLibraryEx 함수를 호출하여 런타임에 DLL을 로드합니다. DLL이 정상적으로 로드되고 나면 GetProcAddress 함수를 사용하여 호출하려는 내보낸 DLL 함수의 주소를 가져올 수 있습니다. 런타임 동적 연결 사용 시에는 가져오기 라이브러리 파일이 필요하지 않습니다.

아래 목록에서는 로드 시간 동적 연결과 런타임 동적 연결을 각각 사용해야 하는 응용 프로그램 기준이 나와 있습니다.

  • 시작 성능

    응용 프로그램의 초기 시작 성능이 중요한 경우에는 런타임 동적 연결을 사용해야 합니다.

  • 손쉬운 사용

    로드 시간 동적 연결에서는 내보낸 DLL 함수가 로컬 함수처럼 작동합니다. 따라서 이러한 함수를 쉽게 호출할 수 있습니다.

  • 응용 프로그램 논리

    런타임 동적 연결에서는 응용 프로그램이 분기되어 필요에 따라 여러 모듈을 로드할 수 있습니다. 여러 언어 버전을 개발할 때는 이러한 기능이 중요합니다.

DLL 진입점

DLL을 만들 때는 필요에 따라 진입점 함수를 지정할 수 있습니다. 진입점 함수는 프로세스나 스레드가 DLL에 연결되거나 DLL에서 분리될 때 호출됩니다. 진입점 함수를 사용하면 DLL에서 필요한 대로 데이터 구조를 초기화하거나 제거할 수 있습니다. 또한 다중 스레드 응용 프로그램의 경우에는 TLS(스레드 로컬 저장소)를 사용하여 진입점 함수의 각 스레드 전용으로 사용되는 메모리를 할당할 수 있습니다. 아래에는 DLL 진입점 함수의 코드 예제가 나와 있습니다.

BOOL APIENTRY DllMain(
HANDLE hModule,// Handle to DLL module
DWORD ul_reason_for_call,// Reason for calling function
LPVOID lpReserved ) // Reserved
{
    switch ( ul_reason_for_call )
    {
        case DLL_PROCESS_ATTACHED: // A process is loading the DLL.
        break;
        case DLL_THREAD_ATTACHED: // A process is creating a new thread.
        break;
        case DLL_THREAD_DETACH: // A thread exits normally.
        break;
        case DLL_PROCESS_DETACH: // A process unloads the DLL.
        break;
    }
    return TRUE;
}

진입점 함수가 FALSE 값을 반환하는 경우 로드 시간 동적 연결을 사용 중이면 응용 프로그램이 시작되지 않습니다. 런타임 동적 연결을 사용 중이라면 개별 DLL만 로드되지 않습니다.

진입점 함수는 단순한 초기화 작업만 수행해야 하며, 다른 DLL 로드 또는 종료 함수를 호출해서는 안 됩니다. 예를 들어 진입점 함수에서 LoadLibrary 함수나 LoadLibraryEx 함수를 직접 또는 간접적으로 호출해서는 안 됩니다. 또한 프로세스를 종료할 때 FreeLibrary 함수를 호출해서도 안 됩니다.

참고

다중 스레드 응용 프로그램에서는 데이터 손상 가능성을 방지하기 위해 DLL 전역 데이터 액세스가 동기화되는지, 즉 스레드로부터 안전한지 확인해야 합니다. 이렇게 하려면 TLS를 사용하여 각 스레드에 대해 고유한 데이터를 제공합니다.

DLL 함수 내보내기

DLL 함수를 내보내려는 경우 내보내는 DLL 함수에 함수 키워드를 추가하거나 내보내는 DLL 함수가 나열된 모듈 정의(.def) 파일을 만들 수 있습니다.

함수 키워드를 사용하려면 다음 키워드를 통해 내보내려는 각 함수를 선언해야 합니다.
__declspec(dllexport)

애플리케이션에서 내보낸 DLL 함수를 사용하려면 다음 키워드(keyword) 사용하여 가져올 각 함수를 선언해야 합니다.__declspec(dllimport)

일반적으로는 define 문과 ifdef 문이 있는 헤더 파일 하나를 사용하여 내보내기 문과 import 문을 구분합니다.

모듈 정의 파일을 사용하여 내보내는 DLL 함수를 선언할 수도 있습니다. 모듈 정의 파일을 사용할 때는 내보내는 DLL 함수에 대해 함수 키워드를 추가하지 않아도 됩니다. 모듈 정의 파일에서는 DLL에 대해 LIBRARY 문과 EXPORTS 문을 선언합니다. 아래에는 정의 파일의 코드 예제가 나와 있습니다.

// SampleDLL.def
//
LIBRARY "sampleDLL"
EXPORTS HelloWorld

샘플 DLL 및 응용 프로그램

Visual C++ 6.0에서는 Win32 동적 연결 라이브러리 프로젝트 형식 또는 MFC AppWizard(dll) 프로젝트 형식 중 하나를 선택해 DLL을 만듭니다.

다음 코드는 Win32 동적 연결 라이브러리 프로젝트 형식을 사용해 Visual C++에서 생성된 DLL의 예시입니다.

// SampleDLL.cpp
//

#include "stdafx.h"
#define EXPORTING_DLL
#include "sampleDLL.h"
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
)
{
    return TRUE;
}

void HelloWorld()
{
    MessageBox( NULL, TEXT("Hello World"), TEXT("In a DLL"), MB_OK);
}

// File: SampleDLL.h
//
#ifndef INDLL_H
    #define INDLL_H
    #ifdef EXPORTING_DLL
        extern __declspec(dllexport) void HelloWorld();
    #else
        extern __declspec(dllimport) void HelloWorld();
    #endif

#endif

다음 코드는 SampleDLL DLL에서 내보낸 DLL 함수를 호출하는 Win32 Application 프로젝트의 예제입니다.

// SampleApp.cpp
//
#include "stdafx.h"
#include "sampleDLL.h"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HelloWorld();
    return 0;
}

참고

로드 시간 동적 연결에서는 SampleDLL 프로젝트를 빌드할 때 작성되는 SampleDLL.lib 가져오기 라이브러리를 연결해야 합니다.

런타임 동적 연결에서는 다음 코드와 비슷한 코드를 사용하여 SampleDLL.dll 내보낸 DLL 함수를 호출합니다.

...
typedef VOID (*DLLPROC) (LPTSTR);
...
HINSTANCE hinstDLL;
DLLPROC HelloWorld;
BOOL fFreeDLL;

hinstDLL = LoadLibrary("sampleDLL.dll");
if (hinstDLL != NULL)
{
    HelloWorld = (DLLPROC) GetProcAddress(hinstDLL, "HelloWorld");
    if (HelloWorld != NULL)
        (HelloWorld);
    fFreeDLL = FreeLibrary(hinstDLL);
}
...

SampleDLL 응용 프로그램을 컴파일하고 연결하면 Windows 운영 체제는 다음 위치에서 아래와 같은 순서로 SampleDLL DLL을 검색합니다.

  1. 응용 프로그램 폴더

  2. 현재 폴더

  3. Windows 시스템 폴더

    참고

    GetSystemDirectory 함수는 Windows 시스템 폴더의 경로를 반환합니다.

  4. Windows 폴더

    참고

    GetWindowsDirectory 함수는 Windows 폴더의 경로를 반환합니다.

.NET Framework 어셈블리

.NET 및 .NET Framework가 도입됨에 따라 어셈블리를 사용함으로써 DLL과 관련된 대부분의 문제는 해결되었습니다. 어셈블리는 .NET CLR(공용 언어 런타임)의 제어 하에서 실행되는 기능의 논리적 단위입니다. 어셈블리는 물리적인 .dll 파일이나 .exe 파일입니다. 그러나 내부적으로 볼 때 어셈블리는 Microsoft Win32 DLL과는 다릅니다.

어셈블리 파일은 어셈블리 매니페스트, 형식 메타데이터, MSIL(Microsoft Intermediate Language) 코드 및 기타 리소스를 포함합니다. 어셈블리 매니페스트에는 어셈블리의 자체 설명에 필요한 모든 정보를 제공하는 어셈블리 메타데이터가 포함되어 있습니다. 어셈블리 매니페스트에 포함된 정보는 다음과 같습니다.

  • 어셈블리 이름
  • 버전 정보
  • 문화권 정보
  • 강력한 이름 정보
  • 파일의 어셈블리 목록
  • 형식 참조 정보
  • 참조되는/종속 어셈블리 정보

어셈블리에 포함된 MSIL 코드는 직접 실행할 수 없습니다. 대신 CLR을 통해 MSIL 코드 실행을 관리합니다. 기본적으로 어셈블리를 만들 때는 어셈블리가 응용 프로그램 전용으로 지정됩니다. 공유 어셈블리를 만들려면 어셈블리에 강력한 이름을 할당한 다음 전역 어셈블리 캐시에 어셈블리를 게시해야 합니다.

아래 목록에서는 Win32 DLL의 기능과 비교한 어셈블리의 몇 가지 기능에 대해 설명합니다.

  • 자체 설명

    어셈블리를 만들 때는 CLR에서 어셈블리를 실행하는 데 필요한 모든 정보가 어셈블리 매니페스트에 포함됩니다. 어셈블리 매니페스트는 종속 어셈블리 목록을 포함합니다. 따라서 CLR은 응용 프로그램에서 사용되는 일관성 있는 어셈블리 집합을 유지할 수 있습니다. Win32 DLL에서는 공유 DLL을 사용할 때 응용 프로그램에서 사용되는 DLL 집합 간의 일관성을 유지할 수 없습니다.

  • 버전 관리

    어셈블리 매니페스트에서는 버전 정보가 CLR을 통해 기록되고 적용됩니다. 또한 버전 정책을 통해 버전별 사용 방식을 적용할 수도 있습니다. Win32 DLL에서는 운영 체제가 버전 관리를 적용할 수 없습니다. DLL이 이전 버전과 호환되는지 확인해야 합니다.

  • 병렬 배포

    어셈블리는 병렬 배포를 지원합니다. 즉, 응용 프로그램별로 각기 다른 버전의 어셈블리를 사용할 수 있습니다. Windows 2000부터는 응용 프로그램 폴더에 DLL을 저장하면 병렬 배포가 지원됩니다. 또한 Windows 파일 보호에서는 권한이 없는 에이전트의 시스템 DLL 덮어쓰기 또는 바꾸기를 차단합니다.

  • 자체 포함 및 격리

    어셈블리를 사용하여 개발하는 응용 프로그램은 자체 포함 방식으로 컴퓨터에서 실행되는 다른 응용 프로그램으로부터 격리할 수 있습니다. 이 기능을 통해 다른 응용 프로그램에는 전혀 영향을 주지 않고 설치를 할 수 있습니다.

  • 실행

    어셈블리는 어셈블리 매니페스트에서 제공되며 CLR을 통해 제어되는 보안 권한으로 실행됩니다.

  • 언어 독립적

    어셈블리는 지원되는 .NET 언어 중 하나를 사용하여 개발할 수 있습니다. 예를 들어 어셈블리를 Microsoft Visual C#에서 개발하고 Visual Basic .NET 프로젝트에서 사용할 수 있습니다.

데이터 수집

Microsoft 지원의 지원이 필요한 경우 배포 관련 문제에 TSS를 사용하여 정보 수집에 설명된 단계에 따라 정보를 수집하는 것이 좋습니다.

참조