這兩天把一個使用Delphi2007成功升級到了Delphi2010。升級途中很艱辛,總結了 以下經驗與大家分享。另外,D7使用的第三方組件,由於官方沒有發布For Delphi2010的更新,修改的第三 方組件列表見文章尾部。
1,PChar
因為Delphi不支持無類型指針的算術運算,很多程序員使用 PChar來代替Pointer,即使指針指向目標並不是PAnsiChar。
考慮如下代碼:
var
P:PChar;
Buffer:Pointer;
begin
GetMem(Buffer,255);
P:=Buffer;
p^:=#1;
Inc(P);
p^:=#2;
FreeMem(Buffer,255);
end;
在2010中PChar已經不再表示PAnsiChar而是表示PWideChar,如果依然這樣寫,運行時很可能會得到一個內存訪問錯誤。因為每 次Inc(P),實際上指針向前移動了2字節,因為SizeOf(WideChar)=2,Inc(P)相當於 P:=P+SizeOf(WideChar)。
解決方法是把PChar替換成PAnsiChar
2,Move FillChar CopyMemory
這些函數依賴的是字節長度,往往我們直接使用Length(Str)來獲取,這是行不通的。
考慮如下代碼:
var
P1,P2:String;
begin
P1:='test';
SetLength(P2,Length(P1));
Move(P1[1],P2[1],Length(P1));
在2010中String默認映射到UnicodeString,單個字符是2字節,所以上文中P1實際占用了8字節內存,而傳給Move函數的長 度只有4字節,最終結果是P2="te"。
解決辦法1:
修改String為AnsiString,該方案雖然可行,但你的程序就享受不到Unicode待遇了。
解決辦法2:
SetLength 函數不要修改,因為他的長度參數是字符長度,而不是字節長度。
Move函數的最后一個參數 Length(P1) 修改成 Length(P1)*SizeOf(Char)。
注意:不要偷懶使用萬一老師說的ByteLength函數,該函數並沒有For AnsiString的重載,編譯器會 把參數隱式轉化為UnicodeString然后,ByteLength函數計算UnicodeString的長度。例如:一旦你不小心傳入了一個 AnsiString類型長度為4的字符串,函數會返回8,而不是你期望的長度4。
3,Key in ['a'..'z','B','C']
這類代碼最好替換成CharInSet(Key,['a'..'z','B','C']) 不然會當作AnsiChar處理。
4,WideString
代碼中的所有WideString都考慮替換成String,現在 WideString只是為了與COM兼容而存在,且沒有引用計數,性能低下。
5,Tnt控件
如果你的工程使用了Tnt控件或以前的WideTextPos WideStringReplace之類的東西都替換成標准的吧,不用曲線救國了。
待續…………
---------經過修改,可以在Delphi2010下運作的第三方組件--------------
1,PNGDelphi
2,EmbeddedWB
3,SynEdit的語法高亮組件 unihighlighter
4,JEDI Win32API Header
這些組件現在可以在Delphi2010下運作了。
6,引用AnsiStrings單元
如果你有必要使用 AnsiLowerCase AnsiCompareStr之類的函數,一定要引用AnsiStrings單元。
如果你不引用該單元,即便編譯不報錯,你實際上是用的還是Unicode版本的函數,會有隱式的轉化。不信你打開參數自動完成,看看IDE提示給你的類型是什么?天啊AnsiLowerCase參數竟然還是String,而不是AnsiString。看來Delphi2010太迫切的要拋棄Ansi字符串了,以至於你不引用AnsiStrings單元,所有Ansixxxx函數實際上還是Unicode版本。
7,AnsiCopy AnsiPos AnsiDelete
不要用AnsiCopy AnsiPos AnsiDelete,因為Copy Pos Delete三個函數已經有了For Ansi的重載。
8,把Char轉化為小寫用什么?
答案:試試看Character單元的新函數 ToUpper ToLower。以前我都是用System里面的UpCase函數,現在依然可用不過卻找不到LowCase DownCase之類的函數,困擾我好久好久。索性全使用Character單元提供的新函數吧。
9,編譯期警告:[DCC Warning] Unit1.pas(31): W1057 Implicit string cast from 'AnsiString' to 'string'
如果你的代碼中包含了兩種字符串(Unicode、Ansi)之間進行隱式轉化的時候就會出現該提示。
如下代碼就會觸發該警告:
var Unicode:String; Ansi:AnsiString; begin Ansi:='test..'; Unicode:=Ansi;
把舊版本的Delphi項目升級到2010,我通常都是借助編譯警告來快速尋找需要改動的部分。通常你可以把賦值雙方都聲明為String(默認影射到UnicodeString),就可以避免該警告。但如果你確定必須在此處保留Ansi並進行轉化的時候,建議你顯式的轉化他們(例如:Unicode:=String(Ansi);),這樣可以避免該警告,方便你在升級過程中繼續尋找其他需要修改的地方。
10,Readln Writeln 寫入文件時候要注意
如果你傳給Writeln一個AnsiString,那么它也會在文件中寫入AnsiString,那么你讀取得時候就必須傳給Readln一個AnsiString的類型,否則就是亂碼。例如舊工程的配置文件是Ansi的,而你已經把相關讀取配置的代碼升級為支持Unicode,那么運行工程前你首先要用記事本之類的工具把配置文件另存為成Unicode編碼。當然你還要注意跳過Unicode文件頭的兩個字節FE FF。
11,別再用String來操作二進制數據了
一定要記住String只是字符串,不要把它當作緩沖區、內存流使用。我的項目中,有很多地方是使用字符串來處理二進制數據,導致在本次升級中頗為費腦。如果當時用TBytes或TStream就好了。
反面教材:
var Int1,Int2,Int3,Int4:Integer; Buf:String; begin SetLength(Buf,12); Move(Int1,Buf[1],SizeOf(Integer)); Move(Int1,Buf[5],SizeOf(Integer)); Move(Int1,Buf[9],SizeOf(Integer)); Buf:=Buf+'前面有3個Integer。';
12,還是PChar
注意在2010中是這樣的:
PChar= Pointer to a WideChar array;
PAnsiChar = Pointer to a AnsiChar array;
如果你還像是在Delphi 7中那樣:PChar(AnsiString)那后果過是很嚴重的。