樂趣無窮,可能無限的新技術-Web Service
雖然電子商務的狂熱在最近似乎有減溫的現象,讓許多人能夠回歸到正常的步調之中,不過隨著電子商務而發展的軟體技術並沒有稍停腳步,反而更加蓬勃發展。因為由這些技術創造的應用早已成為許多人生活的一部份,甚至是開啟未來趨勢的基石。在目前最熱門且最被看好的技術便是所謂的Web Service了,那麼什麼是Web Service呢?
簡單的說,Web Service是一種想把全世界的Internet/Intranet變成一個虛擬計算環境的觀念和技術。在由Web Service組成的虛擬環境中使用者可以任何的用戶端軟體,例如瀏覽器,一般的Window或是Java應用程式或是電子行動設備等,來呼叫Web Service提供的服務。而Web Service本身則可以由任何的技術實作,例如開發者可以使用Delphi,Java,C/C++或是C#等的語言和工具來開發。
Web Service是建立在開放和標準的規格之上,允許異質的用戶端呼叫以使用它提供的服務。因此各種異質的用戶端必須使用一種共通的溝通標準才能夠順利的和由各種不同技術實作的Web Service互通。目前最流行而且最具潛力的溝通標準當屬SOAP了。
SOAP (Simple Object Access Protocol)是由Don Box起草,並且獲得IBM,Microsoft,Lotus和UserLand等大型公司支持而成為W3C標準之一的通訊協定規格。從SOAP的名稱中我們便可以知道它是讓用戶端呼叫遠端物件服務的一種機制。SOAP以XML標準封裝呼叫遠端服務的格式,有別於其他分散式物件模型呼叫特定的呼叫格式,例如CORBA的GIOP以及DCOM的ORPC。由於SOAP以XML封裝呼叫格式,因此它可以使用任何的實體傳輸層來傳送,例如HTTP,TCP或是SMTP等。也許讓我們使用一個簡單的概例來說明會讓各位更容易的瞭解。
假設現在我在Linux平台上以Java語言實作了一個Web Service,這個Web Service提供了一個服務GetSystemTime。這個服務接受一個使用者名稱和一個密碼,如果成功的登錄之後,這個服務便會回傳Linux平台目前的系統時間。那麼我可以使用Delphi以SOAP的標準封裝使用者名稱和密碼來呼叫這個在Linux平台上的GetSystemTime服務。例如下面就可能是由SOAP封裝的格式:
GordonLi
xx12yh_49
藉由SOAP,Delphi的用戶端應用程式可以輕易的呼叫Linux平台上的Web Service,而無需關心這個Web Service是由什麼技術實作的,或是存在於任何地方,更不需要以特定的二進位格式來封裝呼叫。因此藉由Web Service和SOAP,開發者可以輕易的整合各種異質平台,異質分散式物件模型,而充分的利用所有的計算資源,這在以前是不可能輕易做到的,同時Web Service和SOAP也為未來的發展開啟了另一扇的大門。目前Web Service已經在國外快速的蓬勃發展,各種Web Service也已經在Internet上供人使用,例如搜尋MP3的服務,或是查詢全世界各地氣象的服務等。相信Web Service和SOAP也將很快的在國內發展起來,也終將成為軟體開發人員必備的軟體技能之一。
Web Service本身包含了許多的意義,觀念和技術,在RUN!PC 2001年5月份的『解析Web Service的技術內容與意涵』一文中已經對於Web Service和SOAP有基本的介紹,讀者可以參考該文的說明。
本篇文章的內容在於討論Web Service的技術架構和實作的技巧,並且首先以Delphi 6做為說明如何實際的開發Web Service以及用戶端應用程式來呼叫Web Service。接著再說明如何使用Delphi開發的用戶端應用程式來呼叫Internet上由Java開發的Web Service,來向各位讀者展示Web Service和SOAP的開放性以及標準性。當我們成功的在本地機器呼叫了在世界上某一個角落,由某一個人使用某一種工具開發的Web Service時,相信讀者也會讚嘆Web Service和SOAP所帶來的無限可能和下一波的軟體技術的革命。
Web Service和SOAP的架構
那麼我們要如何才能夠知道每一個Web Service提供的服務?要如何才能夠呼叫到Web Service?又要到那裡找到適合的Web Service呢?簡單的說,Web Service提供的服務是以所謂的WSDL(Web Service Description Language)標準來敘述的,只要我們能夠取得特定Web Service的WSDL,就可以從其中瞭解它提供的服務,以及如何呼叫這個Web Service。
最後一個問題是如何找到適用的Web Service,在目前全世界已經有人公佈了許多的Web Service供人呼叫使用。此外IBM和Microsoft等公司也正在研擬所謂的UDDI標準以提供註冊,搜尋,交換和使用Web Service的標準,開發人員可以藉由UDDI找到需要的Web Service,當然我相信許多的Web Service將會由開發人員根據自己的需求而使用工具開發出來。
說了那麼多,可能讀者會想要知道到底Web Service要如何實作?要使用什麼語言或是工具才能夠撰寫的呢?事實上Web Service並不限定任何特定的工具或是語言才能夠開發,簡單的說你可以使用任何的工具或是語言來開發,甚至可以使用ASP/JSP等稿本語言(Script Language)來實作。當然,開發人員也可以結合各種不同的軟體技術和元件架構來開發。
下圖是以比較實體架構的觀點來敘述Web Service的觀念。圖中的用戶端藉由SOAP和HTTP通訊協定,透過Web Service Provider找到適合的Web Service,再呼叫它。而實體的Web Service可以是實作在Window平台的MTS/COM+或是.NET物件,也可以是實作在Linux\UNIX平台中的CORBA或是EJB物件。這個觀點是以各種元件模型來實作Web Service。
圖一 Web Service的架構示意圖
至於下圖則是以更細微的觀點來看Web Service的實體架構。在這個圖形中呈現了Web Service可以由ASP/JSP或是CGI,ISAPI的形式來實作,以服務用戶端的請求。開發人員可以把所有的Web Service企業邏輯實作在ASP/JSP/CGI/ISAPI/DSO之中。或是只把回應用戶端請求的邏輯實作在ASP/JSP/CGI/ISAPI/DSO之中,而把真正的企業邏輯實作在後端的元件模型之中,或是後端的應用程式之中,例如Delphi的DataSnap伺服器。
圖二 Web Service的實作架構圖
從上面的討論中可以知道,開發人員可以使用任何的技術實作Web Service,只需要根據標準輸出Web Service,就可以由用戶端呼叫使用之。
討論完了觀念之後,現在讓我們回到實際的實作層次中。雖然Web Service可以由任何的技術實作,但是開發人員仍然需要選擇一種方式來開發。開發Web Service除了實作Web Service的企業邏輯之外,也必須提供Web Service的WSDL,並且分發Web Service。由於目前使用Web Service的情形仍然以SOAP/HTTP型式呼叫,因此許多的Web Service也是以Web應用程式的型態實作,例如把Web Service實作成CGI或是ISAPI/DSO的型式,不過只要能夠處理HTTP的呼叫,Web Service也可以實作成一般的獨立應用程式,這一點是讀者必須瞭解的。
開發Web Service雖然不是非常的困難,但是它仍然需要許多的開發步驟和處理程序,這也仍然需要花費一些開發成本。在Borland最新推出的Delphi 6中,Borland特別提供了7個直接的Web Service元件,三個Web Service精靈以及其他數個相關的VCL元件來幫助開發人員快速的開發Web Service,更棒的是,開發人員也可以再結合Delphi原有的MTS/COM+/CORBA/EJB元件模型開發更具延展性的Web Service。下圖便是Delphi 6中直接和Web Service相關的Web Service元件組。
圖三Delphi 6提供的WebServices元件
這7個Web Service元件可以讓開發人員呼叫遠端Web Service,自動產生Web Service的WSDL,以及進行SOAP/HTTP和Object Pascal語言之間的繫結(Binding),以便讓Delphi的程式師能夠使用Object Pascal直接處理SOAP之中的訊息。
下圖則顯示了在用戶端應用程式和遠端Web Service之間如何藉由這些元件溝通,以及每一個元件之間的關係。由於Delphi 6是Window平台上的開發工具,因此它使用了Wininet.dll來傳送HTTP封包資訊。
圖四 Delphi 6 WebServices元件的功能示意圖
從上圖可以知道,Delphi 6用戶端應用程式藉由THTTPRIO呼叫遠端Web Service,而TOPToSoapDomConvert可以把Object Pascal的呼叫和參數自動轉換為SOAP封裝的格式資訊,再藉由THTTPReqResp傳送HTTP封包。而在伺服端THTTPSoapDispatcher則負責處理用戶端傳送來的SOAP/HTTP資訊,並且透過THTTPSoapPascalInvoker元件來自動啟動能夠處理這個SOAP/HTTP請求的Object Pascal程式碼。至於TWSDLHTMLPublish則能夠自動的根據Delphi實作的Web Service來產生WSDL並且輸出此WSDL讓用戶端應用程式能夠使用這個WSDL來呼叫Web Service。
說明了Delphi 6中有關Web Service的元件和其功能之後,現在就讓我們看看在Delphi 6中開發Web Service的步驟。下圖便是在Delphi 6中開發Web Service簡易的步驟:
圖五 使用Delphi 6開發 Web Service的步驟
首先程式師必須撰寫Web Service的核心邏輯,然後定義此Web Service的WSDL,以便讓用戶端能夠遵循標準呼叫。在實作完Web Service之後,接著程式師就可以使用Delphi 6提供的WebServices元件組來實作用戶端應用程式,並且藉由WSDL來呼叫Web Service。
在實作Web Service用戶端應用程式時,Delphi 6提供了非常彈性的方法,允許程式師使用Early Binding或是Late Binding,不像某些解決方案只允許使用Late Binding。這種設計可以讓程式師在開發Web Service解決方案時可以根據執行效率或是執行彈性來決定使用Early Binding或是Late Binding。
為了讓讀者能夠真正的瞭解如何開發Web Service系統並且範例Delphi 6在SOAP和Web Service方面的強勁功能,就讓我們使用一個實際的範例來說明如何使用Delphi 6快速開發Web Service和用戶端應用程式,並且結合資料庫來提供用戶端資訊。這個範例Web Service是把MYESSAYS資料表中的所有我寫的文章輸出給用戶端以便查詢資訊,而且不管用戶端是瀏覽器,一般的Window應用程式,或是Linux下的應用程式都可以。下圖便是儲存這些文章資訊的畫面:
圖六 範例資料表
至於這個範例的整體架構如下圖所示。文章資訊是儲存在InterBase之中,並且藉由Delphi 6的dbExpress來技術存取。至於實作Web Service的主體則是一個由Delphi 6撰寫的簡單Web應用程式。最後我們實作一個原生視窗應用程式藉由SOAP來呼叫此Web應用程式實作的Web Service。
圖七 實作Web Service的架構圖
步驟1 – 開發SOAP伺服端應用程式
首先程式師可以在Delphi中使用SOAP Server Application精靈來開發Web Service伺服器。在Delphi 6中我們只需要點選File|New|Others功能表,然後點選WebServices頁次即可看到下圖的畫面,然後再點選SOAP Server Application圖像。
圖八 Delphi 6的SOAP Server Application精靈
在點選了SOAP Server Application圖像之後,Delphi會詢問程式師要以那一種的實體型態來實作Web Service,如下圖所示。程式師可以選擇欲實作的程式型態,例如在這個範例中我選擇以Web App Debugger executable來實作,因為這個程式型態可以讓我們在開發Web Service時能夠輕易的除錯。當然,程式師也可以選擇以一般的Window應用程式來實作Web Service,而不使用SOAP Server Application精靈提供的下列實體型態的程式。
圖九 以Delphi 6的Web App Debugger應用程式的形式開發Web Service
在點選了圖九的程式型態和Ok按鈕之後,Delphi便會自動幫助程式師產生如下的Web模組。
圖十 Delphi 6建立建立的Web Module以及WebServices元件
在圖十的Web模組之中,Delphi自動產生的THTTPSoapDispatcher元件可以讓Web Server自動呼叫此應用程式,而TWSDLHTMLPublish元件則可以自動產生敘述此Web Service的WSDL內容。
圖十一 範例Web Service的主表單
現在再讓我們在這個Web Service程式的主表單中加入一個TLabel並且設定它的Caption特性值為『我的第一個Web Service』,如上圖所示。
步驟 2 – 定義Web Service的服務介面並且實作它
接下來的步驟便是真正的實作此Web Service。首先在Delphi 6中建立資料模組,並且使用dbExpress連結到InterBase:
圖十二 範例Web Service的資料模組,它使用dbExpress元件存取InterBase
點選File|New|Unit功能表定義如下的IMyEssays介面,這個介面定義了此Web Service提供的服務,用戶端應用程式可以呼叫GetEssayTitles函式取得所有文章的資訊,而這些資訊是儲存在TEssaysInfos的陣列中,這個方法展示了Delphi 6的Web Service可以處理複雜的資料型態。至於GetEssayContent則可以根據用戶端傳遞來的文章ID而回傳此文章的內容給用戶端應用程式。
unit uMyEssaysInf;
interface
uses
Types, XSBuiltIns, uEssaysInfo;
type
IMyEssays = interface(IInvokable)
['{1C8ABA87-455B-4430-9881-239F5FFE7F49}']
function GetEssayTitles : TEssaysInfos ; stdcall;
function GetEssayContent(const iID : Integer) : String; stdcall;
end;
implementation
uses
InvokeRegistry;
initialization
InvRegistry.RegisterInterface(TypeInfo(IMyEssays));
end.
接著我們定義儲存文章資訊的資料結構。為了讓文章資訊能夠自動傳遞回用戶端,我們可以在Delphi 6中從TRemotable類別繼承子類別,如此一來Delphi 6便會自動幫助我們處理資料Marshalling的問題。最後必須呼叫Delphi 6提供的RemClassRegistry物件來註冊這些類別。
unit uEssaysInfo;
interface
uses
InvokeRegistry, XSBuiltIns;
type
TEssaysInfo = class(TRemotable)
private
FEssayID : Integer;
FEssayTitle : WideString;
published
property EssayTitle : WideString read FEssayTitle write FEssayTitle;
property EssayID : Integer read FEssayID write FEssayID;
end;
TEssaysInfos = array of TEssaysInfo;
implementation
initialization
RemClassRegistry.RegisterXSClass(TEssaysInfo, 'http://www.w3.org/2001/XMLSchema', 'TEssaysInfo', '',False );
RemTypeRegistry.RegisterXSInfo(TypeInfo(TEssaysInfos), 'http://www.w3.org/2001/XMLSchema', 'TEssaysInfos');
finalization
RemClassRegistry.UnRegisterXSClass(TEssaysInfo);
RemTypeRegistry.UnRegisterXSInfo(TypeInfo(TEssaysInfos));
end.
現在到了實作ImyEssays介面的時候了,我們定義TMyEssays類別從TInvokableClass繼承下來並且實作IMyEssays介面。TInvokableClass類別可以讓用戶端從遠端呼叫。最後同樣呼叫InvRegistry物件註冊TmyEssays類別,以便讓THTTPSoapDispatcher元件可以啟動。至於IMyEssays介面之中的GetEssayTitles方法則是使用dbExpress從InterBase中讀取所有的資料,並且把文章的ID和名稱儲存在一個TEssaysInfo物件中,再把TEssaysInfo物件儲存在TEssaysInfos陣列中,最後回傳此陣列給用戶端。
unit uMyEssaysImpl;
interface
uses
SysUtils, Classes, InvokeRegistry, XSBuiltIns, uMyEssaysInf, uEssaysInfo, DB, HTTPProd, udmMyEssays, DBXpress;
type
TMyEssays = class(TInvokableClass, IMyEssays)
private
procedure CreateDataModule;
procedure FreeDataModule;
public
{ IISAPITutorials }
function GetEssayTitles: TEssaysInfos; stdcall;
function GetEssayContent(const iID : Integer) : String; stdcall;
end;
implementation
{ TISAPITutorials }
procedure TMyEssays.CreateDataModule;
begin
dmMyEssays := TdmMyEssays.Create(nil);
end;
procedure TMyEssays.FreeDataModule;
begin
if (Assigned(dmMyEssays)) then
begin
dmMyEssays.Free;
dmMyEssays := nil;
end;
end;
function TMyEssays.GetEssayContent(const iID: Integer): String;
begin
Result := '尚未實作, 請待續!!!
';
end;
function TMyEssays.GetEssayTitles: TEssaysInfos;
var
iNo : Integer;
iID : Integer;
eInfo : TEssaysInfo;
TD: TTransactionDesc;
begin
CreateDataModule;
TD.TransactionID := 1;
TD.IsolationLevel := xilREADCOMMITTED;
try
dmMyEssays.sconnMyEssays.StartTransaction(TD);
iNo := dmMyEssays.sdsMyEssays.RecordCount;
SetLength(Result, iNo);
iID := -1;
with dmMyEssays.sdsMyEssays do
begin
while not Eof do
begin
Inc(iID);
eInfo := TEssaysInfo.Create;
eInfo.EssayID := FieldByName('EID').Value;
eInfo.EssayTitle := FieldByName('ETITLE').Value;
Result[iID] := eInfo;
Next;
end;
end;
finally
dmMyEssays.sconnMyEssays.Commit(TD);
FreeDataModule;
end;
end;
initialization
InvRegistry.RegisterInvokableClass(TMyEssays);
end.
現在這個能夠處理複雜資料的Web Service便藉由Delphi 6提供的WebServices元件和精靈完成了,接下來就是開發用戶端應用程式來呼叫此Web Service以取得文章資訊了。
步驟 3 – 開發用戶端應用程式呼叫Web Service
使用Delphi 6開發呼叫Web Service的用戶端應用程式更簡單,因為Delphi 6提供的WebServices元件組中的THTTPRIO元件實在是太方便了,我們只要使用物件檢視器設定THTTPRIO元件的WSDLLocation特性值為欲呼叫的Web Service的WSDL,那麼THTTPRIO元件便可以自動的處理所有呼叫Web Service的細節。
例如下面便是使用Delphi 6開發的用戶端應用程式,在這個應用程式的主表單上使用了一個THTTPRIO元件,並且在它的WSDLLocation特性值中輸入剛才開發的Web Service的WSDL檔案的位址。
圖十三 範例用戶端應用程式的主表單
接著在『 我的文章』按鈕的OnClick事件處理函式中撰寫如下的程式碼:
procedure TForm2.BitBtn1Click(Sender: TObject);
var
oriCursor : TCursor;
eInfos : TEssaysInfos;
iCount : Integer;
lStart, lEnd : Longint;
begin
ShowCaption;
StatusBar1.Panels[0].Text := '呼叫Web Service中...';
StatusBar1.Refresh;
lvMyEssays.Items.BeginUpdate;
lvMyEssays.Items.Clear;
oriCursor := Screen.Cursor;
Screen.Cursor := crHourglass;
lStart := GetTickCount;
try
eInfos := (HTTPRIO1 as IMyEssays).GetEssayTitles;
for iCount := low(eInfos) to High(eInfos) do
begin
with lvMyEssays.Items.Add do
begin
Caption := eInfos[iCount].EssayTitle;
Data := Pointer(eInfos[iCount].EssayID);
end;
end;
finally
lEnd := GetTickCount;
ShowRunTime(lStart, lEnd);
lvMyEssays.Items.EndUpdate;
StatusBar1.Panels[0].Text := '完成呼叫Web Service';
StatusBar1.Refresh;
Screen.Cursor := oriCursor;
end;
end;
procedure TForm2.ShowCaption;
begin
lblCaption.Caption := '太棒了, 我的第一個Web Service程式';
end;
procedure TForm2.ShowRunTime(const lStart, lEnd: Integer);
begin
StatusBar1.Panels[1].Text := FloattoStr((lEnd - lStart) / 1000.0) + '秒';
end;
上面的程式碼藉由THTTPRIO元件呼叫IMyEssays介面的GetEssayTitles方法,取得TEssaysInfos陣列,再從陣列中一一的取出每一篇文章的名稱,最後再填入到主表單中的TListView元件之中。下圖就是執行此用戶端應用程式呼叫Web Service伺服器,並且取得所有文章資訊的畫面。從這麼簡單的數個步驟中,我們已經使用Delphi 6開發了一個真正的Web Service應用系統。
圖十四 範例用戶端應用程式呼叫Web Service得到資料的畫面
雖然這是我們使用Delphi 6建立的第一個Web Service,但是這個範例Web Service展示了Delphi 6的SOAP/Web Service解決方案能夠輕易的傳遞複雜的資料型態,因為在範例Web Service中是使用陣列的型態來傳遞所有的文章資訊。Delphi 6的SOAP/Web Service技術絕不是像一些工具只提供簡單的SOAP/Web Service解決方案,而是充分的提供了一般和複雜的應用程式,並且能夠整合各種元件模型,是目前最具威力,也是最先進的SOAP/Web Service開發工具之一。
Delphi 6除了提供強勁的Web Service開發功能之外,新的Web App Debugger不但可以幫助程式師除錯Web應用程式之外,也可以幫助程式師監督用戶端應用程式和Web Service之中傳遞的資訊。這些資訊包含了SOAP的封包,以及Web Service伺服器回傳回用戶端的所有SOAP封包。這對於程式師學習SOAP和解析SOAP Payload都非常有幫助。例如下圖便是我使用Web App Debugger檢視此範例Web Service應用系統傳遞的SOAP Payload。
圖十五 Delphi 6的Web App Debugger可以顯示用戶端和伺服端之間所有的訊息
為了證明Web Service的威力和相通性,目前全世界已經有許多人成立了各種Web Service的網站,讓開發人員能夠測試Web Service。例如現在WWW.XMethods.COM便是提供各種Web Service的網站之一。這個網站羅列了許多的Web Service,下圖便是這個網站目前提供的Web Service。
圖十六 Internet上的XMethods(www.xmethods.com)提供了許多的Web Service供人呼叫使用
現在讓我們使用Delphi 6開發一個用戶端應用程式來透過Internet/Intranet呼叫遠端由Java實作,執行在Apache上的一個查詢美國各州氣溫的Web Service。下圖是這個Web Service的詳細資訊,這個Web Service的作者甚至提供了Java用戶端應用程式展示如何呼叫這個氣溫Web Service,不過現在我想使用Delphi的用戶端應用程式來呼叫,而不是Java。
圖十七 XMethods上的眾多Web Service之一,Temperature Web Service
下圖便是在我的機器中使用Delphi 6開發的用戶端應用程式,藉由Delphi 6的WebServices元件組來呼叫這個位於遠端,我也不知道在什麼地方的氣溫Web Service的結果畫面。
從下圖中可以證明,雖然我並不知道這個Web Service在那裡,我仍然可以藉由Web Service的標準介面敘述WSDL來使用它,即使它是使用Java實作的,並且執行在Apache之上。
圖十八 Delphi實作的用戶端應用程式呼叫執行在遠端Apache上的Web Service
希望上面的內容可以讓各位讀者瞭解Web Service和SOAP在應用上的潛力以及Delphi 6提供的元件技術可以讓開發人員快速而且輕易的實作出各種威力強大的Web Service。
也許藉由Web Service和SOAP的出現,也會對於目前應用系統架構產生巨大的影響。例如現在『供應鏈』軟體非常的流行,但是許多的供應鏈軟體在整合上,中,下游廠商時,經常會需要所有的廠商使用相同的平台以及基層軟體。但是對於下游廠商而言,可能無法像上游廠商一樣使用昂貴的設備,例如UNIX BOX和大型ERP軟體,許多的下游小廠也許只能使用Window NT或是Linux平台。不過現在Web Service和SOAP可以提供非常完善的解決方案,就如同下圖顯示的一樣,下游廠商可以只使用ASP提供簡單的Web Service讓他的中游廠商呼叫。而上游廠商則可以在UNIX Box中藉由大型的ERP軟體呼叫中游廠商執行在Window 2000中的BizTalk Service。如此一來不但每一個廠商都可以選擇最適合的執行平台和軟體,也可以藉由Web Service和SOAP整合上,中,下游廠商而提供一個及時且完備的供應鏈。Web Service和SOAP正為軟體帶來無限的發展契機。
圖十九 Web Service的應用架構之一
雖然SOAP和Web Service目前已經成為標準並且也已經被世界廠商所接受和支援。但是SOAP和Web Service仍然是在成長期,功能規格仍然在繼續的改善和強化之中,因此SOAP和Web Service的變化也在預期之中。Delphi 6實作的SOAP和Web Service似乎是比較偏向IBM和Java的陣營,因此Delphi 6能夠很容易的和Java以及PHP,Perl等SOAP/Web Service解決方案整合。至於Microsoft的SOAP和Web Service則稍有不同,需要程式師特別注意一下。
如果下次有時間的話,那麼就讓我們繼續討論Microsoft的SOAP Toolkit以及.NET之中的SOAP解決方案,並且比較Delphi和它們之間的差異以及如何整合這些不同的SOAP實作技術。
相关帖子:
看看什么是软件服务时代!大家快来看看!
delphi6 爆发还是灭亡?
李维:我的回忆和一些有趣的事
看IT风云变幻,宝兰与微软背后的故事,
看宝兰, 一年之间连续推出kylix1.0 ,interbase6.0, delphi6,jbuilder5 ,c++builder6也不日即出,敬请关注宝兰2001年与微软对绝的杀手锏kylix
李维:Windows 原生開發工具的瑰寶 – Delphi 6
我推荐的帖子
明修栈道,暗渡陈仓,陈宽达点指开发工具
这篇文章不算精彩,但是引来的评论却很精彩!