How can I automatically configure WCF service on user's machine - c#

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);
}
}
}

Related

Trying titanium web proxy solution

We installed nuget titanium web proxy, created a window service and initiated titanium web proxy. The windows service works, runs, and start and stop times are written to a log file. But the web proxy is supposed to catch internet request and afford them, though no such events happens and nothing is logged, when i open some page with different browsers.
Here is our code:
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;
using Titanium.Web.Proxy;
using Titanium.Web.Proxy.EventArguments;
namespace WebProxy1 {
public partial class MyNewService : ServiceBase {
public ProxyServer proxyServer;
public MyNewService() {
InitializeComponent();
}
protected override void OnStart(string[] args) {
proxyServer = new ProxyServer(true, true, true);
proxyServer.BeforeRequest += OnRequest;
proxyServer.Start();
WriteToFile("Service is started at " + DateTime.Now);
}
protected override void OnStop() {
proxyServer.Stop();
WriteToFile("Service is stopped at " + DateTime.Now);
}
public void WriteToFile(string Message) {
string path = "E:\\Downloads\\Logs";
if (!Directory.Exists(path)) {
Directory.CreateDirectory(path);
}
string filepath = "E:\\Downloads\\Logs\\ServiceLog_" + DateTime.Now.Date.ToShortDateString().Replace('/', '_') + ".txt";
if (!File.Exists(filepath)) {
// Create a file to write to.
using (StreamWriter sw = File.CreateText(filepath)) {
sw.WriteLine(Message);
}
} else {
using (StreamWriter sw = File.AppendText(filepath)) {
sw.WriteLine(Message);
}
}
}
public async Task OnRequest(object sender, SessionEventArgs e) {
WriteToFile(e.HttpClient.Request.Url);
// To cancel a request with a custom HTML content
// Filter URL
if (e.HttpClient.Request.Method.ToUpper() == "GET" && e.HttpClient.Request.RequestUri.AbsoluteUri.Contains("google.com")) {
e.Ok("<!DOCTYPE html>" +
"<html><body><h1>" +
"Website Blocked" +
"</h1>" +
"<p>Blocked by titanium web proxy.</p>" +
"</body>" +
"</html>");
}
// Redirect example
if (e.HttpClient.Request.RequestUri.AbsoluteUri.Contains("wikipedia.org")) {
e.Redirect("https://www.paypal.com");
}
}
}
}
I think you did not set the titanium proxy properly.
Before starting the proxy, you have to set endpoint.
There is titanium proxy using example here.
This is my sample source.
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
{
// Use self-issued generic certificate on all https requests
// Optimizes performance by not creating a certificate for each https-enabled domain
// Useful when certificate trust is not required by proxy clients
//GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password")
};
// Fired when a CONNECT request is received
explicitEndPoint.BeforeTunnelConnect += OnBeforeTunnelConnect;
// An explicit endpoint is where the client knows about the existence of a proxy
// So client sends request in a proxy friendly manner
proxyServer.AddEndPoint(explicitEndPoint);
proxyServer.Start();

Creating running and installing windows services - How to appropriately send a control request to the SCM

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.

Cannot use powershell to remotely start WCFapplication (System.ServiceModel)

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.

cant start nancy self host without admin rights

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.

In C# how do i query the list of running services on a windows server?

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;
}
}
}

Categories

Resources