Question

I have a legacy application written in Delphi, and need to build a mechanism for

  1. reading and
  2. writing

data from/to a TStringGrid.

I don't have the source code of the application, there is no automation interface and it is very unlikely that the vendor will provide one.

Therefore I've created

  1. a C++ DLL, which injects
  2. a Delphi DLL (written by me) into
  3. the address space of the legacy application.

DLL 2 gets access to the TStringGrid instance inside the legacy application, reads cell values and writes them into the debug log.

Reading works fine. But, when I try to write data into a grid cell using a call like

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

an access violation occurs.

Here's the code:

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*)

How can I write data into a TStringGrid without getting access violation problem?

Was it helpful?

Solution

This approach simply is not going to work. You have two VCL instances in the target executable. One owned by the target app and one owned by the DLL. That's one VCL instance too many. You might be able to get away with that if the exact same version of Delphi is used to build both the target app and your DLL.

However, you will still have two heap managers in play. And your code passes heap allocated memory between your different VCL instances. You will be allocating in one heap and deallocating in the other. That doesn't work and will result in access violations.

You are passing a string allocated in the DLL's heap to the string grid object that uses the target app's heap. That just cannot work.

I think the access violation will occur at the point at which the DLL code attempts to deallocate the previous value of Cells[i,j], that was allocated by the target app's heap manager.

Basically what you are attempting is not going to work. You could find out the address of the target app's implementation of TStringGrid.SetCell and fake a call to that. But you'd also need to find the target app's implementation of GetMem, FreeMem etc. and make sure that all dynamic memory that crossed from your DLL to the target app was allocated and deallocated by the target app's heap. You are going to have a devil of a job making this work. Of course, if both target app and the DLL used the shared memory manager, then you might just be able to make this approach fly.

Much simpler would be to fake keyboard input. I personally would scope out the feasibility of that using AutoHotKey.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top