Scheduled Task – Status, Start, Stop of Windows Service

So yesterday I created a task scheduler, today I wanted to be able to figure out the status of service and be able to start/stop it. I thought this would be a great time to just implement a new task in my project for task schedule and be on my way.

I found the code for accessing services using System.ServiceProcess quite easily and in my test project it worked great. Infact too great, I was even concerned at how easy it was to start/stop a service from visual studio further reflection on this I assumed since I was debugging a test project it was most likely running under my admin rights (proved true.) So I dropped it into actual ASP.NET and of course was issued security exceptions.

This led me down needing to figure out how to assume the identity of a user that can start/stop the service and then use their credientials to handle it. With a little bit of shameless stealing from MSDN and code clean up I was able to create my new task.

#region Using Statements

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Security.Principal;
using System.ServiceProcess;

#endregion

namespace Tasks
{
    [assembly: SecurityPermission(SecurityAction.RequestMinimum, UnmanagedCode = true)]
    [assembly: PermissionSet(SecurityAction.RequestMinimum, Name = "FullTrust")]
    public class ServiceUpTask : Task
    {
        public string ServiceName { get; set; }

        #region Overrides of Task

        public override void Run()
        {
            if (!string.IsNullOrEmpty(ServiceName))
            {
                WindowsImpersonationContext impersonationContext = null;

                try
                {
                    impersonationContext = AssumeIdentity("userName", "someDomain", "password");

                    using (var service = new ServiceController(ServiceName))
                    {
                        Debug.Print(string.Format("Service {0}, Status {1}", service.ServiceName, service.Status));

                        if (service.CanStop)
                        {
                            service.Stop();
                        }
                    }

                    using (var service = new ServiceController(ServiceName))
                    {
                        if (service.Status == ServiceControllerStatus.Stopped)
                        {
                            service.Start();
                        }
                    }
                }
                finally
                {
                    //Make sure we release the identity in case of an exception no matter what.
                    StopImpersonating(impersonationContext);
                }
            }
        }

        ///<summary>
        ///Assumes the identity. Steal shamelessly from MSDN.
        ///</summary>
        ///<returns></returns>
        private static WindowsImpersonationContext AssumeIdentity(string userName, string domain, string password)
        {
            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int dwLogonProvider = LOGON32_PROVIDER_DEFAULT;

            //This parameter causes LogonUser to create a primary token.
            const int LOGON32_LOGON_INTERACTIVE = 2;
            const int dwLogonType = LOGON32_LOGON_INTERACTIVE;

            var phToken = new IntPtr();
            if (LogonUser(userName, domain, password, dwLogonType, dwLogonProvider, ref phToken))
            {
                if (phToken != IntPtr.Zero)
                {
                    var windowsIdentity = new WindowsIdentity(phToken);
                    var impersonationContext = windowsIdentity.Impersonate();
                    CloseHandle(phToken);
                    return impersonationContext;
                }
            }

            return null;
        }

        /// <summary>
        /// Stops the WindowsIdentity impersonating.
        /// </summary>
        /// <param name="impersonationContext">The impersonation context.</param>
        private static void StopImpersonating(WindowsImpersonationContext impersonationContext)
        {
            //Stop impersonating the user.
            if (impersonationContext != null) impersonationContext.Undo();

            //Check the identity name.
            //Name of the identity after performing an Undo on the impersonation:
            //Uncomment for debugging if to check identity if needed
            //var identity = WindowsIdentity.GetCurrent();
        }

