简体   繁体   English

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

[英]In Delphi, how can I modify cells in a TStringGrid using a DLL procedure?

In Embarcadero Delphi v10.1 I have both a DLL library with a record, and a VCL application containing a TStringGrid and a TEdit.在 Embarcadero Delphi v10.1 中,我有一个带有记录的 DLL 库和一个包含 TStringGrid 和 TEdit 的 VCL 应用程序。 The idea is to take the shortstring entered into the TEdit;这个想法是将短字符串输入到 TEdit 中; save it to the record in the DLL and then use the data stored in the record to fill in one of the cells in the TStringGrid.将其保存到 DLL 中的记录中,然后使用记录中存储的数据填充 TStringGrid 中的单元格之一。

My problem is that after saving the shortstring to the record I can't seem to find a way to access the TStringGrid while inside the DLL procedure.我的问题是,在将短字符串保存到记录后,我似乎无法在 DLL 过程中找到访问 TStringGrid 的方法。 So far I have tried using both classes and pointers to access the TStringGrid in the DLL but neither has worked:到目前为止,我已经尝试使用类和指针来访问 DLL 中的 TStringGrid,但都没有奏效:

type
  pstringgrid = ^TStringGrid;

//or

type
  pstringgrid = ^stringgrid1;

//or

type
  istringgrid = class(TStringGrid);

I have even tried to import the TStringGrid into the procedure which is supposed to enter the shortstring from the record into the TStringGrid:我什至尝试将 TStringGrid 导入到应该将记录中的短字符串输入到 TStringGrid 的程序中:

procedure AddElement (var grid : stringgrid1); stdcall; 

//or

type
  pstringgrid = ^TStringGrid;

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

So far nothing has worked and all I am getting is the "undecleared identifier" error message from the debugger;到目前为止,一切都没有奏效,我得到的只是来自调试器的“未清除标识符”错误消息; please help!请帮忙! How can I access and edit a TStringGrid while in a DLL procedure?如何在 DLL 过程中访问和编辑 TStringGrid?

Edit:编辑:

Here is the relevant code, sorry for the foreign variable names.这是相关的代码,对于外部变量名称感到抱歉。

The DLL: 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;

[...]

And the VCL application code:和 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;

[...]

My problem is that after saving the shortstring to the record I can't seem to find a way to access the TStringGrid while inside the DLL procedure.我的问题是,在将短字符串保存到记录后,我似乎无法在 DLL 过程中找到访问 TStringGrid 的方法。

Don't do that.不要那样做。 It is bad design.这是糟糕的设计。

For one thing, it is not safe to access objects across the DLL boundary unless both app and DLL are compiled with Runtime Packages enabled so they share a single instance of the RTL and memory manager.一方面,跨 DLL 边界访问对象是不安全的,除非应用程序和 DLL 都是在启用运行时包的情况下编译的,因此它们共享 RTL 和内存管理器的单个实例。

It is best if the DLL has no knowledge of your UI at all.如果 DLL 完全不了解您的 UI,则最好。 If the DLL needs to communicate info to the app, the DLL should define a callback event that the app can assign a handler for, and then the DLL can call that event when needed.如果 DLL 需要与应用程序通信,则 DLL 应定义一个回调事件,应用程序可以为其分配处理程序,然后 DLL 可以在需要时调用该事件。 Let the app decide how to manage its own UI.让应用决定如何管理自己的 UI。

Also, your game record has a pointer member, but pointers cannot be persisted in files.此外,您的game记录有一个指针成员,但指针不能保存在文件中。 You need to remove that member.您需要删除该成员。

Try something more like this:尝试更像这样的事情:

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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM