分享
 
 
 

后台调用外部程序的完美实现(Delphi)

王朝delphi·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

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

说到这里,相信你也有了对该功能的一些初步设想了,没错,其基本思路就是:

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;

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

有任何改进意见,或交流,可以Mail至:tonyki[at]citiz.net

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有