Как запустить модуль динамической компоновки windows

Использование библиотеки динамической компоновки в среде Windows, Русские Блоги, лучший сайт для обмена техническими статьями программиста.

В тексте«Создание DLL библиотеки динамической компоновки в среде Windows»Библиотека динамической компоновки была создана в формате. Здесь будет представлено конкретное использование динамической библиотеки (здесь вам нужно использовать файл DLL и файл LIB, если вы хотите пропустить предыдущее, вы можетекликните сюдаЗагрузите файл DLL и файл LIB)

Неявно загрузить библиотеку динамической компоновки

1. Откройте Visual Studio и создайте новый проект.



Для неявной загрузки необходимо использовать библиотеку импорта динамической библиотеки — файл .lib. Есть три основных способа добавить файл .lib в проект:

  • Скопируйте файл .lib в каталог проекта, а затем сделайте ссылку с помощью комментария #pragma (lib, «DllTest.lib») в программе;
  • Не копируйте файл .lib напрямую, а обращайтесь к нему по абсолютному пути, по которому расположен файл .lib. В программе укажите абсолютный путь через #pragma comment (lib, «D: XX DllTest.lib»);
  • Щелкните правой кнопкой мыши проект -> Свойства -> Компоновщик -> Ввод -> Дополнительные зависимости, введите абсолютный путь, по которому находится файл .lib, в столбце дополнительных зависимостей, например «D: XX DllTest.lib» в программе. Директива #pragma comment (lib, «DllTest.lib») больше не нужна;
    Независимо от того, каким образом файл .lib добавлен в проект, файл .lib больше не нужен после успешной компиляции исполняемого файла .exe, и нет необходимости приносить его при публикации. Освободите файл .lib.
    Исходная ссылка: https://blog.csdn.net/dcrmg/article/details/53437913

Здесь мы применяем первый метод.
Сначала щелкните проект правой кнопкой мыши и выберите «Создать», чтобы создать стандартный каталог проекта.


Поместите NewDLL.dll в каталог Debug в каталоге проекта и поместите NewDLL.lib и NewDLL.h в каталог Implicit в каталоге проекта.


2. Отредактируйте исходный файл.
Запишите исходный код в исходный файл ImplicitDLL.cpp, код выглядит следующим образом:

#include "stdafx.h"
#include "NewDLL.h"
#include <stdio.h>
#include <stdlib.h>

#pragma comment (lib,"NewDll.lib")
extern "C" _declspec(dllexport) void Fun();
extern "C" _declspec(dllexport) void Out(int i);
extern "C" _declspec(dllexport) int Add(int a,int b);

int _tmain(int argc, _TCHAR* argv[])
{
	Fun();
	Out(100);
	printf("3+4=%dn",Add(3,4));
	system("pause");
	return 0;
}


Результат выполнения:

Вы можете видеть, что вызов функции в DLL выполнен успешно.

Явная загрузка библиотеки динамической компоновки

1. Откройте Visual Studio и создайте новый проект с именем ExplicitDLL в соответствии с описанным выше методом. (Вы также можете назвать его в соответствии с вашими привычками)
2. Создайте проект для создания стандартной файловой директории.
3. Поместите NewDLL.dll в каталог Debug в каталоге проекта.

4. Напишите исходный код.
Отредактируйте исходный код в ExplicitDLL.cpp, содержимое кода будет следующим:

#include "stdafx.h"
#include <stdlib.h>
#include <Windows.h>

typedef void (*_Fun)();
typedef void (*_Out)(int i);
typedef int (*_Add)(int a,int b);
int _tmain(int argc, _TCHAR* argv[])
{
	// Загружаем DLL
	HMODULE hDll = LoadLibrary(_T("NewDLL.dll"));
	if(hDll == NULL)
	{
		printf(«Не удалось загрузить динамическую библиотеку  n»);
		return 0;
	}
	// Считываем адрес функции и возвращаем
	_Fun Fun=(_Fun)GetProcAddress(hDll,"Fun");
	Fun();
	_Out Out=(_Out)GetProcAddress(hDll,"Out");
	Out(22);
	_Add Add=(_Add)GetProcAddress(hDll,"Add");
	printf("3+4=%dn",Add(3,4));
	// Выгружаем DLL
	FreeLibrary(hDll);
	system("pause");
	return 0;
}

Полученные результаты:

ИЗМЕНЕНИЕ СИСТЕМНЫХ ЭЛЕМЕНТОВ

Примерные вопросы для контроля усвоения темы «Графический интерфейс пользователя в ОС»

1. Что такое оболочка ОС и ее назначение.

Ответ: Оболочка ОС — визуальное представление файлов, папок, элементов управления. Предназначена для удобной работы пользователя с ОС.

2. Когда оболочка может отсутствовать в ОС.

Ответ: В некоторых случаях, например, во встраиваемых контроллерах и других специализированных приложениях, оболочка может отсутствовать.

3. Что такое интерфейс командной строки.

Ответ: Разновидность текстового интерфейса между человеком и компьютером, в котором инструкции компьютеру даются в основном путём ввода с клавиатуры текстовых строк (команд), также известен под названием консоль.

