Gavin Stevens's Blog

the ramblings of another developer....

  Home  |   Contact  |   Syndication    |   Login
  34 Posts | 0 Stories | 57 Comments | 212 Trackbacks

News

Archives

Post Categories

Friday, April 19, 2013 #

I am doing a presentation tomorrow on T4 templates.

T4 code generation - Intro to the Microsoft Text Template Transformation Toolkit (T4)

Microsoft's T4 toolkit which is built into Visual Studio can help you quickly generate classes, methods or other types of code files. Now's the time to introduce code generation to your company. If you're doing something twice or more, manually, in your company, generate it! T4 templates can help you generate tons of code! You can use T4 to generate Entity Models, Database Structures or entire facades!http://apr2013.desertcodecamp.com/session/653

https://mcsdsoftware.s3.amazonaws.com/T4.zip

This .zip contains my power point presentation and a few sample projects I put together.

Enjoy!


Saturday, March 13, 2010 #

I've been doing some work with T4 templates lately and ran into an issue which I couldn't find an answer to anywhere.  I finally figured it out, so I thought I'd share the solution.

I was trying to generate a code class with a T4 template which used generics

The end result a method like:

public IEnumerator GetEnumerator()
            {
                return new TableEnumerator<Table>(_page);
            }

the related section of the T4 template looks like this:

 public IEnumerator GetEnumerator()
            {
                return new TableEnumerator<#=renderClass.Name#>(_page);
            }

But this of course is missing the Generic Syntax for < > which T4 complains about because < > are reserved.

using syntax like <#<#><#=renderClass.Name#><#=<#> won't work becasue the TextTransformation engine chokes on them.  resulting in : Error 2 The number of opening brackets ('<#') does not match the number of closing brackets ('#>') 

even trying to escape the characters won't work: <#\<#><#=renderClass.Name#><#\<#> this results in:

Error 4 A Statement cannot appear after the first class feature in the template. Only boilerplate, expressions and other class features are allowed after the first class feature block. 

The final solution delcares a few strings to represent the literals like this:

<#+
   void RenderCollectionEnumerator(RenderCollection renderClass)
 {  
  string open = "<";
  string close =">";
#>
   public partial class <#=renderClass.Name#> : IEnumerable
        {
            private readonly PageBase _page;
            public <#=renderClass.Name#>(PageBase page)
            {
                _page = page;
            }

            public IEnumerator GetEnumerator()
            {
                return new TableEnumerator<#=open#><#=renderClass.Name#><#=close#>(_page);
            }
        }
<#+
 } 
#>

This works, and everyone is happy, resulting in an automatically generated class enumerator, complete with generics!

Hopefully this will save someone some time :)


Tuesday, August 25, 2009 #

VS 2008 requires you have VS 2008 Test Edition to see the results of a Generic Test, this wasn't sufficient with out current project as a lot of developers don't use test edition.  I put together this custom xslt transform to convert the .trx test result file to an html report anyone can view, complete with collapsable sections...

Here is a link to the latest version of the XSLT:

http://yourcomputer.com/TRX2HTML.xslt

Here is the TrxConverter.cs file I use to Generate the html.

http://yourcomputer.com/TrxConverter.txt

Rename it to .cs

 

 


Tuesday, May 12, 2009 #

Well, I was recently laid off from my Job of 5 years at Sage Software..  Sage has gone through 2 or 3 rounds of layoffs so far, the latest letting go of 500 employees in North America..

..."To help us better bring our costs in line with revenues, we are reducing staff by about 500 positions across the company, which includes employees as well as open positions that are being closed permanently. "

So, I've decided to take the opportunity to upgrade my Microsoft Certifications to the latest and greatest..  The last tests I took were in 2004, duting the .net 1.1 release..  Hard to believe it's been so long..

Here is my current certs:

Microsoft Certification Status

  Credential Certification / Version   Date Achieved
 
  Microsoft Certified Application Developer   May 07, 2004
  For Microsoft .NET   May 07, 2004
  Microsoft Certified Solution Developer   Jun 11, 2001
  For Microsoft .NET   Sep 01, 2004
  Microsoft Visual Studio 6.0   Jun 11, 2001
  Microsoft Certified Professional   Apr 10, 2000

So, I'm going to take:

http://www.microsoft.com/learning/en/us/exams/70-553.mspx

http://www.microsoft.com/learning/en/us/exams/70-554.mspx

These 2 tests will allow me to upgrade my existing MCSD/MCAD to the new MCPD..  After taking these 2 tests I will have to take a few more to upgrade to .NET 3.0-3.5 certs, the MCTS..  I'm not sure right now if I'll go that far, but maybe I should since I'm doing it.  I'll have plenty of time if I'm unemployed..

These are all very difficult tests, I work fulltime as a contract developer and have for the last 15 years, but there are so many subjects you get tested on that you just don't use everyday..  Hence, it will take alot of studying to get upto speed and feel like I can pass them.  Studying is hard, but you are forced to learn stuff that you should probbly know.  I've scheduled the above 2 tests for June 3rd, June 4th respectively.

I have yet to fail a Microsoft test, I guess I've studied hard enough in the past to make sure I can pass them.  We'll see how it goes, I'm starting to study now, studying everyday during my lunch hour..

On a side note, If anyone knows of any contract positions available, please let me know.  I live in Phoenix, Arizona, so something local would be preferable, or something I can do remote word work also.  Thanks!


06/30/09 Update - Passed the 70-553, tough test...  Going to take the 70-554 next month..


Monday, January 5, 2009 #

Thanks go out to Maarten Balliauw for this awesome post!!

Integrating NUnit test results in Team Build 2008

This rocks!


Friday, August 1, 2008 #

I've seen a lot of developers clueless when it comes to UAC in Vista.  The whole point to UAC is give your application the minimum amount of privileges required for it to run.  If you wrote your application, you should have a pretty good idea of what system resources your application needs to access. 

The solution here is not to require your users to "turn off UAC", or to set your application to run in compatibility mode.  This is the biggest reason UAC has gotten such a bad reputation.. its our fault! Most apps shouldn't require full privilege to run (with exceptions of course, depending on the app) Don't claim your application is "Vista-Compliant" because it works if you run it as administrator.  Microsoft is doing a good thing by working to control the privilege an application can have.

This is a good video done by Chris Corio (Windows Security Team) this will give you a good understanding of UAC.

http://www.microsoft.com/emea/msdn/spotlight/sessionh.aspx?videoid=326

The best advice I have is "Run your app as a standard user" see if everything works as standard.  If it doesn't, why?  What resources are you trying to access that a standard user can't have?  Are you storing things on the file system, outside of the users App Data Context?  Are you writing to the registry?  If your creating WCF endpoints you will have to run as an admin.

Microsoft has a good tool for seeing how an app runs and what it accesses under different privilege levels, the Vista Application Compatibility Toolkit this app acts as a startup wrapper around your app to run it in different security modes and find warnings, errors, etc.  It can help you find the parts of your application that are requiring a higher level privilege and help you fix them if necessary.

Take it upon yourself to learn more about UAC, Code Access Security and Vista security.  Vista is a great operating system if people would stop complaining and take a minute to learn why security had to change.  The plague of spyware, malware, adware and the like is being cured... way to go Microsoft!


Wednesday, July 30, 2008 #

I've been debating lately if it's worth the effort to upgrade my Microsoft Certifications to the latest and greatest.

Microsoft Certification Status

  Credential Certification / Version   Date Achieved
 
  Microsoft Certified Application Developer   May 07, 2004
  For Microsoft .NET   May 07, 2004
  Microsoft Certified Solution Developer   Jun 11, 2001
  For Microsoft .NET   Sep 01, 2004
  Microsoft Visual Studio 6.0   Jun 11, 2001
  Microsoft Certified Professional   Apr 10, 2000

Interestingly, my Microsoft Partner link shows:

  • MCPS
  • MCSD
  • MCAD
  • MCNPS

Not sure where the MCNPS came from... So, yeah... it's been awhile since I took any tests... I'm certified on .NET 1.1, while 2.0, 3.0 and 3.5 are now out.  To Upgrade:

Developers who use the Microsoft .NET Framework 2.0 and Microsoft Visual Studio 2005 should consider the following two credentials:


These credentials give developers a simpler and more targeted framework to showcase their technical and on-the-job skills.

I have to take 2 exams to upgrade to VS2005:  Exam 70–554 and Exam 70–553  While I do have the experience, I used VS2005 for several years, I'm now using VS 2008 which I would then need:

