I am not able to detect this Logon event in Windows.
Here is my code:
namespace ConsoleApplication2
{
public class MyService: ServiceBase
{
public MyService()
{
CanPauseAndContinue = true;
CanHandleSessionChangeEvent = true;
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
base.OnSessionChange(changeDescription);
}
}
class Program
{
static void Main(string[] args)
{
MyService tpl = new MyService();
Thread t = new Thread(delegate()
{
while (true) { }
});
t.Start();
}
}
}
How do I test run this app and then remote desktop into my laptop? I can see the event generated in Windows EventViewer, but my OnSessionChange is never called (I added a breakpoint inside).
Is my code wrong or is the way I am testing wrong?
Normally multiple concurrent remote desktop sessions are not allowed on any of Windows desktop systems. So to use RDP to login as a different user then I assume that you have hacked this, or are using windows server (which rules out XP!).
Regardless, each user logged into the system will therefore have their own applications running and each set of applications are unique to that user. So App1 could be run independently by each user.
That means that your console application cannot detect the other user that is logged on.
To do this you must use a Windows Service. This runs in the background and can detect and work for multiple users and also detect login and logout. See this SO link
This is the purpose of inheriting MyService from ServiceBase. If you are not running the application as a service, then you are not running it correctly!
You need to first install your application as a service and then run it like a service.
You say that you don't think your application can run as a service. I'm not sure why, but if this is the case then you would have to instead look at creating some kind of script to run your application upon start-up/login.
This way, every time somebody logs in then your application would run. This might anyway be simpler for you.
Related
I am trying to restrict my application from having multiple instances at machine level, i.e. A computer will have multiple users logging into it and this application is required to have only one instance for performance reasons. So if User A starts the application, User B should simply get a message that this application is already running on User A's account. Now before you start schooling me on processes, I already tried that, and it doesn't work because for my application to check if a similar process is running, it needs to start the process(the application), in this case, the application will never start.
I am using this to restrict multiple instances, and it works great but it only works at user level.
Microsoft.VisualBasic.ApplicationServices
public class SingleInstanceApplication : WindowsFormsApplicationBase
{
private SingleInstanceApplication()
{
base.IsSingleInstance = true;
}
public static void Run(Form f, StartupNextInstanceEventHandler startupHandler)
{
SingleInstanceApplication app = new SingleInstanceApplication();
app.MainForm = f;
app.StartupNextInstance += startupHandler;
app.Run(Environment.GetCommandLineArgs());
}
}
Any help or advice will be much appreciated...
How are users accessing the application? You could create batch script that checks the process, if one is running then the script simply won't start the application. Not sure if you want to solve this programatically inside your application or through other means. But if you don't want a second instance of said application running your best bet would be outside of your application such as a batch script.
There is also this: cross-user C# mutex.
Where I work we have flags set to specific SQL tables that prevent multiple users from editing records at the same time. Or you could create a hidden lock file when a user logs in and than remove said file once the user logs out.
I have a simple service with the following code:
on Program.Main method I have the code which is generated by vs itself(2010):
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
}
And in Service1.cs I have:
protected override void OnStart(string[] args)
{
System.Media.SoundPlayer myPlayer = new System.Media.SoundPlayer(#"C:\doorbell-1.wav");
myPlayer.Play();
}
protected override void OnStop()
{
}
I have omit writing the usual automatically generated c# codes to reduce the complexity.
Logically a sound should be played when I start the service but nothing happens when I start the service. Please note that:
1-I install the service using installUtill.exe.
2-The service runs under the localSystem account privilege.
3-Duration of the mentioned .wav file is 3Seconds.
How can I play that sound?
Thanks in advance.
The simple answer is that you can't. Windows Services do not interact with the desktop, and thus they are not capable of using desktop functions like audio services.
Remember, Windows is a multi-user operating system. Can you imagine what would happen if 5 simultaneously logged in users started playing audio?
All users have what's known as a "Windows Station", and there is a special Windows Station for the user logged into the physical computer. Windows Services have a null or unique (non-interactive) Windows Station, and thus cannot interact with the console WS.
This Windows Station is what is used to redirect audio, and the audio in the console WS goes to the speakers. All other audio either is redirected to the network station they're using, or does nothing.
A more complex answer is that it might be possible, since the Windows Audio service is itself another service, and you might be able to interact with it directly, but this would be very low level and something you are probably not skilled enough to do.
Finally, it's possible to make services interact with the desktop. However, this is considered a deprecated function and is not always easy to use. It's also a huge security vulnerability, and makes your service susceptible to being used by malware to compromise a machine.
I've been searching all over the internet whole the last night.
This question has been answered so many times but a simple answer is never given. If you have the same question there is a very simple but a tricky way to do something when the service asks for it.
Let's say you want to play a song when service starts.
first of all create an EventLog class:
public class EventLogEngine
{
private string _sourceName, _logName;
public EventLogEngine(string sourceName, string logName)
{
if (!EventLog.SourceExists(sourceName))
EventLog.CreateEventSource(sourceName, logName);
_sourceName = sourceName; _logName = logName;
}
public void WriteLog(string message, EventLogEntryType eventType, int Id)
{
EventLog.WriteEntry(_sourceName, message, eventType, Id);
}
}
protected override void OnStart(string[] args)
{
EventLogEngine eventWriter = new EventLogEngine("mySource","myLog");
eventWriter.WriteLog("sourceName","Service started",EventLogEntryType.Information,"anyId");
}
There is nothig about playing a sound till here and there is nothing complicated yet. But how to play a sound or do something else? Here is the answer :)
1-Go to control panel and open Event Viewer
2-Find your event log and click on it
3-On the right panel you will see your entries which you have write through your code.
4-Right click on the entry and select Attach Task To This Event!
So far you should have understood what I am going to do. right?
5-Select that option and declare what do you want to do when this entry is set. You can simply attach as many as tasks you wish.
Now write a program that plays a sound (Ex:in vb6) and tell Event Viewer to carry out this program each time this entry gets written(each time your service starts)
It is possible to play sounds from a windows service.
Play wave file from a Windows Service (C#)
Playing a sound from a service doesn't violate the rules of interactivity because it's not an interactive function: it's a notification.
I have a MVC4 app in which I need to init a long running process. Currently, the code for this process is in a console app being installed as a service with topshelf. I have the process checking a database every few seconds to see if it needs to be run, but that's not a solution. I need a way for the MVC4 app to kick off the process and forget about it, but the process NOT be unloaded with the web app when the response is returned to the client.
Can someone point me in the right direction?
If I'm understanding your question, what you can do is, in the service (the class that's derived from ServiceBase), override OnCustomCommand:
private const int MY_CUSTOM_COMMAND = 140;
protected override void OnCustomCommand(int command)
{
if (command == MY_CUSTOM_COMMAND)
{
... Do stuff here ...
}
}
You can then trigger the command in your service, from some external application along these lines:
private const int MY_CUSTOM_COMMAND = 140;
using (ServiceController sc = new ServiceController("MyTaskService", "ServiceMachine"))
{
sc.ExecuteCommand(MY_CUSTOM_COMMAND);
}
That's the basic idea. Custom commands can be any value from 128-256 inclusive.
We use a similar system in our web app, which allows users to submit "jobs" that are then run by a windows service. The web app sends a command to the windows service to let it know a new job has been submitted. The service then goes to the DB to get the information about the job to execute.
Basically I need my application to run from system start until system shutdown. I figured out the following approach:
create MyApp.exe and MyService.exe
MyApp should install MyService as a service
MyService is supposed to run at startup and periodically check if MyApp is running. If it's not than start it.
That's the code I wrote for my service:
protected override void OnStart(string[] args)
{
while(true)
{
int processesCount =
Process.GetProcessesByName(Settings.Default.MyAppName).Count() +
Process.GetProcessesByName(Settings.Default.MyAppName + ".vshost").Count() +
Process.GetProcessesByName(Settings.Default.MyAppUpdaterName).Count();
if(processesCount==0)
{
//restore
var p = new Process { StartInfo = { FileName = Settings.Default.MyAppName, Arguments = "" } };
p.Start();
}
else
{
}
System.Threading.Thread.Sleep(3000);
}
}
How can I install this process so that it starts on windows start?
I'm not sure if this infinite loop in OnStart method is a good idea. Is it?
Is the general idea ok?
What I've done is have a windows service that runs the logic and main application code. Then if you need a GUI for it, have the windows service expose a web service via WCF and create a windows app that calls to the web service. On install, put you windows app in the windows startup.
This model will have the main application code running all the time, but the GUI is only up when a user is logged in.
Is the general idea ok?
As Hans points out in comments this is hostile to the user and fortunately won't work on Vista or later because services run in their own windows station. Put whatever logic you need to run all the time in the service and use an IPC mechanism such as WCF to communicate with an (optionally) running UI. If the user disables the service or exits the GUI respect their wishes...
How can I install this process so that it starts on windows start?
Add an entry to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run or HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Runthat points to your GUI application.
I'm not sure if this infinite loop in OnStart method is a good idea.
Is it?
No. You need to return from OnStart if you need to do work after OnStart returns create a Thread to do that work.
I've created a run of the mill C# Windows Service using the windows service project template. I've been able to install it correctly, and start and stop it without any issues.
However, when I go to my event viewer to see the log of it starting and stopping, I get nothing.
Here's the sample code I'm testing:
public MyService()
{
InitializeComponent();
ServiceName = "My Data Service";
EventLog.Log = "Application";
}
protected override void OnStart(string[] args)
{
EventLog.WriteEntry("Starting My Data Service");
}
protected override void OnStop()
{
EventLog.WriteEntry("Ending MyData Service");
}
Also, my OS is Windows Vista.
If you want it to log to the default log use
EventLog.WriteEntry("Starting My Data Service", EventLogEntryType.Information);
Of course, you have to ensure that the service is running under an account with sufficient privileges to write to the log and to "run as a service".
Found this example on SO, Best Way to write to the event log
Here's an example where you specify the source too, rather than displaying as .NET Runtime... MSDN Example
EventLog requires an EventSource created to be able to write.