4. Что такое графический интерфейс пользователя.

Ответ: Разновидность пользовательского интерфейса, в котором элементы интерфейса (меню, кнопки, значки, списки и т. п.), представленные пользователю на дисплее, исполнены в виде графических изображений.

5. Основная парадигма построения интерфейсов современных ОС.

Ответ: Интуитивно-понятные, стандартизированные, надёжные, доступные.

6. Что такое скрипт.

Ответ: Краткое описание действий, выполняемых системой.

7.Основные черты современных командных процессоров. Ответ: многозадачность, графический интерфейс.

8.Основное преимущества интерфейса командной строки:

Ответ: Любую команду можно вызвать небольшим количеством нажатий; легкость автоматизации.

9. Механизм передачи параметров в CLI Unix, DOS, OS/2.

Ответ: Для того что бы передать параметры используется командная строка или консоль.

10.Основной недостаток интерфейса командной строки.

Ответ: Интерфейс командной строки не является «дружелюбным» для пользователей, которые начали знакомство с компьютером с графического

27

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

11.Основное преимущества графического интерфейса пользователя.

Ответ: Графический интерфейс является «дружелюбным» для пользователей, которые начали знакомство с компьютером с графического интерфейса.

12.Основные недостатки графического интерфейса пользователя.

Ответ: Невозможность автоматизации, если она не была заложена автором программы; большее потребление памяти в сравнении с текстовым интерфейсом; сложнее организовать удаленную работу.

13.Основное требования к графическому интерфейсу пользователя.

Ответ: Концепция «делай то, что я имею в виду» или DWIM (англ. Do What I Mean). DWIM требует, чтобы система работала предсказуемо, чтобы пользователь заранее интуитивно понимал, какое действие выполнит программа после получения его команды.

14.Основные свойства графического интерфейса пользователя.

Ответ:

1)Поддерживается многооконный режим работы с экраном терминала. В любой момент времени пользователь может образовать новое окно и связать его с нужной программой, которая работает с этим окном как с отдельным терминалом. Окна можно перемещать, изменять их размер, временно закрывать и т.д.

2)Во всех современных разновидностях графического интерфейса поддерживается управление мышью.

15.Проблемы решаемые Microsoft при создании Windows. Ответ: Затрудняюсь ответить.

16.Главный компонент современных графических оболочек.

Ответ: PowerShell, который является встроенным компонентом ОС начиная с

Windows 7 и Windows 2008 Server.

17.Где впервые был применен графический интерфейс пользователя.

Ответ: Впервые графический интерфейс был применен в 1983г. в компьютерах Apple Computer Lisa. В 1984 г. появился графический интерфейс Apple

Macintosh.

18.Чем отличается GUI Windows от первого графический интерфейса пользователя.

Ответ: В GUI пользователь имеет произвольный доступ (с помощью устройств

28

ввода — клавиатуры, мыши, джойстика) ко всем видимым экранным объектам (элементам интерфейса) и осуществляет непосредственное манипулирование ими. В подавляющем большинстве систем GUI является надстройкой для операционной системы.

19.Определение окна в Windows.

Ответ: Чтобы быстро определить окно, укажите на его кнопку на панели задач. При наведении указателя на кнопку панели задач выполняется эскизный предварительный просмотр окна, которое может содержать документ, фотографию или даже воспроизводимое видео.

20.Механизм работы с окнами в Win32 API.

Ответ: Win32 API: прикладной интерфейс для Windows Взаимоотношения между ними строятся по принципу «родитель-ребенок». Родительское окно может владеть одним или несколькими дочерними окнами. Дочерние окна всегда располагаются поверх родительского окна и закрываются вместе с ним. Родительское окно может быть дочерним окном другого окна, выступающего для него родительским. У окна может быть несколько дочерних окон, но только одно родительское. Если окно создается без родителя, то Windows назначает ему в качестве владельца всю рабочую область экрана.

21.Основные параметры функций работы с окнами.

Ответ: Чтобы не переключаться между окнами, и не менять вручную размер окон, вы можете расположить каждое окно на пол экрана. Таким образом, вам будет доступно одновременно 2 окна и вы сможете с ними работать, не переключаясь между ними.

22.Классы окон.

Ответ: Edit, Listbox, ComboBox, ScrollBar, Buttot, Static.

23.Стили окон.

Ответ: Windows 7 поддерживает несколько вариантов оформления пользовательского интерфейса: Windows Aero; Windows 7 упрощённый стиль, Классический, Пользовательские темы оформления.

24.Родительское окно, дочернее окно и окно хозяин.

Ответ: Когда родительское окно видимое, связанные с ним дочерние окна также видимы. Точно так же, когда родительское окно скрыто, его дочерние окна также скрыты.

25.Механизм передачи данных из окон и между окнами.

Ответ: Файлы копируются в буфер обмена перед тем как начать передаваться между папками.

26.Что значит окно верхнего уровня.

29

Ответ: Активное окно (active window) — окно верхнего уровня прикладной программы, с которым пользователь в настоящее время работает.

27.Перечислить основные элементы графического интерфейса пользователя и их параметры.

