I am writing a test application which is controlling another computer. The test computer is started by sending a command string via the RS-232 port (from a control computer running Windows XP SP2 using a C# application), at which time the test computer will power-on and boot into Windows XP. I would like to know what would be the best method to determine when that computer has completed it boot process and running normally.
I was thinking of the following:
1) I was either thinking of pinging that computer, or
2) Have a shared drive and if able to access that shared drive, or
3) Writing a small service which I can communicate with
Is there different/better approach?
Mark
It all depends on what you consider "completed its boot process and is running normally". For instance, if all you care is the moment the network card is initialized, pinging might be good (as long as the ECHO port isn't closed).
A share is not a good idea as they generally only become available when a user is logged in, which may or may not be the case depending on your situation. But even if, what if you change the configuration or decide it is a security breach to open up a share?
If you want to play it certain or if you just need to wait until all services have started, you should consider your third option. It's easiest to do. Let it listen on port 80 and run from IIS. When queried, it can answer with some details of the machine. This will also give you the most flexibility. Using IIS helps you for not having to write your own service and makes it trivial to install and configure.
If IIS is not an option, you can of course consider writing your own service. Not that hard to do, but it'll require you to write the code to listen to certain ports yourself.
I had the exact problem you did, I found writing a custom service was the most useful. (I actually needed to know when a headless machine had the Remote Desktop service ready to accept connections, the program I wrote actually beeps the PC speaker a little tune when it is ready to be logged in.
EDIT: I dug up the source in case you where interested.
using System.ComponentModel;
using System.Configuration.Install;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Threading;
namespace Beeper
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Beeper()
};
ServiceBase.Run(ServicesToRun);
}
}
public partial class Beeper : ServiceBase
{
public Beeper()
{
}
protected override void OnStart(string[] args)
{
if (MainThread != null)
MainThread.Abort();
MainThread = new Thread(new ThreadStart(MainLoop));
MainThread.Start();
}
protected override void OnStop()
{
if (MainThread != null)
MainThread.Abort();
}
protected void MainLoop()
{
try
{
//main code here
}
catch (ThreadAbortException)
{
//Do cleanup code here.
}
}
System.Threading.Thread MainThread;
}
[RunInstaller(true)]
public class BeeperInstaller : Installer
{
private ServiceProcessInstaller processInstaller;
private ServiceInstaller serviceInstaller;
public BeeperInstaller()
{
processInstaller = new ServiceProcessInstaller();
serviceInstaller = new ServiceInstaller();
processInstaller.Account = ServiceAccount.LocalSystem;
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.ServiceName = "MyProgram";
serviceInstaller.ServicesDependedOn = new string[] { "TermService" }; //Optional, this line makes sure the terminal services is up and running before it starts.
Installers.Add(serviceInstaller);
Installers.Add(processInstaller);
}
}
}
Related
I am working on a project which is a sort of file uploader. In the project, there are certain log files which are created by an application and saved in a particular folder. The content of these files is always text. The contents of these files are uploaded to the server and this task is done by a windows service. The service reads the files one by one and then transfer their contents to the server.
Earlier this task used to be done by the application itself. But it was separated from application because it is possible that when the application is running the user is offline. So these files will remain pending for upload.
The advantage of using service is obvious that the user doesn't have to worry about upload thing. Whenever the user is connected to internet the service will take care of it.
Now the requirement is that when service uploads the contents of the file and the application via which the files are created is running at that time, the service should send a message to the application that which file's content is being uploaded.
Now the problem is that the service and the application are not communicating. I've spawn seperate threads from both service and application which are using namedpipes for communication.
Here is the code which I've tried.
Service code:
public partial class Service : ServiceBase
{
Thread t;
public Service()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
File.WriteAllText("D:\\log.txt", "Service started.\n");
ThreadStart ts = new ThreadStart(Connection);
t = new Thread(ts);
t.Start();
}
void Connection()
{
NamedPipeServerStream server = new NamedPipeServerStream("server");
server.WaitForConnection();
File.WriteAllText("D:\\log.txt", "Connection established.");
}
protected override void OnStop()
{
if (t.IsAlive)
{
t.Abort();
File.AppendAllText("D:\\log.txt", "Service stopped.");
}
}
}
Application code:
public delegate void SendMsg(string s);
public partial class UI : Form
{
Thread t;
public UI()
{
InitializeComponent();
}
private void UI_Load(object sender, EventArgs e)
{
lblStatus.Text = "Waiting...";
ThreadStart ts = new ThreadStart(Connection);
t = new Thread(ts);
t.Start();
}
void DisplayMsg(string s)
{
lblStatus.Text = s;
}
void Connection()
{
try
{
NamedPipeClientStream client = new NamedPipeClientStream("server");
SendMsg msg = new SendMsg(DisplayMsg);
msg("Connecting...");
client.Connect();
msg("Connected");
}
catch (Exception ex)
{
File.WriteAllText("D:\\log.txt", ex.Message);
}
}
}
What is wrong with this piece of code?
The same logic works if I use another form application in place of service.
While googling about this, I came to know about Session 0 in Windows Vista onward versions of Windows which is used specially for windows services and is isolated from the other sessions used by logged on users. I'm using Windows 8 in my system. Can this be a problem? Please explain in detail.
I'll admit I'm not much of an expert on named pipes. My only experience with them is via the Windows Communication Foundation (WCF), and I've had intermittent problems with them, albeit perhaps because I'm doing something wrong. So I cannot speak intelligently to your named pipe problem. I do know that when Hans Passant chimes in with a word of advice, it is usually best to heed it.
All that said, if you expect the communication requirements to remain simple, there's nothing wrong with using named pipes (sockets, memory-mapped files, etc.). Of course, if the communication between the service and application is expected to grow, it might make sense to consider something more formal like WCF.
I'm attempting to create a service which opens an application, however I have had no luck. Thus, I downloaded the following sample code, and attempted to create a service based on it. However, it does not work either. What happens is that the code executes, however the executable is never called (in the following case, the calculator is not opened).
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.Threading.Tasks;
namespace Open_Calculator
{
public partial class Service1 : ServiceBase
{
public static Process process;
public Service1()
{
//InitializeComponent();
string[] args = { "1", "2" };
OnStart(args);
}
protected override void OnStart(string[] args)
{
start_calc();
}
protected override void OnStop()
{
}
static protected void start_calc()
{
process = new Process();
process.StartInfo.FileName = #"C:\Windows\system32\calc.exe";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.ErrorDialog = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
process.Start();
}
}
}
The same exact code copy-pasted in a console application works.
The service is set to interact with the desktop, and it is set to run as "LocalSystem".
The account running the service does have access to C:\Windows\System32 as I tested it. Process.Start() does not return any errors, and calc is not running in the background (checked through task manager)
Execute it with "Run as administrator" permission
This question has been the cause of great frustration, and I have finally solved my problem. Yes, I have managed to make a service load a GUI application, even though everyone says that it is impossible. There is a warning though - the "fix" can be considered as exploiting a loophole in Windows, as the code which I used basically passes the Vista and Windows 7 UAC. Essentially, the application is always executed with full rights and bypasses the UAC.
If anyone has this same problem, what you need to do is iterate through a list of logged in users on the PC, and choose to open the UI application in this session (which has the user's desktop), rather than in session 0 where the service is supposed to be running (and which does not have a desktop).
For some people, this might not be a solution as it is not secure. But in my case, security is not a main concern, and I just needed it to work (had to be a service by force).
Hope this helps anyone who has the same problem that I had.
how to add some delay between startup projects in solution?
I want Client project to be started after 2-3 seconds from starting WindowsService.
Why I need this?
WindowsService runs socket server and Client runs socket to connect to server. WindowsService loads slowly than Client, and this causes an exception on client side when connecting to server which is not run yet
I would probably add a retry mechanism within the client. That way not only does it help in the "starting up from Visual Studio" case - it also helps if the server happens to be restarting while the real client connects. The fact that the server is on a faster machine doesn't mean the server will never need to restart, does it?
Indeed, you may well want to add this retry mechanism in such a way that the client can recover even if the server is restarted while it's connected. It depends on what the project is doing, of course.
You can use Mutex locking to sync the two startup project.
Program 1 (StartUp Project 1):
namespace ConsoleApplication1
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
class Program1
{
private static bool isNewMutexCreated = true;
private static Mutex mutex;
static void Main(string[] args)
{
mutex = new Mutex(true, "Global\\ConsoleApplication1", out isNewMutexCreated);
AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
Console.WriteLine("Application1 executed on " + DateTime.Now.ToString());
Console.ReadKey();
}
static void CurrentDomain_ProcessExit(Object sender, EventArgs e)
{
if (isNewMutexCreated)
{
Console.WriteLine("Mutex Released");
mutex.ReleaseMutex();
}
}
}
}
Program 2 (StartUp Project 2):
namespace ConsoleApplication2
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
class Program2
{
static void Main(string[] args)
{
Mutex mutex = null;
Thread.Sleep(5000);
while (mutex == null)
{
try
{
mutex = Mutex.OpenExisting("Global\\ConsoleApplication1");
}
catch (Exception)
{
Console.WriteLine("Mutex not found on " + DateTime.Now.ToString());
Thread.Sleep(3000);
}
}
Console.WriteLine("Application2 executed on " + DateTime.Now.ToString());
Console.ReadKey();
}
}
}
Another simpler option for testing is to just delay the client if the debugger is attached like this:
if (System.Diagnostics.Debugger.IsAttached)
{
System.Threading.Thread.Sleep(2000);
}
You might wrap that in an #if DEBUG block if you like. Anyway I think this should be the least amount of work :)
In case of multiple start-up projects, they are loaded in the order they are specified neither simultaneously nor randomly. http://msdn.microsoft.com/en-us/library/09138bex(v=vs.90).aspx
So ,may be if you specify "client" after "window service", then it may workout fine. And if you don't want to got the coded way suggested above, then (for testing only) you can manually attach the "client" process to you solution from a different solution after your desired delay.
http://msdn.microsoft.com/en-us/library/c6wf8e4z(v=vs.100).aspx
If the client needs to be started after, you need to adjust your list, as at the moment its started before!
I would also code a "/wait" which on loading app if it finds that flag, waits for it maybe useful in use too.
You can set the server project as the single startup project and use this macro to launch the server and the client with a delay:
Sub DebugServerAndClientWithDelay()
DTE.Debugger.Go(False)
System.Threading.Thread.Sleep(2000)
DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer).Activate()
DTE.ActiveWindow.Object.GetItem("SolutionName\ClientProjectName").Select(vsUISelectionType.vsUISelectionTypeSelect)
DTE.ExecuteCommand("ClassViewContextMenus.ClassViewProject.Debug.Startnewinstance")
End Sub
You can add a button to your toolbar or use a shortcut key to run this macro.
Just add a procedure to check whether the socket is open or not. If the socket is open continue executing your code and try checking again if the socket is not open. This way even if you start the windows service later there will be no problem.
For the n-tier application I am currently working on, I combined the Mutex method suggested by Romil (slightly different code but same principle) and encapsulated it within a method with a [Conditional("DEBUG")] attribute applied (so it gets stripped out in release mode). We also surround the mutex logic with if (System.Diagnostics.Debugger.IsAttached) {...} since QA builds use Debug mode.
We originally just used a Thread.Sleep with a wait period that worked for most developers machines, but we ran into problems because devs' computer speeds vary and as we added more and more to the server bootstrapper, we had to keep increasing the wait period.
Why don't you just pass an argument to the client application which sets the delay?
static void main(string[] args)
{
// Sleep some time
int delay;
if (args.Length > 0 && int.TryParse(args, out delay))
{
Thread.Sleep(delay);
}
// Initialize client
}
Now you can add the delay in milliseconds to the command-line arguments for the project startup.
I also agree that if possible, it's better to solve your problem structurally, so it doesn't matter when your client and server start.
I currently have a single application that needs to be started from a windows service that i am coding in .net 3.5. This application is currently running as the user who ran the service, in my case the SYSTEM user. If running as the SYSTEM user it does not show the application to the users desktop. Thoughts? advice?
//constructor
private Process ETCHNotify = new Process();
//StartService()
ETCHNotify.StartInfo.FileName = baseDir + "\\EtchNotify.exe";
ETCHNotify.StartInfo.UseShellExecute = false;
//BackgroundWorkerThread_DoWork()
if (!systemData.GetUserName().Equals(""))
{
// start ETCHNotify
try {
ETCHNotify.Start();
}
catch (Exception ex)
{
systemData.Run("ERR: Notify can't start: " + ex.Message);
}
}
I only execute the try/catch if the function i have written GetUserName() (which determines the username of the user running explorer.exe) is not null
again to reiterate: desired functionality is that this starts ETCHNotify in a state that allows it to interact with the currently logged in user as determined by GetUserName()
Collage of some post found around (this and this)
Note that as of Windows Vista, services are strictly forbidden from interacting directly with a user:
Important: Services cannot directly interact with a user as of Windows
Vista. Therefore, the techniques mentioned in the section titled Using
an Interactive Service should not be used in new code.
This "feature" is broken, and conventional wisdom dictates that you shouldn't have been relying on it anyway. Services are not meant to provide a UI or allow any type of direct user interaction. Microsoft has been cautioning that this feature be avoided since the early days of Windows NT because of the possible security risks.
There are some possible workarounds, however, if you absolutely must have this functionality. But I strongly urge you to consider its necessity carefully and explore alternative designs for your service.
Use WTSEnumerateSessions to find the right desktop, then CreateProcessAsUser to start the application on that desktop (you pass it the handle of the desktop as part of the STARTUPINFO structure) is correct.
However, I would strongly recommend against doing this. In some environments, such as Terminal Server hosts with many active users, determining which desktop is the 'active' one isn't easy, and may not even be possible.
A more conventional approach would be to put a shortcut to a small client app for your service in the global startup group. This app will then launch along with every user session, and can be used start other apps (if so desired) without any juggling of user credentials, sessions and/or desktops.
Ultimately in order to solve this i took the advice of #marco and the posts he mentioned. I have created the service to be entirely independent of the tray application that interacts with the user. I did however install the Tray application via registry 'start up' methods with the service. The Service installer will now install the application which interacts with the user as well... This was the safest and most complete method.
thanks for your help everyone.
I wasn't going to answer this since you already answered it, (and it's oh, what? going on 2.5 years OLD now!?) But there are ALWAYS those people who are searching for this same topic, and reading the answers...
In order to get my service to Interact with the Desktop, no matter WHAT desktop, nor, how MANY desktops, nor if the service was even running on the SAME COMPUTER as the desktop app!! None of that matters with what I got here... I won't bore you with the details, I'll just give you the meat and potatoes, and you and let me know if you want to see more...
Ok. First thing I did was create an Advertisement Service. This is a thread that the service runs, opens up a UDP socket to listen for broadcasts on the network. Then, using the same piece of code, I shared it with the client app, but it calls up Advertise.CLIENT, rather than Advertise.SERVER... The CLIENT opens the port I expect the service to be on, and broadcasts a message, "Hello... Is there anybody out there??", asking if they're there ANY servers listening, and if so, reply back to THIS IP address with your computer name, IP Address and port # where I can find the .NET remoting Services..." Then it waits a small amount of time-out time, gathers up the responses it gets, and if it's more than one, it presents the user with a dialog box and a list of services that responded... The Client then selects one, or, if only ONE responded, it will call Connect((TServerResponse) res); on that, to get connected up. At this point, the server is using Remoting Services with the WellKnownClientType, and WellKnownServerType to put itself out there...
I don't think you are too interested in my "Auto-Service locater", because a lot of people frown on UDP, even more so when your app start broadcasting on large networks. So, I'm assuming you'd be more interested in my RemotingHelper, that gets the client connected up to the server. It looks like this:
public static Object GetObject(Type type)
{
try {
if(_wellKnownTypes == null) {
InitTypeCache();
}
WellKnownClientTypeEntry entr = (WellKnownClientTypeEntry)_wellKnownTypes[type];
if(entr == null) {
throw new RemotingException("Type not found!");
}
return System.Activator.GetObject(entr.ObjectType, entr.ObjectUrl);
} catch(System.Net.Sockets.SocketException sex) {
DebugHelper.Debug.OutputDebugString("SocketException occured in RemotingHelper::GetObject(). Error: {0}.", sex.Message);
Disconnect();
if(Connect()) {
return GetObject(type);
}
}
return null;
}
private static void InitTypeCache()
{
if(m_AdvertiseServer == null) {
throw new RemotingException("AdvertisementServer cannot be null when connecting to a server.");
}
_wellKnownTypes = new Dictionary<Type, WellKnownClientTypeEntry>();
Dictionary<string, object> channelProperties = new Dictionary<string, object>();
channelProperties["port"] = 0;
channelProperties["name"] = m_AdvertiseServer.ChannelName;
Dictionary<string, object> binFormatterProperties = new Dictionary<string, object>();
binFormatterProperties["typeFilterLevel"] = "Full";
if(Environment.UserInteractive) {
BinaryServerFormatterSinkProvider binFormatterProvider = new BinaryServerFormatterSinkProvider(binFormatterProperties, null);
_serverChannel = new TcpServerChannel(channelProperties, binFormatterProvider);
// LEF: Only if we are coming form OUTSIDE the SERVICE do we want to register the channel, since the SERVICE already has this
// channel registered in this AppDomain.
ChannelServices.RegisterChannel(_serverChannel, false);
}
System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IPawnStatServiceStatus)));
RegisterType(typeof(IPawnStatServiceStatus),m_AdvertiseServer.RunningStatusURL.ToString());
System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IPawnStatService)));
RegisterType(typeof(IPawnStatService), m_AdvertiseServer.RunningServerURL.ToString());
System.Diagnostics.Debug.Write(string.Format("Registering: {0}...\n", typeof(IServiceConfiguration)));
RegisterType(typeof(IServiceConfiguration), m_AdvertiseServer.RunningConfigURL.ToString());
}
[SecurityPermission(SecurityAction.Demand, Flags=SecurityPermissionFlag.RemotingConfiguration, RemotingConfiguration=true)]
public static void RegisterType(Type type, string serviceUrl)
{
WellKnownClientTypeEntry clientType = new WellKnownClientTypeEntry(type, serviceUrl);
if(clientType != RemotingConfiguration.IsWellKnownClientType(type)) {
RemotingConfiguration.RegisterWellKnownClientType(clientType);
}
_wellKnownTypes[type] = clientType;
}
public static bool Connect()
{
// Init the Advertisement Service, and Locate any listening services out there...
m_AdvertiseServer.InitClient();
if(m_AdvertiseServer.LocateServices(iTimeout)) {
if(!Connected) {
bConnected = true;
}
} else {
bConnected = false;
}
return Connected;
}
public static void Disconnect()
{
if(_wellKnownTypes != null) {
_wellKnownTypes.Clear();
}
_wellKnownTypes = null;
if(_serverChannel != null) {
if(Environment.UserInteractive) {
// LEF: Don't unregister the channel, because we are running from the service, and we don't want to unregister the channel...
ChannelServices.UnregisterChannel(_serverChannel);
// LEF: If we are coming from the SERVICE, we do *NOT* want to unregister the channel, since it is already registered!
_serverChannel = null;
}
}
bConnected = false;
}
}
So, THAT is meat of my remoting code, and allowed me to write a client that didn't have to be aware of where the services was installed, or how many services were running on the network. This allowed me to communicate with it over the network, or on the local machine. And it wasn't a problem to have two or more people running the app, however, yours might. Now, I have some complicated callback code in mine, where I register events to go across the remoting channel, so I have to have code that checks to see if the client is even still connected before I send the notification to the client that something happened. Plus, if you are running for more than one user, you might not want to use Singleton objects. It was fine for me, because the server OWNS the objects, and they are whatever the server SAYS they are. So, my STATS object, for example, is a Singleton. No reason to create an instance of it for EVERY connection, when everyone is going to see the same data, right?
I can provide more chunks of code if necessary. This is, of course, one TINY bit of the overall picture of what makes this work... Not to mention the subscription providers, and all that.
For the sake of completeness, I'm including the code chunk to keep your service connected for the life of the process.
public override object InitializeLifetimeService()
{
ILease lease = (ILease)base.InitializeLifetimeService();
if(lease.CurrentState == LeaseState.Initial) {
lease.InitialLeaseTime = TimeSpan.FromHours(24);
lease.SponsorshipTimeout = TimeSpan.FromSeconds(30);
lease.RenewOnCallTime = TimeSpan.FromHours(1);
}
return lease;
}
#region ISponsor Members
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure)]
public TimeSpan Renewal(ILease lease)
{
return TimeSpan.FromHours(12);
}
#endregion
If you include the ISponsor interface as part of your server object, you can implement the above code.
Hope SOME of this is useful.
When you register your service, you can tell it to allow interactions with the desktop. You can read this oldie link http://www.codeproject.com/KB/install/cswindowsservicedesktop.aspx
Also, don't forget that you can have multiple users logged in at the same time.
Apparently on Windows Vista and newer interacting with the desktop has been made more difficult. Read this for a potential solution: http://www.codeproject.com/KB/cs/ServiceDesktopInteraction.aspx
Firstly I would like to apologize for my English language. I try to create a Windows Service which run program for BuckUp data when the computer is shutting down.
Problem is that the operating system during shutdown to kill my Windows Service before BackUp data is executed by to the end of. I changed the registry value HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\WaitToKillServiceTimeout to 3600000 but it didn't help, my Windows Service is killed before it is executed. Maybe someone knows how to make the operating system does't kill the Windows Service as quickly to BackUp data could be made. Please help me, I'm waiting for your response. Below I include my code Windows Service:
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.IO;
namespace backUp_ser
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
this.CanShutdown = true;
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
protected override void OnShutdown()
{
ProcessStartInfo stratInfo = new ProcessStartInfo();
stratInfo.WindowStyle = ProcessWindowStyle.Hidden;
stratInfo.FileName = "C:\\Program Files\\Cobian Backup 10\\Cobian.exe";
stratInfo.Arguments = "list:C:\\Program Files\\Cobian Backup 10\\DB\\MainList.lst -bu -nogui -autoclose";
Process process = Process.Start(stratInfo);
process.WaitForExit(360000);
}
}
}
Apart from your query, I want to remind you that the services are running in a separate logon session and the services won't interact with the logged in desktop session (mostly).
So, you need to intercept the shutdown event in your service code. Then, you need to hold the shutdown event till you complete your backup process. You can hook those Windows events through message pumps/queues. You need to intercept the WM_ENDSESSION/ WM_QUERYENDSESSION events.
This query is already discussed in this post. You can refer that.