-
Notifications
You must be signed in to change notification settings - Fork 0
/
VCLFixes.pas
158 lines (129 loc) · 4.8 KB
/
VCLFixes.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
unit VCLFixes;
interface
implementation
uses Windows, Messages, Controls, SysUtils;
// WMDrawItem fails under WOW64, see http://qc.codegear.com/wc/qcmain.aspx?d=19859
{$IFDEF VER150} // Delphi7
function GetMethodAddress(AMessageID: Word; AClass: TClass; out MethodAddr: Pointer): Boolean;
var
DynamicTableAddress: Pointer;
MethodEntry: ^Pointer;
MessageHandlerList: PWord;
EntryCount, EntryIndex: Word;
begin
Result := False;
DynamicTableAddress := Pointer(PInteger(Integer(AClass) + vmtDynamicTable)^);
MessageHandlerList := PWord(DynamicTableAddress);
EntryCount := MessageHandlerList^;
if EntryCount > 0 then
for EntryIndex := EntryCount - 1 downto 0 do begin
Inc(MessageHandlerList);
if (MessageHandlerList^ = AMessageID) then begin
Inc(MessageHandlerList);
MethodEntry := Pointer(Integer(MessageHandlerList) + 2 * (2 * EntryCount - EntryIndex) - 4);
MethodAddr := MethodEntry^;
Result := True;
end;
end;
end;
function PatchInstructionByte(MethodAddress: Pointer; ExpectedOffset: Cardinal;
ExpectedValue: Byte; NewValue: Byte): Boolean;
var
BytePtr: PByte;
OldProtect: Cardinal;
begin
Result := False;
BytePtr := PByte(Cardinal(MethodAddress) + ExpectedOffset);
if BytePtr^ = NewValue then begin
Result := True;
Exit;
end;
if BytePtr^ <> ExpectedValue then
Exit;
if VirtualProtect(BytePtr, SizeOf(BytePtr^), PAGE_EXECUTE_READWRITE, OldProtect) then begin
try
BytePtr^ := NewValue;
Result := True;
finally
Result := Result
and VirtualProtect(BytePtr, SizeOf(BytePtr^), OldProtect, OldProtect)
and FlushInstructionCache(GetCurrentProcess, BytePtr, SizeOf(BytePtr^));
end;
end;
end;
function PatchInstructionBytes(MethodAddress: Pointer; ExpectedOffset: Cardinal;
const ExpectedValues: array of Byte; const NewValues: array of Byte;
const PatchedValues: array of Byte): Boolean;
var
BytePtr, TestPtr: PByte;
OldProtect, Index, PatchSize: Cardinal;
begin
BytePtr := PByte(Cardinal(MethodAddress) + ExpectedOffset);
Result := True;
TestPtr := BytePtr;
for Index := Low(PatchedValues) to High(PatchedValues) do begin
if TestPtr^ <> PatchedValues[Index] then begin
Result := False;
Break;
end;
Inc(TestPtr);
end;
if Result then
Exit;
Result := True;
TestPtr := BytePtr;
for Index := Low(ExpectedValues) to High(ExpectedValues) do begin
if TestPtr^ <> ExpectedValues[Index] then begin
Result := False;
Exit;
end;
Inc(TestPtr);
end;
PatchSize := Length(NewValues) * SizeOf(Byte);
if VirtualProtect(BytePtr, PatchSize, PAGE_EXECUTE_READWRITE, OldProtect) then begin
try
TestPtr := BytePtr;
for Index := Low(NewValues) to High(NewValues) do begin
TestPtr^ := NewValues[Index];
Inc(TestPtr);
end;
Result := True;
finally
Result := Result
and VirtualProtect(BytePtr, PatchSize, OldProtect, OldProtect)
and FlushInstructionCache(GetCurrentProcess, BytePtr, PatchSize);
end;
end;
end;
procedure PatchWinControl;
var
MethodAddress: Pointer;
begin
if not GetMethodAddress(WM_DRAWITEM, TWinControl, MethodAddress) then
raise Exception.Create('Cannot find WM_DRAWITEM handler in TWinControl');
if (not PatchInstructionByte(MethodAddress, 13, $4, $14)) // release and package
and (not PatchInstructionByte(MethodAddress, 23, $4, $14)) then // debug
raise Exception.Create('Cannot patch WM_DRAWITEM');
if not GetMethodAddress(WM_COMPAREITEM, TWinControl, MethodAddress) then
raise Exception.Create('Cannot find WM_COMPAREITEM handler in TWinControl');
if (not PatchInstructionByte(MethodAddress, 13, $04, $8)) // release and package
and (not PatchInstructionByte(MethodAddress, 23, $04, $8)) then // debug
raise Exception.Create('Cannot patch WM_COMPAREITEM handler');
if not GetMethodAddress(WM_DELETEITEM, TWinControl, MethodAddress) then
raise Exception.Create('Cannot find WM_DELETEITEM handler in TWinControl');
if (not PatchInstructionByte(MethodAddress, 13, $04, $0C)) // release and package
and (not PatchInstructionByte(MethodAddress, 23, $04, $0C)) then // debug
raise Exception.Create('Cannot patch WM_DELETEITEM handler');
if not GetMethodAddress(WM_MEASUREITEM, TWinControl, MethodAddress) then
raise Exception.Create('Cannot find WM_MEASUREITEM handler in TWinControl');
if (not PatchInstructionBytes(MethodAddress, 10, [$08, $8B], [$04, $90, $90, $90], [$04, $E8])) // release and package
and (not PatchInstructionBytes(MethodAddress, 20, [$08, $8B], [$04, $90, $90, $90], [$04, $E8])) then // debug
raise Exception.Create('Cannot patch WM_MEASUREITEM handler');
end;
{$ENDIF}
// end of "WMDrawItem fails under WOW64" patch
initialization
{$IFDEF VER150} // Delphi7
PatchWinControl;
{$ENDIF}
end.