Delphi TThread.CurrentThread y EAccessViolation-¿Es esto un error o mi incompetencia...?
delphi-2009 (4)
En Delphi 2009 estoy descubriendo que cada vez que uso TThread.CurrentThread en una aplicación, aparece un mensaje de error como el siguiente cuando se cierra la aplicación:
Exception EAccessViolation in module ntdll.dll at 0003DBBA.
Access violation at address 7799DBBA in module ''ntdll.dll''. Write of
address 00000014.
A menos que sea solo mi máquina, puede replicar esto en unos pocos segundos: cree una nueva aplicación de formularios de Delphi, agregue un botón al formulario y use algo como lo siguiente para el manejador de eventos del botón:
procedure TForm1.Button1Click(Sender: TObject);
begin
TThread.CurrentThread;
end;
Tanto en mi máquina Vista como en mi máquina XP encuentro que, si no hago clic en el botón, todo está bien, pero si hago clic en el botón, aparece el mensaje de error anterior cuando cierro la aplicación.
Entonces ... me pregunto si esto es un error, pero al mismo tiempo creo que es bastante probable que simplemente no entiendo algo muy básico sobre cómo se supone que debes trabajar con TThreads en Delphi. Soy un poco novato en Delphi, me temo.
¿Hay algo obviamente mal con el uso de TThread.CurrentThread así?
Si no, y tiene Delphi 2009, ¿tiene el mismo problema si implementa mi proyecto de muestra simple?
Actualización: Como señaló François a continuación, esto en realidad es un error en Delphi 2009 en este momento; puedes votar aquí .
Actualización: este error se corrigió en Delphi 2010.
Creo que CurrentThread se agrega en 2009 (o 2007). Tengo 2006 en casa. ¿Pero estás seguro de que es una propiedad de clase?
Lamentablemente, parece un error relacionado con el orden de llamada de la sección de finalización en la unidad de Clases:
DoneThreadSynchronization
borra la estructura ThreadLock
, luego
FreeExternalThreads
desea destruir el objeto Thread que acaba de crear al llamar a CurrentThread
, y
eso requiere que ThreadLock ya esté inicializado en la llamada a
EnterCriticalSection(ThreadLock)
en TThread.RemoveQueuedEvents
...
ACTUALIZAR :
Ahora hay un parche de solución en el informe de control de calidad .
Hasta que CodeGear emita una solución, puede usar el siguiente parche. Guárdelo en una unidad independiente y úselo en cualquier lugar de su programa. Trataré de agregarlo al control de calidad también.
Esta versión funciona con D2009 (original), actualización 1 y actualización 2.
{ Fix Delphi 2009''s invalid finalization order in Classes.pas.
Written by Primoz Gabrijelcic, http://gp.17slon.com.
No rights reserved - released to public domain.
}
unit FixD2009Classes;
interface
implementation
uses
Windows,
SysUtils,
Classes;
type
TCode = array [0..109] of byte;
{$WARN SYMBOL_PLATFORM OFF}
procedure PatchClasses;
{$IFDEF ConditionalExpressions}
{$IF RTLVersion = 20}
var
i : integer;
oldProtect: cardinal;
pCode : ^TCode;
tmp : DWORD;
const
COffsets_Call: array [1..12] of integer = (0, 15, 24, 34, 49, 59, 69, 79, 89, 94, 99, 109);
COffset_UnRegisterModuleClasses = 106;
COffset_DoneThreadSynchronization = 94;
COffset_FreeExternalThreads = 99;
CCallDelta = COffset_FreeExternalThreads - COffset_DoneThreadSynchronization;
{$IFEND}
{$ENDIF}
begin
{$IFDEF ConditionalExpressions}
{$IF RTLVersion = 20}
pCode := pointer(cardinal(@TStreamReader.ReadToEnd) + COffset_UnRegisterModuleClasses);
Win32Check(VirtualProtect(pCode, COffsets_Call[High(COffsets_Call)], PAGE_READWRITE, oldProtect));
try
for i := Low(COffsets_Call) to High(COffsets_Call) do
if pCode^[COffsets_Call[i]] <> $E8 then
raise Exception.Create(''Unexpected version of Classes - cannot patch'');
tmp := PDword(@pCode^[COffset_DoneThreadSynchronization+1])^;
PDword(@pCode^[COffset_DoneThreadSynchronization+1])^ :=
PDword(@pCode^[COffset_FreeExternalThreads+1])^ + CCallDelta;
PDword(@pCode^[COffset_FreeExternalThreads+1])^ := tmp - CCallDelta;
finally VirtualProtect(pCode, COffsets_Call[High(COffsets_Call)], oldProtect, oldProtect); end;
{$IFEND}
{$ENDIF}
end;
initialization
PatchClasses;
end.
Unidad de parche para Delphi 2009 Actualización 3.
{ Fix Delphi 2009''s invalid finalization order in Classes.pas.
Written by Primoz Gabrijelcic, http://gp.17slon.com.
No rights reserved - released to public domain.
D2009 update 3 only.
}
unit FixD2009Classes;
interface
implementation
uses
Windows,
SysUtils,
Classes;
type
TCode = array [0..144] of byte;
{$WARN SYMBOL_PLATFORM OFF}
procedure PatchClasses;
{$IFDEF ConditionalExpressions}
{$IF RTLVersion = 20}
var
i : integer;
oldProtect: cardinal;
pCode : ^TCode;
tmp : DWORD;
const
COffsets_Call: array [1..12] of integer = (0, 15, 24, 42, 47, 58, 73, 91, 101, 111, 134, 139);
COffset_UnRegisterModuleClasses = 107;
COffset_DoneThreadSynchronization = 134;
COffset_FreeExternalThreads = 139;
CCallDelta = COffset_FreeExternalThreads - COffset_DoneThreadSynchronization;
{$IFEND}
{$ENDIF}
begin
{$IFDEF ConditionalExpressions}
{$IF RTLVersion = 20}
pCode := pointer(cardinal(@TStreamReader.ReadToEnd) + COffset_UnRegisterModuleClasses);
Win32Check(VirtualProtect(pCode, COffsets_Call[High(COffsets_Call)], PAGE_READWRITE, oldProtect));
try
for i := Low(COffsets_Call) to High(COffsets_Call) do
if pCode^[COffsets_Call[i]] <> $E8 then
raise Exception.Create(''Unexpected version of Classes - cannot patch'');
tmp := PDword(@pCode^[COffset_DoneThreadSynchronization+1])^;
PDword(@pCode^[COffset_DoneThreadSynchronization+1])^ :=
PDword(@pCode^[COffset_FreeExternalThreads+1])^ + CCallDelta;
PDword(@pCode^[COffset_FreeExternalThreads+1])^ := tmp - CCallDelta;
finally VirtualProtect(pCode, COffsets_Call[High(COffsets_Call)], oldProtect, oldProtect); end;
{$IFEND}
{$ENDIF}
end;
initialization
PatchClasses;
end.