Чтение ресурсов в Dll. Нужен совет!
Значится так, была у меня прога (MDI), в главном окне которой слева торчал ControlBar, который содержал TreeCtrl и ListCtrl, причем TreeCtrl был основательно напичкан кодом, имел иконки и вызывал к жизни три разных диалога.
Но тут я осознал, что Бар выполняет чисто вспомогательные функции, перегружает проект кодом, возможно я захочу его отключить или переделать и т. п. Короче, созрела идея задействовать Бар как плагин. Я перегнал код в MFC DLL, DLL динамически присоединил к приложению. Связь с плагином организовал по типу COM, т. е. из приложения во внешнюю функцию DLL передается указатель на унифицированный базовый класс (читай интерфейс), в этой функции создается объект класса, порожденного от базового, и указатель возвращается в программу, которая юзает готовый объект (активирует плагин). ControlBar появляется, все Ок, но ни иконок ни диалогов, которые, естественно, хранятся в ресурсах DLL, нет. Теперь внимание вопрос: как мне заставить прогу когда нужно хавать ресурсы из DLL, а когда нужно - из EXE?
P.S. До этого иконы грузились так
AfxGetApp()->LoadIgon(...)
Все диалоги - порождения CDialog (что, впрочем, естественно :)).
Всем привет.
Значится так, была у меня прога (MDI), в главном окне которой слева торчал ControlBar, который содержал TreeCtrl и ListCtrl, причем TreeCtrl был основательно напичкан кодом, имел иконки и вызывал к жизни три разных диалога.
Но тут я осознал, что Бар выполняет чисто вспомогательные функции, перегружает проект кодом, возможно я захочу его отключить или переделать и т. п. Короче, созрела идея задействовать Бар как плагин. Я перегнал код в MFC DLL, DLL динамически присоединил к приложению. Связь с плагином организовал по типу COM, т. е. из приложения во внешнюю функцию DLL передается указатель на унифицированный базовый класс (читай интерфейс), в этой функции создается объект класса, порожденного от базового, и указатель возвращается в программу, которая юзает готовый объект (активирует плагин). ControlBar появляется, все Ок, но ни иконок ни диалогов, которые, естественно, хранятся в ресурсах DLL, нет. Теперь внимание вопрос: как мне заставить прогу когда нужно хавать ресурсы из DLL, а когда нужно - из EXE?
P.S. До этого иконы грузились так
AfxGetApp()->LoadIgon(...)
Все диалоги - порождения CDialog (что, впрочем, естественно :)).
Используй
HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName);
где первый параметр - значение, которое возвращает LoadLibrary, т.е. HMODULE
Используй
HICON LoadIcon(HINSTANCE hInstance, LPCTSTR lpIconName);
где первый параметр - значение, которое возвращает LoadLibrary, т.е. HMODULE
Я конечно могу передавать при инициализации плагина ему его же собственный HMODULE, правда не очень изяШное решение. Неужели нельзя получить HMODULE при инициализации библиотеки в самой библиотеке? Но это даже не самая проблема. Самая проблема - как заставить корректно создаваться диалоги. Диалоги создаются в DLL, а ресурсы пытаются читать из главного модуля, где их, естественно, нет!
Я конечно могу передавать при инициализации плагина ему его же собственный HMODULE, правда не очень изяШное решение. Неужели нельзя получить HMODULE при инициализации библиотеки в самой библиотеке? Но это даже не самая проблема. Самая проблема - как заставить корректно создаваться диалоги. Диалоги создаются в DLL, а ресурсы пытаются читать из главного модуля, где их, естественно, нет!
Есть у меня один секретный способ. :D
Только никому!... :)
HMODULE GetModuleBaseAddress()
{
MEMORY_BASIC_INFORMATION mbi;
return ((::VirtualQuery(GetModuleBaseAddress, &mbi, sizeof(mbi)) != 0) ?
(HMODULE) mbi.AllocationBase : NULL);
}
Есть у меня один секретный способ. :D
Только никому!... :)
HMODULE GetModuleBaseAddress()
{
MEMORY_BASIC_INFORMATION mbi;
return ((::VirtualQuery(GetModuleBaseAddress, &mbi, sizeof(mbi)) != 0) ?
(HMODULE) mbi.AllocationBase : NULL);
}
Спасибо за подсказку, но где брать GetModuleBaseAddress?
Ага, врубился. Хитрая конструкция получается. Только что пробовал так:
HMODULE hModule = (::VirtualQuery(this, &mbi, sizeof(mbi)) != 0) ? (HMODULE) mbi.AllocationBase : NULL;
(Вызывается из класса)
Нифига не вышло.
О! Есть, заработало! Спасибо. Теперь буду пытаться оживлять диалоги. А може у тебя и на энтот случай решение есть? Никому не скажу, чесслово :D
Спасибо за подсказку, но где брать GetModuleBaseAddress?
Ага, врубился. Хитрая конструкция получается. Только что пробовал так:
HMODULE hModule = (::VirtualQuery(this, &mbi, sizeof(mbi)) != 0) ? (HMODULE) mbi.AllocationBase : NULL;
(Вызывается из класса)
Нифига не вышло.
О! Есть, заработало! Спасибо. Теперь буду пытаться оживлять диалоги. А може у тебя и на энтот случай решение есть? Никому не скажу, чесслово :D
А какая проблема с диалогами?
Если ты их создаешь в DLL, то в основной модуль (exe) передаешь, наверное, HWND или указатель на некоторый объект, связанный с окном диалога?
Так в чем проблема?
А какая проблема с диалогами?
Если ты их создаешь в DLL, то в основной модуль (exe) передаешь, наверное, HWND или указатель на некоторый объект, связанный с окном диалога?
Так в чем проблема?
Диалоги создаются в DLL на основе ресурсов:
CDialog myDialog;
myDialog.Create(IDD_MYDIALOG, ... );
Ну а поскольку Create рвется читать ресурсы из EXE, где их нет, то и диалог не создается.
Ладно, подумаю над этим завтра. X)-
Диалоги создаются в DLL на основе ресурсов:
CDialog myDialog;
myDialog.Create(IDD_MYDIALOG, ... );
Ну а поскольку Create рвется читать ресурсы из EXE, где их нет, то и диалог не создается.
Ладно, подумаю над этим завтра. X)-
Ох уж этот MFC... :x
Вообще-то в методе CDialog::Create происходит перебор всех загруженных модулей на предмет поиска необходимого ресурса (см. AfxFindResourceHandle в dllinit.cpp), но видимо не совсем корректно.
Я бы добавил такие методы
{
HRSRC hResource = ::FindResource(hInst, lpszTemplateName, RT_DIALOG);
HGLOBAL hTemplate = LoadResource(hInst, hResource);
BOOL bResult = CreateIndirect(hTemplate, pParentWnd, hInst);
FreeResource(hTemplate);
return bResult;
}
BOOL CBaseDialog::Create(HINSTANCE hInst, UINT nIDTemplate, CWnd* pParentWnd = NULL)
{ return Create(hInst, MAKEINTRESOURCE(nIDTemplate), pParentWnd); }
Диалоги создаются в DLL на основе ресурсов:
CDialog myDialog;
myDialog.Create(IDD_MYDIALOG, ... );
Ну а поскольку Create рвется читать ресурсы из EXE, где их нет, то и диалог не создается.
Ладно, подумаю над этим завтра. X)-
В MFC есть такая функция (глобальная) AfxSetResourceHandle(HINSTANCE) - после того, как ты вызовишь ее с нужным HMODULE (для Exe или Dll), то все MFC'шные функции (будь то CDialog::Create, или CString::LoadString итп) будут грузить себя из ресурсов указанного HMODULE.
Так что перед созданием диалога, попробуй поиграться с этой функцией (не стоит забывать все возвращать на место).
А затем на InitInstance приложения сделать LoadLibrary для этой ресурсной DLL'ки (то есть не линковать ее статически), и сразу же вызвать AfxSetResourceHandle(HMODULE ресурсной DLL).
Тогда выиграем следующее:
1. Из любого модуля можно пользовать любые ресурсы из всего приложения.
2. Лего настраивается локализация (переводишь все ресурсы на другой язык, компилишь ресурсную DLL'ку с другим именем - и уже пользователю можно опционально задавать язык, на котором будет его приложение)
И еще, раз уж у вас так раскиданы ресурсы (EXE использует ресурсы из DLL) - то лучше создать отдельную DLL'ку с ресурсами (из всех модулей), при этом не забыть отрубить линковку ресурсов во всех модулях.
А затем на InitInstance приложения сделать LoadLibrary для этой ресурсной DLL'ки (то есть не линковать ее статически), и сразу же вызвать AfxSetResourceHandle(HMODULE ресурсной DLL).
Тогда выиграем следующее:
1. Из любого модуля можно пользовать любые ресурсы из всего приложения.
2. Лего настраивается локализация (переводишь все ресурсы на другой язык, компилишь ресурсную DLL'ку с другим именем - и уже пользователю можно опционально задавать язык, на котором будет его приложение)
Спасибо за советы, сейчас буду пробовать.
А создавать отдельную DLL с ресурсами не желательно по определению. Вся суть переделки первой версии программы как раз сводилась к тому, чтобы она хавала унифицированные плагины, коих может быть n-ное количество, и не плакала, если вдруг чего не найдет. Плагин добавляется простым переписыванием DLL в специальную папку. Т. е. до того, как прога найдет DLL в папке Plugins, она вообще не подозревает о его существовании. В такой ситуации общие ресурсы - не лучшее решение.
В MFC есть такая функция (глобальная) AfxSetResourceHandle(HINSTANCE) - после того, как ты вызовишь ее с нужным HMODULE (для Exe или Dll), то все MFC'шные функции (будь то CDialog::Create, или CString::LoadString итп) будут грузить себя из ресурсов указанного HMODULE.
Так что перед созданием диалога, попробуй поиграться с этой функцией (не стоит забывать все возвращать на место).
Отлично! Все заработало, когда я переопределил DoModal следующим образом:
int CBaseCreateFolderDlg::DoModal()
{
HMODULE hMainModule = (HMODULE)AfxGetResourceHandle();
AfxSetResourceHandle(GetModuleBaseAddress());
int nResult = CDialog::DoModal();
AfxSetResourceHandle(hMainModule);
return nResult;
}
Всем спасибо! :)