如何在一个Delphi应用程序中触发另一个Delphi应用程序的事件?

4

在标记为重复之前,请先阅读以下内容。

我正在创建一系列依赖于智能卡进行身份验证的应用程序。到目前为止,每个应用程序都单独控制智能卡读卡器。几周后,我的一些客户将同时使用多个应用程序。因此,我想也许创建一个控制身份验证进程的服务应用程序会更实际。我希望我的桌面应用程序告诉服务应用程序它们对身份验证过程感兴趣,然后服务应用程序会向它们提供有关当前用户的信息。这部分很容易,使用命名管道。困难的部分是,服务如何告诉桌面应用程序发生了事件(UserLogInUserLogOutPermissionsChanged等)。到目前为止,我脑海中有两种方法。回调函数和消息传递。还有其他更好的想法吗?我相信肯定有。


5
你也可以使用命名管道来实现这个功能。你需要一个在管道上阻塞的线程。当服务向管道发送数据时,该线程会被唤醒,读取数据并将消息传递给主线程。然后它会在管道上等待下一条消息。 - David Heffernan
有时候你可以使用数据库服务器来实现这个功能,前提是所有的应用程序共享同一个数据库,并且服务器支持事件。 - Arioch 'The
"Marshalling" 是 RPC 的术语,用于将本地回调调用转换为消息,然后再转换为远程回调调用。这里没有任何二分法 :-) - Arioch 'The
1
你可能需要重写所有需要“远程调用”的事件处理程序,以便只需调用API/接口,然后通过远程系统进行调用。针对接口编码,而不是像TButton.OnClick(Sender:TObject)这样的实现细节。我会将每个事件编码为整数ID,就像窗口消息一样。 - Warren P
3个回答

9

您想用Delphi进行进程间通信(IPC)。

有许多链接可以帮助您,Cromis IPC只是其中之一,可以让您了解您需要什么。

类似于您的问题,这里有一个相似的SO问题 here

如果您想使用纯Windows API,请查看 how OutputDebugString communications is implemented
多个工具可以侦听该机制,许多应用程序可以将信息发送到它。

搜索DBWIN_DATA_READYDbWin32以获取有关OutputDebugString协议的更多信息。

这篇文章这篇文章都是很好的阅读材料。


我需要一个事件触发机制。所以,我想我终究得使用消息。顺便问一下,RPC是什么?从名字上看,它应该是关于在另一个进程中调用过程的,对吧? - iMan Biglari
事件处理程序不就是一个过程吗? - Arioch 'The
1
这个问题不仅涉及到IPC,还涉及到实现回调事件。因此,Cromis IPC在这里并不符合需求,除非每个桌面应用程序都成为一个服务器。 - Arnaud Bouchez
1
我曾经考虑过为回调函数设计一个独立的解决方案,以便将其挂钩到我的IPC上。但由于时间不足,我还没有取得任何进展。说实话,让每个客户端也成为服务器非常容易,并且对资源消耗很小。但是这肯定不是一种优雅的解决方案。 - Runner
@Arioch'The 当然,WebSocket 是用于 Web 的。但它是在您提出的阻塞模式上实现的。 - Arnaud Bouchez
显示剩余5条评论

5

当涉及到IPC时,一些提示:

  • 不要被绑定在一个协议上:例如,如果您实现了命名管道通信,您可能以后需要在网络上运行它,甚至在HTTP上运行;
  • 不要重新发明轮子,也不要使用专有消息,而是使用标准格式(如XML / JSON / BSON);
  • 回调事件有些难以实现,因为常见的模式可能是为每个桌面客户端实现一个服务器,以从服务器接收通知。
