Foredecker

Jibe!

C# IShellLink COM Interop Example #1

with 2 comments

I recently needed to use C# to programmatically create a shortcut (.LNK file) to a console executable. This is easily done in native C/C++ using the ShellLink CoClass, the IShellLink interface and their related interfaces.

I looked around and couldn’t find a good C# COM interop example for the related native COM interfaces so I wrote one.   This was a good excuse for me to learn a litle bit about C# and COM interoperability.  You can find the source code here, provided under the Microsoft Permissive License.

This first cut provides a set of C# classes and related values for manipulating Windows Shell Links.  The public methods, properties and enumerations (or just publics) are C# style – I didn’t let the underlying native names leak through.

For example, an important native enumeration is the native SHELL_LINK_DATA_FLAGS enumeration.   I’ve seen some interop examples that expose the native names of things publicly in C#.   This works, but doesn’t follow the .NET Design Guidelines for Class Library Developers (in hardback on Amazon).   Exposing the native names is a bit ugly in .NET languages, and in C# in particular.

In the public aspects of my C# I provide the native functionally in a modern C# manner.  For example, I  provide a .NET enuneration named ShellLinkFlags which provides the values in SHELL_LINK_DATA_FLAGS.   These enumerations have C# style names like RunAsUser instead of SLDF_RUNAS_USER.

Another important attribute is that all of the publics are CLS compliant.  This enables all .NET languages to easily access these classes, not just C#.

There are some limitations to this code.  I built this so I could create shell links to EXEs – primarily console applications.    This code does not yet support the IShellLink.GetIDList() and IShellLInk.SetIDList() methiods.  So, it can only be used to link to things in the file system, and not shell objects such as printers.

The Project

This code is provided in a Visual Studio 2010 project built with C# and .NET 4.0 using the client profile.

The actual shell interop code is built in a separate class library named Eidos, which is the name of my general purpose C# library.   The namespace is Eidos.Shell.   You can use this directly, or easily incorporate it into your own code.

I’ve also included a couple of basic unit tests – you can extend these as needed.

There is also a simple program that will dump the contents of a Shell link file to the console.

The Code

The primary public class is ShellLink.  This provides a set of properties and methods that provides the functionality of IShellLink but in a C# manner.

One of the biggest differences between the C# APIs and the native COM interfaces are how Data Blocks and IShellLinkDataList are treated.    These are not exposed in the my .NET classes.  instead, they are wrapped using standard properties on the C# ShellLink class.

This works really well.  It means you can simply get and set property values just like you would with a “normal” .NET class.   The IShellLink class handles all the work of creating, writing and reading the data blocks using an internal IShellLinkDataList interface.

Download the Source

You can find the source code here, provided under the Microsoft Permissive License.

Advertisements

Written by foredecker

December 5, 2010 at 5:21 pm

Posted in Coding

Advice to a Younger Man

with one comment

I ran across Ted Dziuba’s post Advice to Old Men from a Young Man.   I though I’d return the favor:

  1. Unless you are dating Scarlett Johansson, we don’t care about your girlfriend.
  2. Stay out of the left lane unless your car is faster than mine, and you are actually going faster. See #3, its likely I make way more money than you so I probably have a better/nicer/faster car; unless I spent that money on a big boat.
  3. Yes, things will always get more expensive – we make lots more money than you so it really doesn’t matter.
  4. Can do on the war stories – glad you like them.
  5. That’s good advice 🙂
  6. We don’t own you anything either – especially a job.
  7. Respect your elders – if it wasn’t for us, you wouldn’t be working on whatever you are are working on. You’d be working on what we already did.
  8. The right way to respond to advice form a more experienced man is.
    * Thanks dude
    * Thanks man
    * Cool, thanks
    * Pretty much any phrase containing the word ‘thanks’
  9. No problem. Vegas is a short flight away. First round is on me.
  10. That kind of victory can be really, really expensive. Beware the ruby lipped tonsil sucker; they are fun – for a while. Then they turn into soul leaching psychopaths. That’s advice by the way…

Written by foredecker

November 25, 2010 at 6:52 pm

Posted in Miscelaneous

Tagged with

WPF: Styling the Expander Control

with one comment

