---摘自:http://www.bravos.com.tw/big5/tutor/profession/componentvcl2activex/
1.前言: ActiveX vs Java
未來我們朝向以Windows作業平台為主的Web Application系統開發時,Delphi將會被定位為軟體元件開發工具以提供ActiveX供ASP使用。本文詳述如何將VCL 模組轉換成 ActiveX 元件,對於ASP、VB程式開發期間若遇到強悍(tough)的功能需求時,保證各位不會捉襟見肘的。Code Reusing à Programmer生涯海闊天空。 |
以下敘述為擁ActiveX的論點,不代表本臺立場。 ActiveX offers several benefits not available to Java, its primary competition: | |
l | It's cached. OCX files brought down from the Web are brought once, assuming cache-time and disk allotment aren't exceeded. Java code must be downloaded and run with each hit to a Java-enabled site. |
l | It's fast. ActiveX files are compiled code, and run natively as extensions to the clients' browsers. Java, to date, is interpreted (there are some exceptions for JIT compilers and the JBuilder native compiler), and takes between five and 20 times the amount of time to provide identical functionality if achievable. |
l | It can be 100 percent Delphi. One of the things I dislike about Java is that - it's Java. I took up Delphi specifically because I didn't want to learn C. Perhaps I'm lazy, but I am reticent to give up the skills it's taken me a few years to develop. JBuilder is a great product, and I do see a future in Java, but I don't want to learn the syntax. Installed development shops that have a lot invested in Delphi will find the ActiveX route a potential gold mine in resource usage. |
It also shares some benefits with Java: | |
l | It's instantly updated. As soon as a new OCX file is delivered to the Web site, new hits draw an updated version of the application. |
l | It's thin. That's right, thin. Although even a small ActiveX application deploys at around a half a megabyte (the sample application in this article deploys at 1078KB), when deployed with run-time packages, that total can shrink to something really puny (the sample mentioned deploys to 337KB under run-time packages). There's an additional benefit in using CAB file compression (more on this later). |
l | It can be N-Tier. In fact, I recommend this highly. Because the user has a network connection already (otherwise he or she would not be able to tag your Web site), there isn't anything that should prevent your application from being aware of a middle-tier server. Using TClientDataSet in your ActiveX application provides two benefits: It maintains the "Ivory Tower" of data control, and further "thins" the application by not requiring a BDE installation or configuration (all this is maintained at the middle tier). If the only requirements are the OCX, some run-time packages, and a copy of Dbclient.dll, your client should be getting downright skinny. |
2.Delphi提供的VCL元件轉換成ActiveX元件功能
l | VCL元件轉換成ActiveX元件-操作說明 | |||||
| Step 1. | File è New,選取ActiveX TabSheet點選ActiveX Control | ||||
| ||||||
| Step 2. | 按VCL Class Name Combo Box選取一個已installed於Delphi中的元件(eg. TBvIdleCheck),給予一個新的 | ||||
|
| New ActiveX Name | (eg. BvIdleCheckX), | |||
|
| implementation unit | (eg. BvIdleCheckImpl1.pas) | |||
|
| Project Name | (eg. BvIdleCheckAx.dpr) | |||
| ||||||
|
| 按OK | ||||
|
| 此時Delphi build會依據VCL元件程式碼內容產生對應的typed library程式碼BvIdleCheckAx_TLB.pas) | ||||
| Step 3. | Build Project | ||||
|
| 此時Delphi build產生BvIdleCheckAx.OCX元件 |
l | VCL元件轉換成ActiveX元件-機制說明 |
| Suppose your orignal VCL compoent named TFoo, Delphi Convert to ActiveX component named TFooX. TFooX inherited from the following two classes (TActiveXControl and IFooX): |
| |
| |
TFooX = class(TActiveXControl, IFooX) 新產生的ActiveX元件包含以下幾個重要函數: |
ActiveX methods | Description |
DefinePropertyPages
| 透過DefinePropertyPage()於此函數中定義客製化元件屬性頁內容(property page). eg. DefinePropertyPage(Class_FooXPage); |
EventSinkChanged
| 於此函數中放置設定ActiveX元件支援的EventSink程式碼. |
InitializeControl
| ActiveX元件初始化時回呼此函數 |
InitiateAction
| 客製化ActiveX元件相關的Action Link更新動作(類似MFC中的UpdateCommandUI功能) 當Application OnIdle時此函數會被回呼 |
3.能夠被轉換成ActiveX元件之VCL元件必須符合之條件
能夠被轉換成ActiveX元件之VCL元件必須符合以下條件 | |
l | The VCL control must be installed in Delphi. If the component isn't installed in the Component palette, it's not registered with Delphi |
l | The VCL control must descend from TWinControl. This may force you to alter the parentage of your VCL control, but there is usually a suitable alternative. For example, if you're trying to port a non-visual control to an ActiveX control, you might consider using TCustomControl as the ancestor, and provide a simple Paint method. |
l | Because an ActiveX control is based on OLE, the VCL control can only translate properties that have a corresponding native-OLE data type. In addition, you can provide an adapter to convert the non-standard data type into something that OLE can understand. For example, Delphi comes bundled with adapters, so OLE can use properties of types TString, TPicture, and TFont. If Delphi can't map a data type to a type that OLE can understand, Delphi will omit that property from the generated ActiveX control. You can add properties and methods to the ActiveX control manually by selecting Edit | Add To Interface, or editing the type library itself. Or, you can use the Type Library editor (by selecting View | Type Library) and have Delphi 3 do most of the work. |
因此在撰寫VCL元件時若考慮要轉換成為ActiveX元件時,即使為非視覺化VCL元件也要考慮由 TCustomControl來繼承,改寫要領參考8.6.4 |
4.VCL與ActiveX Control不相容之Data Type
注意:若程式碼參數或property中運用到Delphi特有的Data Type而與OLE不相容時, Delphi將VCL元件轉換成ActiveX Control時會忽略此參數或property,如此可會造成ActiveX功能之限制,以下列出不相容之處及解決方案 |
Delphi特有的Data Type
| 解決辦法: 轉換成ActiveX Control前需改寫
|
set property(集合屬性)轉換成ActiveX元件時會失效。例如 TStringGrid.Options轉換成ActiveX時於VB中看不到此propery | 新增Boolean property一一對應至集合屬性 範例參照: 下載範例 AxGrid.zip |
一些Delphi特有的class: TStrings, TList, TStream… 例如: TMemo.Lines, TListBox.Items這些TStrings property轉換成ActiveX 元件後無法利用 TStrings class member操作物件。 例如: Delphi中memo1.Lines.Add(s);敘述無法於 ActiveX元件中使用 | 封裝TStrings物件 (參 AxMemo.PAS): 例如: 原本例如: MemoX1.Lines.Add(s);可透過封裝隱藏 TStrings的存取方法。範例參照: 下載範例 AxMemo.zip function TAxMemo.Add(s: string): integer; begin
Result:=Lines.Add(s); end; |
VCL元件的RunTime、DesignTime檢查敘述無法辨識 例如: 轉換成ActiveX 元件後以下的statement 無法正確執行 if (csDesigning in ComponentState)then … | Delphi轉換的成的ActiveX元件繼承TActiveXControl 可利用其member ClientSite的 IAmbientDispatch.UserMode判別 eg. 以下函數用來判別ActiveX元件是否處於Design Time或 Run Time之狀況 function IsAxDesignTime(ax: TActiveXControl): boolean; begin //[ Result:=true; if (ax.ClientSite<>nil) and (ax.ClientSite as IAmbientDispatch).UserMode then Result:=false; end; // ] IsAxDesignTime |
l | 實作1: 封裝隱藏TMemo.Lines存取方法 |
| 將Delphi的TMemo改成TAxMemo並封裝隱藏TMemo.Lines存取方法。 |
l | 實作2:新增相對應於元件集合屬性之Boolean prooerties |
| 將TStringGrid原本的集合屬性 property Options: TGridOptions; 改成加上 optXXX Boolean屬性一一對應至TGridOptions集合屬性以便轉成ActiveX元件時可相容 |
5.改寫非視覺化VCL元件以利轉換至ActiveX元件
舉 TBvIdleCheck 元件為例原本 TBvIdleCheck繼承於 TComponent現需將其改寫使元件於Design Time時模仿TComponent一般只顯示一個小Icon, Run Time時為invisible。改寫實請依照以下範例套用: | |
l | 修改使元件繼承TCustomControl |
l | 新增一個override的 Paint方法,於Design Time時畫一個小Icon |
l | 新增一個處理WM_SIZE之程序,防止物件於Design Time時被Resize |
l | 判斷DesignTime時元件為Visible, RunTime時Invisible |
l | 於Delphi中安裝註冊改寫完畢的VCL元件 |
l | 測試VCL元件 |
l | 利用Delphi ActiveX Control Wizard將VCL元件轉換成ActiveX元件 |
l | 小幅度修改BvIdleCheckImpl1.PAS |
l | 註冊OCX元件(BvIdleCheckAx.OCX) |
l | 於VB中測試新產生的元件 |
l | 修改使元件繼承TCustomControl |
| TBvIdleCheck = class(TComponent) 改為 TBvIdleCheck = class(TCustomControl) |
l | 新增一個override的 Paint方法,於Design Time時畫一個小Icon |
| TBvIdleCheck = class(TCustomControl) private … protected procedure Paint; override; … end; procedure TBvIdleCheck.Paint; begin //這是我寫的副程式,Design Time時模仿TComponent一般只顯示一個小Icon PaintComponentIcon(Self, Self.Canvas); end; |
l | 新增一個處理WM_SIZE之程序,防止物件於Design Time時被Resize |
| TBvIdleCheck = class(TCustomControl) private … protected procedure WMSize(var Message: TWMSize); message WM_SIZE; … end; procedure TBvIdleCheck.Create(AOwner: TComponent); begin … Width:=24; Height:=24; //固定object size 為 24x24 end; // 收到WM_SIZE訊息時固定object size 為 24x24 procedure TBvIdleCheck.WMSize(var Message: TWMSize); {$J+} const s_bOnOp: boolean=false; begin if s_bOnOp then exit; s_bOnOp:=true; Width:=24; Height:=24; s_bOnOp:=false; end; |
l | 判斷DesignTime時元件為Visible, RunTime時Invisible |
| // 元件被載入Loaded時, 設定物件寬高為24 // 判斷若是RunTime時è設定物件為invisible procedure TBvIdleCheck.Loaded; begin … if not (csDesigning in ComponentState) then Visible:=false
else Invalidate; …
end; |
l | 於Delphi中安裝註冊改寫完畢的VCL元件 | |||
| 於Delphi中ComponentàInstall Compoent 安裝TBvIdleCheck元件 | |||
| ||||
|
l | 測試VCL元件 | |||
| ||||
|
利用Delphi ActiveX Control Wizard將VCL元件轉換成ActiveX元件 | ||||
| 參照8.6.1 利用將VCL元件轉換成ActiveX元件,將TBvIdleCheck元件包裝成OCX元件 | |||
| ||||
| 請注意公司內定之Naming Convention : (Project規定存放至 /sdk/ActiveX/ 路徑中) |
| 以TBvIdleCheck為例
| Delphi產生之預設名稱
| 將之改稱我們的Naming Convention
| |
| New ActiveX Name | BvIdleCheckX | BvIdleCheckX | |
| Implemntation Unit | BvIdleCheckImpl1.pas | BvIdleCheckImpl1.pas | |
| Project Name | BvIdleCheckXControl1.dpr | BvIdleCheckAx.dpr (注意: Project Name不得與ActiveX Name同名)
| |
| Build Project後產生 BvIdleCheckAx.OCX 註冊OCX新元件: 執行 TRegSvr BvIdleCheckAx.OCX | |||
|
|
|
|
|
l | 小幅度修改BvIdleCheckImpl1.PAS | ||
| // 於 EventSinkChanged() 決定物件是否為 visible
procedure TBvIdleCheckX.EventSinkChanged(const EventSink: IUnknown); begin | ||
| // IsAxDesignTime() 這是我包裝的副程式,可以檢查繼承自
// TActiveXControl的 ActiveX元件是否處於DesignTime或RunTime
// 加上此行, 若ActiveX元件為 Design Time èVisible, // Run Time èinvisible
FDelphiControl.Visible:=IsAxDesignTime(Self); | ||
| end; … | ||
| // 於 TActiveXControlFactory constructor
// 給ActiveX元件狀態初始化值為 Invisible At Run Time
initialization | ||
| TActiveXControlFactory.Create( | ||
| ComServer, TBvIdleCheckX, TBvIdleCheck, Class_BvIdleCheckX, 1, '{EEB41A8A-5EFE-4759-84A5-27DD7BCA0886}', OLEMISC_INVISIBLEATRUNTIME, //給ActiveX元件狀態初始化值 tmApartment); | ||
| end. | ||
l | 註冊OCX元件(BvIdleCheckAx.OCX) | |||
|
l | 於VB中測試新產生的元件 |
|