-
-
Notifications
You must be signed in to change notification settings - Fork 327
/
SynFastWideString.pas
258 lines (204 loc) · 9.66 KB
/
SynFastWideString.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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/// this unit will patch the System.pas RTL to use a custom NON OLE COMPATIBLE
// WideString type, NOT using the slow Windows API, but FastMM4 (without COW)
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SynFastWideString;
interface
(*
This file is part of Synopse Framework.
Synopse Framework. Copyright (c) Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the License.
The Original Code is Synopse Framework.
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (c)
the Initial Developer. All Rights Reserved.
Contributor(s):
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****
DISCLAIMER:
Purpose of this unit is to patch the system.pas unit for older versions
of Delphi, so that WideString memory allocation would use FastMM4 instead
of the slow BSTR Windows API.
It will speed up the WideString process a lot, especially when a lot of
content is allocated, since FastMM4 is much more aggressive than Windows'
global heap and the BSTR slow API. It could be more than 50 times faster,
especially when releasing the used memory.
The WideString implementation pattern does NOT feature Copy-On-Write, so is
slower than the string=UnicodeString type as implemented since Delphi 2009.
This is the reason why this unit won't do anything on Unicode versions of
the compiler, since the new string type is to be preferred.
HOW TO USE:
Just add the unit at the TOP of your .dpr uses clause, just after FastMM4
(if you use it, and you should!) i.e. before all other units used by your
program. It should be initialized before any WideString is allocated.
Then the patch will be applied at runtime. Nothing to recompile!
program MyProgram;
uses
{$I SynDprUses.inc} // will enable FastMM4 prior to Delphi 2006
SynFastWideString, // will use FastMM4 prior to Delphi 2009
...
NOTE:
Since we add a trailing 0 byte at the end of the buffer, we need the
memory manager to let enough place for it: oldest Borland MM does not :(
SO IF YOU WORK WITH A VERSION PRIOR TO DELPHI 2006, ADD FASTMM4 TO YOUR .DPR
WARNING:
------------------------------------------------------------------
USING THIS UNIT MAY BREAK COMPATIBILITY WITH OLE/COM LIBRARIES !
------------------------------------------------------------------
You won't be able to retrieve and release WideString/BSTR variables from an
OleDB / ADO database provider, or any COM object.
Do not use this unit if you are calling such external call!
In practice, if you only SEND some BSTR content to the provider (e.g. if
you use our SynOleDB unit without stored procedure call, or if you use
TWideString fields for most SynDBDataSet classes), it will work.
You would have issues only if you *retrieve* a BSTR from the COM object,
or expect the COM object to *change* the BSTR size, e.g. with a "var"
WideString parameter or a COM method returning a WideString. In this case,
you could use the WideStringFree() procedure to release such an instance.
It is for educational purpose only, and/or if you are 100% sure that your
code will stay self-contained, under Delphi 7 or Delphi 2007, and need use
of WideString instead of string=AnsiString.
-----------------------------------------------
YOU HAVE BEEN WARNED - USE AT YOUR OWN RISK ! :)
-----------------------------------------------
*)
/// this low-level helper can be used to free a WideString returned by COM/OLE
// - WideString instances created with this unit can be safely sent to any
// COM/OLE object, as soon as they are constant parameters, but not a "var"
// parameter or a callback function result
// - any WideString instance returned by a COM object should NOT be released
// by Delphi automatically, since the following would create a memory error:
// ! TrueBSTRWideStringVariable := '';
// - if you are using SynFastWideString, you should use this procedure to
// release true BSTR WideString instance, as such:
// ! type
// ! _Catalog = interface(IDispatch)
// ! // this method will be safe to use with our unit
// ! function Create(const ConnectString: WideString): OleVariant; safecall;
// ! // this method won't be safe, since it returns a true BSTR as WideString
// ! function GetObjectOwner(const ObjectName: WideString; ObjectType: OleVariant;
// ! ObjectTypeId: OleVariant): WideString; safecall;
// ! end;
// !...
// !function CheckCatalogOwner(const catalog: _Catalog): string;
// !var bstr: WideString;
// !begin
// ! try // force manual handling of this true BSTR instance lifetime
// ! bstr := catalog.GetObjectOwner('name',null,null);
// ! result := bstr; // conversion to string will work
// ! finally
// ! WideStringFree(bstr); // manual release, and set bstr := nil
// ! end;
// !end;
// - do a regular TrueBSTRWideStringVariable := '' since Delphi 2009, or
// call the low-level oleaut32.dll API for older versions, as expected by COM
procedure WideStringFree(var TrueBSTRWideStringVariable: WideString);
implementation
{$ifdef UNICODE}
// since Delphi 2009, use string=UnicodeString type, which features CopyOnWrite
// -> do not patch anything
{$define NOOVERRIDE}
{$endif}
{$ifdef FPC}
// our low-level Delphi Win32 specific hack won't work with FPC
{$define NOOVERRIDE}
{$endif}
{$ifdef NOOVERRIDE}
procedure WideStringFree(var TrueBSTRWideStringVariable: WideString);
begin
TrueBSTRWideStringVariable := ''; // regular handling via System.pas
end;
{$else}
uses
Windows;
{$RANGECHECKS OFF}
{$STACKFRAMES OFF}
{$OPTIMIZATION ON}
{$DEBUGINFO OFF}
type // some types here since we do not want any dependency on any other units
PByteArray = ^TByteArray;
TByteArray = array[0..MaxInt-1] of byte;
// we need to patch the oleaut32.dll library calls as defined in System.pas
// -> retrieve CALL address from low-level funtions asm, then the API slot
function _SysAllocStringLen: pointer;
asm
lea eax,System.@WStrFromPWCharLen+8
end;
function _SysReAllocStringLen: pointer;
asm
lea eax,System.@WStrAsg+8
end;
function _SysFreeString: pointer;
asm
lea eax,System.@WStrClr+8
end;
procedure PatchAPI(source,dest: pointer);
var RestoreProtection,Ignore: DWORD;
begin
while PByte(source)^<>$e8 do // search first CALL within the function code
inc(PByte(source));
inc(PByte(source));
inc(PByte(source),PInteger(source)^+SizeOf(integer)); // go to the CALL stub
if PWord(source)^<>$25ff then // expect "jmp dword ptr []" asm
halt;
inc(PWord(source));
source := PPointer(source)^; // get "dword ptr []" address of API redirection
if VirtualProtect(source,SizeOf(source),PAGE_EXECUTE_READWRITE,RestoreProtection) then begin
PPointer(source)^ := dest; // replace oleaut32.dll API with our own function
VirtualProtect(source,SizeOf(source),RestoreProtection,Ignore);
FlushInstructionCache(GetCurrentProcess,source,SizeOf(source));
end;
end;
// those are the 3 redirected API calls -> just use AnsiString for allocation
function SysAllocStringLen(P: PAnsiChar; Len: integer): pointer; stdcall;
begin
result := nil;
Len := Len*2;
SetString(AnsiString(result),P,Len);
PByteArray(result)[Len+1] := 0; // ensure finishes with a #0 WideChar
end;
function SysReAllocStringLen(var S: AnsiString; P: PAnsiChar; Len: integer): LongBool; stdcall;
begin
Len := Len*2;
SetString(S,P,Len);
PByteArray(S)[Len+1] := 0; // ensure finishes with a #0 WideChar
result := true;
end;
procedure SysFreeString(S: pointer); stdcall;
begin
AnsiString(S) := '';
end;
procedure OleAut32SysFreeString(S: pointer); stdcall;
external 'oleaut32.dll' name 'SysFreeString'
procedure WideStringFree(var TrueBSTRWideStringVariable: WideString);
begin
if pointer(TrueBSTRWideStringVariable)<>nil then begin
OleAut32SysFreeString(pointer(TrueBSTRWideStringVariable));
pointer(TrueBSTRWideStringVariable) := nil;
end;
end;
initialization
PatchAPI(_SysAllocStringLen,@SysAllocStringLen);
PatchAPI(_SysReAllocStringLen,@SysReAllocStringLen);
PatchAPI(_SysFreeString,@SysFreeString);
{$endif NOOVERRIDE}
end.