Link to home
Start Free TrialLog in
Avatar of kaptaindotnet
kaptaindotnet

asked on

Convert C# console app into a Windows Service

Hi all,
I have a multithreaded C# Console app that needs to run every minute or every 30 seconds 24/7/365. There is nothing in the code related to a timer or Windows service (although that can be modified as I have the source) I can think of some ways of accomplishing that:
1. Leave console app code as it is and separately write a Windows Service that "calls" the app after every interval
2. Somehow modify the source code of the console app so it becomes a service with all the funcionality of the original app

Unfortunately, I have tried and don't know enough about Windows Service coding to make this work. Right now I have a  Windows Service separately that just writes text to a file on startup and and on shutdown. I don't know how I can merge the Console App into this service and also add a timer into the final product.
HELP!!!

Avatar of bigjim2000
bigjim2000

Keep in mind that services run even when there is no user logged into the computer.  Services cannot do things like display consoles or forms (directly).  You need to create a form, and have the form communicate with the service.
For what you want to do, perhaps look at Scheduled Tasks in the control panels.  You can schedule something to run at certain intervals, times of days, etc.

-Eric
Avatar of kaptaindotnet

ASKER

Thanks for prompt response,

1. Thats what I want, the service should keep running even when no one is logged on, although there might be a problem here, because the console app accesses a shared drive on the network which is mapped automatically when the user is logged on. I don't know what will happen when the user is not logged on, can the service access access the shared drive directly i.e. \\OtherComputer\Share ?

2. I do not need any user interaction with the service, so display/forms are not needed

3. If I want to run it every 30 seconds, don't you think making it  a service will make more sense than using scheduled tasks?

Actually, if you wait until someone logs in, you can call the APIs for creating windows and catching WM_ messages.  What you won't get from in a service, even if a user has logged on, is shell messages, e.g., system tray event notifications.

That aside,  I'd just modify the code and make the meaty part of it the target of a timer:

* set timer interval
enable timer
...


TimerTriggered:
disable timer
do what you have to
re-enable timer
as for #1, yes.

#2:  Ok, then just make a new service project in Visual studio, and you can just copy most of your code... just comment out the stuff for display.

#3:  That just depends.  If you use your program as-is, it might get kinda annoying to see the console pop up every 30 seconds, but technically it will work fine.  If you make a service, then you can just hide it in the background.

-Eric
Getting to a network drive from a service still requires credentials for the remote server.  A common way of doing that is haed-coding encrypted credentials in the source and decrypting them at run-time.  Even with a user logged on, you wouldn't be able to get their password unless you write a GINA replacement.  I doubt your security folks would be happy with that.
cookre:
So you're saying, I don't even make it a service? Just add a timer to the console app such that the "meaty" part is excuted every 30 seconds but the app itself is started only once?

Eric:
The app will run on a dedicated server, so the annoying pop-up every 30 seconds is not an issue, although it would be nice if it can be avoided.

Ok, the question again, then how do I make it a service so it does what I listed in the original post. The issue is that it has to run 24/7/365 ..and excute the "meaty" part every 30 seconds.
Thanks for the continued help
cookre, if he doesen't use a service, then it won't run all the time.  Services run so long as it is started, and the computer is on.

As for the network security, just have the service run in the contect of a user that has permissions to access the network resource.

-Eric
Here is a short turorial that is pretty decent that should give you what you need to know to make a simple service:
http://www.fawcette.com/archives/premier/mgznarch/vbpj/2001/08aug01/ce0108/ce0108-1.asp

Basically, there is a start method, a stop method, and a pause method (which correspond to starting, stopping, and pausing the service...)  You make a thread in the "global" scope of your service class, then start it.  This thread will loop, and execute the function you want every 30 seconds, then sleep the rest of the time.

Hope this helps.

-Eric
So, the questions is HOW will that be done. I am not asking anyone to write the code for me, but I need some pointers with regards to how this can be done. Maybe the structures of what I have will help:

1. The Console app  (basic structure)

main()
{
// Some declarations
// initial setup
ConnectToDatabase();
//the following is what should be done every 30 seconds, aka the meaty part [I like this term Cookre used:-)]
QueryDatabase()
if( resultsReturned > 0 )
// Create new thread for every result returned.
}

When this is run as console app, it runs as long as it has records to process and then exits. And also queries the DB only once. Of course if there are no records to process, it will exit rightaway. We don't want that, it should query DB again after 30 secs for records...and so on forever!

2. Service  (completely separate from the above)

