7 марта 2024 г.

Тайна Access Denied

К нам обратился клиент, который сообщил, что не может установить EurekaLog. Вернее, EurekaLog устанавливается, но последующий запуск IDE возбуждает ошибку:
Can't load package C:\Program Files (x86)\Neos Eureka S.r.l\EurekaLog 7\Packages\Studio25\EurekaLogExpert250.bpl. Access is denied.
Причём клиент утверждал, что файл существует, доступ к нему есть, переустановка EurekaLog не помогает.

Дополнительно было установлено, что код из пакета EurekaLogExpert250.bpl действительно не получает управления (не выполняется).

IDE использует ту же функцию LoadPackage из модуля SysUtils, что и вы (ваш код). Никакого секретного кода IDE не выполняет. Функция LoadPackage реализована следующим образом:
function LoadPackage(const Name: string; AValidatePackage: TValidatePackageProc): HMODULE;
begin
  Result := SafeLoadLibrary(Name);
  if Result = 0 then
    raise EPackageError.CreateResFmt(@sErrorLoadingPackage, [Name, SysErrorMessage(GetLastError)]); // -----
  try
    InitializePackage(Result, AValidatePackage);
  except
    FreeLibrary(Result);
    raise;
  end;
end;
где функция InitializePackage реализована так:
procedure InitializePackage(Module: HMODULE; AValidatePackage: TValidatePackageProc);
type
  TPackageLoad = procedure;
var
  PackageLoad: TPackageLoad;
begin
  CheckForDuplicateUnits(Module, AValidatePackage);
  @PackageLoad := GetProcAddress(Module, 'Initialize'); //Do not localize
  if Assigned(PackageLoad) then
    PackageLoad
  else
    raise EPackageError.CreateFmt(sInvalidPackageFile, [GetModuleName(Module)]);
end;
Как вы можете видеть: единственным местом, где при загрузке пакета может возбуждаться исключение с кодом ошибки от ОС (5 = ERROR_ACCESS_DENIED) является помеченная строка. Откуда следует, что ошибка Access Denied при загрузке пакета может произойти только при неудачном вызове функции LoadLibrary.

Поэтому клиенту было предложено создать новое пустое VCL приложение с таким тестовым кодом:
procedure TForm1.Button1Click(Sender: TObject);
var
  Lib: HMODULE;
begin
  Lib := LoadPackage('C:\Program Files (x86)\Neos Eureka S.r.l\EurekaLog 7\Packages\Studio25\EurekaLogExpert250.bpl');
  if Lib = 0 then
    RaiseLastOSError
  else
    MessageBox(0, 'The package was loaded!', 'Test', 0);
end;
Как оказалось, тестовое приложение успешно загружает пакет.

Чтобы точно быть уверенным, что IDE не выполняет никакого дополнительного скрытого кода, мы попросили клиента создать новые Design-Time пакет с модулем, содержащим только тестовый код:
unit Unit1;

procedure Register;

implementation

uses
  Windows, SysUtils;

procedure Register;
var
  Lib: HMODULE;
begin
  Lib := LoadPackage('C:\Program Files (x86)\Neos Eureka S.r.l\EurekaLog 7\Packages\Studio25\EurekaLogExpert250.bpl');
  if Lib = 0 then
    RaiseLastOSError
  else
    MessageBox(0, 'The package was loaded!', 'Test', 0);
end;

end.
И при попытке загрузить этот тестовый модуль IDE снова выдала сообщение об ошибке.

Сделаем промежуточный итог:
  • Тестовое приложение успешно загружает пакет;
  • Тестовый пакет (и IDE) не могут загрузить пакет.
Это решительно намекает, что проблема не в пакете, а в IDE.

Мы также попытались сделать следующее:
  1. Запустить IDE (без EurekaLog);
  2. Открыть пункт меню Run / Load Process;
  3. Указать bds.exe как целевой процесс для отладки;
  4. Запустить второй экземпляр IDE. При этом первый экземпляр будет отладчиком, а второй - отлаживаемым;
  5. В отлаживаемом процессе попытаться добавить пакет EurekaLogExpert250.bpl;
  6. В отладчике следить за возникающими исключениями.
Данный алгоритм не дал никакой новой информации: в отладчике мы увидели, что в отлаживаемом процессе функция LoadLibrary возвращает 0, что и приводит к возбуждению ошибки - как мы и предполагали выше.
Поскольку и мы, и клиент уже по сто раз проверили права доступа к файлу EurekaLogExpert250.bpl и не увидели проблем, нужно было делать что-то ещё.

Пакет EurekaLogExpert250.bpl является Design-Time пакетом. Он зависит от Run-Time пакета EurekaLogCore250.bpl. Это означает, что если функция LoadLibrary загружает пакет EurekaLogExpert250.bpl, то она увидит, что пакет EurekaLogExpert250.bpl ссылается на EurekaLogCore250.bpl и попробует загрузить и его. И если проблема возникла при загрузке пакета EurekaLogCore250.bpl - она всплывёт на верхний уровень и будет возвращаена вызывающему функцией LoadLibrary.

