Как устранить предупреждение C4191 о вызовах GetProcAddress с помощью FARPROC?

Недавно я попытался использовать параметр /Wall Visual C++ для включения всех предупреждений и обнаружил, что следующий код:

typedef BOOL ( WINAPI * TIsWow64ProcessFunction )( HANDLE, BOOL* );
TIsWow64ProcessFunction isWow64ProcessFunction = reinterpret_cast<TIsWow64ProcessFunction> (
    ::GetProcAddress( kernel32DllHandle, "IsWow64Process" ) );

породил C4191:

warning C4191: 'reinterpret_cast' : unsafe conversion from 'FARPROC' to 'TIsWow64ProcessFunction'
Calling this function through the result pointer may cause your program to fail

Если я использую приведение в стиле C, появляется такое же предупреждение, но теперь в нем упоминается «приведение типов» вместо «reinterpret_cast».

То же самое предупреждение повторяется для любого случая, когда я вызываю GetProcAddress() и преобразую его возвращаемое значение в какой-нибудь пригодный для использования указатель на функцию.

Как мне реагировать на эти предупреждения? Нужно ли мне вносить изменения в мой код?


person sharptooth    schedule 16.11.2010    source источник
comment
Предупреждение #pragma(disable:4191) кажется подходящим, если цель состоит в том, чтобы достичь 0 предупреждений: это конкретное предупреждение бесполезно, поскольку нет (очевидного) способа избежать его при сохранении функциональности.   -  person Chris Becke    schedule 16.11.2010
comment
Он выдает вам предупреждение? Я никогда не использовал /wall, только уровень предупреждения P4 в VS. typedef BOOL (WINAPI DISABLEREDIR) (PVOID); DISABLEREDIR fpnDisableRedir = NULL; fpnDisableRedir = (DISABLEREDIR)GetProcAddress(hKernel32, Wow64DisableWow64FsRedirection);   -  person Kra    schedule 16.11.2010
comment
@Kra: Да, он порождает C4191 с /Wall. Ключ C4191 по умолчанию отключен, а /Wall включает его.   -  person sharptooth    schedule 16.11.2010
comment
интересно, еще одно предупреждение игнорировать, как я вижу :)   -  person Kra    schedule 16.11.2010
comment
@Kra - вы можете добавить следующие предупреждения, связанные с безопасностью, в компиляторы Microsoft: pragma warning(once: 4191 4242 4263 4264 4266 4302 4826 4905 4906 4928). См. также Предупреждения компилятора «Отключено по умолчанию» в Visual C++ в блоге Microsoft.   -  person jww    schedule 04.09.2015
comment
Я считаю, что ответ @Cody ниже является правильным решением здесь. Нет необходимости отключать предупреждения и использовать прагмы. Единственное, что нужно, это привести GetProcAddress к VOIDP или void*. Затем вы можете привести к чему угодно, например TIsWow64ProcessFunction. Для полноты картины Clang тоже иногда требует этого хака. Это не ограничивается компиляторами MS.   -  person jww    schedule 10.10.2016


Ответы (4)


Вы приводите FARPROC (указатель функции без аргументов) к указателю функции с аргументами. Обычно это очень глупая вещь, которая, вероятно, приведет к повреждению стека.

Теперь оказывается, что GetProcAddress() на самом деле не возвращает FARPROC, и вы на самом деле знаете, что делаете, но компилятор этого не знает и чувствует себя обязанным предупредить вас.

Единственный способ заставить его замолчать — использовать #pragma или переключатель компилятора, чтобы отключить предупреждение. Это уродливо и грязно, но это программирование для Windows. :-)

person Michael J    schedule 18.11.2010
comment
dlsym возвращает пустой указатель, поэтому я не думаю, что компилятор выдаст предупреждение. Предполагается, что void * равен указателю на функцию. Я не знаю, всегда ли это так. Я не думаю, что стандарт гарантирует это. - person Michael J; 25.04.2012
comment
VS 2010 сообщает об этом как об ошибке. #pragma не помогает решить проблему. - person null; 22.03.2013
comment
article codeguru.com/cpp/w-p/dll/importexportissues/article.php/c123/ обсуждают эту проблему, но она мне не очень понятна. Кто-нибудь объясните. - person null; 22.03.2013
comment
Привет, Аджай. Статья codeguru посвящена более сложному экспорту функций-членов класса. Это то, что ты делаешь? Попробуйте написать очень короткую программу, показывающую ошибку, и опубликуйте ее в stackoverflow. Вероятно, мне нужно увидеть код, чтобы диагностировать проблему. - person Michael J; 22.03.2013

Как уже упоминалось в других ответах, это полезное предупреждение. Обычно такой тип принуждения может быть серьезной ошибкой, скрывающейся в вашем приложении.

Поэтому вы, вероятно, не хотите отключать его глобально с помощью переключателя компилятора. Тем не менее, вам все еще нужно вызывать GetProcAddress, и вы хотите, чтобы ваши сборки компилировались без предупреждений.

У вас есть два хороших варианта:

  1. Подавляйте каждое отдельное предупреждение с помощью прагмы, специфичной для MSVC. В новой строке сразу над искаженным приведением добавьте следующий код:

    #pragma warning(suppress: 4191)
    

    Это подавляет предупреждение для самой следующей строки кода только, гарантируя, что оно не будет подавлено глобально, и вы все равно получите предупреждение, если попытаетесь сделать что-то глупое в другом месте кода. Конечно, вам нужно будет добавлять это каждый раз, когда вы используете GetProcAddress, что довольно сложно. Что еще хуже, это непереносимое, специфичное для MSVC расширение, которое искажает ваш код.

    Итак, альтернативно

  2. Вы можете отключить предупреждение, явно приведя результат GetProcAddress (a FARPROC) к void*, а затем приведя это void* к определенному типу указателя на функцию. Например:

    typedef BOOL ( __stdcall *TIsWow64ProcessFunction )( HANDLE, BOOL* );
    
    TIsWow64ProcessFunction isWow64ProcessFunction =
        reinterpret_cast<TIsWow64ProcessFunction>(
           reinterpret_cast<void*>(
           ::GetProcAddress(hInstance, "IsWow64Process")));
    

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

person Cody Gray    schedule 18.07.2016

В основном компилятор не может гарантировать, что функция имеет соответствующий тип, поэтому вам небезопасно вызывать результирующий указатель. Однако в программе VS вам не нужно явно связывать или загружать библиотеки Windows .dll, они будут загружены для вас, и любая функция в заголовке Windows всегда доступна для использования.

person Puppy    schedule 16.11.2010
comment
Предположим, мне действительно нужно динамически загрузить библиотеку. Что делать с предупреждением? Я игнорирую это? Означает ли это предупреждение, что я должен убедиться, что функция действительно имеет правильную подпись в реализациях? - person sharptooth; 16.11.2010
comment
@sharptooth: В принципе, да. Единственным решением этой проблемы является статическая загрузка DLL, что и происходит с заголовками Windows. - person Puppy; 16.11.2010
comment
@Puppy - вы можете преобразовать результат GetprocAddress в void*. Это отключит предупреждение C4191. Затем приведите его к типу указателя на функцию. - person jww; 09.10.2016

Меня также беспокоили эти предупреждения, поскольку я довольно часто использую подобные вещи. Мне понравился ответ @Cody Grey, поскольку он подавляет предупреждение только в момент использования, но не отфильтровывает другие, возможно, действительные предупреждения.

Я сделал простой помощник function_cast, чтобы сделать его более похожим на С++, например:

template<typename Result, typename Original>
Result function_cast(Original fptr)
{
  return reinterpret_cast<Result>(reinterpret_cast<void *>(fptr));
}

Код OP становится:

auto isWow64ProcessFunction = function_cast<TIsWow64ProcessFunction>(::GetProcAddress( kernel32DllHandle, "IsWow64Process" ) );
person Skillcheck    schedule 04.06.2020
comment
указатель функции приведения к void * является незаконным, см. stackoverflow.com/questions/36645660/ - person OwnageIsMagic; 02.04.2021