Описание dll библиотек windows

From Wikipedia, the free encyclopedia

The Microsoft Windows operating system and Microsoft Windows SDK support a collection of shared libraries that software can use to access the Windows API. This article provides an overview of the core libraries that are included with every modern Windows installation, on top of which most Windows applications are built.

The Windows operating system contains compiled versions of these libraries known as dynamically-linked libraries (.dll), which are executable libraries that can be used by multiple programs while only one copy of the library is loaded into memory. These are canonically referred to as system libraries and all programs installed on the system can utilize them.

The Windows SDK additionally distributes compiled versions of these libraries known as statically-linked libraries (.lib), which are non-executable libraries that, in whole or in part, can be embedded into a program when it is compiled. The most common Windows compilers being Microsoft Visual Studio and MinGW.

Internal components

[edit]

HAL.DLL is a kernel-mode library file and it cannot be used by any user-mode program. NTDLL.DLL is only used by some programs, but it is a dependency of most Win32 libraries used by programs.

The Hardware Abstraction Layer in the architecture of Windows NT

The Windows Hardware Abstraction Layer (HAL) is implemented in hal.dll.[1] The HAL implements a number of functions that are implemented in different ways by different hardware platforms, which in this context, refers mostly to the chipset. Other components in the operating system can then call these functions in the same way on all platforms, without regard for the actual implementation.

For example, responding to an interrupt is quite different on a machine with an Advanced Programmable Interrupt Controller (APIC) than on one without. The HAL provides a single function for this purpose that works with all kinds of interrupts by various chipsets, so that other components need not be concerned with the differences.

The HAL is loaded into kernel address space and runs in kernel mode, so routines in the HAL cannot be called directly by applications, and no user mode APIs correspond directly to HAL routines. Instead, the HAL provides services primarily to the Windows executive and kernel and to kernel mode device drivers. Although drivers for most hardware are contained in other files, commonly of file type .sys, a few core drivers are compiled into hal.dll.

Kernel mode device drivers for devices on buses such as PCI and PCI Express directly call routines in the HAL to access I/O ports and registers of their devices. The drivers use HAL routines because different platforms may require different implementations of these operations. The HAL implements the operations appropriately for each platform, so the same driver executable file can be used on all platforms using the same CPU architecture, and the driver source file can be portable across all architectures.

On x86 systems prior to Windows 8, there are several different HAL files on the installation media. The Windows installation procedure determines which ones are appropriate for the current platform and copies it to the hard drive, renaming it to hal.dll if necessary. Among the criteria for this selection are: the presence of an ACPI-compatible BIOS, the presence of an APIC, and whether or not multiple processors are present and enabled. (The multiple cores of a multi-core CPU, and even the «logical processors» implemented by a hyperthreading CPU, all count as «processors» for this purpose.) On x86-64 and Itanium platforms there is just one possible hal.dll for each CPU architecture. On Windows 8 and later, the x86 version also only has one HAL.

HAL is merged (or statically linked) into ntoskrnl.exe[2] starting with version 2004 of Windows 10, and the dll only serves as a stub for backwards compatibility.

NTDLL.DLL exports the Windows Native API. The Native API is the interface used by user-mode components of the operating system that must run without support from Win32 or other API subsystems. Most of this API is implemented in NTDLL.DLL and at the upper edge of ntoskrnl.exe (and its variants), and the majority of exported symbols within these libraries are prefixed Nt, for example NtDisplayString. Native APIs are also used to implement many of the «kernel APIs» or «base APIs» exported by KERNEL32.DLL.[3][4][5] The large majority of Windows applications do not call NTDLL.DLL directly.[6]

Applications that are linked directly against this library are said to use the native subsystem; the primary reason for their existence is to perform tasks that must run early in the system startup sequence before the Win32 subsystem is available. An obvious but important example is the creation of the Win32 subsystem process, csrss.exe. Before the csrss.exe process exists, no Win32 processes may be created, therefore the process that creates it (Smss.exe, the «session manager») must use the native subsystem. csrss.exe itself is such an application.

Despite having an «.exe» file extension, native applications cannot be executed by the user (or any program in the Win32 or other subsystems). An example is the autochk.exe binary that runs chkdsk during the system initialization «Blue Screen». Other prominent examples are the services that implement the various subsystems, such as csrss.exe.

Unlike Win32 applications, native applications instantiate within the Kernel runtime code (ntoskrnl.exe) and so they must have a different entry point (NtProcessStartup, rather than (w)(Win)MainCRTStartup as is found in a Win32 application),[4] obtain their command-line arguments via a pointer to an in-memory structure, manage their own memory using the Rtl heap API, (which the Win32 heap APIs are just wrappers around—no real difference there) and return execution with a call to RtlExitUserProcess (as opposed to ExitProcess). A common library linked with Native applications is nt.lib, which contains startup code for Native applications, similar to how the C runtime provides startup code for Win32 apps.[7]

Though most of the API is not publicly documented or supported. This allows the API to evolve without having to guarantee retro-compatibility, and breaking changes are thus possible without notification. Native Applications can be built using the Windows Driver Development Kit.

The libraries in this section each implement various subsets of the Win32 API.

KERNEL32.DLL exposes to applications most of the Win32 base APIs, such as memory management, input/output (I/O) operations, process and thread creation, and synchronization functions.[8]

GDI32.DLL exports Graphics Device Interface (GDI) functions that perform primitive drawing functions for output to video displays and printers. It is used, for example, in the XP version of Paint. Applications call GDI functions directly to perform low-level drawing (line, rectangle, ellipse), text output, font management, and similar functions.[8][9]

Initially, GDI supported 16 and 256 color EGA/VGA display cards and monochrome printers. The functionality has expanded over the years, and now includes support for things like TrueType fonts, alpha channels, and multiple monitors.[10]

USER32.DLL implements the Windows USER component that creates and manipulates the standard elements of the Windows user interface, such as the desktop, windows, and menus.
It thus enables programs to implement a graphical user interface (GUI) that matches the Windows look and feel. Programs call functions from Windows USER to perform operations such as creating and managing windows, receiving window messages (which are mostly user input such as mouse and keyboard events, but also notifications from the operating system), displaying text in a window, and displaying message boxes.

Many of the functions in USER32.DLL call upon GDI functions exported by GDI32.DLL to do the actual rendering of the various elements of the user interface. Some types of programs will also call GDI functions directly to perform lower-level drawing operations within a window previously created via USER32 functions.

COMCTL32.DLL implements a wide variety of standard Windows controls, such as File Open, Save, and Save As dialogs, progress bars, and list views. It calls functions from both USER32.DLL and GDI32.DLL to create and manage the windows for these UI elements, place various graphic elements within them, and collect user input.

COMDLG32.DLL, the Common Dialog Box Library, implements a wide variety of Windows dialog boxes intended to perform what Microsoft deems ‘common application tasks’. Starting with the release of Windows Vista, Microsoft considers the «Open» and «Save as» dialog boxes provided by this library as deprecated and replaced by the ‘Common Item Dialog API’.[11]

WS2_32.DLL implements the Winsock API, which provides TCP/IP networking functions and provides partial, broken compatibility with other network APIs. wsock.dll and wsock32.dll are older versions for Win3.11 and Win95 compatibility.

ADVAPI32.DLL, the Advanced Windows 32 Base API DLL,[12] provides security calls and functions for manipulating the Windows Registry.

NETAPI32.DLL provides functions for querying and managing network interfaces.

