桌面切换






4.58/5 (43投票s)
2004 年 7 月 8 日
5分钟阅读

340950

6989
展示如何实现桌面切换的文章。
引言
由于有一台闲置的电脑,我决定尝试一下 Mandrake Linux,看看它是什么样的(我以前从未使用过 Linux)。我发现它的桌面切换功能很有趣,并开始想知道在我的 Windows 机器上是否也能实现同样的功能。在 MSDN 上快速搜索后,我发现了 Desktop API 函数,这些函数允许我实现这一点。根据文档,这项功能自 Windows 2000 Professional 起就可用了,既然我使用的是 XP,我想我会试试。
Windows 加载并登录后,会自动创建一个名为“default”的桌面。使用提供的函数,您可以创建、打开和切换桌面,但不提供删除功能(稍后详细介绍)。
使用 Desktop 类
创建桌面
创建桌面非常简单,如下面的代码示例所示。可以使用实例成员或静态成员来创建桌面。
// Instance method
Desktop desktop = new Desktop();
desktop.Create("myDesktop");
// Static method
Desktop desktop = Desktop.CreateDesktop("myDesktop");
注意:桌面名称不能包含反斜杠字符。
使用以上任一方法都将得到一个 `Desktop` 对象,并带有打开的桌面句柄(前提是桌面创建成功)。新桌面没有任何正在运行的进程——如果您切换到它,您只会看到默认的壁纸。我提供了 `Prepare` 方法,该方法将 explorer 加载到桌面上,使其可用。
切换桌面
切换桌面与创建桌面一样简单,只需调用声明桌面名称的静态方法或打开的 `Desktop` 对象的实例方法即可。
// instance method.
Desktop desktop = new Desktop();
desktop.Open("myDesktop");
desktop.Show();
// static method.
Desktop.Show("myDesktop");
在桌面上打开进程
kernel32.dll 中的 `CreateProcess` 函数接受一个 `STARTUPINFO` 类型的参数,其中有一个参数(“lpDesktop”)允许您指定进程要创建的桌面。因此,我选择导入此函数,而不是使用 .NET 框架的 `Process` 类,这意味着在创建进程时功能有所减少,但我希望在下一版本中解决此问题。现在是代码,此示例显示了创建进程的两种方法。
// instance method
Desktop desktop = Desktop.OpenDesktop("myDesktop");
Process p = desktop.CreateProcess("calc.exe");
// static method
Process p = Desktop.CreateProcess("calc.exe", "myDesktop");
`CreateProcess` 方法都返回一个 `Process` 对象,该对象可用于终止进程,或根据您的需要使用。
删除桌面
删除桌面有点棘手。删除桌面的唯一方法是终止运行在桌面上的所有进程,此时,桌面会自动删除。到目前为止,我无法获取除输入桌面之外的桌面上运行的进程列表(也不确定这是否可能),但我所做的是提供了 `GetInputProcesses` 方法,该方法将返回当前输入桌面(用户可见的桌面)上所有正在运行的进程的数组。
考虑到这一点,删除用户访问过的桌面的唯一方法是位于该桌面上,枚举进程,然后逐个终止它们。确保您的应用程序在有机会切换桌面之前不会被终止(除非您希望如此)。
Process[] processes = Desktop.GetInputProcesses();
Process thisProc = Process.GetCurrentProcess();
foreach(Process p in processes)
{
if (p.ProcessName != thisProc.ProcessName)
{
p.Kill();
}
}
设置线程的桌面
您的进程的线程可以移动到不同的桌面,前提是它们*当前桌面上没有任何钩子或窗口*。
Desktop desktop = Desktop.OpenDesktop("myDesktop");
Desktop.SetCurrent(desktop);
上面的代码示例会将调用线程移动到“myDesktop”,但如果该线程有窗口或钩子,则会失败。
幕后
`Desktop` 的桌面切换功能是通过以下 API 函数实现的(从 user32.dll 导入)
CreateDesktop
OpenDesktop
OpenInputDesktop
CloseDesktop
SwitchDesktop
SetThreadDesktop
GetThreadDesktop
创建桌面
[DllImport("user32.dll")]
private static extern IntPtr CreateDesktop(string lpszDesktop,
IntPtr lpszDevice,
IntPtr pDevmode,
int dwFlags,
long dwDesiredAccess,
IntPtr lpsa);
`CreateDesktop` 函数从 user32.dll 导入。当您尝试创建一个新桌面时调用它,只需指定桌面名称 (lpszDesktop)、标志 (dwFlags) 和所需的访问权限 (dwDesiredAccess)。
m_desktop = CreateDesktop(name, IntPtr.Zero, IntPtr.Zero, 0,
AccessRights, IntPtr.Zero);
返回值是桌面的句柄,如果发生错误则为 `IntPtr.Zero`。
切换桌面
[DllImport("user32.dll")]
private static extern bool SwitchDesktop(IntPtr hDesktop);
`SwitchDesktop` 函数从 user32.dll 导入。切换桌面时,将要成为输入桌面的桌面句柄作为唯一参数传递,返回值指示切换是否成功。
bool result = SwitchDesktop(m_desktop);
在桌面上打开进程
[DllImport("kernel32.dll")]
private static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
int dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
ref PROCESS_INFORMATION lpProcessInformation);
`CreateProcess` 函数从 kernel32.dll 导入。在桌面上创建进程时,使用此函数,并仅指定命令行 (lpCommandLine)、继承句柄 (bInheritHandles)、创建标志 (dwCreationFlags)、启动信息 (lpStartupInfo) 和进程信息 (lpProcessInformation),如以下代码所示。
// set startup parameters.
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = m_desktopName;
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
// start the process.
bool result = CreateProcess(null, path, IntPtr.Zero, IntPtr.Zero, true,
NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref si, ref pi);
指定 `PROCESS_INFORMATION` 结构允许我检索刚创建进程的进程 ID,从而可以为其创建托管的 `Process` 对象。
return Process.GetProcessById(pi.dwProcessId);
设置线程的桌面
[DllImport("user32.dll")]
private static extern bool SetThreadDesktop(IntPtr hDesktop);
`SetThreadDesktop` 函数从*user32.dll*导入。调用 `SetCurrent` 将导致调用此函数。传递给 `SetCurrent` 的 Desktop 对象,如果已打开,将包含一个有效的桌面句柄,可以通过 `DesktopHandle` 属性访问,该属性被传递给 `SetThreadDesktop`。
return SetThreadDesktop(desktop.DesktopHandle);
Example usage
我没有提供带有源代码的示例应用程序,因为它非常简单明了,并且所有成员(私有成员除外)都有 XML 注释。但是,如果您想了解如何在应用程序中使用它,请访问我的网站,我在那里用它制作了一个小型的桌面切换应用程序。
历史
版本 |
注释 |
1.0 |
|
1.1 2004 年 6 月 6 日 |
|
1.2 2004 年 7 月 8 日 |
|