Подтвердить что ты не робот

Заставка Windows 7 для входа в Delphi

У меня возникают проблемы при использовании приложения Delphi в качестве заставки для Windows 7 (для 32-разрядной и 64-разрядной Windows). Даже пустое приложение (новый проект без какого-либо дополнительного кода) выдает ошибку.

Приложение Delphi 7 бросает сообщение "Ошибка памяти не может быть прочитано", а приложение Delphi 2010 выбрасывает "Исключение неизвестного программного обеспечения в приложении", а затем "Ошибка выполнения 217". Эта ошибка возникает перед любой инициализацией формы и перед любой инициализацией обработчиков исключений.

Настройка notepad.exe в качестве заставки для входа в систему работает нормально.

Любые идеи, что здесь происходит?

4b9b3361

Ответ 1

Как я уже сказал в своем комментарии, это не "невидимый код", просто код в разделе инициализации какой-то единицы, вызывающей проблему. Мне удалось выследить преступника (ну хотя бы один из них - могут быть и другие).

Когда вы используете блок Forms, он имеет зависимость от блока Classes.

Раздел инициализации вызывает InitThreadSynchronization, который, среди прочего, вызывает следующее:

SyncEvent := CreateEvent(nil, True, False, '');
if SyncEvent = 0 then
  RaiseLastOSError;

Кажется, что вызов API CreateEvent невозможен при вызове из экрана входа. К сожалению, я не уверен, что экран входа в систему: (a) запрещает CreateEvent вообще (b) требует CreateEventEx вместо или (c) будет работать с соответствующим аргументом lpEventAttributes. Я написал более конкретный вопрос, чтобы, надеюсь, узнать: CreateEvent с экрана входа в Windows-7

Вы можете проверить проблему со следующим консольным приложением:

program TestLoginScreensaver;

{$APPTYPE CONSOLE}

uses
  Windows,
  SysUtils;

var
  SyncEvent: THandle;

begin
  try
    SyncEvent := CreateEvent(nil, True, False, '');
    if SyncEvent = 0 then
      RaiseLastOSError;
    CloseHandle(SyncEvent); //So handle is closed if it was created (e.g. while logged in)
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
  Readln;
end.

Цель SyncEvent - включить экземпляры TThread для синхронизации с основным потоком. Поэтому, если вы пишете однопоточное приложение или создаете свои потоки с помощью чего-то другого, кроме TThread, вам вообще не нужно/использовать SyncEvent.

SIDE-RANT. Это яркий пример проблемы с использованием раздела инициализации. Простое включение устройства может ввести ненужные побочные эффекты. Они в основном безвредны, но не в этом случае. Теперь вы можете утверждать, что Classes.pas раздувается, и я не буду спорить. Но дело в том, что если инициализация классов была вызвана явно из DPR, эта проблема была бы легче идентифицировать и найти обходной путь для.


EDIT: новое решение

Как заметил Реми Лебо в другом вопросе, который я опубликовал.
Строка:

    SyncEvent := CreateEvent(nil, True, False, '');

Необходимо изменить на:

    SyncEvent := CreateEvent(nil, True, False, nil);

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

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

Ответ 2

После небольшой игры. Это должно быть связано с скрытым основным (реальным) окном Delphi, вам нужно будет серьезно относиться к Application.initialise или Application.HookMainWindow().

Потому что удивительно, что этот код не вызывает проблемы:

program w7logonsaver;
{$APPTYPE CONSOLE}

var
  i: Integer;
begin
  for i := 1 to 20 do
    writeln;
  write('K ');
  ReadLn;
end.

Просто нажмите enter, чтобы выйти.