2012-09-17 42 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將不得不安裝新的內存管理器作爲它所做的第一件事,但不使用堆就可以做到這一點。雖然會很瘋狂。 –