Ручная загрузка функций из DLL или как в программе использовать функции из Vista API
October 26th, 2009 Begemot Posted in Программирование
Наконец и до меня дошел прогресс и я все-таки решил перейти на Vista Clipboard API в своем Clipboard Manager. Логику продумал, код написал, код что-то наподобие :
CallNewVistaAPI();
else
UseOldAPI();
Запускаю в дебаге под XP, программа выдает ошибку и закрывается:
Clipdiary.exe – Entry Point Not Found
—————————
The procedure entry point AddClipboardFormatListener could not be located in the dynamic link library USER32.dll.
То есть даже в случае когда код не пытается вызвать новую функцию, программа все равно не запускается, плохо. Гугл мне почему-то не чем не помог, зато помогли добрые люди. Решение – не вызывать напрямую функции api появившиеся только в висте, а тихонько вручную подгружать их из USER32.dll, запоминать адреса и использовать через указатели. Мне собственно дали готовый класс для всего этого, который я переписал под свои нужды и свой стиль, и теперь могу поделиться:)
Заголовочный файл:
#pragma pack (push, 8)
class CDLLFunctionImports
{
public:
CDLLFunctionImports(void);
typedef BOOL (WINAPI * PADDCLIPBOARDFORMATLISTENER)(HWND);
typedef BOOL (WINAPI * PREMOVECLIPBOARDFORMATLISTENER)(HWND);
PADDCLIPBOARDFORMATLISTENER pRemoveClipboardFormatListener;
PREMOVECLIPBOARDFORMATLISTENER pAddClipboardFormatListener;
private:
void Import(void);
HMODULE GetModuleHandle(TCHAR * ModuleName);
};
extern CDLLFunctionImports DLLFunctionImports;
#pragma pack (pop)
typedef’ом определяем типы указатели на функции с нужной нам сигнатурой. В принципе, мне бы тут хватило и одного типа, но пусть уж будет два для благозвучия. Потом определяем экземпляры указателей на эти функции. Все просто. Еще объявляем глобальный объект, экземпляр нужного класса.
Файл реализации:
#include "dllfunctionimports.h"
CDLLFunctionImports DLLFunctionImports;
CDLLFunctionImports::CDLLFunctionImports(void)
{
pAddClipboardFormatListener = NULL;
pRemoveClipboardFormatListener = NULL;
Import();
}
void CDLLFunctionImports::Import(void)
{
OSVERSIONINFO OSVersionInfo;
OSVersionInfo.dwOSVersionInfoSize=sizeof(OSVersionInfo);
if (GetVersionEx(&OSVersionInfo))
{
if (OSVersionInfo.dwMajorVersion >= 6)
{
HMODULE hModule = GetModuleHandle(_T("USER32.DLL"));
if (hModule != NULL)
{
pAddClipboardFormatListener = PADDCLIPBOARDFORMATLISTENER(GetProcAddress(hModule, "AddClipboardFormatListener"));
pRemoveClipboardFormatListener = PREMOVECLIPBOARDFORMATLISTENER(GetProcAddress(hModule, "RemoveClipboardFormatListener"));
}
}
}
}
HMODULE CDLLFunctionImports::GetModuleHandle(TCHAR * ModuleName)
{
HMODULE hModule = ::GetModuleHandle(ModuleName);
if (hModule == NULL)
{
hModule = ::LoadLibrary(ModuleName);
}
return hModule;
}
В конструкторе вызываем import, который убедившись что у нас подходящая версия операционки, получает и сохраняет адреса необходимых процедур. Все магия закончилась, дальше когда нам необходимо вызвать новую функцию, используем такой вызов:
Пользуясь случаем, хочу сказать спасибо человеку, который это все написал и поделился кодом. Еще он написал отличный и пока еще бесплатный lightweight yet powerful icon and bitmap editor, так что если кому приходится редактировать иконки, берите, а то он скоро зарелизит и захочет денег 🙂
October 26th, 2009 at 11:52
Бывает, нужно втаскивать так функции не только из USER32.DLL, а ещё и из KERNEL32.DLL, и из SHELL32.DLL, и из DWMAPI.DLL, да мало ли откуда ещё 🙂
October 26th, 2009 at 12:04
Если кому надо искать в DWMAPI.DLL вместо USER32.DLL, поменяйте там в коде USER32.DLL на DWMAPI.DLL, ну и имя функции тоже свое подставьте 🙂
October 26th, 2009 at 1:05
мда… не понимаю, зачем писать очевидные вещи 😉
вот если бы ты не торопился, а прочитал и использовал отложенную загрузку DLL в Visual Studio, да еще бы статейку накатал, я бы порадовался 🙂
October 26th, 2009 at 1:17
Ну для тебя очевидная, а я например попал в затруднение с этим…
А отложенная загрузка это когда мы открываем свойства проекта – linker и там прописываем имена длл которые не надо автоматом грузить? так чо там сложного то 🙂
October 26th, 2009 at 2:01
ну дык о каком затруднении тогда речь ? 😉 вот и прописал бы нужную тебе DLL и никаких классов городить не надо.
p.s. я сам так не делал, но есть подозрения, что такая тема прокатит. если будешь проверять, обязательно напиши 😉
October 26th, 2009 at 2:25
хм, похоже действительно тема прокатила. А может у нее последствия какие нехорошие есть?
Ну ничего, это я только в одной программе такое сделал, а у меня еще вторая есть – туда планировал завтра влепить, попробую использовать Delay Loaded DLLs вдруг опять поможет 🙂
October 26th, 2009 at 5:27
Я что-то не понял, вы хэлпер для отложенной загрузки USER32 писать собрались?
October 26th, 2009 at 5:36
Точнее, Failure Hook?
October 26th, 2009 at 6:06
что есть Failure Hook не знаю, но добавив USER32.DLL в список длл для отложенной загрузки я кажется решил проблему. завтра проверю в реальном приложении под реальной семеркой:)
October 26th, 2009 at 6:21
Даже если оно вот так просто заработало (что странно),
1) я нахожу этот механизм стрёмным
2) я нахожу этот механизм применяемым не по назначению
3) он не пригоден для загрузки импортов из KERNEL32
4) ну и со своей любимой кросс-компиляторностью можешь попрощаться 🙂
October 26th, 2009 at 6:39
Под XP тестируй, под семёркой же оно и без выкрутасов работает.
October 26th, 2009 at 6:55
А вообще, если оно тупо вместо импорта вначале ставит указатели на заглушки с индивидуальными Loadlibrary/GetProcAddress для каждой функции, то может, и сработает без последствий. Кто-нибудь нашёл в документации описание реализации? Если его нет, то полагаться на недокументированный механизм нехорошо.
Да, и проверь заодно – влияет ли отложенная загрузка на размер кода?
October 27th, 2009 at 6:30
Сколько эмоций 😉
Так и не понял, почему это стремный метод и по какому тогда назначению его применять.
http://msdn.microsoft.com/ru-ru/library/151kt790.aspx
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Следует установить задержку в загрузке DLL, если:
1) Ваша программа может не вызвать функцию в DLL.
2) Функция в DLL может не получить вызов до тех пор, пока не будет выполняться в программе.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
второй пункт как раз то, что нужно бегемоту.
а на размер кода оно конечно влияет, раз дополнительная либа линкуется.
а о какой кросплатформенности можно говорить, если речь о виндовой user32.dll и виндовом API?
October 27th, 2009 at 6:36
эээ а как может быть наоборот? :))
October 27th, 2009 at 7:20
Потому что речь идёт об отложенной загрузке DLL, а не отложенном импорте определённых функций. Вам кто-то гарантировал, что при первом обращении к одной из функций данной DLL не будет попытки импорта и остальных?
Да нет, не только в либе дело. Если при этом генерируется индивидуальная заглушка для каждой функции из DLL с отложенной загрузкой, то код этих заглушек тоже будет занимать место. Я не настаиваю, что правильно понял работу механизма, опровергните меня, если знаете, чем пользуетесь 🙂
October 27th, 2009 at 7:26
А я что-то говорил о кроссплатформенности?
October 27th, 2009 at 4:36
контраргументов без знания работы отложенной загрузки в Visual Studio разумеется привести не могу.
убедил =)
p.s. не слушай меня бегемот, грузи руками, уверенность в правильности работы и четкое понимание того что делаешь будет 100% 😉 это самое главное.
October 27th, 2009 at 4:46
да, я попробовал отложенную загрузку – проблему решает, добавляет 3Кб к ехе файлу. Но поскольку я уже имел проблемы с отложенной загрузкой dll, то я лучше перестрахуюсь и буду все сам контролировать, благо не сложно.
October 27th, 2009 at 4:48
и еще,
пишется с Большой Буквы 🙂
October 28th, 2009 at 12:41
А ещё название класса и объекта DLLFunctionImports звучит примерно как МасляноеМасло 🙂