I recently needed to style a WPF expander control for a prototype project. Our team’s UX designer came up with a nice visual design.    So, I began to build my prototype and match his visual style (mocked up in Visio) in WPF.   I thought it would be easy…   it was, once I spent all morning  figuring out how to do it…

Here is screen shot of the prototype – it is pretty close to the mock up.

ScreenShot1

I started off with the Expander Control Template example on MSDN.  This wasn’t a bad start – it showed me how to change expander toggle button – nice.

But, I also needed to change the font used in the expander header.  That wasn’t shown in the example.   Ok, no problem – just use a setter in the style like this – right?

    <Style x:Key="MainViewExpander" TargetType="Expander">
        <Setter Property="FontSize" Value="18" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Expander">

Example1Bzzzztttt… wrong.   That set the font appearance in the Expander header like I wanted, but all the controls in the Expander content property inherited the font appearance.  That’s not what I wanted at all.

I needed to style the appearance of the Expander header and its contents separately.

So, i dug around in MSDN a little more.  The key information turned out to be in the documentation for the Headered Content Control class.

This is an important class as it defines the base functionality for controls including the Expander, Group Box and TabItem controls – all very commonly used controls.

They key to getting the look I wanted is using Data Templates.   I’ve seen this used in many MSDN examples and WPF blog posts but until now, I never really understood them.

In short, data templates are like control templates but instead of defining how WPF structures the visual tree for a control, data template defines how WPF displays data.

We’ll, you might say “Duh Richard!”  That’s what the docs say….  Ya, Duh! For sure…  I didn’t grok exactly what this meant until now: they key for me was finally understanding that the data template is bound to the data it is intended to display.

All data template intentions are bound to a property on an object instance.   You can refer to this binding by using the simple most binding which is “{Binding}”.  You can see how this is used below.

The key is that all Headered Content Controls have a data template for the header content.    So, setting the font appearance for the Expander header was a simple matter of adding this setter to my style


        <Setter Property="HeaderTemplate">
            <Setter.Value>
                <DataTemplate>
                    <TextBlock
                        Text="{Binding}"
                        FontSize="18"
                        FontWeight="Bold"
                        Foreground="White"
                        />
                </DataTemplate>
            </Setter.Value>
        </Setter>

This worked great!  As you can see in the screen shot at the beginning of the post, I set the text appearance to the white, 18 size bold text I wanted.   Most importantly, the appearance of the content was not affected by the header’s appearance.

Now, in one case, I do want to determine the font appearance of all the children in an Expander’s content property.   As you can seen in the screen shot, the commands are in a larger, bold font style.

To accomplish this, I simply created a style derived from the main expander style -like this:

    <Style x:Key="MainViewExpanderCommands"
           BasedOn="{StaticResource MainViewExpander}"
           TargetType="Expander"
           >
        <Setter Property="FontSize" Value="20" />
        <Setter Property="FontWeight" Value="Bold" />
    </Style>

Then, in the MainView.xaml file, I used the derived style like this:

        <Expander
            Header="Commands"
            Grid.Column="0"
            IsExpanded="True"
            Style="{StaticResource                   MainViewExpanderCommands}"
            >
            <StackPanel>
                <Label Content="Open"    Foreground="#FFB4B4A9" />
                <Label Content="Save"    Foreground="#FFB4B4A9"/>
                <Label Content="Save As" Foreground="#FFB4B4A9"/>
                <Label Content="Close"   Foreground="#FFB4B4A9"/>
            </StackPanel>
        </Expander>

This worked fine – mostly.  If you look, you will see that each of the labels has the foreground set to a grey color.   I tried to do this in the derived style, bit alas, this didn’t work because the the intervening stack panel (which is the content for the expander) does not have the foreground property, so it is not inherited.

Here is the source code download

This code builds with the release candidate version of Visual Studio 2010.   It does not need any additional components (like NUnit).

Installing and building the project is easy:   Just download and run the installer.   It will put the project on your desktop.  Note, it doesn’t really ‘install’ anything – there are no shortcuts and it doesn’t write to the registry.  You can move the sample directory wherever you would like, or just delete it when you are done.  No un-installing is needed.   The contents are licensed with the Microsoft Public License.

Credit – I’d like to thank Josh Smith for his excellent examples of using the MVVM design pattern.   All my new WPF projects are MVVM and I learned the ropes from his blog posts.  The MVVM classes in this example are inspired by Josh’s MSDN article here.   I’m still a noob with both WPF and MVVM – the learning curve is very steep, and long.  But Josh’s blog and articles have been hugely helpful (as have many others…)

