Earlier today, I received this request from a customer:
This (http://www.microsoft.com/downloads/details.aspx?FamilyID=07fb0bd8-f390-458d-a629-6f0258ac7cdf) is a .NET 1.1 app and I have installed .NET 1.1 SP1 on Windows 7 32-bit and all .NET 1.1 SP1 security patches but it doesn't run. Gives me this error: An internal error has occurred: Object reference not set to an instance of an object" and then it quits.
Always being up for a challenge, I decided to have a look and see what wasn’t working. Reproducing the error is pretty straightforward. Just run the app – it’ll gladly crash for you. But the question is, why? And can you fix it?
If you hit this with a debugger, you’ll find the following stack when you hit an AV:
0:000> !dumpstack -ee
Current frame: (MethodDesc 0x2098d28 +0x1c SystemTimeZone.ConvertToTimeZoneInfoStructure)
ChildEBP RetAddr Caller,Callee
0018f4f4 0206d4ab (MethodDesc 0x2098d08 +0x43 SystemTimeZone.ToLocalTime)
0018f55c 0206cfc0 (MethodDesc 0x2098cd8 +0x38 SystemTimeZone.Convert)
0018f574 0206a484 (MethodDesc 0x2e5680 +0x15c frmMain.ShowCurrentTimes_Click)
0018f5d8 02067686 (MethodDesc 0x2e56d0 +0x446 frmMain.frmMain_Load)
0018f61c 02060365 (MethodDesc 0x2e5620 +0x2ed frmMain.Main)
Now, you could take a stab at this with SOS and start pulling out objects, but I didn’t believe that this would be the easiest way to solve the problem. Instead, I decided to reverse engineer the code and have a look. If you take a peek at the structure you’re trying to create from the SystemTimeZone class, you’ll find where the instance of the time zone came from – the code was rampaging through the registry looking for it. Here’s the method in question:
m_TZHash = new Hashtable();
m_TZList = new ArrayList();
RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\");
string subKeyNames = key.GetSubKeyNames();
Type t = typeof(TZIStructure);
int num2 = Marshal.SizeOf(t);
int num3 = subKeyNames.Length - 1;
for (int i = 0; i <= num3; i++)
RegistryKey key2 = key.OpenSubKey(subKeyNames[i]);
SystemTimeZone zone = new SystemTimeZone();
SystemTimeZone zone2 = zone;
zone2.DaylightName = StringType.FromObject(key2.GetValue("Dlt"));
zone2.DisplayName = StringType.FromObject(key2.GetValue("Display"));
zone2.Index = IntegerType.FromObject(key2.GetValue("Index"));
zone2.MapId = StringType.FromObject(key2.GetValue("MapID"));
zone2.StandardName = StringType.FromObject(key2.GetValue("Std"));
zone2.Name = key2.Name.Substring(key2.Name.LastIndexOf(@"\") + 1);
zone2 = null;
byte buffer = (byte) key2.GetValue("TZI");
if ((buffer != null) && (buffer.Length >= num2))
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
zone.TZI = (TZIStructure) Marshal.PtrToStructure(ptr, t);
If you spend enough time with the !do command, you can eventually stumble upon the answer, but the easier approach is, again, to stay out of the debugger and leverage your favorite search engine. Here’s a KB article that points out the problem:
Relevant information: “The Index registry value does not exist in Windows Vista and in Windows Server 2008.”
Aha. See the source code above? It wants that value, and it’s not there. Root cause found.
Now, on to solutions.
First of all, I wonder if you need a solution at all. If you click on the clock, and then click on Change date and time settings… you will get to the Date and Time control panel. Click on the Additional Clocks tab, and you can add 2 additional clocks for 2 new time zones. If you need more, you can always use Gadgets to fill the rest of your needs. So, for most people, this should provide a built-in solution to the problem – you don’t need the app any more because the OS provides 2 solutions already.
But, if you really really love that particular app, you could always copy that piece of the registry from Windows XP onto your Windows 7 computer, drop it in a new place inside of the registry, and then use the VirtualRegistry shim to redirect that app’s checks of the timezone portion of the registry to the copy you brought over.
Special thanks to my friend Gov for pointing me to the KB article, and for coming up with the VirtualRegistry workaround idea.