gpt4 book ai didi

Delphi:有什么方法可以链接到 BPL 中尚未打包的变量吗?

转载 作者:行者123 更新时间:2023-12-03 15:48:53 27 4
gpt4 key购买 nike

我正在 RAD Studio 2007 中开发一个项目,使用 C++ 中的 VCL 类。

TDBLookupControl 是 VCL 的一部分,并且有一些不良行为,这是由于使用内部变量 SearchTickCount

var
SearchTickCount: Integer = 0; //file scope in DBCtrls.pas

procedure TDBLookupControl.ProcessSearchKey(Key: Char);
var
TickCount: Integer;
S: string;
begin
//some code removed for brevity
TickCount := GetTickCount;
if TickCount - SearchTickCount > 2000 then SearchText := '';
SearchTickCount := TickCount;
//some code removed for brevity
end;

但是,SearchTickCount 从未在 VCL 中打包,如下例所示。

extern PACKAGE int SearchTickCount;

我想在我的 C++ 代码中将 SearchTickCount 设置为零(按需)。在我的代码中外部它使 c++ 编译。但是,链接器(显然)找不到该变量。

namespace Dbctrls
{
extern int SearchTickCount;
}
// later on, inside a function
Dbctrls::SearchTickCount = 0;

有什么方法/解决方法可以链接到这个变量吗?

编辑:不幸的是,我们还使用了一些从 TDBLookupControl 派生的自定义控件,因此我试图避免创建更多自定义控件。

最佳答案

问题

SearchTickCount 是在单元的实现部分中声明的全局(单元级别)变量,不应该在该单元之外访问它。如果您使用 Delphi,而不是 C++ Builder,您也会遇到同样的问题。

合理的解决方案

  • 子类化 TDBLookupControl,重写 ProcessSearchKey() 并确保它使用您自己的 SearchTickCount(易于访问)。令人高兴的是 ProcessSearchKey() 是虚拟的,理论上这应该可行,但实际上代码依赖于 FListField,这是一个私有(private)字段,所以我们将回到第 1 步。
  • 将整个 TDBLookupControl 复制到您自己的 TMyDBLookupControl 并确保您可以访问 SearchTickCount。这绝对有效。

HACKY 解决方案

当然,黑客更有趣。 CPU 可以毫无问题地找到 SearchTickCount,因为该地址被编码到构成 ProcessSearchKey 代码的 ASM 指令中。 CPU能读的,我们也能读。

评估 ProcessSearchKey 方法的代码,它仅使用一个全局变量 (SearchTickCount),并在两个地方使用它。本次测试中的第一个:

if TickCount - SearchTickCount > 2000 then

然后在这个指令中:

SearchTickCount := TickCount;

如果您查看该例程的反汇编列表,很容易发现全局变量访问,因为它在方括号中给出了变量的地址,没有其他限定符。为了使 if 正常工作,编译器会执行以下操作:

SUB EAX, [$000000]

对于赋值,编译器会执行如下操作:

MOV [$000000], EAX // or ESI on Delphi 7 with debug enabled

如果您查看汇编指令的左侧,您可以轻松地看到实际的操作码(以十六进制表示法)。例如 SUB EAX,[$000000] 看起来像这样:

2B0500000000

我的黑客解决方案利用了这一点。我获取实际过程的地址 (TDBLookupControl.ProcessSearchKey),扫描代码查找操作码 (2B 05) 并获取地址。就是这样,并且有效。

当然,这有潜在的问题。这取决于使用这些确切的寄存器编译的代码(在我的示例中为EAX)。编译器可以自由选择不同的寄存器。我使用Delphi7和Delphi 2010进行了测试,代码编译为调试,编译为不调试。在所有 4 种情况下,编译器都选择使用 EAX 作为 SUB 指令,并且在 3/4 种情况下选择使用 ESI 作为MOV 指令。因此,我的代码只查找 SUB 指令。

另一方面,如果代码运行一次,则代码每次都运行。代码一旦发布就不会改变,所以如果你可以在开发机器上正确测试,你就不会在客户端机器上遇到讨厌的反病毒软件。但使用风险自负,这毕竟是一种黑客行为!

代码如下:

unit Unit2;

interface

uses DbCtrls;

function GetSearchTickCountPointer: PInteger;

implementation

type
THackDbLookupControl = class(TDBLookupControl); // Hack to get address of protected member
TInstructionHack = packed record
OpCodePrefix: Word;
OpCodeAddress: PInteger;
end;
PInstructionHack = ^TInstructionHack;

function GetSearchTickCountPointer: PInteger;
var P: PInstructionHack;
N: Integer;
begin
P := @THackDbLookupControl.ProcessSearchKey;
N := 0; // Sentinel counter, so we don't look for the opcode for ever
while N < 2000 do
begin
if P.OpCodePrefix = $052B then // Looking for SUB EAX, [SearchTickCount]
begin
Result := P.OpCodeAddress;
Exit;
end;
Inc(N);
P := PInstructionHack(Cardinal(P)+1); // Move pointer 1 byte
end;
Result := nil;
end;

end.

您可以像这样使用 hacky 版本:

var P: PInteger;
begin
P := GetSearchTickCountPointer;
if Assigned(P) then
P^ := 1; // change SearchTickCount value!
end;

关于Delphi:有什么方法可以链接到 BPL 中尚未打包的变量吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6504089/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com