Written by foredecker

March 28, 2010 at 5:48 pm

Posted in Coding

Tagged with ,

Data Doctor Recovery for USB Sticks – Super

leave a comment »

Today, disaster struck.   All the homework on the 12 year old’s USB stick disappeared.  

There was the phone call, the tears, the anguish.  

Someone at school had given kiddo some really bad advice.  

So, I did a little research and downloaded the trial version of Data Doctor Recover for USB Digital Storage.

Worked great.

The files where recovered.

Family happiness abounds.

Written by foredecker

March 3, 2010 at 3:42 am

Posted in Miscelaneous

What I think about FOSS

with 8 comments

I like free open source software and I admire people that write it and drive the big successful FOSS projects.  FOSS is cool.  It is even noble.   It’s pretty spiffy that some developers chose to spend time writing software then give it away free – with source code. I’ve done this in my own very tiny way here on this blog.

Some FOSS software is very good – Linux, Apache, MySQL, Mozilla are all things I have used and find technically pretty groovy.   For quite a while I used FireFox as my primary browser.   Its fast and I like how it handles tabs and downloading.

I think many software developers like to give software and source code away for free.   It seems to be part of our nature – or at least for many of us.   We like it when people find our stuff useful.  For the most part, its cool when other people leverage our code. But this isn’t always true.

I know lots of other developers (and non developers) at Microsoft feel this way.  They often say similar things.   This is a common topic of conversation at lunch, or over beers

If I could make a great living writing FOSS I probably would.  If I’m ever lucky enough to be independently wealthy, I probably will – when I’m not riding around on my big boat in the Bahamas.

Internally at Microsoft, we have a culture very similar to the public FOSS culture.    this is especially true within each of the major business units.   For example, in Windows, everyone can see all the code.   Teams often share code – either at the source level or by publishing it in DLL’s or libraries.    There is lots of internal discussion about code, coding, bug fixing and related topics.   team’s often have cross team code reviews.    Anyone can file bugs on anything.  We have a global bug tracking system.

We even have an internal version of CodePlex that is accessible to everyone in Microsoft – its quite popular and there is a lot of cool stuff there.   A lot of this is internal stuff that wouldn’t really be interesting to anyone else (like internal bug management tools, or project management tools).  But some of this makes it to CodePlex.

None of this isn’t rocket science – its pretty mundane stuff.  I’m sure the culture at Google, Apple, IBM and Oracle are similar.  The point is that while its hard for outsiders to see – I don’t think there is much difference culturally between Microsoft developers and FOSS developers.

I believe that you will see Microsoft and even the Windows org, publish more free open source things over time.   There are lots of good reasons to do so.   I’m a big proponent of publishing my next big team project as free and open source. This idea has a lot of traction.

But there are differences.  The most obvious two are that we think its fine to sell software.   We also think its fine that we don’t provide the source code to our bread and butter products.

Hey, I think its fine – noble even – if a person, group or company wants to write software, give it away for free and publish the source code under an Open License.

But the problem is the idea that all software should be free and open source and that software which isn’t free is somehow bad.  This is Richard Stallman’s position and that of many other FOSS advocates.   For example, the Free Software Foundation claim that Windows-7, OSX, and the iPhone are somehow threats to people’s freedom.  Really?  That is simply stupid.

What’s busted about this ideology is that they believe others are somehow evil, or sinful if they do not hew to the FSF ideals.  Don’t believe me?  Just read their web pages.

Note, i think its great if people want to make their software free. I don’t begrudge them the liberty to do this for a second.  The problem lies in the idea that anyone who disagrees with this, or doesn’t provide their software as ‘free’ is somehow bad or not supporting the freedom of others.

This is just busted.   Its a freaking free country – this is true from a libertarian, conservative, liberal, Democratic, Republican, or Tea party perspective.   Everyone has liberties.

Free Software folks are at liberty to write software and make it free.  Microsoft is at liberty to write software, sell it and keep the source to ourselves.  So is any other corporation or individual.  Don’t like it –  bummer for you.  Move to another country.   Or get the laws changed – just don’t bitch about it.

The issue here is that RMS and others are confusing freedom with liberty.  What is freedom to them isn’t freedom to me.  I want to write software, sell it, make a good or even great living and keep the source to most things to myself.   Its fully right and proper that I’m at liberty to do this.    Same for Microsoft.

Its awesome, right, proper, good and noble when the FSF and RMS defends their own liberty, and the liberty of others to write free software.  Its good to stand up for the rights of others.

But, it is  not ‘freedom’ when a person or organization wants to restrict the liberty of someone else.  This is exactly what RMS advocates – that all software should be free.  If he had it is way, all people would be forced to make their software free – That is exactly what RMS advocates.  This is un-American; it is not libertarian.  This attitude is simply selfish.

RMS and the FSF is great at wrapping the selfish notions in the language of political freedom and liberty – but make no mistake about it.  The are simply being selfish in many of their attitudes.

Remember, I fully support the right of people to write and publish free open source software. Its cool, its noble, its admirable.

But, it is immoral, snide, selfish and contrary to the ideals of American liberty to suggest that others that want to sell proprietary software are somehow bad, or sinful.

To you Mr. Richard M. Stallman and to like minded individuals, I say this: do not let your ideals of freedom tread on the liberties of others – that is not freedom – it is tyranny.

In America, yours and anyone else’s ability to to give people your definition of freedom is unimpeded in any way  The Free Software Community is at full liberty to do so. The path is actually very simple; write great software that 100’s of millions of people across the world want to use.

If you cannot do that then tough noogies.  Don’t lean on a poor ideology, demonizing language, and the desire to restrict the liberties of others just to suit your own selfishness and make up for your own shortcomings.

RMS and friends – you have the ultimate commercial weapon – you are giving your stuff away for free.

Put up or shut up; get off your ass and write great software – that is they way you win.

Written by foredecker

February 27, 2010 at 5:50 pm

Posted in Rants

Apple is not big enough to do search… They need Microsoft

with 43 comments

I’ve said a few times on Slashdot that Apple is likely to partner with Microsoft for search on the iPhone.  A few people have given me rebuttal directly and a portion of the Slasdot crowd thinks this is unlikely.  Here is why I think Apple is not big enough to do search on their own.

In a Slashdot reply node_3 replied to me that “Apple is one of the largest companies in the world”.  I’m not sure what he means by large.  Sure – Apple is in the top Fortune Global 500 for 2009 at #253.   But consider these items from the Fortune list:

Rank Company Revenue Profits Assets SH Eq
117 Microsoft 60,420 17.7 72.8 36.3
253 Apple 32,479 4.8 40.0 21.0
423 Google 21,796 4.2 31.8 28.2

Now consider Apple’s business model – they sell ‘things’ (as in physical do-dads) and music.  They have a relatively small “software and services” Business at ~8% of total revenue (cite).   Its fair to say they are not a software company.   NOTE – do not panic!  Apple develops some great software!  OSX is truly a world class client operating system and the iPhone changed the game dramatically.  But, their business model isn’t selling software – its selling ‘things’.  Just look at the numbers (all in billions of dollars).  79% of their Q2 revenue came from selling ‘things’.  Only 8% was “software and services” and I suspect most of that is services.

Product Q2 2009
Desktop 1.05 13%
Portables 1.86 23%
iPod 1.67 21%
Music 1.05 13%
iPhone 1.50 18%
Peripherals 0.36 4%
Software & Services 0.62 8%
Total 8.11

Compare the Apple data to Microsoft (cite)

Division Q2 2009
Windows 3.980 24%
Server & Tools 3.740 22%
Business 4.880 29%
Online Services 0.866 5%
Entertainment and Devices 3.180 19%
Total 16.646

76% of our Q2 2009 revenue is just software.  And not just any software – world class products that have been consistent market leaders for years and years.   Say what you want, but people keep buying Microsoft software despite very real and credible competition – some of which is free.   (that’s a topic of a future post…)

Just Windows alone (our “client” business) is 36% larger than Apples Mac business (desktop + Portables) by revenue alone.  Unfortunately, I cannot find a public source that indicates Windows unit shipments in that time frame, but in Q2 2009, Apple had about a 7.6% market share in the US at about 1.21 million Macs (cite).  Apple has their best share in the US, with much lower market share in other world wide markets.   Its safe to say that Windows shipments dwarf Apple’s client shipments.