MCPD: Enterprise Application Development 3.5 and MCTS: .NET Framework 3.5, Windows Forms Applications and MCTS: .NET Framework 3.5, ASP.NET Applications and MCTS: .NET Framework 3.5, Windows Communication Foundation Applications and MCTS: .NET Framework 3.5, ADO.NET Applications

Is it worth it? 


Tuesday, July 31, 2007 #

I recently had a strange problem with XMLSerialization throwing the error: There was an error creating the XML Document.  Unable to cast type Configuration to type Configuration

Here was the simple configuration class:

Serializable]public class Configuration

{

public bool Active;

public string lastPSM = "";

 }

and the Save method:

public

{

XmlSerializer serial = new XmlSerializer(typeof(Configuration));

TextWriter writer = new StringWriter();

serial.Serialize(writer, config);

Console.WriteLine(writer.ToString());

writer.Close();

}

cant get much easier than that, but everytime i called Serialize I would get the error, so I changed the code to this:

public

{

XmlSerializer serial = new XmlSerializer(config.GetType());

TextWriter writer = new StringWriter();

serial.Serialize(writer, config);

Console.WriteLine(writer.ToString());

writer.Close();

}

Low and behold that fixed it, and now, funny enough, changing the code back to the way it was before works fine now...  Anyone care to share why things like this would happen?  I even tried closing, re-opening studio to make sure something hadn't gotten whacked...  strange I can't make it break now while before I couldnt make it work...

Ahhh the joys of development....


Friday, July 27, 2007 #

Maybe I need to re-subscribe to MSDN magazine or something, it seems there are soooo many things you can learn about programming, there is never enough time..  If you spend all your time programming, you never have time to learn about  new stuff.  And even if you learn a ton, things seem to slip by...  It seems everything most of us developers know is because we had to do something at sometime in the past and had to find a way to do it.  If the ways and means we do things are the correct way, who knows..  If it works, great!!!

When you first start programming you start to run into state management and storage issues. You always run into a problem that requires that you store something that something else can make use of.  Say for example you have a list of Cities that you read from a file on disk. So, you can create a new city object, and make a Collection of Cities in your application somewhere to load them into.  As a beginner programmer, I would always start a windows app with some "Control" class which would be my "everything class" something like this:

Public class control
{

private collection cities
private frmForm1 form1
private frmForm1 form2

private void loadcities(){load the file, store the values in the collection}

public void GO()
{
 loadcities();
 form1 = new frmForm1(cities);
 form1.showdialog(); 

 form2 = new frmForm2(cities, form1.SelectedCity);
 form2.showdialog();

}

}

While I agree this is pretty bad...  for a beginner long ago, it served its purpose.  I was able to load the city list once and query state on one form from another without having to read/write anything else to the disk, Or store anything anywhere else for that matter.  I found that by creating this "control" class I had a global place that I could store things and something that would know about both forms.

This type of design leads to a very strictly coupled application.  Nothing can ever change on either form, or in my control class without bad consequences.  Plus you can't develop object oriented, interface based stuff this way...

Web Developers have been lucky since the asp days as they have a global "state bag", ie "session"  they can stick things in while navigating from page to page.  But, as far as I knew there was nothing like this for a windows developer, I would always store state in a scenario like the above example, or stick things in a database or the registry for storage.

Enter the AppDomain and the Thread.

While I was always aware these objects existed, they didn't seem to be something you mess with that often. I had no idea that you could store things in them and provide a global, or thread specific "state bag".  I can load my cities list and stick it on the Thread Context.

Saving Data to the Thread:

Thread.AllocateNamedDataSlot("CitiesList");

LocalDataStoreSlot myData;
myData=Thread.GetNamedDataSlot("CitiesList");
Thread.SetData(myData, cities);

Getting it back out:

LocalDataStoreSlot myData;
myData=Thread.GetNamedDataSlot("CitiesList");
Collection cities=(Collection)Thread.GetData(myData);

Finally, we need to free the data slot that we allocated in the beginning of our application.

Thread.FreeNamedDataSlot("CitiesList");

This allows you to take an object and stick it in the Current thread's local storage.  You can also do the same thing with the AppDomain, if you need to access your data with mutiple threads of execution.  Just make sure you lock, unlock the objects when your thread is accessing them so you don't mess up your data by having mutiple thread try to change the values at the same time.

