I'm working in .NET 3.5 and I have the problem with stopping some service using ServiceController. I searched whole internet and I found no solution to my problem ;)
Here's how I do that:
using (ServiceController service = new ServiceController("Service"))
{
try
{
TimeSpan timeout = TimeSpan.FromSeconds(timeoutSeconds);
service.Stop();
service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
I'm waiting until my service stops. Then I want to replace it's libraries (update it). And the exception is thrown:
UnauthorizedAccessException
Exception Details: System.UnauthorizedAccessException: Access to the path 'blablabla' is denied.
I'm sure that I have access to this path, because I run my application as an Administrator.
What is interesting when this piece of code executes, this Service disappear from the List of current services (in Task Manager). So it actually stops it, but some thread has to still use it, so I can not move any Service's files. Even when I try to move files by myself (I go to the Service directory and try to move its files using mouse) I can not do it. But when I stop the Service manually (Task Manager and 'End Task') I can do whatever I want with its files. So what's the difference between stopping it from C# code (using ServiceController) and stopping it using Task Manager?
I don't know if it's important but I run this application (and this Service) on Windows Server 2008 in Virtual Box. (I needed to run it on some other machine then my computer).
Any ideas how can I solve this problem? ;)
Thanks for any help.
Best wishes,
Pete.
Ok, I solved my problem.
First I used an Administrator Command-Prompt Command of Net Stop to stop the Service and it worked, but I started to wonder why. The answer is it took a lot of time!
ServiceController actually stopped it, but some processes was still using it.
service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
Also workerd fine, because it imidaitely changed the status of the service (it's just one change in Windows Services list), but closing politely all threads using certain Service takes some time :)
So all You need to do is just wait, and then check again if it's still running. If it is, then kill it with Process class.
Related
I'm trying to delete a directory used by a service. Because the directory is used by the service. That is why I have to stop the service. I can start or stop the service by the following code.
static void ToggleHostService(HostStatus serviceStatus)
{
var hostServiceName = "ServiceHost";
if (serviceStatus == HostStatus.run)
{
using (var controller = new ServiceController(hostServiceName))
{
if (controller.Status != ServiceControllerStatus.Running)
controller.Start();
controller.Refresh();
}
}
else if (serviceStatus == HostStatus.stop)
{
using (var controller = new ServiceController(hostServiceName))
{
if (controller.Status != ServiceControllerStatus.Stopped)
controller.Stop();
controller.Refresh();
}
}
}
But when I'm trying to delete the directory, I get exception as
Unhandled Exception: System.IO.IOException: The process cannot access
the file ' backup.wal' because it is being used by another process.
I can see in Service manager window that the service is really stopped. But why it still complains it can't access. To make sure I run the app/code in Admin mode. I tried Please tell me how can I really force delete that directory.
I assume that you have a separate application or service to delete the directory. If you are using the directory before for some reason, then there is possibility of locking the folder by that application or service. In order to free anything related to the directory by using the below code. This needs to be written just before the delete directory:
GC.Collect();
GC.WaitForPendingFinalizers();
Also check If(Directory.Exists()) before deleting the directory to handle relevant exceptions. You can use dispose() also to free up the memory used by the directory.
controller.Stop();
does not wait until the service is stopped, it just places the service in the "StopPending" state.
You have to wait until the service is stopped. I just looked into some code I wrote which does this and runs in production for some years and changed your code:
if (controller.Status != ServiceControllerStatus.Stopped)
{
controller.Stop();
// we do not need Refresh
// controller.Refresh();
controller.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(30));
}
For me, this is the most obvious reason for your "file in use" problem.
But, also look at Lalithanand Barla's suggestion for checking if something still uses something in this dir.
There is an additional possible problem:
Normally, a Windows service starts one or more worker threads / tasks. In OnStop() method, a service should wait until all threads are stopped.
If the service you are trying to stop does not behave this way, there could be threads still alive which also cause file in use.
I don't really have a good solution for that, perhaps retrying your delete operation with some sleep (a second or so) between.
There could also be the possibilty that the service does not only has pending threads, but it also starts another process during OnStart. Then it will be even more difficult.
The C# console application running .NET 4.5.2 (app1) opens a COM application (app2) and does some work with that app2's API. So far all of the work is successful, but sometimes when app1 attempts to close app2, app2 hangs permanently.
If the process for app2 is ended with task manager then app1 reports access denied. Does that occur because the terminated process is no longer available or does it occur because it was blocking a thread in app1 and it was unable to report the error until the thread was allowed to continue?
The code used to terminate app2 is
private static void CloseSW(SldWorks swApp, Process sw_proces)
{
// Close with API call
if (Task.Run(() => { swApp.CloseAllDocuments(true); swApp.ExitApp(); }).Wait(TimeSpan.FromSeconds(20)))
return;
// Kill process if API call failed
if (Task.Run(() => { SWHelper.CloseSW(sw_proces); }).Wait(TimeSpan.FromSeconds(20)))
return;
// Unable to close SolidWorks, ignore error and continue
// This will eventually cause SolidWorks to crash and the crash handler will take over
}
This code should never take much more than 40 seconds to complete, but maybe the COM interop is causing some unexpected behaviour?
I am unable to reproduce this error on a development machine. What it the best way to trace the exact point of failure? It is possible that the failure is not in CloseSW but some point before this. Is there a better way to trace the error than to write each line to a log file?
It is also worth noting that this code works for 60 - 150 runs before it has any errors and both applications are closed between each run.
I have control of the remote environment so remote debugging is an option, but I've never set that up before.
Typically with COM interops causing issues is that IIS is having issues with the object using the current ISAPI.dll. Please verify that your permissions are configured within your assembly to work with your current version of IIS>
A few questions to help assist would be, which framework version are you using, which version of IIS and what is your Application Pool using for a framework.
HTH
I have written a few C# apps that I have running via windows task scheduler. They are running successfully (as I can see from the log files that they are writing ) but windows task scheduler shows them returning a last run result of 0xE0434352. Is there something I need to do in my C# application so that it returns a success code to the windows task scheduler?
Another option is to simply use the Application log accessible via the Windows Event Viewer. The .Net error will be recorded to the Application log.
You can see these events here:
Event Viewer (Local) > Windows Logs > Application
When setup a job in new windows you have two fields "program/script" and "Start in(Optional)". Put program name in first and program location in second.
If you will not do that and your program start not in directory with exe, it will not find files that are located in it.
Hans Passant was correct, I added a handler for AppDomain.CurrentDomain.UnhandledException as described here http://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception(v=vs.71).aspx I was able to find the exception that was occurring and corrected it.
I was referencing a mapped drive and I found that the mapped drives are not always available to the user account that is running the scheduled task so I used \\IPADDRESS instead of MAPDRIVELETTER: and I am up and running.
In case it helps others, I got this error when the service the task was running at didn't have write permission to the executable location. It was attempting to write a log file there.
I had this issue and it was due to the .Net framework version. I had upgraded the build to framework 4.0 but this seemed to affect some comms dlls the application was using. I rolled back to framework 3.5 and it worked fine.
I got the same error but I have fixed it by changing the file reading path from "ConfigFile.xml" to AppDomain.CurrentDomain.BaseDirectory.ToString() + "ConfigFile.xml"
In my case, this error due to file path error because task manager starts program from "System32" as initial path but the folder we thought.
I was getting the same message message within dotNet Core 2.2 using MVC 5, however nothing was being logged to the Windows Event Viewer.
I found that I had changed the Project sdk from Microsoft.NET.Sdk.Web to Microsoft.NET.Sdk.Razor (seen within the projects.csproj file). I changed this back and it worked fine :)
In my case it was because I had message boxes. Once I commented that code out, it started working. I remembered that could be a problem when I looked at the event log as suggested in this thread. Thank you everyone!
I encountered this problem when working with COM objects. Under certain circumstances (my fault), I destroyed an external .EXE process, in a parallel thread, a variable tried to access the com interface app.method and a COM-level crash occurred. Task Scheduler noticed this and shut down the app. But if you run the app in the console and don't handle the exception, the app will continue to work ...
Please note that if you use unmanaged code or external objects (AD, Socket, COM ...), you need to monitor them!
Also message box in PowerShell. I converted PowerShell script to exe. When running as admin it's worked but in task schedule I received this error also.
There was an line in PowerShell script with write-output. After commented this line and compile new exe Task Schedule was completed successfully.
It is permission issue in my case the task scheduler has a user which doesn't have permission on the server in which the database is present.
I read the MSDN article on the topic. To quote:
Because a service must be run from
within the context of the Services
Control Manager rather than from
within Visual Studio, debugging a
service is not as straightforward as
debugging other Visual Studio
application types. To debug a service,
you must start the service and then
attach a debugger to the process in
which it is running. You can then
debug your application using all of
the standard debugging functionality
of Visual Studio.
Now my problem is that my service fails to start in the first place. First it crashes, and says:
An unhandled exception
(System.Runtime.InteropServices.COMException)
occurred in MyServiceName.exe[3596])
and suggests me to debug it (the debugger instance instantly crashes when I choose one). Then it says
Could not start the MyServiceName
service on Local Computer. Error
1053: The service did not respond to
the start or control request in a
timely fashion
So, how can I investigate/debug the reason that my service won't start? The thing is I created a console application that does EXACTLY what the service does and it works fine. (I mean I just copied the OnStart() method's and the main loop's contents to main).
Any help would be appreciated.
The Service is written in C# with heavy use of interop. I am using VS2008
You could use a parameter to let your application decide whether to start as service or regular app (i.e. in this case show a Form or start the service):
static void Main(string[] args)
{
if ((1 == args.Length) && ("-runAsApp" == args[0]))
{
Application.Run(new application_form());
}
else
{
System.ServiceProcess.ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new MyService() };
System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}
}
Now if you pass the parameter "-runAsApp" you can debug the application normally - the SCM won't pass this parameter, so you can also use it as service w/o any code change (provided you derive from ServiceBase)
Edit:
The other difference with windows services is identity (this might be especially important with InterOp) - you want to make sure you are testing under the same identity in "app" mode as well as service mode.
To do so you can use impersonation (I can post a C# wrapper if it helps, but this can be easily googled) in app mode to use the same identity your windows service will be running under i.e. usually LocalService or NetworkService.
If another identity is required you can add settings to the app.config that allow you to decide whether to use credentials, and if so which user to impersonate - these settings would be active when running as app, but turned off for the windows service (since the service is already running under the desired identity):
<appSettings>
<add key="useCredentials" value="false"/>
<add key="user" value="Foo"/>
<add key="password" value="Bar"/>
</appSettings>
I usually just manually set a breakpoint, then point it to the currently open project in c#. The code to set a breakpoint is:
System.Diagnostics.Debugger.Break();
That should get you started, then you can just step through your code and see what's really happening.
I stole this from C. Lawrence Wenham, so I can't really take credit, but you can programmatically attach a debugger to a service, WITHOUT breaking execution at that point, with the following code:
System.Diagnostics.Debugger.Launch();
Put this in your service's OnStart() method, as the first line, and it will prompt you to choose an instance of VS to attach its debugger. From there, the system will stop at breakpoints you set, and on exceptions thrown out. I would put an #if DEBUG clause around the code so a Release build won't include it; or you can just strip it out after you find the problem.
You can use WinDbg/NTSD (another debugger from the "Debugging tools for windows" package) to start a debugger together with your service.
To do this open "gflags" (also available in the above mentioned package) to the "Image file" tab and set the path to debugger executable for your image file (service);
If your service is marked as interactive (only possible if it runs under the SYSTEM account) you can directly start WinDbg, just set the debugger to something like "PATH_TO_WINDBG\windbg.exe -g -G" (the -g / -G are needed so that the debugger doesn't break execution on application start or end - the default behaviour). Now when starting your service the windbg window should pop-up and will catch any unhandled exception.
If your service is not interactive you can start the NTSD debugger (a command line debugger) in remote mode and connect to it from WinDbg (that can even be running in another PC). To do this set the debugger in gflags to something like "PATH_TO_NTSD\ntsd -remote tcp:port=6666,server=localhost". Then connect to the remote debugger by starting windbg with something like "windbg -remote tcp:port=6666,server=localhost" and you should have complete control over the other debugging session.
As for finding the source of the exception itself a windbg tutorial is over the topic here but as a start try to execute the "!analyze -v" command after the exception was caught - with some luck this is all information you'll need..
Note: maybe this is overkill for your case but with this approach you can even debug services during system start-up (I had once a timing problem with a service had an issue only when starting the first time with the system)
One thing I do (which may be kind of a hack) is put a Thread.Sleep(10000) right at the beginning of my OnStart() method. This gives me a 10-second window to attach my debugger to the service before it does anything else.
Of course I remove the Thread.Sleep() statement when I'm done debugging.
One other thing you may do is the following:
public override void OnStart()
{
try
{
// all your OnStart() logic here
}
catch(Exception ex)
{
// Log ex.Message
if (!EventLog.SourceExists("MyApplication"))
EventLog.CreateEventSource("MyApplication", "Application");
EventLog.WriteEntry("MyApplication", "Failed to start: " + ex.Message);
throw;
}
}
When you log ex.Message, you may get a more detailed error message. Furthermore, you could just log ex.ToString() to get the whole stack trace, and if your .pdb files are in the same directory as your executable, it will even tell you what line the Exception occurred on.
Add lots of verbose logging in your OnStart. It's painful and old school, but it works.
Seems like the problem is with the user context. Let me confirm whether my assumptions are right.
When you say that the code works perfectly from console application, I assume you are executing the Console application under the same user which you had logged in.
When you say that the same code crashes while called from the windows service, I assume the service is running in "Local System" account in your development machine.
If both my assumptions are right, please try out the following steps.
In the services listing right-click your service, select properties and then "Log On" tab.
Select the option "This account" and provide the existing username and password.
Now try starting the service. It should now start without any errors.
Following could be the root cause of your error
If you are using SQL Server make sure you are not using SSPI authentication.
If you are trying to read any shared folder\resource which you don't have permission when using "local system" account.
If any of the required dependencies required by the application is in a different folder which the "Local System" user doesn't have permission to access.
If you are using VBA automation which wont work in "Local System" account.
Try disabling your firewall or antivirus.
You could add some logging around the interop calls to find out which one fails.
Also services by default aren't associated with a desktop; if you open the services.msc control panel applet, get the properties of your service, go to the "Log On" tab, you could check "Allow service to interact with desktop". This could fix the problem for you in some cases.
I would assume the reason could be causing because of heavy use of interops. So you need to tackle this problem differently. I would suggest create a windows or console app with same logic of you service and make sure that it works first without any issues, and then you may want to go with creation of the Win service.
Debugging services is a pain, particularly since startup seems to be when many of the problems manifest (at least for us).
What we typically do is extract as much of the logic as possible to a single class that has start and stop methods. Those class methods are all that the service calls directly. We then create a WinForm application that has two buttons: one to invoke start, another to invoke stop. We can then run this WinForm applicaiton directly from the debugger and see what is happening.
Not the most elegant solution, but it works for us.
Check out this question, which discusses how to catch unhandled exceptions in a window service.
In order to attach a debugger to the Windows Service, it needs to be started first. The reason why the service failed to start can be checked in Windows Event Log.
After that the process of attaching a debugger is pretty straight forward from Visual Studio Debug->Attach To Process.
What I've done is implemented by OnStart() to look something like this:
_myBusinessObject = new MyBusinessObject();
After the Business Object has been constructed, timers and IPC handlers do all the real (Service) work.
Doing it like this allows you to create a Forms/WPF application that call the same code above in the Form_Loaded handler. This way, debugging the Forms application is the exact same as debugging the Service.
The only issue is that if you are using app.config values, there will be a second app.config file that needs to be kept up-to-date.
Use following Code in Service OnStart Method:
System.Diagnostics.Debugger.Launch();
Choose Visual Studio option from Pop Up message
read the 2 articles mentioned here:
http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/01/c-toolbox-debug-able-self-installable-windows-service-template-redux.aspx
Step 1 - Add #if region to your Program.cs
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new StockInfoService()
};
ServiceBase.Run(ServicesToRun);
#if (!DEBUG)
ServiceBase[] ServicesToRun = new ServiceBase[] { new SqlBackupService() };
ServiceBase.Run(ServicesToRun);
#else
StockInfoService service = new StockInfoService();
service.OnStart();
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
#endif
}
Step 2 - In Service.cs change your OnStart(string[] args) method without parameter one. (I commended mine.)
public void OnStart()
//protected override void OnStart(string[] args)
{
**Do your thing.
}
Step 3 - Simply hit Start (F5) and debug your code.
I have 2 other services running on a server and they start and stop without a problem, however one of them will not start. I can't see any difference in their implementation or config files. I'm receiving the following messages when attempting to start the service after installing it with InstallUtil:
The service is not responding to the control function
more help is available by typing NET HELPMSG 2186
NET HELPMSG 2186:
Explanation: The service cannot run your command at this time
Thanks very much in advance!
This is most likely due to service installed, then uninstalled and now you are trying to install again. Reboot the machine and try again.
UPDATE
According to the event log error, you are trying to run the service as the current logged in user (I guess you are connected using remote desktop). This is not the correct approach, you need to run the service as the LocalSystem. In the project properties window, change the identity of the service.
UPDATE 2
In the design view of the service/component class, click on serviceProcessInstaller1 (or similar) and then in tmhe properties you see a drop down: Account with 4 entries: User/LocalNetwork/LocalService/LocalSystem. Make it LocalSystem
On Start event can you put
try
{
//...
}
catch(Exception ex)
{
EventLog.WriteEntry(ex.Message + ex.StackTrace);
}
and watch whats happening?
or
attach (Tools > Attach To Process) your process to Visual Studio for DEBUG. You can see whats happening with debug, but EventLog works good aswell.