Программирование на Visual C++. Архив рассылки - Алекс Jenter
Шрифт:
Интервал:
Закладка:
Каждый из способов имеет свои достоинства и недостатки. В случае неявного подключения все библиотеки, используемые приложением, загружаются в момент его запуска и остаются в памяти до его завершения (даже если другие запущенные приложения их не используют). Это может привести к нерациональному расходу памяти, а также заметно увеличить время загрузки приложения, если оно использует очень много различных библиотек. Кроме того, если хотя бы одна из неявно подключаемых библиотек отсутствует, работа приложения будет немедленно завершена. Явный метод лишён этих недостатков, но делает программирование более неудобным, поскольку требуется следить за своевременными вызовами LoadLibrary(Ex) и соответствующими им вызовами FreeLibrary, а также получать адрес каждой функции через вызов GetProcAddress.
В Visual C++ 6.0 появился ещё один способ подключения DLL, сочетающий в себе почти все достоинства двух рассмотренных ранее методов – отложенная загрузка DLL (delay-load DLL). Отложенная загрузка не требует поддержки со стороны операционной системы (а значит будет работать даже под Windows 95), а реализуется линкером Visual C++ 6.0.
При отложенной загрузке DLL загружается только тогда, когда приложение обращается к одной из содержащихся в ней функций. Это происходит незаметно для программиста (то есть вызывать LoadLibrary/GetProcAddress не требуется). После того как работа с функциями библиотеки завершена, её можно оставить в памяти или выгрузить посредством функции __FUnloadDelayLoadedDLL. Вызов этой функции – единственная модификация кода, которую может потребоваться сделать программисту (по сравнению с неявным подключением DLL). Если требуемая DLL не обнаружена, приложение аварийно завершается, но и здесь ситуацию можно исправить, перехватив исключение с помощью конструкции __try/__except. Как видим, отложенная загрузка DLL – весьма удобное средство для программиста.
Теперь рассмотрим, каким образом описанные способы подключения DLL используются на практике. Для этого будем считать, что нам требуется вызвать функцию X, экспортируемую библиотекой MyLib.dll. Пусть функция X имеет простейший прототип: void X(void);
Будем также считать, что библиотека импорта находится в файле MyLib.lib.
Неявное подключение
Первое, что нам нужно сделать – это передать линкеру имя библиотеки импорта нашей DLL. Для этого необходимо открыть окно настройки проекта (Project->Settings) и на вкладке Link дописать "MyLib.lib" в конец списка Object/Library modules. Альтернативный подход заключается в использовании директивы #pragma. В нашем случае необходимо вставить в код программы следующую строку:
#pragma comment(lib,"MyLib")
Второе, что нужно проделать – это добавить объявление функции в код программы (обычно объявления функций, экспортируемых DLL, сводятся в заголовочный файл – тогда требуется просто подключить его). Для нашей функции X объявление выглядит так:
__declspec(dllimport) void X(void);
Вот и всё. Теперь к функции X можно обращаться, как и к любой другой функции, статически прилинкованной к нашей программе:
X();
Явное подключение
Как уже говорилось ранее, при явном подключении к DLL программист должен сам позаботиться о загрузке библиотеки, получении адреса функций и выгрузке библиотеки. Таким образом последовательность шагов может быть следующей.
Загружаем библиотеку:
HINSTANCE hLib = LoadLibrary("MyLib.dll");
Получаем указатель на функцию и вызываем её:
void (*X)();
(FARPROC &)X = GetProcAddress(hLib, "X");
X();
Выгружаем библиотеку из памяти:
FreeLibrary(hLib);
Отложенная загрузка
Сначала необходимо повторить шаги, которые мы проделывали при неявном подключении: передать линкеру имя библиотеки импорта и добавить в программу объявление функции X. Теперь, чтобы отложенная загрузка заработала, нужно добавить ключ линкера /DELAYLOAD:MyLib.dll и прилинковать к приложению библиотеку Delayimp.lib, реализующую вспомогательные функции механизма отложенной загрузки. Хотя эти опции можно добавить в настройки проекта, я предпочитаю использовать директивы #pragma:
#pragma comment(lib, "Delayimp")
#pragma comment(linker, "/DelayLoad:MyLib.dll")
Если после вызова функции X нам требуется выгрузить библиотеку MyLib.dll из памяти, можно воспользоваться функцией FUnloadDelayLoadedDLL. Чтобы эта функция работала корректно, необходимо добавить ещё один ключ линкера /DELAY:unload. Кроме того, нужно подключить заголовочный файл , в котором эта функция объявлена. Выглядеть это может примерно так:
#include <Delayimp.h>
#pragma comment(linker, "/Delay:unload")
.
.
X();
__FUnloadDelayLoadedDLL("MyLib.dll");
Имя, передаваемое функции FUnloadDelayLoadedDLL, должно в точности совпадать с именем, указанным в ключе /DELAYLOAD. Так, если передать ей имя "MYLIB.DLL", библиотека останется в памяти.
В заключение хочется отметить ещё один интересный момент. Когда я попытался воспользоваться отложенной загрузкой в своей программе, линкер отказался подключать библиотеку Delayimp.lib, выдавая сообщение о внутренней ошибке и подробную отладочную информацию. Чтобы решить эту проблему, я просто взял файлы Delayhlp.cpp и Delayimp.h из каталога Vc98Include, добавил в файл Delayhlp.cpp строки:
PfnDliHook __pfnDliNotifyHook = NULL;
PfnDliHook __pfnDliFailureHook = NULL;
и перестроил эту библиотеку заново. После этого отложенная загрузка заработала нормально.
Ссылки
Поскольку я рассказал об отложенной загрузке далеко не все, рекомендую обратиться за дополнительной информацией к следующим статьям в MSDN:
– December 1998, Microsoft systems journal, Win32 Q&A
– December 1998, Microsoft systems journal, Under the hood
– Linker support for delay-loaded DLLs
Alexander Shargin ([email protected]) ВОПРОС-ОТВЕТQ. Все, наверное, знают программы, называемые Viewbar, которые показывают рекламные баннеры. Но вот как они ограничивают часть экрана, не позволяя другим окнам находиться поверх них? Например, если разрешение экрана 800×600, как они выделяют полосу сверху, в которой находятся, т.ч. программы, развернутые на полный экран, имеют высоту где-то на 60 пикселей меньше. Причем и немаксимизированные окна не могут "влезть" в эту полосу.
Alexander PopovДля рассылки пока уникальный случай: на вопрос ответил сам автор.
A1 Спасибо за опубликование вопроса. Теперь я сам же могу на него ответить. Для создания приложения, похожего на панель задач или панель MS Office, используется AppBar. Последний достаточно хорошо описан в MSDN (см. Extend the Windows 95 Shell with Application Desktop Toolbars, Application Desktop Toolbars) А вообще, достаточно много интересного содержится в Windows Shell API, в частности: работа с панелью задач, как написать ScreenSaver, работа с панелью управления, Band Objects в Internet Explorer.
Alexander PopovA2 Этого можно добиться, используя функцию SystemParametersInfo. У этой исключительно полезной функции существуют параметры SPI_GETWORKAREA и SPI_SETWORKAREA, позволяющие получить размер рабочей области экрана или установить для неё собственный размер (перед завершением работы программы его рекомендуется восстановить). Напрмер, следующий фрагмент "резервирует" полосу шириной в 100 пикселей в верхней части экрана:
CRect rcOld, rcNew;
SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)&rcOld, 0);
rcNew = rcOld;
rcNew.top = 100;
SystemParametersInfo(SPI_SETWORKAREA, 0, (PVOID)&rcNew, 0);
Чтобы восстановить исходный размер, достаточно вызвать:
SystemParametersInfo(SPI_SETWORKAREA, 0, (PVOID)&rcOld, 0);
После того как нужная область зарезервирована, можно, например, поместить туда своё окно (вызовом CWnd::MoveWindow) и лишить пользователя возможности убрать его оттуда (так как в противном случае оно туда не вернётся), после чего рисовать в нём баннеры.
В заключение отмечу, что именно этой функцией пользуются программы типа Magnify.exe из комплекта Windows.
Alexander Shargin ([email protected])Фактически, ответы, конечно, одинаковые (в статье из MSDN как раз и используется SystemsParametersInfo), просто первый в отличие от самого ответа содержит ссылку на него.
Эти два ответа – все, что я получил. Два из восьми тысяч. Действительно, не очень-то сильно читатели хотят отвечать на вопросы. Так что я по всей видимости был прав по поводу поощрений… Господа! Прошу поактивнее! Или я могу решить что рубрика вам неинтересна и закрою ее…
Многие спрашивают, почему я лично не отвечаю на вопросы. Это неправда, иногда все-таки отвечаю ;)
Ну а главное: посмотрите Microsoft Systems Journal. Там человек В МЕСЯЦ отвечает на ДВА вопроса, причем это – его работа, т.е. он получает за это деньги. Потом, далеко не на всякий вопрос можно сходу дать однозначный ответ. Как правило, те вопросы, для которых можно это сделать – неинтересны. Так что наверное гораздо эффективнее разделять эту задачу с читателями.
ОБРАТНАЯ СВЯЗЬAlexander Shargin по поводу ответа A1 (Sergey Emantayev) из №21 пишет:
Справедливости ради следует отметить, что всплывающие меню не обновляются в Idle loop'е. В нём обновляются тулбары, статус бар и т.п., но всплывающие меню обновляются только в ответ на WM_INITMENUPOPUP. Этой практики следует придерживаться и в собственных приложениях.