我有一个 Windows 服务应用程序。目前所有的管理任务都是通过配置编辑完成的。
我想添加某种命令行界面 - 我希望它通过 powershell 来完成。
而且我不知道我应该从哪里开始 - 在这种情况下我该如何创建应用程序界面。powershell 应该如何与服务通信?在这种情况下还需要远程功能。
(在该功能中可能还有其他管理工具 - 使用 GUI 或通过浏览器。)
我有一个 Windows 服务应用程序。目前所有的管理任务都是通过配置编辑完成的。
我想添加某种命令行界面 - 我希望它通过 powershell 来完成。
而且我不知道我应该从哪里开始 - 在这种情况下我该如何创建应用程序界面。powershell 应该如何与服务通信?在这种情况下还需要远程功能。
(在该功能中可能还有其他管理工具 - 使用 GUI 或通过浏览器。)
稍微扩展一下 LB 的简短评论:让特权服务与用户桌面交互并不是最好的想法,因为这样做可能会打开特权提升的途径。例如,粉碎攻击就是这样工作的。
处理与用户交互的更好方法是在 localhost(例如127.0.0.1:5555
)上有一个非特权侦听器,它将显示通过该端口提交的消息,并让特权服务连接到侦听器以向用户发送消息。
下面的代码片段 - 虽然留有很大的改进空间 - 应该让您大致了解这样的侦听器的外观:
$addr = "127.0.0.1"
$port = 5555
[byte[]]$byte = @(0)
$enc = [System.Text.Encoding]::ASCII
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$socket = New-Object System.Net.Sockets.TcpListener([System.Net.IPAddress]::Parse($addr), $port)
$socket.Start()
while ( $true ) {
$client = $socket.AcceptTcpClient()
$stream = $client.GetStream()
[byte[]]$input = @()
while ( ($i = $stream.Read($byte, 0, 1)) -ne 0 ) { $input += $byte }
$client.Close()
[System.Windows.Forms.MessageBox]::Show($enc.GetString($input), "Title")
}
Windows 服务最初看起来像这样:
using System.ServiceProcess;
internal partial class MyService : ServiceBase
{
static void Main()
{
ServiceBase[] ServicesToRun = new ServiceBase[] { new MyService() };
ServiceBase.Run( ServicesToRun );
}
}
我所做的是修改Main()
,以便我可以使用它来启动服务和处理命令行内容,如下所示:
using System;
using System.Runtime.InteropServices;
using System.ServiceProcess;
internal partial class MyService : ServiceBase
{
const int ATTACH_PARENT_PROCESS = -1;
[DllImport( "kernel32.dll" )]
static extern bool AttachConsole( int dwProcessId );
[DllImport( "kernel32.dll" )]
static extern bool FreeConsole();
static void Main()
{
if ( Environment.UserInteractive ) {
try {
// Redirect console output to the parent process.
AttachConsole( ATTACH_PARENT_PROCESS );
// Process command line arguments here...
} catch {
// Handle exceptions here...
} finally {
// Detach from the console.
FreeConsole();
}
} else {
ServiceBase[] ServicesToRun = new ServiceBase[] { new MyService() };
ServiceBase.Run( ServicesToRun );
}
}
}
构建可执行文件后,我像往常一样在操作系统中注册它(实际上,我使用 -install 命令行选项来执行此操作)。服务启动时,UserInteractive
标志为false,因此服务照常启动。但是,在命令提示符下,UserInteractive
标志为true,因此命令行处理会接管。
此时您需要的只是让可执行文件的命令行实例通过某种 IPC(套接字、管道、共享内存、WCF 等)与可执行文件的服务实例进行通信。