首页
社区
课程
招聘
[原创]虚拟桌面技术的初步探讨
发表于: 2009-2-21 21:43 66997

[原创]虚拟桌面技术的初步探讨

2009-2-21 21:43
66997

【文章标题】: 虚拟桌面技术的初步探讨  
【文章作者】:  newjueqi  
【作者邮箱】: zengjiansheng1@126.com
【作者QQ号】: 190678908
【编写语言】:VC++6.0
【操作平台】: XP-SP2
【作者声明】: 这几天研究了虚拟桌面技术,感觉到这是一种非常有意思的技术,这篇就当成是学习笔记吧!本人只是感兴趣,没有其它目的,失误之处敬请给位大侠原谅!

什么是桌面?
      每一个运行着Window NT 的系统中都有一个Window 工作站对象,这个对象是安全对象的第一层,是所有用户安全对象的继承之源,每一个Window 工作站对象可以拥有一些桌面对象,每一个桌面都拥有一个窗口链。窗口链里存放着显示在所属桌面的各种窗口。Window NT 用了两个桌面窗口对象,一个是用来处理登陆界面、屏蔽、锁住工作站等,一个是我们登陆之后进来操作的窗口了。
    Window NT通过"explorer.exe"进程来管理这个桌面对象。这就是为什么我们在任务管理器里杀掉"explorer.exe",我们的桌面就会消失的原因。

什么是虚拟桌面?
     虚拟桌面是一种可以在电脑原来桌面基础上再创造一个新的桌面出来,在新的桌面上可以进行日常的操作。

虚拟桌面的用途?
(1)  本人觉得这门技术最重要的用途就是可以把任何有UI界面的软件变成一个后台软件(即看不到任何界面,包括启动界面)
(2)  可以时工作时是一个桌面,娱乐时是一个桌面(大家可以去下载网站上搜索一下这类软件的用途,上面的功能描述非常有意思)

虚拟桌面的实现方法
在windows中,要创建一下新的桌面可用到API:CreateDesktop(),函数声明如下
HDESK CreateDesktop(
  LPCTSTR lpszDesktop,         // 新桌面的名称
  LPCTSTR lpszDevice,          // 为NULL
  LPDEVMODE pDevmode,          // 为NULL
  DWORD dwFlags,               // 指定应用程序在桌面的兼容方式
  ACCESS_MASK dwDesiredAccess, // 指定新桌面的权限
  LPSECURITY_ATTRIBUTES lpsa   //指定句柄是否能被继承
);

返回值是新创建的桌面的句柄。

那么新建了一个桌面后,怎么在这个新的桌面上运行程序呢?先不要着急,我们先来回顾一下创建进程的函数CreateProcess(),在这个函数的参数中StartupInfo中有 lpDesktop这么一个属性,如果这个属性为NULL则在当前的桌面创建线程,如果指定了桌面的名称,则进程将会在指定的桌面上启动,所以想在创建的新桌面里初始化一些程序,只要把lpDesktop参数指定为新桌面的名称即可。

另外也有一个简单的方法可把新的线程挂在新创建的桌面下,就是使用API函数SetThreadDesktop(),声明如下:
BOOL SetThreadDesktop(
  HDESK hDesktop  // 指向指定的桌面句柄
);

但使用这个函数要注意一点,根据MSDN的说法:The SetThreadDesktop function will fail if the calling thread has any windows or hooks on its current desktop (unless the hDesktop parameter is a handle to the current desktop)  意思就是除非要指定的桌面句柄是当前的桌面,不然的话这个函数的调用会失败如果当前线程拥有任何的窗口(即UI界面).

怎么实现不同桌面之间的切换呢?
     要在不同的桌面之间切换,可用API函数SwitchDesktop,声明如下:
BOOL SwitchDesktop(
  HDESK hDesktop  // 桌面的句柄
);

另外也可通过点击“切换”按钮实现桌面的切换。

