Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/windows/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用CreateDesktop/SwitchDesktop在新桌面中创建表单_C++_Windows_Delphi_Winapi - Fatal编程技术网

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

C++ 使用CreateDesktop/SwitchDesktop在新桌面中创建表单,c++,windows,delphi,winapi,C++,Windows,Delphi,Winapi,我需要为一个实用程序创建一个系统模式表单,它应该阻止整个窗口,直到输入某些值。所以我正在尝试创建桌面和切换。到目前为止,创建一个桌面切换到它,然后返回对我来说很好 但是,当我尝试从一个新线程中创建表单时,表单不会显示,但应用程序会保留在新创建的空白桌面中,因此在我注销之前会永远阻塞屏幕 我是根据这里的代码制作的: 好吧,现在我正试图将其“翻译”到德尔福,到目前为止,这就是我所拥有的: unit Utils; interface uses Windows, Messages, SysUt

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

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

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

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

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。 我使用如下所示的过程创建了一个进程,将桌面作为参数发送,以便将进程分配给该桌面。 这样我甚至不需要创建一个新的线程。。。到目前为止它还在工作

这里面有什么瑕疵吗


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

即使不是这样,您的代码也不能做任何有用的事情。这是因为线程不包含消息循环。表单一创建,它所关联的线程就终止了

您可以通过对
CreateWindow
等的原始Win32调用来实现这一点,但您至少需要在线程中运行一个消息循环,以便在创建的任何窗口的生命周期内运行

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

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


也许我遗漏了一些东西,但我不清楚为什么您试图从另一个线程运行此表单。有没有什么原因可以解释为什么它不能在主GUI线程中运行

回答我自己的问题,我遗漏了一些东西。根据以下文件:

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


“这不起作用”是毫无帮助的。如果我们知道它会做什么,那会更有帮助。我在第一段中解释道:“表单不会显示,但应用程序保留在新创建的空白桌面上,因此在我注销之前会永远阻塞屏幕。”问题是VCL不是线程安全的,而且,您无法使用
Application.CreateForm
的方式在辅助线程中创建表单。如果要从主(GUI)线程以外的线程创建和显示窗口,则需要使用API调用来完成此操作。(请参见MSDN上的
RegisterClass
CreateWindow
)听起来像是恶意软件。无论如何,创建一个新的流程并使用IPC在两者之间进行通信听起来是最稳健的设计,而不管您在其他方法方面存在什么问题。我也做过类似的事情(虽然在同一个桌面上),它工作得很好——我使用命名管道进行通信。我建议你坚持下去!谢谢,是的,我知道我错过了循环。但我想尝试一下现有的代码。您的主要观点是真正的答案:VCL表单仅与主GUI线程相关联。所以,如果我想显示一个“表单”,我想我需要创建一个表单应用程序,由CreateProcess调用调用。。。。对吗?为什么您需要此表单位于不同的线程或进程中?为什么不能从主GUI线程中运行它呢?我在一个线程上执行了在主问题中显示的C++示例。为了“帮助”窗体显示在另一个桌面上,这对我来说也是有意义的,但是在您解释了delphi窗体运行于哪些线程之后,我很清楚,无论如何都不需要它。实际上,在我找到C++代码之前,我在主线程中已经有了这个代码,但是我认为创建专用线程将是为什么它不工作的关键。但在主线程内或使用新线程时,我一直得到相同的结果:只需切换,表单从未出现,也从未切换回。好的,请参阅我的最新更新。您确实需要一个新线程,以便这是与该线程相关联的第一个窗口。谢谢,David,所以只需回顾一下。1) 我保持线程类2)使用CreateWindow手动创建表单
var
  frmBlockScreen : TfrmBlockScreen;
  frmShowThread : TFormShowThread;
begin

  frmShowThread := TFormShowThread.Create(frmBlockScreen);
  frmShowThread.Priority := tpNormal;
  frmShowThread.OnTerminate := ThreadDone;
  frmShowThread.Start();
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;