Ручная загрузка функций из DLL или как в программе использовать функции из Vista API

October 26th, 2009 Begemot Posted in Программирование

Наконец и до меня дошел прогресс и я все-таки решил перейти на Vista Clipboard API в своем Clipboard Manager. Логику продумал, код написал, код что-то наподобие :

if (vista)
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 once

#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 "ph.h"
#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, который убедившись что у нас подходящая версия операционки, получает и сохраняет адреса необходимых процедур. Все магия закончилась, дальше когда нам необходимо вызвать новую функцию, используем такой вызов:

DLLFunctionImports.pAddClipboardFormatListener(handle);

Пользуясь случаем, хочу сказать спасибо человеку, который это все написал и поделился кодом. Еще он написал отличный и пока еще бесплатный lightweight yet powerful icon and bitmap editor, так что если кому приходится редактировать иконки, берите, а то он скоро зарелизит и захочет денег 🙂

Related:

20 Responses to “Ручная загрузка функций из DLL или как в программе использовать функции из Vista API”

  1. не вызывать напрямую функции api появившиеся только в висте, а тихонько вручную подгружать их из USER32.dll

    Бывает, нужно втаскивать так функции не только из USER32.DLL, а ещё и из KERNEL32.DLL, и из SHELL32.DLL, и из DWMAPI.DLL, да мало ли откуда ещё 🙂

  2. Если кому надо искать в DWMAPI.DLL вместо USER32.DLL, поменяйте там в коде USER32.DLL на DWMAPI.DLL, ну и имя функции тоже свое подставьте 🙂

  3. мда… не понимаю, зачем писать очевидные вещи 😉

    вот если бы ты не торопился, а прочитал и использовал отложенную загрузку DLL в Visual Studio, да еще бы статейку накатал, я бы порадовался 🙂

  4. Ну для тебя очевидная, а я например попал в затруднение с этим…
    А отложенная загрузка это когда мы открываем свойства проекта – linker и там прописываем имена длл которые не надо автоматом грузить? так чо там сложного то 🙂

  5. ну дык о каком затруднении тогда речь ? 😉 вот и прописал бы нужную тебе DLL и никаких классов городить не надо.

    p.s. я сам так не делал, но есть подозрения, что такая тема прокатит. если будешь проверять, обязательно напиши 😉

  6. хм, похоже действительно тема прокатила. А может у нее последствия какие нехорошие есть?

    Ну ничего, это я только в одной программе такое сделал, а у меня еще вторая есть – туда планировал завтра влепить, попробую использовать Delay Loaded DLLs вдруг опять поможет 🙂

  7. Я что-то не понял, вы хэлпер для отложенной загрузки USER32 писать собрались?

  8. Ippi: Я что-то не понял, вы хэлпер для отложенной загрузки USER32 писать собрались?

    Точнее, Failure Hook?

  9. что есть Failure Hook не знаю, но добавив USER32.DLL в список длл для отложенной загрузки я кажется решил проблему. завтра проверю в реальном приложении под реальной семеркой:)

  10. Даже если оно вот так просто заработало (что странно),

    1) я нахожу этот механизм стрёмным
    2) я нахожу этот механизм применяемым не по назначению
    3) он не пригоден для загрузки импортов из KERNEL32
    4) ну и со своей любимой кросс-компиляторностью можешь попрощаться 🙂

  11. Begemot: автра проверю в реальном приложении под реальной семеркой

    Под XP тестируй, под семёркой же оно и без выкрутасов работает.

  12. А вообще, если оно тупо вместо импорта вначале ставит указатели на заглушки с индивидуальными Loadlibrary/GetProcAddress для каждой функции, то может, и сработает без последствий. Кто-нибудь нашёл в документации описание реализации? Если его нет, то полагаться на недокументированный механизм нехорошо.

    Да, и проверь заодно – влияет ли отложенная загрузка на размер кода?

  13. Сколько эмоций 😉
    Так и не понял, почему это стремный метод и по какому тогда назначению его применять.

    http://msdn.microsoft.com/ru-ru/library/151kt790.aspx
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Следует установить задержку в загрузке DLL, если:

    1) Ваша программа может не вызвать функцию в DLL.

    2) Функция в DLL может не получить вызов до тех пор, пока не будет выполняться в программе.
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    второй пункт как раз то, что нужно бегемоту.
    а на размер кода оно конечно влияет, раз дополнительная либа линкуется.

    а о какой кросплатформенности можно говорить, если речь о виндовой user32.dll и виндовом API?

  14. wholegroup: 2) Функция в DLL может не получить вызов до тех пор, пока не будет выполняться в программе.

    эээ а как может быть наоборот? :))

  15. wholegroup: Так и не понял, почему это стремный метод и по какому тогда назначению его применять

    Потому что речь идёт об отложенной загрузке DLL, а не отложенном импорте определённых функций. Вам кто-то гарантировал, что при первом обращении к одной из функций данной DLL не будет попытки импорта и остальных?

    wholegroup: а на размер кода оно конечно влияет, раз дополнительная либа линкуется

    Да нет, не только в либе дело. Если при этом генерируется индивидуальная заглушка для каждой функции из DLL с отложенной загрузкой, то код этих заглушек тоже будет занимать место. Я не настаиваю, что правильно понял работу механизма, опровергните меня, если знаете, чем пользуетесь 🙂

  16. wholegroup: а о какой кросплатформенности можно говорить, если речь о виндовой user32.dll и виндовом API?

    А я что-то говорил о кроссплатформенности?

  17. контраргументов без знания работы отложенной загрузки в Visual Studio разумеется привести не могу.
    убедил =)

    p.s. не слушай меня бегемот, грузи руками, уверенность в правильности работы и четкое понимание того что делаешь будет 100% 😉 это самое главное.

  18. да, я попробовал отложенную загрузку – проблему решает, добавляет 3Кб к ехе файлу. Но поскольку я уже имел проблемы с отложенной загрузкой dll, то я лучше перестрахуюсь и буду все сам контролировать, благо не сложно.

  19. и еще,

    wholegroup: бегемот

    пишется с Большой Буквы 🙂

  20. А ещё название класса и объекта DLLFunctionImports звучит примерно как МасляноеМасло 🙂