線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:3943
推到 Plurk!
推到 Facebook!

如何將外部執行檔放到專案中呢?

答題得分者是:flyup
SKYSTAR
中階會員


發表:76
回覆:198
積分:64
註冊:2002-06-10

發送簡訊給我
#1 引用回覆 回覆 發表時間:2003-02-05 02:21:45 IP:211.74.xxx.xxx 未訂閱
各位大大好.... 有個奇怪的問題...上來請教一下,看看是否有解? 如主題...就是如果在一個專案中,去執行一個外部程式,如: 小作家..計算機.. 或是Word...Excel...或 IE....等等...但是我要這一個外部程式的整個外觀及 操作畫面,全都塞在專案中的某一個視窗,或是一個Panel.或是任何容器vcl中.. ..請問...可以做得到嗎? 又應該如何做呢? 簡單說...就是不要出現這個外部程式的"獨立視窗"....即: 專案一個視窗,而 外部程式是另一個視窗!! 先謝謝啦!!
flyup
資深會員


發表:280
回覆:508
積分:385
註冊:2002-04-15

發送簡訊給我
#2 引用回覆 回覆 發表時間:2003-02-05 02:37:39 IP:61.225.xxx.xxx 未訂閱
如何得知工作列的位置與狀態     http://delphi.ktop.com.tw/topic.php?topic_id=24875    
SKYSTAR
中階會員


發表:76
回覆:198
積分:64
註冊:2002-06-10

發送簡訊給我
#3 引用回覆 回覆 發表時間:2003-02-05 22:53:49 IP:211.74.xxx.xxx 未訂閱
引言: 如何得知工作列的位置與狀態 http://delphi.ktop.com.tw/topic.php?topic_id=24875
您好: 不好意思...您可能誤會我要的功能了.... 最簡單的說,,,,就是在執行Delphi所完成的EXE檔時,想要開啟記事本..但是在 開啟時,這個記事本軟體的外框是在前面所說之EXE檔的畫面範圍內....就好 像...站上離線包中使用ie一樣....!! Thanks!!
flyup
資深會員


發表:280
回覆:508
積分:385
註冊:2002-04-15

發送簡訊給我
#4 引用回覆 回覆 發表時間:2003-02-05 22:56:31 IP:61.225.xxx.xxx 未訂閱
跟VB一樣,嵌入OLE元件即可!!    
SKYSTAR
中階會員


發表:76
回覆:198
積分:64
註冊:2002-06-10

發送簡訊給我
#5 引用回覆 回覆 發表時間:2003-02-06 00:27:15 IP:211.74.xxx.xxx 未訂閱
引言: 跟VB一樣,嵌入OLE元件即可!!
謝謝!! 可是我試過用ole來做....像Wordpad文件.... 會無法顯示出整個Wordpad的樣子...如: 該軟體的Menu選單... 另外...如果今天用別的語言寫了一支exe檔....而在ole中找不到這個exe的選 項...那又該如何能將此exe檔的執行畫面給放入delphi所寫的專案中呢? Thanks!!
flyup
資深會員


發表:280
回覆:508
積分:385
註冊:2002-04-15

發送簡訊給我
#6 引用回覆 回覆 發表時間:2003-02-06 09:42:36 IP:61.216.xxx.xxx 未訂閱
你要的功能,在delphi-->Demos-->ActiveX-->Olectnrs資料夾 都有範例參考阿! 我想你要的就是這一個,分為MDI及SDI方式! 使用方式: 開新檔案-->插入物件選擇(wordpad)-->就會變更為WordPad功能 發表人 - flyup 於 2003/02/06 09:43:40
SKYSTAR
中階會員


發表:76
回覆:198
積分:64
註冊:2002-06-10

發送簡訊給我
#7 引用回覆 回覆 發表時間:2003-02-06 16:52:46 IP:211.74.xxx.xxx 未訂閱
引言: 你要的功能,在delphi-->Demos-->ActiveX-->Olectnrs資料夾 都有範例參考阿! 我想你要的就是這一個,分為MDI及SDI方式! 使用方式: 開新檔案-->插入物件選擇(wordpad)-->就會變更為WordPad功能 發表人 - flyup 於 2003/02/06 09:43:40
多謝.... 不過...如果我今天所要插入的物件...並不是Active X...而是EXE檔時...那又 該怎麼做呢? 是否無解呢? 因為我試過用Delphi的Demo去做...和我自己開新專案來做時....都是使用 [從檔案建立]的選項來做...因為我要加入的檔案是exe...所以在active x中會 找不到...所以才用[從檔案建立].... 可是在加入後,只能顯示出一個圖示......而無法像active x一樣顯示出該加入 檔案的全部外觀....!! Thanks!!
flyup
資深會員


發表:280
回覆:508
積分:385
註冊:2002-04-15

發送簡訊給我
#8 引用回覆 回覆 發表時間:2003-02-07 10:54:12 IP:61.225.xxx.xxx 未訂閱
你要執行哪一種EXE程式?    
SKYSTAR
中階會員


