Delphi TThread.CurrentThread и EAccessViolation - это ошибка или моя несостоятельность..?

В Delphi 2009 я обнаружил, что в любое время, когда я использую TThread.CurrentThread в приложении, я получаю сообщение об ошибке, подобное приведенному ниже, когда приложение закрывается:

Exception EAccessViolation in module ntdll.dll at 0003DBBA.
Access violation at address 7799DBBA in module 'ntdll.dll'.  Write of
address 00000014.

Если это только моя машина, вы можете воспроизвести это через несколько секунд: создайте новое приложение форм Delphi, добавьте кнопку в форму и используйте для обработчика событий кнопки что-то вроде следующего:

procedure TForm1.Button1Click(Sender: TObject);
begin
  TThread.CurrentThread;
end;

Как на моей машине Vista, так и на моей машине XP я нахожу это, если я не нажимаю кнопку все хорошо, но если я нажму кнопку, я получу вышеуказанное сообщение об ошибке, когда я закрою приложение.

Итак... Мне интересно, если это ошибка, но в то же время я думаю, что это довольно вероятно, что я просто не понимаю что-то очень простое о том, как вы должны работать с TThreads в Delphi. Боюсь, что я немного новичок в Delphi.

Есть ли что-то явно неправильное в использовании TThread.CurrentThread?

Если нет, и у вас есть Delphi 2009, возникает ли у вас такая же проблема, если вы реализуете мой простой пример проекта?


Обновление: как отметил Франсуа, это на самом деле ошибка в Delphi 2009 на данный момент - вы можете проголосовать за нее здесь.


Обновление: эта ошибка была исправлена ​​в Delphi 2010.

Ответы

Ответ 1

К сожалению, это похоже на ошибку, связанную с порядком вызова секции завершения в модуле Classes:

DoneThreadSynchronization очищает структуру ThreadLock, а затем FreeExternalThreads хочет уничтожить объект Thread, который вы только что создали при вызове CurrentThread, и
который требует, чтобы ThreadLock уже инициализировался при вызове на EnterCriticalSection(ThreadLock) в TThread.RemoveQueuedEvents...

UPDATE:
В отчет QC теперь находится патч .

Ответ 2

Пока CodeGear не выдает исправление, вы можете использовать патч ниже. Сохраните его в автономном устройстве и используйте его в любом месте вашей программы. Я также попытаюсь добавить его в QC.

Эта версия работает с D2009 (оригинал), обновлением 1 и обновлением 2.

{ Fix Delphi 2009 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.

Ответ 3

Патч-модуль для Delphi 2009 Update 3.

{ Fix Delphi 2009 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.

Ответ 4

Я думаю, что CurrentThread добавлен в 2009 году (или 2007). У меня дома в 2006 году. Но уверены ли вы, что это свойство класса?