Учебная работа. Реферат: Реализация отложенной загрузки библиотек на С
Андрей Солодовников
Вы все еще грузите библиотеки вручную?
Тогда мы идем к для вас!
Короткая предыстория
По специфике моей работы мне достаточно нередко приходится вручную загружать библиотеки и динамически, с помощью GetProcAddress, импортировать огромное количество функций. Это происходит частично поэтому, что требуется обеспечить сопоставимость с разными версиями Windows, в каких мотивированные функции могут отсутствовать, частично поэтому, что так бывает удобнее (к примеру, при реализации механизма плагинов). естественно, постоянно охото это заавтоматизировать, в особенности если функций и библиотек много. С одной стороны, в линейке Visual C++ для этого есть поддержка компиляторалинкера в виде механизма Delay Load, с иной стороны, существует мировоззрение, что употреблять этот способ является дурным тоном, и, наверняка, это так. одна из главных обстоятельств, которую охото отметить в особенности – этот механизм является microsoft-specific, другими словами никаких гарантий, что написанный Вами код будет работать и на остальных компиляторах либо платформах, нет. Наиболее того, несколько раз «попав» на странноватое поведение этого механизма (к примеру, см. Q218613), мы от его использования в собственных проектах отказались.
Последующим шагом был поиск готового пригодного функционала. Как ни удивительно, такового не находилось, невзирая на то, что неувязка вправду имеет пространство быть. Почти все решения были очень ординарны и неоптимальны (к примеру, это решение). Они не дозволяли определять импорт сходу нескольких функций из одной библиотеки, или для этого необходимо было написать солидное количество кода. Они вызывали GetProcAddress и LoadLibrary в хоть какое время, когда им вздумается, а по сути – чуток ли не при любом воззвании к импортируемой функции. Остальные (к примеру, такое решение) было довольно трудно и неловко употреблять.
ПРИМЕЧАНИЕ
По сути, обозначенные варианты полностью могут быть применены в маленьких проектах, когда не требуется импортировать огромное количество функций. Но их внедрение в любом случае просит довольно много усидчивости и терпения, по последней мере, меня это не устраивало.
И общий недочет всех этих решений – они были и есть неоптимальны. В особенности это касается количества кода, генерируемого компилятором (ну и программером) на одну импортируемую функцию и быстродействия приобретенного кода.
Все это, вместе с потраченным временем, сподвигло меня к необходимости написания еще одного велика в виде библиотеки эмуляции Delay Load, также и данной для нас статьи.
Требования к библиотеке, реализующей механизм Delay load
В данном параграфе мы разглядим наиболее тщательно, каким базисным требованиям должен удовлетворять механизм динамической загрузки библиотек.
Исходя из описанного чуть повыше, можно сконструировать последующие требования к механизму поддержки динамической загрузки библиотек:
Как можно большая независимость от компилятора С++ (в границах ANSI C++). Малые требования к компилятору – библиотека обязана быть стопроцентно функциональна на всех Visual C++ компиляторах, начиная с Visual C++ 6.0;
Малое количество кода, генерируемого компилятором, которое приходится на одну импортируемую функцию;
Удобство определения в проекте импортируемых библиотекфункций;
Возможность задания собственных стратегий (реакций) на ошибки загрузки библиотекинахождения функции;
Минимизация вызовов LoadLibrary. Для одной библиотеки (модуля) вызов LoadLibrary должен делается один раз вне зависимости от количества импортируемых из нее функций. Данный механизм должен работать не только лишь в границах одной единицы трансляции, да и проекта в целом. Таковым образом, обязана создаваться единая для приложения таблица применяемых модулей;
Минимизация вызовов GetProcAddress. GetProcAddress должен вызываться лишь при первом воззвании к импортируемой функции, в предстоящем все вызовы импортируемой функции должны выполняться впрямую;
библиотека обязана обеспечивать обычный синтаксис вызова – не обязано быть никаких наружных различий от обыденного вызова функции из С/С++;
Должны поддерживаться Unicode версии импортируемых функций, при этом, лучше, на базе заголовков от соответственных статически линкуемых библиотек, в каких определены надлежащие макросы.
Из обрисованных выше требований более необходимыми и увлекательными представляются пункты 3,5,6 и 7. Индивидуальности их реализации будут рассмотрены наиболее тщательно дальше вкупе с программной реализацией библиотеки. Для тех, кому детали реализации не увлекательны, а увлекательны способы использования библиотеки, предлагается приступать сходу к разделу Внедрение библиотеки.
Предлагаемая реализация библиотеки
Класс, инкапсулирующий работу с модулями
Для начала разглядим требование (5) к реализации загрузки библиотекмодулей. Разумеется, что обеспечить выполнение данного требования с условием уникальности экземпляра библиотеки в границах всех единиц трансляции проекта можно внедрением паттерна Singlton для загружаемого модуля. При всем этом для всякого различного загружаемого модуля должен создаваться свой экземпляр синглтона, который и будет обеспечивать «разовую» загрузку в конструкторе и в предстоящем выгрузку библиотеки в деструкторе. Эта задачка решается определением шаблонного класса CModule. имя библиотеки обязано служить значением, относительно которого делается инстанцирование объекта, инкапсулирующего загрузку библиотеки. Так как в качестве паттерна Singlton употребляется синглтон Мейерса, то в качестве приза мы получаем отложенную загрузку библиотек (так как создание экземпляра синглтона делается при первом воззвании к порождающей функции).
СОВЕТ
Напомню, что простая реализация синглтона Мейерса смотрится последующим образом:
template <class T>
struct CMeyersSinglton
{
static T& GetInstance()
{
static T obj;
return obj;
}
};
В связи с сиим 1-ый вариант определения шаблона CModule мог бы смотреться так:
template <LPCTSTR Name>
class CModule;
здесь следует создать маленькое отступление. Как было бы отлично, если б хоть какой абстрактный язык программирования, применяемый нами, обеспечивал бы всякую востребованную нами возможность. Но, разумеется, по суждениям здравого смысла, это неосуществимо, потому приходится воспользоваться тем, что есть. А есть таковая противная вещь – в С++ впрямую инстанцировать шаблон строковым литералом не получится. Шаблон быть может инстанцирован лишь константой с external linkage, а строковый литерал имеет internal linkage. На 1-ый взор, все довольно грустно. Но, как обычно, решение лежит на поверхности. Оно весьма обычное и явное. Мы будем инстанцировать шаблон модуля неповторимым классом, инкапсулирующим строковый литерал. Сам же класс будет формироваться с помощью макросов:
#define DECLARE_NAME_ID_IMPL(id, name, ret, text)
struct NAME_ID(id)
{
enum {length = sizeof(name)};
static ret GetStr(){return text(name);}
};
#define DECLARE_NAME_ID_A(id, name) DECLARE_NAME_ID_IMPL(id, name, LPCSTR, DL_EMPTY())
#define DECLARE_NAME_ID(id, name) DECLARE_NAME_ID_IMPL(id, name, LPCTSTR,_T)
Данный класс является всепригодным и будет применен в предстоящем и для представления имен импортируемых функций. Да и здесь есть один небольшой аспект – так как функция GetProcAddress употребляет лишь ANSI строчки, то мы обязаны это предугадать, объявив доп макрос DECLARE_NAME_ID_A.
Итак, в связи со всем вышеизложенным, определение шаблона CModule без учета стратегий будет смотреться так:
template <class Name>
class CModule;
сейчас добавим стратегии загрузкивыгрузки модуля. Так как стратегия контролирует процессы, связанные с загрузкой и выгрузкой, у нее обязано быть как минимум 2 функции. одна отвечает за загрузку модуля, 2-ая за его выгрузку:
struct CModulePolicy
{
static HMODULE Load(LPCTSTR szFileName);
static BOOL Free(HMODULE hModule);
};
сейчас у нас все есть, что нужно для полного написания класса CModule. Реализация его в предлагаемой библиотеке приведена в листинге ниже:
struct CModuleLoadLibraryPolicy
{
static HMODULE Load(LPCTSTR szFileName)
{
return ::LoadLibrary(szFileName);
}
static BOOL Free(HMODULE hModule)
{
return ::FreeLibrary(hModule);
}
};
struct CModuleGetModuleHandlePolicy
{
static HMODULE Load(LPCTSTR szFileName)
{
return ::GetModuleHandle(szFileName);
}
static BOOL Free(HMODULE hModule)
{
return TRUE;
}
};
template <class Name, class LoadPolicy = CModuleLoadLibraryPolicy>
class CModule
{
public:
typedef CModule<Name, LoadPolicy> type;
typedef Name name_type;
static type &GetModule()
{
#ifdef DL_MT
static volatile LONG lMutex = FALSE;
CLWMutex theMutex(lMutex);
CAutoLock<CLWMutex> autoLock(theMutex);
#endif //DL_MT
static type Module;
return Module;
}
HMODULE GetModuleHandle() const
{
return m_hModule;
}
BOOL IsLoaded() const
{
return m_hModule != NULL;
}
// Caution — use with care. Not thread-safe
BOOL UnloadModule()
{
HMODULE hModule = m_hModule;
m_hModule = NULL;
return LoadPolicy::Free(hModule);
}
~CModule()
{
if (m_hModule)
UnloadModule();
}
private:
CModule()
{
m_hModule = LoadPolicy::Load(name_type::GetStr());
}
HMODULE m_hModule;
};
Класс модуля дозволяет очевидно выгружать библиотеку (модуль) с помощью функции UnloadModule, но воспользоваться данной для нас возможностью нужно с большенный осторожностью.
Реализация динамического поиска функций и глобальной таблицы импорта
сейчас разглядим детали реализации пт 6 и 7 (поиск адресов импортируемых функций и их вызов). Это более нетривиальная и увлекательная в плане программирования часть библиотеки, так как функции могут иметь различное число характеристик, также разные типы возвращаемых значений. И напомню основное требование – естественный синтаксис вызова функций и минимизация воззваний к GetProcAddress.
В данном случае для обеспечения требования минимизации вызовов GetProcAddress мы будем употреблять технику сотворения прокси-функций. Практически, при первом вызове импортируемой функции мы будем попадать в сформированную компилятором прокси-функцию, в какой будет выполняться поиск адреса функции по ее имени в библиотеке и зависимо от удачливости поиска делается или вызов функции, или выполнение операции, данной в стратегии реакции на ошибки поиска. Для того, чтоб в предстоящем вызывалась конкретно импортируемая функция, а не прокси, адресок, приобретенный в прокси, запоминается в глобальной для всех единиц трансляции таблице указателей на функции. Для сотворения таблицы употребляется техника, схожая используемой в синглтоне Мейерса. В очень облегченном виде это смотрится так:
template <class proxy>
struct CGlobalProxyTable
{
static FARPROC &GetProxy()
{
static FARPROC proxy;
return proxy;
}
};
В данном примере для всякого входного типа будет сгенерирован неповторимый глобальный указатель типа FARPROC, практически являющийся ячейкой глобальной в определениях единиц трансляций таблицы функций.
Для того, чтоб найти интерфейс ячейки таблицы функций, выясним, от что зависит импортируемая функция. Разумеется, это имя функции, модуль, из которого нужно ее импортировать, и прокси, применяемый для определения адреса функции в загружаемой библиотеке. В связи с сиим определим класс CDynFunction, инкапсулирующий ячейку для хранения адреса функции в глобальной таблице импортируемых функций:
template <class Module, class Name, class proxy>
class CDynFunction
Беря во внимание все вышеупомянутое, реализация класса элементарна и будет смотреться так:
template <class Module, class Name, class proxy>
class CDynFunction
{
public:
typedef CDynFunction<Module, Name, proxy> type;
typedef Proxy proxy_type;
typedef Module module_type;
typedef Name name_type;
static typename proxy_type::fun_type &GetProxy()
{
static typename proxy_type::fun_type proxy = proxy_type::template proxy<type>::ProxyFun;
return proxy;
}
static BOOL InitFunction()
{
#ifdef DL_MT
static volatile LONG lMutex = FALSE;
#endif // DL_MT
const module_type &theModule = module_type::GetModule();
if (theModule.IsLoaded())
return DL_GetProcAddressImpl(
#ifdef DL_MT
lMutex,
(const FARPROC)proxy_type::template Proxy<type>::ProxyFun,
#endif //DL_MT
(volatile FARPROC &)GetProxy(),
theModule.GetModuleHandle(),
name_type::GetStr()
);
return FALSE;
}
};
Функция DL_GetProcAddressImpl представляет собой обертку GetProcAddress, и вынесена в отдельный многофункциональный элемент для уменьшения размера кода при поддержке многопоточности. Статический способ GetProxy() возвратит глобальный в смысле единиц трансляции адресок в таблице функций, при этом вначале по этому адресу находится адресок прокси функции. Таковым образом, вызывая функцию по указателю, приобретенному с помощью GetProxy(), мы сначало вызываем прокси, а в предстоящем будем вызывать импортируемую функцию впрямую.
Реализация прокси функций
Ранее момента все было довольно разумеется и достаточно просто. Но при попытке реализации класса, определяющего функционал прокси-функции, мы сталкиваемся с неуввязками. Чтоб осознать, в чем они заключаются, разглядим характеристики, нужные для генерации прокси функции. Это:
тип возвращаемого значения импортируемой функции;
перечень типов характеристик импортируемой функции;
стратегия реакции на ошибку поиска функции в модуле;
тип ячейки глобальной таблицы указателей на импортируемые функции (CDynFunction), который будет применен при разработке прокси.
Как понятно, С++ не поддерживает шаблоны с переменным количеством характеристик. В связи с сиим придется употреблять генерацию экземпляров шаблона с помощью макросов а-ля boost::preprocessor. Разъяснять тщательно тут, как это работает, я не буду – это тема для отдельной статьи. Не считая того, все это наслаждение осложняется тем, что Visual C 6.0 не может возвращать из void функции тип void. Для обхода данной для нас трудности приходится создавать отдельные классы для «обычных» типов и для void, а потом употреблять специализацию шаблона по возвращаемому значению с следующим наследованием.
Разглядим реализацию, предлагаемую в библиотеке:
#define FUN_proxy(n) DL_CAT(CFunProxy,n)
#define FUN_PROXY_IMPL(n) DL_CAT(FUN_PROXY(n),Impl)
#define DECLARE_FUN_proxy(param_count)
template <typename R>
struct FUN_proxy_IMPL(param_count)
{
template <class DynFunction, DL_REPEAT_N(param_count, typename P), class Policy> struct RetProxy
{
static R WINAPI ProxyFun(DL_REPEAT_PARAM_N(param_count, P, v))
{
if (DynFunction::InitFunction())
return DynFunction::GetProxy()(DL_REPEAT_N(param_count, v));
return Policy::template FunctionTrait<DynFunction>::MakeReturn();
}
};
};
template <>
struct FUN_proxy_IMPL(param_count) <void>
{
template <class DynFunction, DL_REPEAT_N(param_count, typename P), class Policy> struct RetProxy
{
static void WINAPI ProxyFun(DL_REPEAT_PARAM_N(param_count, P, v))
{
if (DynFunction::InitFunction())
DynFunction::GetProxy()(DL_REPEAT_N(param_count, v));
else
Policy::template FunctionTrait<DynFunction>::MakeReturn();
}
};
};
template <typename R, DL_REPEAT_N(param_count, typename P), class Policy = CFunProxyValuePolicy<R> >
struct FUN_proxy(param_count)
{
typedef R (WINAPI *fun_type)(DL_REPEAT_N(param_count, P));
typedef R ret_type;
template <class DynFunction> struct proxy:public FUN_PROXY_IMPL(param_count)<R>::template RetProxy<DynFunction, DL_REPEAT_N(param_count, P), Policy>
{
};
};
Главным в реализации является макрос DECLARE_FUN_proxy(param_count), который описывает шаблон класса прокси-функции с количеством характеристик импортируемой функции, обозначенным в param_count. В итоге внедрения этого макроса порождается набор шаблонных классов прокси-функций для количества характеристик от 1 до 16. Макросы DL_REPEAT_N и DL_REPEAT_PARAM_N сформировывают перечень формальных и поименованных характеристик соответственно.
В целом, опосля подстановки макросов, получаемый класс для количества характеристик n смотрится так:
template <typename R, typename P1, typename P2, …, typename Pn , class Policy = CFunProxyValuePolicy<R> >
struct CFunProxyn
{
typedef R (WINAPI *fun_type)(P1, P2, .. , Pn));
typedef R ret_type;
template <class DynFunction> struct proxy:public CFunProxynImpln<R>::template RetProxy<DynFunction, P1, P2, .. ,Pn, Policy>
{
};
};
Главным является вложенный шаблон proxy, конкретно он наследует прокси-функцию ProxyFun из CFunProxynImpl. Класс CFunProxynImpl нужен из-за невозможности возвратить тип void с помощью оператора return в Visual C++ 6.0. В качестве обходного маневра употребляется специализация реализации прокси по типу возвращаемого значения – раздельно для типа void и раздельно для всех других типов.
прокси-функция ProxyFun будет применена в CDynFunction для начальной инициализации адреса указателя на функцию:
static typename proxy_type::fun_type &GetProxy()
{
static typename proxy_type::fun_type proxy = proxy_type::template proxy<type>::ProxyFun;
return proxy;
}
Для обеспечения способности реакции на ошибку нахождения функции в модуле употребляется соответственная стратегия. Стратегия состоит из класса, вложенного в него шаблона, принимающего в качестве параметра тип ячейки таблицы импортируемых функций и имеющего статическую функцию MakeReturn, которая и вызывается при ошибке поиска адреса функции либо при ошибке загрузки библиотеки. На данный момент реализованы 2 стратегии. Одна (CFunProxyThrowPolicy) – выбрасывает исключение (по дефлоту CDynFunException) при ошибке поиска функциизагрузки библиотеки, иная (CFunProxyValuePolicy) – возвращает определенное юзером
template <class R>
struct CFunProxyThrowRetTypeTrait
{
template <class F>
struct FunctionTraitImpl
{
static R MakeReturn()
{
F::MakeReturnImpl();
return R();
}
};
};
template <>
struct CFunProxyThrowRetTypeTrait<void>
{
template <class F>
struct FunctionTraitImpl
{
static void MakeReturn()
{
F::MakeReturnImpl();
}
};
};
template<class E = CDynFunException>
struct CFunProxyThrowPolicy
{
template <class DynFunction>
struct FunctionTrait:public CFunProxyThrowRetTypeTrait<typename DynFunction::proxy_type::ret_type>::template FunctionTraitImpl<FunctionTrait<DynFunction> >
{
static void MakeReturnImpl()
{
TCHAR szMessage[DynFunction::name_type::length + 64];
_stprintf(szMessage, _T(«Can’n resolve procedure <%s>: %d»), DynFunction::name_type::GetStr(), GetLastError());
throw E(szMessage);
}
};
};
// we need not implement void return type value policy,
// coz void function can only throw on error
template<class R, R value = R()>
struct CFunProxyValuePolicy
{
template <class DynFunction>
struct FunctionTrait
{
static typename DynFunction::proxy_type::ret_type MakeReturn()
{
return value;
}
};
};
Крайние штришки
Фактически, на этом главные элементы библиотеки реализованы, сейчас нужно обрисовать базисные макросы, которые дозволят употреблять ее наиболее просто. В библиотеке для объявления импортируемых функций употребляется интерфейс, очень напоминающий карту сообщений MFC. Интерфейс состоит из 3-х типов макросов.
Макросы, определяющие модуль и открывающие секцию импортируемых из него функций (DL_USE_xxx_BEGIN);
Макросы, определяющие импортируемые функции (DL_DECLARE_FUN_xxx);
Макрос, закрывающий секцию импорта (DL_USE_MODULE_END).
Таковым образом, обычное объявление динамически импортируемых из библиотеки функций смотрится как
// объявление библиотеки и места имен функций, импортируемых из нее
DL_USE_MODULE_xxx_BEGIN(name_space, “some_lib.dll”)
DL_DECLARE_FUN_xxx(ImportedFunction1Name, … )
DL_DECLARE_FUN_xxx(ImportedFunction2Name, … )
…
DL_USE_MODULE_END()
Исходя из описанного интерфейса, определены последующие базисные макросы:
Макрос DL_USE_MODULE_LOAD_POLICY_BEGIN(nmspace, name, load_policy)
#define DL_USE_MODULE_LOAD_POLICY_BEGIN(nmspace, name, load_policy)
namespace nmspace
{
DECLARE_NAME_ID(DL_CAT(_MODULE_, nmspace), name)
typedef delayload::CModule<NAME_ID(DL_CAT(_MODULE_, nmspace)), load_policy> module_type;
описывает в пространстве имен nmspace (тем открывая секцию импорта функций для данной библиотеки) класс модуля, применяемого для загрузки библиотеки с именованием name, при всем этом применяя политику загрузки load_policy. Также в пространстве имен функций импортируемой библиотеки определяется тип module_type, который представляет собой тип класса модуля для данной библиотеки и быть может применен для управления временем жизни библиотеки, к примеру, для ее выгрузки с помощью статического способа UnloadModule.
Макрос DL_DECLARE_FUN_ERR_POLICY(name_id, r, p, pl)
#define DL_DECLARE_FUN_ERR_POLICY(name_id, r, p, pl)
DECLARE_NAME_ID_A(name_id, DL_STRINGIZE(name_id))
static r (WINAPI *&name_id)(DL_SEQ_ENUM(p)) = delayload::CDynFunction<module_type, NAME_ID(name_id), delayload::FUN_proxy(DL_SEQ_SIZE(p))<r, DL_SEQ_ENUM(p), pl > >::GetProxy();
описывает ссылку name_id на указатель на функцию с именованием name_id, типом возвращаемого значения r, перечнем характеристик p и политикой реакции на ошибку загрузки библиотекипоиска функции pl. Вначале этот указатель показывает на подобающую прокси-функцию, но опосля первого вызова функции указатель показывает конкретно на саму функцию. Таковым образом, внедрение импортируемой функции из программки элементарно – это обыденный вызов функции из места имен (nmspace::name_id).
Неочевидной, но увлекательной индивидуальностью таковой реализации становится то, что автоматом добавляется поддержка Unicode версий импортируемых функций при подключении заголовков от соответственных статически линкуемых библиотек, где определены макросы ИмяФункцииW и ИмяФункцииA.
Внедрение библиотеки
Потому что при разработке библиотеки одной из главных целей было обеспечение простоты ее использования, то более пригодным интерфейсом объявления импортируемых библиотек и функций оказался интерфейс, снаружи напоминающий карты сообщений MFC. В библиотеке определено несколько макросов, которые существенно упрощают ее внедрение. Это макросы:
DL_USE_MODULE_BEGIN(nmspace, name) – открывает секцию импорта функций из библиотеки. Параметр nmspace – заглавие места имен, в которое будет помещены импортируемые функции, name – имя библиотеки, которую нужно загрузить. Для загрузки употребляется LoadLibrary;
DL_USE_MODULE_NON_LOAD_BEGIN(nmspace, name) – аналогично предшествующему, но для загрузки употребляется GetModuleHandle;
DL_DECLARE_FUN(name_id, r, p) – описывает функцию с именованием name_id, типом возвращаемого значения r, и перечнем типов характеристик p в виде (type1)(type2)…(typen). В случае ошибки при загрузке библиотекипоиске функции из функции ворачивается случае Visual C++ 6.0 это просто не скомпилируется);
DL_DECLARE_FUN_ERR(name_id, r, p, e) – аналогично предшествующему, но в случае ошибки при загрузке библиотекипоиске функции ворачивается не r(), а
DL_DECLARE_FUN_THROW(name_id, r, p) – аналогично предшествующему, но в случае ошибки при загрузке библиотекипоиске функции выбрасывается исключение CDynFunException;
DL_USE_MODULE_END() – закрывает секцию импорта функций из модуля.
При вызове функции будет употребляться синтаксис nmspace::name_id.
Разглядим пример использования библиотеки в настоящей программке:
#include «stdafx.h»
#include <windows.h>
#include «../delayimphlp.h»
// объявление секции импорта из kernel32.dll
DL_USE_MODULE_BEGIN(kernel, «kernel32.dll»)
DL_DECLARE_FUN_ERR(GetProcAddress, FARPROC, (HMODULE)(LPCTSTR), NULL)
DL_DECLARE_FUN(GetModuleHandle, HMODULE, (LPCTSTR))
DL_DECLARE_FUN_THROW(InitializeCriticalSection, void, (LPCRITICAL_SECTION))
DL_USE_MODULE_END()
int main(int argc, char* argv[])
{
try
{
CRITICAL_SECTION cs;
HMODULE hm = kernel::GetModuleHandle(«ntdll.dll»);
kernel::InitializeCriticalSection(&cs);
FARPROC p = kernel::GetProcAddress(hm, «NtQuerySystemInformation»);
}
catch (delayload::CDynFunException &E)
MB_ICONERROR);
return 0;
}
В данном примере мы загружаем библиотеку kernel32.dll, потом импортируем из нее функции GetProcAddress, GetModuleHandle и InitializeCriticalSection. Как лицезреем, все довольно просто и элементарно. В случае наличия обычных заголовков к статически линкуемым библиотекам, где с помощью макросов определены ANSI и Unicode варианты импортируемых функций, при подключении этих заголовков зависимо от типа проекта (ANSI либо Unicode), подходящим образом будут изменяться и динамически импортируемые функции, обеспечивая импорт корректных версий функций.
Заключение
Итак, в данной статье рассмотрен инструментарий, позволяющий комфортно употреблять в коде огромное количество динамически загружаемых библиотек и импортируемых из их функций, попутно рассмотрев несколько увлекательных приемов программирования на C++ в критериях ограниченной поддержки шаблонов. библиотека вышла, на мой взор, довольно эластичная и отлично расширяемая, просит довольно не много ресурсов в плане памятикода и получаемый при ее использовании итог почти всегда по быстродействию не уступает статически импортируемым функциям. Почти все в ней реализовано так, а не по другому, из расчета поддержки как можно большего количества компиляторов. библиотека проверялась на работоспособность с Visual C++ 6.0, 7.0 и 7.1, но особенных заморочек при портировании на остальные компиляторы (не считая, пожалуй, линейки от Borland) быть не обязано. Создатель выражает благодарность всем участникам обсуждения данной библиотеки на форуме RSDN за полезные мысли, советы и поправки. Надеюсь, что данная библиотека поможет хотя бы отчасти упростить жизнь программерам WinAPI и не только лишь.
]]>