Ответ: Рабочий стол с пиктограммами, панель задач на которой размешаются программные кнопки и индикаторы. Панель быстрого запуска. Главное меню (кнопка пуск), контекстное меню.

28.Как выбрать варианты оформления GUI в Windows (метафоры рабочего стола).

Ответ: Процесс установки не отличается чем-то особенным. Запускаем

PRES1GUIWINDOWSWIN32SetupAll.exe.

29.Основные варианты оформления GUI в Windows.

Ответ: Варианты оформления GUI в Windows можно посмотреть в панели управления. Для Windows 10 можно выбрать множество различных оформлений.

30.Дополнительные средства оформления GUI в Windows. Ответ: Можно создать свои средства оформления GUI в Windows

31.Что такое панели инструментов в Windows.

Ответ: Панель инструментов — элемент графического интерфейса пользователя, предназначенный для размещения на нём нескольких других элементов.

32.Назначение элементов панели инструментов в Windows.

Ответ: Панель инструментов состоит из кнопки Пуск, значков быстрого запуска программ, кнопок открытых программ, области уведомления и кнопки свернуть все окна.

33.Что называется панелью обозревателя.

Ответ: Для быстрого и легкого доступа к средствам поиска и избранным Webузлам можно воспользоваться средством просмотра, которое называется панелью обозревателя.

34.Перечислить доступные панели обозревателя.

Ответ: В большинстве окон доступны следующие панели обозревателя:

1)Поиск, представляющая некоторые популярные средства поиска в Интернете. Можно выбрать средство поиска или воспользоваться услугами текущего поставщика.

2)Избранное, отображающая элементы, указанные пользователем в качестве избранных

файлов, папок, каналов и Web-узлов.

3)Журнал, содержащая список посещенных Web-узлов и файлов, сгруппированных по

30

дням и неделям.

4)Каналы, отображающая панель каналов, которая содержит ссылки на Webузел поставщика содержимого. Канал представляет собой особый способ получения содержимого Интернета. Вы можете подписаться на конкретные интересующие вас каналы — поставщик канала обновляет содержимое и доставляет его вам.

5)Все папки, отображающая иерархию доступных дисководов и папок в виде дерева. Эта панель обозревателя доступна только в проводнике Windows.

35.Как просмотреть содержимое компьютера.

Ответ: C помощью AIDA 64. Это программа служит для получения сведений, диагностики и тестирования аппаратных средств компьютера.

36.Как отобразить в Windows web-документ.

Ответ: Создать новую Web-страницу с помощью Microsoft Word

37.Что отображается на рабочем столе Windows независимо от стиля оформления.

Ответ: Панель управления, элементы рабочего стола (ярлыки, папки, файлы)

38.Как изменить системные элементы рабочего стола (размеры элементов экрана).

Ответ: Внешний вид Рабочего стола можно изменить с помощью его контекстного меню. Оно вызывается правым щелчком в любом свободном месте Рабочего стола.

39.Как изменить расстояния между пиктограммами.

Ответ: Для этого щелкните правой кнопкой мыши по свободному месту на рабочем столе и из появившегося меню выберите пункт Свойства (Properties). Перейдите на вкладку Оформление (Appearance) и щелкните по кнопке Дополнительно (Advanced). В появившемся диалоговом окне из выпадающего меню выберите Интервал между значками (гориз.) (Icon Spacing (Horizontal)) и измените значение в поле Размер (Size).

40.Как добавить элементы активного рабочего стола.

Ответ: Чтобы добавить новый активный элемент, следует щелкнуть на кнопке Создать. Операционная система предложит подключиться к галерее компании Microsoft, содержащей каталог We6-cTpainm, предоставляющих активное содержание.

41.Как исследовать компьютер с помощью проводника и какие графические элементы, используют при этом.

Ответ: При работе с этой программой содержимое вашего компьютера представлено в виде иерархического дерева. При этом вы можете видеть содержимое каждого диска и папки, как на вашем компьютере, так и на тех

31

компьютерах, которые связаны с вашим по компьютерной сети.

42.Линейки прокрутки, условия их появления и использования в Windows. Ответ: В Windows существует два варианта использования линеек прокрутки. Первый, когда линейка прокрутки может функционировать как часть обычного окна или диалога. Второй, когда она может действовать самостоятельно, как и любой другой элемент управления. Хотя в обоих вариантах управление линейкой прокрутки осуществляется одним и тем же способом, использование линейки прокрутки как элемента управления требует применения некоторых специальных приемов.

43.Создание папок и ярлыков ссылок на них. Отличительные особенности ярлыка от папки.

Ответ: Создание папок и ярлыков ссылок на них, создаются очень просто ПКМ, а далее: cоздать ярлыкпапку. Ярлык является прямой ссылкой на программуфайл. А папка хранит файлы и изображения.

44.Как, используя ярлыки в Windows вызвать программу с опциями.

Ответ: Найдите ярлык программы, которую вы хотите запустить и щелкните правой кнопкой мыши на нем. Это вызовет контекстное меню в нижней части экрана.

45.Что такое настройка пользовательского меню.

Ответ: Можно настроить вид учетной записи, изменив цвет заднего плана, который отображается при входе в систему, а также упорядочив область содержимого в виде почтового ящика, ежедневника и задач таким образом, чтобы информация в них отображалась в нужном порядке.

46.Как настроить главное меню Windows.

Ответ: Главное меню представляет все основные операции, которые можно выполнить на компьютере. Оно обеспечивает единообразный Доступ к программам, документам, устройствам, а также к средствам настройки операционной системы.

47.Как настроить панель задач и использовать ее для хранения активных процессов Windows.

Ответ: Для настройки панели задач нужно нажать ПКМ на неё и выбрать раздел Параметры панели задач.

48.Как управлять видимостью панели задач.

Ответ: Можно включить ее автоматическое скрытие: панель задач не будет отображаться в те моменты, когда не используется. Можно закрепить панель задач, чтобы случайно не передвинуть ее и не поменять ее размер, или поместить панель задач в определенное место и придать ей определенный вид. Поэтому сначала разместите панель задач, настройте ее размер, а затем закрепите. В этом

32

случае панель задач всегда будет на одном месте, и вам не придется ее искать.

49.Какое окно называют расщепленным (splitted).

Ответ: Это комбинированный режим, в котором окно документа расщепляется на две секции. В одной секции отображается одна информация, а во втором другая. Например, в Dremweaver можно настроить рабочую область таким образом чтобы в одной отображался html код, а во второй — планировку страницы со всеми объектами и форматированием.

50.Что такое внутренние границы окна и как их можно менять.

Ответ: Пользователь Windows может произвольно изменять размеры окна приложений при помощи мыши или клавиатуры. Изменение размеров окон может быть полезно, например, в случае если вы хотите разместить в рабочем пространстве Windows сразу несколько расположенных рядом окон приложений и переключаться между ними одним щелчком мыши.

51.Какие панели инструментов «плавают» (docking view).

Ответ: Если переместить панель инструментов в область текста, она превратится в «плавающую» панель — мини-окно с кнопками), которое располагается поверх всех остальных окон и имеет собственную кнопку Закрыть. Если панель инструментов является плавающей (не закрепленной), ее можно расположить в любом месте на экране.

52.Как изменить параметры заголовка окна и использовать заголовок для изменения размеров окна.

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

53.Какие действия предпринять с «зависшим» окном.

Ответ: Переключится на другое открытое приложение с помощью комбинации клавиш Alt+Tab. Если программа осталась зависшей, можно воспользоваться Диспетчером задач, который можно вызвать сочетанием клавиш Ctrl+Alt+Del

для Windows XP и Ctrl+Shift+Esc для Windows 10. После запуска Диспетчера задач, выбираем вкладку (Приложения) и в списке запущенных приложений, находим нашу зависшую программу. Чтобы снять задачу с зависшей программы, нужно кликнуть мышью, выделив ее и нажать кнопочку (Снять задачу). Либо здесь же в Диспетчере задач кликаем правой кнопкой мыши по не отвечающему приложению и выбираем из списка пункт (Перейти к процессу). Программа

33

диспетчера задач автоматически откроет вкладку (Процессы) и выделит зависший процесс, на котором вам останется нажать правой кнопкой мыши и выбрать из списка (Завершить процесс).

54.Что такое «ставни с паролем» на окнах. Ответ: Окна, требующие пароль для доступа

55.Как скопировать информацию из окна DOS и поместить информацию в окно

DOS.

Ответ: Через буфер обмена, выделив объект, использовать команды копировать

– вставить.

56.Как перенести изображение рабочего стола и активного окна.

Ответ: Нажимаем комбинацию Alt+пробел. Она открывает специальное системное меню активного окна. Далее нажимаем стрелку вниз на клавиатуре и выделяем второй пункт — Переместить.

57.Как выделить несколько файлов в окне. Ответ: С помощью сочетания Ctrl+клик мыши.

58.Как открыть новую папку из открытого окна. Ответ: Двойным щелчком ЛКМ.

59.Как открыть проводник в существующем окне.

Ответ: Открытое окно – это кнопка на панели задач. Щелкнуть ПКМ на нем, выбрать – «Проводник».

60.Как создать системные папки, используя ActiveX компоненты Windows. Ответ: Подробнее об ActiveX в прошлом задании

61.Где используются системные папки.

Ответ: Системная папка содержит смесь различных объектов. В ней могут присутствовать как виртуальные объекты, так и обычные файлы и папки. Если на компьютере у каждого пользователя в Windows имеется своя учетная запись, то файлы, которые он помещает на рабочий стол видны лишь внутри этой учетной записи. Они хранятся на жестком внутри папки профиля в папке содержания рабочего стола. Однако, помимо личного рабочего стола пользователя, существует общий рабочий стол, объекты которого отображаются во всех учетных записях. Например, при установке программ, ярлыки на рабочем столе появляются в каждой учетной записи. Они тоже хранятся на диске, но размещены в так называемом общем профиле. Таким образом, системная папка Рабочий стол отображает не виртуальные объекты, такие как Корзина, Мой компьютер, но и содержание сразу двух обычных папок, которые расположены по разным путям на диске.

34

62.Что такое код CLSID и глобальный идентификатор GUID.

