2012-09-17 40 views
4

我在Delphi编写的遗留应用程序,并需要建立一种机制,将数据写入德尔福TStringGrid

  1. 阅读和
  2. /写入

数据到TStringGrid。

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

所以我创建

  1. 一个C++ DLL中注入
  2. 德尔福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没有得到访问冲突问题?

+0

+1 - 好问题! – Jeff

+0

+1 - 好方案! – CloudyMarble

+0

您是否设法同步对象树和内存映射?在DLL和EXE中有两个不同的TObject类,两个不同的GetMem函数。 –

回答

1

这种方法根本不起作用。目标可执行文件中有两个VCL实例。一个由目标应用程序拥有,另一个由DLL拥有。这是一个VCL实例太多了。如果使用完全相同版本的Delphi来构建目标应用程序和DLL,那么您可能会得到解决。

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

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

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

基本上你正在尝试的是不会工作。您可以找到目标应用程序的执行地址TStringGrid.SetCell,并假装致电该应用程序。但是您还需要找到目标应用程序的实现GetMem,FreeMem等,并确保从您的DLL跨越到目标应用程序的所有动态内存已被目标应用程序的堆分配和释放。做这项工作你会有一份工作的魔鬼。当然,如果目标应用程序和DLL都使用了共享内存管理器,那么您可能只能使这种方法飞行。

更简单的是假键盘输入。我个人会考虑使用AutoHotKey的可行性。

1

与堆使用相关的一切都面临着非常大的风险。 您可以尝试Jedi CodeLib来合并对象树并确保EXE和DLL中有相同的单个堆,但这将是完全脆弱的解决方案。

希望这个VMT调用都或多或少的安全paranoically试图阻止编译器释放字符串,草图是这样的:

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; 

但是,这可能会调用TStringGrid.OnSetEditText,如果主机EXE使用它。

+0

如何合并两堆?我很难相信这是可能的。 –

+1

@DavidHefferman在技术上没有“合并”。将DLL和EXE切换到同一个管理器,并尽早完成,以便在运行期间不会分配交换器之前分配的内存。 “拥有共同的堆”将是更正确的说法。 –

+1

这实际上是合理的。注入的DLL将不得不安装新的内存管理器作为它所做的第一件事,但不使用堆就可以做到这一点。虽然会很疯狂。 –