I am developing a Windows Service in C# to centrally manage some application connectivity. It's a sleeper service in general, which performs some actions when awoken by an external executable. To this end I'm using named events, specifically the .NET EventWaitHandle. My code boils down to, at the service end:
EventWaitHandleSecurity sec = new EventWaitHandleSecurity();
sec.AddAccessRule(new EventWaitHandleAccessRule(
new SecurityIdentifier(WellKnownSidType.WorldSid, null),
EventWaitHandleRights.FullControl,
AccessControlType.Allow));
evh = new EventWaitHandle(false, EventResetMode.AutoReset, EVENT_NAME,
out created, sec);
Log(created ? "Event created" : "Event already existed?");
As it's an internal application on trusted servers I don't mind that granting 'Full Control' to 'World' in general wouldn't be smart.
At the client end I have:
EventWaitHandle.TryOpenExisting(EVENT_NAME, EventWaitHandleRights.Modify, out evh)
The code above works perfectly when I run my service in console-based interactive mode. The event is found on both ends, the client can set, and the service kicks to work. Everybody's happy.
When installing the service however it doesn't work. The logging still reports that the event was created anew, but the client cannot find the event. As I thought it was security-related I added the World Full Control Allow access rule, but it didn't change anything. I changed the service to run as Local Admin, even as my own user account, but nothing - the client cannot find the event even though logs show the service is happily polling away on it. If I change the TryOpenExisting to OpenExisting I get an explicit exception:
System.Threading.WaitHandleCannotBeOpenedException: No handle of the given name exists.
What am I missing?
Starting with Windows Vista, services are isolated and run in Session 0 (see Service Changes for Windows Vista). When calling CreateEvent (which EventWaitHandle does), the event object is created in the local namespace by default, also called session namespace. An event object created by a service in session 0 with a name in the session namespace is visible in session 0 only. It is invisible to applications running in an interactive user session.
To create an event object by a service (running in session 0) that can be discovered by application code (running in an interactive user session), you have to create it into the global namespace. This is done by prefixing the event name with "Global\", as documented under CreateEvent.
A helpful tool to track down kernel object-related bugs is Sysinternal's WinObj.
Related
As I understand Azure Worker roles run by the help of Host application called WaWorkerHost.exe and there is another application called WaHostBootstrapper.exe which checks if WaWorkerHost.exe is running and if not it will run the WaWorkerHost.exe.
How often does this 'worker role status check' occurs?
How can I quickly restart the Worker role myself? I can either reboot the machine worker role is running and wait for few minutes or chose the following traditional method:
Taskkill /im /f WaWorkerHost.exe
and wait for few minutes for the WaHostBootstrapper.exe to kick in but this very inefficient and slow.
Is there any (instant)method of restarting the worker role?
Can I run something like the following and expect similar results to the WaHostBootstapper.exe or there are other consideration?
WaWorkerHost.exe {MyAzureWorkerRole.dll}
The bootstrapper checks the WaWorkerHost status every 1 second.You can see it in the bootsrapper logs (c:\resources\WaHostBootstrapper.txt), by looking at interval of the trace:
"Getting status from client WaWorkerHost.exe"
You can use AzureTools which is a utility used by Azure support team.
One of the its features is gracefully recycle the role instance:
Alternatively, you can restart the instance programmatically:
Upload management certificate to your subscription.
Use the following code to programmatically restart the instance:
Using Microsoft Azure Compute Management library:
X509Certificate2 cert = new X509Certificate2("");
var credentials = new CertificateCloudCredentials("your_subscription_id", cert);
using (var managementClient = new ComputeManagementClient(credentials))
{
OperationStatusResponse response =
await managementClient.Deployments.RebootRoleInstanceByDeploymentSlotAsync(
"cloud_service_name",
DeploymentSlot.Production, // or staging
"instance_name");
}
This is not recommended, for three reasons:
The bootsrapper checks every second, which should be enough for most cases.
It could lead to weird issues. For example, you kill the worker, bootstrapper identifies that the worker is down, you manually start the worker, bootstrapper also tries to start the worker and fail (will crash? will enter zombie state?). It can lead to unhealthy bootstrapper, means that nothing takes care of the worker process.
It depends, of course, on what's the bootstrapper does other than starting the worker. But even if it is currently does nothing other than starting the role, you cannot know for sure if tomorrow Azure team will decide to add it more responsibilities/actions.
If the role itself is aware that it needs to restart, it can call RoleEnvironment.RequestRecycle to cause the role instance to be restarted.
I'm developing an open source .NET assembly (WinSCP .NET assembly) that spawns a native (C++) application and communicates with it via events and file mapping objects.
The assembly spawns the application using the Process class, with no special settings. The assembly creates few events (using the EventWaitHandle) and file mapping (using the PInvoked CreateFileMapping) and the application "opens" these using the OpenEvent and the OpenFileMapping.
It works fine in most cases. But now I'm having a user that uses the assembly from an ASPX application on Windows Server 2008 R2 64 bit.
In his case both the OpenEvent and the OpenFileMapping return NULL and the GetLastError returns the ERROR_ACCESS_DENIED.
I have tried to improve the assembly code by explicitly granting the current user necessary permissions to the event objects and the application code to require only the really needed access rights (instead of original EVENT_ALL_ACCESS) as per Microsoft Docs example. It didn't help. So I did not even bother to try the same for the file mapping object.
The C# code that creates the event is:
EventWaitHandleSecurity security = new EventWaitHandleSecurity();
string user = Environment.UserDomainName + "\\" + Environment.UserName;
EventWaitHandleAccessRule rule;
rule =
new EventWaitHandleAccessRule(
user, EventWaitHandleRights.Synchronize | EventWaitHandleRights.Modify,
AccessControlType.Allow);
security.AddAccessRule(rule);
rule =
new EventWaitHandleAccessRule(
user, EventWaitHandleRights.ChangePermissions, AccessControlType.Deny);
security.AddAccessRule(rule);
new EventWaitHandle(
false, EventResetMode.AutoReset, name, out createdNew, security);
The C++ code that "opens" the events is:
OpenEvent(EVENT_MODIFY_STATE, false, name);
(For other events the access level is SYNCHRONIZE, depending on needs).
I have also tried to add Global\ prefix to the object names. As expected this didn't solve the problem.
Does anyone have any idea what causes the "access denied" error in OpenEvent (or CreateFileMapping)?
My guess is that the event is created by either the anonymous user or the logged in user depending on how the website is setup. But the sub-process is being launched with the base process user. This can be checked by using process monitor and looking at the acl for the event handle to see who the creator is. Then look at the sub process to see who it is running as.
If this is the case then you can update the acl on the event to include the base process. In addition to this, you may still need to prefix with "global" to make sure that the event can be used across user boundaries.
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.
I have a created window service and installed successfully. I have enclosed a exe file in service but it does not start .exe.
Protected Overrides Sub OnStart(ByVal args() As String)
System.Diagnostics.Process.Start("C:\Users\Dr.Fazan\Desktop\Debug\Macro Recording System.exe")
You should add a Logger class to your service, and catch any unhandled exceptions.
You're probably getting an exception when trying to Start() your process.
My guess is that your service is lacking the right permissions to launch that .EXE file
You can try changing the User on which your service runs on (through the control panel, or, through the command line.
Windows service doesn't usually have rights to start a new process for security reasons. You will need to grant these rights first.
One of the possible way to do that is log service on as administrator. Right-click on the service in services.msc > Properties > Log On > This account. I am only aware of this method, but it must only work for testing and must never be used in production due to the opened security hole.
Ok so part two of I have no will power experiment is:
Summary Question -
Is there a way to set the CanStop property on a windows service dynamically?
Whole Spiel -
I have a service that is currently checking and killing processes (IE Games) I have told it to if it's day I'm not allowed. Great. I set the CanStop to false so that I can't just kill the service if I give into the addiction. I have a program that will have a password check (Someone else enters the password) that will stop the service if the password is correct. (If I have serious withdrawals) Problem is using the ServiceController class.
Far as I can tell, ServiceController just is a decorator (yah design patern guess) and so I have no way to get at the actual service it represents. First attempt was Property Info, but I was too dumb to realize what that would be pointless. Second was Field Info because I thought there might be a private field that "represents" the service. As you might guess, both failed.
Any ideas?
EDIT 1
I'd like to avoid having the CanStop value somewhere I can get to it easily like a config file or registry. So I am attempting, though not successfully, to make this completely handled in program.
New (Failed) Attempts:
ManagementObject service;
ManagementBaseObject stopService;
service = new
ManagementObject("Win32_Service.Name='StopProgram'");
stopService = service
.InvokeMethod("StopService", null,
null);
Not sure this did anything. I assume it couldn't stop because of the CanStop situation.
The "CanStop" is a attribute of the services registration in the windows service control manager. You can't change it mid-stride.
And, of course, if you're smart enough to write your own service then you're smart enough to bring up task-man and simply kill the service process. CanStop will not prevent you from pulling the rug out from under the service. CanStop only tells the service control manager not to send "Stop" commands to the service.
If you want to allow something to pass then use a global event to enable/disable the checking the service does -- or just remove the games from the PC! :-)
Rather than trying to directly access and control the Service, could you set a flag somewhere, (like the registry or a file), that is then checked by your service before it executes the Event you're trying to control.