為Delphi應用增加指令碼支援

2021-09-08 08:27:47 字數 3475 閱讀 7762

上次說到我想為directui增加指令碼支援,今天我們就來研究下指令碼的實現問題。

雖然現在有了諸如antlr、gold parser、tp lex and yacc等等cc工具,大大方便了指令碼引擎的開發,但我仍然覺得在這個框架裡自己實現一套指令碼引擎是件毫無意義的事。所以我決定使用現有的指令碼引擎。

事實上delphi可用的第三方指令碼引擎很多,這裡列幾個常見的:

fastscript

號稱最快的delphi指令碼引擎,支援 vb、js、cpp、pascal 語法,可以在指令碼中使用自定義的型別和物件,內建了對delphi vcl的支援。
ifps/remobjects pascalscript

使用pascal語法的指令碼引擎,可以在指令碼中使用自定義的型別和物件。
tms scripter studio

tms出品的指令碼引擎,除了指令碼引擎,還附帶有一堆可視控制項,可以用來開發ide。

支援在指令碼中使用自定義的型別和物件,也內建了對delphi vcl的支援。

paxscript / paxcompiler

這是我用過的delphi裡最強大的指令碼引擎了。同樣,她支援 vb、js、cpp、pascal 語法,也支援在指令碼中使用自定義的型別和物件,內建了對delphi vcl的支援。

另外,她還支援將指令碼**編譯成中間位元組碼,以後執行是可以直接載入中間位元組碼執行,節約了**解析的時間。如果使用paxcompiler,你甚至可以將指令碼**編譯成機器碼直接執行!

但是最終我並沒有在其中進行選擇,因為上述這些指令碼引擎都有乙個問題,就是對自定義的型別和物件的支援方式。

在這些引擎中,如果你要增加自定義型別的支援,就必須為每乙個類的每乙個方法和屬性生成乙個**函式,然後通過引擎通過這些**函式對物件進行操作。

想想我的directui引擎,其中大大小小有上百個類,光生成這些**函式都能讓我瘋了。雖然它們也有提供工具,可以根據源**直接生成**函式,但問題是我以後如果修改了源**,豈不是又得重新生成一遍?想想都煩啊。

最後我把目光定在了ms身上。其實ms早就為我們提供了一套好用的指令碼引擎:ms scriptcontrol。見過html裡的vbs和js嗎?那裡用的指令碼引擎跟ms scriptcontrol是同一套核心。

ms scriptcontrol系統預設支援vbs和js,但實際上他的語法是可以擴充套件的。例如你機器上如果裝有python,你就能在指令碼中使用python。

ms scriptcontrol可以訪問自動化物件(對應到delphi裡就是tautoobject),將物件用addobject方法註冊到引擎裡,就可 以在指令碼中訪問了。在執行時,引擎會取得物件的idispatch介面實現,並通過idispatch.getidsofnames取得要訪問的方法或屬 性的id,然後在呼叫idispatch.invoke執行此方法或取得此屬性的值。

聰明的你一定想到了,我只要實現乙個tautoobject**物件,然後用這個物件通過rtti,就可以訪問到我們的自定義物件了!借助 rtti,我只需要實現乙個通用的**物件,就能讓指令碼訪問所有型別的物件,即使型別有更改,**物件都不需要做任何改動!真正的一勞永逸啊,哇咔 咔~~~

好了,飯要一口一口吃,現在我們先把ms scriptcontrol封裝一下,後面再去做**物件。

我們先來定義一套介面,這套介面定義了我們的指令碼引擎所需要實現的功能,然後再封裝ms scriptcontrol來實現。這樣做的好處是,如果我以後想換其他的指令碼引擎,只需要把這套介面重新實現一遍就好了,外部呼叫指令碼引擎的地方基本無需改動。

簡單的東西就不寫了,去google吧,這裡講幾個網上找不到相關說明的細節。

ms scriptcontrol中有模組的概念,就像我們ide中同一工程下不同的單元檔案一樣。預設有乙個主模組,名字叫global,ms scriptcontrol中的addcode、eval、executestatetment等方法都是對這個模組訪問的封裝。其他模組是需要用 modules.add自己新增的。

這裡有個細節,modules.add方法有2個引數:name,模組名稱,必填;obj,olevariant型別,是個可選引數。這個引數做什 麼用的我到現在也沒明白,本來我也不想關心他的,可惜繞不過去。在vb中,dispinte***ce的可選引數可以不寫,直接 modules.add("module1")就ok,可是delphi下這個引數是var的,必須填。於是我

varobj: olevariant; 

begin

obj := unassigned;

modules.add(

'module1

', obj);

end;

複製**

執行報錯。。。於是我再obj := null,錯。。。再obj := 0,又錯。。。數次失敗後,我都快絕望了。。。後來我想,這個引數我也不用,不是有個全域性的emptyparam麼,省了我宣告個變數,於是modules.add('

module1

', emptyparam),結果。。。通過了。。。神那,你能告訴我這引數到底幹蝦公尺用的啊。。。淚啊。。。

繼續。。。與編譯器對待單元檔案的方式不同,這些模組都是封閉的,除了其他的模組可以直接呼叫主模組中方法外,其他模組之間無法直接互相呼叫,主模組也無法呼叫其他模組。挺奇怪的。

後來我想了個轍。每個模組都有個屬性codeobject,即該模組的**物件,通過他是可以呼叫到模組中的**的。這個物件可是idispatch哦,那麼我將他註冊到指令碼引擎裡,不就可以在其他模組中用 模組名.物件 來訪問了嗎?我真是太聰明了,咔咔。。。

vari: integer;

module: iscriptmodule;

begin

for i := 

1to modules.count 

dobegin

module := modules[i];

fscript.addobject(module.name, module, false);

end;

end;

複製**

f5,嗯,出錯?物件名已存在??nn個熊,要存在我怎麼訪問不到!算了,只好出昏招,我註冊名跟模組名不一樣,總可以了吧。。。

唉,折騰了一天,總算是可以用了~

圖上是呼叫第乙個模組的foo方法的執行結果

好了,羅羅嗦嗦了這麼多,就到這裡吧,下次再來實現**物件。

看清這世界的美麗與殘酷

分類:

borland,

win32 platform,

我的軟體

標籤:

delphi,

script,

ms

DELPHI 查詢,增加,修改,刪除

unit unit1 inte ce uses windows,messages,sysutils,variants,classes,graphics,controls,forms,dialogs,grids,dbgrids,db,adodb,stdctrls type tform1 class t...

Delphi 增加 查詢Win使用者

附 在delphi帶的win32 developer s references檢視user info 1 知道所用標頭檔案是lmaccess.h,但是delphi並沒有帶這個檔案,pb帶了這個檔案,裡面確實有常數user priv user 的定義,下面的內容是從網上找到的。const se cre...

delphi 判斷字元為中文

function bytetype const s string index integer tmbcsbytetype 告訴你乙個非常有用的函式。bytetype 它可以判斷乙個字串中,某個 char 是單個字母,還是雙位元組的前一位或 後一位。mbsinglebyte 單字母 mbleadbyt...