OLE32.DLL provides the Component Object Model, as well as Object Linking and Embedding.

SHSCRAP.DLL is part of the Object Linking and Embedding (OLE) mechanism. It implements support for shell scrap files, which are automatically created when you drag selected content from an OLE-capable application into an Explorer window or desktop,[13] but you can also use the Object Packager to create them. They can then be dragged into another OLE-capable application.

This functionality was removed from Windows Vista (and therefore later versions) to improve security and rid the operating system of generally unused functionality.[14] Scrap (.shs) files have been used by viruses because they can contain a wide variety of files (including executable code), and the file extension is not shown even when «Hide file extensions from known file types» is disabled.[15] The functionality can be restored by copying registry entries and the DLL from a Windows XP system.[16]

WINMM.DLL provides access to the original WinMM audio API.

IMM32 is responsible for invoking and interacting with the Input Method Editor.

MSVCRT.DLL, MSVCP*.DLL and CRTDLL.DLL

[edit]

MSVCRT.DLL is the C standard library for the Visual C++ (MSVC) compiler from version 4.2 to 6.0. It provides programs compiled by these versions of MSVC with most of the standard C library functions. These include string manipulation, memory allocation, C-style input/output calls, and others. MSVCP*.DLL is the corresponding C++ library.

It has shipped with Windows versions since Windows 95 OSR2.5 for use by other Windows components; earlier versions shipped with the CRTDLL.DLL library instead. In older versions of Windows, programs which linked against MSVCRT.DLL were expected to install a compatible copy in the System32 folder, but this contributed to DLL Hell because many installers failed to check the library version against the installed version before replacing it.

Versions of MSVC before 4.0 and from 7.0 to 12.0 used differently named DLLs for each version (MSVCR20.DLL, MSVCR70.DLL, MSVCR71.DLL, MSVCP110.DLL, etc.). Applications are required to install the appropriate version,[17] and Microsoft offers Visual C++ Redistributable packages for this purpose, though Windows typically comes with one version already installed.

This runtime library is used by programs written in Visual C++ and a few other compilers (e.g. MinGW). Some compilers have their own runtime libraries.

With Version 14.0 (Visual Studio 2015), most of the C/C++ runtime was moved into a new DLL, UCRTBASE.DLL, which conforms closely with C99[1]. Universal C Run Time (UCRT) from Windows 10 onwards become a component part of Windows[2], so every compiler (either non MS, like GCC or Clang/LLVM) can link against UCRT[3]. Additionally, C/C++ programs using UCRTBASE.DLL need to link against another new DLL, the Visual C++ Runtime. At Version 14.0, this was VCRUNTIME140.DLL.[18] The name has the potential to change at future versions, but has not done so as far as of Version 17.0.

Source code for runtime libraries is included in Visual C++[19] for reference and debugging (e.g. in C:\Program Files\Microsoft Visual Studio 11.0\VC\crt\src).

Other runtime libraries

[edit]

  • ATL*.DLL – Active Template Library
  • MFC*.DLL – Microsoft Foundation Classes
  • MSVBVM60.DLL – Visual Basic 6.0 Virtual Machine (Visual Basic.NET programs require .NET Framework instead)
  • VCOMP*.DLL – Microsoft OpenMP runtime
  • VCRUNTIME*.DLL – Microsoft Visual C++ Runtime, for MSVC 14.0+
  • MSVCIRT.DLL – Microsoft C++ Library, contains the deprecated C++ classes from <iostream.h> (note the file extension) for MS C 9 and 10 (MSVC 2.x, 4.x) (Back then, the draft C++ Standard Library was integrated within MSVCRT.DLL. It was split up with the release of Visual C++ 5.0)

.NET Framework libraries

[edit]

Programs written in C#, Visual Basic.NET, C++/CLI and other .NET languages require the .NET Framework. It has many libraries (one of them is mscorlib.dll – Multilanguage Standard Common Object Runtime Library, formerly Microsoft Common Object Runtime Library[20]) and so-called assemblies (e.g. System.Windows.Forms.dll).

  • Architecture of Windows NT
  • Booting process of Windows
  • List of Microsoft Windows components
  • Windows API
  • Dynamic-link library
  1. ^ Blunden, Bill (2009). The Rootkit Arsenal: Escape and Evasion in the Dark Corners of the System. Jones & Bartlett Learning. p. 101. ISBN 978-1-59822-061-2.
  2. ^ @PetrBenes (25 July 2019). «Did I miss something? Routines…» (Tweet) – via Twitter.
  3. ^ Eilam, Eldad (2011). Reversing: Secrets of Reverse Engineering. John Wiley & Sons. pp. 68–69. ISBN 978-1-118-07976-8.
  4. ^ a b «Inside Native Windows Applications». Microsoft. Archived from the original on 2010-09-12. Retrieved 2011-12-14.
  5. ^ Russinovich, Mark A. & Solomon, David A. (2009). Windows® Internals. O’Reilly Media. p. 136. ISBN 978-0-7356-3796-2.
  6. ^ Marceau, Carla & Stillerman, Matt (2006). «Modular behavior profiles in systems with shared libraries». In Neng, Peng; et al. (eds.). Information and Communications Security: 8th International Conference, ICICS 2006 – Raleigh, NC, USA, December 4–7, 2006 – proceedings. Springer. p. 371. ISBN 978-3-540-49496-6.
  7. ^ «Inside Native Applications». Archived from the original on 2010-10-23. Retrieved 2017-08-26.
  8. ^ a b «Identifying Functions in DLLs». .NET Framework documentation. Microsoft. 2022-03-11 – via Microsoft Learn.
  9. ^ See also, the documentation for the Wine implementation of GDI32.DLL: Wine API: gdi32.dll
  10. ^ Yuan, Feng (2001). Windows graphics programming: Win32 GDI and DirectDraw. Prentice Hall Professional. p. 71. ISBN 978-0-13-086985-2.
  11. ^ «Common Dialog Box Library (Windows)». msdn.microsoft.com. Retrieved 2017-10-25.
  12. ^ Microsoft (8 October 2009). «How RPC Works: Remote Procedure Call (RPC) | Microsoft Learn». Retrieved 11 September 2023.
  13. ^ «WD: What is a Scrap (.shs) file?». Microsoft Knowledge Base.
  14. ^ Raymond Chen. «Windows Confidential: Scrapping the Scraps». Retrieved 2011-12-14.
  15. ^ «VBS.Stages.A». Symantec. Archived from the original on November 10, 2006.
  16. ^ «How to open SHS files». Retrieved 2011-12-14.
  17. ^ «C Run-Time Libraries». Archived from the original on 2011-12-07. Retrieved 2011-12-14.
  18. ^ «C++ binary compatibility 2015-2022». 11 March 2024.
  19. ^ «Source Code for the C Run-Time Functions». 15 September 2006.
  20. ^ «What does the «ms» in «mscorlib» stand for — hint: It’s not «Microsoft»«. 31 January 2004.
  • API calls list (USER32.DLL) Archived 2015-11-21 at the Wayback Machine – Tips for using the User API Client Library with Visual Basic
  • API calls list (KERNEL32.DLL) Archived 2015-11-21 at the Wayback Machine – Tips for using the Kernel API Client Library with Visual Basic
  • Native API reference
  • Unofficial website that documents most of the Native API methods
  • Retrieving the KERNEL32.DLL base address

From Wikibooks, open books for an open world

