Delphi TThread.CurrentThread и EAccessViolation - это ошибка или моя некомпетентность? - PullRequest
11 голосов
/ 09 октября 2008

В 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 Forms, добавить кнопку в форму и использовать что-то вроде следующего для обработчика событий кнопки:

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

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

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

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

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


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


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

Ответы [ 4 ]

15 голосов
/ 09 октября 2008

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

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

UPDATE
Теперь в отчете QC .

есть исправление для обхода проблемы.
12 голосов
/ 10 октября 2008

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

Эта версия работает с D2009 (оригинал), обновлением 1 и обновлением 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.
5 голосов
/ 11 июня 2009

Патч для Delphi 2009 Обновление 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.
0 голосов
/ 09 октября 2008

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

...