在Delphi中,如何使用DLL过程修改TStringGrid中的单元格?

时间:2017-01-21 19:15:55

标签: class delphi pointers dll tstringgrid

在Embarcadero Delphi v10.1中,我有一个带有记录的DLL库,以及一个包含TStringGrid和TEdit的VCL应用程序。想法是将短线串输入TEdit;将它保存到DLL中的记录,然后使用存储在记录中的数据填充TStringGrid中的一个单元格。

我的问题是,在将shorttring保存到记录之后,我似乎找不到在DLL过程中访问TStringGrid的方法。到目前为止,我已经尝试使用类和指针来访问DLL中的TStringGrid,但两者都没有用:

type
  pstringgrid = ^TStringGrid;

//or

type
  pstringgrid = ^stringgrid1;

//or

type
  istringgrid = class(TStringGrid);

我甚至尝试将TStringGrid导入到应该从记录到TStringGrid的shorttring中输入的过程:

procedure AddElement (var grid : stringgrid1); stdcall; 

//or

type
  pstringgrid = ^TStringGrid;

procedure AddElement (var grid : ^pstringgrid); stdcall;

到目前为止,没有任何工作,我得到的是来自调试器的“未清除标识符”错误消息;请帮忙!如何在DLL过程中访问和编辑TStringGrid?

编辑:

这是相关代码,对不起外来变量名称。

DLL:

library BibliotekaDLL;

uses
  System.SysUtils,
  System.Classes;

type
  StringGrid1 = class(TStringGrid);
  plist = ^game;
  game = record
    nazwa: shortstring;
    wydawca: shortstring;
    rokwyd: integer;
    gatunek1: shortstring;
    gatunek2: shortstring;
    pointer: plist;
  end;

var
  BazaDanych : file of game;
  first, current: plist;

[...]

procedure WyswietlListe; stdcall;
var
  row : integer;
begin
  AssignFile(BazaDanych, 'c:\Baza_Danych_Gier.dat');
  if not FileExists('c:\Baza_Danych_Gier.dat') then
    ShowMessage ('Baza Danych Nie Instnieje' +E.Message)
  else
    begin
    Reset(BazaDanych);
    Read(BazaDanych, first);
    Close(BazaDanych);
    current := first;
    row := 1;
    while current^.pointer <> nil do
      begin
      current := first;
      StringGrid1.Cells[0,row] := current^.nazwa;
      StringGrid1.Cells[1,row] := current^.wydawca;
      StringGrid1.Cells[2,row] := current^.rokwyd;
      StringGrid1.Cells[3,row] := current^.gatunek1;
      StringGrid1.Cells[4,row] := current^.gatunek2;
      current := current^.pointer;
      row = row +1;
      StringGrid1.RowCount := row;
      end;
    if current^.pointer = nil do
      begin
        StringGrid1.Cells[0,row] := current^.nazwa;
        StringGrid1.Cells[1,row] := current^.wydawca;
        StringGrid1.Cells[2,row] := current^.rokwyd;
        StringGrid1.Cells[3,row] := current^.gatunek1;
        StringGrid1.Cells[4,row] := current^.gatunek2;
      end;
    end;
end;

[...]

VCL应用程序代码:

[...]