Dynamic Link Libraries (DLLs) were introduced with the first releases of the Microsoft Windows operating system, and today are a fundamental structural component of the OS. They are found not only on the OS core, but as part of many of the frameworks created by Microsoft like the MFC, ATL, .NET, etc, even C and the C++ runtime libraries are distributed as DLLs.

DLLs allow certain code fragments to be compiled into a single library, and to be linked to by multiple programs. This means that only one copy of the library needs to exist, and multiple programs can share the functions and the data between them. The Windows system makes itself accessible, in several of its user-space features, by providing DLLs that programmers can use.

The difference between a DLL and a static library is that when you compile your programs, the DLL is not compiled into your executable, but instead remains a separate module. This feature helps to keep executable size low, and also allows for a DLL to be loaded into memory only when it is needed. Moreover, DLL code is shared over multiple different processes. Nearly every Windows executable shares kernel32.dll, and many share msvcrt.dll, the Visual C runtime. As a self contained entity a DLL also permits kick and target updates to the system and to applications. By simply replacing a DLL with a newer version that contains fixes or improvements, it is easy to extend the alteration to multiple dependent programs instantly.

The exact method of building a DLL file is dependent on the compiler you are using. However, the way in which DLLs are programmed is universal. We will talk about how to program DLL files in this chapter.

The common problem referred generically as «DLL hell» has always been a bane to Windows programmers and it really doesn’t seem to have a solution in the horizon. The problem was stated in the 90s and that was when the term was coined. The issue is on the permissibility of the OS to let incorrect DLLs version to be loaded upon the request from an application, that would invariably lead to a crash. Today an application will simply refuse to run.

While a stable DLL will not open a hell, changing oder upgrading a DLL may make bugs in applications visible that rely to older or undocumented behaviour of the old DLL. In general, DLL loads are resolved by file name. For Windows XP and onwards, so-called application manifests (XML resource with ID 24) come into play to resolve DLL loads to a finer grade. Moreover, for 64-bit Windows, file system virtualization exists to resolve bitness equivalence. For unknown reason, Microsoft decided to keep DLL file names equal between 32 and 64 bit. There is simply no kernel64.dll, kernel32.dll exists twice, one for 32 bit and one for 64 bit.

The __declspec keyword is a strange new keyword that is not part of the ANSI C standard, but that most compilers will understand anyway. __declspec allows a variety of non-standard options to be specified, that will affect the way a program runs. Specifically, there are two __declspec identifiers that we want to discuss:

  • __declspec(dllexport)
  • __declspec(dllimport)

When writing a DLL, we need to use the dllexport keyword to denote functions that are going to be available to other programs. Functions without this keyword will only be available for use from inside the library itself. Here is an example:

__declspec(dllexport) int MyFunc1(int foo)

The __declspec identifier for a function needs to be specified both in the function prototype and the function declaration, when building a DLL.

To «import» a DLL function into a regular program, the program must link to the DLL, and the program must prototype the function to be imported, using the dllimport keyword, as such:

__declspec(dllimport) int MyFunc1(int foo);

Now the program can use the function as normal, even though the function exists in an external library. The compiler works with Windows to handle all the details for you.

Many people find it useful to define a single header file for their DLL, instead of maintaining one header file for building a DLL, and one header file for importing a DLL. Here is a macro that is common in DLL creation:

#ifdef BUILDING_DLL
#define DLL_FUNCTION __declspec(dllexport)
#else
#define DLL_FUNCTION __declspec(dllimport)
#endif

Now, to build the DLL, we need to define the BUILDING_DLL macro, and when we are importing the DLL, we don’t need to use that macro. Functions then can be prototyped as such:

DLL_FUNCTION int MyFunc1(void);
DLL_FUNCTION int MyFunc2(void);
.......

(just a note: Microsoft did not intend for this __declspec syntax to be used. Instead the intention was that the public api of a DLL would be declared in an «exports» file. However the above syntax, despite requiring the macro to switch it back and forth, was much more convenient and is pretty much used by all software today).

When Windows links a DLL to a program, Windows calls the library’s DllMain function. This means that every DLL needs to have a DllMain function. The DllMain function needs to be defined as such:

BOOL APIENTRY DllMain (HINSTANCE hInstance, DWORD reason, LPVOID reserved)

The keywords «BOOL», «APIENTRY», «HINSTANCE», etc., are all defined in <windows.h> so you must include that file even if you don’t use any Win32 API functions in your library.

APIENTRY or WINAPI are keywords that denote the calling convention for the Windows API. They are both defined as __stdcall. Remember that the calling convention is part of a function’s signature.
The variable «hInstance» is the HINSTANCE handle for the library, and you can keep this and use it, or you can trash it.
reason will be one of four different values:

DLL_PROCESS_ATTACH
a new program has just linked to the library for the first time.
DLL_PROCESS_DETACH
a program has unlinked the library.
DLL_THREAD_ATTACH
a thread from a program has linked to the library.
DLL_THREAD_DETACH
a thread from a program has just unlinked the library.

The DllMain function doesn’t need to do anything special for these cases, although some libraries will find it useful to allocate storage for each new thread or process that is being used with the library.

The DllMain function must return TRUE if the library loaded successfully, or FALSE if the library had an error and could not load. Applications that cannot successfully open a library should fail gracefully.

Here is a general template for a DllMain function:

BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID lpReserved)
{
   switch (reason)
   {
     case DLL_PROCESS_ATTACH:
       break;
     case DLL_PROCESS_DETACH:
       break;
     case DLL_THREAD_ATTACH:
       break;
     case DLL_THREAD_DETACH:
       break;
   }
   return TRUE;
}

However, if you aren’t interested in any of the reasons, you can remove the entire switch statement from your program and return TRUE.

DLL libraries can be linked to an executable in two ways: Statically and Dynamically.

When static linking to a DLL, the linker will do all the work, and it will be transparent to the programmer that the functions are located in an external library. That is, it will be transparent if the library writer has properly used the _DECLSPEC modifier in the library’s header file.

When compiling a DLL, the compiler will generate two files: the DLL library file, and a static-linking stub .LIB file. The .LIB file acts like a mini static library, that tells the linker to statically link the associated DLL file. When using a DLL in a project, you can either provide the linker with the .LIB stub file, or some linkers allow you to specify the DLL directly (and the linker will then try to find the .LIB file, or may even attempt to create the .LIB file automatically).

The real power behind DLL files is that they can be loaded into your program dynamically at execution time. This means that while your program is running, it can search for and load in new components, without needing to be recompiled. This is an essential mechanism for programs that allow plugins and extensions to be loaded at execution time. To Dynamically load the DLL file, you can call the LoadLibrary function to get a handle to that library, and then pass that handle to one of several other functions to retrieve data from the DLL. The prototype for LoadLibrary is:

HMODULE WINAPI LoadLibrary(LPCTSTR lpFileName);

HMODULE is a HANDLE to a program module. lpFileName is the file name of the DLL you want to load. Keep in mind that when loading a module, the system will check in your PATH first. If you want the system to check in other specified directories first, use the SetDllDirectory function first.

Once a DLL is loaded, and you have a handle to the module, you can do various things:

  • Use GetProcAddress to return a function pointer to a function in that library.
  • Use LoadResource to retrieve a resource from the DLL.

Once you are finished with a DLL file, and you want to remove it from memory, you can call the FreeLibrary function with the DLL’s module handle.

  • Programming Windows With OSS Tools

Sign in to your MUO account

Someone using a Windows 11 laptop

The first time most people encounter the term «DLL» is when a program fails to open due to a missing DLL file. And maybe that’s what happened to you, too.

While you can easily search online to fix a DLL-related issue, it does prompt the question: what is a DLL file, and why does your computer care when one is missing? So, let’s start with a brief introduction about DLL files and how they fit into the whole Windows environment.

What Are Windows DLL Files?

First up, let’s explore what «DLL» stands for. The initialism stands for «Dynamic Link Libraries,» and to better understand what a DLL file is, we need to establish a good grip on what a «library» is in computer terms.

A library on your computer is a collection of reusable code. Libraries are independent programs that can be used over and over again by a programmer or another program.

DLL files are a special implementation of these kinds of libraries. DLL files contain numerous functions, classes, and variables that other programs can use as and when the need arises.

When you run a word processor, it might have to execute a specific task that it doesn’t have the code for. For example, let’s say you want to print off something in the word processor, but the software has no idea how to do that. In this case, the program will need to borrow the instructions from another program that’s built specifically to provide print functionality.

This is where those pre-built libraries come in. They will provide all the necessary code to the word processor to help it print out your work whenever the program requires it. This concept of libraries is based on the Modular programming framework, a concept in software development where a program is divided into independent sub-programs that can run on their own.

But how does this help?

First, changes made to a single module will be reflected in all the applications that make use of that module for its operations. This wouldn’t have been possible if we weren’t using the practice of writing reusable code and thus creating libraries.

Secondly, Dynamic Libraries make debugging and tweaking code a walk in the park. This is because different programs are using the exact same code to perform a task, so any errors and required changes can be easily identified and fixed in that one piece of code.

Why Do DDL Files Go Missing?

Missing DLL files is a common problem with major Windows operating systems. You’ll launch a program, only to be met with a pop-up that says a specific DLL file is missing. In some cases, it can even result in Windows boot-up issues.

There are a bunch of reasons why DLL files go missing. A few of them are:

  1. Malware infection
  2. Corruption of a DLL file by an abrupt shutdown.
  3. A DLL file edited by new software.
  4. Accidental deletion by the user.

While this is not an exhaustive list by any means, these are the most probable reasons why a DLL file goes missing. Thankfully, this error can be easily resolved by using Windows troubleshooting tools, such as System Restore, Windows Update, or even a complete reset as a last resort.

There are other methods you can use to fix a DLL error, too. If you want the full run-down, be sure to check out our guide on how to fix DLL files missing errors in Windows.

That’s All About Dynamic Link Libraries

Dynamic Link Libraries are an essential component of the Windows operating system. We’d be stuck with a slow PC startup if it weren’t for Dynamic link libraries. In fact, the Windows operating system is home to many functions and libraries that work together to make your PC run the way it does.

DLL (Шаблон:Lang-en — «библиотека динамической компоновки», «динамически подключаемая библиотека») в операционных системах Microsoft Windows и IBM OS/2 — динамическая библиотека, позволяющая многократное использование различными программными приложениями. K DLL относятся также элементы управления ActiveX и драйверы. В системах UNIX аналогичные функции выполняют так называемые общие объекты (Шаблон:Lang-en).

Формат файлов DLL придерживается тех же соглашений, что и формат исполняемых файлов, сочетая код, таблицы и ресурсы, отличаясь лишь интерпретацией некоторых полей.

Назначение[]

Первоначально предполагалось, что введение DLL позволит эффективно организовать память и дисковое пространство, используя только один экземпляр библиотечного модуля для различных приложений. Это было особенно важно для ранних версий Microsoft Windows с жёсткими ограничениями по памяти.

Далее предполагалось улучшить эффективность разработок и использования системных средств за счёт модульности. Замена DLL-программ с одной версии на другую должна была позволить независимо наращивать систему, не затрагивая приложений. Кроме того, динамические библиотеки могли использоваться разнотипными приложениями — например, Microsoft Office, Microsoft Visual Studio и т. п.

В дальнейшем идея модульности выросла в концепции Component Object Model и System Object Model.

Фактически полных преимуществ от внедрения динамически подключаемых библиотек получить не удалось по причине явления, называемого DLL hell («DLL-ад»). DLL hell возникает, когда несколько приложений требуют одновременно различные, не полностью совместимые версии библиотек, что приводит к сбоям в этих приложениях и к конфликтам типа DLL hell, резко снижая общую надёжность операционных систем. Поздние версии Microsoft Windows стали разрешать параллельное использование разных версий DLL (технология Side-by-side assembly), что свело на нет преимущества изначального принципа модульности.

Ссылки[]

Шаблон:Викиучебник

  • Dynamic-Link Libraries (Windows)Шаблон:Ref-en
  • «Tutorial for making and using DLL’s»
  • «Delay Load Dlls Error Recovery»
  • Полное описание процесса создания dll в Visual Studio

Шаблон:Soft-stub

Шаблон:Перевести

  Компоненты Microsoft Windows
Основные

Aero •
ClearType •
Диспетчер рабочего стола
DirectX •
Панель задач
(Пуск

Область уведомлений) •
Проводник
(Пространство имён

Специальные папки
Ассоциации файлов) •
Windows Search
(Smart folders •

iFilters) •
GDI •
WIM •
SMB •
.NET Framework •
XPS •
Active Scripting
(WSH •

VBScript
JScript) •
COM
(OLE •

DCOM •
ActiveX •
Структурированное хранилище
Сервер транзакций) •
Теневая копия •
WDDM •
UAA
Консоль Win32

Службы
управления

Архивация и восстановление
COMMAND.COM •
cmd.exe •
Средство переноса данных •
Просмотр событий
Установщик •
netsh.exe •
PowerShell •
Отчёты о проблемах
rundll32.exe •
Программа подготовки системы (Sysprep) •
Настройка системы (MSConfig) •
Проверка системных файлов •
Индекс производительности •
Центр обновления •
Восстановление системы •
Дефрагментация диска
Диспетчер задач
Диспетчер устройств •
Консоль управления •
Очистка диска •
Панель управления
(элементы)

Приложения

Контакты •
DVD Maker
Факсы и сканирование
Internet Explorer •
Журнал
Экранная лупа •
Media Center •
Проигрыватель Windows Media •
Программа совместной работы
Центр устройств Windows Mobile
Центр мобильности •
Экранный диктор
Paint •
Редактор личных символов
Удалённый помощник
Распознавание речи
WordPad •
Блокнот
Боковая панель •
Звукозапись
Календарь
Калькулятор
Ножницы
Почта •
Таблица символов •
Исторические:
Movie Maker •

NetMeeting •
Outlook Express •
Диспетчер программ •
Диспетчер файлов •
Фотоальбом •
Windows To Go

Игры

Chess Titans •
Mahjong Titans •
Purble Place •
Пасьянсы (Косынка •
Паук •
Солитер) •
Сапёр •
Пинбол •
Червы

Ядро ОС

Ntoskrnl.exe •
Слой аппаратных абстракций (hal.dll) •
Бездействие системы •
svchost.exe •
Реестр •
Службы •
Диспетчер управления сервисами
DLL
(формат модулей) •

PE •
NTLDR •
Диспетчер загрузки
Программа входа в систему (winlogon.exe) •
Консоль восстановления
Windows RE
Windows PE •
Защита ядра от изменений

Службы

