Советы по использованию Dynamic Link Libraries (DLLs) в MFCАвтор: xicoloko. Этот документ поможет начинающим программистам получить некоторые знания по разработке приложений для Windows. Когда я создаю новый проект, то наиболее изменяемые куски программы стараюсь поместить в DLL. Это даёт мне возможность изменять уже откомпилированную программу. Чтобы сделать это грамотно, дам Вам несколько советов. Многи могут спросить, а почему бы просто не использовать технологию COM ?. Ответ очевиден, конечно же COM является наиболее частым выбором в таких ситуациях. Однако, DLL это одна из живучих альтернатив для решения таких задач. Я постараюсь объяснить использование технологии DLL в рамках приложения под MFC. Одна из главных проблемм использования DLL (особенно в MFC) кроется в отладочной и релизной версиях. Эти версии не совместимы. Дело в том, что проблемма возникает при запуске отладочной версии Вашего приложения с DLL релизной версии. В таком случае Microsoft советует давать разные имена DLL (для релизной и отладочной версий). Итак релизная версия DLL остаётся в проекте с прежним именем, а отладочная версия должна выглядеть примерно так [Project Name]D.DLL. После этого Вы можете скопировать оба варианта DLL-ки в системную директорию, и быть счастливы :). Вот несколько шагов по созданию такого приложения (имя проекта AAA):
Теперь отладочная версия создаст файлы AAAD.lib и AAAD.DLL. Когда я создаю DLL, я создаю для него заголовок (include header) (думаю, что все так делают), который называю DLL header. Этот заголовок содержит объявления всех экспортируемых классов. Для большей эффективности я включаю "linking stuff" в него, теперь, чтобы использовать DLL вам не нужно добавлять lib файл в Project Settings. Мой header выглядит примерно так:
#ifndef DEF_MUDASDASDASDASDAS #define DEF_MUDASDASDASDASDAS #ifdef _DEBUG #pragma comment(lib, AAAD.lib) #else #pragma comment(lib, AAA.lib) #endif //... сдесь начинаются объявления классов #endif //MUDASDASDASDASDAS Программирование с изменениямиЧтобы использовать класс, содержащийся в DLL, достаточно объявить в Вашем проекте следующее: class AFX_EXT_CLASS CFoo
{
//...
}
В приложении, использующем этот класс, необходимо включить хедер DLL. Проблемма: Вам всё время прийдётся включать переменные-члены или методы в экспортированный класс, а для этого необходимо изменять DLL header, а это значит, что прийдётся перекомпилировать все модули, использующие DLL. Для новых методов я не знаю способа преодоления этой проблеммы, но для новых переменных такой способ есть. Вместо того, чтобы объявлять члены класса непосредственно в теле класса, вы создаёте класс с указателем на включаемый класс: class CFooImpl;
class CFoo
{
protected:
CFooImpl* m_pThis;
};
Теперь класс CFooImpl не требует объявления, чтобы использовать его из DLL. Функции-члены класса CFoo выглядят примерно так: class CFooImpl
{
public:
CString m_sName;
};
CFoo::CFoo()
{
m_pThis = new CFooImpl;
m_pThis->m_sName = _T("Unknown");
}
CFoo::~CFoo()
{
delete m_pThis;
}
Другой способ - это использовать специальных структур Windows API. Вам нужно объявить метод, который использует в качестве входных и выходных параметров LPVOID. Данные указатели - это адреса экземпляров структур. Этот приём определяет первого члена рассмотренной структуры - DWORD, который является размером.
typedef struct tagCHANGEABLE
{
DWORD dwSize;
long lBytes;
}CHANGEABLE, *LPCHANGEABLE;
BOOL CFoo::Method(LPVOID lpIn)
{
LPCHANGEABLE lpChangeable = (LPCHANGEABLE)lpIn;
if (lpChangeable->dwSize == sizeof(CHANGEABLE))
{
//...
return TRUE;
}
return FALSE;
}
Используем его: CFoo myFoo; CHANGEABLE changeable; memset(&changeable, 0, sizeof(changeable)); changeable.dwSize = sizeof(changeable); myFoo.Method(&changeable); DLL загружается при необходимостиИногда возникает ситуация, когда Вам нужно вызвать диалог или инициализировать класс. Тогда Вы решаете поместить его в DLL, но Вы не хотите, чтобы он загружался во время выполнения приложения. Вы хотите загрузить DLL только при необходимости (COM). Это тип DLL я называю Dynamic DLL (масло маслянное “Dynamic Dynamic link libraries”). Итак, Вы объявляете экспортируемую функцию как: __declspec( DLLexport )
void MyExportedFunc(DWORD dw)
{
//...
}
Необходимо включить эту функцию в файлы .def (debug и release). Отладочный def-файл будет выглядеть примерно так: ; AAAD.def : Объявление параметров модуля для DLL. LIBRARY "AAAD" DESCRIPTION 'AAAD Windows Dynamic Link Library' EXPORTS MyExportedFunc @1 ; Explicit exports can go here Теперь, чтобы использовать эту функцию, необходимо загрузить библиотеку, найдите точку входа в функцию и вызовите её. typedef void (*MYFUNC)(DWORD);
#ifdef _DEBUG
HINSTANCE hDLL = AfxLoadLibrary("AAADLLD");
#else
HINSTANCE hDLL = AfxLoadLibrary("AAADLL");
#endif
if (hDLL)
{
FARPROC pnProc = GetProcAddress(hDLL, "MyExportedFunc");
MYFUNC pnMyfunc = (MYFUNC)pnProc;
pnMyfunc(0);
FreeLibrary(hDLL);
}
Не забудьте, что для того, чтобы показать диалог, Вам необходимо позаботиться о необходимых ресурсах (AfxSetResource..). Такой подход позволяет создать копии класса. В определении класса должны использоваться только виртуальные функции (чтобы избежать "unresolved external symbol"). Это примерно также как в COM. Объявление класса будет выглядеть так: class CFoo
{
public:
virtual void Initialize (CString sName) = 0;
};
Включаем этот "interface" в другой класс, не видимый через заголовочный файл DLL. class CFooImp : public CFoo
{
public:
CFooImp();
virtual ~CFooImp();
void Initialize (CString sName)
{
m_sName = sName;
}
protected:
CString m_sName;
};
Чтобы создать образец класса (интерфейс) вам необходимо создать экспортированную функцию. __declspec(DLLexport)
CFoo* CreateFoo(DWORD dwVersion)
{
if (dwVersion == CURRENT_VERSION)
return new CFooImp;
return NULL;
}
Приложение инициализирует класс следующим образом: typedef CFoo* (*MYFUNC)(DWORD);
#ifdef _DEBUG
HINSTANCE hDLL = AfxLoadLibrary("AAADLLD");
#else
HINSTANCE hDLL = AfxLoadLibrary("AAADLL");
#endif
if (hDLL)
{
FARPROC pnProc = GetProcAddress(hDLL, " CreateFoo");
MYFUNC pnMyfunc = (MYFUNC)pnProc;
CFoo* pFoo = pnMyfunc(0);
pFoo->Initialize(_T("Hi"));
delete pFoo;
FreeLibrary(hDLL);
}
Не забудьте, что Вы не можете освободить библиотеку до тех пор пока Вы не удалите объект класса CFoo.
|