This question is a followon from my previous where I have since discovered that it is not working 100% on my computer: WCF service not running on non-dev machine
I'm working through this example:
http://msdn.microsoft.com/en-us/library/ff649818.aspx
It turns out that the InstallUtil step isn't really working here. I've discovered that if VS2010 has the project open, and you go to add a service reference like in step 8 of the tutorial, VS2010 actually starts up the service host and therefore a reference is created.
Here's how i've debugged so far:
Install the service as per InstallUtil, close down VS2010 solution; then open a completely different solution (TESTWCF) Try and add a service reference and it fails - cannot find at the specified address
Open WCFServiceLibrary1 project again as a separate instance of VS2010. Try and add a service reference to TESTWCF and it fails.
Within WCFServiceLibrary1, attempt step 8 - add a service reference. This causes the service host to start and the service is found.
With service host still running, in TESTWCF I then try and add service and it works.
Close down the service host and try and add reference in TESTWCF and it doesn't work again.
This all seems to be totally independant of the service running or not running as installed by InstallUtil.
I've also verified this through the creation of a new virtual server from scratch and loading things on one by one. And only when VS2010 was installed did it start to work - when I observed above.
Any ideas ?
WCF services can be self-hosted in an application (such as a console or a Windows Forms application)
I think you are over complicating it, you don't have to even install it with InstallUtil.
InstallUtil installs it to run as windows service, and you can make console application which will be serving as WCF service.
You have to import:
System.ServiceModel
System.ServiceModel.Web
System.Web.Services
I think those with web will be needed if you want to use it as web service with get and post.
Then you need to specify contract for client and server.
[ServiceContract(Name = "SomeService", Namespace = "http://some.domain.com/some/someservice",SessionMode=SessionMode.Required)]
public interface ISomeService
{
[OperationContract]
string Execute(string expression);
}
You have contract and now you have to implement it in service. nothing special in there just use this interface.
What is very important is app.config, you have to specify it well for client and for service. In config you have all stuff that points to service.
In client you have to add service as reference, it should find it as in point 8 but only if you have configs ok!
In client just do something in code like that:
using (ChannelFactory<ISomeService> channel = new ChannelFactory<ISomeService>("SomeService"))
{
ISomeService svc = channel.CreateChannel();
svc.Execute("my expression to evaluate by the service");
}
Try to make it easiest possible way without InstallUtil and such, it doesn't have to be windows service to serve stuff over network.
Success ! After like 4 days of effort on this, the MSDN tutorial has a fatal flaw.
In the first step of the tutorial you create a wcf service library and by default it names the service Service1. In step 2.6 of the tutorial you are asked to specify the base address:
net.tcp://localhost:8523/Service1
Step 3 you are asked to create a new windows service, and by default this is also called Service1.
In step 5.2 you are asked to make a reference to System.ServiceModel and to WcfServiceLibrary1.
In step 5.6 you replace the Onstart Method to start the service and, Step 8 shows the final code as being:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.ServiceModel;
using WcfServiceLibrary1;
namespace WindowsService1
{
public partial class Service1: ServiceBase
{
internal static ServiceHost myServiceHost = null;
public WCFServiceHost1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if (myServiceHost != null)
{
myServiceHost.Close();
}
myServiceHost = new ServiceHost(typeof(Service1));
myServiceHost.Open();
}
protected override void OnStop()
{
if (myServiceHost != null)
{
myServiceHost.Close();
myServiceHost = null;
}
}
}
}
The crucial line of code which is wrong is:
myServiceHost = new ServiceHost(typeof(Service1));
Well it might behave differently in VS2008 or 2005 or maybe it's a config in VS2010 however, my VS2010 interprets Service1 to be that of the containing class ie:
WindowsService1.Service1
Whereas it should in fact be:
WcfServiceLibrary1.Service1
I noticed that 4 days ago but figured I didn't know enough about WCF and I was wrong somehow - esp when it appeared to work 'cause of VS2010 starting it up itself.
Related
Even though there are similar questions, I couldn't find any that solves mine. I have a simple program that runs as a service and I want to start it programatically. It's as simple as this:
private static void StartService()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new MyService()
};
ServiceBase.Run(ServicesToRun);
}
As expected, I can't just start my service without installing it. Windows gives me the following error message:
Cannot start service from command line or debugger. A windows service must first be installed using installutil.exe and then started with service explorer, Windows services administrative tool or NET start.
So far so good. So I went there and did just as the docs says:
installutil <my_project>.exe
The installation was successful and I can even start my service from Service Manager or net start. The only problem is: when I debug my application (via F5), Windows keeps showing me the exact same message: Cannot start service (...).
I've found a solution here that uses this:
public void onDebug()
{
OnStart(null);
}
Which allows me to run and debug my application normally, but I actually need it to run as a service and Windows refuses to start that way. Is there anything I'm missing?
It is not in your power to just start a Service like a normal programm. The Service must be registered with and started by the Service manager. That is one of the (many) rules of Windows services. And you have to repeat that for every new build.
As this and other Service related rules (no interactive sessions) can make developing them a Pain, a common approach is to develop them using a console application. I could not find my ideal example, but I found something like it:
https://alastaircrabtree.com/how-to-run-a-dotnet-windows-service-as-a-console-app/
Of course a better longterm plan might be to stop using Services alltogether and switch over to the Windows Task Scheduler. It depends heavily on what exactly you need this code to be able to do in practice.
I wrote a Windows Service to run on Win10, and it worked perfectly fine until I decided to change it a bit. I rewrote some logic, tested it in both Debug and Release configurations, and everything was fine. Then I uninstalled the current version of the service using installutil.exe /u %servicename.exe% and reinstalled it again using installutil.exe %servicename.exe%.
For some reason, this new version cannot start, and it crashes with Error 1064. This is the full error text:
Windows could not start %servicename% service on Local Computer. Error 1064: An exception occurred in the service when handling the control request.
The last time I installed this service, I ran into some difficulties, but quickly fixed them by changing the Log On properties. This time, it is not working. Please help with this issue.
Thanks.
Update 1
Here are my Main() and OnStart() service methods:
Main()
static void Main()
{
#if DEBUG
var service = new SalesforceToJiraService();
service.OnDebug();
Thread.Sleep(Timeout.Infinite);
#else
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new SalesforceToJiraService()
};
ServiceBase.Run(ServicesToRun);
#endif
}
OnStart()
protected override void OnStart(string[] args)
{
this.ConfigureServices();
this.timer.Start();
this.logger.Information("SalesforceToJira service started.");
}
Update 2
More code:
ConfigureServices()
protected void ConfigureServices()
{
this.configuration = ConfigurationHelper.LoadConfiguration(ConfigurationPath);
this.logger = ConfigurationHelper.ConfigureLogger(this.configuration.Logs.LogsPath);
this.timer = ConfigurationHelper.ConfigureTimer(this.configuration.ProcessInterval.TotalMilliseconds,
(sender, eventArgs) => this.ProcessCasesAsync(sender, eventArgs).GetAwaiter().GetResult());
this.salesforceClient = new SalesforceCliClient(this.configuration.Salesforce.CliPath);
this.jiraClient = Jira.CreateRestClient(
this.configuration.Jira.Url,
this.configuration.Jira.Username,
this.configuration.Jira.Password);
}
I'm using Newtonsoft.JSON for deserializing a JSON configuration file, Serilog for logging, System.Timers.Timer for periodic events, AtlassianSDK for the Jira API and some wrappers over Salesforce CLI for Salesforce.
Thanks to #Siderite Zackwehdex's comment, I was able to find the full stack trace of the underlying exception in EventViewer, under:
Windows Logs\Application
In my case, my service is named "HttpDispatcher", which appears in the "Source" column in the top pane.
I could see immediately it was due to a dependency issue where my .NET 4.7.2 project was not pulling across my .NET Standard references. (That ol' chestnut).
I faced the same issue. The reason was I forgot to set the Database connection properly in configurations.
I had this exact same error 1064 starting my service. For me the user I had the service registered as was not a valid user in the database. Once added, it worked great.
I also had the same error in my Windows Service.
The reason was it can't read a configuration parameter, so it crash.
Adding some validation (bugfixing), the Windows Services can start it correctly.
In my case the error was due to issues with Event log name
It got fixed after I went to RegEdit and deleted old service name from HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
I have also faced this issue. In my case it is due to connection fail with the data base. I think it is due to code throw the exception.
My error :
Windows could not start the service1 service on local computer.
Error 1064: An exception occured in the service when handling the control request
I corrected my issue by updating the third party DLL.
I faced the same issue, here is how I resolved it after troubleshooting.
If you are running service on the Server with multiple users, make
sure to run the service as admin user. Click on the service
properties and then on Log on tab click on this account and provide
the admin user name and password.
And If your service is accessing some shared drive, then make sure
you have a general user on all servers for accessing the shared
drives and add the user as local admin as well.
For me it happened when I tried to restart a process. Turned out the process was hanging in 'Stopping' so I had to kill it manually via command line and the PID.
I want to be able to use the TopShelf debugging abilities of my service in Visual Studio.
A lot of the examples and documentation out there refer to creating a Windows Console project in Visual Studio first, and then adding TopShelf, OWIN, etc
However, in my case I already have a perfectly good and working Windows Service project called QShipsService.sln, etc... and it uses a simple Connected Service (admittedly to old SOAP legacy services).
Can someone please direct me or provide an example of how to use TopShelf, with an existing non-Console like project?
I found my own solution...
The assumption I made was the default Windows Service project defaulting to wanting to register the program as a service and kick off the OnOpen() and OnClose() methods, once the service is running.
In my case I wanted to re-use an existing service that was based on a Timer(), and it would kick in every 4 hours to call a SOAP call and return some data. What I didn't realise was the ServiceConfigurator was trying to call its own Open() and Close() methods.
So I commented out the OnOpen and OnClose methods and allowed the configurator to call my worker process via Open() method instead, which is what I was meant to have done the first time!
For the noobs out there like me, here is the code...
//using System.ServiceProcess;
using Topshelf;
namespace QShipsService
{
static class Program
{
static void Main(string[] args)
{
HostFactory.Run(
configure =>
{
configure.Service<QShipsService.QshipsService>(
service =>
{
service.ConstructUsing(s => new QShipsService.QshipsService());
service.WhenStarted(s => s.QStart());
service.WhenStopped(s => s.QStop());
});
//Setup Account that window service use to run.
configure.RunAsLocalSystem();
//add details and names about the service
configure.SetServiceName("QshipsService");
configure.SetDisplayName("QshipsService");
configure.SetDescription("QshipsService Windows Service to extract data from the QSHIPS SOAP service. Data is recorded and maintained inside the SPOT's database in POT-DB.");
});
//## USE THIS IF WE'RE NOT USING TOPSHELF !! ##
// //this loads and starts the QshipsService (see QshipsService.cs program)
// ServiceBase[] ServicesToRun;
// ServicesToRun = new ServiceBase[]
// {
// new QShipsService.QshipsService()
// };
// ServiceBase.Run(ServicesToRun);
}
}
}
I have created a GRPC Server in C# using the example given at Link. Now I want to figure out as how should I be hosting this server so that I achieve following:
Should I make this Server a Console application or a a Windows Service. If I make it a windows Service then updating the service will be cumbersome (which is a big negative) and if I make it a console app then updating will simply need shutting down exe. But that comes with the price of closing the same by mistake. Is there any other better way?
With IIS this issue won't b there as I can simply remove the site from LB and stop the website to perform the update but since GRPC won't be a part of IIS, I am not sure what's the way to get this working.
Any references for the better architecture are welcomed.
We can use Microsoft.Extensions.Hosting pacakge to host a .net core console application by using the HostBuilder API to start building gRPC host and setting it up.
In order to run the gRPC service, we first need to start/stop Grpc.Core.Server in a hosted service. A hosted service is basically a piece of code that is run by the host when the host itself is started and the same for when it is stopped. The following code implement a GrpcHostedService to override IHostedService interface:
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Microsoft.Extensions.Hosting;
namespace Grpc.Host
{
public class GrpcHostedService: IHostedService
{
private Server _server;
public GrpcHostedService(Server server)
{
_server = server;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_server.Start();
return Task.CompletedTask;
}
public async Task StopAsync(CancellationToken cancellationToken) => await _server.ShutdownAsync();
}
}
In the Program.cs, use HostBuilder API to start building our grpc host and setting it up:
public class Program
{
public static async Task Main(string[] args)
{
var hostBuilder = new HostBuilder()
// Add configuration, logging, ...
.ConfigureServices((hostContext, services) =>
{
// Better to use Dependency Injection for GreeterImpl
Server server = new Server
{
Services = {Greeter.BindService(new GreeterImpl())},
Ports = {new ServerPort("localhost", 5000, ServerCredentials.Insecure)}
};
services.AddSingleton<Server>(server);
services.AddSingleton<IHostedService, GrpcHostedService>();
});
await hostBuilder.RunConsoleAsync();
}
}
By doing this, the generic host will automatically run StartAsync on our hosted service, which in turn will call StartAsync on the Server instance, essentially start the gRPC server.
When we shut down the host with Control-C, the generic host will automatically call StopAsync on our hosted service, which again will call StopAsync on the Server instance which will do some clean up.
For other configuration in HostBuilder, you can see this blog.
I'm going to add one more option.
With dot net core, you can run this as a Linux Daemon now.
Currently gRPC doesn't support integration with ASP.Net/IIS. You would need to host the server in a console or as a Windows service.
Likely you would want this to be a Windows service to make it easier to keep the server running across reboots or crashes. If you want to easily turn your console application into a Windows service I would recommend using the excellent TopShelf Nuget.
Updating the service can be done as you would a console app.
Stop the Windows service. net stop <service-name}>
Copy the updated assemblies.
Start the Windowsservice net start <service-name>
My company (Shortbar) is building the application server for a hotel management system called HOLMS on gRPC. Our setup is as follows:
HOLMS.Application is a .NET class library (assembly) that does the actual work of the server
HOLMS.Application.ConsoleRunner is a C# console application that hosts HOLMS.Application. The console runner is used by (1) developers for convenience (mentioned in the question) as well as (2) production scenarios running inside a Docker container, where the container runtime (e.g. Amazon ECS) implements job control/scaling. It follows "12 factor app" guidelines, including running itself as a single, standalone, stateless process, fast startup/shutdown, and environment-variable config injection. The system logs to stdout which gets drained however stdout is drained in the prod environment (e.g. Sumo, logstash, etc). This is how our SaaS multi-tenant solution will go into production.
HOLMS.Application.ServiceRunner packages HOLMS.Application into a Windows service, for more traditional, on-premise situations where a customer's IT group will run the service themselves. This package uses the Windows registry for configuration and relies on Windows service job control for startup/shutdown/restarts. It logs to the Windows Event Log.
The ConsoleRunner and ServiceRunner each are only about 200 lines of code; for the most part, they just wrap the Application package, and call into it.
Hope this helps.
I have successfully compiled and run Windows Service with WCF. With installutil, the Windows Service is successfully getting installed and started. I think I am at the end of my development and just need to invoke/call the method DoJobs() inside WCF. I don't need any user interaction and so I don't have any Windows forms or anything. I just want to invoke/call my WCF function programmatically just after serviceHost.Open();
The base address in app.config file is
http://localhost:8733/Design_Time_Addresses/WcfServiceLibrary1/Service1/
I am deploying my WCF from Windows service with the following code.
// Create a ServiceHost for the CalculatorService type and provide the base address.
serviceHost = new ServiceHost(typeof(WcfServiceLibrary1.Service1));
// Open the ServiceHostBase to create listeners and start listening for messages.
serviceHost.Open();
I have also added the service reference and created the below proxy, but not sure of its use.
WcfServiceLibrary1.WCFServiceRef.Service1Client
I have searched tutorials, the examples show how to invoke the WCF function on button_click event of any form after running Windows service. I just want to do that programmatically on start-up of Windows Service.
EDIT: The code inside my DoJobs() fetches the active tab url of firefox with DDE Client, which throws exception when done only in a Windows Service project but runs successfully when done in WCF project. Please see this for reference.
So I made a C#.Net solution with WCF called from a Windows Service and then I called DoJobs() inside Windows Service as shown below.
WcfServiceLibrary1.WCFServiceRef.Service1Client wcfObj = null;
...
protected override void OnStart(string[] args)
{
if (serviceHost != null)
{
serviceHost.Close();
}
serviceHost = new ServiceHost(typeof(WcfServiceLibrary1.Service1));
serviceHost.Open();
if (wcfObj == null)
{
wcfObj = new WcfServiceLibrary1.WCFServiceRef.Service1Client();
wcfObj.DoJobs();
}
}
But, it makes the call happen at the windows service layer, and is throwing the same DdeClient exceptions.
Can the base address url help any way to programmatically invoke DoJobs() in Web-Service? OR there are some other solutions.
Any help is highly appreciated.
Thanks.
This is my aggregated answer from my various comments I made to your post and to Noctis's answer (specifically that we did not know you were using DDE in the OP):
You can't use Dynamic Data Exchange (DDE) in a Windows Service because the latter does not have a message pump. Also DDE requires a Window handle to be passed as a parameter to DDE functions. You can use DDE in programs which do have a message pump does as a WinForms app. See this article for more information
Once your GUI app is running you can either minimize it to a Sys Tray icon or hide the app completely so the user is unaware. Regardless of its visible nature you should have no problem utilising DDE since it will have a message pump.
Now it may be the case you could add a message pump to a Windows Service but I wouldn't recommend it because it falls into the category of because you can do a thing, does not mean you should do a thing. A topic for another time. It's very similar to a recent SO question about how to display a WinForm in a console app - by default you can't and if you managed to you end up with an odd hybrid with much re-inventing of wheels. Not to mention its an ugly hack.
In summary, my best advice is to proceed with a GUI app.
Assuming you have :
// I'm assuming this is your proxy?
var proxy = WcfServiceLibrary1.WCFServiceRef.Service1Client;
// All you need to do is :
proxy.DoJobs() ;
Having seen your update and Micky`s answers, I'm just wondering why you're using DDE. Not sure what your requirements look like, but you can always use your MSMQ to send messages and queue things.