from MSDN:
 // appdomain setup information
        AppDomain currentDomain = AppDomain.CurrentDomain;

        //set predefined system variable application name
        String dataName = "APP_NAME";
        String setappname = "MyApplication";
        currentDomain.SetData(dataName, setappname);

        //Create a new value pair for the appdomain
        String dataValue = "ADVALUE";
        Int32 advalue = 6;
        currentDomain.SetData(dataValue, advalue);

        //get the value specified in the setdata method
        Console.WriteLine(" ADVALUE is: " + currentDomain.GetData("ADVALUE"));

        //get system value specified at appdomainsetup
        Console.WriteLine("System value for application name:" + currentDomain.GetData("APP_NAME"));


There are some samples and better descriptions than I can give here:

http://msdn2.microsoft.com/en-us/library/system.threading.thread.setdata.aspx
http://msdn2.microsoft.com/en-us/library/system.appdomain.setdata(vs.71).aspx

I'm sure alot of you guys know about this stuff, but maybe someone can learn something... 

If any of you more seasoned guys have some best practices for doing this stuff, please feel free to share.

Cheers!


Monday, June 11, 2007 #

Site Pal Avatars, kinda cool, what do you think? SitePal.com

Tuesday, June 5, 2007 #

Well, I just finished up some work in our CAB app to dynamically associate help with specific CAB smartparts.  I thought it was pretty cool in the end to be able to do this with a Builder Strategy and not have to hardcode all of the help references in each component.

So, there are a few parts to make this work.

The HelpComponent is a serializable type we defined to store help info.

The Help Provider Service will be used to load our stored HelpConfiguration file.

The Help Provider Strategy hooks up the help to the smartparts as they are created.

We also need an interface for the HelpProvider Service:

public interface IHelpProviderService
    {
        Dictionary<string, HelpComponent> HelpComponents { get;}
        void InsertItem(string key, HelpComponent item);
        bool Contains(HelpComponent item);
        bool Contains(string key);
    }
Now we define the serializable type:
    public enum HelpNavigator
    {
        AssociateIndex, Find, Index, KeywordIndex, TableOfContents, Topic, TopicId
    }
    [Serializable]
    public class HelpComponent
    {
        private HelpNavigator _navigationType;
        [XmlAttribute]
        public HelpNavigator NavigationType
        {
            get { return _navigationType; }
            set { _navigationType = value; }
        }
        private String _helpNamespace;
        [XmlAttribute]
        public String HelpNamespace
        {
            get { return _helpNamespace; }
            set { _helpNamespace = value; }
        }
        private String _helpKey;
        [XmlAttribute]
        public String HelpKey
        {
            get { return _helpKey; }
            set { _helpKey = value; }
        }
        private String _componentName;
        [XmlAttribute]
        public String ComponentName
        {
            get { return _componentName; }
            set { _componentName = value; }           
        }
      
        public override string ToString()
        {
            if (String.IsNullOrEmpty(_componentName))
            {
                if (base.ParseType() == null)
                    return this.GetType().ToString();
                else
                    return base.ParseType().AssemblyQualifiedName;
            }
            else
            {
                if (this.ParseType() != null)
                    return _componentName + "::" + this.ParseType().AssemblyQualifiedName;
                else
                    return _componentName;
            }
        }
    }
Ok, so this defines the Serializable type, so we can make an XML file like this:
<HelpConfiguration>
    <HelpComponent TypeName="AdminModule.Editors.NewRelationship, AdminModule"
                 NavigationType="TopicId"
                 HelpKey="722412"
                 HelpNamespace="Help\EntityExplorer.chm"/>
    <HelpComponent TypeName="AdminModule.Editors.NewBusinessRuleStep, AdminModule"
                 NavigationType="TopicId"
                 HelpKey="72248"
                 HelpNamespace="Help\EntityExplorer.chm"/>
