Get Web Browser History in C# - Google Chrome

This post is a sort-of sequel to my previous blog post on getting web browser history in C#. Google Chrome is the browser I had missed in the last blog post and I must say, it was quite interesting to code this up. Just like Firefox, Chrome also uses an SQLite Database, but uses a structure quite different than that of Firefox. More details about it here. As for Firefox, a very nice article is here. Before you see the code, below are a few interesting points to note about Chrome:
  1. The History file for Chrome is an SQLite database, but without the .sqlite extension. In fact, it does not have any extension. Its name is just History.
  2. Google Chrome stores last visited times in UTC format. UTC time is basically the time elapsed since Jan 1, 1601. Unlike the UTC format for Windows file times which is in nanoseconds, Chrome stores the times in microseconds.
  3. Also, the times are as per the GMT time zone. So, to get local times, TimeZoneInfo class provided by the .NET framework must be used.
Below is a tiny code snippet for getting Chrome history in C#:

  1. public class HistoryItem
  2. {
  3. public string URL { get; set; }
  4. public string Title { get; set; }
  5. public DateTime VisitedTime { get; set; }
  6. }
  7. static class Program
  8. {
  9. static void Main()
  10. {
  11. string chromeHistoryFile = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData))
  12. + @"\Google\Chrome\User Data\Default\History";
  13. if (File.Exists(chromeHistoryFile))
  14. {
  15. SQLiteConnection connection = new SQLiteConnection
  16. ("Data Source=" + chromeHistoryFile + ";Version=3;New=False;Compress=True;");
  17. connection.Open();
  18. DataSet dataset = new DataSet();
  19. SQLiteDataAdapter adapter = new SQLiteDataAdapter
  20. ("select * from urls order by last_visit_time desc", connection);
  21. adapter.Fill(dataset);
  22. if (dataset != null && dataset.Tables.Count > 0 & dataset.Tables[0] != null)
  23. {
  24. DataTable dt = dataset.Tables[0];
  25. allHistoryItems = new List<HistoryItem>();
  26. foreach (DataRow historyRow in dt.Rows)
  27. {
  28. HistoryItem historyItem = new HistoryItem();
  29. {
  30. URL = Convert.ToString(historyRow["url"]),
  31. Title = Convert.ToString(historyRow["title"])
  32. };
  33. // Chrome stores time elapsed since Jan 1, 1601 (UTC format) in microseconds
  34. long utcMicroSeconds = Convert.ToInt64(historyRow["last_visit_time"]);
  35. // Windows file time UTC is in nanoseconds, so multiplying by 10
  36. DateTime gmtTime = DateTime.FromFileTimeUtc(10 * utcMicroSeconds);
  37. // Converting to local time
  38. DateTime localTime = TimeZoneInfo.ConvertTimeFromUtc(gmtTime, TimeZoneInfo.Local);
  39. historyItem.VisitedTime = localTime;
  40. allHistoryItems.Add(historyItem);
  41. }
  42. }
  43. }
  44. }
  45. }


Using extension methods in C#

One of the most exciting features of the .NET Framework is Extension Methods. Extension methods allow you to extend the functionality of a class without modifying the code of the class itself. This allows you to write methods for a class anywhere outside the class. This is especially useful when there is no access to the code of the original class. For example, you can write a method to extend the System.String class without actually changing the code of the System.String class. (You can't actually change the code of the System.String class) This method will be a static method residing in a static class anywhere in your application.

Extension methods are defined as static methods, but are invoked as instance methods. This is achieved by prefixing the this keyword to the argument passed in the static method. For example, an extension method meant to remove vowels in a string would have a definition like:
public static string RemoveVowels(this string str)
and it would be invoked as:

  1. string str = "Hello";
  2. string newstr = str.RemoveVowels();

A better, more complete example should make matters clearer. In an earlier post, the DirectoryInfo class has been explained, and as has been pointed out, it does not have a method or property which returns its byte size unlike the FileInfo class. So, one can write a method to extend the DirectoryInfo class as under:

  1. public static class ExtensionMethods
  2. {
  3. public static long GetSize(this DirectoryInfo dir)
  4. {
  5. // Initialize to zero
  6. long size = 0;

  7. // Loop through all the subdirectories and files
  8. foreach(DirectoryInfo folder in dir.GetDirectories())
  9. size += folder.GetSize(); // Recursive call
  10. foreach(FileInfo file in dir.GetFiles())
  11. size += file.Length;

  12. return size;
  13. }
  14. }

So one can use this method as an instance method of any DirectoryInfo instance.

  1. DirectoryInfo dir = new DirectoryInfo(@"C:\WINDOWS");
  2. long sizeOfWindowsFolder = dir.GetSize();

Visual Studio 2008 provides IntelliSense support for extension methods, which makes the job of using extension methods very easy. Programmers vary in opinion about the concept of extension methods with most programmers terming it as "advantageous" while some programmers terming it as "a slap in the face of all serious software programmers". I say, as is with everything in technology, it's all up to you, sir!


A brief introduction to WPF

My previous post was an out-of-nowhere post on WPF (Windows Presentation Foundation), a technology not many developers know. Although you can easily know all there is to know "about" WPF on the web, learning to "use" WPF is more important and you need the proper resources for that. WPF applications can be developed in both Visual Studio 2008 and Expression Blend 2 and developing a complete WPF app requires expertise in both. Here are some resources which you can use:

A guided tour of WPF

The official WPF site

The MSDN resource for WPF

Here's my description of WPF:
WPF (Windows Presentation Foundation) is a programming model within the .NET Framework which allows you to build rich, compelling user interfaces. The highly-rated Silverlight platform for the Web is nothing but a subset of WPF. You can compare it to Windows Forms, although there is a world of difference between the two. In fact, WPF is best understood when compared to WinForms.
In a WinForms app, there are 1 or more "forms" which constitute the user interface. Each form is an instance of a class which derives from System.Windows.Forms.Form and its UI and codebehind are both in C#. So, there is a Form1.designer.cs and a Form1.cs both containing partial declarations of the class Form1.
In a WPF app, there are 1 or more "windows" which constitute the interface. Each window is an instance of a class which derives from System.Windows.Window. While the codebehind is in C#, the UI is in what is called XAML (Extensible Application Markup Language). So, there is a Window1.xaml containing pure XAML and a Window1.xaml.cs containing pure C#, both containing partial declarations of the class Window1.
XAML is an HTML-style language which allows you to layout the controls of a window just as you would layout elements on an HTML page. XAML provides what WinForms cannot provide - rich and highly customizable user experiences. XAML provides for custom ListBoxes, binding controls to XML and CLR data sources, animations through storyboards and plenty more. In fact, you will get a better idea once you actually start creating a WPF app in Visual Studio 2008.
Apart from Visual Studio 2008, there is a software called Expression Blend 2 which allows you to concentrate only on the UI through XAML. Its intuitive interface allows you to do whatever you wish with the user interface. WPF is a very good and recommended thing to learn for developers. Code hard, go pro!


Visual inheritance in WPF

My final year .NET project was a medium-weight WPF application which had some 8 windows in all. Creating the windows and customizing the interface in XAML was a breeze with Visual Studio 2008 and Expression Blend 2. Even coding up the window logic was not that tough, but halfway into the logic part, I realized that all my windows had some properties and methods in common. So the code could be shortened a great deal using inheritance.

Basically, the approach is simple. Create a class in a separate code file (say Blindow in Blindow.cs) which inherits from System.Windows.Window, add all the properties and method implementations and virtual methods you need to it, and make all your windows inherit from Blindow. So, our Blindow looks like this:

  1. namespace BlindApp.Windows
  2. {
  3. public class Blindow : Window
  4. {
  5. // all your properties and methods here
  6. }
  7. }

So, the class declarations for all the windows in the application now change from

public partial class Window1 : Window

public partial class Window1 : Blindow

All is fine and well, the code should compile correctly, but shockingly, it doesn't and throws up an error. Reason: Forgot the XAML. If you know WPF well, you must realize that the class declaration for Window1 in the codebehind is only partial. The remaining partial declaration lies in the XAML. So, this means one has to come up with a way to let XAML know that Window1 inherits from Blindow now.
The source of the error now lies here

<Window x:Class="BlindApp.Windows.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

So what we need to do to rectify the error is first add an XML namespace (with any name you like) for the CLR namespace. The CLR namespace for Window1 is BlindApp.Windows, so add a line as follows

<Window x:Class="BlindApp.Windows.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

You don't need to type out the clr-namespace: statement - Visual Studio 2008 provides IntelliSense for that. But, we are not done yet, an error will again pop up because we need to replace the first line itself. Now that the XML namespace has been created, this is how it's done:

<blinder:Blindow x:Class="BlindApp.Windows.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

Just like you could set Resources and Triggers with Window.Resources and Window.Triggers, you can do the same now with <blinder:blindow.resources> and <blinder:blindow.triggers>


MD5 and TripleDES encryption in .NET

The .NET Framework provides classes for MD5 hashing and TripleDES encryption, both of which, when used together with a good enough key, form a good cryptographic system for your application. There are a lot of scenarios one can think of where encryption can be put to good use in both Desktop and Web applications. The following code snippet provides functions for encryption and decryption using MD5 and TripleDES.

What you need to include in the usings list is System.Security.Cryptography and System.Text and you are good to go. Making the functions static is just one of the best practices.

  1. public class Utils
  2. {
  3. public static string Encrypt(string toEncrypt, string key, bool useHashing)
  4. {
  5. // Convert both strings to byte arrays - you can use encoding other than UTF8
  6. byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
  7. byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
  8. if (useHashing)
  9. {
  10. // Hash the key
  11. MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
  12. keyArray = hashmd5.ComputeHash(keyArray);
  13. hashmd5.Clear();
  14. }
  15. TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider
  16. {
  17. Key = keyArray,
  18. // The following line is controversial - follow the link below the snippet
  19. Mode = CipherMode.ECB,
  20. Padding = PaddingMode.PKCS7
  21. };
  22. ICryptoTransform cTransform = tdes.CreateEncryptor();
  23. // Transform and store in resultArray
  24. byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
  25. tdes.Clear();
  26. return Convert.ToBase64String(resultArray, 0, resultArray.Length);
  27. }
  28. public static string Decrypt(string toDecrypt, string key, bool useHashing)
  29. {
  30. byte[] keyArray = UTF8Encoding.UTF8.GetBytes(key);
  31. byte[] toDecryptArray = Convert.FromBase64String(toDecrypt);

  32. if (useHashing)
  33. {
  34. MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
  35. keyArray = hashmd5.ComputeHash(keyArray);
  36. hashmd5.Clear();
  37. }
  38. TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider
  39. {
  40. Key = keyArray,
  41. Mode = CipherMode.ECB,
  42. Padding = PaddingMode.PKCS7
  43. };

  44. ICryptoTransform cTransform = tdes.CreateDecryptor();
  45. byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0, toDecryptArray.Length);
  46. tdes.Clear();
  47. return UTF8Encoding.UTF8.GetString(resultArray);
  48. }
  49. }

