在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;
[...]
答案 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;