Integrating Picasa Web Albums into my Website

image A friend of mine recently moved his website's photo gallery to Picasa Web Albums. I told him that I had considered doing the same except some of the people that visit my site (mostly relatives) already know the URL for my photo gallery. Although I knew that Picasa Web Albums provides a lot of convenient features, I did not want to lose the photo gallery feature in my website. It turned out that I did not know enough of what Picasa has to offer!

I spent some time last weekend looking further into Picasa Web Albums. I started my research with the expectation that I may have to do some hacking (like figuring out URL conventions of the Picasa Web Albums website) but I was pleasantly surprised to find that there is a Picasa Web Albums Data API. It took a few short hours on Sunday night to hook up my website's existing (ASP .NET) photo gallery logic to point to my Picasa Web Albums. Google has done a great job in designing their API and coming up with reference documentation that got me started very quickly. Simply amazing!

My Picasa Web Albums are now integrated into my website. With Picasa Web Albums integeration, my photo gallery has been upgraded to:

  1. Better conversion of photos for web viewing
  2. Flash Slideshow
  3. Better metadata support (I used to maintain an XML file in each album folder to specify album metadata (e.g., title, description) to my web application)
  4. Enterprise level storage
  5. All the features of Picasa Web Albums (via a link in the slideshow that brings you to the Picasa Website). Features like:
    • Captions
    • Download
    • Link
    • Geo-tagging
    • Order Prints
    • Share Album

I don't have any code to share as the API, as I mentioned above, is very well documented. Here's some extra information:

  1. Support for .NET, Java, PHP, and Python
  2. My use case was simple --- to query for all albums in my account. The API exposes a lot more features like album maintenance (e.g., Add, update, delete, and tag photos)

Links:

  1. Overview of the API: http://code.google.com/apis/picasaweb/overview.html
  2. Picasa Web Albums Data API Developer's Guide: http://code.google.com/apis/picasaweb/developers_guide_protocol.html
Technorati Tags:

Set Network Adapter DNS Server Programmatically Using C#

IPv4 PropertiesI needed an application that could set the DNS server properties on a computer's enabled network adapters. This will make our home laptops automatically adapt to the network they are connected to. This is especially useful for my kids when they bring their laptops to a friend's house --- it avoids the extra effort for my oldest child to change the settings manually (yes, she has learned how to do it!). I know there are some applications out there that already provide this feature and more but I had a simple use case, some time, and no budget to pay for third-party software.

This was a relatively quick project for me; took less than an hour. The application is designed to do the following:

  1. Ping a given DNS Server (specified through the application configuration file).
  2. If the ping is successful, the application will set the preferred DNS server of all enabled network adapters to the specified DNS Server.
  3. If the ping is not successful, the application will reset the DNS settings on all network adapters; i.e., set the adapters to 'Obtain DNS server address automatically'.

Here's are screenshots of sample runs of the application:

Sample Run - DNS Server Found

Sample Run - DNS Server Not Found

Notes:

  1. I created a console application with the intention for it to run at system startup through a scheduled task. With this in mind, I included a 'SilentMode' setting in the configuration file that can be set to True to exit when the operation is complete (useful if it is a scheduled task) or False to wait for a keypress before exiting (useful for debugging).
  2. I used the System.Net.NetworkInformation.Ping class to ping the server. A PingException is thrown on the Send method if the operation was not successful, including the case when the host is unknown. I don't know why the PingReply.Status returned (which is an enumeration) could not include a value for this case. I had to do a try-catch to work with this design.
  3. I used Windows Management Instrumentation (WMI) off of the System.Management namespace to set the DNS server address. I wish I could have used System.Net.NetworkInformation namespace but the values exposed by the classes of interest are read-only.
  4. I tested this on Windows Vista 32-bit and Windows XP.

You can download the Visual Studio 2008 solution including Release binaries at: http://aocampo.com/Downloads/SetNetworkProperties.zip.

Technorati Tags: , , ,

Workaround for ASP.Net Compilation Error "'...Profile' hides inherited member"

