вторник, 17 декабря 2013 г.

Задачка на понимание №1

Основную идею задачек я подсмотрел у Александра Алексеева (более известного как 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;

Задача, описать что в данном коде не верно.

14 комментариев:

  1. Значение Result может быть не определенно?

    ОтветитьУдалить
  2. Ну да... ещё нет проверки результата...

    ОтветитьУдалить
  3. DllHandle > HINSTANCE_ERROR по идее там либо ПУСТО (NULL) (если ошибка), либо дескриптор модуля, а HINSTANCE_ERROR = $20.

    ОтветитьУдалить
  4. @DwmIsCompositionEnabledFunc <> nil всегда даст TRUE, собачка лишняя

    ОтветитьУдалить
  5. Как уже написали выше, Result будет не определено в случае ошибки и строка if DllHandle > HINSTANCE_ERROR then неправильная, но работать будет.
    И еще PBool = ^LongBool;, поэтому var Flag: Boolean; неправильно, будет портиться стэк. Правильно var Flag: LongBool;.

    ОтветитьУдалить
  6. Извиняюсь, "@DwmIsCompositionEnabledFunc <> nil всегда даст TRUE, собачка лишняя" - мое ошибочное мнение. Оказывается для переменных процедурного типа оператор @ - является уточнением, что необходимо взять значение переменной DwmIsCompositionEnabledFunc а не вызвать процедуру. (Для получения адреса переменной нужно писать @@) Я этого не знал.

    ОтветитьУдалить
  7. 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]

    ОтветитьУдалить
    Ответы
    1. Размер Boolean 1 байт, размер LongBool (Bool то же самое) 4 байта. В функцию передается указатель на переменную Flag размером в 1 байт. Функция по этому указателю запишет 4 байта.
      Стек может не портиться только потому, что компилятор выровняет все переменные по границе в 4 байта.

      Удалить
  8. 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 нет необходимости

    ОтветитьУдалить
  9. С HINSTANCE_ERROR интрига, однако :)
    В действительности как раз данная проверка является именно классической, чуть позже, когда буду давать развернутый ответ на вопрос, я постараюсь не забыть раскрыть данный нюанс :)
    А вообще, как говорил один хороший дядька - за проверку инстанса модуля с нулем, гвоздь нужно забивать прямо по центру лба :)
    Но - попробую подкинуть интриги, данный код будет работать по разному в зависимости от типа сборки (с оптимизацией или без) правда не под всеми версиями Delphi

    ОтветитьУдалить
    Ответы
    1. >> как говорил один хороший дядька - за проверку инстанса модуля с нулем,
      А вот с этого поподробнее пожалуйста... Какой дядка?
      Чем это код
      if hModule > $20 кошернее чем if hModule > 0

      Удалить
    2. "Хороший дядька" видимо немолодой, и начинал писать код еще во времена Winodws 3.1. В Win16 действительно нужно было делать именно так. Но это было очень, очень давно.
      В MSDN недвусмысленно написано "If the function fails, the return value is NULL". Не думаю, что в наше время стоит писать с оглядкой на Win16.

      Удалить
    3. >> if hModule > $20 кошернее чем if hModule > 0
      LoadLibrary возвращает не хэндл, а HMODULE, т.е. адрес загрузки библиотеки в адресное пространство процесса.
      Библиотека не может быть подгружена в первые 64кб памяти приложения, ибо эта память используется для проверки на битые указатели.
      Поэтому что проверка на ноль, что проверка на HINSTANCE_ERROR ничего не даст в случае если LoadLibrary вернула какой-то адрес меньший чем 0x10000 (а сделать это достаточно просто, проведя модификацию в таблице PLDR_DATA_TABLE_ENTRY)
      Сама-же проверка это как уже сказали, это наследие аж от Win 3.1 постоянно встречающаяся в исходниках Windows.

      Удалить
  10. Думаю больше вариантов ждать смысла не имеет, развернутый ответ готов: http://alexander-bagel.blogspot.ru/2013/12/1_19.html

    ОтветитьУдалить