我正在尝试将我在Delphi中创建的GUI应用程序(实际上是它的Lazarus)转换为库(DLL)。
在GUI应用程序中,我使用了OnDataChange
事件监听器,但我似乎无法弄清楚如何为库做同样的事情。
以下是GUI App中的内容:
procedure TForm1.Datasource2DataChange(Sender: TObject; Field: TField);
begin
ZMakeRankedTable.Close;
GetNN;
end;
在单位的LFM档案中:
object Datasource2: TDatasource
DataSet = ZMakeRankedTable
OnDataChange = Datasource2DataChange
left = 184
top = 95
end
我如何为图书馆做同样的事情?我在哪里初始化事件监听器?
答案 0 :(得分:3)
将表单转换为DataModule并创建一个实例:
DTM := TMyDataModule.Create(nil);
甚至可以在非GUI应用程序中工作。我没有使用Lazarus进行过多次测试,但我认为没有理由说这不起作用。
答案 1 :(得分:3)
创建一个属于委托的新类而不是表单有什么问题:
type
TDataDelegate = class
public
procedure DataChange(Sender: TObject; Field: TField);
etc...
end;
procedure TDataDelegate.DataChange(Sender: TObject; Field: TField);
begin
// Do what you normally would do in your form's event handler
end;
并确保创建一个类
的实例DataDelegate := TDataDelegate.Create;
DataSource2.OnDataChange := DataDelegate.DataChange;
等...
换句话说,使用您编写的类来处理各种类的事件,而不是表单。就像在表单中一样,每个过程都应该具有事件处理程序的签名。唯一的区别是IDE不会为您创建这些方法。
我猜你也可以使用TDataModule,但我不确定其含义。优点是IDE支持。
答案 2 :(得分:1)
事实上,here已经很好地解释了这一点:
问题是方法指针(OnDataChange)需要是一个过程 一个对象(如TForm),而不是常规程序。
答案 3 :(得分:1)
关于周围的事件有很多解释,但大部分都是不完整的,或者不容易理解,压缩太多,或者不是一步一步,或者“不是OO”...所以我决定提供我的方法的描述话题。
编写DLL意味着封装。我想提出以下结构,它使用一个接口类。当然它也可以在没有接口的情况下工作,但是谈论DLL意味着封装......接口是实现它的核心工具/结构。
否则,(实例)接口被引用计数,这意味着,如果你彻底地=总是这样做,代码将“表现得更好”(参见关于接口的其他条目)。
我还提到接口是为了进一步(虽然是相关的)原因 - 它可能不像你猜测的那样偏离主题:它强制你将事物分开=显式,正如你将看到的那样。不过,您可以轻松,简单地访问实现对象的所有“属性”,当然也可以跨越DLL边界。
首先,在DLL中封装内容的一种漂亮的方法是只导出一个过程,这将是
export interfaceProvider;
对应于标准函数(不属于类)
function interfaceProvider() : IYourInterface;
在这个函数中,将调用类构造函数! IYourInterface类型的全局变量(在DLL内部)不是必需的,但可以简化生活 函数interfaceProvider()将位于一种包装或网关单元中 在其他操作方法中,接口IYourInterface也将展示方法
procedure assignDataChangeEvent( _event : TDataChangeEvent);
反过来是由从接口派生的相应类(当然也是DLL的一部分)实现的,如此
TEncapsulatedStuffinDLL = class(Tinterfacedobject, IYourInterface)
现在请记住,事件是一种“优雅的回调”,或“优雅组织的回调”。在Delphi中,关键是特定的类型定义。 作为定义接口的同一单元中的类型,在接口本身定义之前,添加类似于此的
TDataChangeEvent = procedure(const Sender:TObject; const n : integer) of object;
请注意,事件的侦听器/接收器(在DLL外部/使用DLL)必须使用完全相同的参数签名(请参阅下文:proc.dbChangeListener)。 在实现接口的类中,我们将其称为TEncapsulatedStuffinDLL,然后您将首先定义为私有字段
private
OnDataChange : TDataChangeEvent ;
...
接下来我们需要以下两种方法:
procedure TEncapsulatedStuffinDLL.assignDataChangeEvent( _eventListener : TDataChangeEvent ) ;
begin
// here we assign the receiver of the callback = listener to the event
OnDataChange := _eventListener ;
end;
procedure TEncapsulatedStuffinDLL.indicateChange;
begin
// release the event = perform the callback
if Assigned(OnDataChange) then begin
OnDataChange(self);
end;
// note that OnDataChange is pointing to the assigned receiver, since
// the method assignDataChangeEvent has been called
end;
在相关内容发生的部分,我们称之为事件发布
procedure TEncapsulatedStuffinDLL.someMethod();
begin
// sth happening, then "releasing the event" = executing the callback
// upon some condition we now do...
indicateChange ;
end;
最后一点是从外面发起整件事。 让我们假设发生这种情况的类称为TDllHost,因此我们首先定义实际的侦听器方法
public
procedure dbChangeListener(const Sender:TObject; const n : integer);
...像这样实施
procedure TDllHost.dbChangeListener(const Sender:TObject; const n : integer);
begin
.... doing sth based on the provided parameters
end;
并且在运行期间我们这样开始(接口最好在他们自己的单元中定义,当然,尽管德里允许这样做“纠缠”......但这将证实封装的整个想法)
procedure TDllHost.init();
var
dbstuffInterface : IYourInterface ; // could also be global private to TDllHost
begin
// please complete this (off topic) section about late binding a DLL
....
// we would have a retrieval of the interface from the DLL
dbstuffInterface := interfaceProvider();
// and finally we provide the procedure pointer to the class
dbstuffInterface.assignDataChangeEvent( dbChangeListener );
// the assignment of the method to the method variable
// is done by the class itself
end;
使用接口将内容组织到DLL中提供的一个重要好处是IDE可以更好地支持它。然而,不幸的是,人们很少发现严格使用接口的编程示例
如果您不直接使用DLL,init()过程看起来会有所不同。不需要加载DLL,而是需要通过对实现类的构造函数的正常调用来实例化dbstuffInterface。
恕我直言,以这种方式将事件处理到DLL中非常简单,并且从OO角度来看通常是适用的。它应该适用于支持接口(和过程类型)的任何语言。如果我根本不使用DLL,它甚至是我组织回调/事件的首选方式......但是,在稍后的时间点,可以使用DLL轻松切换到完整的封装。唯一(次要)缺点可能是这样的DLL可能无法通过C标准使用。如果你想在Java中使用它,那么为了回到POP-NO(普通的旧程序,没有对象),还需要一个进一步的包装器。