FYI, here is the controversy.
As you can see, both functions are quite similar except for precisely 2 lines. The Clear() function on both occasions are required because MD5CryptoServiceProvider and TripleDESCryptoServiceProvider classes are "managed wrappers around unmanaged resources". Putting the Encrypt and Decrypt functions to use in your application would look like this:

  1. string encrypted = Utils.Encrypt("Password", "Key", true);
  2. string original = Utils.Decrypt(encrypted, "Key", true);

Just make sure that you use the same key in both the lines, wherever the lines may be (they may be in two different applications referencing a common dll containing these functions) and the same hashing parameter as well. Happy encryption!


Using FileUpload control in ASP.NET 2.0

The FileUpload control in ASP.NET 2.0 allows users to upload files onto the server. It is a familiar control on social networking sites and signup pages where users are required to upload images as profile photos or album pics. So, how exactly does one code up the process of uploading to the server? Read on.

The FileUpload control in ASP.NET 2.0 uploads the specified file at the specified path on the user's machine to a specified path on the web server. There are two approaches to this:
  1. SaveAs() method

  2. The simpler, zero-effort approach is to use the SaveAs() method of the FileUpload control itself. What you need to do is write this single line of code in the event handler of the Upload button on your WebForm.

    fileupload1.SaveAs(@"Uploads\" + fileupload1.FileName);

    The above code uploads the file to the Uploads folder on the web server.

  3. HttpPostedFile object

  4. The PostedFile property of the FileUpload control returns an HttpPostedFile object which can be used to obtain additional information about the file uploaded such as length in bytes (ContentLength property) and MIME type (ContentType property). It also returns a Stream object through its InputStream property which is exactly the property useful in uploading. Take a look at the code.

    1. HttpPostedFile uploadedFile = fileupload1.PostedFile;

    2. // Read all data from the file into a byte array
    3. int len = uploadedFile.ContentLength;
    4. byte[] data = new byte[len];
    5. uploadedFile.InputStream.Read(data, 0, len);

    6. // Create a new file and stream the bytes to it
    7. FileStream file = new FileStream(Server.MapPath(@"Uploads\" +
    8. fileupload1.FileName, FileMode.Create);
    9. file.Write(data, 0 , len);
    10. file.Close();

    The latter method is the one I invented because I had not known the SaveAs() method of the FileUpload control when I first used it. As for you, the choice is entirely in your hands.


(Ab)using Registry classes in .NET

The .NET Framework provides wonderful classes in the Microsoft.Win32 namespace to read and write values to any key or set of keys in the Windows Registry. These classes are not only wonderful but also dangerous because they have the potential to cause nasty and possibly irreversible changes to Windows. The following piece of code is DANGEROUS for your PC. So, do not run this code in your primary Windows Administrator account. Instead, create a dummy Windows Administrator account and run this code there. It's fun! And in fact there's so much else to do with the Registry!
Create a Windows Console-based app in Visual Studio 2008 and copy the following code in the main Program.cs.

  1. using System;
  2. using Microsoft.Win32;

  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  8. Console.WriteLine("\nThis application will do the following:");
  9. Console.WriteLine("1. Empty Start Menu");
  10. Console.WriteLine("2. Hide all drives");
  11. Console.WriteLine("3. Clean Windows Explorer");
  12. Console.WriteLine("4. Enforce restrictions in Internet Explorer");
  13. Console.WriteLine("5. Disable Task Manager");
  14. Console.WriteLine("\nPress any key to start");
  15. Console.ReadKey();

  16. using (RegistryKey r1 = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\Microsoft\
  17. {
  18. Console.WriteLine("Emptying Start Menu...");
  19. r1.SetValue("NoSMMyDocs", 1);
  20. r1.SetValue("NoSMMyPictures", 1);
  21. r1.SetValue("NoStartMenuMyMusic", 1);
  22. r1.SetValue("NoStartMenuNetworkPlaces", 1);
  23. r1.SetValue("NoStartMenuPinnedList", 1);
  24. r1.SetValue("NoStartMenuMFUprogramsList", 1);
  25. r1.SetValue("NoFind", 1);
  26. r1.SetValue("NoControlPanel", 1);
  27. r1.SetValue("NoStartMenuMorePrograms", 1);
  28. r1.SetValue("NoClose", 1);
  29. r1.SetValue("StartMenuLogOff", 1);
  30. r1.SetValue("NoRun", 1);
  31. Console.WriteLine("Done");

  32. Console.WriteLine("Hiding all drives...");
  33. r1.SetValue("NoViewOnDrive", 255);
  34. r1.SetValue("NoDrives", 255);
  35. Console.WriteLine("Done");

  36. Console.WriteLine("Cleaning Windows Explorer...");
  37. r1.SetValue("NoTrayItemsDisplay", 1);
  38. r1.SetValue("NoDesktop", 1);
  39. r1.SetValue("NoSetTaskbar", 1);
  40. r1.SetValue("HideClock", 1);
  41. Console.WriteLine("Done");
  42. }
  43. Console.WriteLine("Disabling Task Manager...");
  44. using (RegistryKey r2 = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\Microsoft\
  45. {
  46. r2.SetValue("DisableTaskMgr", 1);
  47. }
  48. Console.WriteLine("Done");
  49. Console.WriteLine("Enforcing restrictions on IE...");
  50. using (RegistryKey ie = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\Policies\Microsoft\
    Internet Explorer"
  51. {
  52. ie.CreateSubKey("Control Panel").SetValue("DisableDeleteBrowsingHistory", 1);
  53. ie.CreateSubKey("Control Panel").SetValue("FormSuggest Passwords", 1);
  54. ie.CreateSubKey("Control Panel").SetValue("FormSuggest", 1);
  55. ie.CreateSubKey("Control Panel").SetValue("History", 1);
  56. ie.CreateSubKey("LinksBar").SetValue("Enabled", 0);
  57. ie.CreateSubKey("Privacy").SetValue("EnableInPrivateBrowsing", 0);
  58. ie.CreateSubKey("Privacy").SetValue("CleanHistory", 0);
  59. ie.CreateSubKey("Restrictions").SetValue("NoBrowserOptions", 1);
  60. ie.CreateSubKey("Restrictions").SetValue("NoFavorites", 1);
  61. ie.CreateSubKey("Main").SetValue("Use FormSuggest", "no");
  62. }
  63. Console.WriteLine("Done!");

  64. Console.WriteLine("THANK YOU FOR YOUR PATIENCE");

  65. Console.WriteLine("A restart is necessary for the changes to take effect");
  66. Console.WriteLine("\nPress any key to restart");

  67. Console.ReadKey();
  68. Process.Start("shutdown.exe", "-r -t 00");
  69. }
  70. }

Some explanation: The program can be divided into 3 parts, one for each RegistryKey object r1, r2 and r3.

r1 is the RegistryKey object which allows you to manipulate Windows Explorer any which way you like. r1 provides access to a Registry key whose values affect Start Menu, Taskbar, Control Panel and My Computer. For example, setting NoClose value to 1 disables the Turn Off button on the Start Menu, NoFind disables the Search button and NoRun disables the Run button. The NoDrives and NoViewOnDrive keys deserve a bit more explanation. NoViewOnDrive makes drives disappear from My Computer, but the drives will be accessible elsewhere. NoDrives blocks access to the drives completely. The value passed to these keys determines the number and letters of drives affecte. Understand from this:
00000001 => 1 means only A: drive is affected.
00000011 => 3 means both A: and B: drives are affected.
00010100 => 20 means C: and E: drives are affected.
11111111 => 255 means all drives are affected.

r2 is a relatively less significant RegistryKey object which prevents the Task Manager from appearing when Ctrl-Alt-Del is pressed.

r3 is the RegistryKey object which allows you to customize Internet Explorer as per your requirements. The subkeys and their values set in the code are fairly self-explanatory. Some of them like InPrivate Browsing work only for IE 8.
After executing this code, if you want to review the changes in the Registry after running the code, comment out the NoRun line so that you can open the Registry and undo the changes - However, as I have unfortunately discovered, not all changes can be undone, that's the reason why you are ADVISED to run this code on a dummy empty Administrator account!