2001, A Space Oddity

I'm sorry, but I just had to link this (requires Shockwave):

It's so awful I laughed the entire time it was playing. Smile I've been considering showing this movie to my kids, but I hazard a guess they'll understand it no better than I did the first few times I saw it. (In fact, I almost hate to admit that I didn't understand it until I saw a 2001 parody backed by 2010...what did apes beating up each other with bones have to do with spaceships to the moon? Doh! So, tell me again, exactly who's evolved?)

Nah, the kids won't get it, and probably wouldn't enjoy it. Not enough weaponry. Best to stick to Transformers. They are part of the iPod generation, after all. Smile


Tags:
Categories: General

1 Comments
Actions: E-mail | Permalink | Comment RSSRSS comment feed

Learning MVC 2, Workflow Foundation 4, and maybe Silverlight 3, Part III

Moving along with my example MVC/workflow application (see Part I and Part II), for this post I thought I'd share some of the architecture and show a first, rough-cut at the workflow.

The solution is broken down as you see here:

 

The Common library is shared by the presentation layer as well as the logic/data layers. From a project perspective, it looks like this:

 

Here you see I have interfaces for data access and services (although there is only one service for this example). I have a "data transfer object," which I'll use to pass employee information between the layers. And I have factories that will create instances of the data access layer (DAL) and services. This allows me to make changes to the DAL or services without breaking existing code/tests, assuming I don't modify existing interface method signatures (I can add methods, just not change existing ones). It also allows me to use a strategy pattern for loading the appropriate assembly.

For example, here is the employee service factory:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Reflection;
using KoozKooz.Common.Interfaces;

namespace KoozKooz.Common.Services
{
    public sealed class EmployeeServiceFactory
    {
        private static IEmployeeService _instance = null;

        static EmployeeServiceFactory()
        {   
            // TODO: create custom section
            // TODO: refactor code to generic loader
            string asm = ConfigurationManager.AppSettings["EmpSvs-Assembly"];
            string cls = ConfigurationManager.AppSettings["EmpSvs-Type"];

            // Dynamically load the assembly and instantiate the type  
            Assembly a = Assembly.Load(asm);
            _instance = a.CreateInstance(cls) as IEmployeeService; 
        }
        
        public static IEmployeeService GetEmployeeService()
        {
            return _instance;
        }
    }
}

The assembly information is read from the configuration file:

<add key="EmpSvs-Assembly" value="KoozKooz.Logic"/>
<add key="EmpSvs-Type" value="KoozKooz.Logic.Services.EmployeeService"/>

A quick unit test would look like this:

///<summary>
/// A test for retrieving the employee service
///</summary>
[TestMethod()]
public void GetEmployeeServiceTest()
{
    IEmployeeService svc = EmployeeServiceFactory.GetEmployeeService();
    Assert.IsTrue(svc != null);
}

The DAL code is very similar.

I've not completed the service layer, but my unit tests for loading the DAL and requesting an employee are passing. I'm now turning to the workflow. WF 4 is a very different animal than WF 3.5, and I think once I understand the changes, I'm going to like them. Two things stand out--the flow of information to and from the workflow when executed within an application seems to have improved, and the persistence model appears greatly enhanced.

With WF 3.5, if you wanted to pass information into the workflow, you could do so when the workflow was initiated (and you had to pass in a Dictionary object). If you wanted to communicate with the hosting application, you had to add a service and something I called a "bridge," which was glorified "glue" code that married the particular activity with events the host application could monitor. With WF 4, the story has improved through the use of InArgument and OutArgument. I still have more to learn, but I'll be digging into this aspect soon for this learning application. As for persistence, WF 4 has the concept of a "bookmark." Instead of adding a Delay activity to kick off workflow persistence, you now add a bookmark to your (native) activity. When the bookmark is acted upon, WF will persist the workflow and wait for some external force to re-activate the workflow. Once it is re-activated, execution resumes in an event handler you provide.

There is a slight down-side with bookmarks at present, however. Bookmarks are available only to NativeActivity-based activities. NativeActivity is a form of CodeActivity, which is how you write custom workflow activities. If using CodeActivity as your activity's base class, you can still use the designer. However, when using NativeActivity, you lose all designer support. Your workflow is then entirely code-based. Ah well. It is a beta after all. Smile

My activity will not be very complex, and I reserve the right to refactor as required. This is my first rough-cut... The activity, which I named SendManagerVacationApprovalRequestActivity, merely sends an e-mail to the employee's manager:

namespace KoozKooz.Logic.Workflow
{
    internal sealed class SendManagerVacationApprovalRequestActivity :
                                                                 NativeActivity
    {
        // Manager's employee DTO
        public InArgument<EmployeeDTO> Manager { get; set; }

        // Employee's employee DTO
        public InArgument<EmployeeDTO> Requestor { get; set; }

        // Days requested...this should aleady be filtered against the
        // employee's available days, but we'd be remiss not to check
        // here too.
        public InArgument<Int32> RequestedDays { get; set; }

        // If your activity returns a value, derive from CodeActivity<TResult>
        // and return the value from the Execute method.
        protected override void Execute(NativeActivityContext context)
        {
            // Pull runtime values
            string fname = this.Requestor.Get(context).FirstName;
            string lname = this.Requestor.Get(context).LastName;
            Int32 requestedDays = this.RequestedDays.Get(context);
            Int32 availableDays = this.Requestor.Get(context).RemainingVacation;
            string callbackUrl = String.Format("{0}&InstanceId={1}",
                          ConfigurationManager.AppSettings"VacationCallback"],
                          context.WorkflowInstanceId.ToString());

            // Format the request message body:
            //
            // 1) Requesting employee first name
            // 2) Requesting employee last name
            // 3) Number of days requested
            // 4) Number of days available
            // 5) Callback URL, with workflow instance ID
            string messageBody =
                 String.Format(Properties.Resources.VacationRequestMessage,
                     fname, lname, requestedDays, availableDays, callbackUrl);

            // Pull manager's e-mail address
            string managerEMail = this.Manager.Get(context).EMail;

            // Mail request notification
            MailHelper mailer = new MailHelper();
            mailer.From =
                  ConfigurationManager.AppSettings["SystemEMailAccount"];
            mailer.To = managerEMail;
            mailer.Subject = Properties.Resources.VacationRequestSubject;
            mailer.SendMail(messageBody, true);

            // Create named bookmark
            context.CreateBookmark("VacationApproval",
                     new BookmarkCallback(this.OnApproval),
                     BookmarkOptions.NonBlocking);
        }

        private void OnApproval(NativeActivityContext context,
                                Bookmark bookmark, object obj)
        {
            // Do nothing...allow completion
        }
    }
}

I may, for example, return the SMTP result code if the SendMail method fails rather than allow it to simply crash (which is what it'll do without any exception handling code...at least that's what would happen using WF 3.5). Something else to learn about WF 4.