        #endregion

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
                                            int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public static extern bool CloseHandle(IntPtr handle);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool DuplicateToken(IntPtr ExistingTokenHandle,
                                                 int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
    }
}

These changes are all checked into my SVN for access see the Assembla page or the post below.

BloggingContext.ApplicationInstance.CompleteRequest();

Advertisements

Assembla – Free Online SVN Source Control Hosting

So I signed up for an account over at Assembla and must say I’m pretty impressed. It’s a website that offers free hosting of user managed source control using SVN. The free accounts can store up to 250MB of source (that’s alot) and open source/volunteer projects can be eligible receive their commercial accounts for free.

Aside from hosting source code it allows many full featured areas of project management from bug and ticket tracking, contionous integration connectivity, project wiki, file hosting and so on. This website is quite amazingly actually for how much it offers.

I will be hosting the source I include with my blog (starting with TaskScheduler) on Assembla. I have set up my space with public view access so you should be able to grab my source with no problems. Should anyone wish to contribute back to the source contact me and I will gladly add you to edit access. I will be adding a page for Assembla along with all my links to it.

http://www.assembla.com/wiki/show/MarisicDotNet

BloggingContext.ApplicationInstance.CompleteRequest();

Update: Scheduled Tasks in ASP.NET!

So as I promised I did write my sample project for using this. I’ll include the full files later as the majority of it is just wrappers around classes the files that really do all the magic is the code behind for the default.aspx and the TaskManager.cs file.

public partial class _Default : Page
{
private readonly TaskManager taskManager = new TaskManager();

protected void Page_Load(object sender, EventArgs e)
{
var printTask = taskManager.TaskList.Find(t =&gt; t.Id.GetValueOrDefault() == 1);

lblStatus.Text = printTask != null ? (printTask.Active ? "Active" : "InActive") : "Task not found";
}

protected void btnStartTasks_Click(object sender, EventArgs e)
{
taskManager.TaskList.Add(new PrintTask {Active = true, Id = 1, Name = "PrintTask1"});
taskManager.TaskList.Add(new PrintTaskOther {Active = true, Id = 2, Name = "PrintTaskOther2"});
taskManager.TaskList.Add(new PrintTask {Active = true, Id = 3, Name = "PrintTask3"});
taskManager.TaskList.Add(new PrintTask {Active = true, Id = 4, Name = "PrintTask4"});
taskManager.TaskList.Add(new PrintTaskOther {Active = true, Id = 5, Name = "PrintTaskOther5"});
}
}

The real magic that is occuring in here basically your just loading up the task objects into a List object however a few operations are overloaded that change the normal functionality of a List

public class TaskList : IList
{
private readonly List _tasks = new List();
private readonly Cache _cache = HttpRuntime.Cache;
//How long for jobs to wait before waking.
private const double sleepLength = 1;

public List Tasks
{
get
{
if (Count == 0)
{
var enumerator = _cache.GetEnumerator();
do
{
if (enumerator.Current is Task)
_tasks.Add(enumerator.Current as Task);
} while (enumerator.MoveNext());
}
return _tasks;
}
}
public void Add(Task item)
{
var task = _tasks.Find(t =&gt; t.Name == item.Name);
if (task == null)
{
_tasks.Add(item);
CacheAdd(item);
}
else
{
task.Id = item.Id;
task.Active = item.Active;
task.LastRan = item.LastRan;

CacheUpdate(item);
}
}

private void CacheUpdate(Task item)
{
_cache.Remove(item.Name);

CacheAdd(item);
}

private void CacheAdd(Task item)
{
if (_cache[item.Name] == null)
{
_cache.Add(item.Name, item, null, DateTime.Now.AddMinutes(sleepLength),
Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable,
CacheItemRemovedCallback);
}
}

public CacheItemRemovedCallback CacheItemRemovedCallback { get; set; }

Basically all it does is overrides saving the list solely to the server’s ram and writes it out to the Cache also when objects are added. It also has the Get method overrided so it will read the existing cache objects out by default for the list.

What pulls it all together to make everything work is the

public CacheItemRemovedCallback CacheItemRemovedCallback { get; set; }

This is the method that will get called when a task is removed from the cache’s dictionary. This was set in

public CacheItemRemovedCallback CacheItemRemovedCallback { get; set; }

So whenever a cache item is removed from the stack the event fires off to

public static void TaskExecuting(string key, object value, CacheItemRemovedReason reason)

Which is where we do the real execution of the job. At the end of the job we stick the item back in the cache so it will continue to loop. This is also the spot where you would put in logic to stop the service after so many iterations or it reaches some kind of predefined condition.

Updated: I have moved my source over to Assembla, you can download it directly or setup an SVN connection to view my source. Download Task Scheduler.

BloggingContext.ApplicationInstance.CompleteRequest();

kick it on DotNetKicks.com

Scheduled Tasks in ASP.NET!

I never would have thought there was a way to get around from needing a scheduled task to execute without implementing a windows service or a scheduled task run of a compiled executable.

Well I thought wrong.

Lately I’ve been doing alot of reading to keep up with everything .Net related and have been finding some incredible information, this has got to be one of the most profound things I have found in my ASP.NET experience.

The whole premise of a task scheduler is it needs to be atomic that every predefined interval the scheduler needs to wake up and figure out if it needs to do anything and then sleep until the next interval when it’s finished. Emulating this in ASP.NET would be impossible as far as I ever thought due to the fact that so much of all code execution is all instance based and the server itself will even sleep due to inactivity. Neither of those 2 core functionalities are desirable for a scheduler as it defeats the whole purpose of it.

This is where caching comes in. Yes, you read that right, caching. Not the standard practice of caching data for performance we’re all used to do doing but using the cache to store a cache object with a callback function and near future expiration to create our atomized tick.

Bill Beckelmen on his blog refreshes information that was spawned the whole way back in 2005 on CodeProject and improved from Microsoft’s MVP Omar Zabir’s original idea. Bill wrote Simulate a Windows Service Using ASP.NET to Run Scheduled Jobs

For the basic idea take a look over at Bill’s blog or check back as I might implement a draft of this to test it myself for a project that could potentially use it as a web driven service manager.

BloggingContext.ApplicationInstance.CompleteRequest();

Sql Server 2008 File Streaming

So today I was talking in the office about how much Sql Server has grown especially with the relase of 2008 where it really can stand up against Oracle performance wise and even beat Oracle in performance when dealing with Binary and other BLOB type data structures.

If you’re not aware of this feature in Sql Server 2008 you can chose to stream data to be sent to the file system of the server (or probably any mapped network location) instead of it being stored inside the table structure which massively shrinks the table size which immensely improves performance that the table doesn’t have a few hundred megs or into gigabytes of binary data to sift through while it’s doing row relational operations.

I found an example of this on RDoherty’s blog on MSDN while also browsing his blog his newest post also had top 10 new features / enhancements of Sql Server 2008 for Developers both are a great read.

Getting Traction with SQL Server 2008 Filestream

SQL Server 2008 Top 10 List for Developers

BloggingContext.ApplicationInstance.CompleteRequest();

SvcUtil Service Client Generation Batch Script

WCF services are pretty well just damn amazing in every way the only real area of pain can generating service contracts setting up a batch script will definitely relieve most of those pressures!

This example will have 2 service clients generated the first DataServiceClient is compromised of 2 data contract assemblies when you return objects that are defined in dlls absolutely make sure to include the dll as a datatcontract assembly otherwise you will have tons of headaches later. It also has the generic list class added in for the service to be able to return List<DataServices.DataContracts.SomeObject> make sure not to miss adding any generic classes provided by microsoft from your definitions!

The second client will be for WorkflowService which in this example is a basic service that only accepts basic type input parameters and return values.

@echo off
echo.
echo ==========================================================================
echo   GenerateServiceClients.bat
echo      Runs generation scripts for the Services.Host Project
echo ==========================================================================
echo.

set configuration=Debug
set vsDir=”C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\”
set outputDir=”C:\Projects\My_Project\Service Clients\
set library=C:\Projects\My_Project\Library\
@set PATH=%PATH%;%vsDir%

if /i “%1″==”/c” (
set configuration=%1
SHIFT
)

:run

@echo —————————————-
@echo GenerateServiceClients.bat Started
@echo —————————————-
@echo.

@echo —————————————-
@echo Generating DataServiceClient
@echo —————————————-
@echo.

set dataContractAssembly=”C:\Projects\My_Project\DataServices.Host\bin\DataServices.DataContracts.dll”
set dataContractAssembly1=”C:\Projects\My_Project\DataServices.Host\bin\Common.ExceptionHandling.dll”
set serviceUrl=”http://localhost/DataServices.Host/DataService.svc?wsdl&#8221;
set className=DataServiceClient.cs”
svcutil /noconfig /o:%outputDir%%className% /r:%dataContractAssembly% /r:%dataContractAssembly1% /ct:System.Collections.Generic.List`1 %serviceUrl%
@if errorlevel 1 goto :error

@echo —————————————-
@echo Generating WorkflowServiceClient
@echo —————————————-
@echo.

set serviceUrl=”http://localhost/WorkflowServices.Host/WorkflowService.svc?wsdl&#8221;
set className=WorkflowServiceClient.cs”
svcutil /noconfig /o:%outputDir%%className% %serviceUrl%
@if errorlevel 1 goto :error

@echo —————————————-
@echo GenerateServiceClients.bat Completed
@echo —————————————-
@echo.

@rem  —————————————-
@rem  Restore the command prompt and exit
@rem  —————————————-
@goto :exit

@rem  ——————————————-
@rem  Handle errors
@rem  ——————————————-
:error
@echo An error occured in GenerateServiceClients.bat – %errorLevel%
@exit errorLevel

:exit

@exit
echo on

BloggingContext.ApplicationInstance.CompleteRequest();

Failed to access IIS metabase

So I’m trying to get my home box setup to run IIS locally and first I must say I am pretty infuriated that there apparently is absolutely no way on this planet to install IIS6 or IIS7 on Windows Xp Pro 32bit, I spent atleast an hour searching and found absolutely nothing in reference to this so I decide I’ll get over with it and install IIS 5.1 the garbage that it is onto my WinXp.

After jumping through all the hoops I run into an error where port 80 is already in use (go Skype ::face palm:: so I resolved that in their advanced options) then I’m presented with this fantastic error message

Failed to access IIS metabase.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Web.Hosting.HostingEnvironmentException: Failed to access IIS metabase.

The process account used to run ASP.NET must have read access to the IIS metabase (e.g. IIS://servername/W3SVC). For information on modifying metabase permissions, please see http://support.microsoft.com/?kbid=267904.

So I say wtf to myself and figure the KB article will solve everything, that article provided aboslutely NO information useful to solving this quickly so I turn to google and find Steve Lippert’s web plog where he posted the solution that required just the single command line call of

%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis.exe -i

You can check out his original article at Failed to access IIS metabase.

BloggingContext.ApplicationInstance.CompleteRequest();

DebuggerDisplayAttribute Makes Debugging Clearer

So today I came across another very useful blog post this one was about the attribute DebuggerDisplayAttribute. It allows you you to change what is output when you look at an object (especially useful in a collection of objects) to see something other than the value of what .ToString() displays which is generally just the name space and class definition which is less than optimal.

Example used in the article

[DebuggerDisplay(“Id = {Id}, Title = {Title}, Rating = {Rating}”)]
public class Movie
{
public int Id { get; set; }
public string Title { get; set; }
public float Rating { get; set; }
}

I think it’s pretty clear of the intent with this snippit but take a look at Greg Beech’s blog over at Tips & Tricks: Use DebuggerDisplayAttribute for easier debugging to see more detailed usage and some action screen shots.

BloggingContext.ApplicationInstance.CompleteRequest();

Short Guid

In my daily perusing of DotNetKicks and related blogs for the desire to continue to expand my knowledge of C# development I came across Short Guid. This is a very simple concept but not one I’d have ever considered myself. In summation the idea of Short Guid is

Is take a regular guid like: c9a646d3-9c61-4cb7-bfcd-ee2522c8f633

And shortens it to a smaller string like this: 00amyWGct0y_ze4lIsj2Mw

This was basically all handled by just converting the guid to a btye array then base64 encoding of it handling 2 special cases of specific characters to be encoded easily for URLs. See the full article on Dave Transom’s blog at:

ShortGuid – A shorter and url friendly GUID class in C#

Which was based off the orginal idea on Mads Kristensenblog at:

A shorter and URL friendly GUID

BloggingContext.ApplicationInstance.CompleteRequest();

Working with SSRS – Sql Server Reporting Services

So I started my new job doing .NET Consulting work using the 3.5 framework last week unfortunately the start of my job has mostly been limited to updating stored procedures in sql server (thank god I’m done with you oracle) and the reports in sql server reporting services (SSRS) that use them. SSRS sure seems to have alot of quirks and some unusual functionality.

Through out my upcoming days I’ll be posting any minor tips or ins and out that I stumble upon to have them tied together in 1 post both for my reference and hopefully the benefit of others.

Tip #1 –  Inserting text or a Line Break into a text box on the report with data fields

=Fields!LastName.Value & “, ” & Fields!FirstName.Value & vbCrLf & Fields!MiddleName.Value

Tip #2 – Convert a DateTime field to only display the Date portion

=CDate(Fields!BirthDate.Value).ToShortDateString()

Tip #3 – Working with bit flags and IIf statement proper comparison, note case after the equality

iif(Parameters!IsBitFlagSetForSomething.Value = True, “Bit Flag is set”, “Bit Flag is not set”)

Tip #4 Alternating Row background colors, set the background property to:

=iif(RowNumber(Nothing) Mod 2, “LightGrey”,”white”)

More to come hopefully

BloggingContext.ApplicationInstance.CompleteRequest();