Autorun.inf •
Фоновая интеллектуальная служба передачи
Файловая система стандартного журналирования
Отчёты об ошибках
Планировщик классов мультимедиа
Теневая копия •
Планировщик задач
Беспроводная настройка

Файловые
системы

ReFS •
NTFS
(Жёсткая ссылка •

Точка соединения •
Точка монтирования
Точка повторной обработки
Символьная ссылка •
TxF •
EFS) •
WinFS •
FAT •
exFAT •
CDFS •
UDF
DFS •
IFS

Сервер

Active Directory •
Службы развёртывания •
Служба репликации файлов
DNS
Домены
Перенаправление папок
Hyper-V •
IIS •
Media Services
MSMQ
Защита доступа к сети (NAP) •
Службы печати для UNIX •
Удалённое разностное сжатие
Службы удаленной установки
Служба управления правами
Перемещаемые профили пользователей
SharePoint
Диспетчер системных ресурсов
Удаленный рабочий стол
WSUS •
Групповая политика •
Координатор распределённых транзакций

Архитектура

NT •
Диспетчер объектов
Пакеты запроса ввода/вывода
Диспетчер транзакций ядра
Диспетчер логических дисков
Диспетчер учетных записей безопасности
Защита ресурсов •
lsass.exe
csrss.exe •
smss.exe •
spoolsv.exe
Запуск

Безопасность

BitLocker
Защитник •
Предотвращение выполнения данных
Обязательный контроль целостности
Защищённый канал данных
UAC •
UIPI
Брандмауэр •
Центр обеспечения безопасности •
Защита файлов

Совместимость

Подсистема UNIX (Interix) •
Виртуальная машина DOS •
Windows on Windows •
WOW64

Шаблон:OS/2 API

Книги: [Классика] [Базы данных] [Internet/WWW] [Сети] [Программирование] [UNIX] [Windows] [Безопасность] [Графика] [Software Engineering] [ERP-системы] [Hardware]

Динамические библиотеки для начинающих

(статья была опубликована в журнале «Программист»)

В наше время Windows-разработчик шагу не может ступить без динамических библиотек (Dynamic Link Library — DLL); а перед начинающими программистами, желающими разобраться в предмете, встает масса вопросов:

    1. как эффективно использовать чужие DLL?
    2. как создать свою собственную?
    3. какие способы загрузки DLL существуют, и чем они отличаются?
    4. как загружать ресурсы из DLL?

Обо всем этом (и многом другом) рассказывает настоящая глава. Материал рассчитан на пользователей Microsoft Visual C++, а поклонникам других языков и компиляторов придется разбираться с ключами компиляции приведенных примеров самостоятельно.

Создание собственной DLL

С точки зрения программиста — DLL представляет собой библиотеку функций (ресурсов), которыми может пользоваться любой процесс, загрузивший эту библиотеку. Сама загрузка, кстати, отнимает время и увеличивает расход потребляемой приложением памяти; поэтому бездумное дробление одного приложения на множество DLL ничего хорошего не принесет.

Другое дело — если какие-то функции используются несколькими приложениями. Тогда, поместив их в одну DLL, мы избавимся от дублирования кода и сократим общий объем приложений — и на диске, и в оперативной памяти. Можно выносить в DLL и редко используемые функции отдельного приложения; например, немногие пользователи текстового редактора используют в документах формулы и диаграммы — так зачем же соответствующим функциям впустую «отъедать» память?

Загрузившему DLL процессу доступны не все ее функции, а лишь явно предоставляемые самой DLL для «внешнего мира» — т. н. экспортируемые. Функции, предназначенные сугубо для «внутреннего» пользования, экспортировать бессмысленно (хотя и не запрещено). Чем больше функций экспортирует DLL — тем медленнее она загружается; поэтому к проектированию интерфейса (способа взаимодействия DLL с вызывающим кодом) следует отнестись повнимательнее. Хороший интерфейс интуитивно понятен программисту, немногословен и элегантен: как говорится, ни добавить, ни отнять. Строгих рекомендаций на этот счет дать невозможно — умение приходит с опытом

Для экспортирования функции из DLL — перед ее описанием следует указать ключевое слово __declspec(dllexport), как показано в следующем примере:

// myfirstdll.c
#include <stdio.h>

// Ключевое слово __declspec(dllexport)
// делает функцию экспортируемой
__declspec(dllexport) void Demo(char *str)
{
  // Выводим на экран переданную функции Demo строку
  printf(str);
}

Листинг 10 Демонстрация экспорта функции из DLL

Для компиляции этого примера в режиме командной строки можно запустить компилятор Microsoft Visual Studio: «cl.exe myfirstdll.c /LD«. Ключ «/LD» указывает линкеру, что требуется получить именно DLL.