發表:76
回覆:198
積分:64
註冊:2002-06-10

發送簡訊給我
#9 引用回覆 回覆 發表時間:2003-02-08 00:01:55 IP:211.74.xxx.xxx 未訂閱
引言: 你要執行哪一種EXE程式?
您好: 至於要執行那一種EXE檔...? 我想應該不用限定吧! 因為最主要的是我想要加 入到Delphi專案中的EXE檔...是沒有製作成OLE型態....因此很好奇的想試試看 是否可以有此功能? 一旦這個功能可以成真....那我就可以將之前用別的程式語言所做成的東東,納 入到目前的專案之中,而不會在執行時,看起來就好像是RUN外部程式一樣,這樣 子看起來比較陽春..不太理想!! (因為該別的程式語言並無法做成OLE) 如果您要試的話...應該可以從WIN中的任何一支EXE去著手...只要不是加入已 經為OLE的...就應該是我所說的情形:只會出現一個圖示...然後必須手動去將 它執行!! THANKS!!
flyup
資深會員


發表:280
回覆:508
積分:385
註冊:2002-04-15

發送簡訊給我
#10 引用回覆 回覆 發表時間:2003-02-08 00:18:09 IP:61.216.xxx.xxx 未訂閱
瞭解!你要做的就是跟OFFICE軟體之中,類似Microsoft 文件夾程式一樣 整合word.excel.powerpoint.access的功能。    以你的程式來實現類似這樣的功能,對吧!    那還是建議你參考 Delphi 插件(Plug-ins)創建、調試與使用應用程序擴展製作 為最佳方法。 有沒有使用過Adobe Photoshop?如果用過,你就會對插件的概念比較熟悉。 對外行人來說,插件僅僅是從外部提供給應用程序的程式碼塊而已(舉個例子來說,在 一個DLL中)。一個插件和一個普通DLL之間的差異在於插件具有擴展父應用程序功能 的能力。例如,Photoshop本身並不具備進行大量的圖像處理功能。插件的加入使其 獲得了產生諸如模糊、斑點,以及其他所有風格的奇怪效果,而其中任何一項功能都 不是父應用程序自身所具有的。 對於圖像處理程序來說這很不錯,可是為什麼要花偌大的力氣去完成支持插件的商業 應用程序呢?假設,我們舉個例子,你的應用程序要產生一些報表。你的客戶肯定會 一直要求更新或者增加新的報表。你可以使用一個諸如Report Smith的外部報表生成 器,這是個不怎麼樣的解決方案,需要發佈附加的文件,要對用戶進行額外的培訓, 等等。你也可以使用QuickReport,不過這會使你身處版本控制的噩夢之中--如果每 改變一次字體你就要Rebuild你的應用程序的話。 然而,只要你把報表做到插件中,你就可以使用它。需要一個新的報表嗎? 沒問題,只要安裝一個DLL,下次應用程序啟動時就會看見它了。另外一個例子是處理 來自外部設備(比如條形碼掃瞄器)的資料的應用程序,為了給用戶更多的選擇,你 不得不支持半打的各種設備。通過將每種設備介面處理例程寫成插件,不用對父應用 程序作任何變動就可以獲得最大程度的可伸縮性。 入門 在開始寫程式碼之前最重要的事情就是搞清楚你的應用程序到底需要擴展哪 些功能。這是因為插件是通過一個特定的介面與父應用程序交互的,而這個介面將 根據你的需要來定義。在本文中,我們將建立3個插件,以便展示插件與父應用程 序相交互的幾種方式。 我們將把插件製作成DLL。不過,在做這項工作之前,我們得先製作一個外 殼程序來載入和測試它們。圖1顯示的是加載了第一個插件以後的測試程序。第一個 插件沒有完成什麼大不了的功能,實際上,它所做的只是返回一個描述自己的字符 串。不過,它證明了很重要的一點--不管有沒有插件應用程序都可以正常執行。 如果沒有插件,它就不會出現在已安裝的插件列表中,但是應用程序仍然可以正常 的行使功能。 圖1:插件測試外殼程序 我們的插件外殼程序與普通應用程序之間的唯一不同就在於工程源文件 中出現在uses子句中的Sharemem單元和加載插件文件的程式碼。任何在自身與子DLL 之間傳遞字串參數的應用程序都需要Sharemem單元,它是DelphiMM.dll(Delphi 提供該文件)的介面。要測試這個外殼,需要將DelphiMM.dll文件從Delphi\Bin 目錄複製到path環境變量所包含的路徑或者應用程序所在目錄中。發佈最終版本 時也需要同時分發該文件。 插件通過LoadPlugins過程載入到這個測試外殼中,這個過程在主視窗 的FormCreate事件中調用,見圖2。該過程使用FindFirst和FindNext函數在應用 程序所在目錄中查找插件文件。找到一個文件以後,就使用圖3所示的LoadPlugins過 程將其載入。 { 在應用程序目錄下查找插件文件 } procedure TfrmMain.LoadPlugins; var sr: TSearchRec; path: string; Found: Integer; begin path := ExtractFilePath(Application.Exename); try Found := FindFirst(path cPLUGIN_MASK, 0, sr); while Found = 0 do begin LoadPlugin(sr); Found := FindNext(sr); end; finally FindClose(sr); end; end; 圖 2: 尋找插件 { 加載指定的插件 DLL. } procedure TfrmMain.LoadPlugin(sr: TSearchRec); var Description: string; LibHandle: Integer; DescribeProc: TPluginDescribe; begin LibHandle := LoadLibrary(Pchar(sr.Name)); if LibHandle < > 0 then begin DescribeProc := GetProcAddress(LibHandle, cPLUGIN_DESCRIBE); if Assigned(DescribeProc) then begin DescribeProc(Description); memPlugins.Lines.Add(Description); end else begin MessageDlg('File "' sr.Name '" is not a valid plug-in.', mtInformation, [mbOK], 0); end; end else MessageDlg('An error occurred loading the plug-in "' sr.Name '".', mtError, [mbOK], 0); end; 圖 3: 載入插件 LoadPlugin方法展示了插件機制的核心。首先,插件被寫成DLL。其次, 通過LoadLibrary API它被動態的加載。一旦DLL被加載,我們就需要一個訪問它 所包含的過程和函數的途徑。API調用GetProcAddress提供這種機制,它返回一個 指向所需例程的游標。在我們這個簡單的演示中,插件僅僅包含一個名為 DescribePlugin的過程,由常數cPLUGIN_DESCRIBE指定(過程名的大小寫非常重 要,傳遞到GetProcAddress的名稱必須與包含在DLL中的例程名稱完全一致)。如 果在DLL中沒有找到請求的例程,GetProcAddree將返回nil,這樣就允許使 用Assigned函數測定返回值。 為了以一種易用的方式存儲指向一個函數的游標,有必要為用到的變量 創建一個特定的類型。注意,GetProcAddress的返回值被存儲在一個變量中, DescribeProc,屬於TpluginDescribe類型。下面是它的宣告: type TPluginDescribe = procedure(var Desc: string); stdcall; 由於過程存在於DLL內部,它通過標準調用轉換編譯所有導出例程, 因此需要使用stdcall指示字。這個過程使用一個var參數,當過程返回的時候它包 含插件的描述。 要調用剛剛獲得的過程,只需要使用保存地址的變量作為過程名,後面跟 上任何參數。就我們的例子而言,宣告: DescribeProc(Description) 將會調用在插件中獲得的描述過程,並且用描述插件功能的字串填充Description 變量。 構造插件 我們已經創建好了父應用程序,現在該輪到創建我們希望加載的插件了。 插件文件是一個標準的Delphi DLL,所以我們從Delphi IDE中創建一個新DLL工程, 保存它。由於導出的插件函數將用到字串參數,所以要在工程的uses子句中把 Sharemen單元放在最前面。圖4列出的就是我們這個簡單插件的工程源文件。 uses Sharemem, SysUtils, Classes, main in 'main.pas'; {$E plg.} exports DescribePlugin; begin end. 圖 4: 簡單插件的工程源文件 雖然插件是一個DLL文件,但是沒有必要一定要給它一個.DLL的擴展名。 實際上,一個原因就足以讓我們有理由改變擴展名:當父應用程序尋找要加載的文 件時,新的擴展名可以作為特定的文件掩模。通過使用別的擴展名(我們的例子使 用了*.plg),你可以在一定程度上確信應用程序只會載入相應的文件。編譯指示 字$X可以實現這個改變,也可以通過Project Options對話框的Application頁來設 置擴展名。 第一個例子插件的程式碼是很簡單的。圖5顯示了包含在一個新單元中的代 碼。注意,DescribePlugin原型與外殼應用程序中的TpluginDescribe類型相一致, 使用附加的export保留字指定該過程將被導出。被導出的過程名稱也將會出現在主 工程源程式碼的exports段中(在圖4中列出)。 unit main; interface procedure DescribePlugin(var Desc: string); export; stdcall; implementation procedure DescribePlugin(var Desc: string); begin Desc := 'Test plugin v1.00'; end; end. 圖 5: 例子插件的主程序 在測試這個插件之前,要先把它複製到主應用程序的路徑下。最簡單的辦法就 是在主目錄的子目錄下創建插件,然後把輸出路徑設置為主路徑(Project Options對 話框的Directories/Conditionals也可以作這個設置)。 調試 現在介紹一下Delphi 3中一個較好的功能:從IDE中調試DLL的能力。在DLL工 程中可以通過Run paramaters對話框指定某程序為宿主應用程序,這就是指向將調 用DLL的應用程序的路徑(在我們這個例子中,就是剛剛創建的測試外殼程序)。然後 你就可以在DLL程式碼中設置斷點並且按F9執行它--就像在一個普通應用程序中做的那 樣。Delphi會執行指定的宿主程序,並且,通過編譯帶有調試信息的DLL,把你指引到 DLL程式碼內的斷點處。 延伸父應用 這個簡單的插件不錯,不過它不能做什麼有用的事情。第二個例子就是糾正這個問題。 這個插件的目標就是在父應用程序的主選單中加入一個項目。這個選單項目,當被單 擊時,就會執行插件內的一些程式碼。圖6顯示外殼程序的改進版,兩個插件都已經加 載。在這個版本的外殼程序中,一個名為Plug-in的新選單項目,被添加到主選單中。 插件會在執行時加入一個選單項。 圖6:加載了兩個插件的外殼程序的改進版 為了實現這個目的,我們必須在插件DLL中定義第二個介面。現有的DLL只導出了一 個過程,DescribePlugin。第二個插件將宣告一個叫做InitPlugin的過程。不過,在 這個過程可以在主應用程序中看到以前,必須修改LoadPlugin來配合它。 圖7所示的程式碼展示了改進的過程。 procedure TfrmMain.LoadPlugin(sr: TSearchRec); var Description: string; LibHandle: Integer; DescribeProc: TPluginDescribe; InitProc: TPluginInit; begin LibHandle := LoadLibrary(Pchar(sr.Name)); if LibHandle < > 0 then begin // 查找 DescribePlugin. DescribeProc := GetProcAddress(LibHandle, cPLUGIN_DESCRIBE); if Assigned(DescribeProc) then begin // 調用 DescribePlugin. DescribeProc(Description); memPlugins.Lines.Add(Description); // 查找 InitPlugin. InitProc := GetProcAddress(LibHandle, cPLUGIN_INIT); if Assigned(InitProc) then begin // 調用 InitPlugin. InitProc(mnuMain); end; end else begin MessageDlg('File "' sr.Name '" is not a valid plugin.', mtInformation, [mbOK], 0); end; end else begin MessageDlg('An error occurred loading the plugin "' sr.Name '".', mtInformation, [mbOK], 0); end; end; 圖 7: 改進過的LoadPlugin方法 如你所見,當GetProcAddress第一次查找調用描述過程之後,又調用了一次 GetProcAddress。這一次,我們要尋找的是常量cPLUGIN_INIT,定義如下: const cPLUGIN_INIT = 'InitPlugin'; 返回值存儲在TpluginInit類型的變量中,定義如下: type TPluginInit = procedure(ParentMenu: TMainMenu); stdcall; 當InitPlugin方法被執行時,父應用程序的主選單被當作一個參數傳遞給它。這個 過程可以按照自己的意願修改選單。由於所有GetProcAddress的返回值都用assigned 測試,新版本的LoadPlugin過程仍然會加載不包含InitPlugin過程的第一個插件。在 這個過程中第一次調用尋找DescribePlugin方法會通過,第二次尋找InitPlugin會 無響應失敗。 現在新的介面已經定義好了,可以為新的InitPlugin方法編寫程式碼了。像原先一樣, 新插件的實現程式碼存在於一個單獨的單元中。圖8顯示了修改過的包含InitPlugin方法 的main.pas。 unit main; interface uses Dialogs, Menus; type THolder = class public procedure ClickHandler(Sender: TObject); end; procedure DescribePlugin(var Desc: string); export; stdcall; procedure InitPlugin(ParentMenu: TMainMenu); export; stdcall; var Holder: THolder; implementation procedure DescribePlugin(var Desc: string); begin Desc := 'Test plugin 2 - Menu test'; end; procedure InitPlugin(ParentMenu: TMainMenu); var i: TMenuItem; begin // 創建新選單項. i := NewItem('Plugin &Test', scNone, False, True, Holder.ClickHandler, 0, 'mnuTest'); ParentMenu.Items[1].Add(i); end; procedure THolder.ClickHandler; begin ShowMessage('Clicked!'); end; initialization Holder := THolder.Create; finalization Holder.Free; end. 圖 8: 第二個插件的程式碼 很明顯,對原始插件的第一個改變就是增加了InitPlugin過程。像原先一樣,帶有 export關鍵字的原型被加入到單元頂端的列表中,過程名也被加入到工程源程式碼的 exports子句列表中。這個過程使用NewItem函數創建一個新的選單項,返回值是 TmenuItem對象。新選單項通過下列語句被加入到應用程序主選單中: ParentMenu.Items[1].Add(I); 在測試外殼主選單上的Items[1]是選單項Plug-in,所以這個語句在Plugin選單條 上添加一個叫Plug-in Test的選單項。 為了處理對新選單項的響應,作為它的第五個參數,NewItem可以接受一個 TNotifyEvent類型的過程,這個過程將在選單項被點擊時調用。不幸的是,按照定 義,這種類型的過程是一個對像方法,然而在我們的插件中並沒有對象。如果我們想 用通常的游標來指向函數,那麼得到的將只會是Delphi編譯器的抱怨。所以,唯一的 解決辦法就是創建一個處理選單點擊的對象。這就是Tholder類的用處。它只有一個方 法,是一個叫做ClickHandler的過程。一個叫做Holder的全局變量,在修改過的main.pas 的var段中被宣告為Tholder類型,並且在單元的initialization段中被創建。現在我們就 有一個對象了,我們可以拿它的方法(Holder.ClickHandler)當作NewItem函數的參數。 搞了這一通,ClickHandler除了顯示一個"Clicked!"消息對話框以外什麼以沒干。 也許這不怎麼有趣,不過它仍然證明了一點:插件DLL成功的修改了父應用的主選單, 表現了它的新用途。並且如同第一個例子一樣,不管這個插件在不在應用程序都能執行。 由於我們創建了一個對像來處理選單點擊,那麼在不再需要這個插件時,就要釋放這 個對象。修改後的單元中會在finalization段中處理這件事情。Finalization端時與 initialization段相對應的,如果前面有一個initialization段,那麼在應用程序終 止時finalization段一定會得到執行。把下面的語句 Holder.Free 加到finalization段中,以確保Holder對像會被正確的釋放。 顯而易見,雖然這個插件只是修改了外殼應用的主選單,但是它可以輕易地操縱傳 遞到InitPlugin過程中的任何其他對象。如果有必要,插件也可以打開自己的對話框, 向列表框(List boxes)和樹狀視圖(tree views)中添加項目,或者在畫布(canvas) 中繪畫。 事件驅動的插件 到現在為止我們所描述的技術可以產生一種通用的擴展應用程序的方法。通過增加 新選單、視窗和對話框,就可以實現全新的功能而不必對父應用做任何修改。不過仍 然有一個限制:這只是一種單側(one-sided)機制。正如所看到的,系統依賴用戶的 某些操作才能啟動插件程式碼,比如點擊選單或者類似的動作。程式碼執行起來以後,又要 依靠另外一個用戶動作來停止它,例如,關閉插件可能已經打開的視窗。克服這種缺 陷的一種可行的方法就是使插件可以響應父應用中的動作--模仿在Delphi中工作地 很好的事件驅動編程模型的確有效。 在最後一個例子插件中,我們將創建一種機制,插件可以藉此響應父應用中產生的事 件。通常情況下,可以通過判定需要觸發哪些事件、在父應用中為每個事件創建一個 Tlist對像來實現。然後每個Tlist對象都被傳遞到插件的初始化過程中,如果插件想 在某個事件中執行動作,它就把負責執行的函數地址加入到對應的TList中。父應用在 適當的時刻循環這些函數游標的列表,按次序調用每個函數。通過這種方法,就為多 個插件在同一事件中執行動作提供了可能。 應用程序產生的事件完全依賴於程序已確定的功能。例如,一個TCP/IP網路應用程序 可能希望通過TclientSocket的onRead事件通知插件資料抵達,而一個圖形應用程序可 能對調色板的變化更感興趣。 為了說明事件驅動的插件應答的概念,我們將創建一個用於限制主視窗最小尺寸 的插件。這個例子有點兒造作,因為把這個功能做到應用程序裡邊會比這簡單的多。 不過這個例子的優點在語容易編碼而且易於理解,而這正是本文想要做到的。 很明顯,我們要做的第一件事情就是決定到底要產生哪些事件。在本例中,答案 很簡單:要限制一個應用程序視窗的尺寸,有必要捕獲並且修改Windows消息 WM_GETMINMAXSINFO。因此,要創建一個完成這項功能的插件,我們必須捕獲這個消 息並且在這個消息處理器中調用插件例程。這就是我們要創建的事件。 接下來我們要創建一個TList來處理這個事件。在主視窗的initialization段中將 會創建lstMinMax對象,然後,創建一個消息處理器來捕獲Windows消息 WM_GETMINMAXINFO。圖9中的程式碼顯示了這個消息處理器。 { 捕獲 WM_GETMINMAXINFO. 為每個消息調用插件例程. } procedure TfrmMain.MinMaxInfo(var msg: TMessage); var m: PMinMaxInfo; file://在 Windows.pas 中定義. i: Integer; begin m := pointer(msg.Lparam); for i := 0 to lstMinMax.count -1 do begin TResizeProc(lstMinMax[i])(m.ptMinTrackSize.x, m.ptMinTrackSize.y); end; end; 圖 9: WM_GETMINMAXINFO 的消息處理器 外殼應用的LoadPlugin過程必須再次修改以便調用初始化例程。這個新初始化 函數把我們的TList當作參數接受,在其中加入修改消息參數的函數地址。圖10 顯示了LoadPlugin過程的最終版本,它可以執行到目前為止所討論的全部幾個插件 的初始化工作。 { 加載指定的插件DLL. } procedure TfrmMain.LoadPlugin(sr: TSearchRec); var Description: string; LibHandle: Integer; DescribeProc: TPluginDescribe; InitProc: TPluginInit; InitEvents: TInitPluginEvents; begin LibHandle := LoadLibrary(Pchar(sr.Name)); if LibHandle < > 0 then begin // 查找 DescribePlugin. DescribeProc := GetProcAddress(LibHandle, cPLUGIN_DESCRIBE); if Assigned(DescribeProc) then begin // 調用 DescribePlugin. DescribeProc(Description); memPlugins.Lines.Add(Description); file://查找InitPlugin. InitProc := GetProcAddress(LibHandle, cPLUGIN_INIT); if Assigned(InitProc) then begin file://調用InitPlugin. InitProc(mnuMain); end; // 為第三方插件查找 InitPluginEvents InitEvents := GetProcAddress(LibHandle, cPLUGIN_INITEVENTS); if Assigned(InitEvents) then begin // 調用 InitPlugin. InitEvents(lstMinMax); end; end else begin MessageDlg('File "' sr.Name '" is not a valid plugin.', mtInformation, [mbOK], 0); end; end else begin MessageDlg('An error occurred loading the plugin "' sr.Name '".', mtInformation, [mbOK], 0); end; end; 最後一步是創建插件自身。如同前面的幾個例子,插件展示一個標誌自身的描述 過程。它也帶有一個初始化例程,在本例中只是接受一個TList作為參數。最後,它還 包含一個沒有引出(Export)的歷程,叫做AlterMinTrackSize,它將修改傳遞給它的 數值。 最終插件的完整程式碼。 unit main; interface uses Dialogs, Menus, classes; procedure DescribePlugin(var Desc: string); export; stdcall; procedure InitPluginEvents(lstResize: TList); export; stdcall; procedure AlterMinTrackSize(var x, y: Integer); stdcall; implementation procedure DescribePlugin(var Desc: string); begin Desc := 'Test plugin 3 - MinMax'; end; procedure InitPluginEvents(lstResize: TList); begin lstResize.Add(@AlterMinTrackSize); end; procedure AlterMinTrackSize(var x, y: Integer); begin x := 270; y := 220; end; end. 最終插件的程式碼 InitPluginEvents過程是這個插件的初始化例程。它接受一個TList作為參數。這 個TList就是在父應用程序中創建的保存相應函數地址的列表。下面的語句: lstResize.Add(@AlterMinTrackSize); 把AlterMinTrackSize函數的地址加入到了這個列表中。它被宣告為類型stdcall以 便與其他過程相配,不過用不著export指示字。由於函數被直接通過它的地址調用, 所以也就沒有必要按照通常的方式把它從DLL中引出。 所以,事件序列如下所列: 1、 在應用程序初始化時,創建一個TList對象。 2、 在啟動時這個列表被傳遞到插件的初始化過程InitPluginEvents中。 3、 插件過程把一個過程的地址加入到列表中。 4、 每次視窗大小改變時所產生的Windows消息WM_GETMINMAXINFO被我們的應用程 序所捕獲。 5、 該消息被我們的消息處理器TfrmMain.MainMaxInfo所處理,見圖10。 6、 消息處理器遍歷列表並調用它所包含的函數,把當前的X和Y最小視窗尺寸作為 參數傳遞。要注意,TList類只是存儲游標,所以如果想用保存的地址做些什麼事情 的話,我們必須把游標轉換成所需要的類型--在本例中,要轉換成TresizeProc。 TResizeProc = procedure (var x, y: Integer); stdcall; 7、 插件過程AlterMinTrackSize(列表中的游標所指向的),接受X和Y值作為可變 的var參數並且修改它們。 8、 控制權返回到父應用的消息處理器,按照最小視窗尺寸的新值繼續執行下去。 9、 應用程序退出時TList會在主程式碼的finalization段被釋放。 結論 使用該體系結構時,可能利用Delphi提供的package功能是個不錯的主意。在通 常情況下,我不是一個分割執行時模塊的狂熱愛好者,但是當你認為任一包含大量代 碼的Delphi DLL超過200KB時,它就開始變得有意義了。 這篇文章應該還是有些用處的,至少它可以讓你思考一些程序設計方面的問題,比 如如何讓它變得更加靈活。我知道如果我在以前的應用程序中使用一些這種技術的 話,我就可以省掉在修改程序方面的好多工作。我並不想把插件作為一種通用的解 決方案。很明顯,有些情況下額外的複雜度無法驗證其正確性,或者應用程序壓根兒 就不打算把自身搞成幾塊可擴展的單元。還有一些其它的方法也可以達成同樣的效果。 Delphi自身提供了一個介面來創作能集成到IDE中的模塊,比起我所說明的技術這種方 法更加面向對像(或者說更"乾淨"),而我也確信你可以在自己的應用中模仿這一技 術。在執行時加載Delphi包也不是做不到的。探索一下這種可能性吧。 本文所介紹的技術在Delphi 4下工作的很好。實際上,Delphi 4增加了工程選項,使 這類應用程序加強DLL(application-plus-DLL)的開發變得更加容易了。
SKYSTAR
中階會員


