2014-01-22 4 views
2

Я импортирую около 1500 функций из предоставленной поставщиком dll (все __stdcall). Для утомительных причин dll существует в нескольких версиях, содержащих различные подмножества полного списка функций (хотя все функции, которые существуют, имеют общий интерфейс). Все функции возвращают UINT, указывающий код ошибки, но принимают различные количества аргументов разных типов. В настоящее время, если GetProcAddress не работает (поскольку соответствующая функция не существует в конкретной версии dll), указатель функции - left = nullptr, который необходимо проверять каждый раз, когда клиент вызывает функцию из dll. Я хотел бы вместо этого назначить указатель на функцию, которая возвращает UINT соответствующего кода ошибки. Предлагаемое решение было вдоль этих линий:Предоставление указателя функции __stdcall больше аргументов, чем ожидается

UINT missing_func() { 
    return ERR_MISSING_FUNC; 
} 
..... 
typedef UINT(*LibFuncTy)(int a, const char *b, double c); 
..... 
//GetProcAddress returned nullptr... set generic function 
LibFuncTy LibFunc = reinterpret_cast<LibFunctTy>(missing_func); 
..... 
//programme tries to call library function 
errorval = LibFunc(arg1, arg2, arg3); 

Этот подход работает, как написано (несмотря на то, явно не определено стандартом), но не как только звонки __stdcall (предположительно потому, что мы испортили стек). Я посмотрел на использование (/ злоупотребление) bind, но, похоже, не помогает с тем, что я пытаюсь сделать (если я не пропущу что-то). Многие вызовы функций библиотеки оказываются в глубоко вложенном цикле, поэтому я не хочу брать на себя какие-либо (нетривиальные) накладные расходы в случаях, когда GetProcAddress смог найти эту функцию.

Есть ли «правильный» стандарт, способ выполнения этого поведения, который будет работать с соглашением о вызове __stdcall, или мне придется создавать 1500 различных версий missing_func() с различными списками аргументов, все возвращаемые такое же значение?

+0

Не удается ли во время компиляции или выполнения? – Trenin

+0

Правильно, вы перепутали стек. Вместо этого выберите исключение. –

+0

Он терпит неудачу, потому что stdcall полагается на вызов, чтобы поп-аргументы со стека, а 'missing_func()' ничего не выйдет. Надеемся, что количество функций, которые вы импортируете, имеющих разные подписи, намного меньше 1500. Самое легкое, если это утомительное решение - создать перегрузки 'missing_func()' для каждого из них и назначить их соответствующим образом. – Praetorian

ответ

1

Создание обертки, которая проверяет наличие nullptr перед отправкой вызова импортированной функции, похоже, сработает.

template<typename... Args> 
struct get_proc_address_wrapper; 

template<typename... Args> 
struct get_proc_address_wrapper<unsigned(Args...)> 
{ 
    typedef unsigned(__stdcall *func_type)(Args...); 
    get_proc_address_wrapper(func_type pf) 
     : pf(pf) 
    {} 

    unsigned operator()(Args... args) 
    { 
     if (not pf) return static_cast<unsigned>(-1); 
     else return (*pf)(args...); 
    } 

    func_type pf; 
}; 

Адрес demo о том, как вы его используете. Как вы можете видеть, он изящно возвращает код ошибки, если оболочка была инициализирована nullptr. Я тестировал тот же код на VS2013, и он возвратил тот же результат.

+0

В точности, что мне нужно - большое спасибо. Я до сих пор не сталкивался с вариантными функционалами и не могу найти ничего о них в Stroustrup. Правильно ли, что вы объявляете переменный класс get_proc_address_wrapper, а затем объявляете специализацию типа unsigned (Args ...)? – Xphraz

+0

@Xphraz Я использую [variadic templates] (http://en.cppreference.com/w/cpp/language/parameter_pack). [4-е издание] (http://www.stroustrup.com/4th.html) книги Страуструпа рассказывает о них. И специализация на самом деле не нужна, она просто позволяет вам использовать синтаксис 'get_proc_address_wrapper ' при создании экземпляра, который, по моему мнению, более естественен для объявления сигнатур функций. – Praetorian

2

Для каждого размера кадра стека требуется одна фиктивная функция, так как инструкция ret для этого соглашения соответственно корректирует указатель стека.

Обратите внимание, что есть стандартный код ошибки для «не реализован», я думаю, что символическое имя, например, E_NOTIMPL или подобное. Проверьте это.

Смежные вопросы