程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 再談後台調用外部程序的完美實現

再談後台調用外部程序的完美實現

編輯:Delphi

  上次介紹了如何在Delphi中使用發送消息的方式控制外部程序,一開始我在自己的項目中也確實是這麼做的,但是後來遇到了這麼一個問題:

  我所調用的程序,會在執行一段處理過程中,將結果顯示到一個ListView中,那麼為了知道我發出的命令,到底被那個程序執行後結果如何,我就必須監視ListView中的內容,察看最後出現的結果文字是什麼,從而知道到底是成功還是失敗了。那麼,我的想法是,不斷的查詢ListVIEw中Items的個數,並且當個數大於0的時候,取出最後一條Item(就是最後加入的結果描述),然後取得其中的文字,通過判斷字符串,就可以知道結果了。

  首先,ListView的窗口Handle我當然是有了,然後取得ListView中的Item個數,我發現有這麼個函數可以使用:ListView_GetItemCount(),它在CommCtrl模塊中有定義,其實只是對SendMessage的一個封裝而已,同樣的,該模塊中還有另一個函數:ListVIEw_GetItemText(),使用它可以取得指定行處的Item文字。

  那麼只要在我的程序中使用這兩個函數就可以了咯?很抱歉,我用實際經歷告訴你:這樣將會導致外部程序的崩潰!!

  要說明為什麼,首先讓我們來看一下ListView_GetItemText()函數到底做了什麼(另外還有ListView_GetItemTextA和ListVIEw_GetItemTextW這兩個函數,暫時不用去理它們):

  function ListVIEw_GetItemText(hwndLV: HWND; i, iSubItem: Integer;
    pszText: PChar; cchTextMax: Integer): Integer;
  var
    Item: TLVItem;
  begin
    Item.iSubItem := iSubItem;
    Item.cchTextMax := cchTextMax;
    Item.pszText := pszText;
    Result := SendMessage(hwndLV, LVM_GETITEMTEXT, i, Longint(@Item));
  end;

  相信你能看明白,它將一個字符串指針pszText放到item結構中,再用SendMessage將該結構地址傳給ListVIEw窗口,在本地程序中這當然不會有問題,但是試想一下在外部exe中的情況,Item和pszText都是我的程序中的變量,外部exe有自己的獨立進程空間,同樣的變量地址在它的進程空間中指向的是完全不同的數據,並且很有可能該數據正在被其它程序段使用,那麼當外部程序收到Message,並向該錯誤的地址中寫入數據的時候,程序就這麼崩潰了!

  難道就沒有辦法解決這個問題了嗎?當然有!很簡單,我只需要保證外部程序收到Message時,將數據寫入到本地進程空間中的地址中就可以了,那麼這就意味著我必須在外部程序中開辟一塊內存,讓SendMessage來寫入,並且能夠從中將數據讀回來,有什麼辦法呢?在這裡就要感謝Robert Kuster這個大師級人物了,他在以下這篇文章中詳細的介紹了多種將代碼注入外部程序的方法:

  Three Ways to inject Your Code into Another Process

  我選擇了第二種,即制作一個dll,由主程序將這個dll注入到外部程序的進程空間中,以後就可以為所欲為了,哈哈!Robert Kuster的例子中,只需要用dll中的代碼取一個文本框中的密碼就可以了,而我的稍有不同,我需要不斷的監視ListVIEw中的狀態,所以我使用了一個線程,dll被注入後,立即啟動自己的線程,並進入線程循環,一直到外部程序關閉,線程會被自動關閉(所以關閉的事,我就不管了)。

  另外,既然有了線程,連控制外部程序的代碼都可以放在線程中,這麼一來就變成這樣的一個流程了:

  1。主程序再虛擬桌面上啟動外部程序,並將自制dll注入外部程序的進程

  2。被注入的dll啟動線程,線程的開始,先找到所有需要使用的窗口的WindowHandle,這一點還是用FindWnidow方法。接著打開一個共享內存區,用來和主程序通訊使用。

  3。進入dll的線程循環,循環中使用一個event進入等待狀態。

  4。當主程序中需要使用外部程序的功能時,將控制命令寫入共享內存中,然後觸發event。

  5。被注入dll中的線程的event被觸發,它從共享內存中取得控制命令,然後使用SendMessage啟動外部程序的功能,並進入循環,等待ListVIEw中出現結束文字。

  6。外部程序中的功能執行結束後,dll中的線程再以event的方式通知主程序。

  好了,這下真的是“完美”實現了!

  由於商業上的原因,代碼我就不能公開了,如果你還有什麼疑問,倒是隨時歡迎來信討論:[email protected]

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved