使用 CreateDesktop/SwitchDesktop 在新桌面中创建表单

2024-02-14

我需要为一个实用程序创建一个系统模式形式,该实用程序应该阻止整个窗口,直到输入某些值。所以我正在尝试创建桌面和切换。到目前为止,创建一个切换到它的桌面,然后返回对我来说效果很好。

But, 当我尝试从新线程中创建表单时,该表单不会显示,但应用程序保留在新创建的空白桌面中,因此永远阻塞屏幕,直到我注销。

我是根据这里找到的代码制作的:

http://developex.com/blog/system-modal-back/ http://developex.com/blog/system-modal-back/

// ScreenLocker.h

#pragma once

using namespace System;
using namespace System::Windows::Forms;

namespace Developex
{
   public ref class ScreenLocker
   {
   private:
      String ^_desktopName;
      Form ^_form;
      void DialogThread(void);

   public:
      static void ShowSystemModalDialog (String ^desktopName, Form ^form);
   };
}


// ScreenLocker.cpp

#include "stdafx.h"
#include "ScreenLocker.h"

using namespace System::Threading;
using namespace System::Runtime::InteropServices;

namespace Developex
{
   void ScreenLocker::DialogThread()
   {
      // Save the handle to the current desktop
      HDESK hDeskOld = GetThreadDesktop(GetCurrentThreadId());

      // Create a new desktop
      IntPtr ptr = Marshal::StringToHGlobalUni(_desktopName);
      HDESK hDesk = CreateDesktop((LPCWSTR)ptr.ToPointer(),
         NULL, NULL, 0, GENERIC_ALL, NULL);
       Marshal::FreeHGlobal(ptr);

      // Switch to the new deskop
      SwitchDesktop(hDesk);

      // Assign new desktop to the current thread
      SetThreadDesktop(hDesk);

      // Run the dialog
      Application::Run(_form);

      // Switch back to the initial desktop
      SwitchDesktop(hDeskOld);
      CloseDesktop(hDesk);
   }

   void ScreenLocker::ShowSystemModalDialog(String ^desktopName, Form ^form)
   {
     // Create and init ScreenLocker instance
      ScreenLocker ^locker = gcnew ScreenLocker();
      locker->_desktopName = desktopName;
      locker->_form = form;

      // Create a new thread for the dialog
      (gcnew Thread(gcnew ThreadStart(locker,
         &Developex::ScreenLocker::DialogThread)))->Start();
   }
}

好吧,现在我正在尝试将其“翻译”为 Delphi,到目前为止,这就是我所拥有的:

unit Utils;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ADODB, Grids, DBGrids, ExtCtrls, ComCtrls, SyncObjs, ShellApi,
  AddTimeU;

type
  TFormShowThread = class(TThread)
    HDesktopglobal: HDESK;
    hDeskOld: HDESK;

    UHeapSize: ULong;
    tempDword: DWORD;

    frm : TfrmBlockScreen;
  private
  protected
    procedure Execute; override;
  public
    constructor Create(form : TfrmBlockScreen);
    destructor Destroy; override;
  end;

implementation

constructor TFormShowThread.Create(form : TfrmBlockScreen);
begin
  FreeOnTerminate := True;
  inherited Create(True);
  frm := form;
end;


destructor TFormShowThread.Destroy;
begin
  inherited;
end;

procedure TFormShowThread.Execute;
begin
    hDeskOld := GetThreadDesktop(GetCurrentThreadId());

    HDesktopglobal := CreateDesktop('Z', nil, nil, 0, GENERIC_ALL, nil);

    SwitchDesktop(HDesktopglobal);
    SetThreadDesktop(HDesktopglobal);

    // tried this
    Application.CreateForm(TfrmBlockScreen, frm);

    // also tried this with same result
    //frm := TfrmBlockScreen.Create(nil);
    //frm.Show();

    SwitchDesktop(hDeskOld);

    CloseDesktop(HDesktopglobal);
end;

end.

我用这段代码运行它:

var
  frmBlockScreen : TfrmBlockScreen;
  frmShowThread : TFormShowThread;
begin

  frmShowThread := TFormShowThread.Create(frmBlockScreen);
  frmShowThread.Priority := tpNormal;
  frmShowThread.OnTerminate := ThreadDone;
  frmShowThread.Start();

我不明白为什么这不起作用,而 C++ 应该可以工作,它在同一应用程序中创建一个新表单。

这就是我结束这样做的方式:

我将想要显示的表单移至一个新项目并将其编译为 timeup.exe。 我使用如下所示的过程创建了一个进程,将桌面作为参数发送,以便我可以将该进程分配给该桌面。 这样我什至不需要创建一个新线程......到目前为止它正在工作。