In any case, the workflow will create an e-mail message and forward that to the manager. Inside the e-mail message is a hyperlink for a "callback page" I specify (in the configuration file for now, for lack of a better place to store a value I'd like to reconfigure dynamically). Once the message is sent, the workflow instance will suspend. The page the manager accesses will then re-activate the workflow, allowing it to run to completion.

Something else to note is when I created the bookmark, I used the BookmarkOptions.NonBlocking option. This allows the workflow execution engine to run past the bookmark. I'll probably play with this a little once I get the workflow hosted. Is it better to block or not block? I might find that the workflow isn't persisted if I don't block, in which case I'll revise the code. It needs to be persisted, of course, since the manager could take a long, long time to authorize the vacation, making this a long-running workflow.

Now, I realize this workflow isn't very complex, but the point of this exercise is to marry a workflow to ASP.NET MVC. The complexity of the workflow isn't the point, so I'm fine with my custom activity merely sending an e-mail. Yes, using a workflow for this at all is probably overkill. Undecided But, again, the point is to get WF 4 working with ASP.MVC 2... Once you get the plumbing in place, you can create whatever workflows you like.

The next post should be interesting as I'll have the workflow invocation code working (or so I hope...if a lot of time passes between posts, you'll know I either got swamped with work or couldn't figure it out!). I've not seen a lot regarding threading models with WF 4, so I'm curious to dig into how WF 4 handles threading, or if it parallels what we used to have to do with WF 3.5.


Tags: , ,
Categories: .NET General | ASP.NET | Workflow

5 Comments
Actions: E-mail | Permalink | Comment RSSRSS comment feed

When Legos and "The Matrix" Collide

I've mentioned in this blog that "The Matrix" is one of my favorite movies (others being "The Mummy", "Tremors", and "Twister"). I also have a fondness for Legos, having helped my children assemble many, many kits. Heck, I had them when I was their age, only the kits weren't as cool. Smile

Imagine my surprise when I stumbled across this (requires Shockwave):

 

This brilliant piece of stop-action animation was put together by Trevor Boyd and Steve Ilett, who took their own favorite scene from the Matrix and recreated it using Legos, frame by frame. I understand it took them 440 hours. Truly incredible. And I thought I had no spare time. Embarassed


Tags: , ,
Categories: General

4 Comments
Actions: E-mail | Permalink | Comment RSSRSS comment feed

Learning MVC 2, Workflow Foundation 4, and maybe Silverlight 3, Part II

In my previous post, I mentioned designing a small database to support a learning application I'd like to write. I also mentioned I'd discern the differences in the two workflow persistence models I see that are available in Workflow Foundation 4. Let's start with the latter.

It turns out there isn't really much to say. WF 3.5 persisted workflows using a "persistence provider model." WF 4, on the other hand, uses persistence according to an "instance store." The two models are incompatible, and the persistence provider is shipped with WF 4 only for backwards compatibility. All new WF 4 workflows should use the instance store, so mystery solved. We'll use the instance store when we get to that point.

As for the database, my goal isn't to design the be-all, end-all of databases for HR (as you may recall, the task will be to authorize vacation/holiday time off, which is an HR function). I just need some basic information:

  • The empoloyee's name, both requestor and manager
  • E-mail addresses
  • Amount of remaining vacation
  • An employee ID, used to uniquely identify everyone in the database

For my sample application, this data should suffice. There is always more we could add, like amount of vacation authorized by the manager but not yet taken, or the amount of vacation rolled over from last year--that sort of thing. For my needs here, however, I'll keep it simple. I'm much more interested in the workflow for vacation authorization and connecting that to an MVC application.

With that information in mind, I created this database scheme:

It's somewhat normalized, but I tend to design database that way. I say "somewhat" because the RemainingVacation column is stuck in the EmployeeData table, which isn't really where I'd probably put it in a true HR database design, but it's better than adding a third table just for this little sample application. So I'll live with that bit of denormalization. Smile I created some test data, which you see in this script (I'll swap out the e-mail addresses for valid ones later):

INSERT INTO [Koozkooz].[dbo].[Employees]
           ([EmployeeID])
     VALUES
           ('5001')
GO

INSERT INTO [Koozkooz].[dbo].[Employees]
           ([EmployeeID])
     VALUES
           ('10001')
GO

INSERT INTO [Koozkooz].[dbo].[EmployeeData]
           ([EmployeeDataID]
           ,[EmployeeID]
           ,[FName]
           ,[LName]
           ,[EMail]
           ,[RemainingVacation]
           ,[ManagerID])
     VALUES
           ('CEBB9D6F-9B50-4C8E-BAC7-B8E03377E5EB'
           ,'5001'
           ,'Tonya'
           ,'Manager'
           ,'tonya@koozkooz.com'
           ,20
           ,'0')
GO

INSERT INTO [Koozkooz].[dbo].[EmployeeData]
           ([EmployeeDataID]
           ,[EmployeeID]
           ,[FName]
           ,[LName]
           ,[EMail]
           ,[RemainingVacation]
           ,[ManagerID])
     VALUES
           ('A57152B2-D4F3-4A5E-8F74-C734C3B5C9A2'
           ,'10001'
           ,'Aimee'
           ,'Employee'
           ,'aimee@koozkooz.com'
           ,5
           ,'5001')
GO

The image depicting the database is from Visual Studio, and you've probably already noted I'm using LINQ to SQL. I've started framing the application in Visual Studio 2010 (Beta 2). I settled on the name "KoozKooz," if only because a quick Web search didn't turn up a lot of hits on the name:

Something in the beta I believe needs to be addressed is the ability to name things. When creating new projects, for example, you tell Visual Studio 2010 where you want the code located, but it names the project. At least the solution is named according to the location, as you'd expect. Anyway, in my case, I created an MVC 2 Web application, but Visual Studio named it "MvcApplication2" for me. I'll probably rename it to be "KoozKooz.Web," but you get the idea. Another limitation in the beta is deciding where Web apps should be hosted. With Visual Studio 2008, you can choose to use Cassini or IIS (locally or remotely). That functionality is not present in the current beta, but clearly it's needed. I'm sure it'll be in the next beta, or whatever release Microsoft intends.

In the next post, I'll start rigging up the data access and application layers, as well as their unit tests.

