ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Category:

Создание библиотеки классов (.dll): экспорт классов

Начало тут:
1. библиотека классов;
2. создание библиотеки классов (в тексте);
3. создание библиотеки классов (объектный файл);
4. создание библиотеки классов (.lib);
5. создание библиотеки классов (.dll): теория;
6. создание библиотеки классов (.dll): проект DLL.

Область применения: язык программирования C++, операционная система «Windows 7», среда разработки «Visual Studio Community 2017».

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

Это можно сделать несколькими способами. Например, на странице сайта «Microsoft Docs», посвященной опции /EXPORT компоновщика среды «Visual Studio Community 2017» таких способов перечислено целых четыре (в порядке рекомендованной приоритетности):
  1. с помощью модификатора __declspec(dllexport) в исходном коде;
  2. с помощью создания специального файла .def и перечисления экспортируемых программных сущностей в разделе EXPORTS этого файла;
  3. с помощью опции /EXPORT компоновщика;
  4. с помощью директивы препроцессора #pragma comment в исходном коде в виде:
    #pragma comment(linker, "/export: программная сущность "), где вместо фразы «программная сущность» следует ввести идентификатор конкретной программной сущности, которую нужно экспортировать.

Мы будем использовать первый (наиболее рекомендуемый) способ из этого списка, с помощью модификатора __declspec(dllexport). Тут надо понимать, что этот модификатор не входит в стандарт языка C++, а является его расширением от компании «Microsoft».

Применение этого модификатора к классам (и другим программным сущностям) несложно. На сайте «Microsoft Docs» есть подробная статья об этом. В нашей библиотеке есть два класса, которые мы будем экспортировать. Вот пример применения этого модификатора к одному из классов в файле mylib.h:

было:
class book
стало:
class __declspec(dllexport) book

то есть модификатор просто вставляется между служебным словом class и названием класса.

Готово

Вставив указанный модификатор в оба наших класса (book и buyer), запустим сборку проекта (пункт меню «Сборка – Собрать решение»). С помощью утилиты DUMPBIN проанализируем полученный файл динамической библиотеки ProjectDLL.dll (как это сделать, я описывал в предыдущем посте):



Как видно на рисунке, теперь в файле нашей динамической библиотеки появилась таблица экспорта. Эта таблица в нашем случае содержит 18 наименований, по 9 наименований на каждый из двух наших классов.

Тут важно отметить, что имена программных сущностей, попавших в таблицу экспорта, попадают в эту таблицу не в оригинальном виде, а в зашифрованном. Такое шифрование имен производит компилятор на этапе трансляции (перевода) программы с языка высокого уровня на машинный язык (объектные файлы .obj). В статьях на сайте «Microsoft Docs» такие зашифрованные имена называют «decorated names» (в дословном переводе на русский «декорированные имена»). Такую обработку имен еще называют по-английски «name mangling» (в переводе на русский «коверкание имён»), этому явлению в википедии посвящена отдельная статья на английском.

Например, рассмотрим первую строчку таблицы экспорта:
1    0     00001130    ??0book@@QAE@$$QAV0@@Z = ??0book@@QAE@$$QAV0@@Z (public: __thiscall book::book(class book &&))

Число 1 в первой колонке таблицы (ordinal) — порядковый номер строки таблицы. Число 0 во второй колонке таблицы (hint) — внутренний номер строки таблицы. Число 00001130относительный виртуальный адрес (колонка RVA таблицы), по которому находится программная сущность.

Далее ??0book@@QAE@$$QAV0@@Z — это и есть упомянутое выше зашифрованное имя программной сущности. Колонка name таблицы экспорта построена по принципу:
имя1 = имя2 (расшифровка)
где имя1 — это имя, которое будет использовать вызывающая нашу DLL программа; имя2 — зашифрованное имя программной сущности; расшифровка — собственно, расшифровка имени программной сущности, зашифрованной в имя2.

Также расшифровать зашифрованное имя можно, применив утилиту UNDNAME («C++ Name Undecorator» компании «Microsoft»), которая входит в состав среды «Visual Studio Community 2017». Я запускал ее также через командную строку среды (пункт меню Средства — Командная строка Visual Studio):



На рисунке видно, что утилита расшифровала зашифрованное имя ??0book@@QAE@$$QAV0@@Z как
public: __thiscall book::book(class book &&).

В следующем посте рассмотрим, как использовать полученную тут динамическую библиотеку ProjectDLL.dll в прикладной программе.
Tags: Образование, Программирование
Subscribe

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments