I have a c# app using System.ServiceModel.dll. I can run the app locally, but when I try to use Power Shell to run it remotely, it hang:
Here is the simple code to recreate the problem:
using System;
using System.ServiceModel;
using System.ServiceModel.Discovery;
namespace PowerShellSecurity
{
class Program
{
static void Main(string[] args)
{
var serviceUri = "net.pipe://localhost/Foo/Bar";
var discoveryUri = new Uri("soap.udp://239.255.255.250:3702/");
var service = new MyService();
var serviceHost = new ServiceHost(service, new Uri(serviceUri));
serviceHost.Description.Behaviors.Add(new ServiceDiscoveryBehavior());
serviceHost.AddServiceEndpoint(new UdpDiscoveryEndpoint(discoveryUri));
serviceHost.AddServiceEndpoint(typeof(IMyService), new NetNamedPipeBinding(), serviceUri);
serviceHost.Open();
Console.WriteLine("It worked!");
}
}
[ServiceContract]
public interface IMyService
{
[OperationContract]
void DoStuff();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class MyService : IMyService
{
public void DoStuff()
{
throw new NotImplementedException();
}
}
}
I can run it at the localhost and it works. But if I run the following powershell command from another host:
icm -ComputerName myHost -ScriptBlock {iex "& 'c:\Users\me\Documents\Visual Studio 2013\Projects\PowerShellSecurity\PowerShellSecurity\bin\Debug\PowerShellSecurity.exe'"}
I can see the process hanging at myHost using procexp.
Then I used visual studio to attach to this process, I can see it is stuck at:
serviceHost.Open();
How can I solve this problem, if I have to use power shell to run the application remotely?
Thank you very much!
If I'm not mistaken, named pipes are local to a single computer. As far as I know, you can't connect "remotely" using named pipes.
In this case, I'd recommend using a different binding (protocol.) For example, you could use http/https, nettcp, or WSHttp.
Related
I've been struggling with this windows service now for almost two weeks, I have scoured the internet for a resolution and in the process I have learned a lot except that I have not been able to resolve my issue.
I can't seem to find the right way to compose and run a service. There are some articles and opinions on this question even on SO but most of the questions on SO don't even have an acceptable answer, I'm hoping my question will be better accepted by the community so we can settle this windows service issue once and for all.
First of all I have set my configuration mode to debug on x86 (Internal reason for this). I have an installer class as follows:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.ServiceProcess;
using System.Threading.Tasks;
namespace Practique
{
[RunInstaller(true)]
public partial class Installer1 : System.Configuration.Install.Installer
{
public Installer1()
{
InitializeComponent();
ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
ServiceInstaller serviceInstaller = new ServiceInstaller();
//# Service Account Information
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
serviceProcessInstaller.Username = null;
serviceProcessInstaller.Password = null;
//# Service Information
serviceInstaller.DisplayName = "Practique";
serviceInstaller.StartType = ServiceStartMode.Manual;
//# This must be identical to the WindowsService.ServiceBase name
//# set in the constructor of WindowsService.cs
serviceInstaller.ServiceName = "Practique";
//S.Nsibande - Add service description.
serviceInstaller.Description = "Practique - application is for testing how I should send control messages to the SCM in best practice manner so as not to get stupid errors on start and stop control requests to the Microsoft OS.";
this.Installers.Add(serviceProcessInstaller);
this.Installers.Add(serviceInstaller);
}
}
}
My entry point into my service application is as follows:
using System.Collections.Generic;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace Practique
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
#if DEBUG
Service1 myService = new Service1();
myService.OnDebug();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#else
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]{ new Service1() };
ServiceBase.Run(ServicesToRun);
#endif
}
}
}
And then the logic performed by my service is as follows:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace Practique
{
//Service class inheriting from the ServiceBase class
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
public void OnDebug()
{
OnStart(null);
}
//Two required overides... OnStart() and OnStop()
protected override void OnStart(string[] args)
{
EventLog log = new System.Diagnostics.EventLog();
log.Source = "Application";
try
{
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "OnStart.txt");
System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "Log.txt");
}
catch (Exception ex)
{
log.WriteEntry(ex.Message + ".Stack trace - " + ex.StackTrace);
if(ex.InnerException != null)
{
log.WriteEntry(ex.InnerException.Message);
}
}
}
protected override void OnStop()
{
System.IO.File.Delete(AppDomain.CurrentDomain.BaseDirectory + "OnStart.txt");
}
private void ServiceStatus()
{
// Toggle the Practique service -
// If it is started (running, paused, etc), stop the service.
// If it is stopped, start the service.
ServiceController sc = new ServiceController("Practique");
string path = AppDomain.CurrentDomain.BaseDirectory + "Log.txt";
// Open the stream and write to it.
using (FileStream fs = File.OpenWrite(path))
{
Byte[] info =
new UTF8Encoding(true).GetBytes("The Ptractique service status is currently set to " + sc.Status.ToString() + ".");
// Add some information to the file.
fs.Write(info, 0, info.Length);
}
if ((sc.Status.Equals(ServiceControllerStatus.Stopped)) || (sc.Status.Equals(ServiceControllerStatus.StopPending)))
{
// Start the service if the current status is stopped.
// Open the stream and write to it.
using (FileStream fs = File.OpenWrite(path))
{
Byte[] info =
new UTF8Encoding(true).GetBytes("Starting the Practique service...");
// Add some information to the file.
fs.Write(info, 0, info.Length);
}
sc.Start();
}
else
{
// Stop the service if its status is not set to "Stopped".
// Open the stream and write to it.
using (FileStream fs = File.OpenWrite(path))
{
Byte[] info =
new UTF8Encoding(true).GetBytes("Stopping the Practique service...");
// Add some information to the file.
fs.Write(info, 0, info.Length);
}
sc.Stop();
}
// Refresh and display the current service status.
sc.Refresh();
// Open the stream and write to it.
using (FileStream fs = File.OpenWrite(path))
{
Byte[] info =
new UTF8Encoding(true).GetBytes("The Practique service status is now set to " + sc.Status.ToString() + ".");
// Add some information to the file.
fs.Write(info, 0, info.Length);
}
}
}
}
I might have made some very stupid basic mistake, but that is all the code on my application. And on debug, it runs just fine, it does what it is expected to do. But once installed successfully using a batch file with the following instructions:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe
"C:\Programming\Test\Practique.exe"
Pause
My question which I hope will be answered by someone that has been creating and using windows services successfully for a long time, is why does my service keep causing the following error:
This is the information from event viewer after a successful install:
A service was installed in the system.
Service Name: Practique Service File Name:
"C:\Programming\Test\Practique.exe" Service Type: user mode
service Service Start Type: demand start Service Account:
LocalSystem
Then when I attempt to start the service, I get the following error in event viewer:
A timeout was reached (30000 milliseconds) while waiting for the
Practique service to connect.
And...
The Practique service failed to start due to the following error: The
service did not respond to the start or control request in a timely
fashion.
Then I also get this popup when trying to start the service:
Please tell me if my approach is wrong, have I missed a basic principle here, what have I done wrong or have I done too much. Any assistance is greatly appreciated.
(Edit) - I am compiling in Debug mode, although I have tried release mode just in case there might be a difference, but this did not help.
With the code you have you must compile in RELEASE mode to install your service: the SCM requires a response from your service that it has started before the service starts doing any work: when your service is compiled in DEBUG mode it starts working straight away, so never reports back to the SCM, thus resulting in the error.
I have an existing .NET application that controls external hardware. I am looking into extending some of the functionality that already exists on the PC to a smartphone app that will be used exclusively over a local network. This is not an enterprise system installed in a single location, it is a system sold to the public. WCF looks like a great solution, but if I'm going to have to walk users through manually setting up the service, configuring IIS, etc, that's a showstopper. How can I programatically deploy a WCF service so it is visible on a local network?
WCF can be hosted several different ways. Here is a great article that should get you going. You can jump to the section called "Exploring Your Hosting Options".
I've got it figured out. There are obviously multiple hosting methods, as Code Chops pointed out. For my requirements, I just need a self hosted solution that is running when the program I'm extending is running. I also used C# exclusively, with no xml configuration. This allows me to programmatically determine the local IP address (not shown). This all runs in a normal console app.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
namespace SelfHost
{
class Program
{
static void Main(string[] args)
{
string localIP = "192.168.1.5";
string port = "8001";
Uri baseAddress = new Uri("http://" + localIP + ":" + port + "/hello");
using (ServiceHost host = new ServiceHost(typeof(HelloWorldService), baseAddress))
{
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
host.AddServiceEndpoint(typeof(IHelloWorldService), new WebHttpBinding(), "");
host.Description.Endpoints[0].Behaviors.Add(new WebHttpBehavior { HelpEnabled = true });
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
}
}
[ServiceContract]
public interface IHelloWorldService
{
[OperationContract]
[WebGet(UriTemplate = "SayHello/{name}")]
string SayHello(string name);
[OperationContract]
[WebGet(UriTemplate = "SayGoodbye/{name}")]
string SayGoodbye(string name);
}
public class HelloWorldService : IHelloWorldService
{
public string SayHello(string name)
{
return string.Format("Hello, {0}", name);
}
public string SayGoodbye(string name)
{
return string.Format("Goodbye, {0}", name);
}
}
}
My app uses Nancy Selfhosting. When I launch it without admin rights I get a System.Net.HttpListenerException "Access Denied".
Here is the code:
static void Main(string[] args)
{
var nancyHost = new Nancy.Hosting.Self.NancyHost(new Uri("http://localhost:80/"));
nancyHost.Start();
Application.Run();
}
I have also tried different ports without success. Strangely, I dont get any Exceptions when launching a HttpListener that listens to the same Url. What could be causing this exception?
You need to set the self-host configuration to not rewrite the localhost route via the RewriteLocalhost property.
namespace NancyApplication1
{
using System;
using Nancy.Hosting.Self;
class Program
{
static void Main(string[] args)
{
var uri = new Uri("http://localhost:3579");
var config = new HostConfiguration();
// (Change the default RewriteLocalhost value)
config.RewriteLocalhost = false;
using (var host = new NancyHost(config, uri))
{
host.Start();
Console.WriteLine("Your application is running on " + uri);
Console.WriteLine("Press any [Enter] to close the host.");
Console.ReadLine();
}
}
}
}
I found this out by trying and failing a bit, but this page explains the reason behind.
Alternatively - From the documentation:
Note that on Windows hosts a HttpListenerException may be thrown with an Access Denied message. To resolve this the URL has to be added to the ACL.
Also but the port may need to be opened on the machine or corporate firewall to allow access to the service.
Add to ACL by running the following command:
netsh http add urlacl url=http://+:8080/ user=DOMAIN\username
if you need to remove from ACL:
netsh http delete urlacl url=http://+:8080/
You can hosting Nancy with Kestrel. It's really simple:
public void Main(string[] args)
{
var owinHost = new WebHostBuilder()
.UseStartup<Startup>()
.UseUrls("http://+:12345/")
.Build();
owinHost.Run();
}
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseOwin(x => x.UseNancy());
}
}
The only difficulty is to prepare all the dlls (30+) required. We should definitely use NuGet to resolve all the dependencies.
I have a windows service which i can't run or start, only install or directly debug the project.
This is my main:
namespace MyService
{
public static class Program
{
private static void Main()
{
var ServicesToRun = new System.ServiceProcess.ServiceBase[] { new Service() };
System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}
}
}
And this is my Service:
public partial class Service : ServiceBase
{
private dynamic _serviceHost;
public Service()
{
this.InitializeComponent();
this.ServiceName = "MyService";
this.CanShutdown = true;
this.CanStop = true;
}
private static string HostName
{
get
{
string hostName = Dns.GetHostName();
IPHostEntry ipHostEntry = Dns.GetHostEntry(hostName);
return ipHostEntry.HostName;
}
}
protected override void OnStart(string[] args)
{
var worker = new Thread(this.InitializeHost) { Name = "Host", IsBackground = false };
worker.Start();
}
private void InitializeHost()
{
var baseAddress = new Uri(string.Format("net.tcp://{0}:{1}/MyService", HostName, "9020"));
var mexAddress = new Uri(string.Format("http://{0}:{1}/MyService", HostName, "8000"));
var cache = Factory.Create<string>("MyAssembly.MyClass", "MyAssembly");
var service = new ServiceWcf<string>(cache);
using (this._serviceHost = new Host<string>(service))
{
this._serviceHost.Open(baseAddress, mexAddress);
}
}
protected override void OnStop()
{
this._serviceHost.Dispose();
}
}
When I try to run without debugging or to start after installing the service i get the following error:
Run (directly or through VS):
Error while trying to run project: Unable to start program
'C:\path\to\my\projects\bin\Release\MyService.exe'.
The system cannot find the specified path.
Service start:
The service "MyService" on local cimpouter could not be started.
Error 3: The system cannot find the specified path.
I have no clue where the error could be.
EDIT:
private void InitializeComponent()
{
this.serviceProcessInstaller = new System.ServiceProcess.ServiceProcessInstaller();
this.serviceInstaller = new System.ServiceProcess.ServiceInstaller();
//
// serviceProcessInstaller
//
this.serviceProcessInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller.Password = null;
this.serviceProcessInstaller.Username = null;
//
// serviceInstaller
//
this.serviceInstaller.ServiceName = "MyService";
this.serviceInstaller.DisplayName = "My service";
this.serviceInstaller.Description = "My service is awesome.";
this.serviceInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
//
// ProjectInstaller
//
this.Installers.AddRange(new System.Configuration.Install.Installer[] {
this.serviceProcessInstaller,
this.serviceInstaller});
}
I think this is just an issue with how the service has been registered - i.e. it's in the wrong place.
run an installutil /u [service] from a VS command prompt to uninstall any service entries you already have.
CD over to the folder where you want to run the service from - be careful here, you will have both a debug and a release build - which one do you want to be installed in the services list?
Use installutil /i [service] on the exe to reinstall.
Now it should work.
I think you might have registered the debug build first and have subsequently run a Clean operation on the build before building the Release version; thus deleting the original executable. Either that - or perhaps you moved the project after originally developing it?
Many of the developers I work with use a different folder for their local service installations that is always the same; they then deploy either a debug or release version to it; thus when they want to switch between the two they just copy different file versions over. I don't do this - I only ever register the debug build; but then I have more work to do when testing the release build :)
I want to query for a list of services running as a specific user on a remote machine and then check the health of each. I'm building a custom console.
ServiceController.GetServices("machineName") returns an array of ServiceController objects for a particular machine.
This:
namespace AtYourService
{
using System;
using System.ServiceProcess;
class Program
{
static void Main(string[] args)
{
ServiceController[] services = ServiceController.GetServices();
foreach (ServiceController service in services)
{
Console.WriteLine(
"The {0} service is currently {1}.",
service.DisplayName,
service.Status);
}
Console.Read();
}
}
}
produces:
The Application Experience service is currently Running.
The Andrea ST Filters Service service is currently Running.
The Application Layer Gateway Service service is currently Stopped.
The Application Information service is currently Running.
etc...
Of course, I used the parameterless version to get the services on my machine.
To use the ServiceController method I'd check out the solution with impersonation implemented in this previous question: .Net 2.0 ServiceController.GetServices()
FWIW, here's C#/WMI way with explicit host, username, password:
using System.Management;
static void EnumServices(string host, string username, string password)
{
string ns = #"root\cimv2";
string query = "select * from Win32_Service";
ConnectionOptions options = new ConnectionOptions();
if (!string.IsNullOrEmpty(username))
{
options.Username = username;
options.Password = password;
}
ManagementScope scope =
new ManagementScope(string.Format(#"\\{0}\{1}", host, ns), options);
scope.Connect();
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(scope, new ObjectQuery(query));
ManagementObjectCollection retObjectCollection = searcher.Get();
foreach (ManagementObject mo in retObjectCollection)
{
Console.WriteLine(mo.GetText(TextFormat.Mof));
}
}
You can use wmi for this (System.Management). You can also use ServiceController.GetServices().
This will check you system's service name with your desired service name which you can mention on parameter
namespace ServiceName
{
using System;
using System.ServiceProcess;
class Service
{
public static bool IsServiceInstalled(string serviceName)
{
ServiceController[] services = ServiceController.GetServices();
foreach (ServiceController service in services)
{
if (service.ServiceName == serviceName)
return true;
}
return false;
}
}
}