The point in all this is that Apple does not have the chops to build a competitive search engine.  Yes, if they decided to do that, they could – they fundamentally have the revenue to do so.  But it would take them years.  first, they would have to hire people and grow the expertise.  It takes a massive investment in people and assets to build a world class search engine. It also takes another key element: the corporate fortitude to stick with it for years. That is why there are just two of them – Google and Bing.  If it was easy, there would be more than two.

I’m sure someone will point out there are more than two search engines…  yes of course, but there are only two that matter… Google and Bing, this is especially true since Bing now powers Yahoo (cite).  And most importantly, Bing is showing slow and steady growth (cite).  Its early yet of course, but remember, Microsoft has a strong history of sticking with things… (yes, we drop things too, but we do it deliberately, no flaming out like Sun…)

So in one sense, you won an ‘internet argument’.  Woot!  Yes, in a narrow technical sense, Apple is ‘big enough’ to build a world class search engine.    But I stand by my statement.

In practicall terms, Apple is not any where near large enough to build their own search engine: they have no foundations on which to build.  its just not in their league.  They would have to start from scratch.  It would take them years to do this and a huge investment in terms of time, effort, energy, and money.

Hey!  If you had a few billion dollars in yearly revenue, you could try it too.   But would you be successful?   Very likely not – even if you are a pretty smart guy.    That aptly describes Apple today with respect to search.

The biggest barriers to entry here isn’t money.   Anybody with a few billion can build a series of world wide data centers.    This isn’t science – its engineering.

Building a successful world class search engine that can successfully complete with Bing and Google requires something much harder to obtain – skilled and experienced people. A skilled and experienced person is the true unobtainum.

These people do not grow on trees.   There really very, very few of them.  In the search space I’d suggest they number in the few hundreds, maybe even less.   Most of them are currently employed at Google or Microsoft.   Its also a lot of work to grow them – Microsoft and Google both do that very successfully.  Yahoo did too, but couldn’t keep up on the business front.

One of the reasons Microsoft wanted to buy yahoo is to get their best people.  Their business was interesting too, but their people were key to the deal.   Dr. Qi Lu is the canonical example.   I’m senior enough to go to periodic internal briefings by senior execs; let me tell you, Dr. Qi Lu is one of the most intelligent and effective people I’ve ever heard.  He is in the same league as Bill himself.  He has had a very positive and dramatic effect on Microsoft, both externally and internally.   He is a high profile example, but only one of many.  There are a lot of other good people that came from Yahoo.   They are doing impressive things.

In summary: Apple needs a world class search engine on the iPhone.  There are two, Google and Bing.  Yes, we compete with Apple and will likely do so directly in the mobile phone market, but Apple cannot build their own effective search engine in any time frame that matters.  I maintain that Bing and Microsoft is by far the best choice for Apple.

Written by foredecker

January 23, 2010 at 6:18 pm

Posted in slashdot, Technology

Tagged with ,

BizzySpinner 2 – A WPF Spinning Busy State Indicator (with source)

with 4 comments

I’ve made a key improvement to my BizzySpinner control form my last post.  It now starts and stop smoothly instead of abruptly.  This is quite visually attractive.  But it was somewhat non-trivial to do.

In my first example, I had a single simple animation that was either running or stopped.  The animation behavior is now like this:

BizzySpinnerAcceleration

I tried to do this all in XAML but it proved quite complex to handle in in that language.   This was complicated as there are two key dependency properties to deal with that leads to several states: The Spin property controls the actually spinning state.   And the IsEnabled property inherited from UIElement.  Both must work correctly together.

This gave me two significant problems:

  1. The XAML was really complicated and difficult to read.  I don’t know about you, but to me, XAML is very verbose and difficult to read.  Syntax highlighting helps, but only to a degree.   There is also no such thing as a XAML debugger so a lot of trial and error is involved with large amounts of XAML.   Kaxaml is really helpful, but this was hard even with that tool.
  2. I couldn’t get a fully correct state machine in XAML: No matter what I tried I could toggle the IsEnabled and Spin dependency properties and cause the spinner to “jump” unattractively.

BizzySpinnerAcceleration-Page2 So, I simply used C# code to do this.   This approach was much simpler.   It was easy to implement the two cooperating state machines needed to implement all cases.   The spinning state machine is shown to the right.   It has four states and six transitions – not at all difficult to implement.

This state machine is implemented in the ControlSpinning() function on line 432 in BizzySpinner.xaml.cs.

The transitions caused by the SpinUp and SpinDown values are straight forward and implemented using a couple of switch statements

The transitions labeled Acceleration Complete and Deceleration Complete are more interesting:  they are implemented using the Completed event on the DoubleAnimation using to spin the control.

For example when the spinner goes form the Not Spinning state to the Accelerating state, the code simply attaches a function to the animation’s Completed event like this:

Code Snippet
  1. spinAnimation.From = SpinAngle;
  2. spinAnimation.To = (SpinAngle + (OneRotation / 8));
  3. spinAnimation.Duration = new Duration(TimeSpan.FromSeconds(SpinRate/4));
  4. spinAnimation.DecelerationRatio = 0.0;
  5. spinAnimation.AccelerationRatio = 1.0;
  6. spinAnimation.Completed += SpinContinuously;
  7. theSpinState = SpinState.Accelerating;
  8. this.BeginAnimation(SpinAngleProperty, spinAnimation);

This is the code that causes the spinner to accelerate to its normal spinning rate.  The key line is #7.   When the spinner has finished accelerating the SpinContinuously member is called.  It looks like this:

Code Snippet
  1. void SpinContinuously(object sender, EventArgs e)
  2. {
  3. spinAnimation.Completed -= SpinContinuously;
  4. spinAnimation.From = SpinAngle;
  5. spinAnimation.To = SpinAngle + OneRotation;
  6. spinAnimation.Duration = new Duration(TimeSpan.FromSeconds(SpinRate));
  7. spinAnimation.DecelerationRatio = 0.0;
  8. spinAnimation.AccelerationRatio = 0.0;
  9. spinAnimation.RepeatBehavior = RepeatBehavior.Forever;
  10. theSpinState = SpinState.Running;
  11. this.BeginAnimation(SpinAngleProperty, spinAnimation);
  12. }

Note line 3 which disconnects the SpinContinuously from the animations Completed event.   This is super important as we don’t want this called again because the next state transition when the animation completes is a different one.

The other important state machine is the one that handles the IsEnabled and Spin dependency properties.   This is what sends the SpinUp and SpinDown commands to the spinning state machine.

BizzySpinnerAcceleration-Page3

This is simpler than it looks:  there are only four important transitions – the ones in green where the SpinUp and SpinDown commands are sent to the spinning state machine.

This is all handled easily in the IsEnabledChanged event handler like this:

Code Snippet
  1. private void IsEnabledChangedHandler(Object sender, DependencyPropertyChangedEventArgs e)
  2. {
  3. if ( !(bool)e.NewValue)
  4. {
  5. //
  6. // Going enabled
  7. //
  8. Background = BackgroundBrushSave;
  9. LeaderBrush = LeaderBrushSave;
  10. TailBrush = TailBrushSave;
  11. // The control is enabled, turn on spinning if the Spin property is ture
  12. ControlSpinning(Spin ? SpinCommand.SpinUp : SpinCommand.SpinDown);
  13. } else {
  14. //
  15. // Going disabled
  16. //
  17. if (theSpinState == SpinState.NotSpinning)
  18. {
  19. SetDisabledBrushes();
  20. }
  21. else
  22. {
  23. ControlSpinning(SpinCommand.SpinDown);
  24. }
  25. }
  26. }

The resulting animation is very pleasing – the control spins up and down smoothly.   Its rotational rate can also be controlled dynamically.

You will need NUnit 2.5.3 (download) as I’ve added assertions. The code builds for C# 3.0 and .NET 3.5 using the client profile.  As with the last post, the project is for the Visual Studio 2010 BETA which you can download for free..

Installing and building the project is easy:   Just download and run the installer.   It will put the project on your desktop.  Note, it doesn’t really ‘install’ anything – there are no shortcuts and it doesn’t write to the registry.  You can move the sample directory wherever you would like, or just delete it when you are done.  No un-installing is needed.   Also note that the contents are licensed with the Microsoft Public License.

Here is the source code download

Written by foredecker

January 11, 2010 at 3:49 am

Posted in Coding

Tagged with ,