Delphi 2009: 如何在Vista下实现Windows服务和桌面应用之间的通信?

5
一个桌面应用程序如何与Windows服务在Vista/Windows2008/Windows7下通信?该应用程序需要向服务发送小字符串并接收字符串响应。两者都是使用Delphi 2009编写的。(请提供示例代码)

太棒了!我问了几乎相同的问题,而且我更加谦虚,但是我只得到了批评而不是答案...;-) - NaN
6个回答

6

推荐使用命名管道,你可能需要查看不同完整性级别之间的通信。

本文探讨了如何在Vista中实现这一点。虽然它是用C++编写的,但只是基本的Windows API调用,因此应该很快就能翻译成Delphi。

如果你想搜索更多关于这个主题的内容,这种通信称为进程间通信,但更好的搜索术语是IPC。


1
这是绝对正确的吗?我编写了几个同时充当TCP服务器和TCP客户端的Windows服务,它们在“本地系统”下都运行正常。我一直认为限制在于网络资源(//machine/resource/...)。 - Martin Liesén
1
第一个断言完全是胡扯。我写过的每个Windows服务都是某种类型的网络守护进程。唯一需要特殊权限的情况是那些需要显示用户界面的服务。 - Steve Gilham
是的,我记错了,不允许创建到外部资源的网络连接,或者你必须以NetworkService身份运行。我删除了第一个断言。但是,命名管道仍然是与您的服务通信的更好方法(在查找有关IPC的Vista兼容性文章方面遇到问题)。 - Davy Landman
我选择命名管道方法是因为(1)客户端和服务器在同一台机器上,(2)我不想处理TCP/IP连接的端口号冲突/配置。 - Vic

5
使用Indy,您可以相对容易地在应用程序之间创建TCP连接。特别是如果您只需要发送字符串消息。对于客户端(在您的情况下是桌面应用程序),它基本上是这样的。
var
  Client : TIdTCPClient;
...
Client.Host := 'localhost';
Client.Port := AnyFreePortNumber;
Client.Connect;
Client.IOHandler.Writeln (SomeString);
Response := Client.Readln;
...
Client.Disconnect;

针对服务器(在您的情况下是服务),

var
  Server  : TIdTCPServer;
  Binding : TIdSocketHandle;
...
Server.DefaultPort := SameFreePortNumberAsInClient;
Binding := Server.Bindings.Add;
Binding.IP := '127.0.0.1';    
Binding.Port := Server.DefaultPort;
Server.OnConnect := HandleConnection;
Server.OnDisconnect := HandleDisconnection;
Server.OnExecute := HandleCommunication;
Server.Active := True;

只需实现HandleCommunication方法即可。每当客户端决定发送某些内容时,该方法将被调用。例如:

procedure MyClass.HandleCommunication (AContext : TIdContext);
var
  Request : String;
begin
  Request := AContext.Connection.IOHandler.Readln;
  if (Request = Command1) then
    HandleCommand1
  else if (Request = Command2) then
    HandleCommand2
  ...
end;

据我所知,一个服务只能拥有图形用户界面或网络访问权限,因此如果您的服务需要 GUI(无论如何都应该避免),那么这可能是个问题(请参见此问题)。不过我不知道在 Windows Vista 及以后版本中如何处理。

3
请查看在局域网上两个应用程序之间交换数据的答案,这与现在的问题基本相同。通过TCP进行本地通信是标准的。正如我在那里的回答中所说,使用“远程过程调用”类型接口的解决方案效果很好。我使用RemObjects SDK来处理这种情况,如果您希望以后扩展到网络控制,则可以轻松实现。

这两种方法都允许您创建一个连接,对于大部分代码而言它是“透明”的,您只需调用一个接口即可将数据发送到远程并获取结果。然后,您可以像平常一样编程,忘记套接字等细节。


1
一个服务即使只是本地监听,难道不需要特殊权限才能使用网络吗? - Rob Kennedy
@Rob,这取决于服务运行的帐户是什么。 - Kevin
我在使用系统账户或用户账户时,使用RemObjects没有遇到任何问题。问题更多地与域用户权限有关,而不是独立的TCP/IP套接字访问。 - mj2008
一个服务可以使用TCP套接字(除原始套接字外)而不必考虑用户帐户。 - Remy Lebeau

3
你需要将服务用户从localsystem更改为networkservice,然后该服务就可以很好地使用TCPIP。我有几个使用TCPIP进行外部控制的服务。只需确保您的服务端口是可配置的,这样您就可以处理任何冲突。
我的一些控制接口基于从内部HTTP服务器提供的XML页面。这使我能够使用任何可以访问该机器上的端口的Web浏览器远程检查服务的状态。使用HTTP的优点是在需要通过现有网络硬件工作时它可以很好地工作。
如果您仅在本地通信,则命名管道、邮件槽或内存映射文件可能是最佳方法。

你不需要使用NetworkService账户来使用TCP,LocalSystem和LocalService账户同样可以正常工作。 - Remy Lebeau

2

我没有尝试过,但我认为您可以使用命名管道。


1

我在我的服务应用程序中使用一个组件集,名为Simple IPC,它是一款带源代码的免费软件。

可以在torry.net上搜索到。当与桌面应用程序通信时,它在我所有的服务应用程序中都运行得非常好。

约翰


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