Delphi创建虚拟桌面实现后台调用外部程序

核心提示:最近在做的一个软件,其中有一部分功能需要调用其它的软件来完成,而那个软件只有可执行文件,根本没有源代码,幸好,我要做的事不难,只需要在我的程序启动后,将那个软件打开,在需要的时候,对其中的一个文本框设置一些文字,再点击一个按钮就可以了。...

最近在做的一个软件,其中有一部分功能需要调用其它的软件来完成,而那个软件只有可执行文件,根本没有源代码,幸好,我要做的事不难,只需要在我的程序启动后,将那个软件打开,在需要的时候,对其中的一个文本框设置一些文字,再点击一个按钮就可以了。

说到这里,相信你也有了对该功能的一些初步设想了,没错,其基本思路就是:
1)调用CreateProcess()打开目标程序。
2)用FindWindow()找到目标程序的窗口Handle。
3)找到文本框的Handle,以及按钮的MessageID,用SendMessage()方法设置文字,并触发事件。

好了,这样确实很简单吧,但是当我实现它后,却发现这样做的结果则是:当我的程序启动并打开目标程序时,它的Splash窗口,以及主窗口都将显示出来,即使当我用FindWindow()找到主窗口Handle后,调用SendMessage(WindowHandle, 
SW_HIDE)来隐藏该窗口,还是会有一瞬主窗口被显示出来的,这样的效果实在是追求完美的我不忍心看到的。

那么怎么解决这个问题呢,首先我当然在CreateProcess()上面寻找方法,可惜,它只有一个参数可以设置窗口的默认显示方式,但是一旦这个窗口自己重设了显示方式,它就没有任何作用了。。。。继续查找文档,这时我看到CreateProcess()的一个参数TStartupInfo中有 
lpDesktop这么一个属性,按照MSDN的说法,如果该指针为NULL,那么新建的Process将在当前Desktop上启动,而如果对其赋了一个Desktop的名称后,Process将在指定的Desktop上启动,恩,看来不错,就从它入手了:

1)首先,建立一个虚拟的Desktop,
const
DesktopName = 'MYDESK';

FDesktop:=CreateDesktop(DesktopName,nil,nil,0,GENERIC_ALL,nil);
Windows中可以建立多个Desktop,可以使用SwitchDesktop()来切换哪个Desktop被显示出来,以前有过将Windows模拟成Linux的形式,可以在多个虚拟Desktop中切换的程序,其实那种程序也是用的Windows本身的虚拟Desktop功能来实现的,另外 
Windows的启动画面,以及屏保画面也都是用虚拟Desktop实现的,好了,关于这方面不多介绍了,感兴趣的话,可以到MSDN中查看更详细资料:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/enumdesktops.asp

2)在CreateProcess的时候,指定程序在我新生成的Desktop上运行:


3)用FindWindow去找程序的主窗口
开始我直接写下了这样的代码:

但是,实践证明,这样是找不到不在当前Desktop中的Window的,那怎么办呢:
答案是,可以用SetThreadDesktop()函数,这个函数可以设置当前Thread工作所在的Desktop,于是我在以上代码前又加了一句:

if not SetThreadDesktop(FDesktop) then begin
exit;
end;

但是,程序运行后,该函数却返回了false,说明方法调用失败了,再仔细看MSDN,发现有这么一句话:

The SetThreadDesktopfunction will fail if the calling thread has any windows or hooks on its current desktop (unless the hDesktopparameter is a handle to the current desktop).

哦,原来需要切换Desktop的线程中不能有任何UI方面的东西,而我是在程序的主线程中调用该方法的,当然会失败拉,知道了这点就好办了,我只需要用一个“干净”的线程,让它绑定到新的Desktop上,再让它用FindWindow()方法找到我要找的WindowHandle,不就可以了吗,于是,这一步就需要借助一个线程了,线程的代码如下:

而主程序中的代码变成这样:

呵呵,成功,这样果然可以顺利的找到窗口Handle了。
4)最后,再用这个主窗口Handle,找出里面的EditBox的Handle,如这样:
FEditWindow:=FindWindowEx(FMainWindowHandle,0,PChar('Edit'),nil);
我在这里指定了这个文本框的ClassName,这个名称可以用Spy++得到。

初始化的工作就到此结束了,如果顺利,程序就真正在后台被运行了起来。那么功能调用呢,还是和一般的做法一样:

if (FMainWindowHandle=0) or (FEditWindow=0) then begin
exit;
end;
SendMessage(FEditWindow,WM_SETTEXT,0,LongInt(@AText[1]));
SendMessage(FMainWindowHandle,WM_COMMAND,$8012,$0);

