Tricky Quartz.NET Scenario - c#

Ok, little bit of background here. I have a large scale web application (MVC3) which does all kinds of unimportant stuff. I need this web application to have the ability to schedule ad-hoc Quartz.NET jobs in an Oracle database. Then, I want the jobs to be executed later on via a windows service. Ideally, I'd like to schedule them to run in even intervals, but with the option to add jobs via the web app.
Basically, the desired architecture is some variation of this:
Web app <--> Quartz.NET <--> Database <--> Quartz.NET <--> Windows Service
What I have coded up so far:
A windows service which (for now) schedules AND runs the Jobs. This obviously isn't going to be the case in the long run, but I'm wondering if I can keep just this and modify it to have it basically represent both "Quartz.NET's" in the diagram above.
The web app (details I guess aren't very important here)
The jobs (which are actually just another windows service)
And a couple important notes:
It HAS to be run from a windows service, and it HAS to be scheduled through the web app (to reduce load on IIS)
The architecture above can be rearranged a little bit, assuming the above bullet still applies.
Now, a few questions:
Is this even possible?
Assuming (1) passes, what do you guys think is the best architecture for this? See first bullet on what I've coded up.
Can somebody maybe give me a few Quartz methods that will help me out with querying the DB for jobs to execute once they're already scheduled?
There will be a bounty on this question in as soon as it is eligible. If the question is answered in a satisfactory way before then, I will still award the bounty to the poster of the answer. So, in any case, if you give a good answer here, you'll get a bounty.

I'll try answering your questions in the order you have them.
Yes, it's possible to do this. It's actually a common way of working with Quartz.Net. In fact, you can also write an ASP.Net MVC application that manages Quartz.Net schedulers.
Architecture. Ideally and at a high level, your MVC application will use the Quartz.Net API to talk to a Quartz.Net server that is installed as a windows service somewhere. Quartz.Net uses remoting to communicate remotely, so any limitations of using remoting apply (like it's not supported in Silverlight, etc). Quartz.Net provides a way to install it as a windows service out of the box, so there really isn't much work to be done here, other than configuring the service itself to use (in your case) an AdoJobStore, and also enabling remoting. There is some care to be taken around how to install the service properly, so if you haven't done that yet, take a look at this post.
Internally, in your MVC application you'll want to get a reference to the scheduler and store it as a singleton. Then in your code you'll schedule jobs and get information about the scheduler through this unique instance. You could use something like this:
public class QuartzScheduler
{
public QuartzScheduler(string server, int port, string scheduler)
{
Address = string.Format("tcp://{0}:{1}/{2}", server, port, scheduler);
_schedulerFactory = new StdSchedulerFactory(getProperties(Address));
try
{
_scheduler = _schedulerFactory.GetScheduler();
}
catch (SchedulerException)
{
MessageBox.Show("Unable to connect to the specified server", "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
public string Address { get; private set; }
private NameValueCollection getProperties(string address)
{
NameValueCollection properties = new NameValueCollection();
properties["quartz.scheduler.instanceName"] = "RemoteClient";
properties["quartz.scheduler.proxy"] = "true";
properties["quartz.threadPool.threadCount"] = "0";
properties["quartz.scheduler.proxy.address"] = address;
return properties;
}
public IScheduler GetScheduler()
{
return _scheduler;
}
}
This code sets up your Quart.Net client. Then to access the remote scheduler, just call
GetScheduler()
Querying
Here is some sample code to get all the jobs from the scheduler:
public DataTable GetJobs()
{
DataTable table = new DataTable();
table.Columns.Add("GroupName");
table.Columns.Add("JobName");
table.Columns.Add("JobDescription");
table.Columns.Add("TriggerName");
table.Columns.Add("TriggerGroupName");
table.Columns.Add("TriggerType");
table.Columns.Add("TriggerState");
table.Columns.Add("NextFireTime");
table.Columns.Add("PreviousFireTime");
var jobGroups = GetScheduler().GetJobGroupNames();
foreach (string group in jobGroups)
{
var groupMatcher = GroupMatcher<JobKey>.GroupContains(group);
var jobKeys = GetScheduler().GetJobKeys(groupMatcher);
foreach (var jobKey in jobKeys)
{
var detail = GetScheduler().GetJobDetail(jobKey);
var triggers = GetScheduler().GetTriggersOfJob(jobKey);
foreach (ITrigger trigger in triggers)
{
DataRow row = table.NewRow();
row["GroupName"] = group;
row["JobName"] = jobKey.Name;
row["JobDescription"] = detail.Description;
row["TriggerName"] = trigger.Key.Name;
row["TriggerGroupName"] = trigger.Key.Group;
row["TriggerType"] = trigger.GetType().Name;
row["TriggerState"] = GetScheduler().GetTriggerState(trigger.Key);
DateTimeOffset? nextFireTime = trigger.GetNextFireTimeUtc();
if (nextFireTime.HasValue)
{
row["NextFireTime"] = TimeZone.CurrentTimeZone.ToLocalTime(nextFireTime.Value.DateTime);
}
DateTimeOffset? previousFireTime = trigger.GetPreviousFireTimeUtc();
if (previousFireTime.HasValue)
{
row["PreviousFireTime"] = TimeZone.CurrentTimeZone.ToLocalTime(previousFireTime.Value.DateTime);
}
table.Rows.Add(row);
}
}
}
return table;
}
You can view this code on Github

Related

Firebase Xamarin.Forms - too many concurrent connections

currently I'm developing an App with a small team using Xamarin.Forms. We need to communicate with a database to get some locations , order details and so on. We are using Google's Firebase (Realtime DB) for this purpose. Everything is working fine when we are writing and reading data. However in the Firebase Console, in the usage tab, there are over 50 concurrent connections. This is weird since we are currently developing and didn't release any version of our app. There should be at most 5 concurrent connections (we are a team of 5).
We are using the NuGet-Package FirebaseDatabase.net (4.0.4) https://www.nuget.org/packages/FirebaseDatabase.net/ to read and write to the database.
Multiple Listeners are used to be able to react to changes in the database (so far it seems that each listener is taking up one connection which doesn't seem to be correct).
The code below shows the initialization of the FirebaseClient which is called once in the constructor.
private FirebaseClient InitDbClient()
{
var dbClient = new FirebaseClient(Constants.Values.FIREBASE_DATABASE_URL, new FirebaseOptions()
{
AuthTokenAsyncFactory = () => Task.FromResult(_authToken)
});
return dbClient;
}
Each listener is implemented in a similar way to the following code:
public IDisposable SubscribeToChatMessages(string orderID)
{
var observer = _dbClient.Child($"orders/{orderID}/Chat/Messages").AsObservable<JObject>();
var subscribe = observer.Subscribe(t =>
{
if (t.EventType == Firebase.Database.Streaming.FirebaseEventType.Delete)
{
return;
}
ChatMessage msg;
try
{
msg = JsonConvert.DeserializeObject<ChatMessage>(t.Object.ToString());
}catch(Exception e)
{
Debug.WriteLine(e.Message);
msg = new ChatMessage() { C = null, T = new DateTime(), U = null };
}
//...do something with the chat message
});
return subscribe;
}
Since I'm not sure what the problem is I just put some of our code in here. It would be awesome if anyone has a solution for this problem or has any idea what we might try.
I found the answer myself. As I already suspected each listener uses one connection. The reason for that is simple: the package is built on top of the rest api of firebase. In some other question it was mentioned that each listener is basically a streaming web socket (or something like that). Each of these consume one of the concurrent connections.
As a workaround (to reduce the amount of concurrent connections) I replaced all listeners that are not actually necessary with a combination of a timer and a simple db request. To be able to query for new data I added timestamps to the data itself. This allows me to use a db query similar to
dbClient.Child($"orders/{orderID}/Chat/Messages").orderBy("Time").startAt(lastRead).OnceAsync<>()...

How to reset perfmon counter added using System.Diagnostics.PerformanceCounter

I inherited a project that uses custom performance counters to monitor number of successful/failed requests and their processing time for an IIS deployed web service. Code I used to log counters is like below:
using (var performanceCounter = new PerformanceCounter() { CategoryName = MyConstants.Constants.PerformanceCountersRootName, CounterName = counterName, MachineName = "." })
{
performanceCounter.ReadOnly = false;
if (operationCounter != OperationCounter.ProcessingTime)
{
performanceCounter.Increment();
}
else
{
performanceCounter.RawValue = value;
}
}
Now my question is how could I reset counters after a fixed period of time, say once every week. Here is a similar question, but my problem is I don't want to reset counters using code.
I need to do so using some scheduling mechanism, may be some sort of powershell script or windows schedule or something of this sort.
I have checked this answer and I don't feel this to be approach for me. Please suggest.
Thank You
Ravi

How can I send a "Message" to another process in .Net, without using SendMessage?

I have multiple applications running on a single machine. One is doing work and writing logs to disk about what it has done (I'll call this WorkerApp) and another that summarizes support information about the status of WorkerApp along with some more details (I'll call this Dashboard).
From the Dashboard i want to instruct the WorkerApp to take an action (say, "Ping remote services") and I would like the WorkerApp to send the Dashboard the "pong" response when it gets it.
I have seen examples of using the SendMessage, but this seems to be pretty archaic (isn't there anything more standard now in 2016 for between process communications?).
I have very little experience with Akka.Net but the Remoting feature of it seem like a good approach, although setting this up seems a little overkill for what I would like to do.
What is the easiest way currently to go about communicating between two processes in .Net? And is there some examples of this, working on a localmachine?
I put together an Akka.Net example for this. This is what it looks like.
DashBoard (sends messages)
using System;
using Akka.Actor;
using Akka.Configuration;
namespace DashBoard
{
class Program
{
static void Main(string[] args)
{
var config = ConfigurationFactory.ParseString(#"
akka {
actor {
provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
}
remote {
helios.tcp {
transport-class = ""Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote""
applied-adapters = []
transport-protocol = tcp
port = 0
hostname = localhost
}
}
}
");
using (var system = ActorSystem.Create("Dashboard", config))
{
var server = system.ActorSelection("akka.tcp://WorkerApp#localhost:8081/user/WorkerAppActor");
while (true)
{
var input = Console.ReadLine();
server.Tell(input);
}
}
}
}
}
WorkerApp (receives messages)
using System;
using Akka.Actor;
using Akka.Configuration;
namespace WorkerApp
{
class Program
{
static void Main(string[] args)
{
var config = ConfigurationFactory.ParseString(#"
akka {
actor {
provider = ""Akka.Remote.RemoteActorRefProvider, Akka.Remote""
}
remote {
helios.tcp {
transport-class = ""Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote""
applied-adapters = []
transport-protocol = tcp
port = 8081
hostname = localhost
}
}
}
");
using (var system = ActorSystem.Create("WorkerApp", config))
{
system.ActorOf<WorkerAppActor>("WorkerAppActor");
Console.ReadLine();
}
}
}
class WorkerAppActor : TypedActor, IHandle<string>
{
public void Handle(string message)
{
Console.WriteLine($"{DateTime.Now}: {message}");
}
}
}
Have a look at .Net remoting. It's a bit more modern than SendMessage, but not a whole lot. It's pretty easy to use though. I think the official way to do this these days is probably using WCF, but I'm pretty sure it's just the same under the hood.
.Net remoting supports various channels (Http, TCP), but in your case I'd suggest IPC remoting. It sits on top of named pipes.
Probably easiest if you google (.Net remoting), but the general idea is to define a class in your "server" app derived from MarshalByRefObject. Once you've done that, register it with the remoting infrastructure using RemotingConfiguration.RegisterWellKnownServiceType.
Your client app can then create an instance of the class using Activator.CreateObject and then you're good to go.
One thing to be aware of: it looks like you'll want a callback mechanism - so your Dashboard isn't blocked waiting for your WorkerApp. This is supported in .Net remoting, but you'll need to create two channels - one for the outgoing calls (from Dashboard to WorkerApp) and then another for the incoming callbacks.
Another suggestion: your worker class (the one derived from MarshalByRefObject) will be easier to deal with if exposes an interface as well. Put that interface in a DLL available to both apps and life will be easier.
One suggestion may be to use a database or other persistent storage to create a type of 'Queue' which the dashboard can insert tasks into. This could also store statuses about the tasks which can be updated by the worker process. Although this could be considered 'overkill' it brings a vartiety of benefits such as auditing, historical reporting and server crash/power-off redundancy. It would also probably make scaling up the application in the future easier.

Send notification on specific date and time

In our App, We are storing questions with Question's startdate, enddate and resultdate. We need to send notification to app (iPhone and Andorid) once startdate of question is arrives.
Can anybody let me know how can we achieve this?
We don't want to use pull method. like in particular time interval it will check for question startdate and send notification.
I have a URL to send Notification for question. I need to call this URL when question's startdate is arrived.
Thanks.
Take a look at Quartz :
Quartz.NET is a full-featured, open source job scheduling system that can be used from smallest apps to large scale enterprise systems
Quartz Enterprise Scheduler .NET
You can create a new Quarts Job, lets call it QuestionSenderJob. Then your application can schedule a task in Quartz scheduler, jobs can have many instances of same Job with custom data - in your case QuestionId.
Additionally it supports storing Job scheduling in your SQL database (there are DDL Scripts included) so you can create some relations if you need for UI for example.
You can find table-creation SQL scripts in the "database/dbtables" directory of the Quartz.NET distribution
Lesson 9: JobStores
This way you leave firing in right moment to Quartz engine.
When you will go through Quartz .NET basics, see this code snippet I made a for your case to schedule job. Perhaps some modifications will be necessary thought.
IDictionary<string, object> jobData = new Dictionary<string, object> { { "QuestionId", questionId } };
var questionDate = new DateTime(2016, 09, 01);
var questionTriggerName = string.Format("Question{0}_Trigger", questionId);
var questionTrigger = TriggerBuilder.Create()
.WithIdentity(questionTriggerName, "QuestionSendGroup")
.StartAt(questionDate)
.UsingJobData(new Quartz.JobDataMap(jobData))
.Build();
scheduler
.ScheduleJob(questionSenderJob, questionTrigger);
Then in Job you will get your questionId through JobExecutionContext.
public class QuestionSenderJob: IJob
{
public void Execute(JobExecutionContext context)
{
JobDataMap dataMap = context.JobDetail.JobDataMap;
// Extract question Id and send message
}
}
What about using the Task Scheduler Managed Wrapper?
You do not want to use pooling, but if you write your own class that will encapsulate Timer (e.g. System.Thread.Timer) and check for the time each second, that will not take much resources. Depending on how exact you need it, you could check also less often, e.g. each minute. Maybe you should reconsider it.
If you use any third party service to manage your push notification such as Azure Notification Hub, Parse.com, ... they offer an integrated way to schedule push notifications. Either by passing in a send date or let them run a job periodically. I'm a user of the Azure service and it works very well.
The best implementation i can advice right now is for you to send the notification from a server.
All you just need is a good scheduler that can dispatch operation.
For me, my server is powered by Javascript (NodeJS) so i use "node-schedule". All i just do is
var schedule = require('node-schedule');
//Reporting rule at minute 1 every hour
var rule = new schedule.RecurrenceRule();
rule.minute = 1;
schedule.scheduleJob(rule, function () {
console.log(new Date().toTimeString() + ' Testing Scheduler! Executing Every other minute');
//sendPush()
});

Starting application from service running as SYSTEM that can interact with the user

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

Categories

Resources