Thinking way too long about the subtitle RSS 2.0
 Saturday, February 26, 2011

Shopping List App I had a great time presenting Windows Azure at the Boise Code Camp today. This is a great venue and I'm enjoying the awesome talks.

Adam Cogan kicked off the event with his keynote and I'm winding down with Glenn Block in his REST talk.

You can download my Azure presentation. Inside the zip file, you'll find my slide deck, helpful nuggets and the sample Shopping App solution I used to demonstrate some features of Windows Azure.

I've also created a short list of helpful Azure links that relate to some of my Azure speaking points. I hope you enjoyed the event as much as I did!

Saturday, February 26, 2011 10:49:05 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Azure | events | learning
 Friday, November 05, 2010

So, who out there already knew what a coworker and I just learned via trial and (mostly) error?

Jim and I were working on a simple Silverlight audio player with a couple of tracks, nothing elaborate whatsoever. It runs great on Windows. Yet, it failed to play audio on Mac OSX, regardless of browser.

After a bit of voodoo, standing on one foot and other meaningless postulates, Jim says, “Hey, can you move the player back onto the screen so we can see the trace statements?” I was using CSS to position the player off the screen, as it has no user interface. Jim was showing trace events in a simple textblock.

As soon one pixel of the Silverlight control existed inside the visible browser viewport, the audio would play on our Macbook Pro. Move it off the screen, then no audio for you.

But wait there’s more! Setting CSS visibility to hidden works in Mac/Safari, but still fails in Mac/Firefox. To play in all browsers, the Silverlight player must have at least one visible pixel on the screen. I didn’t try layering it behind another element because a single dot on our page was invisible on the edge.

Uhg.

Friday, November 05, 2010 8:31:50 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Apple | Silverlight
 Friday, August 20, 2010

I was using a clean machine for some Azure development and it just happened to have the developer edition of SQL Server, as the default instance, installed instead of SQL Server Express Edition. When I pressed F5 to launch the local developer app fabric, I received an error message about the local developer storage. By default, the local developer storage is looking for .\SQLExpress on your machine.

Here’s the error message that popped up in Visual Studio 2010:

Failed to initialize Development Storage service. See output window for more information.

I looked in the output window and found this message:

Windows Azure Tools: Failed to initialize Development Storage service. Unable to start Development Storage. Failed to start Development Storage: the SQL Server instance ‘.\SQLExpress’ could not be found.   Please configure the SQL Server instance for Development Storage using the ‘DSInit’ utility in the Windows Azure SDK.

I needed to configure the default instance of SQL Server for Azure local development. So I searched online for “Azure DSInit” and the first hit shows how to invoke the command.

http://msdn.microsoft.com/en-us/library/dd179457.aspx

To configure development storage against the default SQL Server instance:

DSInit /sqlInstance:.

The program DSInit.exe is found in the Azure SDK:

C:\Program Files\Windows Azure SDK\v1.0\bin\devstore\

Once I ran that program to initialize the local development app storage, I was off and running with my cloud development tasks.

Friday, August 20, 2010 3:02:26 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Azure | SQL Server | Visual Studio
 Thursday, August 19, 2010

With some spare time on my hands, I took at look at the improved JavaScript SDK from Facebook.

In the past, I really disliked Facebook coding because the developer experience was really hard. The documentation was wrong or obsolete, the sample code libraries rarely worked and it was really frustrating.

Now with the push towards the JavaScript SDK, things are smooth sailing! Here’s the reference: http://developers.facebook.com/docs/reference/javascript/

It’s super easy to sign in to Facebook from my external site and remove the need to have my own authentication system. I’m always a fan of having one less username and password to remember. The following sample page can be used to connect to your facebook application and do a couple of simple tasks like retrieving your name. This Facebook code comes right out of the JavaScript SDK reference.

The 5 steps to set up this code are embedded in comments of this HTML file:

   1: <!DOCTYPE html>
   2: <html lang="en">
   3: <head>
   4:     <title>Sample Facebook JavaScript SDK</title>
   5:  
   6:     <!--
   7:         How to get started:
   8:  
   9:             1. Register an app on http://www.facebook.com/developers/
  10:             2. Helpful table of facebook app settings can be found here: http://msdn.microsoft.com/en-us/windows/ee395718.aspx
  11:             3. Create a Web Forms or MVC project in Visual Studio
  12:             4. Inside Visual Studio, set the specific PORT NUMBER to match the Facebook app registration
  13:             5. Copy your app id from Facebook registration page to the JavaScript below
  14:     -->
  15:  
  16:     <style>
  17:         input {
  18:             width:130px;
  19:         }
  20:         table {
  21:             width:100%;
  22:         }
  23:         .left {
  24:             width:200px;
  25:             vertical-align:top;
  26:             text-align:left;
  27:         }
  28:         .right {
  29:             vertical-align:top;
  30:             text-align:left;
  31:         }
  32:     </style>
  33: </head>
  34: <body>
  35:  
  36:     <table>
  37:         <tr>
  38:             <td class="left">
  39:                 <ol>
  40:                     <li>
  41:                         <input id="getLoginStatusButton" type=button value="Get Login Status" />
  42:                     </li>
  43:                     <li>
  44:                         <input id="loginButton" type=button value="Login" />
  45:                     </li>
  46:                     <li>
  47:                         <input id="getName" type="button" value="Get Name" />
  48:                     </li>
  49:                     <li>
  50:                         <input id="logoutButton" type="button" value="Logout" />
  51:                     </li>
  52:                 </ol>
  53:             </td>
  54:             <td class="right">
  55:                 <h2>Messages</h2>
  56:                 <ol id="messages"></ol>
  57:             </td>
  58:         </tr>
  59:     </table>
  60:     
  61:     <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js"></script>
  62:     <script type="text/javascript">
  63:  
  64:         window.fbAsyncInit = function () {
  65:             FB.init({
  66:                 appId: '[YOUR APP ID GOES HERE]',
  67:                 status: true, 
  68:                 cookie: true,
  69:                 xfbml: true
  70:             });
  71:         };
  72:  
  73:         $(function () {
  74:  
  75:             $('#getLoginStatusButton').click(function () {
  76:                 console.log('inside button click event');
  77:                 FB.getLoginStatus(function (response) {
  78:                     if (response.session) {
  79:                         // logged in and connected user, someone you know
  80:                         $('#messages').append('<li>logged in; access token is ' + response.session.access_token + '</li>');
  81:                     } else {
  82:                         // no user session available, someone you dont know
  83:                         $('#messages').append('<li>not logged in</li>');
  84:                     }
  85:                 });
  86:             });
  87:  
  88:             $('#loginButton').click(function () {
  89:  
  90:                 FB.login(function (response) {
  91:                     if (response.session) {
  92:                         // user successfully logged in
  93:                         $('#messages').append('<li>successful log in; access token is ' + response.session.access_token + '</li>');
  94:                     } else {
  95:                         // user cancelled login
  96:                         $('#messages').append('<li>cancelled logged in</li>');
  97:                     }
  98:                 });
  99:  
 100:             });
 101:  
 102:             $('#getName').click(function () {
 103:  
 104:                 FB.api('/me', function (response) {
 105:                     $('#messages').append('<li>' + response.name + '</li>');
 106:                 });
 107:  
 108:             });
 109:  
 110:             $('#logoutButton').click(function () {
 111:  
 112:                 FB.logout(function (response) {
 113:                     $('#messages').append('<li>logged out</li>');
 114:                 });
 115:  
 116:             });
 117:  
 118:         });
 119:  
 120:     </script>
 121:     
 122:     <div id="fb-root">
 123:         <script src="http://connect.facebook.net/en_US/all.js" type="text/javascript"></script>
 124:     </div>
 125:  
 126: </body>
 127: </html>
Thursday, August 19, 2010 8:36:31 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Facebook | JavaScript | software
 Monday, August 16, 2010

I do (change your EXIF data)We just got back from a great weekend at the beach; my sister-in-law got married on the sands near Haystack Rock in Cannon Beach, OR. Really a great trip from start to finish.

My wife accepted the photographer duties and snapped nearly 2K photos with our Nikon D40 and a Nikon D60 that we borrowed from a friend. She had a zoom lens on one and a short lens on the other to avoid the need to change lenses in mid-moment. That part worked great.

