HUGE gotcha in TransmitPackets
At work I’ve been trying to get TransmitPackets to work in the hopes of improving our performance. In the process I ran across some behavior that can’t be right.
According to the SDK docs, the nSendSize parameter specifies how much data at a time TransmitPackets will send to Winsock. I left this at 0, which uses the OS default (according to Network Programming for Microsoft Windows, Second Edition, the OS default is 64k). However, when I did that, as soon as I passed in an array of TRANSMIT_PACKETS_ELEMENTs whose sum total bytes exceeded 64k, the overlapped operation would fail with ERROR_INVALID_ARGUMENT.
I soon found out that if I set nSendSize to a value greater than or equal to the total number of bytes I was sending with each TransmitPackets call, it would work. No, that doesn’t make sense, and no, I can’t find any independent confirmation. That said, it was a HUGE gotcha.
Hardware I want
I’ve just found some additional hardware I want:
- The Norco DS1220 SATA RAID enclosure holds 12 hot-swappable SATA drives in a 3U rackmount enclosure, for a mere $850
- 12 x 500GB WD SATA drives
- Seagate 300GB eSATA external drive with eSATA controller card
I’ve not come close to filling up aenea‘s 1TB of storage, and yet I already want a 6TB array.
Even Explorer doesn't get mount points right
I’ve been playing with mount points in Windows recently, whereby a directory can be mapped to the contents of a drive. I use this on prospertine to place g:\docs onto my small-ish 48GB RAID 1 array for safe keeping, while everything else on g: (downloads, VMs, etc) is stored on a single 320GB drive without redundancy.
I just ran into a potentially huge gotcha with mount points: Explorer doesn’t full grok them. From the article:
If you try to delete folders that are stored on a mounted drive and send them to the Recycle Bin, you may receive the following error message:
Cannot delete Foldername: Access is denied. The source file may be in use.
Status is also amusing:
Microsoft has confirmed that this is a problem in the Microsoft products that are listed at the beginning of this article.
So, if you use mount points, forget about the recycle bin. This is fine w/ me since I always SHIFT-DELETE stuff, but if you need the security blanket, well, it’s all drive letters for you.
Remote Debug Monitor ignores working directory?
I’ve recently written about the virtues and vices of remote debugging. Today I noticed another vice: the remote debug monitor doesn’t seem to honor the working directory specified in the debug settings.
The application I’m debugging fails noisily if it’s not run with the right working directory. Despite specifying a working directory in the debug settings, the resulting process’s working directory is still wrong. If I run the command without the debugger, using only a shortcut with an explicit working directory, all is well.
Notwithstanding the shittiness of an app that requires a certain working directory, if the remote debugger doesn’t support explicit working directories the least it could do is say so.
Resolved troublesome thermal, stability issues on prospertine
Now that I’m playing Guild Wars full time, I’ve started pushing prospertine‘s Radeon X800XL PCI-Express 16x video board to its limits. I run in 1280×1024, high everything, 4x antialiasing, 32-bit EAX sound, and it’s flawless.
However, it took a bit of work to get here. First, I was running three Western Digital WD740 10k RPM SATA drives. Though these drives were screaming fast, they kick off alot of heat, and while I was gone in Iraq prospertine rebooted and came back with no RAID array! I assumed thermal issues right away; that the drives were generating too much heat and the chipset was overheating.
However, I sucked it up for a while, until the instability cost me a weekend spent recovering data on a blown-away RAID volume. Then, I switched to three Seagate Baracuda 7200.10 320GB drives which I got on sale at NewEgg for $99/ea. In addition to being bigger and slower than my WD740′s, the ‘cudas also use perpendicular recording technology, which improves storage density and I assume accounts for the lower price.
At any rate, that put an end to the instability until I started running Guild Wars: Factions, which couldn’t go an hour without a crash dump. Initially, the crash dumps were random, suggesting a hardware stability issue. I immediately suspected heat, as my RAID controller ‘forgot’ about its member drives again. I powered it down for an hour to cool, and it came back up fine, confirming my suspicions. I then put prospertine (recall she’s a Shuttle SB81P small form factor computer, with a very delicately balanced heat control system) up on blocks so the underside of the chassis is exposed to air. That put an end to the thermal problems, though not the stability issues.
After that, I still got crash dumps, but this time there was a meaningful dump file. Fortunately for me I now work in a place where causing crash dumps is one of my duties, and as such I’m pretty handy with windbg and was able to get a kernel stack trace, clearly implicating the Realtek HD audio driver as the culprit.
As it turns out, I was running the ancient Realtek driver circa 2004 that came with my mobo; I immediately downloaded an updated driver fresh from the oven, Sept 2006. Now, I can play Guild Wars for as long as my frail carbon-based vessel can stand it. I’m not entirely sure that’s a good thing…
I can't stop playing Guild Wars: Factions!
Last Thursday I made a life-ruining mistake: I bought a copy of Guild Wars: Factions, a MMO distinguished by free online gameplay. Now, I can’t stop playing it. It’s worse even than Diablo II, which nearly caused me to stop eating and sleeping.
The game play is rich, the missions well-crafted, the wide-ranging assortment of armor, weapons, runes, skills, and other stuff is remarkable, and there’s no monthly fee. I’m screwed. If I stop shaving, that’s an extra five to ten minutes a week I can play. I only really need four hours of sleep a night. Skipping the gym will free up another 1.5. I can shower once a week…
Shitty experience with Linksys USBBT100 Bluetooth adapter
Before I left for Iraq I got a Linksys USBBT100 USB Bluetooth adapter, and a Plantronics M2500 bluetooth headset, with the intention of using them with Skype to call home. As it happened, our V-Sat connectivity had such high latency that Skype was useless, but now I’m trying to use the same configuration for in-game chatting as I play Guild Wars.
However, due to the Bluetooth suckage problem, Linksys installs the Widcomm Bluetooth stack, which doesn’t get along with the Microsoft Bluetooth stack built into XP SP2. I had to go into Device Manager, select the Generic Bluetooth Device, and choose Update Driver and manually select the Linksys Bluetooth driver, which since it’s unsigned is not chosen by default. If you fail to do this, you’ll be using Microsoft Bluetooth stack, which even has a very similar tray icon as the Widcomm stack, however has shit features, including zero support for headsets.
I know the USBBT100 is ancient, but still, they had XP SP2 back in 2003; you’d think they’d at least make a gruding attempt to get it right.
.NET File APIs Don't Like '\\?\' Prefix!?
I’ve recently run into a situation in which I need to use .NET file I/O APIs like File.Exists and File.OpenRead using a filename of the form:
\?\Volume{guid}\foo.txt
Where guid is a GUID in the standard registry format. Assuming the path refers to a currently available volume (as reported by, for example, mountvol), this is a fully valid path. Try this experiment:
Go to a command prompt, and type mountvol:
The output you’ll get, after usage information, will be the list of current mount points for each mounted volume on your system (in other words, drive letters):
\?\Volume{88370110-213a-11db-9fa6-806e6f6e6963}\
C:\
\?\Volume{9ab4d54b-2171-11db-a24c-005056c00008}\
G:\
\?\Volume{6f7fdfc2-213b-11db-9f17-806e6f6e6963}\
D:\
\?\Volume{2d206468-216c-11db-9f1d-505054503030}\
E:\
\?\Volume{9ab4d552-2171-11db-a24c-005056c00008}\
F:\
This gives you the volume name corresponding to each local disk drive letter on your system. Now, say you have a file on C:\, C:\Test.txt. Run this command:
notepad \?\Volume{88370110-213a-11db-9fa6-806e6f6e6963}\test.txt
Where the GUID junk there is the GUID assigned to your C: drive. If you did it right, notepad will open the file, just as though it was on C:. Note that Explorer doesn’t work with these paths, and neither do the File Open common dialogs. But, if you pass a path like this to CreateFile, as notepad does when you pass a filename on the command line, it will work just like any other file.
However, pull that stunt with a .NET file API, and you’ll get a nice little exception to the tune of Illegal characters in path. What invalid character? Well, for one, the ‘?’ is frowned upon; I don’t think .NET supports the path parsing disabled form of paths at all. I was lucky in that all I needed to do was test for the existence of a specific file with a volume path, so I just did a P/Invoke call to GetFileAttributes. If I’d wanted to read or enumerate such files, I’d be screwed to the wall.
Yes, yes, C/C++ pugilists, rub it in. Basque in this rare instance in which your Win32 API elitism and garbage collector condescension are in fact vindicated. Take a few minutes and savor it; in that time I’ll construct a scalable three-tier web app with authentication, membership, and skinning using a few lines of ASP.NET code, and then you can return to the back of the bus.
Visual Studio Remote Debug Monitor: If I didn't love it so much, I'd hate it
In my new job I do alot of work with low-level device drivers that can badly shitcan one’s machine if something goes wrong (as it so often does). Thus, it was something of a no-brainer to run my development binaries within a VMWare virtual machine.
However, this does lead to some unfortunate complexity. One of the most significant of these is the need to use the debugger on my host machine, to debug code on the virtual machine.
Since Visual C++ 6.0 at least, I’ve known of the Remote Debug Monitor, which is a lightweight executable shipped with Visual C++ that you run on a target machine to enable you to debug processes thereon, from a remote host across the network. It’s gone through various stages of shittiness, and now operates primarily via DCOM (presumably, using sockets was too reliable and straightforward).
If you’ve ever coded with DCOM, you probably sense I’m about to relate a tale of misery and woe. DCOM sucks for a few reasons:
- Its security model is only fully understood by Keith Brown, and even he’s forgotten alot of it now that no one uses DCOM anymore.
- When something goes wrong (and it always does with DCOM) the error message you get is typically something like “Error -8234286245: You’re shit out of luck”.
- The parts of it that matter are virtually undocumented, leaving you with few options but to scrounge ten year old USENET postings for stupid hacks that no one understands but that seemed to work for a guy in Brazil once.
Add to this the fact that you’re trying to get a remote debugger to work on top of DCOM, and it’s fairly obvious that your day is about to get a whole lot worse.
Installation
To start, you need to get the Remote Debug Monitor installed on the machine running the process you want to debug. The MSDN article Howto: Setup Remote Debugging, is fairly comprehensive, albeit a bit dense.
The gist is, either install Visual Studio 2005, or install the RDM separately from the Visual Studio 2005 installation media. Since I’m running on a VM that is supposed to roughly approximate a real-world environment, I’m not about to install VS2k5, so I opted for the separate install. It’s in the Remote Debugger folder, with subfolders for the x86, x64, and ia64 platforms.
The installer gives you the option to run the monitor as a service; I declined, and run it explicitly from a shortcut I put on my desktop, when I need remote debugging.
XP SP2 and Firewalls
If you have the misfortune of running XP SP2 or a personal firewall, you’ve some work to do. The aforementioned article enumerates all the ports you have to open. Port tcp/135 on the machine you debug from; tcp/135, tcp/139, tcp/445, udp/137, and udp/138 on the machine running the debug monitor. There are also a couple of IPsec ports if you use IPsec, which I don’t.
Intuitively, you might expect only the remote machine would need firewall rule changes, but the magic of DCOM means there are comms in both directions. Forget this at your peril.
KB 833977 has some additional steps to try on XP SP2.
Running
When you’re ready to do some debugging on the remote host, startup the debug monitor. By default it will run with Windows authentication, which also seems to mean DCOM. You can switch it out of this mode, but the resulting dumbed-down TCP-based mode doesn’t support managed debugging, which makes it a non-starter for a .NET programmer such as myself. Accept the defaults.
How you configure Visual Studio to use the remote debugger depends on what kind of project you’re trying to debug. Visual C++ projects have a drop-down box in the Debugging section of the project Properties dialog, which defaults to ‘Local Windows Debugger’; change that to ‘Remote Windows Debugger’, and fill out the debug fields as appropriate, taking care to enter the name or IP address of the machine running the debug agent, in the ‘Remote Server Name’ field. ‘Connection’ and ‘Debugger Type’ leave at their defaults.
.NET projects have a completely different project properties screen; go to the Debug tab, check the ‘Use remote machine’ checkbox, and enter the remote machine name.
A few things to note, regardless of which type of project you’re debugging:
- The command/external program to run is evaluated on the remote machine, so the path you specify must be valid on that machine. Specifying the path to the executable on your Visual Studio machine won’t work, unless by some happy coincidence they’re the same.
- Ditto for any paths or file names given as command line arguments
- It’s imperative that the remote machine have the exact binaries you just built with Visual Studio. If not, the debugger will detect that its symbols don’t match the binaries, and breakpoints won’t work, not to mention any changes you made since you last updated the remote host won’t be there. Do not mess this up.
At this point, kick off the debugger with F5. If you’re very lucky, things will Just Work. If not, consider the troubleshooting steps below:
Gotchas
The real problem with the Visual Studio’s remote debugging support is that it doesn’t provide much in the way of useful error messages; it either works or claims there’s no remote debug monitor running on the target.
To that end, try the following steps:
- If at all possible, make sure the username and password are the same on both the host and the remote machine.
- According to Remote Debugging Permissions you can remotely debug a process owned by the account you authenticate with to the remote debug agent. I run without admin privs on my host machine, but I’ve never tried to run the debug monitor without admin rights, so ymmv.
- DCOM privs may get in the way. Adjust the DCOM privs as specified in this article on the remote machine.
- KB 908099, though ostensibly for XP SP2, is mostly applicable to W2k3 server as well (sans the firewall stuff, obviously). Pay particular attention to the
dcomcnfgsteps - Sometimes the Visual Studio remote debug code doesn’t use the right username to find the remote debug monitor instance. In my case, the host and remote machines were both running as
administrator, but the remote machine was running under a domainadministratoraccount for a domain controlled by the remote machine itself, and Visual Studio reported:Unable to start debugging.
The Microsoft Visual Studio Remote Debugging Monitor (MSVSMON.EXE) does not appear to be running on the remote computer. Please see Help for
assistance.This despite the fact that the agent was indeed running. In this case, try explicitly specifying the user name running the debug monitor. So, if your remote machine is ‘buggy’, run the remote debug monitor, and look at the first line of output it produces. It should look something like:
Msvcmon started a new server named 'WHATEVER\someone@buggy'. Waiting for new connectionsBack in the Visual Studio debug settings, specify that entire quoted string (without quotes, obviously) as the hostname. Everything before the ‘@’ will be interpreted as the username.
- Finally, if it just won’t work, try to simplify the situation as much as possible. Make sure the usernames and passwords match, that both ends are admins, etc. If all else fails, you can switch the debug monitor and Visual Studio to use the native mode without authentication (See Tools|Options), but this won’t allow managed debugging, so if you’re writing .NET code you’re screwed.
Good luck, and godspeed. You’ll need both.
WTF is wrong with WPF focus!?
There’s something badly broken about Windows Presentation Foundation focus. I’ve read that WPF implements its own concept of focus, and at the high level a WPF element tree is just a single HWND, and the WPF elements are rendered therein.
Here’s an experiment for you to try on your own: Create a simple XAML Window. Populate it with user controls. Now, try to get the first user control in the window to receive focus (keyboard and logical) when you’re window starts up. The FocusSample does it with a Button, which seems to work OK, but with a UserControl you’re out of luck. Even after setting Focusable to true, it doesn’t work. Yet, you can Tab through the focusable controls without problem.
First, you’ll try calling Keyboard.Focus() from within the Loaded event, but if you put trace output in the OnIsKeyboardFocusWithinChanged method of the user control, you’ll see that the call works, but it reversed again. Yes, it seems something about the window creation process after the Loaded event resets the keyboard focus, yet for some reason Button controls are impervious. TextBoxs may be as well; I’ve not tried.
In the end, I had to come up with a shameful hack. Behold, FocusHelper:
static class FocusHelper
{
private delegate void MethodInvoker();
public static void Focus(UIElement element)
{
//Focus in a callback to run on another thread, ensuring the main UI thread is initialized by the
//time focus is set
ThreadPool.QueueUserWorkItem(delegate(Object foo) {
UIElement elem = (UIElement)foo;
elem.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
(MethodInvoker)delegate()
{
elem.Focus();
Keyboard.Focus(elem);
});
}, element);
}
}
Don’t get all holier-than-thou. War makes a man do things he’d never dream of doing otherwise. So does WPF programming.