【问题标题】:Is there a runtime patch for AV in TMonitor.GetBoundsRect?TMonitor.GetBoundsRect 中是否有 AV 的运行时补丁?
【发布时间】:2015-10-02 11:37:33
【问题描述】:

以下是 Delphi 7 和 2007(可能还有其他版本)中的一个已知错误

Does TMonitor.GetBoundsRect have an access violation bug in Delphi 2007 triggered by VNC?

有一个关于如何通过重新编译 forms.pas 来修复它的答案,但我宁愿不重新编译 RTL 单元。是否有人为其创建了运行时补丁,例如使用 Andy Hausladen 的 VclFixpack 中也使用的技术? (如果是,请与我们分享?)

【问题讨论】:

  • 绕道而行是一种方法。这已经在这里介绍过很多次了。或者简单地编写自己的函数并调用它而不是有缺陷的版本。或者您是否使用依赖于有缺陷功能的第三方代码。
  • 我删除了我的答案,因为 Stefan 指出您特别要求使用与 Andy 使用的相同技术的补丁。所以,虽然我可以给你应用修复的代码,至少对于 D2007,它没有使用所需的技术实现修复,因此不适合你的问题。
  • @DavidHeffernan 在您删除它之前我没有看到您的答案。它使用了什么技术?我并不是要明确要求与 VclFixpack 中相同的技术,这只是我想到的第一个选项。 (我不能简单地调用我自己的函数,因为该函数是在 VCL 内部调用的,例如用于显示提示。)
  • 我有一个适用于 D2007 的答案,但不适用于 D7,因为它依赖于类助手。你对它感兴趣吗?如果你是,我可以简单地取消删除它。如果问题清楚地表明您对仅适用于 D2007 的答案感兴趣,这将有所帮助。
  • 而且我知道如何以与 VclFixpack 相同的方式制作补丁。

标签: delphi delphi-7 delphi-2007


【解决方案1】:

您可以绕道而行。例如,此答案中给出的代码:https://stackoverflow.com/a/8978266/505088 就足够了。或者您可以选择任何其他绕行库。

除此之外,您还需要破解类才能访问私有成员。毕竟,GetBoundsRect 是私有的。您可以使用类助手破解类。同样,我的一个答案显示了如何做到这一点:https://stackoverflow.com/a/10156682/505088

把两者放在一起,你就有答案了。

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 很好地展示了这种技术。

【讨论】:

  • Delphi 7 没有类助手。
  • @Stefan Delphi 2007 做到了。如果没有类助手,您将不得不重新编译 VCL 单元或进行逆向工程。
  • 安德烈亚斯做了什么并被要求做了什么
  • @StefanGlienke 好吧,我有更广阔的视野,并选择提供另一种解决问题的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-26
  • 2010-09-27
  • 2018-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多