type
  TForm1 = class(TForm)
    Button2: TButton;
    StringGrid1: TStringGrid;
    procedure Button2Click(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

[...]

procedure TForm1.Button2Click(Sender: TObject);
var
  Handle : THandle;
  WyswietlListe : procedure;
begin
  Handle := LoadLibrary('BibliotekaDLL.dll');
  try
    @WyswietlListe:= GetProcAddress(Handle, 'WyswietlListe');
    if @WyswietlListe = nil then raise Exception.Create('Nie Można Znaleźć Procedury w Bibliotece!');
    WyswietlListe;
  finally
    FreeLibrary(Handle);
  end;
end;

[...]

1 个答案:

答案 0 :(得分:1)

  

我的问题是,在将shorttring保存到记录之后,我似乎找不到在DLL过程中访问TStringGrid的方法。

不要那样做。这是糟糕的设计。

首先,跨越DLL边界访问对象是不安全的,除非在启用运行时软件包的情况下编译app和DLL,因此它们共享RTL和内存管理器的单个实例。

最好是DLL根本不了解您的UI。如果DLL需要将信息传递给应用程序,则DLL应该定义应用程序可以为其分配处理程序的回调事件,然后DLL可以在需要时调用该事件。让应用决定如何管理自己的用户界面。

此外,您的game记录有一个指针成员,但指针不能保存在文件中。您需要删除该成员。

尝试更像这样的事情:

library BibliotekaDLL;

uses
  System.SysUtils,
  System.Classes,
  Vcl.Dialogs;

type
  game = packed record
    nazwa: shortstring;
    wydawca: shortstring;
    rokwyd: integer;
    gatunek1: shortstring;
    gatunek2: shortstring;
  end;

  gameCallback = procedure(var g: game; userData: Pointer); stdcall;

procedure WyswietlListe(callback: gameCallback; userData: Pointer); stdcall;
var
  BazaDanych : File of game;
  current: game;
begin
  AssignFile(BazaDanych, 'c:\Baza_Danych_Gier.dat');
  Reset(BazaDanych);
  if IOResult <> 0 then
    ShowMessage ('Baza Danych Nie Instnieje')
  else
  try
    repeat
      Read(BazaDanych, current);
      if IOResult <> 0 then Break;
      if Assigned(callback) then callback(current, userData);
    until False;
  finally
    Close(BazaDanych);
  end;
end;

exports
  WyswietlListe;

end.

interface

type
  TForm1 = class(TForm)
    Button2: TButton;
    StringGrid1: TStringGrid;
    procedure Button2Click(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

type
  game = packed record
    nazwa: shortstring;
    wydawca: shortstring;
    rokwyd: integer;
    gatunek1: shortstring;
    gatunek2: shortstring;
  end;

  gameCallback = procedure(var g: game; userData: Pointer); stdcall;

  pmyCallbackInfo = ^myCallbackInfo;
  myCallbackInfo = record
    Grid: TStringGrid;
    FirstTime: Boolean;
  end;

procedure myCallback(var g: game; userData: Pointer); stdcall;
var
  row: Integer;
begin
  Grid := pmyCallbackInfo(userData).Grid;

  // add a new row only if the initial non-fixed row is already filled...
  if pmyCallbackInfo(userData).FirstTime then
    pmyCallbackInfo(userData).FirstTime := False
  else
    Grid.RowCount := Grid.RowCount + 1;

  row := Grid.RowCount - 1;
  Grid.Cells[0, row] := g.nazwa;
  Grid.Cells[1, row] := g.wydawca;
  Grid.Cells[2, row] := IntToStr(g.rokwyd);
  Grid.Cells[3, row] := g.gatunek1;
  Grid.Cells[4, row] := g.gatunek2;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  DLLHandle : THandle;
  WyswietlListe : procedure(callback: gameCallback; userData: Pointer); stdcall;
  info: myCallbackInfo;
begin
  // clear the TStringGrid. However, it has an odd quirk
  // that it requires at least 1 non-fixed row at all times...
  //
  StringGrid1.RowCount := StringGrid1.FixedRows + 1;
  StringGrid1.Rows[StringGrid1.RowCount - 1].Clear;

  DLLHandle := LoadLibrary('BibliotekaDLL.dll');
  if DLLHandle = 0 then raise Exception.Create(...);
  try
    @WyswietlListe := GetProcAddress(DLLHandle, 'WyswietlListe');
    if not Assigned(WyswietlListe) then raise Exception.Create('Nie Można Znaleźć Procedury w Bibliotece!');
    info.Grid := StringGrid1;
    info.FirstTime := True;
    WyswietlListe(@myCallback, @info);
  finally
    FreeLibrary(DLLHandle);
  end;
end;