Delphi 中的 Windows 7 登录屏幕保护程序

Posted

技术标签:

【中文标题】Delphi 中的 Windows 7 登录屏幕保护程序【英文标题】:Windows 7 logon screensaver in Delphi 【发布时间】:2011-07-30 21:07:40 【问题描述】:

我在将 Delphi 应用程序用作 Windows 7 登录屏幕保护程序(适用于 32 位和 64 位 Windows)时遇到问题。即使是空白应用程序(没有任何额外代码的新项目)也会引发错误。

Delphi 7 应用程序抛出“无法读取内存”错误,Delphi 2010 应用程序抛出“应用程序中发生异常未知软件异常”,然后抛出“运行时错误 217”。此错误发生在任何表单初始化之前和任何异常处理程序初始化之前。

将 notepad.exe 设置为登录屏幕保护程序可以正常工作。

有什么想法吗?

【问题讨论】:

(我只需要尝试将 NOTEPAD.EXE 作为登录屏幕保护程序!真是一团糟!我也只需要尝试文件/打开来查看我可以访问的内容,而不是!) Andreas: LOL ;) 现在我想将频谱 3 加载器屏幕设置为登录屏幕保护程序。所以我们需要找出方法! @AndreasRejbrand 在文件/打开中,您可以按住 Shift 并右键单击文件夹以打开命令提示符。我能够运行 regedit,但无法更改任何内容。似乎安全限制确实限制了您可以做的一些事情。然而,更可怕的不是.DEFAULT,而是您自己的用户帐户。 “屏幕保护程序”(即记事本)在您自己的帐户的上下文中运行。而且您仍然拥有完全访问权限。换句话说,即使我在没有点击切换用户进入主登录屏幕的情况下锁定了我的桌面 - 我也没有被锁定! 【参考方案1】:

玩了一会儿。这必须连接到 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 即可退出。

【讨论】:

它发生在 Application.Initialize 之前。我想在一些不可见的代码中。 @Kaitnieks 不是“不可见的”,只是在某个单元的初始化部分编码。您可以通过在程序中包含/排除某些单元来缩小 VCL 单元的范围。【参考方案2】:

正如我在评论中所说,这不是“隐形代码”,只是某些单元的初始化部分中的代码导致了问题。我已经设法找到了罪魁祸首(至少其中一个 - 可能还有其他人)。

当您使用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:这是使用 initialization 部分问题的主要示例。仅仅包括一个单元就有可能引入不必要的副作用。它们几乎无害,但在这种情况下并非如此。现在你可能会争辩 Classes.pas 臃肿,我不会争辩。但关键是,如果从 DPR 中显式调用类初始化,则该问题会更容易识别并找到解决方法。


编辑:新解决方案

正如 Remy Lebeau 在我发布的另一个问题中指出的那样。 行:

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

必须改为:

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

由于此解决方案涉及重新编译 VCL 单元,您可能需要查看一些关于此主题的 previous questions

有了这个作为唯一的更改(在 D2009 中编译),我能够成功地在登录屏幕上显示一个空白表单。但是,请记住,由于登录屏幕上的安全限制,您通常希望能够执行的某些操作将不受限制。

【讨论】:

这听起来非常复杂,所以我现在不能去测试解决方案,但我没有理由怀疑它是否有效,所以我接受你的回答。 您不需要重新编译 RTL/VCL。由于违规代码仅在implementation 部分而不是interface 部分中,因此您可以复制Classes.pas 并相应地对其进行修改,然后将修改后的文件直接添加到您的项目中。修改后的代码将覆盖从 RTL 导入的本机代码。只要确保运行时包被禁用,否则这将不起作用。 @RemyLebeau 正确;正如你所说。 @Kaitnieks,澄清一下...解决方案只是对Classes.pas 的一个小改动。当我说“涉及重新编译 VCL 单元”时:我并不是说您必须重新编译“整个 RTL/VCL”。 Classes.pas 是一个 RTL/VCL 单元,所以你必须至少重新编译那个单元。我发现我还必须重新编译Controls.pas。我有点惊讶,因为我的改变只是实现;为什么不使用 Forms.pas 等其他单位?但我没有费心去进一步调查。如果您遇到困难,有关重新编译 VCL 单元的问题的链接应该会有所帮助。 Classes.pas 错误现已报告给 QC:#126105 Bad CreateEvent() lpName parameter value being used in System.Classes.InitThreadSynchronization()

以上是关于Delphi 中的 Windows 7 登录屏幕保护程序的主要内容,如果未能解决你的问题,请参考以下文章

Delphi 中的 Windows 7 样式通知浮出控件

适用于 Windows 7 的 Delphi/Paradox 数据库桌面 DBD32.exe 设置

Windows 7 上 Delphi 6 中的“特定于平台”警告

Delphi 在 Windows 7 64 上使用 LockFile

检测 Windows 登录屏幕是不是对 VB.NET 中的用户可见

如何在 C# 中创建自定义登录 Windows 屏幕?