</HelpConfiguration>
So, we build the Help Provider Service to Load the saved HelpConfiguration Data into a Dictionary
using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
    public class HelpProviderService : IHelpProviderService
    {
        private Dictionary<string, HelpComponent> helpComponents;
        public Dictionary<string, HelpComponent> HelpComponents
        {
            get { return helpComponents; }
            set {  }
        }
        public void InsertItem(string key, HelpComponent item)
        {
            HelpComponents.Add(key, item);
        }
        public bool Contains(HelpComponent item)
        {
            foreach (KeyValuePair<string, HelpComponent> comp in HelpComponents)
            {
                if (comp.Value == item)
                    return true;
            }
            return false;
        }
        public bool Contains(string key)
        {
            foreach (KeyValuePair<string, HelpComponent> comp in HelpComponents)
            {
                if (comp.Key == key)
                    return true;
            }
            return false;
        }
        public HelpProviderService()
        {
            helpComponents = new Dictionary<string, HelpComponent>();
        }
    }

 Now, when smartparts are created in the normal CAB manner:

 _propertyGrid = ModuleWorkItem.SmartParts.AddNew<PropertyGridToolWindow>();

The Builder Strategy will pick up the creation and dynamically hook-up the help

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.Practices.ObjectBuilder;

    public class HelpProviderStrategy:BuilderStrategy
    {
        private bool _canConfig;
       
        public HelpProviderStrategy()
        {
        }
        public override object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
        {
            WorkItem wi = context.Locator.Get<WorkItem>(new DependencyResolutionLocatorKey(typeof(WorkItem), null));
            IHelpProviderService _helpProvider = wi.Services.Get<IHelpProviderService>(false);
            if (_helpProvider != null)
            {
                if (_helpProvider.HelpComponents.Count > _helpcomponents.Count)
                { LoadHelpConfiguration(_helpProvider); }
                Control control = existing as Control;
                if (control != null)
                {
                    AddHelpSupport(control);
                }
            }
            return base.BuildUp(context, typeToBuild, existing, idToBuild);
        }
        private void AddHelpSupport(Control c)
        {
            HelpComponent help = _helpcomponents.Find(
                delegate(HelpComponent searchComponent)
                {
                    return searchComponent.ComponentName == c.Name &&
                        c.GetType().Equals(searchComponent.ParseType());
                });
          
            if (help == null)
            {
                help = _helpcomponents.Find(
                    delegate(HelpComponent searchComponent)
                    {
                        return c.GetType().Equals(searchComponent.ParseType());
                    });
            }
            //Assign Help Provider
            if (help != null)
            {
                HelpProvider provider = _providers[help.HelpNamespace];
                provider.SetHelpKeyword(c, help.HelpKey);
                provider.SetHelpNavigator(c, (System.Windows.Forms.HelpNavigator)Enum.Parse(typeof(System.Windows.Forms.HelpNavigator), help.NavigationType.ToString()));
            }
        }
}
}
Don't forget to add the Builder Strategy to you root workitem: 
        protected override void InitializeCommonStrategies(Builder builder)
         {
             base.InitializeCommonStrategies(builder);
......
             builder.Strategies.AddNew<HelpProviderStrategy>(BuilderStage.Initialization);
        }

You may not be able to just copy/paste all the code here and make it work, but if you can follow the example you should get a pretty good idea of how to implement this dynamic help configuration strategy in CAB.

Gavin


Tuesday, March 13, 2007 #

Well, I'm pretty fed up with Microsoft's Windows Live Dev Team.  Not that they have really done anything wrong, it's just that they haven't done anything!!!

Add new abilities to the Windows Live Messenger client with the Messenger Add-in SDK beta.
Published Friday, June 09, 2006 2:55 PM
 
So, it's almost been a year since MS has put out anything to support development with Messenger.  Whats the deal?? To top things off the existing Add-in sdk is crap!  While it works, it only give you access to events and methods that a kid would want.  Look at this:
 

That's all we get???  When MS released Live Messenger they added the Verizon Web Call support to the app.  While this has been around for almost 6 months, there is still no way for a developer to place a call with messenger from another application.  The other thing that really sucks is the current stuff will only fire off if the user's status is set to idle.  While this is good for a "Away" plugin, what if you want to do more?  You would think that would be an option for a developer to respond to the events when they want to. 

I think it would really help MS to open up the Messenger API to allow developers to integrate the ALL the communication functionality in other applications.  They are already doing this in Office, so it's obviously something they want to do, but why prevent outside developers from doing the same?  Seems it would make Messenger a more viable and necessary application if other apps wanted to use its functionality.

