Friday, March 30. 2007
WinInet ASYNC mode sucks
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.
Thursday, March 22. 2007
Dump calls
using System;To use it, I create the object like so:
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;
}
}
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.
Monday, February 26. 2007
Tables and numbers and colorful graphs and...
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

