Delphi 在DLL中填充TStringList
我想在DLL中填充Delphi 在DLL中填充TStringList,delphi,dll,Delphi,Dll,我想在DLL中填充TStringList。关于内存管理文档,我的方法似乎是错误的,但它是有效的,不会导致错误或AV 有人能告诉我,如果代码是正确的吗?不知道如何在DLL中填充类 programm EXE function MyClass_Create: IMyClass; stdcall; external ... var _myClass_DLL: IMyClass; //shared interface in exe and dll procedure FillList; var
TStringList
。关于内存管理文档,我的方法似乎是错误的,但它是有效的,不会导致错误或AV
有人能告诉我,如果代码是正确的吗?不知道如何在DLL中填充类
programm EXE
function MyClass_Create: IMyClass; stdcall; external ...
var
_myClass_DLL: IMyClass; //shared interface in exe and dll
procedure FillList;
var
list: TStringList;
begin
list := TStringList.Create(true); //memory allocated in EXE
try
_myClass_DLL.FillList(list); //memory allocated in DLL???
ShowMessage(list.Text);
finally
list.Free; //memory freed in EXE, frees also TObject created in DLL
end;
end;
DLL代码:
library DLL
TMyClass = class(TInterfacedObject, IMyClass)
public
procedure FillList(aList: TStringList);
end;
procedure TMyClass.FillList(aList: TStringList);
begin
aList.AddObject('Text1', TObject.Create); //memory allocation in DLL?
aList.AddObject('Text2', TObject.Create); //memory allocation in DLL?
end;
我不使用BORLNDMM.DLL或任何其他ShareMem单元
编辑:我将
aList.Add()
调用扩展到aList.AddObject()
。它也不会崩溃,尽管TObject是在DLL中创建的,并在EXE中释放
回答:关于下面接受的答案中的注释,该代码是正确的,因为exe和dll使用相同的delphi版本编译,并且只调用虚拟方法 结论:
只要使用虚拟方法或接口,内存管理就没有问题。这意味着,对象在何处创建或释放并不重要。如果要跨模块边界传递类,则需要使用运行时包链接到RTL/VCL。这是确保DLL中的
TStringList
类与EXE中的类完全相同的唯一方法。这是你目前做法的根本问题。另一方面,如果您已经使用运行时包链接到RTL,那么您就可以了
如果您不想使用运行时包,那么需要完全重新设计界面。您需要停止跨模块边界传递类。您可以使用接口,但不能使用类。您需要控制内存分配,以确保在分配内存的模块中始终释放内存。或者开始使用
ShareMem
如果您严重反对BPLs,那么您最好坚持DLL和接口的COM约定
特别是COM中有类似TStream的接口。VCL有TStreamAdapter类在COM IStream和VCL TStream之间进行转换
这样,您的DLL应该生成一个数据流,将其包装成COM IStream并传递给exe。EXE将从TStream转换回并填充stringlist
更快速和低技术的方法是感觉内存缓冲区,就像WindowsAPI函数一样。他们要么确实感觉到了,要么返回程序错误,请求更大的缓冲区。好的,然后您将调用函数两次—获取缓冲区大小并执行实际工作。如果混合使用PChar之类的指针类型,可能是PAnsiChar或PWideChar,或者传递了错误的缓冲区大小—编译器没有安全网,只会损坏内存。但这将比COM IStream更快
也许您应该创建启用COM的缓冲区对象,它有一种特殊的析构函数,不释放内存,而是将引用传递给DLL后台空闲内存回忆线程。所以,当您不再需要在主EXe中使用它时,它迟早会在DLL中释放。作为TStream使用仍然不太舒服,但至少希望不会破坏堆管理器。对于这种类型的功能,为了保持无内存、无包的共享,我会在dll中使用带有枚举器方法的回调。例如,这就是从windows检索字体的方式。这是我所指的模型:
type
TMyClass = class
private
FList: TStringList; // obv you need to construct this
public
function EnumListItem(s: string): integer;
end;
function TMyClass.EnumListItem(s: string): integer;
begin
FList.Add(s);
end;
procedure TMyClass.FillList;
begin
_myClass_DLL.FillList(@EnumListItem);
ShowMessage(FList.Text);
end;
这只是给你一个起点。。。。在DLL端,使用函数指针回调程序,并一次传入字符串1。从DLL获取字符串列表的最简单方法是: 您必须创建tStringList,然后在Dll中填充它,然后将文本作为return传递 从Dll库:
function getDocuments(customer,depot:Pchar):Pchar;export;
var
s:TstringList;
begin
S:=TStringList.Create;
S.Add('Row 1 '+customer);
S.Add('Row 2 '+depot);
S.Add('Row 3 ');
S.Add('Row 4 ');
S.Add('Row 5 ');
Result:=pchar(s.Text);
S.Free;
end;
从EXE
function GetDLLExternalDocuments(customer,depot:pchar;out fList:TStringList):Word;
var GetDocumentsDLLExport:TGetDocumentsDLLExport;
var s:String;
var HandleDllExport :Thandle;
begin
HandleDllExport := LoadLibrary('my_dll_library.dll');
if HandleDllExport <> 0 then
begin
@GetDocumentsDLLExport := GetProcAddress(HandleDllExport, 'getDocuments');
if @GetDocumentsDLLExport <> nil then
begin
s:=GetDocumentsDLLExport(cliente,impianto);
fList.Text:=S;
result:=0;
end;
FreeLibrary(HandleDllExport);
HandleDllExport:=0;
end;
end;
这是否意味着我必须在exe程序的项目设置中设置标志“使用运行时包”?或者仅仅转换一个包中的dll就足够了吗?在上面的代码中,您只需要链接到RTL/VCL运行时包。如果您想在两个模块之间传递实现的类,那么您也需要将DLL转换为包。我不熟悉包。然后我可以像加载dll一样加载包吗?像“function MyClass_Create:IMyClass;stdcall;external'myPackage.bpl'”不,一旦设置了引用,您只需在exe中源文件的uses子句中列出包中所需的单元。所以您不需要再次声明该函数。这比从DLL导入要简单得多。非常感谢您的帮助!我对BPLs不太满意,因为当使用第三方组件时,它会产生很多依赖关系。你最好传递PChar,而不是跨越边界。
procedure TfMain.Button1Click(Sender: TObject);
var
S:tStringList;
begin
S := tStringList.create;
GetDLLExternalDocuments('123456','AAAAA',S);
Showmessage(S.Text);
s.Free;
end;