Spent most of the day wrestling with this wretched API. What a complete
waste of time. Tells me the request is complete, but wants me to wait
an unknown amount of time before actually letting me retrieve the
response. Ugh.
Forget it. I'll just shove the whole thing off onto a separate thread and let that sit and wait for as long as it needs.
Friday, March 30. 2007
WinInet ASYNC mode sucks
Thursday, March 22. 2007
Dump calls
I'm working with a 3rd-party library that does some machine-specific
calculations. It's called through COM interop, and takes a ton
of parameters. When something doesn't come out right, it's nice to be
able to quickly view what's being send in and returned without spending
an excessive amount of time in the debugger... so I use this
quick-and-dirty little wrapper to dump them to the output window:
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;
using System.Runtime.InteropServices;
using System.Diagnostics;
public class DebugTraceProxy
: RealProxy
{
readonly MarshalByRefObject target;
public static T NewObject<T>()
{
return (T)(new DebugTraceProxy(typeof(T)).GetTransparentProxy());
}
private DebugTraceProxy(Type t)
: base(t)
{
target = (MarshalByRefObject)Activator.CreateInstance(t);
}
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage call = msg as IMethodCallMessage;
Debug.WriteLine(call.MethodName + ":");
for (int i = 0; i < call.InArgCount; ++i)
{
string name = call.GetInArgName(i);
object arg = call.GetInArg(i);
Debug.WriteLine(" " + name + ": " + DumpOb(arg));
}
IMethodReturnMessage ret = RemotingServices.ExecuteMessage(target, (IMethodCallMessage)msg);
Debug.WriteLine("Returned:");
for (int i = 0; i < ret.OutArgCount; ++i)
{
string name = ret.GetOutArgName(i);
object arg = ret.GetOutArg(i);
Debug.WriteLine(" " + name + ": " + DumpOb(arg));
}
return ret;
}
private string DumpOb(object ob)
{
Type t = ob.GetType();
if (t.IsPrimitive || t == typeof(string))
return ob.ToString();
string obDesc = "";
FieldInfo[] infos = t.GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance );
if (infos.Length == 0)
return ob.ToString();
foreach (FieldInfo field in infos)
{
if (obDesc != "")
obDesc += ", ";
obDesc += field.Name + ": " + field.GetValue(ob).ToString();
}
return obDesc;
}
}
To use it, I create the object like so:
CrazyOb myCrazyOb = DebugTraceProxy.NewObject<crazyob>();
Reflection... handy.
Saturday, March 10. 2007
VS2005 DataTips for MFC collection classes (CMap, CArray, etc.)
I spend a lot of time debugging old code. Tracing through libraries written years ago, before the VC++ STL had reached maturity. And one of the often-frustrating aspects of this is how difficult it is to examine the contents of the old, MFC collection classes. CDWordArray, CStringArray, CMapPtrToPtr... not to mention the various templatized versions of these classes. While sometimes these can be easily replaced with newer, better-supported STL equivalents, often the effort involved outweighs the benefit.
At least for the array classes, it's only a minor inconvenience: the array pointer, and the size of the array are both readily available, and with these it's easy enough to view the contents.
But for the CMap classes, things get ugly. Internally, these objects are hash tables, with an array of buckets and each bucket a linked list. Viewing the contents of one of these maps in the debugger requires manually tracing the list of each bucket, then backing up and trying another bucket, then another... until you've found the item, or just given up.
The std::map<> class, implemented as a red-black tree, should be just as much of an annoyance to view - however, it gets flattened into what looks like an array:

Handy, eh? So enough of tracing through buckets.
Open \Program Files\Microsoft Visual Studio 8\Common7\Packages\Debugger\autoexp.dat, and at the end of the [Visualizer] section, add:
;------------------------------------------------------------------------------
; CArray
;------------------------------------------------------------------------------
CArray<*>|CDWordArray|CWordArray|CByteArray|CUIntArray|CPtrArray|CObArray|CStringArray{
preview
(
#( "[", [$c.m_nSize], "](",
#array
(
expr : $c.m_pData[$i],
size : $c.m_nSize
),
")")
)
children
(
#(
#array
(
expr : $c.m_pData[$i],
size : $c.m_nSize
)
)
)
}
Start a debug session. You should now be able to quickly examine the various CArray (CDWordArray...) objects as easily as std::vector<...>s. Note that the preview (...) section controls what's viewed in the initial DataTip or watch line, while the children(...) section controls what's displayed when you expand it.

Now for the fun one. Start with:
;------------------------------------------------------------------------------
; CMap
;------------------------------------------------------------------------------
CMap<*>|CMapPtrToPtr|CMapPtrToWord|CMapWordToPtr|CMapWordToOb|CMapStringToPtr|CMapStringToOb|CMapStringToString{
preview
(
#( "[", [$c.m_nCount], "](",
#array
(
expr : $c.m_pHashTable[$i],
size : $c.m_nHashTableSize
) : #list (
head : &$e,
next : pNext
) : $e,
")")
)
children
(
#(
#array
(
expr : $e.m_pHashTable[$i],
size : $e.m_nHashTableSize
) : #list (
head : &$e,
next : pNext
) : $e
)
)
}
Ah, now this one will save some serious time - it'll flatten the various CMap objects such that they look like arrays in the debugger. A CMap with 3000 items will show 3000 children, numbered 0-2999.

