熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Delphi編程 >> 正文

後台調用外部程序的完美實現(Delphi)

2013-11-11 21:03:59  來源: Delphi編程 
其基本思路就是
  )調用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(DesktopNamenilnilGENERIC_ALLnil);
  Windows中可以建立多個Desktop可以使用SwitchDesktop()來切換哪個Desktop被顯示出來以前有過將Windows模擬成Linux的形式可以在多個虛擬Desktop中切換的程序其實那種程序也是用的Windows本身的虛擬Desktop功能來實現的另外 Windows的啟動畫面以及屏保畫面也都是用虛擬Desktop實現的好了關於這方面不多介紹了感興趣的話可以到MSDN中查看更詳細資料
  sp
  
  )在CreateProcess的時候指定程序在我新生成的Desktop上運行
  var
  StartInfo:TStartupInfo;
  
  FillChar(StartInfo sizeof(StartInfo) );
  StartInfocb:=sizeof(StartInfo);
  StartInfolpDesktop:=PChar(DesktopName);   //指定Desktop的名稱即可
  StartInfowShowWindow:=SW_HIDE;
  StartInfodwFlags:=STARTF_USESHOWWINDOW;
  StartInfohStdError:=;
  StartInfohStdInput:=;
  StartInfohStdOutput:=;
  if not CreateProcess(PChar(FileName)nilnilniltrueCREATE_NEW_CONSOLE+HIGH_PRIORITY_CLASSnilPChar(ExtractFilePath(FilePath))StartInfoFProceInfo) then begin
  MessageBox(ApplicationHandleError when init voice ()PChar(ApplicationTitle)MB_ICONWARNING);
  exit;
  end;
  
  )用FindWindow去找程序的主窗口
  開始我直接寫下了這樣的代碼
  for I:= to do begin //wait seconds for open the main window
  WindowHandle:=FindWindow(nilWindowCaption);
  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 TFindWindowThreadExecute();
  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(nilPChar(WindowCaption));
  if FWindowHandle<> then begin
  break;
  end;
  Sleep();
  end;
  end;
  
  constructor TFindWindowThreadCreate(ACreateSuspended:Boolean;const ADesktop:THandle);
  begin
  inherited Create(ACreateSuspended);
  FDesktop:=ADesktop;
  end;
  
  而主程序中的代碼變成這樣
  FindWindowThread:=TFindWindowThreadCreate(falseFDesktop);
  try
  FindWindowThreadWaitFor;
  FMainWindowHandle:=FindWindowThreadWindowHandle;
  finally
  FindWindowThreadFree;
  end;
  if FMainWindowHandle= then begin
  MessageBox(ApplicationHandleError when init voice ()PChar(ApplicationTitle)MB_ICONWARNING);
  exit;
  end;
  
  呵呵成功這樣果然可以順利的找到窗口Handle了
  
  )最後再用這個主窗口Handle找出裡面的EditBox的Handle如這樣
  FEditWindow:=FindWindowEx(FMainWindowHandlePChar(Edit)nil);
  我在這裡指定了這個文本框的ClassName這個名稱可以用Spy++得到
  
  
  初始化的工作就到此結束了如果順利程序就真正在後台被運行了起來那麼功能調用呢還是和一般的做法一樣
  
  if (FMainWindowHandle=) or (FEditWindow=) then begin
  exit;
  end;
  SendMessage(FEditWindowWM_SETTEXTLongInt(@AText[]));
  SendMessage(FMainWindowHandleWM_COMMAND$$);
  其中$這個數字也是用Spy++來得到的資源ID
  
  最後別忘了關閉程序以及釋放虛擬Desktop
  if FProceInfohProcess<> then begin
  TerminateProcess(FProceInfohProcess);
  end;
  if FDesktop<> then begin
  CloseDesktop(FDesktop);
  end;
From:http://tw.wingwit.com/Article/program/Delphi/201311/8390.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.