Для сборки DLL из интегрированной оболочки Microsoft Visual Studio — при создании нового проекта нужно выбрать пункт «Win32 Dynamics Link Library«, затем «An Empty DLL project«; потом перейти к закладке «File View» окна «Workspace» — и, выбрав правой клавишей мыши папку «Source Files«, добавить в проект новый файл («Add Files to Folder«). Компиляция осуществляется как обычно («Build» ( «Build»).

Если все прошло успешно — в текущей директории (или в директории Release\Debug при компиляции из оболочки) появится новый файл — «MyFirstDLL.dll». Давайте заглянем в него через «микроскоп» — утилиту dumpbin, входящую в штатную поставку SDK и Microsoft Visual Studio: «dumpbin /EXPORTS MyFirstDLL.dll«. Ответ программы в несколько сокращенно виде должен выглядеть так:

  Section contains the following exports for myfirst.dll
             0 characteristics
          0.00 version
             1 ordinal base
             1 number of functions
             1 number of names
             1    0 00001000 Demo

Получилось! Созданная нами DLL действительно экспортирует функцию «Demo» — остается только разобраться, как ее вызывать

Вызов функций из DLL

Существует два способа загрузки DLL: с явной и неявной компоновкой.

При неявной компоновке функции загружаемой DLL добавляются в секцию импорта вызывающего файла. При запуске такого файла загрузчик операционной системы анализирует секцию импорта и подключает все указанные библиотеки. Ввиду своей простоты этот способ пользуется большой популярностью; но простота — простотой, а неявной компоновке присущи определенные недостатки и ограничения:

  1. все подключенные DLL загружаются всегда, даже если в течение всего сеанса работы программа ни разу не обратится ни к одной из них;
  2. если хотя бы одна из требуемых DLL отсутствует (или DLL не экспортирует хотя бы одной требуемой функции) — загрузка исполняемого файла прерывается сообщением «Dynamic link library could not be found» (или что-то в этом роде) — даже если отсутствие этой DLL некритично для исполнения программы. Например, текстовой редактор мог бы вполне работать и в минимальной комплектации — без модуля печати, вывода таблиц, графиков, формул и прочих второстепенных компонентов, но если эти DLL загружаются неявной компоновкой — хочешь не хочешь, придется «тянуть» их за собой.
  3. поиск DLL происходит в следующем порядке: в каталоге, содержащем вызывающий файл; в текущем каталоге процесса; в системном каталоге %Windows%System%; в основном каталоге %Windows%; в каталогах, указанных в переменной PATH. Задать другой путь поиска невозможно (вернее — возможно, но для этого потребуется вносить изменения в системный реестр, и эти изменения окажут влияние на все процессы, исполняющиеся в системе — что не есть хорошо).

Явная компоновка устраняет все эти недостатки — ценой некоторого усложнения кода. Программисту самому придется позаботиться о загрузке DLL и подключении экспортируемых функций (не забывая при этом о контроле над ошибками, иначе в один прекрасный момент дело кончится зависанием системы). Зато явная компоновка позволяет подгружать DLL по мере необходимости и дает программисту возможность самостоятельно обрабатывать ситуации с отсутствием DLL. Можно пойти и дальше — не задавать имя DLL в программе явно, а сканировать такой-то каталог на предмет наличия динамических библиотек и подключать все найденные к приложению. Именно так работает механизм поддержки plug-in’ов в популярном файл-менеджере FAR (да и не только в нем).

Таким образом, неявной компоновкой целесообразно пользоваться лишь для подключения загружаемых в каждом сеансе, жизненно необходимых для работы приложения динамических библиотек; во всех остальных случаях — предпочтительнее явная компоновка.

Загрузка DLL с неявной компоновкой

Чтобы вызвать функцию из DLL, ее необходимо объявить в вызывающем коде — либо как external (т. е. как обычную внешнюю функцию), либо предварить ключевым словом __declspec(dllimport). Первый способ более популярен, но второй все же предпочтительнее — в этом случае компилятор, поняв, что функция вызывается именно из DLL, сможет соответствующим образом оптимизировать код. Например, функция «Demo» из созданной нами библиотеки — «MyFirstDll» вызывается так:

  // ImplictDll.c
  // Объявляем внешнюю функцию Demo
  __declspec(dllimport) void Demo(char *str);

  main()
  {
    // Вызываем функцию Demo из DLL
    Demo("Hello, World!\n");
  }

Листинг 11 Демонстрация вызова функции из DLL неявной компоновкой

Из командной строки данный пример компилируется так: «cl.exe ImplictDll.c myfirstdll.lib«, где «myfirstdll.lib» — имя библиотеки, автоматически сформированной компоновщиком при создании нашей DLL.

Разумеется, «чужие» DLL не всегда поставляются вместе с сопутствующими библиотеками, но их можно легко изготовить самостоятельно! На этот случай предусмотрена специальная утилита implib, поставляемая вместе с компилятором, и вызываемая так: «implib.exe Имя_файла _создаваемой_библиотеки Имя_DLL«.

В нашем случае — не будь у нас файла «MyFirstDLL.lib«, его пришлось бы получить так: «implib.exe MyFirstDLL.lib MyFirstDLL.dll«. Со всеми стандартными DLL, входящими в состав Windows, эту операцию проделывать не нужно, т.к. необходимые библиотеки распространяются вместе с самим компилятором.

Для подключения библиотеки в интегрированной среде Microsoft Visual Studio — в меню «Project» выберите пункт «Project Settings«, в открывшемся диалоговом окне перейдите к закладке «Link» и допишите имя библиотеки в конец строки «Object/Library Modules«, отделив ее от остальных символом пробела.

Если все прошло успешно, появится новый файл «ImplictDll.exe«, который, будучи запущенным, горделиво выведет на экран «Hello, Word!«. Это означает, что наша DLL подключена и успешно работает.

Заглянем внутрь: как это происходит? Запустим «dumpbin /IMPORTS ImplictDll.exe» и посмотрим, что нам сообщит программа:

  File Type: EXECUTABLE IMAGE

    Section contains the following imports:

      myfirstdll.dll
             404090 Import Address Table
             4044C8 Import Name Table
                  0 time date stamp
                  0 Index of first forwarder reference

                0  Demo

      KERNEL32.dll
             404000 Import Address Table
             404438 Import Name Table
                  0 time date stamp
                  0 Index of first forwarder reference

              19B  HeapCreate
              2BF  VirtualFree
               CA  GetCommandLineA
              174  GetVersion
               7D  ExitProcess
              29E  TerminateProcess
               F7  GetCurrentProcess

Вот она — «Myfirstdll.dll» (в тексте выделена жирным шрифтом), и вот функция «Demo«, а кроме нее — обнаруживает свое присутствие библиотека KERNEL32.DLL – она необходима RTL-коду (Run Time Library — библиотека времени исполнения), насильно помещенному компилятором в наше приложение. RTL-код обеспечивает работу с динамической памятью (heap), считывает аргументы командной строки, проверяет версию Windows и многое-многое другое! Отсюда и появляются в таблице импорта функции HeapCreate, GetCommandLine, GetVersion и т.д. Так что — не удивляйтесь, увидев «левый» импорт в своем приложении!

Проследить, как именно происходит загрузка DLL, можно с помощью отладчика. Общепризнанный лидер — это, конечно, SoftIce от NuMega, но для наших экспериментов вполне сойдет и штатный отладчик Microsoft Visual Studio. Откомпилировав нашу вызывающую программу, нажмем <F10> для пошагового прогона приложения

Оппаньки! Не успело еще выполниться ни строчки кода, как в окне «output» отладчика появились следующие строки, свидетельствующие о загрузке внешних DLL: NTDLL.DLL, MyFirstDll.dll и Kernel32.dll. Так и должно быть — при неявной компоновке динамические библиотеки подключаются сразу же при загрузке файла, задолго до выполнения функции main!

Loaded 'C:\WINNT\System32\ntdll.dll', no matching symbolic information found.
Loaded 'F:\ARTICLE\PRG\DLL.files\myfirstdll.dll', no matching symbolic information found.
Loaded 'C:\WINNT\system32\kernel32.dll', no matching symbolic information found.

Загрузка DLL с явной компоновкой

Явную загрузку динамических библиотек осуществляет функция HINSTANCE LoadLibrary(LPCTSTR lpLibFileName) или ее расширенный аналог HINSTANCE LoadLibraryEx(LPCTSTR lpLibFileName, HANDLE hFile, DWORD dwFlags).

Обе они экспортируются из KERNEL32.DLL, следовательно, каждое приложение требует неявной компоновки по крайней мере этой библиотеки. В случае успешной загрузки DLL возвращается линейный адрес библиотеки в памяти. Передав его функции FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName) — мы получим указатель на функцию lpProcName, экспортируемую данной DLL. При возникновении ошибки обе функции возвращают NULL. После завершения работы с динамической библиотекой ее следует освободить вызовом функции BOOL FreeLibrary(HMODULE hLibModule). Для пояснения приведем код примера с подробными комментариями:

// DynCall.c
#include <stdio.h>
#include <windows.h>

main()
{
  // Дескриптор загружаемой dll
  HINSTANCE h;

  // Объявление указателя на функцию, вызываемой из DLL
  // Обратите внимание – имена объявляемой функции и
  // функции, вызываемой из DLL, могут и не совпадать,
  // т.к. за выбор вызываемой функции отвечает
  // GetProcAddress
  void (*DllFunc) (char *str);

  // Загружаем MyFirstDLL
  h=LoadLibrary("MyFirstDLL.dll");

  // Контроль ошибок – если загрузка прошла успешно,
  // функция вернет что-то отличное от нуля
  if (!h)
  {
    printf("Ошибка - не могу найти MyFirstDLL.dll\n");
    return;
  }

  // Вызовом GetProcAddress получаем адрес функции Demo
  // и присваиваем его указателю DllFunc с явным 
  // приведением типов. Это необходимо т.к.
  // GetProcAddress возвращает бестиповой far-указатель
  DllFunc=(void (*) (char *str))
            GetProcAddress(h,"Demo");

  // Контроль ошибок – если вызов функции GetProcAddress
  // завершился успешно, она вернет ненулевой указатель
  if (!DllFunc)
  {
    printf("Ошибка! В MyFirstDLL "
           "отсутствует ф-ция   Demo\n");
    return;
  }

  // Вызов функции Demo из DLL
  DllFunc("Test");

  // Выгрузка динамической библиотеки из памяти
  FreeLibrary(h);
}

Листинг 12 Демонстрация вызова функции из DLL явной компоновкой

Компилировать так: «cl DynCall.c» — никаких дополнительных библиотек указывать не нужно (необходимые kernel32.lib и LIBC.lib компоновщик подключит самостоятельно).

В интегрированной среде Microsoft Visual Studio достаточно щелкнуть мышкой по иконке «Build» — и никаких дополнительных настроек!

Для изучения секции импорта только что полученного файла запустим утилиту Dumpbin – обратите внимание, что здесь отсутствует всякое упоминание о MyFirstDLL.dll, но обнаруживаются две функции: LoadLibrary и GetProcAddress — которые и загружают нашу библиотеку. Это очень важное обстоятельство — изучение секции импорта исследуемого файла не всегда позволяет установить полный перечень функций и динамических библиотек, которые использует приложение. Наличие LoadLibrary и GetProcAddress красноречиво свидетельствует о том, что приложение подгружает какие-то модули во время работы самостоятельно.

Чтобы выяснить какие — запустим его под отладчиком. Сразу же после загрузки исполняемого файла в окне Output появятся строки:

Loaded 'C:\WINNT\System32\ntdll.dll', no matching symbolic information found.
Loaded 'C:\WINNT\system32\kernel32.dll', no matching symbolic information found.

