后台调用外部程序的完美实现(使用CreateDesktop建立隐藏桌面)
最近在做的一个软件,其中有一部分功能需要调用其它的软件来完成,而那个软件只有可执行文件,根本没有源代码,幸好,我要做的事不难,只需要在我的程序启动后,将那个软件打开,在需要的时候,对其中的一个文本矿设置一些文字,再点击一个按钮就可以了。
说到这里,相信你也有了对该功能的一些初步设想了,没错,其基本思路就是:
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上运行:
- var
- StartInfo:TStartupInfo;
- FillChar(StartInfo, sizeof(StartInfo), 0);
- StartInfo.cb:=sizeof(StartInfo);
- StartInfo.lpDesktop:=PChar(DesktopName); //指定Desktop的名称即可
- StartInfo.wShowWindow:=SW_HIDE;
- StartInfo.dwFlags:=STARTF_USESHOWWINDOW;
- StartInfo.hStdError:=0;
- StartInfo.hStdInput:=0;
- StartInfo.hStdOutput:=0;
- if not CreateProcess(PChar(FileName),nil,nil,nil,true,CREATE_NEW_CONSOLE+HIGH_PRIORITY_CLASS,nil,PChar(ExtractFilePath(FilePath)),StartInfo,FProceInfo) then begin
- MessageBox(Application.Handle,'Error when init voice (5).',PChar(Application.Title),MB_ICONWARNING);
- exit;
- end;
3)用FindWindow去找程序的主窗口
开始我直接写下了这样的代码:
- for I:=0 to 60 do begin //wait 30 seconds for open the main window
- WindowHandle:=FindWindow(nil,'WindowCaption');
- if WindowHandle<>0 then begin
- break;
- end;
- Sleep(500);
- end;
但是,实践证明,这样是找不到不在当前Desktop中的Window的,那怎么办呢:
答案是,可以用SetThreadDesktop()函数,这个函数可以设置当前Thread工作所在的Desktop,于是我在以上代码前又加了一句:
- if not SetThreadDesktop(FDesktop) then begin
- exit;
- end;
但是,程序运行后,该函数却返回了false,说明方法调用失败了,再仔细看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).
哦,原来需要切换Desktop的线程中不能有任何UI方面的东西,而我是在程序的主线程中调用该方法的,当然会失败拉,知道了这点就好办了,我只需要用一个“干净”的线程,让它绑定到新的Desktop上,再让它用FindWindow()方法找到我要找的WindowHandle,不就可以了吗,于是,这一步就需要借助一个线程了,线程的代码如下:
- TFindWindowThread = class(TThread)
- private
- FDesktop:THandle;
- FWindowHandle:THandle;
- protected
- procedure Execute();override;
- public
- constructor Create(ACreateSuspended:Boolean;const ADesktop:THandle);reintroduce;
- property WindowHandle:THandle read FWindowHandle;
- end;
- { TFindWindowThread }
- procedure TFindWindowThread.Execute();
- var
- I:Integer;
- begin
- //make the current thread find window on the new desktop!
- if not SetThreadDesktop(FDesktop) then begin
- exit;
- end;
- for I:=0 to 60 do begin //wait 30 seconds for open the main window
- FWindowHandle:=FindWindow(nil,PChar('WindowCaption'));
- if FWindowHandle<>0 then begin
- break;
- end;
- Sleep(500);
- end;
- end;
- constructor TFindWindowThread.Create(ACreateSuspended:Boolean;const ADesktop:THandle);
- begin
- inherited Create(ACreateSuspended);
- FDesktop:=ADesktop;
- end;
而主程序中的代码变成这样:
- FindWindowThread:=TFindWindowThread.Create(false,FDesktop);
- try
- FindWindowThread.WaitFor;
- FMainWindowHandle:=FindWindowThread.WindowHandle;
- finally
- FindWindowThread.Free;
- end;
- if FMainWindowHandle=0 then begin
- MessageBox(Application.Handle,'Error when init voice (6).',PChar(Application.Title),MB_ICONWARNING);
- exit;
- end;
呵呵,成功,这样果然可以顺利的找到窗口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:
- if FProceInfo.hProcess<>0 then begin
- TerminateProcess(FProceInfo.hProcess,0);
- end;
- if FDesktop<>0 then begin
- CloseDesktop(FDesktop);
- end;
好了,这样就几乎完美的实现了一个后台调用程序的功能,它对最终客户来说将是完全透明的,客户根本感觉不到后台还有另一个程序在工作。是不是很爽啊,这样别人的很多程序我们都可以直接拿来用了(当然了,得在遵守版权的基础上才行拉)。
http://peirenlei.iteye.com/blog/424464
后台调用外部程序的完美实现(使用CreateDesktop建立隐藏桌面)的更多相关文章
- MySql UDF 调用外部程序和系统命令
1.mysql利用mysqludf的一个mysql插件可以实现调用外部程序和系统命令 下载lib_mysqludf_sys程序:https://github.com/mysqludf/lib_mysq ...
- 获取ip ,百度地图坐标点 和 在 后台调用 url()
protected void getip() { string ips = HttpContext.Current.Request.UserHostA ...
- 由ASP.NET所谓前台调用后台、后台调用前台想到HTTP——实践篇(二)
在由ASP.NET所谓前台调用后台.后台调用前台想到HTTP——理论篇中描述了一下ASP.NET新手的三个问题及相关的HTTP协议内容,在由ASP.NET所谓前台调用后台.后台调用前台想到HTTP—— ...
- 由ASP.NET所谓前台调用后台、后台调用前台想到HTTP——理论篇
工作两年多了,我会经常尝试给公司小伙伴儿们解决一些问题,几个月下来我发现初入公司的小朋友最爱问的问题就三个 1. 我想前台调用后台的XXX方法怎么弄啊? 2. 我想后台调用前台的XXX JavaScr ...
- WebService – 3.后台调用WebService,根级别上的数据无效
1.因为我的webservice返回的是json, 2.ajax传递跨域不安全, 3.contentType: "application/json; charset=utf-8", ...
- C#winform调用外部程序,等待外部程序执行完毕才执行下面代码
1.简单调用外部程序文件(exe文件,批处理等),只需下面一行代码即可 System.Diagnostics.Process.Start(“应用程序文件全路径”); 2.如果要等待调用外部程序执行完毕 ...
- Delphi 调用外部程序并等待其运行结束
转自:http://blog.csdn.net/xieyunc/article/details/4140620 如何让Delphi调用外部程序并等待其运行结束 1. uses Window ...
- QProcess调用外部程序方式的差异
众所周知QProcess类的作用是启动一个外部的程序并与之交互它有三种方式调用外部程序: 1. execute 2. start 3. startDetached 从调用上看: execute是阻塞调 ...
- js前台与后台数据交互-后台调前台(后台调用、注册客户端脚本)
转自:http://blog.csdn.net/wang379275614/article/details/17049721 客户端脚本一般都在前台,这里讲的是(1)在后台调用前台定义的脚本(2)在后 ...
随机推荐
- 多线程之Future模式
详细参见葛一名老师的<Java程序性能优化> Futrue模式:对于多线程,如果线程A要等待线程B的结果,那么线程A没必要等待B,直到B有结果,可以先拿到一个未来的Future,等B有结果 ...
- CodeForces 519B A and B and Compilation Errors【模拟】
题目意思还是蛮简单的,看 输入数据输出数据还是比较明显的 我用排序来写还是可以AC的 //#pragma comment(linker, "/STACK:16777216") // ...
- Maven创建项目: Failed to execute goal org.apache.maven.plugin( mvn archetype:create)
一.概述: 在使用mvn 命令mvn archetype:create -DgroupId=com.chuanliu.c11 -DartifactId=c11searcher在控制创建maven项目和 ...
- SolrCloud Hello Word
Solr Cloud 设计出来的目的是使你的搜索服务具有更高的可用性,提高容错.容灾能力.下面我们在一台电脑上建立2个solr服务,作为一个solrCloud分片(shard),初步认识一下solrC ...
- poi操作officePOI操作excel中的数据格式(日期类型)
7.3.3 POI中Excel文件Cell的类型 在读取每一个Cell的值的时候,通过getCellType方法获得当前Cell的类型,在Excel中Cell有6种类型,如表7-3所示. 表7-3 C ...
- HapiJS开发手冊
HapiJS开发手冊 作者:chszs.转载需注明.博客主页:http://blog.csdn.net/chszs 一.HapiJS介绍 HapiJS是一个开源的.基于Node.js的应用框架,它适用 ...
- iOS WebCore的WebEvent和EventHandler
WebEvent是iOS专有的类,负责封装和携带从UIKit得到的系统事件信息,并由WebKit层的WAKResponder子类传递到WebCore的EventHandler. UIKit层的逻辑可参 ...
- 一款简单的客户端安卓手机qq源码
给大家分享一款比较简单的安卓手机qq应用源码,效果非常不错,大家可以借鉴一下,希望大家会喜欢. 1.png (7.24 KB, 下载次数: 0) 02.png (68.52 KB, 下载次数: 0 ...
- BZOJ 2510: 弱题( 矩阵快速幂 )
每进行一次, 编号为x的数对x, 和(x+1)%N都有贡献 用矩阵快速幂, O(N3logK). 注意到是循环矩阵, 可以把矩阵乘法的复杂度降到O(N2). 所以总复杂度就是O(N2logK) --- ...
- BZOJ 2795: [Poi2012]A Horrible Poem( hash )
...字符串hash. 假如长度x是一个循环节, 那么对于任意n(x | n)也是一个循环节. 设当前询问区间[l, r]长度为len = ∏piai, 最终答案ans = ∏piai' ,我们只需枚 ...