Compilation ErrorThe following ASP.Net error kept me up for two nights:

Compiler Error Message: CS0108: Warning as Error: 'ASP.controllibrary_toolbar_ascx.Profile' hides inherited member 'MyTestApp.ControlLibrary.MainToolbar.Profile'. Use the new keyword if hiding was intended.

This error occurred on an ASP.Net 1.1 website that I upgraded to 2.0. The error does not occur when the target Framework version is 3.5. I am able to run the application without any problems using Visual Studio but the error occurs when deployed to IIS.

Some forum posts suggested a quick workaround to set the profile element in web.config to false but that will only work if you don't need that feature. Furthermore, in my case, it would get me past the error but throw a similar one, this time for the ApplicationInstance property as in:

c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\MyTestApp\5fcdec59\c0c270f4\App_Web_toolbar.ascx.100f8869.uvdkesxa.0.cs(124,35): error CS0108: Warning as Error: 'ASP.controllibrary_toolbar_ascx.ApplicationInstance' hides inherited member 'MyTestApp.ControlLibrary.MainToolbar.ApplicationInstance'. Use the new keyword if hiding was intended.

The workaround that I found was to uncheck the 'Allow this precompiled site to be updatable' option in the 'Publish Web Site' dialog when publishing the application; I am fine with unchecking this option.

Publish Web Site

Technorati Tags:

Getting Caller ID Using Visual Basic .Net

We have been putting-up with our home VoIP phone these past few months (the battery only last a few minutes) and we decided to replace it recently. I ordered a phone adapter from our VoIP provider instead of the proprietary phone set (which we had). I found this as an opportunity to write an application to add to our network utilities. I planned to write a "Caller ID Notifier" application consisting of two components: a server that listens to the modem port for incoming calls and broadcasts the information over the network, and a client that listens to the broadcast messages. I know there are quite a few applications out there that can do this but I wanted to build it myself because I have some other features I plan to implement in the future.

Before I go into the details of my solution, let me point out that one of the easiest ways that I have found to work with telephony in .Net is by using the C4F Developer Kit. The C4F Developer Kit contains a telephony API among many other components that provide useful wrappers to specialized aspects of Windows programming. The telephony API exposes a rich set of interfaces for receiving and making phone calls using the computer. I initially planned to use this as it seemed to be, and may have been, the simplest solution to the problem that I wanted to solve. Unfortunately for me, the functions for outgoing calls worked but I never got incoming calls to get detected on several machines with different modems.

It turns out that getting caller ID is quite simple (note: the phone service, as well as  the modem must have a caller ID feature). Generally, you would need to:

  1. Open the modem serial port.
  2. Add an event handler to the port's DataReceived event.
  3. Initialize the port to activate caller ID.
  4. Handle the DataReceived event by parsing the data.

I had only a few hours to prototype this project and I leaned on VB.Net's My namespace to save time on working with the SerialPort class. You can download the sample class library I wrote at: http://www.aocampo.com/Downloads/CallerIdListener.zip.

Here's a sample code snippet of using the class library:

Imports CallerIdLib