其中$8012这个数字,也是用Spy++来得到的资源ID。
最后,别忘了关闭程序,以及释放虚拟Desktop:

好了,这样就几乎完美的实现了一个后台调用程序的功能,它对最终客户来说将是完全透明的,客户根本感觉不到后台还有另一个程序在工作。是不是很爽啊,这样别人的很多程序我们都可以直接拿来用了(当然了,得在遵守版权的基础上才行拉)。

[Delphi] 虚拟桌面

虚拟桌面delphi全攻略

网上也有很多关于虚拟桌面的技术文章,但我却没有找到一个讲解得比较详细的,花了两天时间凑合网上的零碎片断做出个虚拟桌面的小程序出来,让高手们见笑了,现在我将我制作这个程序的全部流程详细地贴出来供大家参考:
虚拟桌面说白了,核心就是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的参数用来获取当前桌面句柄。这样就得到了桌面的名称
不多说了附上我的源代码:

虛擬桌面的創建與切換

浅析桌面精灵的实现

1. 软件的开发目的

想必大家对桌面精灵很熟悉吧,想不想自己编一个?笔者非常想编一个,其目的居然是为了取得美眉的喜欢,由此引出了我开发本软件的目的。如果读者有我同样的需求,那么请继续看下去,我将和你共同探讨这个问题。注意以下示例代码均用DELPHI描述。

2. 实现原理

其实桌面精灵的原理很简单,主要分以下几步:

1.获取桌面窗口的HDC。

API 定义如下:

GetDC函数用于获取指定窗口的图形设备描述表

HDC GetDC(

HWND hWnd // 窗口句柄

);

例如:

DeskTopDC:HDC;//定义桌面窗口的图形设备描述表句柄

DeskTopDC:=GetDC(0);

或者DeskTopDC:=GetDC(GetDesktopWindow());

2.创建一个内存位图,把桌面中将要绘图的区域,保存到内存位图中去,以便绘图完成时恢复桌面。为此我定义了一个函数:

3.将动画对象透明地拷贝到桌面的绘图区域,笔者用了一个GDIAPI函数方便地实现了此功能。

定义如下:

注意:

Windows NT: 需要5.0或以上版本

Windows: 需要 Windows 98 或 以上版本

其它低版本不支持。

此函数包含在msimg32.dll.

笔者定义了一个tranbit函数来动态调用TransparentBlt函数,具体定义见第三节。

4.将第二步生成的内存位图拷贝到桌面。这样一帧动画就显示完成。不断循环1-4步,你就能看到连续的动画场景了。

3.具体代码

以下是一个演示程序,在DELPHI5.0+WINDOWS2000P中调试通过。创建一个窗体Form1,放上两个Image控件,命名为Image1,Image2,再放上一个Timer控件,命名为Timer1。准备两张位图,一张放入Image1,另一张放入Image2。笔者用了如下样式的位图(截取了一部分),你可以自己画动画对象,也可以借用别人的,笔者就是用微软画的图片。

从图片你可以看出,图片中包括了许多连续的动画帧,一张图片完成一个动作,如旋转一周等,每帧动画大小完全一样,除了动画对象其它像素用一种透明色填充。好了你可以看具体的代码了。

刷新Windows桌面

在使用计算机的过程中,经常会碰到在Windows桌面上残留有程序运行后的留下一些痕迹,这时我们往往在Windows桌面上单击鼠标右键,然后选择刷新,使桌面变得干净、整洁。其实自己编代码来实现这个功能也很简单,调用一个函数SHChangeNotify即可。首先,在Delphi的单元文件的Uses部分手动添加上ShlObj,然后在按钮点击事件中加上代码如下:
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NiL, NiL);

