I have a c# application that runs as a windows service controlling socket connections and other things.
Also, there is another windows forms application to control and configure this service (systray with start, stop, show form with configuration parameters).
I'm using .net remoting to do the IPC and that was fine, but now I want to show some real traffic and other reports and remoting will not meet my performance requirements. So I want to combine both applications in one.
Here is the problem:
When I started the form from the windows service, nothing happened. Googling around I've found that I have to right click the service, go to Log on and check the "Allow service to interact with desktop" option. Since I don't want to ask my users to do that, I got some code googling again to set this option in the user's regedit during installation time. The problem is that even setting this option, it doesn't work. I have to open the Log On options of the service (it is checked), uncheck and check again.
So, how to solve that? How is the best way to have a windows service with a systray control in the same process, available to any user logging in?
UPDATE: Thanks for the comments so far, guys. I agree it is better to use IPC and I know that it is bad to mix windows services and user interfaces. Even though, I want to know how to do that.
Two separate processes that communicate using your technology of choice. Services with UI is a bad idea. Don't go down this road - you'll regret it.
I've had very good results having service communication through a simple socket connection - document your service protocol well, keep it as simple as possible, and it'll be easier than you think.
In practice you should not couple your service with the management UI.
I agree with Greg. Perhaps you could examine a different IPC mechanism. Perhaps use sockets and your own protocol. Or, if your service control app can only control the service on the local machine, you can use named pipes (even faster).
Here is a way mixing up Services and Forms
http://www.codeproject.com/KB/system/SystemTrayIconInSvc.aspx
I figured out how to do this from this article (click on the "Change" link in the Methods table).
string wmiPath = "Win32_Service.Name='" + SERVICE_NAME + "'";
using (ManagementObject service = new ManagementObject(wmiPath))
{
object[] parameters = new object[11];
parameters[5] = true; // Enable desktop interaction
service.InvokeMethod("Change", parameters);
}
I have the solution in a few steps, this is the plan
we are not going to create a service project with a a windows form, instead we are going to create a visual studio solution that contains a windows service project, a windows form project and a setup project.
The idea is to have a database or a file or anything you are comfortable with storing data in which you would store the parameters your windows service will always use to run. So your windows service and your windows form application should be able to modify and retrieve data from it.
To the Main Form Of Your Windows Application drag and drop a NotifyIcon on the form, in the properties tab, browse and select an .ico image(You can sreate one in visual studio but that is another topic you can get on google or contact me) That it will display in the system tray when you run the application and the main form is active or shown, try it, run the application.
Add both of them as outputs in the setup project of the solution. To Add a project to a setup project they must be in the same solution. Right click the setup project in the solution explorer, highlight add and then select project output, add the windows service and the windows form outputs and you will see them in the solution explorer under the setup project.
adding a windows service goes further than this but that also is another topic google it
Creating shortcut for the windows application and adding it to the startup folder is also another topic google or contact me.
NOTE Program your form in such a way that the close button doesn't show and the form goes Me.visible = false and double clicking the icon in the system tray is the only way to set me.visible=true.that way anytime the computer starts up, your windows form application is also started and visible is immediately set to false but since it has a notifyicon with an icon image, it will show in the system tray and double clicking it makes the form visible to edit the settings that you are storing for the service, the service also starts automatically since you would have set it in setting up the service in the setup project.
my mail is iamjavademon#gmail.com for a better illustration using screen shots And explain in full
It is very simply - your need to create one thread for perform application events.
Like this( source code for C++ with CLR, but your can make this in C#):
ref class RunWindow{
public:
static void MakeWindow(Object^ data)
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
Application::Run(gcnew TMainForm());
};
};
And create thread in main
int main(array<System::String ^> ^args)
{
bool bService = RunAsService(L"SimpleServiceWithIconInTrayAndWindow");
if (bService)
{
System::Threading::Thread ^thread = gcnew System::Threading::Thread(gcnew ParameterizedThreadStart(RunWindow::MakeWindow));
thread->Start();
ServiceBase::Run(gcnew simpleWinService());
Application::Exit();
}
else
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
// Create the main window and run it
Application::Run(gcnew TMainForm());
}
return 0;
}
The main problems with interactive services are:
Security - other process could send it messages through its message pump, thereby gaining access to a SYSTEM/LOCAL process.
Incompleteness - an interactive service never sees shell messages, hence it can't interact with Notification Area icons.
We regularly use TCP and UDP connections to pass info from services to other exes, and, in some cases, MSMQ.
Related
I have a C# service (.NET 4.6.2) that polls an online API. Tied to that is also a tray icon. The service is supposed to keep the tray icon running, and the icon is supposed to keep the service running.
If the service has to restart the tray icon for any reason, then it does, but the icon doesn't come back. I am trying to solve that because the users think that if the icon isn't there that it isn't working. When they restart the application and get the icon back, it runs through this:
Process process = Process.GetCurrentProcess();
var dupl = (Process.GetProcessesByName(process.ProcessName));
if (dupl.Length > 1)
{
foreach (var p in dupl)
{
if (p.Id != process.Id)
p.Kill();
}
}
to make sure there is only a single copy at any one time. I realize that services aren't supposed to access the UI directly, but isn't there an indirect method? Can I do it by passing my call through another DLL or something?
I have looked into this quite a bit and find so many resources that end up being outdated. Even this one from last year, which seemed promising doesn't work.
How to create process as user with arguments
This service is deployed on computers from Windows 32bit to Windows 10. There is not always a user logged in, but let's assume for simplicity's sake that there is and I can error handle the rest later.
Indeed, your service is not supposed to interact with a user and if you find an article that sports a hack on how to do it anyway, it's bound to fail sooner or later.
The correct way to do it is to have two applications. A service and a user interface. The service runs as a service. The user interface app is started as the user (maybe from their autostart). Then those two applications communicate via some kind of IPC, maybe sockets or pipes. The user interface can display a tray icon and display status messages it gets from the service or it can send commands to the service that it gets from the user via for example the tray icons context menu.
So do not try to fake a solution. It will not work properly, because it's not supposed to work properly. Build two applications.
TL;DR: I have a service, a gui, and a tray icon. I want to know how to get the tray icon to run on user login/start-up and be able to stop the service and start the gui
Some Background Info:
I have a windows service I've made that uses a xml file to collect
files from other computers on my network and store them on the local
pc (running the service). the xml has some structures called
'profiles' which have info like FileDestination, LocationToTakeFrom,
FileTypeToTake, and IsProfileActive...
The service basically takes all the active profiles and every hour
scans the location for files created within a 1hr window of the
current date/time on local PC.
My GUI allows the user to make profiles / modify profiles, as
well as determine which profile(s) should be active for collection. I
dont want this gui running at all times, so I plan to have a
systemTrayIcon to allow this GUI to be opened and shutdown.
I'm using Visual Studio 2010 .NET 4.0 everything is in C#, I have 1 solution with separate projects(gui and service)
I'm wondering about the following things as far as the System Tray Icon goes:
1) how do I have the icon start on user login (note that this will be distributed via an installer, not just my personal use. so It has to be done via code)
2) Stop a service via sysTrayIcon
3) where to place the SysTrayIcon... do I make a 3rd project? add it in the GUI project? not quite sure here.
4) if SysTrayIcon IS in a seperate project how can I have it create instances of the GUI?
ie how can I start the GUI application from code in a different project
the project requirements are:
-upon installation the GUI must start, after that the Gui should only be accessed through the tray icon.
-user should be able to stop the service any time via system tray icon
Start the client
There are plenty of ways to start an application on Login under Windows. Just grab SysInternals AutoRuns to get an idea. The obvious ones are (a) the good ol' Startup group and (b) one of the
\SOFTWARE\Microsoft\Windows\CurrentVersion\Run*
keys under HKCU and/or HKLM. That's the typical task of an setup utility, which makes sense since you have to install the service anyway. To do it in code:
Registry: Open the reg key, add the entry, close the reg key.
Startup: determine the value of CSIDL_STARTUP or CSIDL_COMMON_STARTUP using SHGetFolderPath, then create a Shell Link in that folder.
Service communication & control
The GUI part implements the TrayIcon and communicates through some channnel with your service. Again, there are plenty of possibilities how to do this, like disk files or memory mapped files, named pipes, even sockets. It would be too broad to list all the ways here, so I'd suggest to decide for one way and ask again if you have specific questions on that one.
To stop your service from code, use the ControlService() function and pass SERVICE_CONTROL_STOP as the dwControl parameter. To start a service, there's another function named (big surprise) StartService() to achieve that. Note that you may need to start an elevated copy of your app to control services. A quick & dirty way is to simply launch net start/stop MyService elevated with the necessary args.
I am writing a windows service to process emails on a daily basis. This service includes a App.Config file, which has several parameters for the service to work accordingly.
Every time, the admin user has to go and change / add / delete the pair inside the section using a text editor.
I am planning to include a windows form to load all the pair from the section and thinking of doing any modification through the form.
All I would like to know is whether it's possible to have a winform inside a windows service and open it when ever the configuration needs to be changed? I know we can have a seperated windows application and load the App.Config file of the windows service. I just want to avoid having a seperate app for this.
If you have done something very similar to this, please share your thoughts!
Regards,
Sriram
That sounds like a security issue if nothing else.
A Windows service runs in a different context and account and cannot interact with the desktop unless specifically allowed when installing the service. This is not enough of course so you'd also have to have the service running under the same account that is running the desktop - this in itself is really bad design and not something I would recommend.
You could also have the service executable decide what to do during launch, a common pattern is to have it spawn as a console application when debugging to simplify development. But then you'd have to stop the service and launch the service executable manually interactively to get the UI behaviour.
In any way, a separate configuration tool is the way to go.
How can I load a 'login form' in OnStart event of windows Service?! I know windows service is incompatible with UI. but I need to do this without using windows startup..
Is it possible? and how?
Thanks a lot.
How can I load a 'login form' in OnStart event of windows Service?
You cannot do this, because Windows services cannot display a user interface.
I know windows service is incompatible with UI.
Oh. You already knew that. Good.
but I need to do this without using windows startup..
This does not change the fact that it is not supported and will not work.
Is it possible? and how?
No, because:
windows service is incompatible with UI.
So what do I do!?!
The real answer here is that your design is wrong.
If you need someone to log in to your application, you should not be creating a service.
Just make a standard Windows application (e.g., using Windows Forms or WPF) and set it to start automatically when any user logs on to the computer. This can be accomplished easily by adding a shortcut to it to the All Users "Startup" folder.
Then, when your app runs, you can display whatever UI you need to, without the limitations of a service.
If you need to combine UI interaction with a service, you ought to write two programs - the service, which exposes some kind of API, and a client program that interacts with that API (using whatever IPC mechanism you want to choose)
Just remember that multiple users can log onto the same machine, so you ought to write everything to cope with multiple instances of the client program running at the same time.
I am writing an application in C# that needs to run as a service but also have user interaction. I understand that services have no UI, etc, so I've divided up my program into a windows form application and a service that can communicate with each other.
The problem I'm having is that I need the service to make sure the windows form application is always running and restart it if it is not. I'm able to detect if it is running, and restart it with the following code on Windows 2000/XP:
System.Diagnostics.Process.Start("ExePath");
but on Vista, it runs the new process as a Local/System process which is invisible to the user. Does someone way around this? Is there some way to detect which user is currently logged on and run the new process as that user? I don't need to account for fast-user switching at this point. Something - anything - basic would suffice.
I would be grateful for any help or tips you have on the subject.
I need to clarify that I am setting the "Allow service to interact with desktop" option when the service is installed. This is what allows it to work on 2000/XP. However, Vista still has the aforementioned problem.
The general idea for this sort of thing is, if the user needs to interact with a service, they should launch a separate application. If you want to help them out, you can configure that separate application to start with windows by placing a shortcut in the start up menu. You can also build crash recovery into your application so it can automatically restart.
You shouldn't really rely on monitoring the forms application, what if no one is logged in? What if multiple people are logged in? It just gets messy doing things this way.
Having the service just sit there and broadcast to listeners is the way to go. When the forms application starts it can notify the service it wants to listen to events.
See the question: How can a Windows Service execute a GUI application?. It addresses the same question from C/C++ (short answer: CreateProcessAsUser), but the answer's still valid (with some P/Invoke) for C#.
In this case, you will have to have a third monitor process which detects if the program fails and restart it in that case.
However, you end up with an unsolvable problem here, as the monitor process will have to be watched to make sure it doesn't get shut down, and so on, and so on, and so on.
You might want to reconsider this approach.
Its a tough situation. As mentioned in a couple places, if you must have a UI then technically you shouldn't be using a service. Afterall, services run without a user even logged on. If nobody is logged in, you cannot have a UI.
Normally, when I need a service needs to communicate with the outside world, there are two things I opt for. I can either place an entry in the event log, or I can drop a message in a queue.
In your case I'd use a queue. When a user logs in, you can auto start an app for them that monitors the queue. If the app is running, when the message is received, they are alerted that way as well. However, if the user closes the app then the same thing occurs... they won't know.
First, a quick answer: Does the 'Allow service to interact with desktop' option (service -> Properties -> LogOn) or specifying an account allow what you're wanting? If so, both of these can be configured on your service installer class.
Like the others, I suspect there is a better approach to this and either one of the following is true:
-The code inside the service could be included in the winforms app (perhaps running in a background thread), and added to windows startup. Both will be running
-The winforms app can just listen to the service when it's on, and doesn't need to be started from the service. Or similarly, the app could be added to startup.
To have your service run the application as a user (which seems to be what you are trying to do) you need to do the following:
System.Security.SecureString ss = new System.Security.SecureString();
foreach (char c in password)
ss.AppendChar(c);
System.Diagnostics.Process proc = Process.Start(path, arguments, username, ss, domain);
Where:
path = full path (including filename) of the executable.
arguments = string of arguments (use an empty string is none)
username = The name of an user account on your server/computer
domain = your network domain (if your using a network account- blank if none)
Also, In order for your service to have permission to launch an application, it must be running as a service also. To do this, you need to add these lines to your service installer class:
serviceProcessInstaller.Account = ServiceAccount.User;
serviceProcessInstaller.Username = "yourdomain\\yourAccountName"; //Or just "AccountName" for local accounts..
serviceProcessInstaller.Password = "yourPassword";
In Windows 2000 and XP, there is an option (checkbox) on the Logon tab of the service properties window to allow the service to interact with the desktop. I believe this is what you are looking for. I just wrote a quick service in VB.NET with a Process.Start("calc.exe") and Windows Calculator opened just fine.
I'm not 100% sure this works the same way in Vista though.
Sounds like you might not need half of it running as a service (unless there's a requirement of higher privileges), as your service would need to cope with when there is no interactive user logged on as well.