Module Main
    Private WithEvents m_callerIdListener As CallerIdListener

    Sub Main()
        'initialize the modem port
        m_callerIdListener = New CallerIdListener("COM3", 9600, _
                            IO.Ports.Parity.None, 8, IO.Ports.StopBits.One)
        'enable caller id
        m_callerIdListener.Write("AT+VCID=1" + vbCrLf)
        'loop indefinitely to listen to the port
        While True
            System.Threading.Thread.Sleep(50)
        End While
    End Sub

    Private Sub m_callerIdListener_CallerIdInfoReceived(ByVal sender As Object, _
        ByVal e As CallerIdLib.CallerIdListener.CallerIdInfoReceivedEventArgs) _
        Handles m_callerIdListener.CallerIdInfoReceived

        Console.WriteLine("Caller ID Information")
        With e.CallerIdInfo
            Console.WriteLine("Date: " + .DateTime.ToString("MMMM dd, yyyy hh:mm tt"))
            Console.WriteLine("Name: " + .Name)
            Console.WriteLine("Number: " + .Number)
        End With
    End Sub

    Private Sub m_callerIdListener_RingReceived(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles m_callerIdListener.RingReceived

        Console.WriteLine("Ring!")
    End Sub
End Module

Here are some notes on the solution:

  1. It seems that AT#CID=1 is the most common modem command to activate caller ID. However, this does not work for two of my modems. My modems respond to AT+VCID=1 instead. Here are other commands I found that apparently may be recognized by other modems:
    • AT%CCID=1
    • AT#CC1
    • AT*IC1
  2. I wrote a simple function to parse data received from the port. I categorize the data into three types --- a ring, caller id information, and everything else.
  3. Here's a sample expected caller ID data received on the modem port:
    DATE = 0413
    TIME = 1004
    NMBR = 15555555555
    NAME = JOHN DOE
  4. I used a background worker after opening the port to start listening. This wasn't necessary in my initial tests but when I started to consume the class library in a real application, I got cross-threading exceptions.

My caller ID application has been running on my home network for a few weeks now but I am still tweaking it. It has been quite useful having caller ID displayed on our computers. I will make it available for download when I am satisfied with its feature set.

Links:

  1. C4F Developer Kit: http://www.codeplex.com/C4FDevKit
  2. This link helped me troubleshoot my modem problem (provides free software too): http://www.yes-tele.com/modem.html
  3. Here's a nice free caller ID application: http://www.phonetray.com/ptfree_download.htm
Technorati Tags: ,

Hard Drive Upgrade Horror

There are days when I think I am cursed with some weird kind of luck; yesterday was one of them.

My wife and I decided to purchase a new hard drive last week because our media collection has gotten quite large over the past few months (we have been converting our DVD and CD collection to disc images for easy access and playback on computers in our home network). I decided to get a 1TB (2 x 500 GB) external hard drive since our main file server could no longer take any internally (it already has six of them in there). The order arrived yesterday.

My estimate to finish the task of attaching and configuring the device was a maximum of half an hour. Unfortunately, it took me about 18 hours to do this because I had to work around many other issues that, sadly, were not directly related to the installation itself.

I was able to connect the device to my file server (via eSATA) within about 15 minutes from the time I unboxed it. The computer started booting up when I turned it on but it was unusually slow. It seemed like Windows was trying to reconfigure itself due to the hardware changes. I was finally able to login after about five minutes. I immediately installed the device's software after that. The installation required a reboot so I proceeded to restart the machine. It won't start when I powered it up --- "DISK BOOT FAILURE" or something like that, it said. I got that familiar sinking feeling upon reading the error message. I knew it was going to be a long night.

For some reason, it seemed like the machine did not like the new hardware/software combination. After a few failed reboots I hit a dead end --- "NTDETECT failed" error. The installation somehow caused my system partition (distributed among multiple SATA drives) to get corrupted; and horror of horrors, our critical data was in that partition! Although the partition was configured to be mirrored (but not backed up!), I wanted to avoid the painful process of recovering the data by swapping hard drives (I had not noted which drives were the source and which ones were the mirror). I wanted to solve the problem by refreshing the corrupt system files. Here's what happened:

  • I tried to reinstall Windows Sever 2003. When it came time to confirm that I wanted to install on the existing partition, the application warned me that there was no more room in the hard drive and that I needed to reformat it. I couldn't do that. I thought maybe the drive was corrupt; I planned to do a chkdsk.
  • I tried to get into the Recovery Console many times using the Windows Server 2003 installation but it kept telling me that it could not find any partitions. It turns out that my floppy drive was also defective and would intermittently loosen its 'grip' on the floppy inside and so the installation could not load the SATA driver from the floppy. This took me about an hour to figure out.
  • When I finally got into the Recovery Console, I did a fixboot first. That didn't solve it. I tried running chkdsk next. I got an error saying autochk.exe could not be found. This file was expected to be in C:\Windows. Unfortunately for me, the C: drive was empty. I was able to run chkdsk by locating autochk.exe on the Windows Server 2003 install disc. I was relieved to see a directory listing on the C: drive after running chkdsk. It found errors on the partition and restored the file system. I rebooted, hopeful that this time Windows will start. This took me about 45 minutes.
  • On the next reboot, I got a system startup error, notifying me that one of my mirrored disks needs to be duplicated because it was out of sync (I had tried to inspect the disks and disconnected/reconnected cables at some point and this apparently caused them to be out of sync). I ignored the error and proceeded to let Windows start. Windows could not completely start. It reported that some system files were missing. I rebooted again and planned to do a repair installation.
  • I proceeded with the repair installation and that took about 45 minutes. When the installation finished and the machine rebooted, Windows could only get as far as the 'Applying settings' dialog and would not get to the 'Press Ctrl-Alt-Del to login' dialog. Instead, it displayed an empty desktop and startup seemed to be stalled. I thought the issue might be related to the 'mirrored disks out of sync' problem that I have been ignoring so I did another reboot to address that issue.
  • I let my mirrored disks sync with each other. The process took about two hours. I rebooted again, hopeful that this time, Windows will follow-through to the 'Press Ctrl-Alt-Del to login' dialog. No luck. The weird but fortunate thing was that I could connect to the server's drives over the network even at its current state. Since it was about 2:30 in the morning and I was tired, I decided that I would copy the important files from the drive and let the process finish through the morning as I slept. I left the copy process (which was unusually very slow) and went to bed.
  • As I awoke in the morning, I decided that I would 'bite the bullet' and do a clean Windows install. I wanted to avoid this if possible so I would not have to go through the painful process of reconfiguring the server and reinstalling all applications. Unfortunately, it seemed like the only option at this point. I reinstalled Windows.
  • Windows booted up normally after the reinstall. I was able to reinstall all the device drivers, surprisingly, relatively quickly and easily. The process took about an hour and a half. After everything was done, I decided to connect the new hard drive, via USB this time, to make some observations and avoid any further hassles (I had tried the device's USB connection on my other machine and it was 'plug-and-play'). All seemed well. I shutdown the machine.
  • After connecting the new hard drive, I started to power up the computer. It wouldn't start! I couldn't believe it. It seemed like the power supply was dead. I couldn't believe it because I just replaced this power supply last week. This could not be happening! I tried to remain calm but at the same time could not help but anticipate my next ordeal if it indeed was a problem with the power supply. Fortunately, I had the sense to test the power supply first before giving it up for dad. The power supply tester indicated that it was good. After reconnecting the power supply to the motherboard, the computer started up! The worst was over. It took me about another four hours to reconfigure the file server.

Lessons learned/relearned:

  • Although RAID provides much hope in disaster recovery, recovering from disaster is not as simple as I thought. I should still have a data backup procedure.
  • My disk storage configuration is a mirrored array of disks that form one large partition. After this experience, I will break up the mirrored array into individual partitions, one per hard drive; that will make it straightforward to read the hard drives individually from other machines and simplify my disaster recovery plan.
  • Speaking of backup, I have started a scheduled backup for Active Directory. 
Technorati Tags:

Silverlight 2 Beta 1 Released

Microsoft has released Beta 1 of Silverlight 2. This version is a major upgrade to its features (from version 1.1 Alpha) and I am excited to get some hands-on coding, trying to convert some of my old web and WPF applications.

Here are some useful links:

Technorati Tags:

Replacing a Power Supply

My file server got sick last Tuesday. It would not start --- turning the power on would make the keyboard indicator lights go on for a split-second, then "nothing". I was very worried since this is our home's main file server. This is where we store our documents, media, and passwords (encrypted of course). It is also our print server.

I suspected (and hoped) that it was just the power supply that was busted. I did some web searches on diagnosing and replacing a bad power supply. Thankfully, I got a lot of information to be confident enough to take on this problem myself without bringing the machine to a repair service (I have some experience with hardware but I have never touched the power supply or motherboard). It turned out that the power supply was indeed defective and I had a relatively pleasant experience with replacing the part. Here are some lessons learned:

  1. Power supply testers will help you easily diagnose the health of a power supply --- just plug the power supply's motherboard connector to the tester and the tester will show you the state of the power supply. You can also use a multimeter to test the power supply but the process is more involved and technical. I used a power supply tester.
  2. Order the same model power supply if possible. Although power supplies perform the same function given the same specs, consider the issue of size when making a replacement; i.e., a power supply with the same specifications may not fit your machine because of its dimensions. Fortunately for me, I was able to order a power supply with the same model as my machine.
  3. Take "before" pictures before taking things apart. This was very invaluable. In my case, I took a video so that I could voice-annotate the shot that I was taking. I accidentally pulled off cables two times in the replacement process and it helped a lot to just go back to the video and refer to the machine's previous state to undo things.

Note to self: The file server is not backed-up, only mirrored. Address this issue as soon as possible.

Technorati Tags:

Upgrading to 64-bit Vista and the WOW6432node

My wife recently got a new computer, a top-of-the-line gaming laptop with the 'works' running 32-bit Windows Vista Home on a 64-bit processor.  I failed to tell her at least two important 'must-haves' before she finalized her order:

  1. To get Vista Ultimate to enable the computer to join our home network (we have a domain).
  2. To get 64-bit Vista Ultimate to take advantage of the hardware.

Had I told her these, a painful and costly upgrade could have been avoided. And so the upgrade adventure started shortly after her laptop arrived...

The first problem to solve was to get a good deal on Vista Ultimate. The cost of a standard license is around $400 for the full package, and $260 for the upgrade. We got an OEM license for about $190.

The next problem was installing Vista and updating all the drivers. The Vista install was not very problematic as I already had an idea of more or less how much time that would take, having gone through the process for my desktop. However, updating the drivers was the challenging part and took a lot of time due to the following reasons:

  1. Not all drivers are available for 64-bit systems (which made me wonder how the laptop would have been configured had my wife ordered the 64-bit operating system pre-installed). It took some trial-and-error to find matching drivers for the ones that did not have 64-bit versions.
  2. The support website was very vague in its description of some of its drivers.
  3. Windows provides a very generic description of a device that failed to install and so you need to first figure out what the device is (its type brand and model) before you could search for the driver.
  4. Windows is not very helpful in providing a detailed description of an error encountered when a device failed to install.

After all the drivers were installed, the manufacturer's software needed to be installed. Again, some applications are still not available for 64-bit systems and some needed some tweaking. After a few days of configuring the computer (my wife did most of this), there was one final piece of software that needed to be fixed. The computer has special keys that launch applications via software. However, the 'email' button was programmed to run Windows Mail and the software does not present any means to change this (my wife wanted to map this to Outlook). I found a fix for the problem after some web searching. It required editing the registry. Unfortunately, it did not work for me (although it seems to have worked for everyone else based on the forums that I have read). I was convinced that the solution prescribed was effective and that I was just missing something; the only thing that I could think of was that the computer was running 64-bit Vista. This is where 'WOW6432node' comes in.

A search on the web about editing the registry for 64-bit Windows somehow led me to 'WOW6432node' (side note: WOW64 stands for Windows-on-Windows 64-bit). It turns out that 64-bit Windows performs 'registry redirection' where it presents 32-bit programs another view of the registry to make them co-exist with 64-bit applications. This is done by redirecting 32-bit applications to the 'HKEY_LOCAL_MACHINE\Software\WOW6432node' sub key when they request for 'HKEY_LOCAL_MACHINE\Software'. To make a long story short, I had to slightly modify the instructions I found to incorporate WOW6432node because 32-bit software was concerned. And so, instead of modifying 'HKEY_LOCAL_MACHINE\Software\TheApp', for example, I had to target 'HKEY_LOCAL_MACHINE\Software\WOW6432node\TheApp'. The application worked after I made the adjustments.

My wife's computer is now running smoothly although generally I think there will be that possibility of finding incompatible hardware or software from time to time in the (hopefully) short term.

Links:

  1. Registry changes in x64-based versions of Windows Server 2003 and in Windows XP Professional x64 Edition
  2. WOW64 on Wikipedia

Syndicated Client Experiences Starter Kit Released

Microsoft has just released the Syndicated Client Experiences (SCE) Starter Kit, enabling developers to easily create occasionally-connected WPF applications that synchronize content over the web.

The Starter Kit's website contains a lot of content including extensive documentation, training videos and tutorials to get developers started building their own SCE application.

I have been anxiously anticipating the release of Silverlight 1.1 to perform a long-overdue upgrade of  BusterNews but it looks like it is worth trying the SCE Starter Kit as an intermediate step towards that goal.

Link to the SCE Starter Kit: http://windowsclient.net/wpf/starter-kits/sce.aspx

Technorati Tags:

First Look: Microsoft Parallel Extensions December 2007 CTP

Microsoft released the Parallel Extensions to the .Net 3.5 (December 2007 CTP) earlier this month. I was excited to try this out as I could see many cases where this would be very useful in optimizing the performance of applications. Writing a quick test application was surprisingly easy --- the concepts of the library are not hard to understand, and implementing the code is very straightforward.

I wrote a simple Windows Forms application (in C#) that generates a list of random numbers based on the number of items specified in a form (as in the following image).

 image

I then implemented generating the list in two ways: one using the normal, single threaded method, and one using parallel processing. Following is the main code used for the application. Note that I put a 5 millisecond sleep on the thread when generating a random number to add significant processing time for easier comparison of results.

        private void btnOK_Click(object sender, EventArgs e)
        {
            DateTime startTime;
            DateTime endTime;

            //perform normal processing
            MessageBox.Show("Press a key to start normal processing.");
            startTime = DateTime.Now;
            GetRandomNumbers(Convert.ToInt32(nudLoopCount.Value));
            endTime = DateTime.Now;
            double duration = endTime.Subtract(startTime).TotalSeconds;
            
            //perform parallel processing
            MessageBox.Show("Press a key to start parallel processing.");
            startTime = DateTime.Now;
            ParallelGetRandomNumbers(Convert.ToInt32(nudLoopCount.Value));
            endTime = DateTime.Now;
            double parallelDuration = endTime.Subtract(startTime).TotalSeconds;

            string result = string.Format("Normal processing took {0} seconds.\r\n", duration);
            result += string.Format("Parallel processing took {0} seconds.\r\n", parallelDuration);
            MessageBox.Show(result);
        }

        private List<int> GetRandomNumbers(int count)
        {
            List<int> randomNumbers = new List<int>();
            for (int i = 0; i < count; i++)
            {
                randomNumbers.Add(GetRandomNumber());
            }
            return randomNumbers;
        }

        private List<int> ParallelGetRandomNumbers(int count)
        {            
            List<int> randomNumbers = new List<int>();
            Parallel.For(1, count+1, delegate(int i)
            {
                int randomNumber = GetRandomNumber();
                lock (randomNumbers)
                {
                    randomNumbers.Add(randomNumber);   
                }                
            });            
            return randomNumbers;
        }

        private int GetRandomNumber()
        {
            //waste some time
            Thread.Sleep(5);
            return rnd.Next();
        }

Here are the results from running the application for 2,500 iterations. Note that my machine has a dual-core CPU.

image

Quick Lessons:

  1. Set a reference to System.Threading to use the library.
  2. Keep in mind that iterations run independently so be careful with the use of resources. My original code did not contain a lock on the the list that the function was populating and led to an incomplete list at the end of the function (in the example above, the list yielded between 2489 to 2498 items). Surprisingly, no exceptions were raised using this technique and I wouldn't have found the discrepancy if I had not checked the results.
  3. The sample above uses a very simple function. Another test application I wrote performed relatively more complex operations (like I/O) and some tests did not yield a significant difference in processing time no matter how I varied the values used as parameters. In fact, in some cases, on my dual-core machine, the parallel processing approach took longer to process, presumably due to  initialization of threads and such. I had to move the application to my old 6-CPU server to find an obvious performance improvement.

Links: