Quick and dirty subclassing in Win32

Context: So I wanted to implement drag and drop with files the quick and dirty way; specifically, WM_DROPFILES. (You can also do this with COM, but it’s a bit more involved, especially from raw C. I haven’t written about drag and drop yet, so the comparison is covered elsewhere for now.) My use case is simple, so I didn’t need the benefits or complexity of COM drag and drop. Turns out it’s just marking it as having the extended style in the resource editor or calling DragAcceptFiles, or so I thought. While I could get my dialog to accept the files, I couldn’t get the list view to do so. It turns out that the list view doesn’t handle these messages, nor does it send a notification back to the parent.

One strategy to deal with this is subclassing, although we aren’t going through the formal channels to do. That’s trickier, because it involves using things like CreateWindowEx, and might be a pain with dialog resources that already have a list view. Instead, we’ll actually replace the window procedure out from under the already materialized dialog control – basically monkey patching. In fact, Microsoft’s own documentation mentions this (and a better way with caveats). However, it doesn’t provide a concrete example. This article will quickly show you how, and provides an interesting, if brief example of Windows API principles.

I’m assuming you’re doing this from the same translation unit. First, let’s define the old window procedure (it will be initialized soon):

static WNDPROC ListViewProc;

Let’s define our window procedure. This is very simple and doesn’t operate on any state other than what’s passed into it and what can be retrieved with built-in functions. (For more complex cases, Microsoft seems to recommend setting window properties, which are more flexible than the user window long for subclassing purposes. I guess the common controls could already be using that) So let’s define:

static LRESULT CALLBACK MyListViewProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
	case WM_DROPFILES:
		SendMessage(GetParent(hwnd), msg, wParam, lParam);
		break;
	}
	return CallWindowProc(ListViewProc, hwnd, msg, wParam, lParam);
}

This is a pretty typical window procedure – handle the messages you care about, fall through when you don’t. Note that CallWindowProc was used instead of directly calling the function. You need to do this as part of subclassing, and it’ll crash if called directly.

Now, in our initialization function, let’s get the old window procedure, then set the new one, where hwnd is the window we’re subclassing:

ListViewProc = (WNDPROC)GetWindowLong(hwnd, GWL_WNDPROC);
SetWindowLong(lv, GWL_WNDPROC, (LONG)MyListViewProc);

Note that on modern Win32, you should bet using Get/SetWindowLongPtr, LONG_PTR instead of LONG, and GWLP_WNDPROC instead of GWL_WNDPROC. The example above is in a more archaic dialect because that’s how it was written in the code I was using, which is a 32-bit DLL called by other programs. Caveat emptor.

The initialization should be called early as needed to catch the messages you want. If the messages you care about are done before you have a chance (i.e. initialization), then proper subclassing should be done instead of monkey patching. For me, I just needed one message that’s called well after initialization, so it’s adequate and works; I haven’t noticed any ill effects so far. It sucks I needed to do it (why didn’t it send a notification to the parent?), but the time to fix that was a quarter century ago. C’est la vie.

Leave a Reply

Your email address will not be published.