其基本思路就是
)調用CreateProcess()打開目標程序
)用FindWindow()找到目標程序的窗口Handle
)找到文本框的Handle
以及按鈕的MessageID
用SendMessage()方法設置文字
並觸發事件
好了
這樣確實很簡單吧
但是當我實現它後
卻發現這樣做的結果則是
當我的程序啟動並打開目標程序時
它的Splash窗口
以及主窗口都將顯示出來
即使當我用FindWindow()找到主窗口Handle後
調用SendMessage(WindowHandle
SW_HIDE)來隱藏該窗口
還是會有一瞬主窗口被顯示出來的
這樣的效果實在是最求完美的我不忍心看到的
那麼怎麼解決這個問題呢
首先我當然在CreateProcess()上面尋找方法
可惜
它只有一個參數可以設置窗口的默認顯示方式
但是一旦這個窗口自己重設了顯示方式
它就沒有任何作用了
繼續查找文檔
這時我看到CreateProcess()的一個參數TStartupInfo中有 lpDesktop這麼一個屬性
按照MSDN的說法
如果該指針為NULL
那麼新建的Process將在當前Desktop上啟動
而如果對其賦了一個Desktop的名稱後
Process將在指定的Desktop上啟動
恩
看來不錯
就從它入手了
)首先
建立一個虛擬的Desktop
const
DesktopName =
MYDESK
;
FDesktop:=CreateDesktop(DesktopName
nil
nil
GENERIC_ALL
nil);
Windows中可以建立多個Desktop
可以使用SwitchDesktop()來切換哪個Desktop被顯示出來
以前有過將Windows模擬成Linux的形式
可以在多個虛擬Desktop中切換的程序
其實那種程序也是用的Windows本身的虛擬Desktop功能來實現的
另外 Windows的啟動畫面
以及屏保畫面也都是用虛擬Desktop實現的
好了
關於這方面不多介紹了
感興趣的話
可以到MSDN中查看更詳細資料
sp
)在CreateProcess的時候
指定程序在我新生成的Desktop上運行
var
StartInfo:TStartupInfo;
FillChar(StartInfo
sizeof(StartInfo)
);
StartInfo
cb:=sizeof(StartInfo);
StartInfo
lpDesktop:=PChar(DesktopName); //指定Desktop的名稱即可
StartInfo
wShowWindow:=SW_HIDE;
StartInfo
dwFlags:=STARTF_USESHOWWINDOW;
StartInfo
hStdError:=
;
StartInfo
hStdInput:=
;
StartInfo
hStdOutput:=
;
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 (
)
PChar(Application
Title)
MB_ICONWARNING);
exit;
end;
)用FindWindow去找程序的主窗口
開始我直接寫下了這樣的代碼
for I:=
to
do begin //wait
seconds for open the main window
WindowHandle:=FindWindow(nil
WindowCaption
);
if WindowHandle<>
then begin
break;
end;
Sleep(
);
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:=
to
do begin //wait
seconds for open the main window
FWindowHandle:=FindWindow(nil
PChar(
WindowCaption
));
if FWindowHandle<>
then begin
break;
end;
Sleep(
);
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=
then begin
MessageBox(Application
Handle
Error when init voice (
)
PChar(Application
Title)
MB_ICONWARNING);
exit;
end;
呵呵
成功
這樣果然可以順利的找到窗口Handle了
)最後
再用這個主窗口Handle
找出裡面的EditBox的Handle
如這樣
FEditWindow:=FindWindowEx(FMainWindowHandle
PChar(
Edit
)
nil);
我在這裡指定了這個文本框的ClassName
這個名稱可以用Spy++得到
初始化的工作就到此結束了
如果順利
程序就真正在後台被運行了起來
那麼功能調用呢
還是和一般的做法一樣
if (FMainWindowHandle=
) or (FEditWindow=
) then begin
exit;
end;
SendMessage(FEditWindow
WM_SETTEXT
LongInt(@AText[
]));
SendMessage(FMainWindowHandle
WM_COMMAND
$
$
);
其中$
這個數字
也是用Spy++來得到的資源ID
最後
別忘了關閉程序
以及釋放虛擬Desktop
if FProceInfo
hProcess<>
then begin
TerminateProcess(FProceInfo
hProcess
);
end;
if FDesktop<>
then begin
CloseDesktop(FDesktop);
end;
From:http://tw.wingwit.com/Article/program/Delphi/201311/8390.html