Есть ли исправление времени выполнения для AV в TMonitor.GetBoundsRect?

Ниже приведена известная ошибка в Delphi 7 и 2007 (и, возможно, в других версиях).

Имеет ли TMonitor.GetBoundsRect ошибку нарушения доступа в Delphi 2007, вызванную VNC?

Есть ответ о том, как это исправить путем перекомпиляции forms.pas, но я бы предпочел не перекомпилировать модули RTL. Кто-нибудь создал для него патч времени выполнения, например, используя технику, также используемую в VclFixpack Энди Хаусладена? (И если да, не могли бы вы поделиться с нами?)

1 ответ

Вы можете сделать это с объездом. Например, код, указанный в этом ответе: /questions/38947443/patch-rutinnogo-vyizova-v-delphi/38947460#38947460 будет достаточным. Или вы можете выбрать любую другую обходную библиотеку.

Кроме того, вам нужно взломать класс, чтобы получить доступ к закрытым членам. В конце концов, GetBoundsRect это личное. Вы можете взломать класс с помощью помощника класса. Опять же, один из моих ответов показывает, как это сделать: /questions/46454572/kak-ya-mogu-ispravit-chastnyij-metod-klassa-delphi/46454585#46454585

Соедините их вместе, и у вас есть ответ.

unit PatchTScreen;

interface

implementation

uses
  Types, MultiMon, Windows, Forms;

type
  TScreenHelper = class helper for TScreen
    function FindMonitorAddress: Pointer;
    function PatchedFindMonitorAddress: Pointer;
    function PatchedFindMonitor(Handle: HMONITOR): TMonitor;
  end;

function TScreenHelper.FindMonitorAddress: Pointer;
var
  MethodPtr: function(Handle: HMONITOR): TMonitor of object;
begin
  MethodPtr := Self.FindMonitor;
  Result := TMethod(MethodPtr).Code;
end;

function TScreenHelper.PatchedFindMonitorAddress: Pointer;
var
  MethodPtr: function(Handle: HMONITOR): TMonitor of object;
begin
  MethodPtr := Self.PatchedFindMonitor;
  Result := TMethod(MethodPtr).Code;
end;

function TScreenHelper.PatchedFindMonitor(Handle: HMONITOR): TMonitor;
var
  I: Integer;
begin
  Result := nil;
  for I := 0 to MonitorCount - 1 do
    if Monitors[I].Handle = Handle then
    begin
      Result := Monitors[I];
//      break;
      Exit;
    end;
  //if we get here, the Monitors array has changed, so we need to clear and reinitialize it
  for i := 0 to MonitorCount-1 do
    TMonitor(Monitors[i]).Free;
  fMonitors.Clear;
  EnumDisplayMonitors(0, nil, @EnumMonitorsProc, LongInt(FMonitors));
  for I := 0 to MonitorCount - 1 do
    if Monitors[I].Handle = Handle then
    begin
      Result := Monitors[I];
      Exit;
    end;
end;

procedure PatchCode(Address: Pointer; const NewCode; Size: Integer);
var
  OldProtect: DWORD;
begin
  if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then
  begin
    Move(NewCode, Address^, Size);
    FlushInstructionCache(GetCurrentProcess, Address, Size);
    VirtualProtect(Address, Size, OldProtect, @OldProtect);
  end;
end;

type
  PInstruction = ^TInstruction;
  TInstruction = packed record
    Opcode: Byte;
    Offset: Integer;
  end;

procedure RedirectProcedure(OldAddress, NewAddress: Pointer);
var
  NewCode: TInstruction;
begin
  NewCode.Opcode := $E9;//jump relative
  NewCode.Offset := NativeInt(NewAddress)-NativeInt(OldAddress)-SizeOf(NewCode);
  PatchCode(OldAddress, NewCode, SizeOf(NewCode));
end;

initialization
  RedirectProcedure(
    TScreen(nil).FindMonitorAddress,       // safe to use nil, don't need to instantiate an object
    TScreen(nil).PatchedFindMonitorAddress // likewise
  );

end.

Без помощников класса, как в случае с Delphi 7, вам лучше всего перекомпилировать рассматриваемый модуль VCL. Это просто и надежно.

Если вы не можете заставить себя сделать это, вам нужно найти адрес функции. Я бы сделал это, разобрав код во время выполнения и следуя по нему известному вызову функции. Эта техника хорошо продемонстрирована MadExcept.

Другие вопросы по тегам