我在使用 Delphi 应用程序作为 Windows 7 登录屏幕保护程序(适用于 32 位和 64 位 Windows)时遇到问题。即使是空白应用程序(没有任何额外代码的新项目)也会引发错误。
Delphi 7 应用程序抛出“无法读取内存”错误,Delphi 2010 应用程序抛出“应用程序中发生异常未知软件异常”,然后抛出“运行时错误 217”。此错误发生在任何表单初始化之前以及异常处理程序的任何初始化之前。
将 notepad.exe 设置为登录屏幕保护程序效果很好。
你知道这里发生了什么吗?
最佳答案
正如我在评论中所说,这不是“隐形代码”,只是导致问题的某些单元的初始化部分中的代码。我已经成功地找到了罪魁祸首(至少是其中一个 - 可能还有其他人)。
当您使用Forms
时单位,它依赖于 Classes
单位。
初始化部分调用InitThreadSynchronization
,其中包括以下内容:
SyncEvent := CreateEvent(nil, True, False, '');
if SyncEvent = 0 then
RaiseLastOSError;
好像是API调用CreateEvent
从登录屏幕内调用时失败。不幸的是,我不确定登录屏幕是否:(a) 禁止 CreateEvent
(b) 总共需要 CreateEventEx
相反,或 (c) 将与适当的 lpEventAttributes
配合使用争论。我发布了一个更具体的问题希望能找到答案:CreateEvent from Windows-7 Logon Screen
您可以使用以下控制台应用程序验证问题:
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: 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.
编辑:新解决方案
正如 Remy Lebeau 在我发布的另一个问题中指出的那样。
该行:
SyncEvent := CreateEvent(nil, True, False, '');
必须更改为:
SyncEvent := CreateEvent(nil, True, False, nil);
由于此解决方案涉及重新编译 VCL 单元,因此您可能需要查看 previous questions 中的一些内容。关于这个话题
有了这个作为唯一的更改(在 D2009 中编译),我能够成功地在登录屏幕上显示空白表单。但是,请记住,由于登录屏幕上的安全限制,您通常期望能够执行的某些操作将受到限制。
关于delphi - Delphi 中的 Windows 7 登录屏幕保护程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5567775/