發表:76
回覆:198
積分:64
註冊:2002-06-10

發送簡訊給我
#11 引用回覆 回覆 發表時間:2003-02-08 03:24:19 IP:211.74.xxx.xxx 未訂閱
To: flyup    哇....這麼多資料呀.....怎麼辦...有看沒有懂呀!! 只好有空時去試試看了....搞不好試不出來呢!! 另外...看了文中的說明...好像沒有針對 > 不管如何.....太感謝您了....就此先結案囉!!
flyup
資深會員


發表:280
回覆:508
積分:385
註冊:2002-04-15

發送簡訊給我
#12 引用回覆 回覆 發表時間:2003-02-08 12:50:24 IP:61.225.xxx.xxx 未訂閱
相關範例: 請搜尋本討論區文章:    鍵入 plugin 即可搜尋到一堆範例與元件!    
SKYSTAR
中階會員


發表:76
回覆:198
積分:64
註冊:2002-06-10

發送簡訊給我
#13 引用回覆 回覆 發表時間:2003-02-09 04:59:56 IP:211.74.xxx.xxx 未訂閱
引言: 1. 這個問題有解而且非常簡單,大概不超過十行的程式碼就可解決 2. 因為有個朋友也請教我這方面的問題,所以我今天到圖書館去寫程式,我也試過 TOLEContainer 的方式 也試過範例但都不是我所要的,後來終於試出來了,只是尚未全部寫完,我必須先說抱歉這個範例不能公開因為朋友可能要使用在商業用途上 3. 不過我可以把關鍵的方法顯示出來 procedure TFormMainProgram.Button2Click(Sender: TObject); var H1,H2: Hwnd; begin // 請記得 SHELLAPI ShellExecute(H1,'open',PChar('Samples.xls'),Nil,PChar('C:\Program Files\Microsoft Office\Office10\Samples'),0); // 因為使用 ShellExecute 所以必須再使用 FindWindow 找出剛剛執行的外部程式 H1 := FindWindow(Nil,PChar('MicroSoft Excel - SAMPLES.EXE')); // 假設找到執行之程式後要放在 Panel0 H2 := Panel0.Handle; Windows.SetParent(H1,H2); // 將外部程式放在 Panel0 Windows.ShowWindow(H1,SW_MAXIMIZE); end; 目前這程式有一些缺點待改善 1. 會先執行外部程式,等載入完成後才會跑到指定的地方 2. FindWindow無法事先完全確定參數的名稱 謹供大家參考 謝謝
太感謝啦.... 要去試試看囉!!
SKYSTAR
中階會員