The part that we overlooked was the current date/time on each camera; they were not synchronized. This will make sorting photos by time more difficult. No problem, I say, on the drive back home. I’m a developer and JPG files have meta info that I can manipulate, right?

It turns out that its not all that easy. Its actually a remarkable pain in the ass, hence this blog post (that’s the remark-able part).

I started with an online search and found Hanselman’s post about combining PowerShell with Omar Shahine’s library, ImageLibrary.dll that is evidently no longer accessible. Big phat 404 while trying to get that assembly.

Next, I did some searches specific to StackOverflow.com and saw some posts about how difficult reading/writing this meta data can be. At this point, I was beginning to wonder if my wife was going to hear a broken promise or not. It wasn’t looking good.

I found out that JPG files store meta data using EXIF, or Exchangeable Image File Format, a standard used by the digital still camera industry. It looked like C# 4.0 could crack this open, but I might have to do get my hands dirty with some bit shuffling. No worries, I was up for it. It didn’t look like there was a library out there that was going to fall into my lap, so I cracked open Visual Studio 2010 and saw what kind of trouble I could get into with Intellisense and a gin and tonic.

Let’s Take It Out for a Loop

I created a new project, loaded a Bitmap class with a sample JPG and iterated over the properties:

   1: using System;
   2: using System.Drawing;
   3:  
   4: namespace ReadExifInJPG
   5: {
   6:     class Program
   7:     {
   8:         static void Main()
   9:         {
  10:             var bitmap = new Bitmap("c:\\temp\\somephoto.jpg");
  11:  
  12:             foreach (var item in bitmap.PropertyItems)
  13:             {
  14:                 Console.WriteLine("Id: {0}, Type: {1}, Value: {2}", 
  15:                     item.Id, item.Type, item.Value);
  16:             }
  17:         }
  18:     }
  19: }

I had lots of properties in my sample photo:

output of simple iteration

I was thinking that this was going to be smooth sailing until I saw the values listed as byte arrays. Ugh. Then I read a EXIF specification that said the values can be stored in multiple formats and there are a ton of properties. I had over fifty on a single JPG image. I needed to find the property that told me if the given photo was taken by a Nikon D60 or a D40, and then change the date on the D60 images. Here’s the EXIF specifications: http://www.exif.org/specifications.html, and what a great document to read! Ugh.

I Just Want My Property

I found a list of property tags in numerical order on MSDN, note the id values are listed in hex, so 0x0132 (hex) equals 306 (decimal): http://msdn.microsoft.com/en-us/library/ms534418(VS.85).aspx

When you go about reading or changing a given property, there are three parts to consider:

  1. the id number that identifies the given property
  2. the type number which describes the data format
  3. the actual data, encoded in the least accessible format you could want

Here’s a list of numeric types that identify the corresponding data format:

  1. A Byte
  2. An array of Byte objects encoded as ASCII
  3. A 16-bit integer
  4. A 32-bit integer
  5. An array of two Byte objects that represent a rational number
  6. Not used
  7. Undefined
  8. Not used
  9. SLong
  10. SRational

Fortunately, everything I was interested in was of type 2, a byte array. This is a great MSDN article that gave me a bunch of tips on how to manipulate the EXIF data: http://msdn.microsoft.com/en-us/library/xddt0dz7.aspx

The property id of 306 holds the date of the photo, its a type 2 property, so the value is stored in a byte array. Here’s how to read the string value:

   1: var property = bitmap.GetPropertyItem(306);
   2: System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
   3: string value = encoding.GetString(property.Value);


Caution, Byte Counting Ahead

Note, the date properties that I’m interested in updating have values stored as byte arrays. More specifically, they’re exactly 20 bytes long. At first I wasn’t respecting that boundary and I just let .Net do it’s thing to convert my formatted date string into a byte array. When I tried to read the value back, it was empty because I overflowed the value.

