Използване на интерфейси при работа с DLL файлове
Ако някога сте работили с DLL, вече знаете, че DLL има специална функция DllMain(). Тази функция е подобна на WinMain или main() в смисъл, че е вид входна точка към DLL. Операционната система автоматично извиква тази функция, когато DLL се зарежда и разтоварва. Обикновено тази функция не се използва за нищо друго.
Има два метода за свързване на DLL към проект - ранно (по време на компилация на програма) и късно (по време на изпълнение на програмата) свързване. Методите се различават по това как се зарежда DLL и как се извикват функциите, реализирани и експортирани от DLL.
Ранно свързване (по време на компилация на програма)
С този метод на свързване операционната система автоматично зарежда DLL по време на стартиране на програмата. Изисква се обаче .lib файлът (библиотечен файл), съответстващ на този DLL, да бъде включен в проекта за разработка. Този файл дефинира всички експортирани DLL обекти. Декларациите могат да съдържат обикновени C функции или класове. Всичко, което клиентът трябва да направи, е да използва този .lib файл и да включи заглавния файл на DLL - и операционната система автоматично ще зареди този DLL. Както можете да видите, този метод изглежда много лесен за използване. всичко е прозрачно. Въпреки това трябва да сте забелязали, че кодът на клиента трябва да се компилира отново, когато DLL кодът се промени и съответно се генерира нов .lib файл. Дали е удобно за вашето приложение зависи от вас. DLL може да декларира функциите, които иска да експортира по два начина. Стандартният метод е да се използват .def файлове. Такъв .def файл е просто списък на функциите, експортирани от DLL.
И в двата случая всичко, което клиентът трябва да направи, е да добави файла myfirstdll.lib към проекта и да включи заглавен файл, който декларира обектите, които ще бъдат импортирани от DLL, ислед това използвайте тези обекти (функции, класове и променливи) точно както ако са дефинирани и внедрени локално в проекта. Сега нека разгледаме друг метод за използване на DLL, който често е по-удобен и по-мощен.
Късно обвързване (докато програмата работи)
DLL, предназначен за късно свързване, обикновено използва .def файл, за да определи кои обекти иска да експортира. Ако не искате да използвате .def файл, можете просто да използвате префикса __declspec(dllexport) преди експортираните функции. И двата метода правят едно и също нещо. Клиентът зарежда DLL, като предава името на файла на DLL на функцията Win32 LoadLibrary().Тази функция връща манипулатор HINSTANCE, който се използва за работа с DLL и е необходим за разтоварване на DLL от паметта, когато вече не е необходим. След зареждане на DLL, клиентът може да получи указател към всяка функция, използвайки функцията GetProcAddress(), като използва името на необходимата функция като параметър.
Както можете да видите, кодът е доста ясен. А сега да видим как може да се реализира работата с "класове". Както бе споменато по-рано, ако се използва късно свързване, няма директен начин за импортиране на класове от DLL, така че трябва да внедрим „функционалността“ на класа с интерфейс, съдържащ всички публични функции, с изключение на конструктора и деструктора. Интерфейсът ще бъде обикновена C/C++ структура, съдържаща само виртуални абстрактни членски функции. Действителният клас в DLL ще наследи от тази структура и ще реализира всички функции, дефинирани в интерфейса. Сега, за да получите достъп до този клас от клиентското приложение, всичко, което трябва да се направи, е да експортирате функциите в стил C, съответстващи на екземпляра на класа, и да ги свържете към конкретенни интерфейс, така че клиентът да може да ги използва. За прилагането на този метод са необходими още две функции, едната от които ще създаде интерфейса, а втората ще изтрие интерфейса, след като приключи работата с него. Примерно изпълнение на тази идея е показано по-долу.
Тази информация е достатъчна, за да усетите цялото удобство от използването на интерфейсите. Приятно програмиране!