2 {*****************************************************************************}
4 { Tnt Delphi Unicode Controls }
5 { http://www.tntware.com/delphicontrols/unicode/ }
8 { Copyright (c) 2002-2007, Troy Wolbrink (troy.wolbrink@tntware.com) }
10 {*****************************************************************************}
14 {$INCLUDE TntCompilers.inc}
16 {*****************************************************************************}
17 { Special thanks go to Francisco Leong for originating the design for }
18 { WideString-enabled resourcestrings. }
19 {*****************************************************************************}
26 // These functions should not be used by Delphi code since conversions are implicit.
27 {TNT-WARN WideCharToString}
28 {TNT-WARN WideCharLenToString}
29 {TNT-WARN WideCharToStrVar}
30 {TNT-WARN WideCharLenToStrVar}
31 {TNT-WARN StringToWideChar}
33 // ................ ANSI TYPES ................
38 {TNT-WARN CP_ACP} // <-- use DefaultSystemCodePage
39 function DefaultSystemCodePage
: Cardinal; // implicitly used when converting AnsiString <--> WideString.
42 WideCustomLoadResString
: function(ResStringRec
: PResStringRec
; var Value
: WideString
): Boolean;
44 {TNT-WARN LoadResString}
45 function WideLoadResString(ResStringRec
: PResStringRec
): WideString
;
47 function WideParamCount
: Integer;
49 function WideParamStr(Index
: Integer): WideString
;
51 // ......... introduced .........
54 { Each Unicode stream should begin with the code U+FEFF, }
55 { which the standard defines as the *byte order mark*. }
56 UNICODE_BOM
= WideChar($FEFF);
57 UNICODE_BOM_SWAPPED
= WideChar($FFFE);
58 UTF8_BOM
= AnsiString(#
$EF#
$BB#
$BF);
60 function WideStringToUTF8(const S
: WideString
): AnsiString
;
61 function UTF8ToWideString(const S
: AnsiString
): WideString
;
63 function WideStringToUTF7(const W
: WideString
): AnsiString
;
64 function UTF7ToWideString(const S
: AnsiString
): WideString
;
66 function StringToWideStringEx(const S
: AnsiString
; CodePage
: Cardinal): WideString
;
67 function WideStringToStringEx(const WS
: WideString
; CodePage
: Cardinal): AnsiString
;
69 function UCS2ToWideString(const Value
: AnsiString
): WideString
;
70 function WideStringToUCS2(const Value
: WideString
): AnsiString
;
72 function CharSetToCodePage(ciCharset
: UINT
): Cardinal;
73 function LCIDToCodePage(ALcid
: LCID
): Cardinal;
74 function KeyboardCodePage
: Cardinal;
75 function KeyUnicode(CharCode
: Word): WideChar
;
77 procedure StrSwapByteOrder(Str
: PWideChar
);
81 (tsWideResourceStrings
,
82 {$IFNDEF COMPILER_9_UP}tsFixImplicitCodePage
, tsFixWideStrConcat
, tsFixWideFormat
, {$ENDIF}
85 TTntSystemUpdateSet
= set of TTntSystemUpdate
;
88 AllTntSystemUpdates
= [Low(TTntSystemUpdate
)..High(TTntSystemUpdate
)];
90 procedure InstallTntSystemUpdates(Updates
: TTntSystemUpdateSet
= AllTntSystemUpdates
);
95 SysUtils
, Variants
, Forms
, TntWindows
, TntSysUtils
, TntForms
;
98 GDefaultSystemCodePage
: Cardinal;
100 function DefaultSystemCodePage
: Cardinal;
102 Result
:= GDefaultSystemCodePage
;
106 IsDebugging
: Boolean;
108 function WideLoadResString(ResStringRec
: PResStringRec
): WideString
;
110 MAX_RES_STRING_SIZE
= 4097; { MSDN documents this as the maximum size of a string in table. }
112 Buffer
: array [0..MAX_RES_STRING_SIZE
] of WideChar
; { Buffer leaves room for null terminator. }
115 if Assigned(WideCustomLoadResString
) and WideCustomLoadResString(ResStringRec
, Result
) then
116 exit
; { a custom resourcestring has been loaded. }
118 if ResStringRec
= nil then
120 else if ResStringRec
.Identifier
< 64*1024 then
121 SetString(Result
, Buffer
,
122 Tnt_LoadStringW(FindResourceHInstance(ResStringRec
.Module
^),
123 ResStringRec
.Identifier
, Buffer
, MAX_RES_STRING_SIZE
))
125 // custom string pointer
126 PCustom
:= PAnsiChar(ResStringRec
.Identifier
); { I would like to use PWideChar, but this would break legacy code. }
127 if (StrLen
{TNT-ALLOW StrLen}(PCustom
) > Cardinal(Length(UTF8_BOM
)))
128 and CompareMem(PCustom
, PAnsiChar(UTF8_BOM
), Length(UTF8_BOM
)) then
130 Result
:= UTF8ToWideString(PAnsiChar(PCustom
+ Length(UTF8_BOM
)))
137 function WideGetParamStr(P
: PWideChar
; var Param
: WideString
): PWideChar
;
140 Start
, S
, Q
: PWideChar
;
144 while (P
[0] <> #0) and (P
[0] <= ' ') do
146 if (P
[0] = '"') and (P
[1] = '"') then Inc(P
, 2) else Break
;
155 while (P
[0] <> #0) and (P
[0] <> '"') do
172 SetLength(Param
, Len
);
175 S
:= PWideChar(Param
);
182 while (P
[0] <> #0) and (P
[0] <> '"') do
192 if P
[0] <> #0 then Inc(P
);
209 function WideParamCount
: Integer;
214 P
:= WideGetParamStr(GetCommandLineW
, S
);
218 P
:= WideGetParamStr(P
, S
);
219 if S
= '' then Break
;
224 function WideParamStr(Index
: Integer): WideString
;
229 Result
:= WideGetModuleFileName(0)
232 P
:= GetCommandLineW
;
235 P
:= WideGetParamStr(P
, Result
);
236 if (Index
= 0) or (Result
= '') then Break
;
242 function WideStringToUTF8(const S
: WideString
): AnsiString
;
244 Result
:= UTF8Encode(S
);
247 function UTF8ToWideString(const S
: AnsiString
): WideString
;
249 Result
:= UTF8Decode(S
);
252 { ======================================================================= }
253 { Original File: ConvertUTF7.c }
254 { Author: David B. Goldsmith }
255 { Copyright (C) 1994, 1996 Taligent, Inc. All rights reserved. }
257 { This code is copyrighted. Under the copyright laws, this code may not }
258 { be copied, in whole or part, without prior written consent of Taligent. }
260 { Taligent grants the right to use this code as long as this ENTIRE }
261 { copyright notice is reproduced in the code. The code is provided }
262 { AS-IS, AND TALIGENT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR }
263 { IMPLIED, INCLUDING, BUT NOT LIMITED TO IMPLIED WARRANTIES OF }
264 { MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT }
265 { WILL TALIGENT BE LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING, }
266 { WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS }
267 { INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY }
268 { LOSS) ARISING OUT OF THE USE OR INABILITY TO USE THIS CODE, EVEN }
269 { IF TALIGENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. }
270 { BECAUSE SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF }
271 { LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE ABOVE }
272 { LIMITATION MAY NOT APPLY TO YOU. }
274 { RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the }
275 { government is subject to restrictions as set forth in subparagraph }
276 { (c)(l)(ii) of the Rights in Technical Data and Computer Software }
277 { clause at DFARS 252.227-7013 and FAR 52.227-19. }
279 { This code may be protected by one or more U.S. and International }
282 { TRADEMARKS: Taligent and the Taligent Design Mark are registered }
283 { trademarks of Taligent, Inc. }
284 { ======================================================================= }
289 _base64
: AnsiString
= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
290 _direct
: AnsiString
= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789''(),-./:?';
291 _optional
: AnsiString
= '!"#$%&*;<=>@[]^_`{|}';
292 _spaces
: AnsiString
= #9#13#10#32;
296 invbase64
: array[0..127] of SmallInt
;
300 mustshiftsafe
: array[0..127] of AnsiChar
;
301 mustshiftopt
: array[0..127] of AnsiChar
;
304 needtables
: Boolean = True;
306 procedure Initialize_UTF7_Data
;
308 base64
:= PAnsiChar(_base64
);
309 direct
:= PAnsiChar(_direct
);
310 optional
:= PAnsiChar(_optional
);
311 spaces
:= PAnsiChar(_spaces
);
322 mustshiftopt
[i
] := #1;
323 mustshiftsafe
[i
] := #1;
327 limit
:= Length(_Direct
);
331 mustshiftopt
[Integer(direct
[i
])] := #0;
332 mustshiftsafe
[Integer(direct
[i
])] := #0;
335 limit
:= Length(_Spaces
);
339 mustshiftopt
[Integer(spaces
[i
])] := #0;
340 mustshiftsafe
[Integer(spaces
[i
])] := #0;
343 limit
:= Length(_Optional
);
347 mustshiftopt
[Integer(optional
[i
])] := #0;
350 limit
:= Length(_Base64
);
354 invbase64
[Integer(base64
[i
])] := i
;
360 function WRITE_N_BITS(x
: UCS2
; n
: Integer; var BITbuffer
: Cardinal; var bufferbits
: Integer): Integer;
362 BITbuffer
:= BITbuffer
or (x
and (not (-1 shl n
))) shl (32 - n
- bufferbits
);
363 bufferbits
:= bufferbits
+ n
;
364 Result
:= bufferbits
;
365 end; { WRITE_N_BITS }
367 function READ_N_BITS(n
: Integer; var BITbuffer
: Cardinal; var bufferbits
: Integer): UCS2
;
369 buffertemp
: Cardinal;
371 buffertemp
:= BITbuffer
shr (32 - n
);
372 BITbuffer
:= BITbuffer
shl n
;
373 bufferbits
:= bufferbits
- n
;
374 Result
:= UCS2(buffertemp
);
377 function ConvertUCS2toUTF7(var sourceStart
: PWideChar
; sourceEnd
: PWideChar
;
378 var targetStart
: PAnsiChar
; targetEnd
: PAnsiChar
; optional
: Boolean;
379 verbose
: Boolean): Integer;
389 mustshift
: PAnsiChar
;
391 Initialize_UTF7_Data
;
396 source
:= sourceStart
;
397 target
:= targetStart
;
402 mustshift
:= @mustshiftopt
[0]
404 mustshift
:= @mustshiftsafe
[0];
406 done
:= source
>= sourceEnd
;
412 needshift
:= (not done
) and ((r
> $7F) or (mustshift
[r
] <> #0));
413 if needshift
and (not shifted
) then
415 if (Target
>= TargetEnd
) then
422 { Special case handling of the SHIFT_IN character }
423 if (r
= UCS2('+')) then
425 if (target
>= targetEnd
) then
438 { Either write the character to the bit buffer, or pad }
439 { the bit buffer out to a full base64 character. }
442 WRITE_N_BITS(r
, 16, BITbuffer
, bufferbits
)
444 WRITE_N_BITS(0, (6 - (bufferbits
mod 6)) mod 6, BITbuffer
,
446 { Flush out as many full base64 characters as possible }
447 { from the bit buffer. }
449 while (target
< targetEnd
) and (bufferbits
>= 6) do
451 Target
^ := base64
[READ_N_BITS(6, BITbuffer
, bufferbits
)];
454 if (bufferbits
>= 6) then
456 if (target
>= targetEnd
) then
462 if (not needshift
) then
464 { Write the explicit shift out character if }
465 { 1) The caller has requested we always do it, or }
466 { 2) The directly encoded character is in the }
468 { 3) The directly encoded character is SHIFT_OUT. }
470 if verbose
or ((not done
) and ((invbase64
[r
] >= 0) or (r
=
473 if (target
>= targetEnd
) then
483 { The character can be directly encoded as ASCII. }
485 if (not needshift
) and (not done
) then
487 if (target
>= targetEnd
) then
492 Target
^ := AnsiChar(r
);
496 sourceStart
:= source
;
497 targetStart
:= target
;
498 end; { ConvertUCS2toUTF7 }
500 function ConvertUTF7toUCS2(var sourceStart
: PAnsiChar
; sourceEnd
: PAnsiChar
;
501 var targetStart
: PWideChar
; targetEnd
: PWideChar
): Integer;
503 target
: PWideChar
{ Register };
504 source
: PAnsiChar
{ Register };
505 BITbuffer
: Cardinal { & "Address Of" Used };
506 bufferbits
: Integer { & "Address Of" Used };
507 shifted
: Boolean { Used In Boolean Context };
508 first
: Boolean { Used In Boolean Context };
511 base64value
: Integer;
515 junk
: UCS2
{ Used In Boolean Context };
517 Initialize_UTF7_Data
;
524 source
:= sourceStart
;
525 target
:= targetStart
;
530 { read an ASCII character c }
531 done
:= Source
>= SourceEnd
;
539 { We're done with a base64 string if we hit EOF, it's not a valid }
540 { ASCII character, or it's not in the base64 set. }
542 base64value
:= invbase64
[c
];
543 base64EOF
:= (done
or (c
> $7F)) or (base64value
< 0);
547 { If the character causing us to drop out was SHIFT_IN or }
548 { SHIFT_OUT, it may be a special escape for SHIFT_IN. The }
549 { test for SHIFT_IN is not necessary, but allows an alternate }
550 { form of UTF-7 where SHIFT_IN is escaped by SHIFT_IN. This }
551 { only works for some values of SHIFT_IN. }
553 if ((not done
) and ((c
= Integer('+')) or (c
= Integer('-')))) then
555 { get another character c }
557 Done
:= Source
>= SourceEnd
;
562 { If no base64 characters were encountered, and the }
563 { character terminating the shift sequence was }
564 { SHIFT_OUT, then it's a special escape for SHIFT_IN. }
567 if first
and (prevc
= Integer('-')) then
569 { write SHIFT_IN unicode }
570 if (target
>= targetEnd
) then
575 Target
^ := WideChar('+');
580 if (not wroteone
) then
589 if (not wroteone
) then
597 { Add another 6 bits of base64 to the bit buffer. }
598 WRITE_N_BITS(base64value
, 6, BITbuffer
,
602 { Extract as many full 16 bit characters as possible from the }
605 while (bufferbits
>= 16) and (target
< targetEnd
) do
608 Target
^ := WideChar(READ_N_BITS(16, BITbuffer
, bufferbits
));
612 if (bufferbits
>= 16) then
614 if (target
>= targetEnd
) then
622 junk
:= READ_N_BITS(bufferbits
, BITbuffer
, bufferbits
);
629 if (not shifted
) and (not done
) then
631 if (c
= Integer('+')) then
639 { It must be a directly encoded character. }
644 if (target
>= targetEnd
) then
649 Target
^ := WideChar(c
);
654 sourceStart
:= source
;
655 targetStart
:= target
;
656 end; { ConvertUTF7toUCS2 }
658 {*****************************************************************************}
659 { Thanks to Francisco Leong for providing the Pascal conversion of }
660 { ConvertUTF7.c (by David B. Goldsmith) }
661 {*****************************************************************************}
664 SBufferOverflow
= 'Buffer overflow';
665 SInvalidUTF7
= 'Invalid UTF7';
667 function WideStringToUTF7(const W
: WideString
): AnsiString
;
669 SourceStart
, SourceEnd
: PWideChar
;
670 TargetStart
, TargetEnd
: PAnsiChar
;
676 SetLength(Result
, Length(W
) * 7); // Assume worst case
677 SourceStart
:= PWideChar(@W
[1]);
678 SourceEnd
:= PWideChar(@W
[Length(W
)]) + 1;
679 TargetStart
:= PAnsiChar(@Result
[1]);
680 TargetEnd
:= PAnsiChar(@Result
[Length(Result
)]) + 1;
681 if ConvertUCS2toUTF7(SourceStart
, SourceEnd
, TargetStart
,
682 TargetEnd
, True, False) <> 0
684 raise ETntInternalError
.Create(SBufferOverflow
);
685 SetLength(Result
, TargetStart
- PAnsiChar(@Result
[1]));
689 function UTF7ToWideString(const S
: AnsiString
): WideString
;
691 SourceStart
, SourceEnd
: PAnsiChar
;
692 TargetStart
, TargetEnd
: PWideChar
;
698 SetLength(Result
, Length(S
)); // Assume Worst case
699 SourceStart
:= PAnsiChar(@S
[1]);
700 SourceEnd
:= PAnsiChar(@S
[Length(S
)]) + 1;
701 TargetStart
:= PWideChar(@Result
[1]);
702 TargetEnd
:= PWideChar(@Result
[Length(Result
)]) + 1;
703 case ConvertUTF7toUCS2(SourceStart
, SourceEnd
, TargetStart
,
705 1: raise ETntGeneralError
.Create(SInvalidUTF7
);
706 2: raise ETntInternalError
.Create(SBufferOverflow
);
708 SetLength(Result
, TargetStart
- PWideChar(@Result
[1]));
712 function StringToWideStringEx(const S
: AnsiString
; CodePage
: Cardinal): WideString
;
715 OutputLength
: Integer;
717 if CodePage
= CP_UTF7
then
718 Result
:= UTF7ToWideString(S
) // CP_UTF7 not supported on Windows 95
719 else if CodePage
= CP_UTF8
then
720 Result
:= UTF8ToWideString(S
) // CP_UTF8 not supported on Windows 95
722 InputLength
:= Length(S
);
723 OutputLength
:= MultiByteToWideChar(CodePage
, 0, PAnsiChar(S
), InputLength
, nil, 0);
724 SetLength(Result
, OutputLength
);
725 MultiByteToWideChar(CodePage
, 0, PAnsiChar(S
), InputLength
, PWideChar(Result
), OutputLength
);
729 function WideStringToStringEx(const WS
: WideString
; CodePage
: Cardinal): AnsiString
;
732 OutputLength
: Integer;
734 if CodePage
= CP_UTF7
then
735 Result
:= WideStringToUTF7(WS
) // CP_UTF7 not supported on Windows 95
736 else if CodePage
= CP_UTF8
then
737 Result
:= WideStringToUTF8(WS
) // CP_UTF8 not supported on Windows 95
739 InputLength
:= Length(WS
);
740 OutputLength
:= WideCharToMultiByte(CodePage
, 0, PWideChar(WS
), InputLength
, nil, 0, nil, nil);
741 SetLength(Result
, OutputLength
);
742 WideCharToMultiByte(CodePage
, 0, PWideChar(WS
), InputLength
, PAnsiChar(Result
), OutputLength
, nil, nil);
746 function UCS2ToWideString(const Value
: AnsiString
): WideString
;
748 if Length(Value
) = 0 then
751 SetString(Result
, PWideChar(@Value
[1]), Length(Value
) div SizeOf(WideChar
))
754 function WideStringToUCS2(const Value
: WideString
): AnsiString
;
756 if Length(Value
) = 0 then
759 SetString(Result
, PAnsiChar(@Value
[1]), Length(Value
) * SizeOf(WideChar
))
762 { Windows.pas doesn't declare TranslateCharsetInfo() correctly. }
763 function TranslateCharsetInfo(lpSrc
: PDWORD
; var lpCs
: TCharsetInfo
; dwFlags
: DWORD
): BOOL
; stdcall; external gdi32 name
'TranslateCharsetInfo';
765 function CharSetToCodePage(ciCharset
: UINT
): Cardinal;
769 Win32Check(TranslateCharsetInfo(PDWORD(ciCharset
), C
, TCI_SRCCHARSET
));
773 function LCIDToCodePage(ALcid
: LCID
): Cardinal;
775 Buf
: array[0..6] of AnsiChar
;
777 GetLocaleInfo(ALcid
, LOCALE_IDefaultAnsiCodePage
, Buf
, 6);
778 Result
:= StrToIntDef(Buf
, GetACP
);
781 function KeyboardCodePage
: Cardinal;
783 Result
:= LCIDToCodePage(GetKeyboardLayout(0) and $FFFF);
786 function KeyUnicode(CharCode
: Word): WideChar
;
790 // converts the given character (as it comes with a WM_CHAR message) into its
791 // corresponding Unicode character depending on the active keyboard layout
792 if CharCode
<= Word(High(AnsiChar
)) then begin
793 AChar
:= AnsiChar(CharCode
);
794 MultiByteToWideChar(KeyboardCodePage
, MB_USEGLYPHCHARS
, @AChar
, 1, @Result
, 1);
796 Result
:= WideChar(CharCode
);
799 procedure StrSwapByteOrder(Str
: PWideChar
);
804 While (P
^ <> 0) do begin
805 P
^ := MakeWord(HiByte(P
^), LoByte(P
^));
810 //--------------------------------------------------------------------
813 // This system function is used to retrieve a resourcestring and
814 // return the result as an AnsiString. If we believe that the result
815 // is only a temporary value, and that it will be immediately
816 // assigned to a WideString or a Variant, then we will save the
817 // Unicode result as well as a reference to the original Ansi string.
818 // WStrFromPCharLen() or VarFromLStr() will return this saved
819 // Unicode string if it appears to receive the most recent result
821 //--------------------------------------------------------------------
824 //===========================================================================================
826 // function CodeMatchesPatternForUnicode(...);
828 // GIVEN: SomeWideString := SSomeResString; { WideString := resourcestring }
830 // Delphi will compile this statement into the following:
831 // -------------------------------------------------
832 // TempAnsiString := LoadResString(@SSomeResString);
833 // LINE 1: lea edx,[SomeTempAnsiString]
834 // LINE 2: mov eax,[@SomeResString]
835 // LINE 3: call LoadResString
837 // WStrFromLStr(SomeWideString, TempAnsiString); { SomeWideString := TempAnsiString }
838 // LINE 4: mov edx,[SomeTempAnsiString]
839 // LINE 5: mov/lea eax [@SomeWideString]
840 // LINE 6: call @WStrFromLStr
841 // -------------------------------------------------
843 // The order in which the parameters are prepared for WStrFromLStr (ie LINE 4 & 5) is
844 // reversed when assigning a non-temporary AnsiString to a WideString.
846 // This code, for example, results in LINE 4 and LINE 5 being swapped.
848 // SomeAnsiString := SSomeResString;
849 // SomeWideString := SomeAnsiString;
851 // Since we know the "signature" used by the compiler, we can detect this pattern.
852 // If we believe it is only temporary, we can save the Unicode results for later
853 // retrieval from WStrFromLStr.
855 // One final note: When assigning a resourcestring to a Variant, the same patterns exist.
856 //===========================================================================================
858 function CodeMatchesPatternForUnicode(PLine4
: PAnsiChar
): Boolean;
860 SIZEOF_OPCODE
= 1 {byte};
861 MOV_16_OPCODE
= AnsiChar($8B); { we'll assume operand size is 16 bits }
862 MOV_32_OPCODE
= AnsiChar($B8); { we'll assume operand size is 32 bits }
863 LEA_OPCODE
= AnsiChar($8D); { operand size can be 16 or 40 bits }
864 CALL_OPCODE
= AnsiChar($E8); { assumed operand size is 32 bits }
865 BREAK_OPCODE
= AnsiChar($CC); {in a breakpoint}
870 DataSize
: Integer; // bytes in first LEA operand
874 PLine3
:= PLine4
- SizeOf(CALL_OPCODE
) - 4;
875 PLine2
:= PLine3
- SizeOf(MOV_32_OPCODE
) - 4;
877 // figure PLine1 and operand size
878 DataSize
:= 2; { try 16 bit operand for line 1 }
879 PLine1
:= PLine2
- DataSize
- SizeOf(LEA_OPCODE
);
880 if (PLine1
^ <> LEA_OPCODE
) and (not (IsDebugging
and (PLine1
^ = BREAK_OPCODE
))) then
882 DataSize
:= 5; { try 40 bit operand for line 1 }
883 PLine1
:= PLine2
- DataSize
- SizeOf(LEA_OPCODE
);
885 if (PLine1
^ = LEA_OPCODE
) or (IsDebugging
and (PLine1
^ = BREAK_OPCODE
)) then
887 if CompareMem(PLine1
+ SIZEOF_OPCODE
, PLine4
+ SIZEOF_OPCODE
, DataSize
) then
889 // After this check, it seems to match the WideString <- (temp) AnsiString pattern
890 Result
:= True; // It is probably OK. (The side effects of being wrong aren't very bad.)
896 PLastResString
: PAnsiChar
;
897 LastResStringValue
: AnsiString
;
898 LastWideResString
: WideString
;
900 procedure FreeTntSystemThreadVars
;
902 LastResStringValue
:= '';
903 LastWideResString
:= '';
906 procedure Custom_System_EndThread(ExitCode
: Integer);
908 FreeTntSystemThreadVars
;
909 {$IFDEF COMPILER_10_UP}
910 if Assigned(SystemThreadEndProc
) then
911 SystemThreadEndProc(ExitCode
);
913 ExitThread(ExitCode
);
916 function Custom_System_LoadResString(ResStringRec
: PResStringRec
): AnsiString
;
920 // get return address
927 // check calling code pattern
928 if CodeMatchesPatternForUnicode(ReturnAddr
) then begin
929 // result will probably be assigned to an intermediate AnsiString
930 // on its way to either a WideString or Variant.
931 LastWideResString
:= WideLoadResString(ResStringRec
);
932 Result
:= LastWideResString
;
933 LastResStringValue
:= Result
;
935 PLastResString
:= nil
937 PLastResString
:= PAnsiChar(Result
);
939 // result will probably be assigned to an actual AnsiString variable.
940 PLastResString
:= nil;
941 Result
:= WideLoadResString(ResStringRec
);
945 //--------------------------------------------------------------------
946 // WStrFromPCharLen()
948 // This system function is used to assign an AnsiString to a WideString.
949 // It has been modified to assign Unicode results from LoadResString.
950 // Another purpose of this function is to specify the code page.
951 //--------------------------------------------------------------------
953 procedure Custom_System_WStrFromPCharLen(var Dest
: WideString
; Source
: PAnsiChar
; Length
: Integer);
956 Buffer
: array[0..2047] of WideChar
;
957 Local_PLastResString
: Pointer;
959 Local_PLastResString
:= PLastResString
;
960 if (Local_PLastResString
<> nil)
961 and (Local_PLastResString
= Source
)
962 and (System
.Length(LastResStringValue
) = Length
)
963 and (LastResStringValue
= Source
) then begin
964 // use last unicode resource string
965 PLastResString
:= nil; { clear for further use }
966 Dest
:= LastWideResString
;
968 if Local_PLastResString
<> nil then
969 PLastResString
:= nil; { clear for further use }
975 if Length
+ 1 < High(Buffer
) then
977 DestLen
:= MultiByteToWideChar(DefaultSystemCodePage
, 0, Source
, Length
, Buffer
,
981 SetLength(Dest
, DestLen
);
982 Move(Pointer(@Buffer
[0])^, Pointer(Dest
)^, DestLen
* SizeOf(WideChar
));
986 DestLen
:= (Length
+ 1);
987 SetLength(Dest
, DestLen
); // overallocate, trim later
988 DestLen
:= MultiByteToWideChar(DefaultSystemCodePage
, 0, Source
, Length
, Pointer(Dest
),
992 SetLength(Dest
, DestLen
);
996 {$IFNDEF COMPILER_9_UP}
998 //--------------------------------------------------------------------
999 // LStrFromPWCharLen()
1001 // This system function is used to assign an WideString to an AnsiString.
1002 // It has not been modified from its original purpose other than to specify the code page.
1003 //--------------------------------------------------------------------
1005 procedure Custom_System_LStrFromPWCharLen(var Dest
: AnsiString
; Source
: PWideChar
; Length
: Integer);
1008 Buffer
: array[0..4095] of AnsiChar
;
1015 if Length
+ 1 < (High(Buffer
) div sizeof(WideChar
)) then
1017 DestLen
:= WideCharToMultiByte(DefaultSystemCodePage
, 0, Source
,
1018 Length
, Buffer
, High(Buffer
),
1020 if DestLen
>= 0 then
1022 SetLength(Dest
, DestLen
);
1023 Move(Pointer(@Buffer
[0])^, PAnsiChar(Dest
)^, DestLen
);
1028 DestLen
:= (Length
+ 1) * sizeof(WideChar
);
1029 SetLength(Dest
, DestLen
); // overallocate, trim later
1030 DestLen
:= WideCharToMultiByte(DefaultSystemCodePage
, 0, Source
, Length
, Pointer(Dest
), DestLen
,
1034 SetLength(Dest
, DestLen
);
1037 //--------------------------------------------------------------------
1040 // This system function is used to assign an WideString to an short string.
1041 // It has not been modified from its original purpose other than to specify the code page.
1042 //--------------------------------------------------------------------
1044 procedure Custom_System_WStrToString(Dest
: PShortString
; const Source
: WideString
; MaxLen
: Integer);
1046 SourceLen
, DestLen
: Integer;
1047 Buffer
: array[0..511] of AnsiChar
;
1049 if MaxLen
> 255 then MaxLen
:= 255;
1050 SourceLen
:= Length(Source
);
1051 if SourceLen
>= MaxLen
then SourceLen
:= MaxLen
;
1052 if SourceLen
= 0 then
1055 DestLen
:= WideCharToMultiByte(DefaultSystemCodePage
, 0, Pointer(Source
), SourceLen
,
1056 Buffer
, SizeOf(Buffer
), nil, nil);
1057 if DestLen
> MaxLen
then DestLen
:= MaxLen
;
1059 Dest
^[0] := Chr(DestLen
);
1060 if DestLen
> 0 then Move(Buffer
, Dest
^[1], DestLen
);
1065 //--------------------------------------------------------------------
1068 // This system function is used to assign an AnsiString to a Variant.
1069 // It has been modified to assign Unicode results from LoadResString.
1070 //--------------------------------------------------------------------
1072 procedure Custom_System_VarFromLStr(var V
: TVarData
; const Value
: AnsiString
);
1074 varDeepData
= $BFE8;
1076 Local_PLastResString
: Pointer;
1078 if (V
.VType
and varDeepData
) <> 0 then
1079 VarClear(PVariant(@V
)^);
1081 Local_PLastResString
:= PLastResString
;
1082 if (Local_PLastResString
<> nil)
1083 and (Local_PLastResString
= PAnsiChar(Value
))
1084 and (LastResStringValue
= Value
) then begin
1085 // use last unicode resource string
1086 PLastResString
:= nil; { clear for further use }
1088 V
.VType
:= varOleStr
;
1089 WideString(Pointer(V
.VOleStr
)) := Copy(LastWideResString
, 1, MaxInt
);
1091 if Local_PLastResString
<> nil then
1092 PLastResString
:= nil; { clear for further use }
1094 V
.VType
:= varString
;
1095 AnsiString(V
.VString
) := Value
;
1099 {$IFNDEF COMPILER_9_UP}
1101 //--------------------------------------------------------------------
1102 // WStrCat3() A := B + C;
1104 // This system function is used to concatenate two strings into one result.
1105 // This function is added because A := '' + '' doesn't necessarily result in A = '';
1106 //--------------------------------------------------------------------
1108 procedure Custom_System_WStrCat3(var Dest
: WideString
; const Source1
, Source2
: WideString
);
1110 function NewWideString(CharLength
: Longint): Pointer;
1112 _NewWideString
: function(CharLength
: Longint): Pointer;
1116 MOV ECX, offset System.
@NewWideString;
1117 MOV _NewWideString
, ECX
1120 Result
:= _NewWideString(CharLength
);
1123 procedure WStrSet(var S
: WideString
; P
: PWideChar
);
1127 Temp
:= Pointer(InterlockedExchange(Integer(S
), Integer(P
)));
1129 WideString(Temp
) := '';
1133 Source1Len
, Source2Len
: Integer;
1136 Source1Len
:= Length(Source1
);
1137 Source2Len
:= Length(Source2
);
1138 if (Source1Len
<> 0) or (Source2Len
<> 0) then
1140 NewStr
:= NewWideString(Source1Len
+ Source2Len
);
1141 Move(Pointer(Source1
)^, Pointer(NewStr
)^, Source1Len
* sizeof(WideChar
));
1142 Move(Pointer(Source2
)^, NewStr
[Source1Len
], Source2Len
* sizeof(WideChar
));
1143 WStrSet(Dest
, NewStr
);
1150 //--------------------------------------------------------------------
1151 // System proc replacements
1152 //--------------------------------------------------------------------
1155 POverwrittenData
= ^TOverwrittenData
;
1156 TOverwrittenData
= record
1158 OldCode
: array[0..6] of Byte;
1161 procedure OverwriteProcedure(OldProcedure
, NewProcedure
: pointer; Data
: POverwrittenData
= nil);
1162 { OverwriteProcedure originally from Igor Siticov }
1163 { Modified by Jacques Garcia Vazquez }
1170 if Assigned(Data
) and (Data
.Location
<> nil) then
1171 exit
; { procedure already overwritten }
1173 // need six bytes in place of 5
1174 x
:= PAnsiChar(OldProcedure
);
1175 if not VirtualProtect(Pointer(x
), 6, PAGE_EXECUTE_READWRITE
, @ov
) then
1178 // if a jump is present then a redirect is found
1179 // $FF25 = jmp dword ptr [xxx]
1180 // This redirect is normally present in bpl files, but not in exe files
1183 if Word(p
^) = $25FF then
1185 Inc(Integer(p
), 2); // skip the jump
1186 // get the jump address p^ and dereference it p^^
1187 p
:= Pointer(Pointer(p
^)^);
1189 // release the memory
1190 if not VirtualProtect(Pointer(x
), 6, ov
, @ov2
) then
1193 // re protect the correct one
1195 if not VirtualProtect(Pointer(x
), 6, PAGE_EXECUTE_READWRITE
, @ov
) then
1199 if Assigned(Data
) then
1201 Move(x
^, Data
.OldCode
, 6);
1202 { Assign Location last so that Location <> nil only if OldCode is properly initialized. }
1206 x
[0] := AnsiChar($E9);
1207 y
:= integer(NewProcedure
) - integer(p
) - 5;
1208 x
[1] := AnsiChar(y
and 255);
1209 x
[2] := AnsiChar((y
shr 8) and 255);
1210 x
[3] := AnsiChar((y
shr 16) and 255);
1211 x
[4] := AnsiChar((y
shr 24) and 255);
1213 if not VirtualProtect(Pointer(x
), 6, ov
, @ov2
) then
1217 procedure RestoreProcedure(OriginalProc
: Pointer; Data
: TOverwrittenData
);
1221 if Data
.Location
<> nil then begin
1222 if not VirtualProtect(Data
.Location
, 6, PAGE_EXECUTE_READWRITE
, @ov
) then
1224 Move(Data
.OldCode
, Data
.Location
^, 6);
1225 if not VirtualProtect(Data
.Location
, 6, ov
, @ov2
) then
1230 function Addr_System_EndThread
: Pointer;
1232 Result
:= @System
.EndThread
;
1235 function Addr_System_LoadResString
: Pointer;
1237 Result
:= @System
.LoadResString
{TNT-ALLOW LoadResString};
1240 function Addr_System_WStrFromPCharLen
: Pointer;
1242 mov eax, offset System.
@WStrFromPCharLen;
1245 {$IFNDEF COMPILER_9_UP}
1246 function Addr_System_LStrFromPWCharLen
: Pointer;
1248 mov eax, offset System.
@LStrFromPWCharLen;
1251 function Addr_System_WStrToString
: Pointer;
1253 mov eax, offset System.
@WStrToString;
1257 function Addr_System_VarFromLStr
: Pointer;
1259 mov eax, offset System.
@VarFromLStr;
1262 function Addr_System_WStrCat3
: Pointer;
1264 mov eax, offset System.
@WStrCat3;
1268 System_EndThread_Code
,
1269 System_LoadResString_Code
,
1270 System_WStrFromPCharLen_Code
,
1271 {$IFNDEF COMPILER_9_UP}
1272 System_LStrFromPWCharLen_Code
,
1273 System_WStrToString_Code
,
1275 System_VarFromLStr_Code
,
1276 {$IFNDEF COMPILER_9_UP}
1277 System_WStrCat3_Code
,
1278 SysUtils_WideFmtStr_Code
,
1280 Forms_TApplication_ShowException_Code
,
1281 SysUtils_RaiseLastOsError_Code
: TOverwrittenData
;
1283 procedure InstallEndThreadOverride
;
1285 OverwriteProcedure(Addr_System_EndThread
, @Custom_System_EndThread
, @System_EndThread_Code
);
1288 procedure InstallStringConversionOverrides
;
1290 OverwriteProcedure(Addr_System_WStrFromPCharLen
, @Custom_System_WStrFromPCharLen
, @System_WStrFromPCharLen_Code
);
1291 {$IFNDEF COMPILER_9_UP}
1292 OverwriteProcedure(Addr_System_LStrFromPWCharLen
, @Custom_System_LStrFromPWCharLen
, @System_LStrFromPWCharLen_Code
);
1293 OverwriteProcedure(Addr_System_WStrToString
, @Custom_System_WStrToString
, @System_WStrToString_Code
);
1297 procedure InstallWideResourceStrings
;
1299 OverwriteProcedure(Addr_System_LoadResString
, @Custom_System_LoadResString
, @System_LoadResString_Code
);
1300 OverwriteProcedure(Addr_System_VarFromLStr
, @Custom_System_VarFromLStr
, @System_VarFromLStr_Code
);
1303 {$IFNDEF COMPILER_9_UP}
1304 procedure InstallWideStringConcatenationFix
;
1306 OverwriteProcedure(Addr_System_WStrCat3
, @Custom_System_WStrCat3
, @System_WStrCat3_Code
);
1309 procedure InstallWideFormatFixes
;
1311 OverwriteProcedure(@SysUtils
.WideFmtStr
, @TntSysUtils
.Tnt_WideFmtStr
, @SysUtils_WideFmtStr_Code
);
1315 procedure InstallWideExceptions
;
1317 OverwriteProcedure(@Forms
.TApplication
.ShowException
, @TTntApplication
.ShowException
, @Forms_TApplication_ShowException_Code
);
1318 OverwriteProcedure(@SysUtils
.RaiseLastOsError
, @TntSysUtils
.WideRaiseLastOsError
, @SysUtils_RaiseLastOsError_Code
);
1321 procedure InstallTntSystemUpdates(Updates
: TTntSystemUpdateSet
= AllTntSystemUpdates
);
1323 InstallEndThreadOverride
;
1324 if tsWideResourceStrings
in Updates
then begin
1325 InstallStringConversionOverrides
;
1326 InstallWideResourceStrings
;
1328 {$IFNDEF COMPILER_9_UP}
1329 if tsFixImplicitCodePage
in Updates
then begin
1330 InstallStringConversionOverrides
;
1331 { CP_ACP is the code page used by the non-Unicode Windows API. }
1332 GDefaultSystemCodePage
:= CP_ACP
{TNT-ALLOW CP_ACP};
1334 if tsFixWideStrConcat
in Updates
then begin
1335 InstallWideStringConcatenationFix
;
1337 if tsFixWideFormat
in Updates
then begin
1338 InstallWideFormatFixes
;
1341 if tsWideExceptions
in Updates
then begin
1342 InstallWideExceptions
1346 {$IFNDEF COMPILER_9_UP}
1348 StartupDefaultUserCodePage
: Cardinal;
1351 procedure UninstallSystemOverrides
;
1353 RestoreProcedure(Addr_System_EndThread
, System_EndThread_Code
);
1354 // String Conversion
1355 RestoreProcedure(Addr_System_WStrFromPCharLen
, System_WStrFromPCharLen_Code
);
1356 {$IFNDEF COMPILER_9_UP}
1357 RestoreProcedure(Addr_System_LStrFromPWCharLen
, System_LStrFromPWCharLen_Code
);
1358 RestoreProcedure(Addr_System_WStrToString
, System_WStrToString_Code
);
1359 GDefaultSystemCodePage
:= StartupDefaultUserCodePage
;
1361 // Wide resourcestring
1362 RestoreProcedure(Addr_System_LoadResString
, System_LoadResString_Code
);
1363 RestoreProcedure(Addr_System_VarFromLStr
, System_VarFromLStr_Code
);
1364 {$IFNDEF COMPILER_9_UP}
1365 // WideString concat fix
1366 RestoreProcedure(Addr_System_WStrCat3
, System_WStrCat3_Code
);
1368 RestoreProcedure(@SysUtils
.WideFmtStr
, SysUtils_WideFmtStr_Code
);
1371 RestoreProcedure(@Forms
.TApplication
.ShowException
, Forms_TApplication_ShowException_Code
);
1372 RestoreProcedure(@SysUtils
.RaiseLastOsError
, SysUtils_RaiseLastOsError_Code
);
1376 {$IFDEF COMPILER_9_UP}
1377 GDefaultSystemCodePage
:= GetACP
;
1379 {$IFDEF COMPILER_7_UP}
1380 if (Win32Platform
= VER_PLATFORM_WIN32_NT
) and (Win32MajorVersion
>= 5) then
1381 GDefaultSystemCodePage
:= CP_THREAD_ACP
// Win 2K/XP/...
1383 GDefaultSystemCodePage
:= LCIDToCodePage(GetThreadLocale
); // Win NT4/95/98/ME
1385 GDefaultSystemCodePage
:= CP_ACP
{TNT-ALLOW CP_ACP};
1388 {$IFNDEF COMPILER_9_UP}
1389 StartupDefaultUserCodePage
:= DefaultSystemCodePage
;
1391 IsDebugging
:= DebugHook
> 0;
1394 UninstallSystemOverrides
;
1395 FreeTntSystemThreadVars
; { Make MemorySleuth happy. }