I'm having problems while using Delphi application as Windows 7 logon screensaver (for both 32-bit and 64-bit Windows). Even blank application (New Project without any extra code) throws an error.
Delphi 7 application throws "The memory could not be read" error and Delphi 2010 application throws "The exception unknown software exception occurred in the application" and then "Runtime error 217". This error happens before any form initialization and before any initialization of exception handlers.
Setting notepad.exe as logon screensaver works fine.
Any ideas what goes on here?
As I said in my comment, it's not "invisible code", just code in the initialization section of some unit that's causing the problem. I've managed to track down the culprit (well at least one of them - there may be others).
When you use the Forms
unit, it has a dependency on the Classes
unit.
The initialization section calls InitThreadSynchronization
, which amongst other things calls the following:
SyncEvent := CreateEvent(nil, True, False, '');
if SyncEvent = 0 then
RaiseLastOSError;
It seems the API call CreateEvent
fails when called from within the login screen. Unfortunately I'm unsure whether the login screen: (a) forbids CreateEvent
altogether (b) requires CreateEventEx
instead or (c) would work with an appropriate lpEventAttributes
argument. I've posted a more specific question to hopefully find out: CreateEvent from Windows-7 Logon Screen
You can verify the problem with the following console app:
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.
The purpose of SyncEvent
is to enable TThread
instances to synchronise back to the main thread. So if you write a single threaded app, or create your threads using something other than TThread
, you don't actually need/use SyncEvent
at all.
SIDE-RANT: This is a prime example of the problem with using the initialization section. Merely including a unit has the potential to introduce unnecessary side-effects. They're Mostly Harmless, but not in this case. Now you may argue that
Classes.pas
is bloated, and I won't argue. But the point is that if Classes initialization were called explicitly from the DPR, this problem would have been easier to identify and find a workaround for.
As Remy Lebeau noted in the other question I posted.
The line:
SyncEvent := CreateEvent(nil, True, False, '');
Must be changed to:
SyncEvent := CreateEvent(nil, True, False, nil);
Since this solution involves recompiling VCL units, you may want to go through a few of the previous questions on this subject
With this as the only change (compiled in D2009) I was able to successfully show a blank form at the Logon screen. However, bear in mind that some things you may normally expect to be able to do will be off limits due to the security restrictions at the Logon screen.
After a little playing around. This has to be connected to Delphi's hidden main (real main) window you will need to look seriously at Application.initialise or Application.HookMainWindow().
Because amazingly this code does not cause a problem:
program w7logonsaver;
{$APPTYPE CONSOLE}
var
i: Integer;
begin
for i := 1 to 20 do
writeln;
write('K ');
ReadLn;
end.
Just hit enter to quit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With