Arnout's Eclectica

But I digress…

Synchronizing two batch files using WAITFOR.exe

28 August 2008 22:58 — Development,Reminder,Windows

I was writing some unit tests today where I wanted to start some process asynchronously, but have it automatically exit after a specified number of seconds. Preferably something that's available on a standard W2K3 box, so that I wouldn't be creating any additional prerequisites for other developer boxes or the build machine.

Usually I just use ping -t -n count host for this, but I was also thinking that it would be nice to be able to terminate the process before the timeout. Not sure exactly how I came across it, but somehow I found waitfor.exe lying around in my system32 directory.

WAITFOR uses a mailslot to wait on or send a signal to. The waiting and sending can happen on the same machine, or the signal can be sent across the network.

In one command prompt, you perform the wait:

C:\\> WAITFOR /T 42 SignalName

In another, you send the signal:

C:\\> WAITFOR /SI SignalName

Way nicer than the old-fashioned approach of having process A poll a directory until process B has created a particular file. And, no less important, if the signal is not received within the specified timeout, you get:

C:\\> WAITFOR /T 1 Godot
ERROR: Timed out waiting for 'Godot'.

C:\\>_

:-)

*UPDATE* on REGEDIT experiments

21 August 2008 22:03 — Development,Regedit,Windows

In my previous post, I wrote about some findings w.r.t. REGEDIT. One of them was about a limitation on the depth of key hierarchies during a registry export.

When I found out about this limitation, I was actually encountering stack overflows in REGEDIT. However, shortly afterwards I wasn't able to reproduce that behaviour, so I considered it to be some fluke and just wrote about the limitation itself.

However, today I managed to overflow REGEDIT's stack again by just having it export a deep hierarchy to .REG, and this time I captured a crash dump:

0:000> kb 300
ChildEBP RetAddr  Args to Child              
0004308c 77f60938 000002b8 00000000 000430b8 ADVAPI32!LocalBaseRegEnumKey+0x13
000430c4 0100bb37 000002b8 00000000 00043654 ADVAPI32!RegEnumKeyW+0x8c
00043890 0100bb08 000002b8 000438a0 004b0048 regedit!PutBranch+0x124
0004404c 0100bb08 000002bc 0004405c 004b0048 regedit!PutBranch+0xf5
00044800 0100bb08 000002c0 00044810 004b0048 regedit!PutBranch+0xf5
00044fac 0100bb08 000002c4 00044fbc 004b0048 regedit!PutBranch+0xf5

       ... Removed 190 lines that all end in regedit!PutBranch+0xf5

0007f1d4 0100bb08 00000578 0007f1e4 004b0048 regedit!PutBranch+0xf5
0007f458 0100bb08 0000057c 0007f468 004b0048 regedit!PutBranch+0xf5
0007f6c8 0100be1b 80000001 0007f6f0 00070e54 regedit!PutBranch+0xf5
0007f944 01006be3 0007f974 01059de0 00070e54 regedit!ExportWinNT50RegFile+0x16d
0007f958 01007272 00070e54 00000000 0007f974 regedit!RegEdit_ExportRegFile+0x2e
0007fb80 010062eb 00070e54 0000110a 7739c2ee regedit!RegEdit_OnCommandExportRegFile+0x65
0007fb98 01009751 00070e54 00000293 00000000 regedit!RegEdit_OnCommand+0x7f
0007fbbc 01009947 00070e54 00000293 000ad310 regedit!RegEdit_OnKeyTreeCommand+0xc4
0007fc24 010045ba 00070e54 00160c39 0007fc50 regedit!RegEdit_OnKeyTreeContextMenu+0x1a1
0007fc34 0100689f 00130dc2 00070e54 000002c4 regedit!RegEdit_OnContextMenu+0x2a
0007fc50 7739b6e3 00130dc2 0000007b 00070e54 regedit!RegEditWndProc+0x128
0007fc7c 7739b874 01006777 00130dc2 0000007b USER32!InternalCallWinProc+0x28
0007fcf4 7739c2d3 00000000 01006777 00130dc2 USER32!UserCallWinProcCheckWow+0x151
0007fd30 7739c337 00615210 00615118 00070e54 USER32!SendMessageWorker+0x4bd
0007fd50 7745b0ee 00130dc2 0000007b 00070e54 USER32!SendMessageW+0x7f
0007fd74 7745bcfc 00070e54 00000062 00000025 COMCTL32!TV_SendRButtonDown+0xad
0007fdc4 7739b6e3 00070e54 00000204 00000002 COMCTL32!TV_WndProc+0x616
0007fdf0 7739b874 7745b6e6 00070e54 00000204 USER32!InternalCallWinProc+0x28
0007fe68 7739ba92 00000000 7745b6e6 00070e54 USER32!UserCallWinProcCheckWow+0x151
0007fed0 7739bad0 0007fef8 00000000 0007ff1c USER32!DispatchMessageWorker+0x327
0007fee0 01009cb9 0007fef8 ffffffff 00000000 USER32!DispatchMessageW+0xf
0007ff1c 01016e04 01000000 00000000 000a24a6 regedit!WinMain+0x154
0007ffc0 77e6f23b 00000000 00000000 7ffdf000 regedit!WinMainCRTStartup+0x182
0007fff0 00000000 01016c82 00000000 78746341 kernel32!BaseProcessStart+0x23