Service1()
Main()
InitializeComponent()
Dispose()
OnStart()
{ //Write to the log file "Service1 started"}
OnStop()
{ //Write to the log file "Service Stopped"}

And then I have the installer class, which does what it's supposed to do. Now How would make the above service call the console app as an exe (case 1 in Original post) , where would I add the code to do that every 30 secs?
OR
How/where would I copy/paste code form the console app into this service code , so the app becomes a service (case 2 from original post)
Of course I will also need a timer somewhere...
>>>As for the network security, just have the service run in the contect of a user that has permissions to access the network resource.
That's what I meant by embedding credentials in the code.

>>>So you're saying, I don't even make it a service?
There's no real need.  Run the program from AllUsers StartUp and just never exit the program.

If you really want a service, you'll be able to use just about all of your code - as long as it doesn't do any console writes or pop-ups:
1) create a new service project.
2) in the On-Start(), start up the timer that triggers  the code that does all the work.  In the routine that does the bulk of the work, make sure to disable the timer at the beginning and re-enable it at the end.  Not doing so could create some interesting fireworks if what your doing in one of the pass takes longer than the timer is set for, to wit, your processing routine could have another instance triggered while it's still running.

If you have to get to a network drive, you'll have to use somebody's credentials to access.  Running under system context only gets you access to the local file system and the rights to grant yourself other rights as may be needed for some local processing such as reboot,  Registry enumeration, and process enumeration.

Speaking of the Registry, be aware that if no user is logged on, Registry pseudo-key HKCU won't point to anything.  If you need something from what you would normally get from HKCU, you'd have to decide which user's settings to use, then figure out which HKU subtree corresponds to that user.

---

When writing services, I usually prefer to make them self-installing - that makes it nicely self-contained.


SOLUTION
Avatar of bigjim2000
bigjim2000

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
woah! you guys are on the roll... I need some time to try these solutions out to see which one works for me.... will post back ASAP
Thanks gentlemen!
Eric,
What would the main() have, now that I have changed it to mainthreadpool(). VS is complaining about not finding an entry point.
thanks
There would be no "main()" now... only the start method.  That is the main entrypoint of a service.  

You need to create a service installer project in order for your service to run.  You can't just "play" like you can with windows or console applications.  You can also build your project, and install it via the command line.  One you build it, there will be an exe file.  Just load up the command prompt and type:

installutil app.exe  [ where app is your specific application name ]

to uninstall the service after you are done debugging, type:
installutil /u app.exe

Note, that when you deploy your project to other people, you will need to create the installer package.

Also note, what cookre has been saying WILL work... I just think that it is a bit much for what you need to do.  For a simple service such as this, using the tools built into Visual Studio I think will give you a much easier time.

-Eric
>>>There would be no "main()" now... only the start method.
???
Every service I've ever written has a main.  Shoot, even the VS.NET c# IDE generates a static void Main(), since a service starts out as a regulat exe.  If one wants to self install, that can be  done in Main().  In any case, the service process is started from Main() by virtue of running the ServiceBase.  In c# that triggers a rountine that usually just calls Initialize() which then triggers the OnStart.

Note that from the time ServiceBase is started, you've got 30 seconds to post a SERVICE_RUNNING message.  An interesting 'feature' of a c# service is that once control reaches OnStart, .NET sends PENDING messages with hints for you, then sends the RUNNING message as it exits OnStart.  The upshot of this is that if you have more than 30 seconds worth of initializing to do, put it in OnStart and not Initialize().

<SoapBoxMode>
I'm still of two minds about .NET's implementation of services.  On the one hand, it's very easy to create them.  On the other hand, this very simplicity has led to many programs being created as services with little understanding of the intent of services or of the environment under which they run, often engendering no small amount of confusion and hair pulling when trying to get them to run.  
I don't know if this is good or bad.
</SoapBoxMode>
I'm sorry I was unclear.  The Main() method still exists, but you would do all your initialization in the OnStart() method now... at least for the purposes of this type of application.

Normally initialization is done in the Main() method, but you can safely leave it alone for this application.

-Eric
I think at this point I will just conceed to cookre's knowledge of services.  I was trying tor the simplest approach, not the most self-contained or versatile/robust.

-Eric
Simplest is usually best - and most robust, not to mention easier to maintain.

>>>but you can safely leave it alone for this application.
You just triggered something in my little pea-brai - that's probably why .NET sends PENDING messages and hints in OnStart(), in particular since Main() is static.
Why don't you use the windows sheduler service to run your console app every 30 seconds? No coding required, and it will work just af if you wrote your own service (since the sheduler is in fact a service).
Thanks guys for all the comments and suggestions.
AvonWyss: we've discussed that above... and now it's too late to abandon the services idea as most of the work is done....thanks though

All said and done, I have  a service now with a timer that calls the Console app every "interval" minutes. However,  there is one problem. The console app works fine when run separately. But when the service calls it, it just stays there doing nothing. I can see it in Task Manager processes, but it does not do what it's supposed to do. I believe it's a permissions issue, but don't exaclty know how to tackle this. The domain account I am using has administrative privileges on the local machine and has access to the shared drive. Any ideas?
Once we're through this one, this thread will be closed and points allocated.
Thanks again
Maybe you should try to use direct UNC notation instead of a shared drive; shared drives are user-dependent.
Cookre and Eric, with your help, I am able to run the  service now.... Thanks for all the help!