4

我有一个用 Delphi 编写的遗留应用程序,需要建立一个机制

  1. 阅读和
  2. 写作

来自/到 TStringGrid 的数据。

我没有应用程序的源代码,没有自动化接口,供应商不太可能提供。

因此我创建了

  1. 一个 C++ DLL,它注入
  2. 一个 Delphi DLL(由我编写)到
  3. 遗留应用程序的地址空间。

DLL 2 可以访问遗留应用程序内的 TStringGrid 实例,读取单元格值并将它们写入调试日志。

阅读效果很好。但是,当我尝试使用类似的调用将数据写入网格单元格时

realGrid.Cells[1,1] := 'Test';

发生访问冲突。

这是代码:

procedure DllMain(reason: integer) ;
type
  PForm = ^TForm;
  PClass = ^TClass;
  PStringGrid = ^TStringGrid;
var
[...]
begin
  if reason = DLL_PROCESS_ATTACH then
  begin
    handle := FindWindow('TForm1', 'FORMSSSSS');

    formPtr := PForm(GetVCLObjectAddr(handle) + 4);

    if (not Assigned(formPtr)) then
    begin
      OutputDebugString(PChar('Not assigned'));
      Exit;
    end;

    form := formPtr^;

    // Find the grid component and assign it to variable realGrid
    [...]

    // Iterate over all cells of the grid and write their values into the debug log
    for I := 0 to realGrid.RowCount - 1 do
      begin
        for J := 0 to realGrid.ColCount - 1 do
          begin
            OutputDebugString(PChar('Grid[' + IntToStr(I) + '][' + IntToStr(J) + ']=' + realGrid.Cells[J,I]));
            // This works fine
          end;
      end;

    // Now we'll try to write data into the grid
    realGrid.Cells[1,1] := 'Test'; // Crash - access violation
  end;
end; (*DllMain*)

如何在不出现访问冲突问题的情况下将数据写入 TStringGrid?

4

2 回答 2

1

Everything related to Heap usage is under very heavy risk there. You can try Jedi CodeLib to merge object trees and ensure same single heap in EXE and DLL, but that would be utterly fragile solution.

Hoping the VMT calls are more or less safe and paranoically trying to prevent compiler from freeing string, the sketch to be like that:

type TSGCracker = class(Grids.TStringGrid); // access to protected functions.
....
var s: string;
function dummy(s: string);  // not-inline! pretend we need and use the value!
   begin Result := s + '2'; end; 
begin
   ...
   s := 'Test';   
   TSGCracker(realGrid).SetEditText(1,1, s);
   dummy( s+'1');
   ...
end;

But that might call TStringGrid.OnSetEditText, if host EXE uses it.

于 2012-09-17T11:22:21.953 回答
1

这种方法根本行不通。目标可执行文件中有两个 VCL 实例。一个由目标应用程序拥有,一个由 DLL 拥有。一个 VCL 实例太多了。如果使用完全相同版本的 Delphi 来构建目标应用程序和您的 DLL,您也许可以摆脱这种情况。

但是,您仍然有两个堆管理器在使用。并且您的代码在不同的 VCL 实例之间传递堆分配的内存。您将在一个堆中分配并在另一个堆中释放。这不起作用,并且会导致访问冲突。

您将在 DLL 堆中分配的字符串传递给使用目标应用程序堆的字符串网格对象。那是行不通的。

我认为访问冲突将发生在 DLL 代码尝试释放Cells[i,j]目标应用程序的堆管理器分配的先前值的点。

基本上你正在尝试的东西是行不通的。您可以找到目标应用程序实现的地址TStringGrid.SetCell并伪造对它的调用。但是您还需要找到目标应用程序的实现GetMemFreeMem,并确保从您的 DLL 跨到目标应用程序的所有动态内存都由目标应用程序的堆分配和释放。你将有一份工作完成这项工作。当然,如果目标应用程序和 DLL 都使用共享内存管理器,那么您也许可以使这种方法可行。

更简单的是伪造键盘输入。我个人会确定使用 AutoHotKey 的可行性。

于 2012-09-17T09:54:58.813 回答