DELPHI 6 Update 2# 的 Bug 及其解決方案

來源:互聯網
上載者:User
DELPHI 6 Update 2# 的 Bug 及其解決方案

    Borland發布了對 Delphi 6 意義重大的第二個補丁,在SOAP/Web Service開發方面有了很大的增強,詳見《DELPHI 6.02 搶先研究 -- BizSnap/SOAP/WebService 之四 -- 補丁2#的意義》。不過有一個方面我在那篇文章裡沒有說到,那就是用 SOAP 進行多層應用開發,當時我對這部分只是大致看了一下,沒發現與補丁前相比有什麼太多的不同,所以也沒在意,直到不久前,一位叫 liaoqian 的網友給我發來 Mail 詢問有關這方面的問題時,我才注意到 Delphi 6 Update 2# 在這部分有相當大的變化(當然不是表面上的),並且存在一個小 Bug 。

    首先仿照《DELPHI 6 搶先研究 -- BizSnap/SOAP/WebService 之三 -- 用 SOAP 實現三層資料庫應用》 中的例子做一個 WebService 三層資料應用。當出現所示的自動詢問是否產生一個服務端介面的對話方塊時選擇“No”,然後建立 SOAP Server Data Module ,其它操作與《之三》相同。完成之後在IE中輸入:http://localhost:1024/Demo3.wadSoapDemo3/wsdl 將可以看到如下的介面列表,相比《之三》一文,可以看出增加了一個 IAppServerSOAP 介面,並且其 IWSDLPublish 介面的 Namespace URI 也有不同。

Port Type Namespace URI Documentation WSDL
IAppServer urn:Midas-IAppServer IAppServer
IAppServerSOAP http://www.borland.com/namespaces/Types IAppServerSOAP
ISoapDemo3DM urn:SvrDMSoap-ISoapDemo3DM ISoapDemo3DM
IWSDLPublish http://www.borland.com/namespaces/Types Lists all the PortTypes published by this Service IWSDLPublish

    如果注意的話還可以發現一點問題,即:輸入 http://localhost:1024/Demo3.wadSoapDemo3 不會看到 Delphi 6.02 寫的一般 WebService 那樣的一個頁面,這有點不正常。
到編寫用戶端程式的時候,問題就出現了:在 ClientDataSet 的 ProviderName 屬性中下拉,將不會看到任何可用的 ProviderName ,但一閃而逝的服務端表單表明服務端是運行了的。通過察看 Web App Debugger 的記錄資訊可以看到如下的 SOAP 錯誤響應:

<?xml version="1.0" encoding='UTF-8'?><soap-env:envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" > <soap-env:body>  <soap-env:fault>   <faultcode>SOAP-ENV:Server</faultcode>   <faultstring>No invokable class registered that implements interface SOAPMidas.IAppServerSOAP of (soap action/path) "http://www.borland.com/namespaces/Types-IAppServerSOAP"   </faultstring>  </soap-env:fault> </soap-env:body></soap-env:envelope>

    注意其中 faultstring 標記中的資訊,它說明了錯誤的原因是“沒有可調用的登入的 IAppServerSOAP 介面實作類別”。如果不理睬這個問題,直接把 DataSetProvider1 寫到 ClientDataSet1 的 ProviderName 屬性中,再將 ClientDataSet1 的 Active 屬性設定為 true 將直接出現一個異常,其內容便是上面的 faultstring 的內容。
    我非常喜歡包括 Delphi 在內的 Borland 的幾乎所有產品,也非常信任 Borland ,這種感情來源於我七八年來使用過的數十個不同的 Borland 的產品及其不同版本,所以我不喜歡一些人在一碰到問題時就沒理由地埋怨開發工具有 Bug 。雖然有很多傳聞說 Borland 的產品 Bug 如何的多,其實有軟體就有 Bug ,我也不是說 Borland 就一定沒有 Bug , Borland 當然也有 Bug ,在本站就曾幾次提到,難道 Microsoft 就沒有 Bug 嗎?
    但這一次,我幾乎可以肯定是 Delphi 6 Update 2 所帶來的 Bug ,因為在打補丁2#之前,我也是完全按照上面的做法一步步做的,沒有任何問題,打了補丁2#就出現了這樣的問題。即便如此,我還是要找到具體的證據來證明這是一個 Bug 並要找到解決的辦法,所以我讀了 Delphi 6.02 的部分 SOAP 相關原始碼,並進行了調試。

附:《設定源碼察看和調試》
    SOAP 部分源碼察看設定:在 Delphi 6 中選擇 Tools|Environment Options 選中 Library 頁,在 Browsing path 中加入路徑:$(DELPHI)/source/Soap ,然後就可以在程式中通過 Ctrl+MouseLeftButton 快速跳到相應的 SOAP 源碼檔案中了。
    源碼調試設定:在 Delph 6 中選擇 Project|Options 選中 Compiler 頁,將其中 Debugging 框中的 Use Debug DCUs 一項選取後,重新編譯器,即可調試進入 VCL 的源碼中了。

    通過跟蹤調試發現異常發生在 SOAPHTTPPasInv 單元中的 Procedure THTTPSoapPascalInvoker.DispatchSOAP 中:

      ...      if IntfInfo = nil then        raise Exception.CreateFmt(SInvInterfaceNotReg, [SoapAction])      else if InvClassType = nil then        RaiseNoIntfException(SoapAction)      ...

    因為其中 InvClassType 為 Nil ,所以導致了那個異常。這提供的資訊和前面的出錯資訊其實是一樣的:即 IAppServerSOAP 介面的實作類別不存在或未註冊。所以我又找到了 IAppServerSOAP 介面的定義,在 SOAPMidas 單元中:

  IAppServerSOAP = interface(IInvokable)    ['{C99F4735-D6D2-495C-8CA2-E53E5A439E61}']    function  SAS_ApplyUpdates(...): OleVariant; stdcall;    function  SAS_GetRecords(...): OleVariant; stdcall;    function  SAS_DataRequest(...): OleVariant; stdcall;    function  SAS_GetProviderNames: TWideStringDynArray; stdcall;    function  SAS_GetParams(...): OleVariant; stdcall;    function  SAS_RowRequest(...): OleVariant; stdcall;    procedure SAS_Execute(...); stdcall;  end;

    熟悉 MIDAS 技術的人看了這段代碼,立即可以發現它與 IAppServer 介面極其相似,只是在 IAppServer 中,那些方法都是以 AS_ 開頭,而這裡是以 SAS_ 開頭。那麼為什麼既然已經有了一個 IAppServer 介面了, Borland 還要在 Update 2 加入這麼一個 IAppServerSOAP 介面呢?在打補丁2之前,我也看過 SOAP 的部分代碼,那時的 SOAP Server Data Module 是從 IAppServer 派生的,雖然 IAppServer 並不是從 IInvokable 派生的(按 Delphi 6 的要求,所有 SOAP 介面都要從 IInvokable 介面派生),但其實 IInvokable 是直接從 IInterface 派生的,與 IInterface 完全相同,而 IInterface 其實就是所有 COM 介面的祖先介面--IUnknown,而 IAppServer 是從 IDispatch 介面(是 COM 的一個主要介面,用於 Automation ,其祖先當然也是 IUnknown)派生出來的,所以直接用 IAppServer 也並無不可。
    在 IAppServerSOAP 介面定義前面有一段注釋,大意就是說: IAppServerSOAP 用 StdCall 類型的方法調用替代了 IAppServer 中的 SafeCall 類型的方法調用,為的是在不支援 SafeCall 的環境中也可以實現 IAppServer 的功能。這就是 IAppServerSOAP 出現的原因。
    瞭解了這些就好多了。所有的 SOAP Server Data Module 都是從 TSoapDataModule 派生的,它定義在 SOAPDm 單元中,其中有如下代碼:

    { IAppServerSOAP }    function  SAS_ApplyUpdates(...): OleVariant; virtual; stdcall;    function  SAS_GetRecords(...): OleVariant; virtual; stdcall;    function  SAS_DataRequest(...): OleVariant; virtual; stdcall;    function  SAS_GetProviderNames: TWideStringDynArray; virtual; stdcall;    function  SAS_GetParams(...): OleVariant; virtual; stdcall;    function  SAS_RowRequest(...): OleVariant; virtual; stdcall;    procedure SAS_Execute(...); virtual; stdcall;

    這裡就是 IAppServerSOAP 介面的實現,由於所有的 SOAP Server Data Module 都是從 TSoapDataModule 派生的,所以 IAppServerSOAP 介面的實作類別一定是已經實現的,但有沒有註冊呢?在我們的 SOAP Server Data Module 單元--SvrDMSoap 中有如下代碼:

procedure TSoapDemo3DMCreateInstance(out obj: TObject);begin obj := TSoapDemo3DM.Create(nil);end;initialization   InvRegistry.RegisterInvokableClass(TSoapDemo3DM, TSoapDemo3DMCreateInstance);

    顯然它也已經註冊了,但為什麼會出錯呢?看看 ISoapDemo3DM 介面的 WSDL 吧……怎麼只有 AS_XXX ?沒有 SAS_XXX ?原來如此,服務端只匯出了 IAppServer 介面,並沒有匯出 IAppServerSOAP 介面。這就是問題所在!
    再來看看 SvrDMSoap 單元:

  ISoapDemo3DM = interface(IAppServer)    ['{4F618288-9F81-4090-81EF-4ACE0BF6D0BE}']  end;  TSoapDemo3DM = class(TSoapDataModule, ISoapDemo3DM, IAppServer)

    原來如此,原來 ISoapDemo3DM 居然還是從 IAppServer 介面派生的,顯然這是 SOAP Server Data Module Wizard 的一個 Bug ,它還是按 IAppServer 介面的方法來產生 SOAP Server Data Module 的。知道問題的所在就好辦了,將上面的代碼中的兩個 IAppServer 都改為 IAppServerSOAP ,然後重新編譯。再來看看用戶端程式,一切正常了, ISoapDemo3DM 的 WSDL 也正確地匯出了 SAS_XXX ,特別是 hhttp://localhost:1024/Demo3.wadSoapDemo3 也可以看到標準的 Delphi 6.02 的 WebService 頁面了。
    至此,這個 Bug 已經完全解決。另外,要補充說明的是:在 C++ Builder 6 中不存在這個問題。

[Mental Studio]猛禽 Apr.23-02

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.