但又引申出一个新的问题,必须要知道各个桌面的句柄,获取桌面的句柄可通过API函数GetThreadDesktop,函数的声明如下:
HDESK GetThreadDesktop(
  DWORD dwThreadId   //线程的ID);

返回值就是指定线程所在的桌面了。
    而且我们必须要认清的是创建新桌面的线程启动是在旧的桌面上,所以可以用下面的语句轻松获得当前桌面的句柄:
GetThreadDesktop(GetCurrentThreadId());   
返回值就是旧的桌面句柄。

怎么关闭新创建的桌面?
     这个问题其实也不用我们担心,微软已经替我们想好了^-^ , 用CloseDesktop函数可轻松实现这个功能,函数声明如下:
    BOOL CloseDesktop(
  HDESK hDesktop  // 指定要关闭的桌面的句柄
);

下面贴一段代码的例子,是在新创建的桌面上运行计算器(calc.exe)实现计算器的后台运行

#include <windows.h>

HINSTANCE hInst;  //当前的实例句柄
HWND hWnd;        //窗口句柄
HDESK hvirtualDesk;    //新创建的虚拟桌面句柄
PROCESS_INFORMATION pi; //计算器进程信息

//消息循环
LRESULT CALLBACK WinProc(
      HWND hwnd,      // handle to window
      UINT uMsg,      // message identifier
      WPARAM wParam,  // first message parameter
      LPARAM lParam   // second message parameter
              )
{
  switch(uMsg)
  {
  case WM_CLOSE:
      TerminateProcess( pi.hProcess, 1 );
      CloseDesktop( hvirtualDesk );
      DestroyWindow(hwnd);
      PostQuitMessage(0);
      break;

  case WM_DESTROY:

      CloseDesktop( hvirtualDesk );
      PostQuitMessage(0);
      break;

  case WM_HOTKEY:

    if ( 0x0001 == wParam ) //为退出桌面热键Alt+Q
    {
            SendMessage(hwnd,WM_CLOSE,0,0);   
    }
    break;

  default:
    return DefWindowProc(hwnd,uMsg,wParam,lParam);
  }
  return 0;
}

//创建虚拟桌面
void CrateVirtualDesk()
{
  //把新创建的虚拟桌面句柄存放在hvirtualDesk
  hvirtualDesk=CreateDesktop( "newdesk",
                NULL,
                NULL,
                DF_ALLOWOTHERACCOUNTHOOK,   
                GENERIC_ALL,     
                NULL);   

}

//在虚拟桌面上运行一个计算器的实例
void RunCalc()
{
  STARTUPINFO si;   
  
  ZeroMemory( &si, sizeof(si) );  
  si.cb = sizeof(si);   
    si.lpDesktop = "newdesk";   
  
  ZeroMemory( &pi, sizeof(pi) );   
  
  if( !CreateProcess( NULL,                  
    "calc",                                       
    NULL,                       
    NULL,                     
    FALSE,                     
    0,                     
    NULL,                          
    NULL,                     
    &si,                       
    &pi ) )      
  {     
    MessageBox(NULL,"运行计算器失败","Error",0);     
    ExitProcess(1);     
  }     

}

int WINAPI WinMain(
    HINSTANCE hInstance,      // handle to current instance
    HINSTANCE hPrevInstance,  // handle to previous instance
    LPSTR lpCmdLine,          // command line
     int nCmdShow              // show state
           )
{
  WNDCLASS wndcls;
  MSG msg;

  hInst=hInstance;
  ZeroMemory( &wndcls,sizeof(wndcls) );
  wndcls.cbClsExtra=0;
  wndcls.cbWndExtra=0;
  wndcls.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
  wndcls.hInstance=hInstance;
  wndcls.lpfnWndProc=WinProc;
  wndcls.lpszClassName="hello";
  wndcls.lpszMenuName=NULL;
  wndcls.style=CS_HREDRAW | CS_VREDRAW;
  RegisterClass(&wndcls);
  
  hWnd=CreateWindow( "hello","hello",WS_OVERLAPPEDWINDOW,
    300,300,100,100,NULL,NULL,hInstance,NULL );
  
  //注册所需的热键,Alt+Q为退出创建的虚拟桌面
  if( !RegisterHotKey( hWnd,0x0001,MOD_ALT ,'Q' ) )     
    {     
        //处理退出进程     
        return TRUE;     
    }     

  //创建虚拟桌面
  CrateVirtualDesk();
  
  //在虚拟桌面上运行一个计算器的实例
  RunCalc();

  ShowWindow( hWnd,SW_SHOWNORMAL );
  UpdateWindow( hWnd );
  
  while( GetMessage( &msg,NULL,0,0 ) )
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return msg.wParam;
}

程序运行后如下图



红框的virtualDesktop.exe就是代码创建的程序,calc.exe就是计算器的进程,大家可以看一下任务栏,是没有计算器的影踪的,证明是运行在另一个桌面上,从另一个角度来看,就是运行在后台(只要不切换到另一桌面)。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (56)
雪    币: 485
活跃值: (12)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
2
也来研究一下,慢慢学习
2009-2-21 21:53
0
雪    币: 709
活跃值: (2420)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
3
谢谢分享      .
2009-2-21 22:00
0
雪    币: 6
活跃值: (133)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
这个东西我支持,希望做更多的尝试。
2009-2-21 23:23
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
这个很有用啊,游戏多开,不知可以不
2009-2-23 12:02
0
雪    币: 212
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
晕死  &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

我按照你的方法  创建了一个桌面  并进入了该桌面
可是该桌面除了 计算器之外  
啥也没有 ,想返回原桌面  却又无法返回     
只好重新启动系统   
原来写的代码都丢失了 5555555555555
2009-2-23 15:07
0
雪    币: 212
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  hvirtualdesk:thandle;
  si:STARTUPINFO;
  pi:PROCESS_INFORMATION ;
  caclpid:dword;
implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  hvirtualDesk:=CreateDesktop('newdesk',
                NiL,
                Nil,
                DF_ALLOWOTHERACCOUNTHOOK,
                GENERIC_ALL,     
                NiL);
  ZeroMemory(@si, sizeof(si));
  si.cb := sizeof(si);
  si.lpDesktop := pchar('newdesk');
  ZeroMemory(@pi, sizeof(pi));
  if CreateProcess(NiL,
    'calc.exe',                                       
    NiL,
    NiL,
    FALSE,                     
    0,                     
    NiL,
    NiL,
    si,
    pi ) then begin
    caclpid :=pi.dwThreadId;
  end else begin
    MessageBox(0,'运行计算器失败','Error',0);
    ExitProcess(1);
  end;

end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  SwitchDesktop(GetThreadDesktop(caclpid));
end;

end.

按一下计算机主机开关 就回到了 原始桌面 原理不清楚
2009-2-23 15:20
0
雪    币: 212
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
7楼的代码有时好使  有时 又不管用
why?
2009-2-23 16:17
0
雪    币: 5286
活跃值: (3679)
能力值: ( LV13,RANK:283 )
在线值:
发帖
回帖
粉丝
9
handle leak
2009-2-23 16:19
0
雪    币: 251
活跃值: (25)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
10
想在新创建的桌面上看到“我的电脑”和开始菜单,任务栏等必须要创建"explorer"进程
2009-2-23 21:45
0
雪    币: 208
活跃值: (18)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
谢谢共享,研究研究一下
2009-2-24 00:02
0
雪    币: 190
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
把所有explorer结束掉 再创建一个就返回原来桌面了
不过楼主应该设置一快接见
2009-2-24 01:03
0
雪    币: 125
活跃值: (35)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
多谢分享....
2009-2-24 12:25
0
雪    币: 200
活跃值: (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
呵呵,有意思,真快!
2009-2-24 13:17
0
雪    币: 39
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
sysinternals.com有个工具和楼主的应该一样叫Desktops http://technet.microsoft.com/en-us/sysinternals/cc817881.aspx
2009-2-24 13:26
0
雪    币: 212
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
var
  Form1: TForm1;
  hvirtualdesk:thandle;
  si:STARTUPINFO;
  pi:PROCESS_INFORMATION ;
  caclpid:dword;
  hproc:boolean;
implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  hvirtualDesk:=CreateDesktop('newdesk',
                NiL,
                Nil,
                DF_ALLOWOTHERACCOUNTHOOK,
                GENERIC_ALL,
                NiL);
  if hvirtualdesk=0 then begin
    messagebox(0,'创建桌面失败','error',0);
    exit;
  end;
  ZeroMemory(@si, sizeof(si));
  si.cb := sizeof(si);
  si.lpDesktop := pchar('newdesk');
  ZeroMemory(@pi, sizeof(pi));
  hproc:=CreateProcess(NiL,'explorer.exe',NiL,NiL,FALSE,0,
                   NiL,NiL,si,pi);
  if hproc=true then  begin
    caclpid :=pi.dwThreadId; // 这里取 DWTHREADID 怎么会是无效的参数呢 ?
  end else begin
    MessageBox(0,'打开进程失败','Error',0);
    ExitProcess(1);
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  hd:dword;
begin
  hd:=GetThreadDesktop(caclpid);
  showmessage(inttostr(getlasterror()));
  //这里会返回87 号错误  经查 是 无效的参数 错误
  SwitchDesktop(hd);
end;
2009-2-24 13:46
0
雪    币: 27
活跃值: (127)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
17
看到Delphi我就昏...
2009-2-25 16:17
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
学习了 谢谢
2009-2-25 23:11
0
雪    币: 206
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
下载Desktops和LZ的东西研究一下
2009-2-25 23:20
0
雪    币: 217
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
为什么在新建的桌面下创建explorer.exe无效呢?
创建其它进程是可有建的。
是不是winXP sp2的explorer.exe有问题,即不能同时运行两个实例?
2009-2-26 11:00
0
雪    币: 103
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
虚拟桌面可以用来隐藏一些信息啊,不错
2009-2-26 17:27
0
雪    币: 251
活跃值: (25)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
22
创建explorer新线程时STARTUPINFO 的lpDesktop 必须是新桌面的名字
2009-2-27 09:45
0
雪    币: 217
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
谢谢了。
现在又出现一个问题,当我不再使用虚拟桌面时,如何删除新建的虚拟桌面?虽然说用closedesktop函数,但是这个函数的作用只是类似closehandle而已,虚拟的桌面并没有被删除,用了closedesktop任务管理器中还是有两个explorer.exe。也就是说新建的虚拟桌面还是存在,就算是把新桌面下的explorer.exe结束掉,虚拟桌面仍然存在,用opendesktop还能打开。要怎样彻底删除新建的桌面,删除后让opendesktop都不能再打开?
2009-2-27 10:04
0
雪    币: 217
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
还有一个问题就是在新建的桌面下按ctrl+alt+delete这三个键调不出任务管理器了,在新桌面上建winlogon.exe进程也不行。
2009-2-27 11:24
0
雪    币: 225
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
[QUOTE=;]...[/QUOTE]
好东西,支持!一直想学习虚拟桌面!今天才看到源码!
2009-4-4 08:58
0
游客
登录 | 注册 方可回帖
返回
//