Заставка входа в Windows 7 в Delphi - PullRequest
12 голосов
/ 06 апреля 2011

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

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

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

Есть идеи, что здесь происходит?

Ответы [ 2 ]

5 голосов
/ 13 июля 2014

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

Когда вы используете блок 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 : Это яркий пример проблемы с использованием раздела initialization . Простое включение устройства может привести к ненужным побочным эффектам. Они В основном безвредны , но не в этом случае. Теперь вы можете утверждать, что Classes.pas раздут, и я не буду спорить. Но дело в том, что если бы инициализация классов была вызвана явно из DPR, эту проблему было бы легче идентифицировать и найти обходной путь для.


РЕДАКТИРОВАТЬ: новое решение

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

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

Должен быть изменен на:

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

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

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

1 голос
/ 07 апреля 2011

После небольшой игры.Это должно быть связано со скрытым главным (реальным главным) окном 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, чтобы выйти.

...