我的建议是,在桌面应用程序上不要使用回调函数,而是在无状态架构上使用轮询。您可以打开与服务器的通信通道,然后每秒/半秒(在UI中使用TTimer),发出小请求以获取更改内容(您可以放置上次检索的修订号或时间戳)。因此,您可以将桌面数据与待处理事件同步。在现有连接上请求更新非常快速,并且如果没有更改,仅会在网络上发送一个IP数据包来回传输。这是一个非常小的任务,既不会减慢客户端也不会减慢服务器(如果您使用一些内存缓存)。实际应用中,这样的无状态架构非常响应,从最终用户的角度来看,更容易部署。您不需要在每个桌面应用程序上创建服务器,因此无需打开防火墙端口等。由于HTTP是无状态的,它甚至更适合互联网。
如果你想开发服务,可以使用DataSnap,类似于RemObjects,或者尝试我们的开源mORmot框架,它能够创建基于接口的服务,并使用轻量级JSON消息通过REST在进程内、使用GDI消息、命名管道或TCP/HTTP进行通信,而且是免费的,具有无与伦比的性能和内置安全性,支持从Delphi 6到XE2。对于你的事件驱动任务,仅仅使用mORMot中可用的客户端-服务器ORM就足够了:创建一个存储事件的表/类(你甚至可以定义一个循环内存存储 - 不需要在这里使用SQLite3引擎或DB),然后请求自上次刷新以来的所有待处理事件。服务器可以安全地作为后台服务或普通应用程序运行 - 一些mORMot用户甚至将相同的可执行文件配置为独立应用程序、服务器服务、应用程序服务器或UI客户端,只需更改配置即可。
mORMot路线图上,我们添加了一个新的即将推出的功能,可以轻松地从服务器实现单向回调。也就是说,向我们的面向服务架构框架添加透明的“推送”模式。目标是通过一些接口定义,甚至通过单个HTTP连接,非常容易地从Delphi代码中触发来自服务器端的通知事件 - 例如,WCF不允许这样做:它需要双重绑定,因此需要打开防火墙端口等等。它将用于通过Event Collaboration进行简单的发布/订阅模式,并允许Event Sourcing。我将尝试使其实现两种模式:轮询和锁定等待。直接回答您的问题。

1
我认为轮询和服务器之间并没有二分法。唯一的区别在于“谁打开连接”。例如,HTTP彗星协议和Firebird/Interbase。一方打开连接,另一方在准备好时推送事件。轮询不一定返回“没有新闻”,但可以锁定直到有事件发生,就像Win32 GetMessage一样。因此,您的MainForm最多轮询WM_*,就像HTTP服务器轮询新数据包到达一样。区别在于锁定等待与非等待,而不是“谁打开连接”。 - Arioch 'The
使用GDI消息。据我所知,服务不再允许与桌面窗口交互,而UAC应用程序也会将它们隔离。这对于主题发起者可能不是一个选项。 - Arioch 'The
@Arioch'锁定拉取可能会让人感到困惑,例如如果连接中断。WebSockets有时会使用这种模式实现,但它比周期性无锁快速请求更难实现。但是锁定和等待也会锁定您的桌面应用程序,因此在TTimer事件中不友好,因此您需要一个专用线程。使用直接返回模式,您可以拥有仅具有一个线程的桌面应用程序,更容易调试。当然,GDI消息不能作为服务,但在同一台PC上的两个应用程序之间是最快的(比命名管道更快)。 - Arnaud Bouchez
轮询(每秒)会导致不必要的网络流量 - 当客户端数量增加时,服务器将越来越忙于处理“无可用信息”。更具可伸缩性的解决方案是心跳和仅在有实际数据需要发送到客户端的服务器->客户端数据包的组合。 - mjn
1
@mjn OP 可能在谈论本地访问、企业网络或 VPN。对于一个具有数千个全球分布式客户端的 AJAX 应用程序,Websockets 是有意义的。但这样的心跳+锁定需要在服务器上进行调整,例如不要为每个客户端浪费一个线程:这不是一项容易的任务,因此不是我在这里的首选。在企业网络中,轮询很容易编码和调试,并且通过适当的内存缓存非常快速。看看您网络插头上的 LED:它一直在闪烁,不会减慢您的浏览器或最喜爱的 IDE。;) - Arnaud Bouchez
显示剩余5条评论

0

您可以使用简单的TCP套接字连接(双向)来允许在同一套接字上异步服务器到客户端的消息。

一个例子是Indy TIdTelnetClient类,它使用一个线程来处理来自服务器的传入消息。

您可以构建一个类似的基于文本的协议,并且只需要在服务中拥有一个Indy TCP服务器实例和一个应用程序中的Indy客户端实例即可。


请注意,使用任何原始TCP解决方案时,应小心防火墙设置。 - Jeroen Wiert Pluimers
@JeroenWiertPluimers,像Comodo这样的复杂防火墙可以拦截任何类型的IPC。显然这是有原因的。 - Arioch 'The

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接