Поэтому клиент также проверил файл EurekaLogCore250.bpl (он находится в папке C:\Windows\System32) и не нашёл проблем.

Далее мы попросили клиента использовать утилиту Process Monitor от Microsoft / SysInternals, чтобы пронаблюдать за тем, как IDE производит доступ к файлам. Необходимо было запустить IDE, открыть диалог добавления пакета, затем запустить Process Monitor и указать фильтр "Process Name=bds.exe => include". После чего попытаться добавить пакет, увидеть сообщение об ошибке и сохранить полученный отчёт.

В полученном отчёте мы запустили поиск по слову "Eureka" и сразу же увидели, что bds.exe смогла открыть и прочитать файл EurekaLogExpert250.bpl - что дополнительно подтверждает, что с файлом EurekaLogExpert250.bpl всё в порядке.

А вот строчки сразу ниже ввели нас в недоумение: Process Monitor сообщил, что bds.exe пытается получить доступ к EurekaLogCore250.bpl из папки \bin IDE. Это странно, потому что современные версии EurekaLog не трогают папку \bin IDE.
(Очень) старые версии EurekaLog устанавливали файлы ecc32.exe в папку \bin IDE. Нам пришлось убрать это поведение, потому что современные версии IDE могут выбросить ошибку проверки целостности/лицензии, если в папке \bin будут находиться сторонние файлы:
Question: I’ve installed and registered C++ Builder or Delphi, yet when it starts I am brought to a web page with the error “Product or License Validation Error”. How can I fix this?
Answer: By far the most common cause for this error is having files or applications not provided by Embarcadero that are copied into the bin folder below where RAD Studio is installed. Only files provided by Embarcadero may reside in the bin folder.
Поэтому современные версии EurekaLog не копируют никаких файлов в папку \bin IDE.
Но когда мы попытались проверить права доступа к файлу \bin\EurekaLogCore250.bpl, то... не нашли такого файла!

Как оказалось: дело в том, что \bin\EurekaLogCore250.bpl - это каталог! Тайна раскрыта!

Итак, происходило следующее:
  1. IDE вызывает функцию LoadPackage для загрузки пакета;
  2. Функция LoadPackage вызывает функцию LoadLibrary для загрузки файла EurekaLogExpert250.bpl;
  3. Функция LoadLibrary (успешно) открывает и читает файл EurekaLogExpert250.bpl;
  4. Функция LoadLibrary видит, что файл EurekaLogExpert250.bpl содержит ссылку на файл EurekaLogCore250.bpl;
  5. Функция LoadLibrary пытается загрузить файл EurekaLogCore250.bpl;
  6. Функция LoadLibrary использует стандартные правила поиска библиотек операционной системы и видит, что EurekaLogCore250.bpl находится прямо тут: в папке \bin IDE;
  7. Функция LoadLibrary пытается загрузить EurekaLogCore250.bpl из папки \bin IDE;
  8. Функция LoadLibrary возвращает ошибку 5 (Access Denied), поскольку папка EurekaLogCore250.bpl из папки \bin IDE не имеет права доступа "EXECUTE".

И, действительно, всё заработало после удаления папки EurekaLogCore250.bpl из папки \bin IDE.

Но откуда взялась папка EurekaLogCore250.bpl в папке \bin IDE? Я не знаю. Установщик EurekaLog сделан с помощью InnoSetup. Разумеется, установочный файл InnoSetup для EurekaLog понятия не имеет про установленные у вас IDE и каталоги, в которые они установлены. И поэтому установочный файл InnoSetup для EurekaLog не сможет создать там папку - просто потому, что он не знает, где это.

Регистрация же EurekaLog в IDE производится программой регистрации: .exe файлом, скомпилированным в Delphi из нашего исходного кода. Проблема в том, что в нашем исходном коде для регистрации EurekaLog нет ни одного вызова MkDir или ForceDirectories: программа регистрации EurekaLog не создаёт каталогов, она копирует файлы и вносит данные о них в реестр Windows.

Даже если бы EurekaLogCore250.bpl в папке \bin IDE создавался нашим установщиком: почему это каталог, а не файл? И почему он один? Почему там же нет EurekaLogExpert250.bpl и EurekaLogComponent250.bpl - ведь эти три файла идут вместе.

Пока всё выглядит так, словно кто-то создал каталог EurekaLogCore250.bpl в папке \bin IDE. Возможно, это какой-то IDE эксперт?

Если вы наш клиент, если вы увидели эту ошибку и знаете, кто создаёт каталог EurekaLogCore250.bpl в папке \bin IDE - пожалуйста, сообщите нам.

Комментариев нет :

Отправить комментарий

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и (опционально) ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.

Примечание. Отправлять комментарии могут только участники этого блога.