After some noodling, I changed my code to write out exactly 20 bytes of data, starting at precisely 23 bytes into my new byte array, as follows.

   1: private void ChangeTimeStamp(Bitmap bitmap, int minutesToAdd)
   2: {
   3:     DateTime originalDateTime = GetOriginalDateTime(bitmap);
   4:  
   5:     var newDateTime = originalDateTime.AddMinutes(minutesToAdd);
   6:  
   7:     BinaryFormatter bf = new BinaryFormatter();
   8:     MemoryStream ms = new MemoryStream();
   9:     string formattedNewDateTime = newDateTime.ToString("yyyy:MM:dd HH:mm:ss");
  10:     bf.Serialize(ms, formattedNewDateTime);
  11:     ms.Seek(0, 0);
  12:  
  13:     var tempArray = ms.ToArray();
  14:  
  15:     byte[] byteArray = new byte[20];
  16:  
  17:     var x = 0;
  18:     for (int i = 23; i < (23 + 19); i++)
  19:     {
  20:         byteArray[x] = tempArray[i];
  21:         x++;
  22:     }
  23:  
  24:     SetNewDateTime(bitmap, byteArray, 306);
  25:     SetNewDateTime(bitmap, byteArray, 36867);
  26:     SetNewDateTime(bitmap, byteArray, 36868);
  27: }
 

Note, the previous code shows I’m writing the same date to three different property settings (306, 36867, and 36868). They all had the same date value, so I figured the best thing to do was keep them all in sync.

It Works on My Machine

So, I was finally done with the code and I tested it with a bunch of files on my machine. Worked great. I installed VS 2010 C# Express on my netbook so I could create a console application and run it on my wife’s laptop. I had been abusing a ASP.Net Web Forms application with Visual Web Developer Express on my netbook. I made a quick console app, copied it to my wife’s laptop and ka-pow! It no worky. Puzzled, I tried to think what was wrong for about 30 minutes. After a couple of assertions inserted into my code, I realized she had some non-JPG files in the folder and my program wasn’t filtering for only JPG files. Doh! I did manage to get in a quick “hey, it works on my machine” comment to my wife, but she didn’t think it was as funny as I did.

Nothing But The Code, The Whole Code, So Help Me .Net Runtime?

Here’s the final class I built, in “good enough” format. The entire coding time, not including the installation of VS 2010 Express on my netbook, or .Net 4 on my wife’s laptop was about 90 minutes. The console program just takes this class, passes in the source and destination directories, along with the number of minutes to adjust the timestamp on the photo.

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.IO;
   6: using System.Drawing;
   7: using System.Runtime.Serialization.Formatters.Binary;
   8:  
   9: namespace ImageMetaTool
  10: {
  11:     public class TimeEditorService
  12:     {
  13:         public void AdjustDateTime(string soureDir, string destDir, int minutesToAdd)
  14:         {
  15:             if (!Directory.Exists(soureDir))
  16:             {
  17:                 Console.WriteLine("Source directory does not exist.");
  18:                 return;
  19:             }
  20:  
  21:             if (!Directory.Exists(destDir))
  22:             {
  23:                 Console.WriteLine("Destination directory does not exist.");
  24:                 return;
  25:             }
  26:  
  27:             int x = 0;
  28:             var list = Directory.GetFiles(soureDir, "*.jpg");
  29:             foreach (var filename in list)
  30:             {
  31:                 Console.WriteLine("Processing image {0} of {1}; {2}", x, list.Length, filename);
  32:  
  33:                 using (var fs = File.OpenRead(filename))
  34:                 {
  35:                     var bitmap = new Bitmap(fs);
  36:                     ProcessFile(bitmap, minutesToAdd);
  37:                     bitmap.Save(Path.Combine(destDir, Path.GetFileName(filename)));
  38:                     x++;
  39:                 }
  40:             }
  41:  
  42:             Console.WriteLine("Processing completed.");
  43:         }
  44:  
  45:         private void ProcessFile(Bitmap bitmap, int minutesToAdd)
  46:         {
  47:             if (!IsD60(bitmap))
  48:                 return;
  49:  
  50:             ChangeTimeStamp(bitmap, minutesToAdd);
  51:         }
  52:  
  53:         private void ChangeTimeStamp(Bitmap bitmap, int minutesToAdd)
  54:         {
  55:             DateTime originalDateTime = GetOriginalDateTime(bitmap);
  56:  
  57:             var newDateTime = originalDateTime.AddMinutes(minutesToAdd);
  58:  
  59:             BinaryFormatter bf = new BinaryFormatter();
  60:             MemoryStream ms = new MemoryStream();
  61:             string formattedNewDateTime = newDateTime.ToString("yyyy:MM:dd HH:mm:ss");
  62:             bf.Serialize(ms, formattedNewDateTime);
  63:             ms.Seek(0, 0);
  64:  
  65:             var tempArray = ms.ToArray();
  66:  
  67:             byte[] byteArray = new byte[20];
  68:  
  69:             var x = 0;
  70:             for (int i = 23; i < (23 + 19); i++)
  71:             {
  72:                 byteArray[x] = tempArray[i];
  73:                 x++;
  74:             }
  75:  
  76:             SetNewDateTime(bitmap, byteArray, 306);
  77:             SetNewDateTime(bitmap, byteArray, 36867);
  78:             SetNewDateTime(bitmap, byteArray, 36868);
  79:         }
  80:  
  81:         private void SetNewDateTime(Bitmap bitmap, byte[] newDateTime, int propertyNumber)
  82:         {
  83:             var property = bitmap.GetPropertyItem(propertyNumber);
  84:             property.Value = newDateTime;
  85:  
  86:             bitmap.SetPropertyItem(property);
  87:         }
  88:  
  89:         private DateTime GetOriginalDateTime(Bitmap bitmap)
  90:         {
  91:             var property = bitmap.GetPropertyItem(306);
  92:  
  93:             System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
  94:             string value = encoding.GetString(property.Value);
  95:  
  96:             string value2 = value.Split(' ')[0].Replace(":", "/") + " " + value.Split(' ')[1];
  97:  
  98:             return DateTime.Parse(value2); //2010:08:14 14:23:14
  99:         }
 100:  
 101:         private bool IsD60(Bitmap bitmap)
 102:         {
 103:             var modelProperty = bitmap.GetPropertyItem(272);
 104:  
 105:             System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
 106:             string modelName = encoding.GetString(modelProperty.Value);
 107:  
 108:             if (modelName.Contains("D60")) // NIKON D60
 109:                 return true;
 110:  
 111:             return false;
 112:         }
 113:     }
 114: }

 

