{*****************************************************************************} { } { Tnt Delphi Unicode Controls } { http://www.tntware.com/delphicontrols/unicode/ } { Version: 2.3.0 } { } { Copyright (c) 2002-2007, Troy Wolbrink (troy.wolbrink@tntware.com) } { } {*****************************************************************************} unit TntSystem; {$INCLUDE TntCompilers.inc} {*****************************************************************************} { Special thanks go to Francisco Leong for originating the design for } { WideString-enabled resourcestrings. } {*****************************************************************************} interface uses Windows; // These functions should not be used by Delphi code since conversions are implicit. {TNT-WARN WideCharToString} {TNT-WARN WideCharLenToString} {TNT-WARN WideCharToStrVar} {TNT-WARN WideCharLenToStrVar} {TNT-WARN StringToWideChar} // ................ ANSI TYPES ................ {TNT-WARN Char} {TNT-WARN PChar} {TNT-WARN String} {TNT-WARN CP_ACP} // <-- use DefaultSystemCodePage function DefaultSystemCodePage: Cardinal; // implicitly used when converting AnsiString <--> WideString. var WideCustomLoadResString: function(ResStringRec: PResStringRec; var Value: WideString): Boolean; {TNT-WARN LoadResString} function WideLoadResString(ResStringRec: PResStringRec): WideString; {TNT-WARN ParamCount} function WideParamCount: Integer; {TNT-WARN ParamStr} function WideParamStr(Index: Integer): WideString; // ......... introduced ......... const { Each Unicode stream should begin with the code U+FEFF, } { which the standard defines as the *byte order mark*. } UNICODE_BOM = WideChar($FEFF); UNICODE_BOM_SWAPPED = WideChar($FFFE); UTF8_BOM = AnsiString(#$EF#$BB#$BF); function WideStringToUTF8(const S: WideString): AnsiString; function UTF8ToWideString(const S: AnsiString): WideString; function WideStringToUTF7(const W: WideString): AnsiString; function UTF7ToWideString(const S: AnsiString): WideString; function StringToWideStringEx(const S: AnsiString; CodePage: Cardinal): WideString; function WideStringToStringEx(const WS: WideString; CodePage: Cardinal): AnsiString; function UCS2ToWideString(const Value: AnsiString): WideString; function WideStringToUCS2(const Value: WideString): AnsiString; function CharSetToCodePage(ciCharset: UINT): Cardinal; function LCIDToCodePage(ALcid: LCID): Cardinal; function KeyboardCodePage: Cardinal; function KeyUnicode(CharCode: Word): WideChar; procedure StrSwapByteOrder(Str: PWideChar); type TTntSystemUpdate = (tsWideResourceStrings {$IFNDEF COMPILER_9_UP}, tsFixImplicitCodePage, tsFixWideStrConcat, tsFixWideFormat {$ENDIF} ); TTntSystemUpdateSet = set of TTntSystemUpdate; const AllTntSystemUpdates = [Low(TTntSystemUpdate)..High(TTntSystemUpdate)]; procedure InstallTntSystemUpdates(Updates: TTntSystemUpdateSet = AllTntSystemUpdates); implementation uses SysUtils, Variants, TntWindows, TntSysUtils; var GDefaultSystemCodePage: Cardinal; function DefaultSystemCodePage: Cardinal; begin Result := GDefaultSystemCodePage; end; var IsDebugging: Boolean; function WideLoadResString(ResStringRec: PResStringRec): WideString; const MAX_RES_STRING_SIZE = 4097; { MSDN documents this as the maximum size of a string in table. } var Buffer: array [0..MAX_RES_STRING_SIZE] of WideChar; { Buffer leaves room for null terminator. } PCustom: PAnsiChar; begin if Assigned(WideCustomLoadResString) and WideCustomLoadResString(ResStringRec, Result) then exit; { a custom resourcestring has been loaded. } if ResStringRec = nil then Result := '' else if ResStringRec.Identifier < 64*1024 then SetString(Result, Buffer, Tnt_LoadStringW(FindResourceHInstance(ResStringRec.Module^), ResStringRec.Identifier, Buffer, MAX_RES_STRING_SIZE)) else begin // custom string pointer PCustom := PAnsiChar(ResStringRec.Identifier); { I would like to use PWideChar, but this would break legacy code. } if (StrLen{TNT-ALLOW StrLen}(PCustom) > Cardinal(Length(UTF8_BOM))) and CompareMem(PCustom, PAnsiChar(UTF8_BOM), Length(UTF8_BOM)) then // detected UTF8 Result := UTF8ToWideString(PAnsiChar(PCustom + Length(UTF8_BOM))) else // normal Result := PCustom; end; end; function WideGetParamStr(P: PWideChar; var Param: WideString): PWideChar; var i, Len: Integer; Start, S, Q: PWideChar; begin while True do begin while (P[0] <> #0) and (P[0] <= ' ') do Inc(P); if (P[0] = '"') and (P[1] = '"') then Inc(P, 2) else Break; end; Len := 0; Start := P; while P[0] > ' ' do begin if P[0] = '"' then begin Inc(P); while (P[0] <> #0) and (P[0] <> '"') do begin Q := P + 1; Inc(Len, Q - P); P := Q; end; if P[0] <> #0 then Inc(P); end else begin Q := P + 1; Inc(Len, Q - P); P := Q; end; end; SetLength(Param, Len); P := Start; S := PWideChar(Param); i := 0; while P[0] > ' ' do begin if P[0] = '"' then begin Inc(P); while (P[0] <> #0) and (P[0] <> '"') do begin Q := P + 1; while P < Q do begin S[i] := P^; Inc(P); Inc(i); end; end; if P[0] <> #0 then Inc(P); end else begin Q := P + 1; while P < Q do begin S[i] := P^; Inc(P); Inc(i); end; end; end; Result := P; end; function WideParamCount: Integer; var P: PWideChar; S: WideString; begin P := WideGetParamStr(GetCommandLineW, S); Result := 0; while True do begin P := WideGetParamStr(P, S); if S = '' then Break; Inc(Result); end; end; function WideParamStr(Index: Integer): WideString; var P: PWideChar; begin if Index = 0 then Result := WideGetModuleFileName(0) else begin P := GetCommandLineW; while True do begin P := WideGetParamStr(P, Result); if (Index = 0) or (Result = '') then Break; Dec(Index); end; end; end; function WideStringToUTF8(const S: WideString): AnsiString; begin Result := UTF8Encode(S); end; function UTF8ToWideString(const S: AnsiString): WideString; begin Result := UTF8Decode(S); end; { ======================================================================= } { Original File: ConvertUTF7.c } { Author: David B. Goldsmith } { Copyright (C) 1994, 1996 Taligent, Inc. All rights reserved. } { } { This code is copyrighted. Under the copyright laws, this code may not } { be copied, in whole or part, without prior written consent of Taligent. } { } { Taligent grants the right to use this code as long as this ENTIRE } { copyright notice is reproduced in the code. The code is provided } { AS-IS, AND TALIGENT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR } { IMPLIED, INCLUDING, BUT NOT LIMITED TO IMPLIED WARRANTIES OF } { MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT } { WILL TALIGENT BE LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING, } { WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS } { INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY } { LOSS) ARISING OUT OF THE USE OR INABILITY TO USE THIS CODE, EVEN } { IF TALIGENT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. } { BECAUSE SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF } { LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE ABOVE } { LIMITATION MAY NOT APPLY TO YOU. } { } { RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the } { government is subject to restrictions as set forth in subparagraph } { (c)(l)(ii) of the Rights in Technical Data and Computer Software } { clause at DFARS 252.227-7013 and FAR 52.227-19. } { } { This code may be protected by one or more U.S. and International } { Patents. } { } { TRADEMARKS: Taligent and the Taligent Design Mark are registered } { trademarks of Taligent, Inc. } { ======================================================================= } type UCS2 = Word; const _base64: AnsiString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; _direct: AnsiString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789''(),-./:?'; _optional: AnsiString = '!"#$%&*;<=>@[]^_`{|}'; _spaces: AnsiString = #9#13#10#32; var base64: PAnsiChar; invbase64: array[0..127] of SmallInt; direct: PAnsiChar; optional: PAnsiChar; spaces: PAnsiChar; mustshiftsafe: array[0..127] of AnsiChar; mustshiftopt: array[0..127] of AnsiChar; var needtables: Boolean = True; procedure Initialize_UTF7_Data; begin base64 := PAnsiChar(_base64); direct := PAnsiChar(_direct); optional := PAnsiChar(_optional); spaces := PAnsiChar(_spaces); end; procedure tabinit; var i: Integer; limit: Integer; begin i := 0; while (i < 128) do begin mustshiftopt[i] := #1; mustshiftsafe[i] := #1; invbase64[i] := -1; Inc(i); end { For }; limit := Length(_Direct); i := 0; while (i < limit) do begin mustshiftopt[Integer(direct[i])] := #0; mustshiftsafe[Integer(direct[i])] := #0; Inc(i); end { For }; limit := Length(_Spaces); i := 0; while (i < limit) do begin mustshiftopt[Integer(spaces[i])] := #0; mustshiftsafe[Integer(spaces[i])] := #0; Inc(i); end { For }; limit := Length(_Optional); i := 0; while (i < limit) do begin mustshiftopt[Integer(optional[i])] := #0; Inc(i); end { For }; limit := Length(_Base64); i := 0; while (i < limit) do begin invbase64[Integer(base64[i])] := i; Inc(i); end { For }; needtables := False; end; { tabinit } function WRITE_N_BITS(x: UCS2; n: Integer; var BITbuffer: Cardinal; var bufferbits: Integer): Integer; begin BITbuffer := BITbuffer or (x and (not (-1 shl n))) shl (32 - n - bufferbits); bufferbits := bufferbits + n; Result := bufferbits; end; { WRITE_N_BITS } function READ_N_BITS(n: Integer; var BITbuffer: Cardinal; var bufferbits: Integer): UCS2; var buffertemp: Cardinal; begin buffertemp := BITbuffer shr (32 - n); BITbuffer := BITbuffer shl n; bufferbits := bufferbits - n; Result := UCS2(buffertemp); end; { READ_N_BITS } function ConvertUCS2toUTF7(var sourceStart: PWideChar; sourceEnd: PWideChar; var targetStart: PAnsiChar; targetEnd: PAnsiChar; optional: Boolean; verbose: Boolean): Integer; var r: UCS2; target: PAnsiChar; source: PWideChar; BITbuffer: Cardinal; bufferbits: Integer; shifted: Boolean; needshift: Boolean; done: Boolean; mustshift: PAnsiChar; begin Initialize_UTF7_Data; Result := 0; BITbuffer := 0; bufferbits := 0; shifted := False; source := sourceStart; target := targetStart; r := 0; if needtables then tabinit; if optional then mustshift := @mustshiftopt[0] else mustshift := @mustshiftsafe[0]; repeat done := source >= sourceEnd; if not Done then begin r := Word(source^); Inc(Source); end { If }; needshift := (not done) and ((r > $7F) or (mustshift[r] <> #0)); if needshift and (not shifted) then begin if (Target >= TargetEnd) then begin Result := 2; break; end { If }; target^ := '+'; Inc(target); { Special case handling of the SHIFT_IN character } if (r = UCS2('+')) then begin if (target >= targetEnd) then begin Result := 2; break; end; target^ := '-'; Inc(target); end else shifted := True; end { If }; if shifted then begin { Either write the character to the bit buffer, or pad } { the bit buffer out to a full base64 character. } { } if needshift then WRITE_N_BITS(r, 16, BITbuffer, bufferbits) else WRITE_N_BITS(0, (6 - (bufferbits mod 6)) mod 6, BITbuffer, bufferbits); { Flush out as many full base64 characters as possible } { from the bit buffer. } { } while (target < targetEnd) and (bufferbits >= 6) do begin Target^ := base64[READ_N_BITS(6, BITbuffer, bufferbits)]; Inc(Target); end { While }; if (bufferbits >= 6) then begin if (target >= targetEnd) then begin Result := 2; break; end { If }; end { If }; if (not needshift) then begin { Write the explicit shift out character if } { 1) The caller has requested we always do it, or } { 2) The directly encoded character is in the } { base64 set, or } { 3) The directly encoded character is SHIFT_OUT. } { } if verbose or ((not done) and ((invbase64[r] >= 0) or (r = Integer('-')))) then begin if (target >= targetEnd) then begin Result := 2; Break; end { If }; Target^ := '-'; Inc(Target); end { If }; shifted := False; end { If }; { The character can be directly encoded as ASCII. } end { If }; if (not needshift) and (not done) then begin if (target >= targetEnd) then begin Result := 2; break; end { If }; Target^ := AnsiChar(r); Inc(Target); end { If }; until (done); sourceStart := source; targetStart := target; end; { ConvertUCS2toUTF7 } function ConvertUTF7toUCS2(var sourceStart: PAnsiChar; sourceEnd: PAnsiChar; var targetStart: PWideChar; targetEnd: PWideChar): Integer; var target: PWideChar { Register }; source: PAnsiChar { Register }; BITbuffer: Cardinal { & "Address Of" Used }; bufferbits: Integer { & "Address Of" Used }; shifted: Boolean { Used In Boolean Context }; first: Boolean { Used In Boolean Context }; wroteone: Boolean; base64EOF: Boolean; base64value: Integer; done: Boolean; c: UCS2; prevc: UCS2; junk: UCS2 { Used In Boolean Context }; begin Initialize_UTF7_Data; Result := 0; BITbuffer := 0; bufferbits := 0; shifted := False; first := False; wroteone := False; source := sourceStart; target := targetStart; c := 0; if needtables then tabinit; repeat { read an ASCII character c } done := Source >= SourceEnd; if (not done) then begin c := Word(Source^); Inc(Source); end { If }; if shifted then begin { We're done with a base64 string if we hit EOF, it's not a valid } { ASCII character, or it's not in the base64 set. } { } base64value := invbase64[c]; base64EOF := (done or (c > $7F)) or (base64value < 0); if base64EOF then begin shifted := False; { If the character causing us to drop out was SHIFT_IN or } { SHIFT_OUT, it may be a special escape for SHIFT_IN. The } { test for SHIFT_IN is not necessary, but allows an alternate } { form of UTF-7 where SHIFT_IN is escaped by SHIFT_IN. This } { only works for some values of SHIFT_IN. } { } if ((not done) and ((c = Integer('+')) or (c = Integer('-')))) then begin { get another character c } prevc := c; Done := Source >= SourceEnd; if (not Done) then begin c := Word(Source^); Inc(Source); { If no base64 characters were encountered, and the } { character terminating the shift sequence was } { SHIFT_OUT, then it's a special escape for SHIFT_IN. } { } end; if first and (prevc = Integer('-')) then begin { write SHIFT_IN unicode } if (target >= targetEnd) then begin Result := 2; break; end { If }; Target^ := WideChar('+'); Inc(Target); end else begin if (not wroteone) then begin Result := 1; end { If }; end { Else }; ; end { If } else begin if (not wroteone) then begin Result := 1; end { If }; end { Else }; end { If } else begin { Add another 6 bits of base64 to the bit buffer. } WRITE_N_BITS(base64value, 6, BITbuffer, bufferbits); first := False; end { Else }; { Extract as many full 16 bit characters as possible from the } { bit buffer. } { } while (bufferbits >= 16) and (target < targetEnd) do begin { write a unicode } Target^ := WideChar(READ_N_BITS(16, BITbuffer, bufferbits)); Inc(Target); wroteone := True; end { While }; if (bufferbits >= 16) then begin if (target >= targetEnd) then begin Result := 2; Break; end; end { If }; if (base64EOF) then begin junk := READ_N_BITS(bufferbits, BITbuffer, bufferbits); if (junk <> 0) then begin Result := 1; end { If }; end { If }; end { If }; if (not shifted) and (not done) then begin if (c = Integer('+')) then begin shifted := True; first := True; wroteone := False; end { If } else begin { It must be a directly encoded character. } if (c > $7F) then begin Result := 1; end { If }; if (target >= targetEnd) then begin Result := 2; break; end { If }; Target^ := WideChar(c); Inc(Target); end { Else }; end { If }; until (done); sourceStart := source; targetStart := target; end; { ConvertUTF7toUCS2 } {*****************************************************************************} { Thanks to Francisco Leong for providing the Pascal conversion of } { ConvertUTF7.c (by David B. Goldsmith) } {*****************************************************************************} resourcestring SBufferOverflow = 'Buffer overflow'; SInvalidUTF7 = 'Invalid UTF7'; function WideStringToUTF7(const W: WideString): AnsiString; var SourceStart, SourceEnd: PWideChar; TargetStart, TargetEnd: PAnsiChar; begin if W = '' then Result := '' else begin SetLength(Result, Length(W) * 7); // Assume worst case SourceStart := PWideChar(@W[1]); SourceEnd := PWideChar(@W[Length(W)]) + 1; TargetStart := PAnsiChar(@Result[1]); TargetEnd := PAnsiChar(@Result[Length(Result)]) + 1; if ConvertUCS2toUTF7(SourceStart, SourceEnd, TargetStart, TargetEnd, True, False) <> 0 then raise ETntInternalError.Create(SBufferOverflow); SetLength(Result, TargetStart - PAnsiChar(@Result[1])); end; end; function UTF7ToWideString(const S: AnsiString): WideString; var SourceStart, SourceEnd: PAnsiChar; TargetStart, TargetEnd: PWideChar; begin if (S = '') then Result := '' else begin SetLength(Result, Length(S)); // Assume Worst case SourceStart := PAnsiChar(@S[1]); SourceEnd := PAnsiChar(@S[Length(S)]) + 1; TargetStart := PWideChar(@Result[1]); TargetEnd := PWideChar(@Result[Length(Result)]) + 1; case ConvertUTF7toUCS2(SourceStart, SourceEnd, TargetStart, TargetEnd) of 1: raise ETntGeneralError.Create(SInvalidUTF7); 2: raise ETntInternalError.Create(SBufferOverflow); end; SetLength(Result, TargetStart - PWideChar(@Result[1])); end; end; function StringToWideStringEx(const S: AnsiString; CodePage: Cardinal): WideString; var InputLength, OutputLength: Integer; begin if CodePage = CP_UTF7 then Result := UTF7ToWideString(S) // CP_UTF7 not supported on Windows 95 else if CodePage = CP_UTF8 then Result := UTF8ToWideString(S) // CP_UTF8 not supported on Windows 95 else begin InputLength := Length(S); OutputLength := MultiByteToWideChar(CodePage, 0, PAnsiChar(S), InputLength, nil, 0); SetLength(Result, OutputLength); MultiByteToWideChar(CodePage, 0, PAnsiChar(S), InputLength, PWideChar(Result), OutputLength); end; end; function WideStringToStringEx(const WS: WideString; CodePage: Cardinal): AnsiString; var InputLength, OutputLength: Integer; begin if CodePage = CP_UTF7 then Result := WideStringToUTF7(WS) // CP_UTF7 not supported on Windows 95 else if CodePage = CP_UTF8 then Result := WideStringToUTF8(WS) // CP_UTF8 not supported on Windows 95 else begin InputLength := Length(WS); OutputLength := WideCharToMultiByte(CodePage, 0, PWideChar(WS), InputLength, nil, 0, nil, nil); SetLength(Result, OutputLength); WideCharToMultiByte(CodePage, 0, PWideChar(WS), InputLength, PAnsiChar(Result), OutputLength, nil, nil); end; end; function UCS2ToWideString(const Value: AnsiString): WideString; begin if Length(Value) = 0 then Result := '' else SetString(Result, PWideChar(@Value[1]), Length(Value) div SizeOf(WideChar)) end; function WideStringToUCS2(const Value: WideString): AnsiString; begin if Length(Value) = 0 then Result := '' else SetString(Result, PAnsiChar(@Value[1]), Length(Value) * SizeOf(WideChar)) end; { Windows.pas doesn't declare TranslateCharsetInfo() correctly. } function TranslateCharsetInfo(lpSrc: PDWORD; var lpCs: TCharsetInfo; dwFlags: DWORD): BOOL; stdcall; external gdi32 name 'TranslateCharsetInfo'; function CharSetToCodePage(ciCharset: UINT): Cardinal; var C: TCharsetInfo; begin Win32Check(TranslateCharsetInfo(PDWORD(ciCharset), C, TCI_SRCCHARSET)); Result := C.ciACP end; function LCIDToCodePage(ALcid: LCID): Cardinal; var Buf: array[0..6] of AnsiChar; begin GetLocaleInfo(ALcid, LOCALE_IDefaultAnsiCodePage, Buf, 6); Result := StrToIntDef(Buf, GetACP); end; function KeyboardCodePage: Cardinal; begin Result := LCIDToCodePage(GetKeyboardLayout(0) and $FFFF); end; function KeyUnicode(CharCode: Word): WideChar; var AChar: AnsiChar; begin // converts the given character (as it comes with a WM_CHAR message) into its // corresponding Unicode character depending on the active keyboard layout if CharCode <= Word(High(AnsiChar)) then begin AChar := AnsiChar(CharCode); MultiByteToWideChar(KeyboardCodePage, MB_USEGLYPHCHARS, @AChar, 1, @Result, 1); end else Result := WideChar(CharCode); end; procedure StrSwapByteOrder(Str: PWideChar); var P: PWord; begin P := PWord(Str); While (P^ <> 0) do begin P^ := MakeWord(HiByte(P^), LoByte(P^)); Inc(P); end; end; //-------------------------------------------------------------------- // LoadResString() // // This system function is used to retrieve a resourcestring and // return the result as an AnsiString. If we believe that the result // is only a temporary value, and that it will be immediately // assigned to a WideString or a Variant, then we will save the // Unicode result as well as a reference to the original Ansi string. // WStrFromPCharLen() or VarFromLStr() will return this saved // Unicode string if it appears to receive the most recent result // of LoadResString. //-------------------------------------------------------------------- //=========================================================================================== // // function CodeMatchesPatternForUnicode(...); // // GIVEN: SomeWideString := SSomeResString; { WideString := resourcestring } // // Delphi will compile this statement into the following: // ------------------------------------------------- // TempAnsiString := LoadResString(@SSomeResString); // LINE 1: lea edx,[SomeTempAnsiString] // LINE 2: mov eax,[@SomeResString] // LINE 3: call LoadResString // // WStrFromLStr(SomeWideString, TempAnsiString); { SomeWideString := TempAnsiString } // LINE 4: mov edx,[SomeTempAnsiString] // LINE 5: mov/lea eax [@SomeWideString] // LINE 6: call @WStrFromLStr // ------------------------------------------------- // // The order in which the parameters are prepared for WStrFromLStr (ie LINE 4 & 5) is // reversed when assigning a non-temporary AnsiString to a WideString. // // This code, for example, results in LINE 4 and LINE 5 being swapped. // // SomeAnsiString := SSomeResString; // SomeWideString := SomeAnsiString; // // Since we know the "signature" used by the compiler, we can detect this pattern. // If we believe it is only temporary, we can save the Unicode results for later // retrieval from WStrFromLStr. // // One final note: When assigning a resourcestring to a Variant, the same patterns exist. //=========================================================================================== function CodeMatchesPatternForUnicode(PLine4: PAnsiChar): Boolean; const SIZEOF_OPCODE = 1 {byte}; MOV_16_OPCODE = AnsiChar($8B); { we'll assume operand size is 16 bits } MOV_32_OPCODE = AnsiChar($B8); { we'll assume operand size is 32 bits } LEA_OPCODE = AnsiChar($8D); { operand size can be 16 or 40 bits } CALL_OPCODE = AnsiChar($E8); { assumed operand size is 32 bits } BREAK_OPCODE = AnsiChar($CC); {in a breakpoint} var PLine1: PAnsiChar; PLine2: PAnsiChar; PLine3: PAnsiChar; DataSize: Integer; // bytes in first LEA operand begin Result := False; PLine3 := PLine4 - SizeOf(CALL_OPCODE) - 4; PLine2 := PLine3 - SizeOf(MOV_32_OPCODE) - 4; // figure PLine1 and operand size DataSize := 2; { try 16 bit operand for line 1 } PLine1 := PLine2 - DataSize - SizeOf(LEA_OPCODE); if (PLine1^ <> LEA_OPCODE) and (not (IsDebugging and (PLine1^ = BREAK_OPCODE))) then begin DataSize := 5; { try 40 bit operand for line 1 } PLine1 := PLine2 - DataSize - SizeOf(LEA_OPCODE); end; if (PLine1^ = LEA_OPCODE) or (IsDebugging and (PLine1^ = BREAK_OPCODE)) then begin if CompareMem(PLine1 + SIZEOF_OPCODE, PLine4 + SIZEOF_OPCODE, DataSize) then begin // After this check, it seems to match the WideString <- (temp) AnsiString pattern Result := True; // It is probably OK. (The side effects of being wrong aren't very bad.) end; end; end; threadvar PLastResString: PAnsiChar; LastResStringValue: AnsiString; LastWideResString: WideString; procedure FreeTntSystemThreadVars; begin LastResStringValue := ''; LastWideResString := ''; end; procedure Custom_System_EndThread(ExitCode: Integer); begin FreeTntSystemThreadVars; {$IFDEF COMPILER_10_UP} if Assigned(SystemThreadEndProc) then SystemThreadEndProc(ExitCode); {$ENDIF} ExitThread(ExitCode); end; function Custom_System_LoadResString(ResStringRec: PResStringRec): AnsiString; var ReturnAddr: Pointer; begin // get return address asm PUSH ECX MOV ECX, [EBP + 4] MOV ReturnAddr, ECX POP ECX end; // check calling code pattern if CodeMatchesPatternForUnicode(ReturnAddr) then begin // result will probably be assigned to an intermediate AnsiString // on its way to either a WideString or Variant. LastWideResString := WideLoadResString(ResStringRec); Result := LastWideResString; LastResStringValue := Result; if Result = '' then PLastResString := nil else PLastResString := PAnsiChar(Result); end else begin // result will probably be assigned to an actual AnsiString variable. PLastResString := nil; Result := WideLoadResString(ResStringRec); end; end; //-------------------------------------------------------------------- // WStrFromPCharLen() // // This system function is used to assign an AnsiString to a WideString. // It has been modified to assign Unicode results from LoadResString. // Another purpose of this function is to specify the code page. //-------------------------------------------------------------------- procedure Custom_System_WStrFromPCharLen(var Dest: WideString; Source: PAnsiChar; Length: Integer); var DestLen: Integer; Buffer: array[0..2047] of WideChar; Local_PLastResString: Pointer; begin Local_PLastResString := PLastResString; if (Local_PLastResString <> nil) and (Local_PLastResString = Source) and (System.Length(LastResStringValue) = Length) and (LastResStringValue = Source) then begin // use last unicode resource string PLastResString := nil; { clear for further use } Dest := LastWideResString; end else begin if Local_PLastResString <> nil then PLastResString := nil; { clear for further use } if Length <= 0 then begin Dest := ''; Exit; end; if Length + 1 < High(Buffer) then begin DestLen := MultiByteToWideChar(DefaultSystemCodePage, 0, Source, Length, Buffer, High(Buffer)); if DestLen > 0 then begin SetLength(Dest, DestLen); Move(Pointer(@Buffer[0])^, Pointer(Dest)^, DestLen * SizeOf(WideChar)); Exit; end; end; DestLen := (Length + 1); SetLength(Dest, DestLen); // overallocate, trim later DestLen := MultiByteToWideChar(DefaultSystemCodePage, 0, Source, Length, Pointer(Dest), DestLen); if DestLen < 0 then DestLen := 0; SetLength(Dest, DestLen); end; end; {$IFNDEF COMPILER_9_UP} //-------------------------------------------------------------------- // LStrFromPWCharLen() // // This system function is used to assign an WideString to an AnsiString. // It has not been modified from its original purpose other than to specify the code page. //-------------------------------------------------------------------- procedure Custom_System_LStrFromPWCharLen(var Dest: AnsiString; Source: PWideChar; Length: Integer); var DestLen: Integer; Buffer: array[0..4095] of AnsiChar; begin if Length <= 0 then begin Dest := ''; Exit; end; if Length + 1 < (High(Buffer) div sizeof(WideChar)) then begin DestLen := WideCharToMultiByte(DefaultSystemCodePage, 0, Source, Length, Buffer, High(Buffer), nil, nil); if DestLen >= 0 then begin SetLength(Dest, DestLen); Move(Pointer(@Buffer[0])^, PAnsiChar(Dest)^, DestLen); Exit; end; end; DestLen := (Length + 1) * sizeof(WideChar); SetLength(Dest, DestLen); // overallocate, trim later DestLen := WideCharToMultiByte(DefaultSystemCodePage, 0, Source, Length, Pointer(Dest), DestLen, nil, nil); if DestLen < 0 then DestLen := 0; SetLength(Dest, DestLen); end; //-------------------------------------------------------------------- // WStrToString() // // This system function is used to assign an WideString to an short string. // It has not been modified from its original purpose other than to specify the code page. //-------------------------------------------------------------------- procedure Custom_System_WStrToString(Dest: PShortString; const Source: WideString; MaxLen: Integer); var SourceLen, DestLen: Integer; Buffer: array[0..511] of AnsiChar; begin if MaxLen > 255 then MaxLen := 255; SourceLen := Length(Source); if SourceLen >= MaxLen then SourceLen := MaxLen; if SourceLen = 0 then DestLen := 0 else begin DestLen := WideCharToMultiByte(DefaultSystemCodePage, 0, Pointer(Source), SourceLen, Buffer, SizeOf(Buffer), nil, nil); if DestLen > MaxLen then DestLen := MaxLen; end; Dest^[0] := Chr(DestLen); if DestLen > 0 then Move(Buffer, Dest^[1], DestLen); end; {$ENDIF} //-------------------------------------------------------------------- // VarFromLStr() // // This system function is used to assign an AnsiString to a Variant. // It has been modified to assign Unicode results from LoadResString. //-------------------------------------------------------------------- procedure Custom_System_VarFromLStr(var V: TVarData; const Value: AnsiString); const varDeepData = $BFE8; var Local_PLastResString: Pointer; begin if (V.VType and varDeepData) <> 0 then VarClear(PVariant(@V)^); Local_PLastResString := PLastResString; if (Local_PLastResString <> nil) and (Local_PLastResString = PAnsiChar(Value)) and (LastResStringValue = Value) then begin // use last unicode resource string PLastResString := nil; { clear for further use } V.VOleStr := nil; V.VType := varOleStr; WideString(Pointer(V.VOleStr)) := Copy(LastWideResString, 1, MaxInt); end else begin if Local_PLastResString <> nil then PLastResString := nil; { clear for further use } V.VString := nil; V.VType := varString; AnsiString(V.VString) := Value; end; end; {$IFNDEF COMPILER_9_UP} //-------------------------------------------------------------------- // WStrCat3() A := B + C; // // This system function is used to concatenate two strings into one result. // This function is added because A := '' + '' doesn't necessarily result in A = ''; //-------------------------------------------------------------------- procedure Custom_System_WStrCat3(var Dest: WideString; const Source1, Source2: WideString); function NewWideString(CharLength: Longint): Pointer; var _NewWideString: function(CharLength: Longint): Pointer; begin asm PUSH ECX MOV ECX, offset System.@NewWideString; MOV _NewWideString, ECX POP ECX end; Result := _NewWideString(CharLength); end; procedure WStrSet(var S: WideString; P: PWideChar); var Temp: Pointer; begin Temp := Pointer(InterlockedExchange(Integer(S), Integer(P))); if Temp <> nil then WideString(Temp) := ''; end; var Source1Len, Source2Len: Integer; NewStr: PWideChar; begin Source1Len := Length(Source1); Source2Len := Length(Source2); if (Source1Len <> 0) or (Source2Len <> 0) then begin NewStr := NewWideString(Source1Len + Source2Len); Move(Pointer(Source1)^, Pointer(NewStr)^, Source1Len * sizeof(WideChar)); Move(Pointer(Source2)^, NewStr[Source1Len], Source2Len * sizeof(WideChar)); WStrSet(Dest, NewStr); end else Dest := ''; end; {$ENDIF} //-------------------------------------------------------------------- // System proc replacements //-------------------------------------------------------------------- type POverwrittenData = ^TOverwrittenData; TOverwrittenData = record Location: Pointer; OldCode: array[0..6] of Byte; end; procedure OverwriteProcedure(OldProcedure, NewProcedure: pointer; Data: POverwrittenData = nil); { OverwriteProcedure originally from Igor Siticov } { Modified by Jacques Garcia Vazquez } var x: PAnsiChar; y: integer; ov2, ov: cardinal; p: pointer; begin if Assigned(Data) and (Data.Location <> nil) then exit; { procedure already overwritten } // need six bytes in place of 5 x := PAnsiChar(OldProcedure); if not VirtualProtect(Pointer(x), 6, PAGE_EXECUTE_READWRITE, @ov) then RaiseLastOSError; // if a jump is present then a redirect is found // $FF25 = jmp dword ptr [xxx] // This redirect is normally present in bpl files, but not in exe files p := OldProcedure; if Word(p^) = $25FF then begin Inc(Integer(p), 2); // skip the jump // get the jump address p^ and dereference it p^^ p := Pointer(Pointer(p^)^); // release the memory if not VirtualProtect(Pointer(x), 6, ov, @ov2) then RaiseLastOSError; // re protect the correct one x := PAnsiChar(p); if not VirtualProtect(Pointer(x), 6, PAGE_EXECUTE_READWRITE, @ov) then RaiseLastOSError; end; if Assigned(Data) then begin Move(x^, Data.OldCode, 6); { Assign Location last so that Location <> nil only if OldCode is properly initialized. } Data.Location := x; end; x[0] := AnsiChar($E9); y := integer(NewProcedure) - integer(p) - 5; x[1] := AnsiChar(y and 255); x[2] := AnsiChar((y shr 8) and 255); x[3] := AnsiChar((y shr 16) and 255); x[4] := AnsiChar((y shr 24) and 255); if not VirtualProtect(Pointer(x), 6, ov, @ov2) then RaiseLastOSError; end; procedure RestoreProcedure(OriginalProc: Pointer; Data: TOverwrittenData); var ov, ov2: Cardinal; begin if Data.Location <> nil then begin if not VirtualProtect(Data.Location, 6, PAGE_EXECUTE_READWRITE, @ov) then RaiseLastOSError; Move(Data.OldCode, Data.Location^, 6); if not VirtualProtect(Data.Location, 6, ov, @ov2) then RaiseLastOSError; end; end; function Addr_System_EndThread: Pointer; begin Result := @System.EndThread; end; function Addr_System_LoadResString: Pointer; begin Result := @System.LoadResString{TNT-ALLOW LoadResString}; end; function Addr_System_WStrFromPCharLen: Pointer; asm mov eax, offset System.@WStrFromPCharLen; end; {$IFNDEF COMPILER_9_UP} function Addr_System_LStrFromPWCharLen: Pointer; asm mov eax, offset System.@LStrFromPWCharLen; end; function Addr_System_WStrToString: Pointer; asm mov eax, offset System.@WStrToString; end; {$ENDIF} function Addr_System_VarFromLStr: Pointer; asm mov eax, offset System.@VarFromLStr; end; function Addr_System_WStrCat3: Pointer; asm mov eax, offset System.@WStrCat3; end; var System_EndThread_Code, System_LoadResString_Code, System_WStrFromPCharLen_Code, {$IFNDEF COMPILER_9_UP} System_LStrFromPWCharLen_Code, System_WStrToString_Code, {$ENDIF} System_VarFromLStr_Code {$IFNDEF COMPILER_9_UP} , System_WStrCat3_Code, SysUtils_WideFmtStr_Code {$ENDIF} : TOverwrittenData; procedure InstallEndThreadOverride; begin OverwriteProcedure(Addr_System_EndThread, @Custom_System_EndThread, @System_EndThread_Code); end; procedure InstallStringConversionOverrides; begin OverwriteProcedure(Addr_System_WStrFromPCharLen, @Custom_System_WStrFromPCharLen, @System_WStrFromPCharLen_Code); {$IFNDEF COMPILER_9_UP} OverwriteProcedure(Addr_System_LStrFromPWCharLen, @Custom_System_LStrFromPWCharLen, @System_LStrFromPWCharLen_Code); OverwriteProcedure(Addr_System_WStrToString, @Custom_System_WStrToString, @System_WStrToString_Code); {$ENDIF} end; procedure InstallWideResourceStrings; begin OverwriteProcedure(Addr_System_LoadResString, @Custom_System_LoadResString, @System_LoadResString_Code); OverwriteProcedure(Addr_System_VarFromLStr, @Custom_System_VarFromLStr, @System_VarFromLStr_Code); end; {$IFNDEF COMPILER_9_UP} procedure InstallWideStringConcatenationFix; begin OverwriteProcedure(Addr_System_WStrCat3, @Custom_System_WStrCat3, @System_WStrCat3_Code); end; procedure InstallWideFormatFixes; begin OverwriteProcedure(@SysUtils.WideFmtStr, @TntSysUtils.Tnt_WideFmtStr, @SysUtils_WideFmtStr_Code); end; {$ENDIF} procedure InstallTntSystemUpdates(Updates: TTntSystemUpdateSet = AllTntSystemUpdates); begin InstallEndThreadOverride; if tsWideResourceStrings in Updates then begin InstallStringConversionOverrides; InstallWideResourceStrings; end; {$IFNDEF COMPILER_9_UP} if tsFixImplicitCodePage in Updates then begin InstallStringConversionOverrides; { CP_ACP is the code page used by the non-Unicode Windows API. } GDefaultSystemCodePage := CP_ACP{TNT-ALLOW CP_ACP}; end; if tsFixWideStrConcat in Updates then begin InstallWideStringConcatenationFix; end; if tsFixWideFormat in Updates then begin InstallWideFormatFixes; end; {$ENDIF} end; {$IFNDEF COMPILER_9_UP} var StartupDefaultUserCodePage: Cardinal; {$ENDIF} procedure UninstallSystemOverrides; begin RestoreProcedure(Addr_System_EndThread, System_EndThread_Code); // String Conversion RestoreProcedure(Addr_System_WStrFromPCharLen, System_WStrFromPCharLen_Code); {$IFNDEF COMPILER_9_UP} RestoreProcedure(Addr_System_LStrFromPWCharLen, System_LStrFromPWCharLen_Code); RestoreProcedure(Addr_System_WStrToString, System_WStrToString_Code); GDefaultSystemCodePage := StartupDefaultUserCodePage; {$ENDIF} // Wide resourcestring RestoreProcedure(Addr_System_LoadResString, System_LoadResString_Code); RestoreProcedure(Addr_System_VarFromLStr, System_VarFromLStr_Code); {$IFNDEF COMPILER_9_UP} // WideString concat fix RestoreProcedure(Addr_System_WStrCat3, System_WStrCat3_Code); // WideFormat fixes RestoreProcedure(@SysUtils.WideFmtStr, SysUtils_WideFmtStr_Code); {$ENDIF} end; initialization {$IFDEF COMPILER_9_UP} GDefaultSystemCodePage := GetACP; {$ELSE} {$IFDEF COMPILER_7_UP} if (Win32Platform = VER_PLATFORM_WIN32_NT) and (Win32MajorVersion >= 5) then GDefaultSystemCodePage := CP_THREAD_ACP // Win 2K/XP/... else GDefaultSystemCodePage := LCIDToCodePage(GetThreadLocale); // Win NT4/95/98/ME {$ELSE} GDefaultSystemCodePage := CP_ACP{TNT-ALLOW CP_ACP}; {$ENDIF} {$ENDIF} {$IFNDEF COMPILER_9_UP} StartupDefaultUserCodePage := DefaultSystemCodePage; {$ENDIF} IsDebugging := DebugHook > 0; finalization UninstallSystemOverrides; FreeTntSystemThreadVars; { Make MemorySleuth happy. } end.