Reputation: 321
I have a Windows service application. Currently all admin tasks are done through config editing.
I want to add some sort of command line interfaces - and i want it to do through powershell.
And i don't know where should i start - how can i create application interface in this case. How powershell should communicate with service? remote feature also required in this case.
(In the feature there are maybe other admin tools - with GUI or through browser.)
Upvotes: 1
Views: 871
Reputation: 46044
A Windows service initially looks something like this:
using System.ServiceProcess;
internal partial class MyService : ServiceBase
{
static void Main()
{
ServiceBase[] ServicesToRun = new ServiceBase[] { new MyService() };
ServiceBase.Run( ServicesToRun );
}
}
What I have done is modify Main()
so that I can use it both to launch the service and to process command-line stuff, like this:
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 );
}
}
}
When the executable is built, I register it with the operating system as normal (actually, I use a -install command-line option to do that). When the service is started, the UserInteractive
flag is false, so the service starts as usual. From a command prompt, though, the UserInteractive
flag is true, so the command-line processing takes over.
All you need at this point is have the command-line instance of your executable communicate with the service instance of your executable via some sort of IPC - socket, pipe, shared memory, WCF, etc.
Upvotes: 2
Reputation: 200303
Expanding a little on L.B's short-ish remark: having a privileged service interact with the user's desktop is not the best of ideas, because doing so may open a route for privilege elevation. Shatter attacks for instance worked that way.
A better way to handle interaction with the user would be to have an unprivileged listener on localhost (e.g. 127.0.0.1:5555
) that will display messages submitted via that port, and have the privileged service connect to the listener to send messages to the user.
The code snippet below - while leaving a lot room for improvement - should give you a general idea of how such a listener could look like:
$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")
}
Upvotes: 2