Delphi 虚拟桌面的更多相关文章

  1. Windows 10 虚拟桌面切换

    从Windows 10开始,终于有了和Mac一样的虚拟桌面了.但总感觉用着非常的别扭.在Mac中,切换虚拟桌面的操作可谓方便至极:除了触控板和Magic Mouse原生的支持外,通过罗技M557/55 ...

  2. 键盘键与虚拟键码对照表+delphi虚拟键码对应关键

    键盘键与虚拟键码对照表 字母和数字键 数字小键盘的键 功能键 其它键 键 键码 键 键码 键 键码 键 键码 A 65 0 96 F1 112 Backspace 8 B 66 1 97 F2 113 ...

  3. virgo虚拟桌面

    转载: http://www.appinn.com/virgo-virtual-desktop-for-windows/ virgo 是一款 Windows 下的极简虚拟桌面,源程序自身只有 7KB, ...

  4. [IT新应用]如何部署CITRIX 虚拟桌面

    1.搭建AD,并部署dhcp. 2.安装ddc服务器,加入域.这台服务器就是用来发布后端服务器或者WIN7的PC给用户使用.相当于调度. 3.安装windows server或者win7,用于发布给用 ...

  5. [项目机会]citrix 虚拟桌面对于java等高CPU占用率如何解决

    citrix 虚拟桌面对于java等高CPU占用率如何解决 问题1:java等客户端对于虚拟桌面cpu影响较大,但是有些用户的确需要使用java支持的程序,是否可以通过其他途径来解决? 问题2:对于其 ...

  6. 虚拟桌面基础架构(VDI)与终端服务和传统PC对比

    VDI(Virtual Desktop Infrastructure),即虚拟桌面基础架构,正迅速成为一个热门词汇,它将颠覆企业向终端用户交付应用的游戏规则.这篇专题就是想通过VDI与两种传统技术的对 ...

  7. 从零开始部署小型企业级虚拟桌面 -- Vmware Horizon View 6 For Linux VDI -- 结构规划

    环境说明 注,本套环境所用机器全部是64位的. 管理服务器载体:安装win7操作系统,通过VMware Workstation安装4台虚拟机,用作vCenter,Connection Server,D ...

  8. VMware vSphere 服务器虚拟化之二十二桌面虚拟化之创建View Composer链接克隆的虚拟桌面池

    VMware vSphere 服务器虚拟化之二十二桌面虚拟化之创建View Composer链接克隆的虚拟桌面池 在上一节我们创建了完整克隆的自动专有桌面池,在创建过程比较缓慢,这次我们将学习创建Vi ...

  9. virtenv 0.8.6 发布,虚拟桌面配置工具 - 开源中国社区

    virtenv 0.8.6 发布,虚拟桌面配置工具 - 开源中国社区 virtenv 0.8.6 发布,virtenv 是一个用 QT4 开发的应用,用来配置和启动基于 LXC 的虚拟桌面环境.该容器 ...

随机推荐

  1. 初学Linux基本的命令操作应当记牢

    Linux管理文件和目录的命令 命令 功能 命令 功能 pwd 显示当前目录 ls 查看目录下的内容 cd 改变所在目录 cat 显示文件的内容 grep 在文件中查找某字符 cp 复制文件 touc ...

  2. eclipse开发安卓 发短信打电话发送邮件功能

    1.在mainfiest中添加   //添加拨打电话的功能    <uses-permission android:name="android.permission.CALL_PHON ...

  3. express上传图片

    var express = require('express') var app = express() var proxy = require('http-proxy-middleware') co ...

  4. python运行时禁止生成pyc文件

    方法 在环境变量文件~/.bashrc中添加 export PYTHONDONTWRITEBYTECODE=False source ~/.bashrc加载即可 如何从项目中删除所有.pyc文件 fi ...

  5. Win10 打开MSDTC

    标签:log   com   http   it   sp   src   c   bs   io 1,Win+R 打开运行窗口,输入 dcomcnfg,打开组件服务窗口 2,在组件服务 catalo ...

  6. 2019杭电多校第六场hdu6638 Snowy Smile(线段树+枚举)

    Snowy Smile 题目传送门 解题思路 先把y离散化,然后把点按照x的大小进行排序,我们枚举每一种x作为上边界,然后再枚举其对应的每一种下边界.按照这种顺序插入点,这是一个压维的操作,即在线段树 ...

  7. linux替换rm命令,防止误删

    1. 在/home/username/ 目录下新建一个目录,命名为:.trash 2.. 在/home/username/tools/目录下,新建一个shell文件,命名为: remove.sh #! ...

  8. Gradle教程

    Ant和Maven共享在Java市场上相当大的成功.ANT是在2000年发布了第一个版本的工具,它是基于程序编程思想的发展. 后来,人们在 Apache-Ivy的帮助下,网络接受插件和依赖管理的能力有 ...

  9. java 重新学习 (四)

    一.内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类中的成员之间可以相互访问.但外部类不能访问内部类的实现细节,例如内部类的成员变量.匿名内部类适合用于创建仅需要一次使用 ...

  10. 使用yarn搭建vue项目

    今天尝试了一下用yarn的方式搭建vue项目,方法其实是和npm的用法一样.但是在创建过程中报错了.现在整理一下,便于后期查错时使用. 以windows系统为例 1.全局安装yarn,三种方式 官网上 ...