發表:76
回覆:198
積分:64
註冊:2002-06-10

發送簡訊給我
#14 引用回覆 回覆 發表時間:2003-02-09 05:53:59 IP:211.74.xxx.xxx 未訂閱
引言: 1. 這個問題有解而且非常簡單,大概不超過十行的程式碼就可解決 2. 因為有個朋友也請教我這方面的問題,所以我今天到圖書館去寫程式,我也試過 TOLEContainer 的方式 也試過範例但都不是我所要的,後來終於試出來了,只是尚未全部寫完,我必須先說抱歉這個範例不能公開因為朋友可能要使用在商業用途上 3. 不過我可以把關鍵的方法顯示出來 procedure TFormMainProgram.Button2Click(Sender: TObject); var H1,H2: Hwnd; begin // 請記得 SHELLAPI ShellExecute(H1,'open',PChar('Samples.xls'),Nil,PChar('C:\Program Files\Microsoft Office\Office10\Samples'),0); // 因為使用 ShellExecute 所以必須再使用 FindWindow 找出剛剛執行的外部程式 H1 := FindWindow(Nil,PChar('MicroSoft Excel - SAMPLES.EXE')); // 假設找到執行之程式後要放在 Panel0 H2 := Panel0.Handle; Windows.SetParent(H1,H2); // 將外部程式放在 Panel0 Windows.ShowWindow(H1,SW_MAXIMIZE); end; 目前這程式有一些缺點待改善 1. 會先執行外部程式,等載入完成後才會跑到指定的地方 2. FindWindow無法事先完全確定參數的名稱 謹供大家參考 謝謝
哈....我試出來了喲!! 不過要將ShellExecute改成WinExec.... 否則,必須按第二次Button之後,才會將外部exe給放到panel中.... 不過後來又發現一個問題.... 就是如果我要放入的exe檔,不想讓它最大化時,而又想要設定其顯示的位置... 那麼...請問該如何做呢? (比如說顯示在panel的右上角100,100的地方) 多謝啦!!
SKYSTAR
中階會員


