【问题标题】:In Delphi, how can I modify cells in a TStringGrid using a DLL procedure?在 Delphi 中,如何使用 DLL 过程修改 TStringGrid 中的单元格?
【发布时间】:2017-01-21 19:15:55
【问题描述】:

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

我的问题是,在将短字符串保存到记录后,我似乎无法在 DLL 过程中找到访问 TStringGrid 的方法。到目前为止,我已经尝试使用类和指针来访问 DLL 中的 TStringGrid,但都没有奏效:

type
  pstringgrid = ^TStringGrid;

//or

type
  pstringgrid = ^stringgrid1;

//or

type
  istringgrid = class(TStringGrid);

我什至尝试将 TStringGrid 导入到应该将短字符串从记录输入到 TStringGrid 的过程中:

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;

[...]

【问题讨论】:

  • 不明白你对指针的使用。类已经是指针。太多的间接性。除非您使用包,否则跨模块边界传递任何这些类型都不起作用。现在是 2017 年。停止使用短字符串。向我们展示minimal reproducible example,以便我们了解互操作。
  • 我很同情你,但是这里有很多问题,几乎不可能知道从哪里开始。我只是没有精力做这个

标签: class delphi pointers dll tstringgrid


【解决方案1】:

我的问题是,在将短字符串保存到记录后,我似乎无法在 DLL 过程中找到访问 TStringGrid 的方法。

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

一方面,跨 DLL 边界访问对象是不安全的,除非应用程序和 DLL 都在启用运行时包的情况下进行编译,以便它们共享 RTL 和内存管理器的单个实例。

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

另外,您的 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;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-27
    • 1970-01-01
    • 2012-02-07
    • 2016-02-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多