But it can still be better: each of those children is a key-value pair, specifically a CMap::CAssoc object. It'd be nice if we could get a preview of those keys and values as we scroll through those 3000 items, rather than having to expand each one...
CMap<*>::CAssoc|CMapPtrToPtr::CAssoc|CMapPtrToWord::CAssoc|CMapWordToPtr::CAssoc|CMapWordToOb::CAssoc|CMapStringToPtr::CAssoc|CMapStringToOb::CAssoc|CMapStringToString::CAssoc {
preview
(
#(
"(",
$e.key,
",",
$e.value,
")"
)
)
children
(
#(
key: $c.key,
value: $c.value
)
)
}
There! All happy.
Of course, you can write rules for your own datatypes too, if you want them to be immediately visible in the preview. For a good reference, see the excellent writeup over at virtualdub.org
Friday, March 2. 2007
Why I hate software
"I'll buy it if I want it and can't write it". Or, to put it another way, if I think I could cook up the app myself in a Saturday's worth of hacking and drinking, then I expect to get it free. That might not be entirely reasonable... but it's how i think. The result of this is that for smaller programs, I tend to open my wallet for the ones that really go above and beyond the norm.
Notable "utility" apps I've laid out money for in the past few years:
- Trillian
- TV Tool (a nice little utility for bypassing the onerous DRM restrictions and sad TV support implemented by NVidia drivers).
- Nero
- Firebug (ok, this was a donation… but I’d have paid it if I’d needed to)
They all do things I needed, couldn't get free, and would have spent months working out on my own. And with the exception of Firebug, they all have terrible user interfaces. Heck, just about every single app I use has a rotten UI. VS2005 is probably one of the better apps, but it can hardly be classified as "utility" - it's huge, written by hundreds of developers, and… still has a lot of problems. I certainly would never use words like "beautifully" (or even "seamlessly") to describe how they integrate with the core system. Indeed, if there's one thing common to most of the apps I use, it’s how eager they are to make you aware that you’re using them. Splash screens, heavy UIs, custom dialogs for things that the system does better, modal dialogs for things that should be non-modal…
…but such ranting gets old, eventually. Instead, I'll just give an example: source control.
At this moment, I have four source control clients
installed on this machine. Three of them offer "integration" by way of
a Visual Studio plug-in. But only the fourth actually integrates at all
well into my workflow: the TortoiseSVN client makes source control almost
seamlessly available from any OS-provided file system view, including
the standard Open and Save dialogs. The rest all provide pale
imitations of the old two-pane Fileman/Explorer interface, usually with
a few more panes tacked on for for status or other information. All
three, without fail, require me to have either a command
prompt or an Explorer window open on the "working directory" for
whatever project I’m viewing, because they don’t provide a UI for
common file management tasks. And that token nod at “integration”? VS
becomes slower with it turned on, and continually locks files I don’t
want locked. In short, they provide the worst of both worlds: UIs I
could have written on a drunken Saturday, but would never have wanted to.
Tortoise, for all its problems, provides something I’d willingly pay
for out of pocket: I’m actually more productive with it than without
it. None of the rest, paid commercial products though they are, comes
close to this.
So, Paul - I'm glad you're happy with Mac software. I don't know that I really believe it's as good as what you make it out to be, but I'll believe it is that good for you. Because, somewhere, someone must be writing usable software. I want to believe it...
Monday, February 26. 2007
Tables and numbers and colorful graphs and...
A while back, I needed to generate some reports off of a small database
I maintain. I needed them to be updated frequently, and didn't want to
spend a lot of time putting them together.
Of course, I could have used Crystal Reports. But it just didn't seem
right - I keep a large number of knives at home, and should I ever
desire death by a thousand cuts, I'm sure it could be arranged on my
own time. No reason to mix such activities with work.
So based on a recommendation, I decided to give SQL Server Reporting
Services a shot. Installed it on my desktop, threw together a few
reports, and that was it. It was quick and easy, and I was happy. Until
it suddenly stopped working. I messed around with keys and permissions
and database users, and after about a day it was all working again...
until, roughly a month later, it suddenly wasn't. This went on for some
time, and then, one day, it was time to bring the reports out of
development and make them available to other people.
And I balked. Here it was, running on a local machine that had no other
users, no onerous loads, a machine that, most days, ran only one very
light application... and it was still breaking periodically. Did I really want to put up with babysitting this thing on a remote server? No, I decided, I did not want that.
So, back to the drawing board. Of course, I didn't exactly have time to
research and learn a whole new reporting framework. Heck, I didn't have
time to do much of anything - any work would have to be done while
waiting for builds to complete or tests to run on other projects. So I
buckled down and started coding: I needed a few basic statistical
routines, a histogram routine, some tables, and a few graphs. Some
quick-and-dirty C# took care of the stats, quick-and-dirty HTML for the
tables... and the newest addition to my toolbox took care of the graphs.
I'd first run across ZedGraph a few years ago, when John Champion posted his article
on The CodeProject. While it looked nice enough, I had no need for such
a thing at the time, and forgot about it. But now, running across it in
a frantic Google search,
it looked like just what I needed. A couple of hours spent playing with
it confirmed this notion: it fit my simple needs like a glove, quickly
turning the dry, drab reports into slick, colorful affairs, ready for
inclusion in any presentation. Best yet, it fit neatly into the quick-and-dirty ASP.NET code I'd already written.
A full day spent hacking (during the aforementioned gaps in
testing...), and the new reports were ready. Fast, ridiculously simple,
and perfectly suitable for XCOPY deployment.
previous page
(Page 3 of 5, totaling 21 entries)
next page

