Основную идею задачек я подсмотрел у Александра Алексеева (более известного как GUNSMOKER), и подумал - а почему бы и мне не открыть такой подраздел, ибо таких у меня накопилось предостаточно от собеседований кандидатов на вакансию :)
Итак, дана функция, реализованная в классическом стиле :
Задача, описать что в данном коде не верно.
Итак, дана функция, реализованная в классическом стиле :
function IsAeroEnabledCheck: Boolean; type _DwmIsCompositionEnabledFunc = function(IsEnabled: PBool): HRESULT; stdcall; var DllHandle: THandle; Flag: Boolean; DwmIsCompositionEnabledFunc: _DwmIsCompositionEnabledFunc; begin DllHandle := LoadLibrary('dwmapi.dll'); if DllHandle > HINSTANCE_ERROR then try @DwmIsCompositionEnabledFunc := GetProcAddress(DllHandle, 'DwmIsCompositionEnabled'); if (@DwmIsCompositionEnabledFunc <> nil) then begin DwmIsCompositionEnabledFunc(@Flag); Result := Flag; end; finally FreeLibrary(DllHandle); end; end;
Задача, описать что в данном коде не верно.
Значение Result может быть не определенно?
ОтветитьУдалитьНу да... ещё нет проверки результата...
ОтветитьУдалитьDllHandle > HINSTANCE_ERROR по идее там либо ПУСТО (NULL) (если ошибка), либо дескриптор модуля, а HINSTANCE_ERROR = $20.
ОтветитьУдалить@DwmIsCompositionEnabledFunc <> nil всегда даст TRUE, собачка лишняя
ОтветитьУдалитьКак уже написали выше, Result будет не определено в случае ошибки и строка if DllHandle > HINSTANCE_ERROR then неправильная, но работать будет.
ОтветитьУдалитьИ еще PBool = ^LongBool;, поэтому var Flag: Boolean; неправильно, будет портиться стэк. Правильно var Flag: LongBool;.
Извиняюсь, "@DwmIsCompositionEnabledFunc <> nil всегда даст TRUE, собачка лишняя" - мое ошибочное мнение. Оказывается для переменных процедурного типа оператор @ - является уточнением, что необходимо взять значение переменной DwmIsCompositionEnabledFunc а не вызвать процедуру. (Для получения адреса переменной нужно писать @@) Я этого не знал.
ОтветитьУдалитьto Chaa,
ОтветитьУдалить+1, но стек не при чем
Bool != Boolean
Flag: Bool;
unction(var IsEnabled: PBool): HRESULT
[URL=http://saveimg.ru] [IMG]http://saveimg.ru/pictures/18-12-13/3dc29b1fb28106501fe48d8884f8e5ed.png[/IMG][/URL]
Размер Boolean 1 байт, размер LongBool (Bool то же самое) 4 байта. В функцию передается указатель на переменную Flag размером в 1 байт. Функция по этому указателю запишет 4 байта.
УдалитьСтек может не портиться только потому, что компилятор выровняет все переменные по границе в 4 байта.
0) "если что-то работает, еще не значит, что правильно"
ОтветитьУдалить1) конечно же, верный результат не гарантирован. В Win7 код нормально работает, а в XP ожидаемо погоду показывает:
>Test1Aero.exe
Aero enabled
>Test1Aero.exe
Aero is off
2) проверка результата LoadLibrary "неклассическая", как в
http://docwiki.embarcadero.com/RADStudio/XE5/en/Libraries_and_Packages
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686944.aspx
http://msdn.microsoft.com/en-us/library/ms810279.aspx
3) в try..finally нет необходимости
С HINSTANCE_ERROR интрига, однако :)
ОтветитьУдалитьВ действительности как раз данная проверка является именно классической, чуть позже, когда буду давать развернутый ответ на вопрос, я постараюсь не забыть раскрыть данный нюанс :)
А вообще, как говорил один хороший дядька - за проверку инстанса модуля с нулем, гвоздь нужно забивать прямо по центру лба :)
Но - попробую подкинуть интриги, данный код будет работать по разному в зависимости от типа сборки (с оптимизацией или без) правда не под всеми версиями Delphi
>> как говорил один хороший дядька - за проверку инстанса модуля с нулем,
УдалитьА вот с этого поподробнее пожалуйста... Какой дядка?
Чем это код
if hModule > $20 кошернее чем if hModule > 0
"Хороший дядька" видимо немолодой, и начинал писать код еще во времена Winodws 3.1. В Win16 действительно нужно было делать именно так. Но это было очень, очень давно.
УдалитьВ MSDN недвусмысленно написано "If the function fails, the return value is NULL". Не думаю, что в наше время стоит писать с оглядкой на Win16.
>> if hModule > $20 кошернее чем if hModule > 0
УдалитьLoadLibrary возвращает не хэндл, а HMODULE, т.е. адрес загрузки библиотеки в адресное пространство процесса.
Библиотека не может быть подгружена в первые 64кб памяти приложения, ибо эта память используется для проверки на битые указатели.
Поэтому что проверка на ноль, что проверка на HINSTANCE_ERROR ничего не даст в случае если LoadLibrary вернула какой-то адрес меньший чем 0x10000 (а сделать это достаточно просто, проведя модификацию в таблице PLDR_DATA_TABLE_ENTRY)
Сама-же проверка это как уже сказали, это наследие аж от Win 3.1 постоянно встречающаяся в исходниках Windows.
Думаю больше вариантов ждать смысла не имеет, развернутый ответ готов: http://alexander-bagel.blogspot.ru/2013/12/1_19.html
ОтветитьУдалить