發表:76
回覆:198
積分:64
註冊:2002-06-10

發送簡訊給我
#15 引用回覆 回覆 發表時間:2003-02-10 00:02:34 IP:211.74.xxx.xxx 未訂閱
引言: Windows.SetParent(H1,H2); Windows.ShowWindow(H1,SW_NORMAL); MoveWindow(H1,100,200,300,400,True); // MoveWindow(hWnd,X,Y,W,H,B) : RETURN BOOL //hWnd: HWND //X:Integer; {horizontal} //Y:Integer; {vertical} //W:Integer; {the new window width} //H:Integer; {the new window height} //B:BOOL; {the repaint flag} 也可用 SetWindowPos(H1,0,200,200,300,500,SWP_SHOWWINDOW); 但這參數比較複雜 發表人 - 懷舊的人 於 2003/02/09 10:42:48
嘻....真是高手如雲....謝啦!! 對了...不知您是怎麼知道這些函數的呀....是經驗的累積嗎? 否則在多的數不清的函數及程序中....您怎麼會知道有這些個參數可加利用呢? 另外...可否最後再請教一個問題.... 像我用WinExec或是ShellExecute時,其最後一個參數設成Hide時,如:SW_HIDE.. 為什麼所執行的外部程式還是會看得到呢? 不是應該是隱藏的嗎? 因為我發現當套用您所提供的方式及變更成用WinExec時,卻會讓畫面閃來閃去 的....因為是先執行外部程式...然後才將外部程式放到Panel中....所以我才 想到用Hide來讓外部程式執行時,先讓外部程式的介面看不到...最後在放到 Panel後,才讓Windows.ShowWindow把它顯示在Panel中....可是卻沒想到... Hide沒有作用?? 是我在那裡出了問題嗎? 再次感謝您囉!!
懷舊的人
高階會員