Ответ: CLSID — аббревиатура для идентификатора класса, точно так же как GUID — аббревиатура для глобального уникального идентификатора. Обе аббревиатуры являются 128-битными (16 байт) целыми числами с малой вероятностью совпадения, поэтому они могут быть использованы в компьютерах и сетях в качестве уникальных идентификаторов. Обычно идентификатор представляется либо 16-элементным байтовым массивом либо специально отформатированной строкой шестнадцатеричных цифр (где символы a—f или A—F представляют десятичные цифры 10—15. Это строковое представление состоит либо из 32 последовательных шестнадцатеричных цифр, либо из групп, состоящих из 8, 4, 4, 4 и 12 шестнадцатеричных цифр, разделенных дефисами. Строковое представление с дефисами при необходимости может быть заключено в фигурные или круглые скобки ({}).

63.Как найти CLSID код в Windows.

Ответ:В реестре мы найдем перечень всех имеющихся ключей CLSID по этому адресу : HKEY_CLASSES_ROOTCLSID

64.Как запустить приложение из командной строки. Ответ: Описать полный путь от диска к EXEшному файлу

65.Как открыть окно из командной строки.

Ответ: Чтобы открыть окно Свойства в Командной строке с помощью клавиатуры, нажать клавиши ALT+ ПРОБЕЛ +Q.

66.Как с помощью мыши запустить приложение в окне сеанса MS-DOS. Ответ: Запуск Сеанса MS-DOS можно выполнить двумя способами: двойным щелчком на значке приложения MS-DOS или из Главного меню. Если мы хотим запустить сеанс для запуска приложения, применяется первый метод. Если мы

просто хотим поработать с операционной системой MS-DOS и использовать команды MS-DOS в режиме командной строки, надо использовать второй метод. Размерами окна можно управлять с помощью мыши, хотя это управление происходит не столь гибко, как при управлении размерами окон Windows. Размеры окна изменяются ступенями в зависимости от избранного шрифта.

67.Манипулирование с окнами при удалении программ.

Ответ: Иногда в ходе удаления приложений Windows случаются сбои, в результате чего и после завершения процедуры программа остается в списке установленных приложений. Повторные попытки запустить мастер деинсталляции вызывает сообщение об ошибке с информацией об отсутствии файлов, необходимых для корректного удаления и невозможности завершить процедуру. Несуществующая программа по-прежнему остается в списке.

Для того, что бы удалить программу нужно выполнить следующие действия:

35

Выберите в меню Пуск пункт Выполнить, введите в поле Открыть команду regedit и нажмите клавишу ВВОД. Найдите и выделите следующий раздел реестра:

HKEY_LOCAL_MACHINESoftwareMicrosoftWindowsCurrentVersionUninstal l. Выделив раздел Uninstall, выберите в меню Реестр пункт Экспорт файла реестра. В окне Экспорт файла реестра в поле Сохранить выберите Рабочий стол, введите в поле Имя файла имя uninstall и нажмите кнопку Сохранить.

68.Что такое апплет.

Ответ: Это несамостоятельный компонент программного обеспечения, работающий в контексте другого, полновесного приложения, предназначенный для одной узкой задачи и не имеющий ценности в отрыве от базового приложения.

69.Как запустить апплет из командной строки.

Ответ: Для этого из командной строки выполните: control <имя апплета> Некоторые .cpl файлы содержат более одного апплета. Поэтому для запуска определенного апплета нужно добавить параметр: srvmgr.cpl — Services, Devices

или Server; main.cpl — Fonts, Mouse, Printers или Keyboard; mmsys.cpl — Sounds или Multimedia т.е. control main.cpl printers запустит апплет принтеров.

70.Назначение .cpl файлов Windows.

Ответ: Каждый инструмент панели управления представлен файл .cpl в папке Windows системы. Файлы .cpl в папке Windows/System автоматически загружаются при запуске панели управления.

71.Как удалить апплет из Панели Управления.

Ответ: Переименовать соответствующий .cpl файл. Добавить в файл control.ini, расположенный в системной директории (обычно, Windows), нужное имя файла апплета + «=no»

72.Основные варианты запуска Windows.

Ответ: Обычный запуск, диагностический запуск, выборочный запуск.

73.Чем отличается защищенный режим ОС от безопасного.

Ответ: Безопасный режим — это режим запуска системы Windows, предназначенный для устранения неполадок, в котором используется ограниченный набор служб и компонентов. Загружаются только базовые файлы и драйверы, необходимые для запуска Windows.

74.Что такое модуль динамической компоновки.

Ответ: Вместо того, чтобы windows выполняла динамическое связывание с dll при первой загрузке приложения в оперативную память, можно связать программу с модулем библиотеки во время выполнения программы (при таком способе в процессе создания приложения не нужно использовать библиотеку

36

импорта). В частности, можно определить, какая из библиотек dll доступна пользователю, или разрешить пользователю выбрать, какая из библиотек будет загружаться. Таким образом можно использовать разные dll, в которых реализованы одни и те же функции, выполняющие различные действия. Например, приложение, предназначенное для независимой передачи данных, сможет в ходе выполнения принять решение, загружать ли dll для протокола tcp/ip или для другого протокола.

75.Как запустить модуль динамической компоновки Windows.

Ответ: Первое, что необходимо сделать при динамической загрузке dll, — это поместить модуль библиотеки в память процесса. Данная операция выполняется с помощью функции ::loadlibrary, имеющей единственный аргумент — имя загружаемого модуля. А затем выполнять программу для загрузки.

37

Соседние файлы в папке Филатова_211

  • #
  • #
  • #

Книги: [Классика] [Базы данных] [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»).

Если все прошло успешно — в текущей директории (или в директории ReleaseDebug при компиляции из оболочки) появится новый файл — «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:WINNTSystem32ntdll.dll', no matching symbolic information found.
Loaded 'F:ARTICLEPRGDLL.filesmyfirstdll.dll', no matching symbolic information found.
Loaded 'C:WINNTsystem32kernel32.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.dlln");
    return;
  }

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

  // Контроль ошибок – если вызов функции GetProcAddress
  // завершился успешно, она вернет ненулевой указатель
  if (!DllFunc)
  {
    printf("Ошибка! В MyFirstDLL "
           "отсутствует ф-ция   Demon");
    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:WINNTSystem32ntdll.dll', no matching symbolic information found.
Loaded 'C:WINNTsystem32kernel32.dll', no matching symbolic information found.

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

Loaded 'F:ARTICLEPRGDLL.filesmyfirstdll.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("%sn",&buf[0]);
  }

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

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

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

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

Новости мира IT:

  • 02.02 — Власти РФ согласовали сделку по продаже «Билайна» российскому топ-менеджменту — акции компании выросли
  • 02.02 — Сергей Брин снова начал работать программистом в Google
  • 02.02 — МТС начнёт выпускать в Твери автомобильные мультимедийные системы с навигацией
  • 02.02 — В России возник дефицит чипов для документов — проблема затронула загранпаспорта нового образца
  • 01.02 — Microsoft прекратила прямые продажи Windows 10
  • 01.02 — «Яндекс» интегрирует в поиск и другие сервисы свой аналог ChatGPT до конца года
  • 01.02 — Солнце и ветер дали Европе больше электроэнергии, чем любой другой источник в 2022 году
  • 01.02 — Google тестирует на своих сотрудниках потенциальных конкурентов ChatGPT, включая бот Apprentice Bard
  • 01.02 — Создатель ChatGPT разработал инструмент для выявления текстов, написанных ИИ
  • 30.01 — Роскомнадзор получил более 44 тыс. жалоб о неправомерной обработке персональных данных в 2022 году
  • 30.01 — На Apple подали в суд из-за сбора данных пользователей
  • 30.01 — «Ростелеком», возможно, интересуется покупкой «Мегафона»
  • 30.01 — В идеале Apple стремится создать очки дополненной реальности, которые можно носить весь день
  • 30.01 — Российские мобильные операторы перешли на отечественную техподдержку
  • 30.01 — Продажа «Билайна» российскому топ-менеджменту затягивается
  • 27.01 — «Яндекс» попал в десятку самых посещаемых сайтов в мире
  • 27.01 — В списке кредиторов криптобиржи FTX оказались Apple, Google, Amazon, Microsoft, а также авиакомпании, СМИ и университеты
  • 27.01 — IBM и SAP показали хорошие квартальные результаты, но всё равно сокращают рабочие места
  • 27.01 — От антимонопольного иска против Google выиграет Apple и другие компании
  • 26.01 — Власти США подали иск, чтобы заблокировать сделку между Microsoft и Activision Blizzard

Архив новостей

  • 4VPS.SU: виртуальные серверы в 17 странах мира на любой бюджет

Библиотеки классов

DLL (англ. Dynamic Link Library — «библиотека динамической компоновки», «динамически подключаемая библиотека») в операционных системах Microsoft Windows и IBM OS/2 — динамическая библиотека, позволяющая многократное использование различными приложениями.

Disclaimer

Во-первых, надо отметить, что «классические» DLL содержат функции. А .NET, как мы уже знаем, оперирует класами и пространствами имён. Поэтому DLL, создаваемые в .NET не совместимы с обычными. (Но если очень надо, то как-то это реализуется. Технология Windows Forms использует Win32 API)

Во-вторых, в рамках нашего курса вообще нет особой необходимости в DLL (просто не будет достаточно сложных проектов).

Но, на демо-экзамене и worldskliis в модуле тестирование может встретится задание написать библиотеку классов и unit-тесты для неё. Поэтому надо знать что это такое и уметь реализовать.

Содержание

  • Создание
  • Подключение
  • Динамическая загрузка сборок и позднее связывание

Нередко различные классы и структуры оформляются в виде отдельных библиотек, которые компилируются в файлы dll и затем могут подключать в другие проекты. Благодаря этому мы можем определить один и тот же функционал в виде библиотеки классов и подключать в различные проекты или передавать на использование другим разработчикам.

Создание

Возьмем имеющийся проект приложения .NET Framework, например, созданный в прошлых темах. В структуре проекта нажмем правой кнопкой на название решения и далее в появившемся контекстном меню выберем Добавить -> Создать проект…

Далее в списке шаблонов проекта найдем пункт Библиотека классов (.NET Framework). Будьте внимательны, в списке шаблонов несколько вариантов «Библиотек классов» под разные платформы:

После создания проекта он появится в решении, в моем случае с названием ClassLibrary:

По умолчанию новый проект имеет один пустой класс Class1 в файле Class1.cs. Мы можем этот файл удалить или переименовать, как нам больше нравится.

namespace ClassLibrary
{
    public class Class1
    {
    }
}

Например, переименуем файл Class1.cs в Person.cs, а класс Class1 в Person. Определим в классе Person простейший код:

public class Person
{
    public string name;
    public int age;
}

ОБРАТИТЕ ВНИМАНИЕ!!! Студия может создать класс без модификатора public — установите его, иначе не сможете использовать этот класс в других проектах.

Теперь скомпилируем библиотеку классов. Для этого нажмём правой кнопкой на проект библиотеки классов и в контекстном меню выберем пункт Собрать.

После компиляции библиотеки классов в папке проекта в каталоге bin/Debug мы сможем найти скомпилированный файл dll (ClassLibrary.dll).

Подключение

Подключим созданную библиотеку классов в основной проект. Для этого в основном проекте нажмем правой кнопкой на Ссылки (раньше этот пункт назывался Зависимости) и в контекстном меню выберем пункт Добавить ссылку…:

Далее нам откроется окно для добавления библиотек. В этом окне выберем пункт Проекты -> Решение, который позволяет увидеть все библиотеки классов из проектов текущего решения и поставим отметку рядом с нашей библиотекой.

Если наша библиотека вдруг представляет файл dll, который не связан ни с каким проектом в нашем решении, то с помощью кнопки Обзор мы можем найти местоположение файла dll и также его подключить.

После успешного подключения библиотеки в главном проекте изменим класс Program, чтобы он использовал класс Person из библиотеки классов:

using System;
using ClassLibrary;    // подключение пространства имен из библиотеки классов
 
namespace HelloApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Person tom = new Person { name = "Tom", age = 35 };
            Console.WriteLine(tom.name);
        }
    }
}

Такое подключение библиотек назвается статическим.

Динамическая загрузка сборок и позднее связывание

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

Но также мы можем сами динамически подгружать другие сборки, на которые в проекте нет ссылок.

Для управления сборками в пространстве имен System.Reflection имеется класс Assembly. С его помощью можно загружать сборку, исследовать ее.

Чтобы динамически загрузить сборку в приложение, надо использовать статические методы Assembly.LoadFrom() или Assembly.Load().

Метод LoadFrom() принимает в качестве параметра путь к сборке. Например, исследуем сборку на наличие в ней различных типов:

static void Main(string[] args)
{
    Assembly asm = Assembly.LoadFrom("MyApp.dll");
 
    Console.WriteLine(asm.FullName);
    // получаем все типы из сборки MyApp.dll
    Type[] types = asm.GetTypes();
    foreach(Type t in types)
    {
        Console.WriteLine(t.Name);
    }
    Console.ReadLine();
}

В данном случае для исследования указывается сборка MyApp.dll. Здесь использован относительный путь, так как сборка находится в одной папке с приложением. Стоит учитывать, что загрузке подлежат сборки с расширением dll, но не exe.

Метод Load() действует аналогично, только в качестве его параметра передается дружественное имя сборки, которое нередко совпадает с именем приложения: Assembly asm = Assembly.Load("MyApp");

Получив все типы сборки с помощью метода GetTypes(), мы можем исследовать их или применить в своем проекте.

Исследование типов

Класс System.Type представляет изучаемый тип, инкапсулируя всю информацию о нем. С помощью его свойств и методов можно получить эту информацию. Некоторые из его свойств и методов:

  • Метод FindMembers() возвращает массив объектов MemberInfo данного типа
  • Метод GetConstructors() возвращает все конструкторы данного типа в виде набора объектов ConstructorInfo
  • Метод GetEvents() возвращает все события данного типа в виде массива объектов EventInfo
  • Метод GetFields() возвращает все поля данного типа в виде массива объектов FieldInfo
  • Метод GetInterfaces() получает все реализуемые данным типом интерфейсы в виде массива объектов Type
  • Метод GetMembers() возвращает все члены типа в виде массива объектов MemberInfo
  • Метод GetMethods() получает все методы типа в виде массива объектов MethodInfo
  • Метод GetProperties() получает все свойства в виде массива объектов PropertyInfo
  • Свойство Name возвращает имя типа
  • Свойство Assembly возвращает название сборки, где определен тип
  • Свойство Namespace возвращает название пространства имен, где определен тип
  • Свойство IsArray возвращает true, если тип является массивом
  • Свойство IsClass возвращает true, если тип представляет класс
  • Свойство IsEnum возвращает true, если тип является перечислением
  • Свойство IsInterface возвращает true, если тип представляет интерфейс

Получение типа

Чтобы управлять типом и получать всю информацию о нем, нам надо сперва получить данный тип. Это можно сделать тремя способами: с помощью ключевого слова typeof, с помощью метода GetType() класса Object и применяя статический метод Type.GetType().

  • Получение типа через typeof:

    class Program
    {
        static void Main(string[] args)
        {
            Type myType = typeof(User);
            
            Console.WriteLine(myType.ToString());
            Console.ReadLine();
        }
    }
    
    public class User
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public User(string n, int a)
        {
            Name = n;
            Age = a;
        }
        public void Display()
        {
            Console.WriteLine($"Имя: {Name}  Возраст: {Age}");
        }
        public int Payment(int hours, int perhour)
        {
            return hours * perhour;
        }
    }

    Здесь определен класс User с некоторым набором свойств и полей. И чтобы получить его тип, используется выражение Type myType = typeof(User);

  • Получение типа с помощью метода GetType класса Object:

    User user = new User("Tom", 30);
    Type myType = user.GetType();

    В отличие от предыдущего примера здесь, чтобы получить тип, надо создавать экземпляр класса.

  • И третий способ получения типа — статический метод Type.GetType():

    Type myType = Type.GetType("TestConsole.User", false, true);

    Первый параметр указывает на полное имя класса с пространством имен. В данном случае класс User находится в пространстве имен TestConsole. Второй параметр указывает, будет ли генерироваться исключение, если класс не удастся найти. В данном случае значение false означает, что исключение не будет генерироваться. И третий параметр указывает, надо ли учитывать регистр символов в первом параметре. Значение true означает, что регистр игнорируется.

    В данном случае класс основной программы и класс User находятся в одном проекте и компилируются в одну сборку exe. Однако может быть, что нужный нам класс находится в другой сборке dll. Для этого после полного имени класса через запятую указывается имя сборки:

    Type myType = Type.GetType("TestConsole.User, MyLibrary", false, true);

Позднее связывание

С помощью динамической загрузки мы можем реализовать технологию позднего связывания. Позднее связывание позволяет создавать экземпляры некоторого типа, а также использовать его во время выполнения приложения.

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

Ключевую роль в позднем связывании играет класс System.Activator. С помощью его статического метода Activator.CreateInstance() можно создавать экземпляры заданного типа.

Например, динамически загрузим сборку и вызовем у ней некоторый метод. Допустим, загружаемая сборка MyApp.dll представляет следующий класс:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GetResult(6, 100, 2));
 
        Console.ReadLine();
    }
 
    public static double GetResult(int percent, double capital, int year)
    {
        for (int i = 0; i < year; i++ )
        {
            capital += capital /100 * percent;
        }
        return capital;
    }
}

Итак, у нас стандартный класс Program с двумя методами. Теперь динамически подключим сборку с этой библиотекой в другой программе и вызовем ее методы.

Пусть наша основная программа будет выглядеть так:

static void Main(string[] args)
{
    try
    {
        Assembly asm = Assembly.LoadFrom("MyApp.dll");
         
        Type t = asm.GetType("MyApp.Program", true, true);
         
        // создаем экземпляр класса Program
        object obj = Activator.CreateInstance(t);
         
        // получаем метод GetResult
        MethodInfo method = t.GetMethod("GetResult");
         
        // вызываем метод, передаем ему значения для параметров и получаем результат
        object result = method.Invoke(obj, new object[] { 6, 100, 3 });
        Console.WriteLine((result));
    }
    catch(Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    Console.ReadLine();
}

Исследуемую сборку можно получить с помощью выражения Assembly asm = Assembly.LoadFrom("MyApp.dll").

Затем с помощью метода GetType получаем тип — класс Program, который находится в сборке MyApp.dll: Type t = asm.GetType("MyApp.Program", true, true);

Получив тип, создаем его экземпляр: object obj = Activator.CreateInstance(t). Результат создания — объект класса Program представляет собой переменную obj.

И в конце остается вызвать метод. Во-первых, получаем сам метод: MethodInfo method = t.GetMethod("GetResult");. И потом с помощью метода Invoke вызываем его: object result = method.Invoke(obj, new object[] { 6, 100, 3 }). Здесь первый параметр представляет объект, для которого вызывается метод, а второй — набор параметров в виде массива object[].

Так как метод GetResult возвращает некоторое значение, то мы можем его получить из метода в виде объекта типа object.

Если бы метод не принимал параметров, то вместо массива объектов использовалось бы значение null: method.Invoke(obj, null)

В сборке MyApp.dll в классе Program также есть и другой метод — метод Main. Вызовем теперь его:

Console.WriteLine("Вызов метода Main");
method = t.GetMethod("Main", BindingFlags.DeclaredOnly 
    | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static);
     
method.Invoke(obj, new object[]{new string[]{}});

Так как метод Main является статическим и не публичным, то к нему используется соответствующая битовая маска BindingFlags.NonPublic | BindingFlags.Static. И поскольку он в качестве параметра принимает массив строк, то при вызове метода передается соответствующее значение: method.Invoke(obj, new object[]{new string[]{}})

Понравилась статья? Поделить с друзьями:
  • Как запустить приложение для андроид на windows 11
  • Как запустить планировщик заданий в windows 10 в безопасном режиме
  • Как запустить модем мтс на windows 10
  • Как запустить приложение вместе с windows
  • Как запустить мобильный хот спот на windows 10