A
aimingoo
Unregistered / Unconfirmed
GUEST, unregistred user!
用D6可能是最早的一批,将D6 Uninstall可能也是最早的一批。下面是短短两三天发现的Bug
列表,没时间测试更多的错误了。就下面这些,已经足够我放弃Delphi 6了。
记得论坛里有在borland上班的朋友,如果可以,请提交一个Bug报告给borland吧。我没时间
写成E文了。
发现这些错误已经是近两个月前的事了,大富翁一直不好上,就等到现在才发,算是给所有大
小富翁的一份小礼吧。Aiming最近忙,不一定会来看看这个贴,有更多的问题大家尽管提好了。
哈哈。更多的Bugs列表,请大家附后。——不过,最好要证实过的。^-^
-----------------------------------------------
Delphi 6 Bugs. Find and Fixed By Aiming.
-----------------------------------------------
1. Delphi5/6中一个不兼容的String操作
-----------------------------------------------
const aNameStr='Guest';
type aNameType=string[length(aNameStr)];
var s:string;
procedure TForm1.Button1Click(Sender: TObject);
begin
s:='Guest5';
if aNameType(s)=aNameStr then
showMessage('ture');
end;
分析:
Delphi5中,正常识别aNameType(s)为'Guest',而Delphi6中
识别为'Guest'#0,从而导致不兼容。
2. Delphi6中无法重设线程优先级
-----------------------------------------------
constructor TsysChk.Create();
begin
{$ifndef VER140}
//Delphi6中在ISAPI线程中控制优先级会导致一个IIS的异常!!!
Priority := tpHigher;
{$endif}
inherited Create(False);
end;
3. Delphi4/5/6中均无法有效地对URL中的Unicode进行解码
-----------------------------------------------
function HTTPDecode(const AStr: String): String;
var
//...
pWideChar : array[0..1] of WORD;
pCChar : string[2];
//...
begin
//...
{//next fix by Aiming. old source:
S := '$' + Cp^ + Sp^;
Rp^ := Chr(StrToInt(S));
}
if Cp^<>'u'
then
begin
S := '$' + Cp^ + Sp^;
Rp^ := Chr(StrToInt(S));
end
else
//is UNICODE!!!
begin
Cp := Sp;
inc(Sp);
//skip char 'u'
if Sp^=#0
then
raise EWebBrokerException.CreateFmt(sErrorDecodingURLText, [Cp - PChar(AStr)])
else
begin
S := '$' + Cp^ + Sp^;
inc(Sp);
Cp := Sp;
inc(Sp);
S := S + '$' + Cp^ + Sp^;
pWideChar[0] := StrToInt(S);
pCChar := WideCharToString(@pWideChar);
Rp^ :=pCChar[1];
inc(Rp);
Rp^ :=pCChar[2];
end;
end
//...
end
分析:
Delphi4及以上版本的Web编程中,引用到httpapp.pas文件。该文件中的function HTTPDecode()存在上述错误。导致这个
错误码的原因是因为在IE中的JavaScript中的Escape()函数对双字节字串直接编码成Unicode模式的字串,即"%uxxxx"!而在
Netscape/Opera等浏览器中都按"%xx%xx"来进行编码的。因为前者是个在W3C中没有声明过的处理方法!
上面修正过的处理方法能够正常地处理通过IE的Escape()函数编码过的URL/Content.
4. Delphi6中对INIFile的重名项的取值方法与Delphi3/4/5存在不同
-------------------------------------------------------------------
这个错误将导致一些对IniFile的取值出现与低版本不兼容的情况。如下:
[testSection]
TableName=SectionList
Condition=ConditionValue_1
Condition=ConditionValue_2
Condition=ConditionValue_3
对上述Ini节test_Section取值时,ReadString('testSection','Condition','')取到的值在Delphi5中是ConditionValue_1,而在Delphi6中是ConditionValue_3。即Delphi6在重名的项取值是取最未项,而Delphi5是取最首项。这意味着一些试图利用重名项来传多个参数或者屏蔽项目的操作在D5/D6中存在不同。
分析:
Delphi5中对INIFile的操作是使用标准的winAPI来进行的,在ReadString()中,调用GetPrivateProfileString() API来读取值。
而在Delphi6中,考虑到对大型INI File的处理的速度问题,Delphi6重写了一个THashedStringList类,用来存储INIFILE节中的Name,并用Hash方法来实现Name在StringList中的检索,这样可以大幅度提升TStringList.Value['aName']这个操作的速度。--而这个操作是TStrings的操作中最常用的。
在实现THashedStringList类时,--可能是出于检索速度的考虑,也可能是因为Hash方法本身的需要--Delphi6检索到Name时,得到的结果是最未的一个Hash结点。这样,IniFile的操作中,也就得到了最后一个"Name=Value"值对。
很明显,上述问题是一个Bug,因为Delphi重新实现的ReadString()与WinAPI中的GetPrivateProfileString()函数不兼容。而INIFile是Windows的一个配置文件标准,也就意味着应当使用Windows标准的接口。
5. Delphi x处理for循环初值与TurboPascal不相容
-------------------------------------------------------------------
如下代码:
-----------------------------------------------
var i,j : integer;
begin
j := -2;
for i := 0 to j do
begin
writeln('test');
end;
writeln(i);
readln;
end.
-----------------------------------------------
在TurboPascal上运行时,你会看到i被输出为0,而在Delphi中,输出为一个随机的整数。
在Pascal中,"for i:=0 to n do
;"这样的操作是先给i初值,然后在"end;
"时递增/减,这是Pascal标准的做法。然而Delphi实现时却没有这样做,(可能是出于效率的考虑)它在循环次数为0时,将不赋初值。
因此导致出错。
解决方案:
在for之前加赋初值的代码:
-----------------------------------------------
var i,j : integer;
begin
j := -2;
i := 0;
for i := 0 to j do
begin
writeln('test');
end;
writeln(i);
readln;
end.
---------------------------------------------
6. Delphi5中使用动态数组作为函数入口参数的一个Bug~~~
------------------------------------------------------------
本Bug我在D5(sp2)中发现,没有在D6上测试过(已经uninstall了),大家参考一下。
如下代码:
procedure delNodes(aBox:array of integer;
isGuest:boolean=false);
begin
if isGuest then
showMessage('true') else
showMessage('false');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
delNodes([]);
end;
测试上面的代码,我们会发现:当Button1按下时,delNodes调用结果是出现一个消息框,显示'true'!!!问题是,按照接口描述,如果isGuest参数缺省,则应该取值False,也就是说,应该显示成'false'。
分析:
-----------
这里的结果显然违反了Delphi5对函数缺省参数的定义。但是,原因可能出在Delphi5对函数入口参数的栈的处理有错误。因为无法修改到Delhi RTL级的处理代码,所以,上述问题无法解决。在这里,只能使用delNodes([],true)或者delNodes([],false)来显示地调用这个函数,或者不定义isGuest为缺省参数。
另外,我们不能试图将isGuest调整到aBox参数之前。因为Delphi要求“缺省参数”必须作为函数参数最后的一个/几个参数。
作为一个辅助的解决方案,我们可以用下面的代码来模拟上述的函数定义。——如果你必须要这样做的话。——你的确可以实现类似于缺省参数的效果,但仅是效果而已。
-----------------------------------------------------------------------------
procedure delNodes(aBox:array of integer;
isGuest:boolean);overload;
begin
if isGuest then
showMessage('true') else
showMessage('false');
end;
procedure delNodes(aBox:array of integer);
overload;
begin
delNodes(aBox, false);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
delNodes([]);
end;
-----------------------------------------------------------------------------
7. Delphi 6对断点的设定有错误,另外,常量定义存在重大的BUG!!!
-------------------------------------------------------------------
请参考如下出错图片:
http://202.102.247.230/soft/aDelphiError.jpg
图片中的实例只是一个极简单的项目,就只有那样的几行代码。
有如下问题:
1. 调试断点事实上是"end.
"行的断点,Delphi6居然还数不清楚源代码行???
2. 常量定义的用法没错吧?是的,没错!但结果呢?如果你在断点位置打Ctrl+F7,你会发现,ExtSQLLine_MutilPage常量的值如下:
------------------------------------------------------------------
DECLARE @V1 int, @V2 int, @V3 int, @V4 int, @V5 %S
SET @V1=%S;
SET @V2=%S;
SET @V4=@V1*@V2+1
SELECT @V3=count(%S) FROM %S %S
IF (@V4>@V3) SET @V5=NULL
else
begin
SET ROWCOUNT @V4;
SELECT @V5=%S FROM %S %S %S END
SELECT %S INTO %S FROM %S WHERE (%S%S@V5) %S %S
SELECT @V4=@@RowCount
SET ROWCOUNT 0
------------------------------------------------------------------
天啊!“SET ROWCOUNT @V2”这一行定义居然被Delphi吃掉了!!!!!!!!
一个常量中间的值,还会被吃掉???一个常量定义都会出错!!!Delphi,你是不是真的要灭亡了?????
=============================================
很多的时候,我信任Borland,但更多的时候,我宁可去读完它的源码,然后再来发表对
Borland的看法。我一面承认着那些程序员的优秀和代码的品质,一面抱怨着这样那样的
BUG。无论如何,BUG还是BUG,一如既往的多。作为在对这些在M$ Windows平台上唯一抗
衡的编译器开发人员,和在大富翁里里外外的这些Delphi Fans最高的尊敬,我除了只能
提出这些Bug,还能说什么呢?
接下来的事,可能还是只有低头,并按下alt + p + b键,继续我的工作了罢。~~~
列表,没时间测试更多的错误了。就下面这些,已经足够我放弃Delphi 6了。
记得论坛里有在borland上班的朋友,如果可以,请提交一个Bug报告给borland吧。我没时间
写成E文了。
发现这些错误已经是近两个月前的事了,大富翁一直不好上,就等到现在才发,算是给所有大
小富翁的一份小礼吧。Aiming最近忙,不一定会来看看这个贴,有更多的问题大家尽管提好了。
哈哈。更多的Bugs列表,请大家附后。——不过,最好要证实过的。^-^
-----------------------------------------------
Delphi 6 Bugs. Find and Fixed By Aiming.
-----------------------------------------------
1. Delphi5/6中一个不兼容的String操作
-----------------------------------------------
const aNameStr='Guest';
type aNameType=string[length(aNameStr)];
var s:string;
procedure TForm1.Button1Click(Sender: TObject);
begin
s:='Guest5';
if aNameType(s)=aNameStr then
showMessage('ture');
end;
分析:
Delphi5中,正常识别aNameType(s)为'Guest',而Delphi6中
识别为'Guest'#0,从而导致不兼容。
2. Delphi6中无法重设线程优先级
-----------------------------------------------
constructor TsysChk.Create();
begin
{$ifndef VER140}
//Delphi6中在ISAPI线程中控制优先级会导致一个IIS的异常!!!
Priority := tpHigher;
{$endif}
inherited Create(False);
end;
3. Delphi4/5/6中均无法有效地对URL中的Unicode进行解码
-----------------------------------------------
function HTTPDecode(const AStr: String): String;
var
//...
pWideChar : array[0..1] of WORD;
pCChar : string[2];
//...
begin
//...
{//next fix by Aiming. old source:
S := '$' + Cp^ + Sp^;
Rp^ := Chr(StrToInt(S));
}
if Cp^<>'u'
then
begin
S := '$' + Cp^ + Sp^;
Rp^ := Chr(StrToInt(S));
end
else
//is UNICODE!!!
begin
Cp := Sp;
inc(Sp);
//skip char 'u'
if Sp^=#0
then
raise EWebBrokerException.CreateFmt(sErrorDecodingURLText, [Cp - PChar(AStr)])
else
begin
S := '$' + Cp^ + Sp^;
inc(Sp);
Cp := Sp;
inc(Sp);
S := S + '$' + Cp^ + Sp^;
pWideChar[0] := StrToInt(S);
pCChar := WideCharToString(@pWideChar);
Rp^ :=pCChar[1];
inc(Rp);
Rp^ :=pCChar[2];
end;
end
//...
end
分析:
Delphi4及以上版本的Web编程中,引用到httpapp.pas文件。该文件中的function HTTPDecode()存在上述错误。导致这个
错误码的原因是因为在IE中的JavaScript中的Escape()函数对双字节字串直接编码成Unicode模式的字串,即"%uxxxx"!而在
Netscape/Opera等浏览器中都按"%xx%xx"来进行编码的。因为前者是个在W3C中没有声明过的处理方法!
上面修正过的处理方法能够正常地处理通过IE的Escape()函数编码过的URL/Content.
4. Delphi6中对INIFile的重名项的取值方法与Delphi3/4/5存在不同
-------------------------------------------------------------------
这个错误将导致一些对IniFile的取值出现与低版本不兼容的情况。如下:
[testSection]
TableName=SectionList
Condition=ConditionValue_1
Condition=ConditionValue_2
Condition=ConditionValue_3
对上述Ini节test_Section取值时,ReadString('testSection','Condition','')取到的值在Delphi5中是ConditionValue_1,而在Delphi6中是ConditionValue_3。即Delphi6在重名的项取值是取最未项,而Delphi5是取最首项。这意味着一些试图利用重名项来传多个参数或者屏蔽项目的操作在D5/D6中存在不同。
分析:
Delphi5中对INIFile的操作是使用标准的winAPI来进行的,在ReadString()中,调用GetPrivateProfileString() API来读取值。
而在Delphi6中,考虑到对大型INI File的处理的速度问题,Delphi6重写了一个THashedStringList类,用来存储INIFILE节中的Name,并用Hash方法来实现Name在StringList中的检索,这样可以大幅度提升TStringList.Value['aName']这个操作的速度。--而这个操作是TStrings的操作中最常用的。
在实现THashedStringList类时,--可能是出于检索速度的考虑,也可能是因为Hash方法本身的需要--Delphi6检索到Name时,得到的结果是最未的一个Hash结点。这样,IniFile的操作中,也就得到了最后一个"Name=Value"值对。
很明显,上述问题是一个Bug,因为Delphi重新实现的ReadString()与WinAPI中的GetPrivateProfileString()函数不兼容。而INIFile是Windows的一个配置文件标准,也就意味着应当使用Windows标准的接口。
5. Delphi x处理for循环初值与TurboPascal不相容
-------------------------------------------------------------------
如下代码:
-----------------------------------------------
var i,j : integer;
begin
j := -2;
for i := 0 to j do
begin
writeln('test');
end;
writeln(i);
readln;
end.
-----------------------------------------------
在TurboPascal上运行时,你会看到i被输出为0,而在Delphi中,输出为一个随机的整数。
在Pascal中,"for i:=0 to n do
;"这样的操作是先给i初值,然后在"end;
"时递增/减,这是Pascal标准的做法。然而Delphi实现时却没有这样做,(可能是出于效率的考虑)它在循环次数为0时,将不赋初值。
因此导致出错。
解决方案:
在for之前加赋初值的代码:
-----------------------------------------------
var i,j : integer;
begin
j := -2;
i := 0;
for i := 0 to j do
begin
writeln('test');
end;
writeln(i);
readln;
end.
---------------------------------------------
6. Delphi5中使用动态数组作为函数入口参数的一个Bug~~~
------------------------------------------------------------
本Bug我在D5(sp2)中发现,没有在D6上测试过(已经uninstall了),大家参考一下。
如下代码:
procedure delNodes(aBox:array of integer;
isGuest:boolean=false);
begin
if isGuest then
showMessage('true') else
showMessage('false');
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
delNodes([]);
end;
测试上面的代码,我们会发现:当Button1按下时,delNodes调用结果是出现一个消息框,显示'true'!!!问题是,按照接口描述,如果isGuest参数缺省,则应该取值False,也就是说,应该显示成'false'。
分析:
-----------
这里的结果显然违反了Delphi5对函数缺省参数的定义。但是,原因可能出在Delphi5对函数入口参数的栈的处理有错误。因为无法修改到Delhi RTL级的处理代码,所以,上述问题无法解决。在这里,只能使用delNodes([],true)或者delNodes([],false)来显示地调用这个函数,或者不定义isGuest为缺省参数。
另外,我们不能试图将isGuest调整到aBox参数之前。因为Delphi要求“缺省参数”必须作为函数参数最后的一个/几个参数。
作为一个辅助的解决方案,我们可以用下面的代码来模拟上述的函数定义。——如果你必须要这样做的话。——你的确可以实现类似于缺省参数的效果,但仅是效果而已。
-----------------------------------------------------------------------------
procedure delNodes(aBox:array of integer;
isGuest:boolean);overload;
begin
if isGuest then
showMessage('true') else
showMessage('false');
end;
procedure delNodes(aBox:array of integer);
overload;
begin
delNodes(aBox, false);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
delNodes([]);
end;
-----------------------------------------------------------------------------
7. Delphi 6对断点的设定有错误,另外,常量定义存在重大的BUG!!!
-------------------------------------------------------------------
请参考如下出错图片:
http://202.102.247.230/soft/aDelphiError.jpg
图片中的实例只是一个极简单的项目,就只有那样的几行代码。
有如下问题:
1. 调试断点事实上是"end.
"行的断点,Delphi6居然还数不清楚源代码行???
2. 常量定义的用法没错吧?是的,没错!但结果呢?如果你在断点位置打Ctrl+F7,你会发现,ExtSQLLine_MutilPage常量的值如下:
------------------------------------------------------------------
DECLARE @V1 int, @V2 int, @V3 int, @V4 int, @V5 %S
SET @V1=%S;
SET @V2=%S;
SET @V4=@V1*@V2+1
SELECT @V3=count(%S) FROM %S %S
IF (@V4>@V3) SET @V5=NULL
else
begin
SET ROWCOUNT @V4;
SELECT @V5=%S FROM %S %S %S END
SELECT %S INTO %S FROM %S WHERE (%S%S@V5) %S %S
SELECT @V4=@@RowCount
SET ROWCOUNT 0
------------------------------------------------------------------
天啊!“SET ROWCOUNT @V2”这一行定义居然被Delphi吃掉了!!!!!!!!
一个常量中间的值,还会被吃掉???一个常量定义都会出错!!!Delphi,你是不是真的要灭亡了?????
=============================================
很多的时候,我信任Borland,但更多的时候,我宁可去读完它的源码,然后再来发表对
Borland的看法。我一面承认着那些程序员的优秀和代码的品质,一面抱怨着这样那样的
BUG。无论如何,BUG还是BUG,一如既往的多。作为在对这些在M$ Windows平台上唯一抗
衡的编译器开发人员,和在大富翁里里外外的这些Delphi Fans最高的尊敬,我除了只能
提出这些Bug,还能说什么呢?
接下来的事,可能还是只有低头,并按下alt + p + b键,继续我的工作了罢。~~~