發表:28
回覆:152
積分:141
註冊:2003-01-08

發送簡訊給我
#16 引用回覆 回覆 發表時間:2003-02-10 08:44:39 IP:152.104.xxx.xxx 未訂閱
1. 提到使用 API 我想版主及其他人才是這方面的高手,我個人通常是有要需要時才會去參考,你在程式中使用到的API應該都在 Windows這個unit。 2. 至於參數的用法如果你有 delphi 7.0 他的 help 有一個 window sdk說的很清楚,至於其他的版本我忘了有關於 SDK 的HELP 在那裡 3. 如果你還要深入研究有兩本書你可參考,有範例教你使用這些 API A. Tomes of Delphi: WIn32 Shell API Windows 2000 Edition B. Tomes of Delphi: Win32 Core API Windows 2000 國內沒有人代理,你可委託國內書局幫你訂購,或是到 www.amazon.com 訂購 進了 www.amazon.com 到search 輸入 delphi 按go 再選 books 就可看到所有 delphi 的書 我已經買了這兩本,有需要的時候隨時參考 4. 你提到的 sw_hide 是有效的你必須多做其他的嘗試,我是放棄這樣的方法,我改用 createprocess 的方法試,很快就會有結果,有空時我會另外寫一個完整的範例 祝福你
懷舊的人
高階會員