(Update: I found the KoozKooz domain was available, so I bought the rights. It's nice to be legal. Smile)


Tags: , ,
Categories: .NET General | ASP.NET | Workflow

2 Comments
Actions: E-mail | Permalink | Comment RSSRSS comment feed

Implementing an Asynchronous HttpModule

When I was researching REST and writing my part of Scott's and my book on REST (Effective REST Services via .NET: For .NET Framework 3.5), on page 234 I mentioned that the authentication/authorization module I'd written should be asynchronous and that someday I'd write an asynchronous version. Thanks to reader Carl asking me to come through on my promise, I've completed the asynchronous version. Actually, I refactored the synchronous version as well so that both implementations can share the code that actually deals with authentication and authorization. If you've read the book and have downloaded the source (which I updated, downloadable from here), you can download the AuthModule.cs files themselves for Chapters 6-8 here.

I noticed a distinct lack of example implementations on the Web while deciding how I wanted to implement my version of the asynch module. Jeff Prosise has the most-referenced version (available here), but this wasn't complete enough for my needs. Jeff's version uses FileStream.BeginWrite to generate the necessary IAsyncResult object. In my case, I couldn't use that (of course) since my module is performing authentication and authorization for RESTful services.

Jeff still provides a hint, though. In his article he shows an asynchronous HttpHandler, and the code for that handler implements IAsyncResult. I wanted a touch more functionality--a boolean return value and exception support--so I modified his work to look like the following:

/// <summary>
/// A class containing an IAsyncResult implementation
/// that wraps a ManualResetEvent. Note this is  relatively
/// simple implementation. A more complete (generic)
/// implementation is available by Jeffrey Richter, found at
/// http://msdn.microsoft.com/en-us/magazine/cc163467.aspx.
/// My thanks to Jeff Prosise for the basis of this
/// implementation.
/// </summary>
internal class AsyncAuthResult : IAsyncResult
{
    private AsyncCallback _callback;
    private object _state;
    private ManualResetEvent _event;
    private bool _completed = false;
    private bool _result = false;
    private object _lock = new object();
    private Exception _ex = null;

    public AsyncAuthResult(AsyncCallback callback,
            object state)
    {
        _callback = callback;
        _state = state;
    }

    public Object AsyncState
    {
        get { return _state; }
    }

    public bool CompletedSynchronously
    {
        get { return false; }
    }

    public bool IsCompleted
    {
        get { return _completed; }
    }

    public WaitHandle AsyncWaitHandle
    {
        get
        {
            lock (_lock)
            {
                if (_event == null)
                    _event = new ManualResetEvent(IsCompleted);
                return _event;
            }
        }
    }

    public void CompleteCall(bool result, Exception exception)
    {
        // Passing null for exception means no error occurred
        _ex = exception;

        // Cache result
        _result = result;

        lock (_lock)
        {
            _completed = true;
            if (_event != null)
                _event.Set();
        }

        if (_callback != null)
        {
            // Invoke callback...
            _callback(this);
        }
    }

    public bool EndInvoke()
    {
        // Operation is done...if an exception occured, throw it
        if (_ex != null) throw _ex;

        return _result;
    }
}

I then created an "authentication manager" that implements the "begin" and "end" methods I'd need in my module:

internal sealed class AuthenticationManager
{
    const string BasicAuthProtocol = "basic";
    HttpContext _context = null;

    public AuthenticationManager(HttpContext currentContext)
    {
        _context = currentContext;
    }

    public bool AuthenticateUser()
    {
        return InternalAuthenticateUser();
    }

    public IAsyncResult BeginAuthenticateUser(AsyncCallback callback,
                                              Object state)
    {
        // Create IAsyncResult object identifying the 
        // asynchronous operation.
        AsyncAuthResult ar = new AsyncAuthResult(callback, state);

        // Create a new thread to perform the operation...note we can't
        // use a thread from the thread pool as this defeats the purpose
        // of the asynchronous implementation (we're trying to leave
        // *those* threads alone for ASP.NET to use to service incoming
        // requests).
        Thread worker = new Thread(this.InternalAuthenticateUserAsync);
        worker.Start(ar);

        return ar;  // Return the IAsyncResult to the caller
    }

    public bool EndAuthenticateUser(IAsyncResult asyncResult)
    {
        // We know that it's really an AsyncAuthResult object
        AsyncAuthResult ar = asyncResult as AsyncAuthResult;
        return ar.EndInvoke();
    }

    private void InternalAuthenticateUserAsync(Object asyncResult)
    {
        // We know that it's really an AsyncAuthResult object
        AsyncAuthResult ar = asyncResult as AsyncAuthResult;

        // Perform the operation..if successful set the result. If
        // not, record the exception.
        try
        {
            bool authorized = this.InternalAuthenticateUser();
            ar.CompleteCall(authorized, null);
        }
        catch (Exception ex)
        {
            ar.CompleteCall(false, ex);
        }
    }

    private bool InternalAuthenticateUser()
    {
        // Authorize the user (using HttpContext for Authorization
        // header access) and return success/failure.
    }
}

The module itself then performs the typical asynchronous event connections. In the "begin" authentication event, I create an instance of my authentication manager, call its "begin" method (which spins up a new thread...more in a moment on that), and waits for authentication to complete. Upon completion, it calls the authentication manager's "end" method to retrieve success/failure and proceeds from there:

public class AsyncAuthModule : IHttpModule
{
    #region Fields

    // We *must* cache the context. When the "end" method is
    // invoked, a CLR thread will be executing (versus an IIS
    // thread), so there is no "current" context available
    // for the thread to use. That is, HttpContext.Current
    // will return null. We can cache the context in this manner
    // even though three different threads will eventually
    // use this object since the runtime suspends this module
    // invocation entirely and restarts it in the "end" 
    // method.
    HttpContext _context = null;

    // We'll cache this as well so we can call its "end"
    // method.
    AuthenticationManager _authMgr = null;

    #endregion

    #region IHttpModule Members

    public void Dispose()
    {
    }

    public void Init(HttpApplication application)
    {
        // Hook authentication
        application.AddOnAuthenticateRequestAsync(
            new BeginEventHandler(BeginAuthenticateRequestHandlerExecute),
            new EndEventHandler(EndAuthenticateRequestHandlerExecute)
        );
    }

    #endregion

    #region Event handlers

    IAsyncResult BeginAuthenticateRequestHandlerExecute(Object source,
        EventArgs e, AsyncCallback cb, Object state)
    {
        // Pull the current context
        HttpApplication application = source as HttpApplication;
        _context = application.Context;

        // Create an instance of the authentication manager and invoke
        // its asynchronous method.
        _authMgr = new AuthenticationManager(_context);
        return _authMgr.BeginAuthenticateUser(cb, state);
    }

    void EndAuthenticateRequestHandlerExecute(IAsyncResult ar)
    {
        // Collect authentication/authorziation result...if true, the
        // user is good to go. If false, the context will contain the
        // appropriate response code and headers.
        bool authorized = _authMgr.EndAuthenticateUser(ar); // can throw...

        // If the context contains anything but "OK" we know
        // to terminate the request. The appropriate headers
        // will already be set up by the authentication manager
        // object.
        if (!authorized ||
            _context.Response.StatusCode != (Int32)HttpStatusCode.OK)
        {
            // End the request
            _context.Response.End();
        }
    }

    #endregion
}

As far as I can tell, this is the only example of a complete asynchronous HttpModule that fully implements IAsyncResult for a custom processing workflow that you can search (at least that I could find). I'm not sure why it's so hard to find others, but hopefully this helps someone else. Laughing With this example, and with Jeff's you should be able to get a working asynchronous HttpModule up and running.

One "gotcha" I'd mention is don't fall into the trap I fell into, which is using the wrong callback. That is, ASP.NET provides you with an instance of AsyncCallback as a parameter to the module's "begin" method. That callback is the one your processing should invoke so that ASP.NET knows your workflow is complete. It then will call the module's "end" method for you. Initially I created a new instance of AsyncCallback and passed that in, bypassing ASP.NET entirely. That resulted in the server hanging the page since I broke the HTTP pipeline. (In my best Homer Simpson voice, "Doh!"). In code, the following is correct:

return _authMgr.BeginAuthenticateUser(cb, state);

The following is most assuredly NOT correct:

return _authMgr.BeginAuthenticateUser(
        new AsyncCallback(this.EndAuthenticateRequestHandlerExecute), state);

Now, as for threading. A good question to ask is "why spin up your own thread and not use the thread pool?" The reason is asynchronous modules (and handlers/pages as well) all are assigned CLR threads from the AppDomain's thread pool when IIS hands off the request. If we were to consume one of the AppDomain's pooled threads, we're defeating the purpose of the asynchronous implementation entirely. This means you should not only avoid queueing a thread pool task but also should not use Delegate.BeginInvoke (a trick I often use to gain asynchronous behavior in other apps). Spinning up a new thread is the only alternative you have when you create your own custom asynchronous module/handler/page workflow objects. I've not cracked open other CLR implementations (like FileStream.BeginWrite), but hopefully if I did I'd find those aren't using the AppDomain's thread pool as well.


Tags: , , , ,
Categories: .NET General | ASP.NET | RESTful Services

9 Comments
Actions: E-mail | Permalink | Comment RSSRSS comment feed

Learning MVC 2, Workflow Foundation 4, and maybe Silverlight 3, Part I

About a year ago I wrote some software for Microsoft that included a Web application that drove workflow to create a Word document filled with specific (and dynamic) information. It was interesting combining workflow with ASP.NET as there are things you have to do in addition to the normal workflow hosting. For one thing, workflow persistence is mandatory because the workflows would be considered long-running. For another, you can't use the typical thread management as you'd be tying up IIS threads. I didn't write about this in my workflow book since it wasn't part of the typical workflow thought process at the time, which perhaps was too limiting on my part. I liked the demos I created for that book, but prior to its release, the workflow community did see workflow more related to Windows Forms or WPF applications.

Now that we have ASP.NET MVC 2, WF 4, and a host of new technologies, it's time for me to dig in a little. I also edited Dino's terrific Web architecture book, Microsoft® .NET: Architecting Applications for the Enterprise, and am editing his awesome upcoming MVC book, tentatively entitled "Programming Microsoft® ASP.NET MVC". (If you already own every MVC book out there, trust me you'll want this one too...very "internals" oriented). I'd love to work out an example prescriptive site or two just to put some of this stuff into practice.

So, open kimono time... The only way to learn this stuff is to DO this stuff. So I came up with an idea. I'd wondered about combining workflow in a Web application using SMTP to authorize things. That is, imagine an enterprise-class Web application that forwards authorization requests to a manager for approval. The request could be anything, but I'll pick....oh...vacation authorization. So you go to the HR site, place a demand for vacation time, and the application forwards your manager the request. The e-mail has an embedded link that fires up the app in the manager's browser, they click yes or no, and the system notifies you of their decision. I'm sure everyone but me has written this, but I thought it made for a good scenario to work with MVC 2 and WF 4. We'll see about working in the Silverlight once the foundational components are in place. I say that only because SL can do some really powerful UI's, and this scenario ain't it. But it might make a good seque into RIA services. (!)

What I write here is raw stuff. Maybe it's well architected and maybe things should be refactored. That's fine...I'm playing with concepts more than anything. If you see glaring omissions or errors, by all means comment! We all learn more that way.

The first thing I see I'm going to need to do is determine the difference between traditional WF persistence and something I'm seeing called "workflow instance store." Is one perferred for Web apps? I honestly don't know yet, but I'll research it a bit and report back. It'll matter because I'll create a SQL Server 2008 database and need to run the appropriate scripts (both sets of which are provided with Visual Studio 2010). I'm starting here because, to me, the app is there to serve the data, not the other way around. I'll design a rudimentary database, just enough to support my scenario, and then layer over that LINQ code to interface with the application service layer. That, then, drives the user interface, at least for this scenario.

So, next time I'll describe the differences in WF persistence and design a small database to support this application scenario. If anyone reading this has questions or comments, all are welcome. :)


Tags: , ,
Categories: .NET General | ASP.NET | Workflow

5 Comments
Actions: E-mail | Permalink | Comment RSSRSS comment feed

"The Matrix" Screen Saver

Several years ago I'd written an article about screen savers in .NET. The very detailed article has since gone to PDF form (found here), but I thought it would be good to mention some of the finer points in case anyone else out there found this and was interested in writing a C#-based screen saver. The samples you'll generally find available aren't nearly good enough as they don't provide enough code, or the right code, to do the job.

In this case, I have a screen saver that mimics the screens seen in one  of my favorite movies, "The Matrix." Here's the general idea:

Matrix Screen Image

Aside from the actual simulation, which I'll leave to you to decipher from the code, two things are critical here. First, you have to properly accept (and handle) the command line parameters. And second, you have to deal with multiple screens. Here's some detail.

As for the command line, this is the code you'll need:

string cmd = args.Length > 0 ?
    (args[0].Length > 1 ?
     args[0].ToLower(CultureInfo.InvariantCulture).Trim().Substring(0, 2) :
    args[0].ToLower(CultureInfo.InvariantCulture).Trim().Substring(0, 1)) : "";

if (cmd == "/c" || cmd == "-c"  || cmd == "c" || cmd == "" )
{
    // Options dialog.
} // if
else if (cmd == "/p" || cmd == "-p" || cmd == "p" )
{
    // "Preview" mode.
} // else if
else if (cmd == "/s" || cmd == "-s" || cmd == "s" )
{
    // Run screen saver.
} // else if
else
{
    // Sorry...don't recognize the command line parameter.
} // else

The trickiest part is the "/c" option, which requires you to accept a window handle (as a string) and render the screen output there. (That's how the options dialog shows you the preview.) The details are in the code, which you can download here, but essentially you convert the handle from a string to an IntPtr, and then create a Graphics object from that. The option will come in this form:

/c:1269802

Multiple screens are another tricky aspect. The basic approach is you iterate the screens attached to the system, which you find in Screen.Allscreens, like so:

Int32 totWidth = 0;
Int32 totHeight = 0;
for (Int32 i = 0; i < Screen.AllScreens.Length; i++)
{
   // Check the dimensions of this screen
   if ( Screen.AllScreens[i].Bounds.X == 0 )
   {
       // We simply compare the width of this screen to
       // the total width we've accumulated, and if greater,
       // use this screen's width. Otherwise, we're covered.
       if (Screen.AllScreens[i].Bounds.Width > totWidth )
           totWidth = Screen.AllScreens[i].Bounds.Width;
   } // if
   else
   {
       // The widths are additive...
       totWidth += Screen.AllScreens[i].Bounds.Width;
   } // else

   // Height
   if (Screen.AllScreens[i].Bounds.Y == 0)
   {
       // We simply compare the height of this screen, similar
       // to what we did for width.
       if (Screen.AllScreens[i].Bounds.Height > totHeight)
           totHeight = Screen.AllScreens[i].Bounds.Height;
   } // if
   else
   {
       // The heights are additive...
       totHeight += Screen.AllScreens[i].Bounds.Height;
   } // else
} // for

To install the screen saver, simply copy the Matrix.scr file to your hard drive and right-click. Then select Install from the list of available options (you can alternatively test it or configure it as well). The compiled image in the download is for all CPUs, but feel free to recompile it to match your own system. Be sure to compile a release build, however, so the streams aren't too slow!


Tags: , , , ,
Categories: .NET General

6 Comments
Actions: E-mail | Permalink | Comment RSSRSS comment feed

X-HTTP-Method-Override

While not normally an issue with thick clients, accessing full RESTful capabilities of available services via browsers often is problematic as many (if not all) browsers only allow a form to GET or POST. They don't allow for other HTTP methods, like HEAD, PUT, or DELETE. Google realized this and offers a solution, which is to add a header to the HTTP request, X-HTTP-Method-Override, that is supposed to be interpreted by the service and acted upon regardless of the actual HTTP method used.

The header syntax is as follows:

X-HTTP-Method-Override: PUT

You can learn more about it from http://code.google.com/apis/gdata/docs/2.0/basics.html#UpdatingEntry.

As it happens, ASP.NET MVC, version 2, supports this out of the box with the HttpHelper.HttpMethodOverride method. Just use that helper method in your view, and assuming the service honors the header, your Web page should be able to work with the RESTful service. I thought this blog entry was particularly interesting (he has an interest in using ASP.NET MVC for RESTful services as well).


Tags:
Categories: RESTful Services

9 Comments
Actions: E-mail | Permalink | Comment RSSRSS comment feed

Updated Nerd Score, so I'm Online!

For some reason, I decided to update my home page to fix the Nerd Score link that has been broken for a long, long time. I think I tripped on a recent page that actually had the image associated with the Nerd Score, and it gave me the idea to look for it and update my page. Turns out I did find it, but when I did, I thought to myself, "self, why not take the test again?" And so I did...

I really improved my scrore, and I wasn't trying to! I wound up with a 98%, a Nerd God, so I treated myself and bought the BBQ smock from their online store. I think I can safely say I'm the first Nerd God in my neighborhood!

I am nerdier than 98% of all people. Are you a nerd? Click here to take the Nerd Test, get geeky images and jokes, and write on the nerd forum!

As another treat, I decided it's about time I started adding to the online content and push my own electrons around, so I installed BlogEngine.Net on my server. The installation couldn't have been easier...I simply copied the "web" version to my hosted site, edited the robot text file, and granted the write permission for ASPNET to write to the data folder. I also had to give the blog folder a new IIS application (which fortunately my host allows me to do). Once I did that, created myself as a user, and edited some settings, I was able to actually post this blog item using the blog engine. I'd tried working with DasBlog, but short of recompiling it to fit into my scenario, I just couldn't get it working. This software, however, worked the first time. Oh, I still get errors...I can't edit my profile yet, I need to figure out how they're doing themes, and I can hardly believe there is no CAPTCHA support when adding comments (I'll be fixing THAT real soon, I'm sure), but I'm stoked it came up so easily.

Sorry world...you'll now be subjected to my lunatic ravings. :)

(Update: profiles and themes now working...and BlogEngine.Net has a "hidden" CAPTCHA capability...I'll go with that for the moment.)


Tags:
Categories:

1 Comments
Actions: E-mail | Permalink | Comment RSSRSS comment feed