How to make TestDriven.NET open dotPeek (or any other IL disassembler) instead of Reflector

TestDriven.NET is a free (for personal use) test runner for Visual Studio, that allows you to run unit tests from the context menu (“Run Tests”) using any unit testing framework. What I like particularly about TestDriven.NET, is that it allows running or debugging any arbitrary method as well!

TestDriven.NET comes with a .NET Reflector integration plugin, allowing you to right-click anywhere in the class and choosing “Go To Reflector”. After the RedGate Reflector fiasco many companies created their own (free) Reflector alternatives. Until Jamie Cansdale (creator of TestDriven.NET) updates his wonderful product to reflect (pun somewhat intended) the changes in the .NET world, here’s what you can do to trick TestDriven.NET to open any Reflector alternative: Image File Execution Options.

I previously wrote about using Image File Execution Options to debug a process that is crashing on startup. We can use it to have Windows open any executable when reflector.exe is requested:

  • Run regedit.exe
  • Go to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
  • Create a new key named reflector.exe
  • Create a new string value under reflector.exe. The name of the string value is Debugger, and the value should be a path to your preferred tool, e.g.:
    D:\utils\dotPeek\dotPeek.exe

Now, when you press “Go To Reflector”, it will open dotPeek instead.

Supercharging your ReSharper navigation-fu with Camel Humps

I blog about ReSharper a lot, mostly due to it’s being an awesome product which greatly improves my productivity. One of the major tips any ReSharper user is given – use the keyboard shortcuts. A lot. Some even go completely mouseless!

Here’s a little tip to make navigation your code even better: CamelHumps. CamelHumps is a ReSharper feature that allows navigating and selecting parts of compound names, according to camelCase rules, so that if you have a method named CreateUser_WithValidUsernameAndPassword_UserIsCreated (like I name my unit tests, UnderTest_Scenario_ExpectedResult), with CamelHumps enabled, you would be able to move the caret to the beginning of every word using Ctrl-Left/Right.

While this is a great feature, I wouldn’t want it enabled all the time, since it’s also affects Extend/Shrink Selection (Ctrl-W), which sometimes can be annoying. Fortunately, those actions are available as Visual Studio actions, so they can be used even if CameHumps is turned off globally:

  • ReSharper_HumpNext – move caret to next hump/word
  • ReSharper_HumpPrev – move caret to previous hump/word
  • ReSharper_HumpNextExtend – expand selection to next hump
  • ReSharper_HumpPrevExtend – expand selection to previous hump

I recommend binding the ReSharper_HumpNext and ReSharper_HumpPrev to Alt-Right and Alt-Left, and ReSharper_HumpNextExtend and ReSharper_HumpPrevExtend to Alt-Shift-Right and Alt-Shift-Left, respectively. Make sure you select Text Editor under “Use new shortcut in” in Tools – Options – Keyboard, or your binding might not work.

Please note: the later binding breaks the Box Selection feature in Visual Studio 2008, however greatly improves it in Visual Studio 2010 – it makes box selection respect the camelCase as well!

Here’s how it works – let’s say I want to rename the word Spelling to Proofreading in the method name, and the caret is inside the method body:

Step 1: Press Ctrl-[ to jump to containing method (jumps to start of the method name)

Step 2: Press Alt-Right a few times to jump to the beginning of the word Spelling

Step 3: Press Alt-Shift-Right to select the word Spelling

I’m using Roy Osherove’s excellent Keyboard Jedi utility to demonstrate the keyboard shortcuts. Unfortunately it doesn’t work on a 64-bit Windows, but you can fix it with this little hack by James Kovacs.

Unfortunately, this doesn’t work in the rename dialog (and others). There’s a 3-year old issue reported on the ReSharper’s Issue Tracker about this, however it hasn’t been added yet. Maybe it’s time, guys?

Kindle in Israel–now officially tax free!

Now it’s official – until last week, Kindle was classified as a “monitor” by the Israeli customs, and therefore required a 15% tax in addition to 16% VAT.

Kindle has been re-classified as a “personal reader”, and no longer requires the 15% tax, however the public was not notified about this decision. If you had imported a Kindle (or a similar reader) to Israel, and had to pay an additional tax, you may now submit a form at your local post office to have the tax returned to you.

More information available in the original article at Walla! Tech.

[ייבאתם ספר אלקטרוני? כנראה מגיע לכם כסף/Walla! Tech]

Modifying Windows Live Writer blog content in PublishNotificationHook plugins’ OnPrePublish

I’m writing a PublishNotificationHook plugin for Windows Live Writer (don’t worry, I will soon reveal what it is), and I noticed that there doesn’t seem to be a way to control the contents (text or HTML) of the blog post in this type of plugin.

Further digging led me to understand that there used to be a method modify the contents via Reflection, as described by ScottIsAFool, Live Writer MVP and author of many great plugins, using his WriterUtils, however this doesn’t seem to work anymore with the latest version of Live Writer 2011.

I had discovered that on a Japanese blog called SharpLab, someone had posted a way to excavate an internal BlogManager object, which is used by Live Writer to edit the actual blog posts. The code needed a slight modification, but with the result I was able to modify the contents of my blog post!

I extracted this into an extension method, use this at your own risk!

Announcing Plain Text Offenders

In the worst security breach in Gawker Media history, a 500mb file containing Gawker’s source code, and employee passwords was released to the internet, compromising email addresses and passwords of hundreds of thousands of users.

Studies show that over 30% of sites store our passwords in plain-text in their database.

We had enough! Today, Omer van Kloeten (@omervk) and myself are announcing Plain Text Offenders – a registry of “text offenders” – sites which store and/or send our own passwords to us via email in clear text!

We’re tired of websites abusing our trust and storing our passwords in plain text, exposing us to danger. Here we put these websites to shame.

So please go ahead – report offending sites at http://plaintextoffenders.com!

Supercharge your Unit Tests with Typemock Isolator–Webinar

I’m doing a webinar on supercharging unit testing with Typemock Isolator this Thursday, March 31, 03:00 PM – 04:00 PM GMT (Show in my Time Zone). Attend the webinar and you will learn:

  • How to test the untestable, using the powerful API of Typemock Isolator
  • How to fake (mock) any dependency (SharePoint, databases, file system, etc.)
  • How to make unit tests effective using powerful assertions

So go ahead, register for the webinar now!

Book Review: “Zero Day: A Novel” by Mark Russinovich

The name Mark Russinovich should be familiar to anyone in the IT industry. He’s the co-founder of Winternals (which operated the Sysinternals website), now owned by Microsoft. Mark is a co-author of the Windows Internals books, and the creator of such tools as Process Explorer, Autoruns and Process Monitor, which are just a small subset of the Sysinternals tools arsenal, which is being used daily by programmers, IT and computer forensics experts around the world.

I first heard about “Zero Day: A Novel” in a Mysteries of Memory Management (part 1) talk Mark gave at PDC 2010, and was immediately intrigued with the premise – a cyber-terrorism thriller that provides an in-depth look into an unsettling scenario of a coordinated, world-wide cyber-attack. What made this particular take on the subject most appealing to me, is the author’s in-depth knowledge of computer threats – from viruses to malware, I was expecting a technically accurate, realistic read, and that’s exactly what I got!

Zero Day is Mark Russinovich’s debut novel, well-written in such way that appeals both to computer experts and novices. Mark does a great job balancing the technical explanations in such way that they do not bore more tech savvy readers, and do not to overwhelm others. Combined with a fast-paced action, Zero Day is a perfect page turner for anyone!

Some spoilers ahead!

Zero Day begins with series of seemingly unrelated events – an airplane suddenly loses control over the Atlantic, wrongful prescription of medicine causes numerous deaths in various hospitals, an assembly line robot suddenly begins spinning out of control. While this is going on, an ex-CIA analyst, now computer security expert, Jeff Aiken, is hired by a firm to discover the cause of the crash of their entire computer system, and loss of financial data. Together with his partner, a woman who works at the DHS (Department of Homeland Security) in the US-CERT, they begin to discover the connection between those events, and the link to a Russian hacker, whose online handle, Superphreak, is encoded within the viruses and rootkits they discover. These events seem to increase as the date moves closer to September 11, which makes Jeff suspect that Arab terrorists might be behind it all.

When your software doesn’t work in China

I had an interesting bug submitted by a user, he wrote that our product was crashing in his Visual Studio 2010, which was a Traditional Chinese edition.

Typemock Isolator, being a Visual Studio add-in, adds a menu next to the Tools menu in Visual Studio. It does so by looking for an index of the “Tools” menu in the main menu bar, and simply adding the “Typemock” menu after it. However, the “Tools” menu is only called that in the English locale. In other languages, the menu might be called differently. In French, for instance, it’s called “Outils”.

When creating a new Visual Studio 2008 Add-in (under Other Project Types – Extensibility), if selected the “Yes, create a ‘Tools’ menu item” in page 4 of the wizard, the following code is generated in OnConnect method after completing the wizard:

...
string resourceName;
ResourceManager resourceManager = new ResourceManager("MyAddin.CommandBar", Assembly.GetExecutingAssembly());
CultureInfo cultureInfo = new CultureInfo(_applicationObject.LocaleID);

if(cultureInfo.TwoLetterISOLanguageName == "zh")
{
  System.Globalization.CultureInfo parentCultureInfo = cultureInfo.Parent;
  resourceName = String.Concat(parentCultureInfo.Name, "Tools");
}
else
{
  resourceName = String.Concat(cultureInfo.TwoLetterISOLanguageName, "Tools");
}
toolsMenuName = resourceManager.GetString(resourceName);
...

Unfortunately, this auto-generated code violates almost every known good coding practice. In particular, it gives no clues as to why “zh” is different, and why it’s handled separately.

Visual Studio menus are localized in the following way: a resource file, CommandBar.resx, is added to the project. This resource file contains translations of all common menus in Visual Studio, and each entry is defined by appending the two-letter ISO language code to the menu name, so that the entry for “Debug” in Spanish, for example, is esDebug.

There are two separate entries for both Traditional and Simplified Chinese in the resource file, and the entries are prefixed with zh-CHS and zh-CHT (zh is a short for Zhōngwén). Those identifiers refer to the old culture names. Since Windows Vista, the new “zh-Hans” and “zh-Hant” names are used, and the older names are kept for backwards compatibility. They can be found in the Parent property of the CultureInfo, specifying the current Visual Studio Locale ID (LCID).

Now that I had an understanding why “zh” was different, I was able to solve the bug by simply checking for the Chinese culture, and returning the correct identifier:

private const string ChineseTwoLetterISOLanguageName = "zh";

private string GetCurrentLocaleName(CultureInfo cultureInfo)
{
   // Chinese (Traditional and Simplified) use "old" locale code,
   // zh-CHT and zh-CHS, which is the name of the parent culture

   if (cultureInfo.TwoLetterISOLanguageName == ChineseTwoLetterISOLanguageName)
   {
       return cultureInfo.Parent.Name;
   }

   return cultureInfo.TwoLetterISOLanguageName;
}