分享
 
 
 

DELPHI 6 Update 2# 的 Bug 及其解决方法

王朝delphi·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有