网上也有很多关于虚拟桌面的技术文章,但我却没有找到一个讲解得比较详细的,花了两天时间凑合网上的零碎片断做出个虚拟桌面的小程序出来,让高手们见笑了,现在我将我制作这个程序的全部流程详细地贴出来供大家参考:
虚拟桌面说白了,核心就是CreateDesktop和SwitchDesktop函数,这是别人的说法,但是要想做出一个完整的软件来还需要其它很多必不可少的函数,这里我先介绍下要用到的函数(参数我就不解释了,因为我自已现在不对每个个参数的具体含义的认识都比较模糊,很多参数是照抄网上的):
CreateDesktop//创建虚拟桌面
OpenDesktop//获取桌面句柄
SwitchDesktop//激活/转到指定的桌面
globaladdatom//创建全局原子
globalfindatom//查找全局原子
globaldeleteatom//删除全局原子
getasynckeystate//判断虚拟键的状态
CreateProcess//打开指定的进程
GetCurrentThreadId//获取当前ID
GetThreadDesktop//获取当前桌面句柄
好了就这些,下面我说下流程
刚看网上的资料的时候,感觉好像只要CreateDesktop来创建桌面再用SwitchDesktop来转到指定桌面即可,然而事情却没这么简单.创建虚拟桌面后,我将SwitchDesktop函数输入进程序的按钮事件里,我一按按钮,令我痛哭流涕的事情发生了------转到的桌面干干净净什么都没有,没有桌面图标没有任务栏甚至打不开任务管理器,没办法,含泪按下电脑的重启按钮(好几千呢555).
我继续在网上查找资料,发现要在新创建的桌面显示图标,必须先在新创建的桌面打开桌面进程explorer.exe(一般情问下我们结束掉这个进程桌面就会消失就是这个道理),现在就用到了CreateProcess函数,CreateProcess函数如何能在其它桌面创建进程呢?CreateProcess有个TStartupInfo结构的参数,该结构中有个叫lpDesktop的成员,它指定了在哪个桌面创建进程(不对其赋值则为当前桌面),请看代码:
var sin:TStartupInfo;s:string;
sin.cb:=sizeof(sin);
sin.wShowWindow:=SW_SHOW;
sin.dwFlags:=STARTF_USESHOWWINDOW;
s:='a';
sin.lpDesktop:=pchar(s);
CreateProcess('c:/WINDOWS/explorer.exe',nil,nil,nil,False,0,nil,nil,sin,pin);
其中的字符a为创建桌面的名称,该名称在创建桌面的函数中指定.
接下来的问题是转到新的桌面之后,运行的自身程序就不见了,怎样才能转回原来的桌面呢?我用了一个愚蠢的办法,在创建新桌面和新桌面进程的同时在新桌面上将本程序再次打开,这样实际上创建了几个新桌面自身程序就运行了几个实例,看代码:
CreateProcess(pchar(extractfilepath(application.ExeName)+'这里是程序的名字'),nil,nil,nil,False,0,nil,nil,sin,pin);
将这行代码放到创建桌面进程代码的后面,这个问题就解决了,一波未平一波又起新了问题又产生了,而且还是两个很严重的问题:
1。我创建桌面的代码是写在窗体载入的过程中的,这就意味着每创建一个自身实例就多了N个虚拟桌面。
2。新创建的实例进程并不能保存有第一个实例进程的句柄,这样就无法对桌面进行操作。
对于第一个问题我是用全局原子法来解决的,在程序启动时先使用globalfindatom查找全局原子,若不荐在则表示未运行过实例进程,那么就创建一个全局原子并创建虚拟桌面,若存在,而不创建虚拟桌面,但还是要创建一个全局原子(原因不用我说了吧~),在窗体的退出代码中别忘了globaldeleteatom掉创建的全局原子。
对于第二个问题,即然无法继承就自已查找句柄吧;前面说过在创建虚拟桌面时会为创建的桌面指定一个名称,现在这个名称的作用体现出来了,OpenDesktop函数其中一个参数为桌面的名称,该函数的返回值就是桌面的句柄。新创建的虚拟桌面可以用这个方法获得句柄,但是默认桌面怎么获取呢?这个更简单,因为默认的桌面名称就是“default”,只要把这个字符放入OpenDesktop函数中便能轻松获取默认桌面的句柄。在句柄得到了,只要使用SwitchDesktop(参数就是桌面句柄)函数就能转到指定桌面了。
PS:默认桌面的名称我开始也不知道,但我无意中在网上发现一个函数:getuserobjectinformation,这个函数可以根据桌面的句柄获取桌面的名称,当前桌面的句柄可以用GetThreadDesktop和GetCurrentThreadId()获取,GetCurrentThreadId()的功能是获得当前ID,这个ID作为GetThreadDesktop的参数用来获取当前桌面句柄。这样就得到了桌面的名称
不多说了附上我的源代码:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button4: TButton;
Button5: TButton;
Button6: TButton;
Button7: TButton;
Timer1: TTimer;
Timer2: TTimer;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
procedure Button7Click(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Timer2Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
h1:thandle;
h2:thandle;
h3:thandle;
h4:thandle;
h5:thandle;
h6:thandle;
si:TStartupInfo;
sin:TStartupInfo;
pin:TProcessInformation;
s:string;
desk:thandle;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject); //创建桌面
begin
timer1.Interval:=50;
timer2.Interval:=1000;
sin.cb:=sizeof(sin);
sin.wShowWindow:=SW_SHOW;
sin.dwFlags:=STARTF_USESHOWWINDOW;
if globalfindatom('mfk')=0 then
begin
globaladdatom('mfk');
h1:=GetThreadDesktop(GetCurrentThreadId());
s:='a';
sin.lpDesktop:=pchar(s);
h2:=CreateDesktop(pchar(s),nil,nil,DF_ALLOWOTHERACCOUNTHOOK,GENERIC_ALL,nil);
CreateProcess('c:/WINDOWS/explorer.exe',nil,nil,nil,False,0,nil,nil,sin,pin);
CreateProcess(pchar(extractfilepath(application.ExeName)+'双开工具.exe'),nil,nil,nil,False,0,nil,nil,sin,pin);
s:='b';
sin.lpDesktop:=pchar(s);
h3:=CreateDesktop('b',nil,nil,0,MAXIMUM_ALLOWED,nil);
CreateProcess('c:/WINDOWS/explorer.exe',nil,nil,nil,False,0,nil,nil,sin,pin);
CreateProcess(pchar(extractfilepath(application.ExeName)+'双开工具.exe'),nil,nil,nil,False,0,nil,nil,sin,pin);
s:='c';
sin.lpDesktop:=pchar(s);
h4:=CreateDesktop('c',nil,nil,0,MAXIMUM_ALLOWED,nil);
CreateProcess('c:/WINDOWS/explorer.exe',nil,nil,nil,False,0,nil,nil,sin,pin);
CreateProcess(pchar(extractfilepath(application.ExeName)+'双开工具.exe'),nil,nil,nil,False,0,nil,nil,sin,pin);
s:='d';
sin.lpDesktop:=pchar(s);
h5:=CreateDesktop('d',nil,nil,0,MAXIMUM_ALLOWED,nil);
CreateProcess('c:/WINDOWS/explorer.exe',nil,nil,nil,False,0,nil,nil,sin,pin);
CreateProcess(pchar(extractfilepath(application.ExeName)+'双开工具.exe'),nil,nil,nil,False,0,nil,nil,sin,pin);
s:='e';
sin.lpDesktop:=pchar(s);
h6:=CreateDesktop('e',nil,nil,0,MAXIMUM_ALLOWED,nil);
CreateProcess('c:/WINDOWS/explorer.exe',nil,nil,nil,False,0,nil,nil,sin,pin);
CreateProcess(pchar(extractfilepath(application.ExeName)+'双开工具.exe'),nil,nil,nil,False,0,nil,nil,sin,pin);
end
else
begin
globaladdatom('mfk');
end;
end;
procedure TForm1.Button1Click(Sender: TObject);//切换至桌面1
begin
desk:=OpenDesktop('default',DF_ALLOWOTHERACCOUNTHOOK,False,MAXIMUM_ALLOWED);
SwitchDesktop(desk);
end;
procedure TForm1.Button2Click(Sender: TObject); //切换至桌面2
begin
desk:=OpenDesktop('a',DF_ALLOWOTHERACCOUNTHOOK,False,MAXIMUM_ALLOWED);
SwitchDesktop(desk);
end;
procedure TForm1.Button4Click(Sender: TObject); //切换至桌面3
begin
desk:=OpenDesktop('b',DF_ALLOWOTHERACCOUNTHOOK,False,MAXIMUM_ALLOWED);
SwitchDesktop(desk);
end;
procedure TForm1.Button5Click(Sender: TObject); //切换至桌面4
begin
desk:=OpenDesktop('c',DF_ALLOWOTHERACCOUNTHOOK,False,MAXIMUM_ALLOWED);
SwitchDesktop(desk);
end;
procedure TForm1.Button6Click(Sender: TObject); //切换至桌面5
begin
desk:=OpenDesktop('d',DF_ALLOWOTHERACCOUNTHOOK,False,MAXIMUM_ALLOWED);
SwitchDesktop(desk);
end;
procedure TForm1.Button7Click(Sender: TObject); //切换至桌面6
begin
desk:=OpenDesktop('e',DF_ALLOWOTHERACCOUNTHOOK,False,MAXIMUM_ALLOWED);
SwitchDesktop(desk);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if (getasynckeystate(VK_Control)<>0) and (getasynckeystate(112)<>0) then
begin
desk:=OpenDesktop('default',DF_ALLOWOTHERACCOUNTHOOK,False,MAXIMUM_ALLOWED);
SwitchDesktop(desk);
end;
if (getasynckeystate(VK_Control)<>0) and (getasynckeystate(113)<>0) then
begin
desk:=OpenDesktop('a',DF_ALLOWOTHERACCOUNTHOOK,False,MAXIMUM_ALLOWED);
SwitchDesktop(desk);
end;
if (getasynckeystate(VK_Control)<>0) and (getasynckeystate(114)<>0) then
begin
desk:=OpenDesktop('b',DF_ALLOWOTHERACCOUNTHOOK,False,MAXIMUM_ALLOWED);
SwitchDesktop(desk);
end;
if (getasynckeystate(VK_Control)<>0) and (getasynckeystate(115)<>0) then
begin
desk:=OpenDesktop('c',DF_ALLOWOTHERACCOUNTHOOK,False,MAXIMUM_ALLOWED);
SwitchDesktop(desk);
end;
if (getasynckeystate(VK_Control)<>0) and (getasynckeystate(116)<>0) then
begin
desk:=OpenDesktop('d',DF_ALLOWOTHERACCOUNTHOOK,False,MAXIMUM_ALLOWED);
SwitchDesktop(desk);
end;
if (getasynckeystate(VK_Control)<>0) and (getasynckeystate(117)<>0) then
begin
desk:=OpenDesktop('e',DF_ALLOWOTHERACCOUNTHOOK,False,MAXIMUM_ALLOWED);
SwitchDesktop(desk);
end
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
globaldeleteatom(globalfindatom('mfk'));
end;
有需要源文件的可以在我的资源里下载