这其中有什么缺陷吗?

var
  HDesktopglobal: HDESK;
  hDeskOld: HDESK;

  sDesktopName : String;
begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;

  try
    hDeskOld := GetThreadDesktop(GetCurrentThreadId());

    sDesktopName := 'TimeUpDesktop';

    HDesktopglobal := CreateDesktop(PWideChar(sDesktopName), nil, nil, 0, GENERIC_ALL, nil);

    SwitchDesktop(HDesktopglobal);
    SetThreadDesktop(HDesktopglobal);

    ExecNewProcess('TimeUp.exe', sDesktopName);

    SwitchDesktop(hDeskOld);
    CloseDesktop(HDesktopglobal);

  finally
    SwitchDesktop(hDeskOld);
    CloseDesktop(HDesktopglobal);
  end;

  Application.Run;
end.



procedure ExecNewProcess(ProgramName : String; Desktop : String);
var
  StartInfo  : TStartupInfo;
  ProcInfo   : TProcessInformation;
  CreateOK   : Boolean;
begin

  { fill with known state }
  FillChar(StartInfo,SizeOf(TStartupInfo),#0);
  FillChar(ProcInfo,SizeOf(TProcessInformation),#0);
  StartInfo.cb := SizeOf(TStartupInfo);
  StartInfo.lpDesktop := PChar(Desktop);

  CreateOK := CreateProcess(PChar(ProgramName),nil, nil, nil,False,
              CREATE_NEW_PROCESS_GROUP+NORMAL_PRIORITY_CLASS,
              nil, nil, StartInfo, ProcInfo);

  { check to see if successful }
  if CreateOK then
    //may or may not be needed. Usually wait for child processes
    WaitForSingleObject(ProcInfo.hProcess, INFINITE);
end;

在继续之前,您需要认识到 VCL 设计强制所有 VCL 表单与主 GUI 线程关联。您不能在不同的线程上创建它们。所以你的设计在这方面从根本上来说是有缺陷的。除了主 GUI 线程之外,您永远无法在任何线程中创建 VCL 表单。

即使情况并非如此,您的代码也无法做任何有用的事情。那是因为你的线程不包含消息循环。表单一创建,与其关联的线程就会终止。

您可以通过原始 Win32 调用来完成此工作CreateWindow但您至少需要在线程中创建的任何窗口的生命周期内运行消息循环。

至于为什么你的代码永远不会切换回原来的桌面,我无法确定。也许尝试创建表单的代码中存在异常,因此恢复原始桌面的代码永远不会运行。该代码应该受到 try/finally 的保护。

一般来说,为了调试调用原始 Win32 API 的代码,必须包含错误检查。您没有执行任何操作,因此您不知道哪个 API 调用失败。如果我们还不知道该方法无论如何都注定会失败,那么这将是调试此类问题的第一步。


也许我遗漏了一些东西,但对我来说,为什么你试图从不同的线程中运行这个表单并不明显。有什么原因导致它无法从主 GUI 线程中运行出来吗?

为了回答我自己的问题,我错过了一些东西。从文档SetThreadDesktop http://msdn.microsoft.com/en-us/library/windows/desktop/ms686250.aspx:

如果调用线程在其当前桌面上有任何窗口或挂钩(除非 hDesktop 参数是当前桌面的句柄),则 SetThreadDesktop 函数将失败。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 CreateDesktop/SwitchDesktop 在新桌面中创建表单 的相关文章

随机推荐

  • 如何在数据表JavaScript中制作垂直标题列表?

    如何在数据表中设置垂直标题列 It should be as follows and working as datatble 您正在寻找数学中称为矩阵转置的东西 要获得矩阵 表的转置 请将行转换为列
  • Android、Google Drive:Google 同意屏幕冻结

    我已将我的应用程序升级到 Google Drive REST API 及其 Java 包装器 如下所述 https developers google com drive android deprecation https develope
  • 在没有布局管理器的情况下将 JScrollPane 添加到 JPanel

    在开始之前 我知道不使用布局管理器是一个坏主意 通常我确实使用布局管理器 但是 我还让所有组件根据窗口的大小自动重新调整大小和重新定位 此外 我正在开发的程序在其整个生命周期中仅打算在一台机器上运行 请不要仅仅因为缺少布局管理器而对我投反对
  • 将项目数组拆分为 N 个数组

    我想将数字数组分成N组 必须从larger to smaller groups 例如 在下面的代码中 分割一个数组12数入5数组 结果应该从大 组 到小均匀分割 source 1 2 3 4 5 6 7 8 9 10 11 12 outpu
  • C/C++ 大数计算

    我正在尝试在 C 程序中计算以下数字 result 3 pow 2 500000000 2 1000000000 2 的幂太大 无法正确处理 gt 我的印象是 我可以使用模数将计算分成多个步骤 以减少结果大小 有人有这样做的策略吗 还有其他
  • 将 WP 8.0 应用程序定位到 WP8.1 应用程序?

    我安装了 Visual Studio 2013 含更新 2 我正在 VS2012 中为 Windows Phone 8 0 开发我的项目 现在我决定Upgrade它到 Windows Phone 8 1 应用程序 所以我按照说明进行操作Ta
  • 选择选项卡时更改 div 高度

    我有一个选项卡式内容 有 4 个选项卡 每个选项卡中都有两个 div 构成边框设计 我遇到的问题是 当选择它们所在的选项卡时 我不知道如何对 div 进行动画处理以更改高度 我有一个小提琴供参考 http jsfiddle net jw6t
  • 如何将 CGRect 的大小增加一定的百分比值?

    如何将 CGRect 的大小增加一定的百分比值 我应该使用某种形式的CGRectInset去做吧 Example 假设我有一个 CGRect 10 10 110 110 我想将其大小增加 20 保留相同的中心点 0 0 120 120 您可
  • Android Preview M:授予权限后重新创建活动

    我使用 Preview M 并在其上测试我的应用程序 尤其是 将文件保存到外部存储 部分 在下载 保存过程开始之前 我请求 Manifest permission WRITE EXTERNAL STORAGE 开发者页面上描述的权限 htt
  • 从 Maven 部署到 Nexus 出现错误:ReasonPhrase:Forbidden

    http numberformat wordpress com 2011 05 18 nexus repository http numberformat wordpress com 2011 05 18 nexus repository
  • NetworkStream.Write 与 Socket.Send

    我有一个 C 应用程序 我使用自定义 FTP 库 现在我使用 Socket Send 发送数据 但我想知道使用套接字启动 NetworkStream 并使用 NetworkStream Write 是否会更好 使用其中一种比另一种有什么优势
  • Haskell 中的 HTTP POST 内容

    我正在尝试将一些数据发布到 Haskell 中的服务器 但服务器端为空 我正在使用 Network HTTP 库来处理请求 module Main main where import Network URI URI parseURI uri
  • Postgres `gin_trgm_ops` 索引未被使用

    我试图speed up https stackoverflow com questions 56483600 composite jsonb array query in postgresPostgres 中的一些文本匹配 使用pg trg
  • 如何在 dask/distributed 中存储工作线程局部变量

    使用dask 0 15 0 分布式1 17 1 我想记住每个工作人员的一些事情 比如访问谷歌云存储的客户端 因为实例化它是昂贵的 我宁愿将其存储在某种工作者属性中 执行此操作的规范方法是什么 或者全局变量是正确的选择吗 关于工人 您可以通过
  • jquery ajax 调用不起作用?!是firefox还是xss问题?

    我的问题是 在 Firefox 中我没有得到任何回应 在 ie 中它工作得很好 我想要对本地脚本进行 ajax 调用 以纯文本或其他方式获取一些信息 但这行不通 我认为跨脚本此时应该不是问题 或者 JavaScript 代码很简单 var
  • 如何在程序开始执行时设置断点

    如何在加载任何链接的 DLL 之前停止程序 我尝试过设置LoadLibraryExW函数在Break At Function调试选项 它会在该函数处停止 但在此之前 我在 Visual Studio 输出窗口中有以下内容 test exe
  • Sequelize - 通过匹配所有标签来过滤 FindAll

    assets tags foo tags bar 对上述端点的获取请求应仅返回包含所有提供的标签的记录 foo bar 我当前的尝试是返回与任何给定标签匹配的任何记录 const tags req query res send await
  • Angular 10 中主组件内的延迟加载模块

    我的应用程序中有两个模块 其中一个是app module另一个是user module这会被延迟加载 上app module我有一个sign in成分 sign up组件和main成分 这main component是由导航栏和侧边栏组成的
  • 设置回调数组并尝试使用数组索引作为回调中的值

    当我以这种方式设置回调数组时 我在所有回调的对话框窗口中得到 20 我想获取数组中的索引 这可能吗 调用回调的函数期望回调有一个参数 我不控制回调的调用者 因为它是外部库的一部分 任何帮助表示赞赏 for var i 0 i lt 20 i
  • 使用 CreateDesktop/SwitchDesktop 在新桌面中创建表单

    我需要为一个实用程序创建一个系统模式形式 该实用程序应该阻止整个窗口 直到输入某些值 所以我正在尝试创建桌面和切换 到目前为止 创建一个切换到它的桌面 然后返回对我来说效果很好 But 当我尝试从新线程中创建表单时 该表单不会显示 但应用程