Just had to vent, maybe MS will get thier crap together now that Vista is out...  remains to be seen...


Tuesday, January 30, 2007 #

Well, I recently got a new Toshiba Laptop.  I bought it in December, so no Vista.  It was running Windows Media Center Edition.  It's a nice machine, Dual Core, 2GB RAM, etc...  So, I decided to put the MSDN copy of Vista Ultimate edition on it.  Everything seemed to run fine except for a few issues.  I couldn't run a program called IsoBuster, Vista just wouldn't let me open the .exe no matter what I tried.  And then there are still compatibility issues for VS 2005, which weren't giving me any warm fuzzies..   So, I restored the orginal OS and have been using it with XPMC for the last few weeks....

Meanwhile, and someone please tell me why they do this...  the 200GB hard drive in the laptop could only muster a whopping 4200 RPM...  this is why people hate laptops.. does it matter if you put 2GB RAM and a nice dual core if you bottleneck the whole thing with this shitty harddrive???  So, I got a new 100GB 7200RPM drive, $150, not bad...   at the same time MS sent me my complimentary copy of Vista Business edition for participating in thier Power Together campaign.  http://www.powertogether.com So, with a new harddrive, and a non-upgradable OS from XPMC to Vista Business.  I decided now that MS has "officially" released Vista, they can't leave us developers hanging for too long.  So I'm running Vista Business with Visual Studio 2005.  I had to download VS2005 SP1 and the VS Vista Update Beta.  after all was said and done, I got everything working and compiled our 60 project solution, woohoo.

I still have to wait for drivers for my U3 smart drive and my fingerprint reader, but hey, nothing is perfect right??


Wednesday, December 27, 2006 #

I started working on a new project a few weeks ago.  Were writing a new extensible Shell application with the Microsoft CompositeUI Application Block.  I haven't used this block before.  Looks like Microsoft rushed this thing out the door.  Alot of needed functionality is missing, maybe the goal was to just provide a simple base shell layer and allow developers to extend it to do whatever.  I guess thats how it works when they are trying to release a new pattern or practice. 

So it took me a few weeks to wrap my head around this thing. ObjectBuilder was a new concept for me.  Pretty cool stuff though it turns out, Dependency Injection is a nice way of sharing global objects and making sure everything is referecing the right things.  Here some sample Dependency Injection Code.

Say you create a new object, and you want it to have a reference a global Selection Service..  all you have to do is add this code to your class:

[SmartPart]

public partial class PropertyGridToolWindow : UserControl, ISmartPartInfoProvider

{

   ISelectionService _selectionService;

   [ServiceDependency]

   public ISelectionService SelectionService

   {

   set

   {

   _selectionService = value;

   _selectionService.SelectionChanged += new EventHandler(_selectionService_SelectionChanged);

   }

   get { return _selectionService; }

}

The cool thing about this, is instead of having to create an overloaded constructor to pass the existing instance of the Selection Service to the new object, Object Builder handles it all for you.  When creating the new PropertyGridToolWindow, just have ObjectBuilder do it:

PropertyGridToolWindow _propertyGrid;

_propertyGrid = ModuleWorkItem.SmartParts.AddNew<PropertyGridToolWindow>();

Thats it!  Object Builder will automatically set the reference to the Selection Service when the object is created.

I'll post some more cool things I discover here as I go.

Gavin


Thursday, December 21, 2006 #

Why do we do it?  Why would I even post this blog in the first place?  What does it do for me?  Am I posting, sharing some strange introverted thoughts or ideas that I have no-one else to talk to about?  What are the chances of anything I say here even being read?  You have to ask yourself, If I type and post this blog, and noone ever reads it, will it ever really matter?  I guess if I feel better sharing some specific knowledge I have gained doing something in my life then I will post things.  But, what purpose does it serve other than me sharing my thoughts or experiences with the world? And who knows if anyone even cares about my thoughts or my expereinces. If my blog didn't exist, and you weren't here reading it, would it really matter?  I guess it all depends on your perspective of things.  If noone ever wrote a blog, and never posted it for anyone to read, then there would be no blogs and noone reading them, which would, yes, make things different.  So I guess on one hand my contribution to the digital world of information does mean something.  Obviously I'm in a weird mood today, but does it really matter? Not to you....