發表:28
回覆:152
積分:141
註冊:2003-01-08

發送簡訊給我
#17 引用回覆 回覆 發表時間:2003-02-10 14:58:03 IP:152.104.xxx.xxx 未訂閱
只完成了 99.9 接近成功 ,可惜生命終了,剩下的只好麻煩你了,謝謝 附上範例,注意測試程式是在 WIN2000 , DELPHI6 測試的 請仔細看完程式再執行 程式中有一個至缺點就是按第一次 啟動外部程式 的時候有時會失敗 , 但第二次以後就正常了 所為失敗是外部程式已經啟動了 但沒放到指定的地方(關畢時請記得由工作管理員將它刪除) 請啟動工作管理員觀察 請自行下載範例(會員求助區) 發表人 - 懷舊的人 於 2003/02/10 15:08:04
SKYSTAR
中階會員


發表:76
回覆:198
積分:64
註冊:2002-06-10

發送簡訊給我
#18 引用回覆 回覆 發表時間:2003-02-10 17:40:53 IP:211.74.xxx.xxx 未訂閱
謝謝您....    我會去下載回來參考看看.....< > 另外,在回這篇留言之前...我已經試出來了....而且每次都可以成功....< > 不過只有一個小小的缺點,那就是如果該外部程式原本是最大化視窗時,會導 致第一次執行時,會顯示出最大化時的過程,然後才會放到正確的位置,但是經 由放到正確的位置後,以後在執行時,就不會有這個閃動的問題了,當然啦,除非 這一個外部程式的預設狀態又被設成了最大化,否則就通通是正常的...也就是 已經達到我要的功能了喲!! 在這裡也順便貼上我所改寫後的範例...(註: class="code"> procedure TForm1.Button1Click(Sender: TObject); var H1,H2: Hwnd; begin // 先執行要放入專案的外部EXE檔案(用隱藏方式來執行) WinExec('C:\Program Files\TOOLS\MYIE\MyIE.exe',SW_HIDE); // 再找到該外部EXE檔案 H1 := FindWindow(nil,PChar('MyIE')); // 假設找到執行之程式後要放在 Panel1 H2 := Panel1.Handle; // 將外部程式放在 Panel1 Windows.SetParent(H1,H2); // 設定外部程式顯示的位置及大小 // MoveWindow(hWnd,X,Y,W,H,B) : RETURN BOOL // hWnd: HWND // X:Integer; {horizontal} // Y:Integer; {vertical} // W:Integer; {the new window width} // H:Integer; {the new window height} // B:BOOL; {the repaint flag} MoveWindow(H1,0,0,Panel1.Width,Panel1.Height,True); // Windows.ShowWindow(H1,SW_ShowNormal); end; 最後再向您說聲.....謝謝啦!!
系統時間:2024-05-13 13:05:14
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!