Это опять грузятся обязательные KERNEL32.DLL и NTDLL.DLL (последнее — только под Windows NT/2000). Никакого упоминания о MyFirstDLL.dll еще нет. Пошагово исполняя программу («Debug» ( «Step Over«), дождемся выполнения функции LoadLibrary. Тут же в Output-окне появится следующая строка:

Loaded 'F:\ARTICLE\PRG\DLL.files\myfirstdll.dll', no matching symbolic information found.

Наша динамическая библиотека загрузилась; но не сразу после запуска файла (как это происходило при неявной компоновке), а только когда в ней возникла необходимость!

Если же по каким-то причинам DLL не найдется, или окажется, что в ней отсутствует функция Demo — операционная система не станет «убивать» приложение с «некрологом» критической ошибки, а предоставит программисту возможность действовать самостоятельно. В качестве эксперимента попробуйте удалить (переименовать) MyFirstDLL.dll и посмотрите, что из этого получится.

Выгрузка динамических библиотек из памяти

Когда загруженная динамическая библиотека больше не нужна — ее можно освободить, вызвав функцию BOOL FreeLibrary(HMODULE hLibModule) и передав ей дескриптор библиотеки, ранее возвращенный функцией LoadLibrary. Обратите внимание — DLL можно именно освободить, но не выгрузить! Выгрузка DLL из памяти не гарантируется, даже если работу с ней завершили все ранее загрузившие ее процессы.

Задержка выгрузки предусмотрена специально — на тот случай, если эта же DLL через некоторое время вновь понадобится какому-то процессу. Такой трюк оптимизирует работу часто используемых динамических библиотек, но плохо подходит для редко используемых DLL, загружаемых лишь однажды на короткое время. Никаких документированных способов насильно выгрузить динамическую библиотеку из памяти нет; а те, что есть — работают с ядром на низком уровне и не могут похвастаться переносимостью. Поэтому здесь мы их рассматривать не будем. К тому же — тактика освобождения и выгрузки DLL по-разному реализована в каждой версии Windows: Microsoft, стремясь подобрать наилучшую стратегию, непрерывно изменяет этот алгоритм; а потому и отказывается его документировать.

Нельзя не обратить внимания на одно очень важное обстоятельство: динамическая библиотека не владеет никакими ресурсами — ими владеет, независимо от способа компоновки, загрузивший ее процесс. Динамическая библиотека может открывать файлы, выделять память и т. д., но память не будет автоматически освобождена после вызова FreeLibrary, а файлы не окажутся сами собой закрыты — все это произойдет лишь после завершения процесса, но не раньше! Естественно, если программист сам не освободит все ненужные ресурсы вручную, с помощью функций CloseHandle, FreeMemory и подобных им.

Если функция FreeLibrary пропущена, DLL освобождается (но не факт, что выгружается!) только после завершения вызвавшего процесса. Могут возникнуть сомнения: раз FreeLibrary немедленно не выгружает динамическую библиотеку из памяти, так зачем она вообще нужна? Не лучше ли тогда все пустить на самотек — все равно ведь загруженные DLL будут гарантированно освобождены после завершения процесса? Что ж, доля правды тут есть, и автор сам порой так и поступает; но при недостатке памяти операционная система может беспрепятственно использовать место, занятое освобожденными динамическими библиотеками под что-то полезное — а если DLL еще не освобождены, их придется «скидывать» в файл подкачки, теряя драгоценное время. Поэтому лучше освобождайте DLL сразу же после их использования!

ООП и DLL

Динамические библиотеки ничего не знают ни о каком ООП! Они не имеют ни малейшего представления о существовании классов! Все, что умеют DLL — экспортировать одно или несколько имен функций (ресурсов, глобальных переменных), а уж как его использовать — решать программисту или компилятору.

Что ж; испытаем компилятор на «сообразительность», включив в описание класса ключевое слово __declspec(dllexport)  и посмотрим, что из этого выйдет:

  // DLLclass.cpp
  #include <stdio.h>

  class __declspec(dllexport) MyDllClass{
  public:
    Demo(char *str);
  };

  MyDllClass::Demo(char *str)
  {
    printf(str);
  }

Листинг 13 Демонстрация экспорта класса из DLL

Откомпилируем этот код как обычную DLL и заглянем в таблицу импорта утилитой dumpbin:

dumpbin /EXPORTS DLLclass.dll 

File Type: DLL
 Section contains the following exports for DLLclass.dll
          0 characteristics
   3B1B98E6 time date stamp Mon Jun 04 18:19:18 2001
       0.00 version
          1 ordinal base
          2 number of functions
          2 number of names
   ordinal hint RVA      name

         1    0 00001000 ??4MyDllClass@@QAEAAV0@ABV0@@Z
         2    1 00001020 ?Demo@MyDllClass@@QAEHPAD@Z

Таблица импорта явно не пуста — но выглядит странно. Компилятор искалечил имена, чтобы втиснуть в них информацию о классах и аргументах функций без нарушений жестких ограничений, налагаемых стандартом на символы, допустимые в экспортируемых именах (например, запрещается использовать знак двоеточие, скобка и т. д.).

Как же со всем этим работать? Попробуй-ка угадай — во что превратится то или иное имя после компиляции! Впрочем, при неявной компоновке ни о чем гадать не придется, т. к. обо всем позаботится сам компилятор, а от программиста потребуется лишь описать класс, предварив его ключевым словом __declspec(dllimport):

  // DLLClassCall.cpp
  #include <stdio.h>

  class __declspec(dllimport) MyDllClass{
  public:
    Demo(char *str);
  };

  main()
  {
    MyDllClass zzz;
    zzz.Demo("Привет, ООП! А мы тут!");
  }

Листинг 14 Демонстрация импорта класса из DLL неявной компоновкой

Откомпилируйте пример как обычную программу с неявной компоновкой («cl DLLClassCall.cpp DLLClass.lib«) и попробуйте запустить полученный файл. Работает? Никакой разницы с «классическим» Си нет, не правда ли? Вот только как подключить DLL с явной компоновкой? Неужели нельзя запретить компилятору «калечить» имена функций?! Конечно же, можно

Мангл и как его побороть или импорт классов из DLL явной компоновкой

Искажение функций Cи ++ компилятором называется по-английски «mangle«, и среди русскоязычных программистов широко распространена его калька — «манглить». В принципе «манглеж» функций не препятствует их явному вызову посредством GetProcAddress — достаточно лишь знать, как точно называется та или иная функция, что нетрудно выяснить тем же dumpbin. Однако засорение программы подобными «заученными» именами выглядит не очень-то красиво; к тому же — всякий компилятор «манглит» имена по-своему, и непосредственное использование имен приводит к непереносимости программы.

Существует способ обойти искажение имен — для этого необходимо подключить к линкеру специальный DEF-файл, перечисляющий имена, которые не должны измениться. В нашем случае он должен выглядеть так

  // DllClass.def:
  EXPORTS
    Demo

Листинг 15 Отказ от «замангления» имен

Сперва идет ключевое слово «EXPORTS», за которым следуют одно или несколько «неприкасаемых» имен. Каждое имя начинается с новой строки, и в его конце не указывается точка с запятой.

Для подключения DEF-файла при компиляции из командной строки — используйте опцию «/link /DEF:имя_файла.def«, например так: «cl DLLclass.cpp /LD /link /DEF:DLLClass.def«.

Для подключения DEF-файла в интегрированной среде Microsoft Visual Studio — перейдите к закладке «File View» окна «Workspace» и, щелкнув правой клавишей по папке «Source Files«, выберите в контекстом меню пункт «Add Files to Folder«, а затем укажите путь к DEF-файлу. Откомпилируйте проект как обычно: «Build» ( «Build».

Заглянув в таблицу импорта полученного DLL-файла, мы, среди прочей информации, увидим следующее:

  1    0 00001000 ??4MyDllClass@@QAEAAV0@ABV0@@Z
  2    1 00001020 Demo

Теперь имя функции Demo выглядит «как положено». А абракадабра, расположенная строчкой выше — это конструктор класса MyDllClass, который, хоть и не был специально объявлен, все равно экспортируется из динамической библиотеки.

Однако, избавившись от одной проблемы, мы получаем другую — имя функции Demo потеряло всякое представление о классе, которому оно принадлежало; и теперь придется загружать его вручную, повторяя эту операцию для каждого элемента класса. Фактически — придется в вызываемой программе собирать «скелет» класса из «косточек» заново. Но иного способа явной загрузки класса из DLL не существует.

Следующий пример демонстрирует вызов функции MyDllClass из динамической библиотеки с явной компоновкой. Обработка ошибок для упрощения опущена. Обратите внимание, как объявляется функция Demo — описания класса в DLL и в вызывающей программе существенно отличаются, поэтому с идеей поместить описания класса в общий для всех include-файл придется расстаться.

  // DeMangle.cpp
  #include <windows.h>

  class MyDllClass{
  public:
    void (*Demo) (char *str);
  };

  main()
  {
    HINSTANCE h=LoadLibrary("DllClass.dll");
    MyDllClass zzz;

    // Внимание! Выполнение конструктора / деструктора
    // класса при явной загрузке не происходит
    // автоматически и при необходимости эту операцию
    // следует выполнить вручную
    zzz.Demo=(void (*) (char *str))
               GetProcAddress(h,"Demo");
    zzz.Demo("Test");
  }

Листинг 16 Демонстрация вызова функции MyDllCLass::Demo явной компоновкой

Загрузка ресурсов из DLL

Помимо функций, динамические библиотеки могут содержать и ресурсы — строки, иконки, рисунки, диалоги и т. д. Хранение ресурсов в DLL очень удобно; в частности — при создании приложений с многоязычным интерфейсом: заменив одну DLL на другую, мы заменяем все надписи в программе, скажем, с английского на русский — и, заметьте, без всяких «хирургических вмешательств» в код приложения! Аналогично можно менять иконки, внешний вид диалогов и т. д.

Создание DLL, содержащей ресурсы, ничем не отличается от создания исполняемого приложения с ресурсами; сначала необходимо создать сам файл ресурсов — например, так:

  // MyResDll.rc
  #pragma code_page(1251)

  STRINGTABLE DISCARDABLE 
  BEGIN
      1             "Hello, World!"
  END

Листинг 17 Создание DLL, содержащей одни лишь ресурсы

Файл ресурсов надо скомпилировать — «rc MyResDll.rc» — и преобразовать линкером в DLL, обязательно указав флаг «/NOENTRY«, т. к. эта динамическая библиотека содержит исключительно одни ресурсы и ни строки кода: «link MyRedDll.res /DLL /NOENTRY«.

В Visual Studio это сделать еще проще — достаточно кликнуть по папке «Resourses» окна «File View» и добавить новый файл ресурса, который затем можно будет модифицировать визуальным редактором по своему усмотрению.

Для загрузки ресурса из DLL — в принципе, можно воспользоваться уже знакомой нам функцией LoadLibray, и передавать возращенный ею дескриптор LoadString или другой функции, работающей с ресурсами. Однако загрузку динамической библиотеки можно значительно ускорить, если «объяснить» системе, что эта DLL не содержит ничего, кроме ресурсов, и нам достаточно лишь спроецировать ее на адресное пространство процесса, а обо всем остальном мы сумеем позаботиться и самостоятельно.

Вот тут-то и пригодится функция LoadLibraryEx: ее первый аргумент, как и у коллеги LoadLibrary, задает имя динамической библиотеки для загрузки, второй — зарезервирован и должен быть равен нулю, а третий, будучи равным LOAD_LIBRARY_AS_DATAFILE, заставляет функцию делать именно то, что нам нужно — загружать DLL как базу данных (если динамическая библиотека содержит помимо ресурсов еще и код, то загрузка с этим ключом проходит все равно успешно, но функции загруженной DLL не будут доступны — только ресурсы):

  // DllLoadRes.c
  #include <stdio.h>
  #include <windows.h>

  main()
  {
    HINSTANCE h;
    char buf[100];
    h=LoadLibraryEx("MyResDll.dll",0,
        LOAD_LIBRARY_AS_DATAFILE);

    LoadString(h,1,&buf[0],99);
    printf("%s\n",&buf[0]);
  }

Листинг 18 Демонстрация оптимизированной загрузки DLL, не содержащей ничего кроме ресурсов

Эта программа компилируются точно так же, как и предыдущие примеры явной компоновки — и после запуска победно выводит на экране «Hello, Word!«, подтверждая, что ресурс «строка» из динамической библиотеки был успешно загружен! Аналогичным способом можно загружать ресурсы из исполняемых файлов; с этой точки зрения они ничем не отличаются от динамических библиотек.

Аннотация
Статьи из книги

[Заказать книгу в магазине «Мистраль»]

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Какая версия windows 10 лучше pro или домашняя
  • Как удалить удаленное приложение из списка приложений windows 10
  • Как узнать пароль от вай фай на ноутбуке windows 11
  • Как поменять расширение файла в windows 10 на rar
  • Установка windows 7 на gigabyte h410m s2h v3