If somebody wants to dive into the crash dump, let me know.

Exporting a deep key hierarchy on W2K8 X64 didn't work either, BTW:

Screen shot of REGEDIT crashing on W2K8 X64 while trying to export a deep key hierarchy

REGEDIT experiments

12 August 2008 23:11 — Development,Regedit,Windows

NOTE (2008-08-21): Also see the update to this post.

I've been working on a tool that can export registry information in REGEDIT's .REG format. Since that format doesn't seem to be fully documented, I spent quite some time experimenting with REGEDIT. Here are some of my more interesting findings (all on W2K3).

No expansion beyond 32 levels
When you expand a key in REGEDIT, it passes the "root" HKEY (like HKLM) and the full path to RegOpenKey() instead of the immediate parent and just the name of the key you're trying to expand. This means that it will run into the "up to 32 levels at a time" limitation of the registry API:

Error message when opening level 33

No export beyond 200 levels
Although the registry supports 512 levels of keys, REGEDIT will only export upto a depth of about 200, silently ignoring anything beyond that...
Granted, that's not a very common scenario, but I would expect at least a warning message or something.

Importing values of non-existing types
WinNT.h defines 12 value types, ranging from REG_NONE (0) to REG_QWORD (11). REGEDIT however, will gladly accept all other 32-bit values as well...

[HKEY_CURRENT_USER\RegistryTest]
"Type Test"=hex(42):47,11

results in

A value of type 0x42...

Making HttpWebRequest work while having Fiddler decrypt SSL

Just a quick reminder to myself, so that I can forget about it...

Fiddler can act as a man-in-the-middle and decrypt SSL traffic, but then System.Net.Security rightfully complains about an invalid remote certificate ("The remote certificate is invalid according to the validation procedure."). This results in a System.Net.WebException "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.".

To prevent this from happening:

ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

Just be sure to not include this in production code :-)

URL-encoded slashes in System.Uri

30 April 2008 23:11 — .NET,Development

Two weeks ago, an ex-colleague asked me to take a look at a problem that he and his team had encountered. They tried using a System.Uri with URL-encoded slashes, but those slashes kept ending up unencoded in the resulting URI:

Uri uri = new Uri("http://somesite/media/http%3A%2F%2Fsomesite%2Fimage.gif");
Console.WriteLine(uri.AbsoluteUri);
// Output: http://somesite/media/http%3A//somesite%2Fimage.gif

That's a totally different URL, which the target server refuses to process.

I was sure that they must have overlooked something, and that there would be some way to tell the Uri constructor to leave all encoded characters as-is. But no, it does not seem possible; dots and slashes are always decoded. I find that quite surprising, so if anyone can point me to an official solution, I'd be much obliged.

In the mean time, a reflection-based hack, courtesy of Reflector and the .NET Reference Source:

static class UriHacks
{
    // System.UriSyntaxFlags is internal, so let's duplicate the flag privately
    private const int UnEscapeDotsAndSlashes = 0x2000000;
 
    public static void LeaveDotsAndSlashesEscaped(this Uri uri)
    {
        if (uri == null)
        {
            throw new ArgumentNullException("uri");
        }
 
        FieldInfo fieldInfo = uri.GetType().GetField("m_Syntax", BindingFlags.Instance | BindingFlags.NonPublic);
        if (fieldInfo == null)
        {
            throw new MissingFieldException("'m_Syntax' field not found");
        }
        object uriParser = fieldInfo.GetValue(uri);
 
        fieldInfo = typeof(UriParser).GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);
        if (fieldInfo == null)
        {
            throw new MissingFieldException("'m_Flags' field not found");
        }
        object uriSyntaxFlags = fieldInfo.GetValue(uriParser);
 
        // Clear the flag that we don't want
        uriSyntaxFlags = (int)uriSyntaxFlags & ~UnEscapeDotsAndSlashes;
 
        fieldInfo.SetValue(uriParser, uriSyntaxFlags);
    }
}

Copyright © 2006-2009 Arnout Grootveld — Powered by WordPress — Hosted at pair Networks