I hope you have a good time futzing with your EXIF data!

Monday, August 16, 2010 10:36:13 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
fun | software | utilities
 Thursday, August 05, 2010

I had to search around again for how to create a large file for testing; time for a blog post.

When you need to work with a large file for testing, fsutil.exe (baked into Windows) has a cool feature for making a large file in a snap. This is useful for when you test your app’s ability to move and edit local files or simply upload a big document.

Here’s the command for creating a 20MB file:

fsutil file createnew c:\temp\largefile.txt 20000000
Thursday, August 05, 2010 1:40:21 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
utilities | windows
 Friday, May 07, 2010

I used the simple thumbs up button inside Visual Studio 2010 the other day to send a comment to the authors of Pex, a white box testing framework from Microsoft Research.

Pex and Moles often go together in a test project. Moles can be used to mock any .Net method; a feat that few mocking frameworks can do. Most are limited to virtual methods and interfaces.

Pex is an abbreviation of "Program Explorer"; that was pretty clear. In my comment, I asked them why they used the name "Moles".

Peli de Halleux replied and said "Moles just stands for the little animal that shows up in your garden (coming from nowhere). Is it not an acronym."

Ha! Sometimes a name is just a name. Its very interesting too. I'm having a blast learning more about it.

Friday, May 07, 2010 1:36:17 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
pex | testing
 Friday, February 26, 2010
I was installing VS 2010 RC on a box today and noticed the window for default environment settings. I can't help but wonder what I'm missing when I make a selection. Do the other environments have some cool feature? Did I just pick the n00b environment? I wish there was a setting that was just titled "Everything, and Then Some". Here are the options and descriptions:

General Development Settings

Choose Default Environment Settings

Project Management Settings

Choose Default Environment Settings

Visual Basic Development Settings

Choose Default Environment Settings

Visual C# Development Settings

Choose Default Environment Settings

Visual C++ Development Settings

Choose Default Environment Settings

F# Development Settings

Choose Default Environment Settings

Web Development

Choose Default Environment Settings

Web Development (Code Only)

Choose Default Environment Settings

Friday, February 26, 2010 10:19:03 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Visual Studio
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2012
Andrew Hay
Sign In
All Content © 2012, Andrew Hay
DasBlog theme 'Business' created by Christoph De Baene (delarou)