Categories
Development

Old C++ Code

I was inspired by a recent post by Brent Simmons to write about some ancient C++ code I wrote back in the min-90’s. At that time I was just learning to develop Windows applications in C and C++ was just starting to get some traction, not unlike Swift in the Mac and iOS community today.

When you created a Windows application you’d have to write a WindowProc (Windows Procedure) that would process all messages for a particular window. A Window Proc would receive a message and other parameters. You’d switch on the message and the WPARAM and LPARAM parameters would contain other information specific to a particular message. It was well documented, but it was quite ugly.

LRESULT CALLBACK WindowProc
(
HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam
)
{
switch (message)
{
case WM_CREATE:
// Sent when the Window is created
break;
case WM_COMMAND:
// Handle Menu item selection here
break;
case WM_DESTROY:
// Window is being destroyed
PostQuitMessage(0);
break;
default:
// If we don't handle the message, give it to the Windows
// supplied DefWindowProc
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

Believe it or not this is how a lot of your favorite Windows applications were originally written. Giant switch statements that dissected two other values to determine how to respond to different types of messages.

When I started learning C++ I was trying to find a way to create a Window Proc that didn’t have to implement every Windows message in the system, there must be thousands of them. Could you imagine a base C++ class that responded to every Windows API message in the system? I can’t. It would be a real mess to deal with.

It took me quite a while to come up with a way to do it. I happened upon the answer in the C++ FAQ. This would allow me to create a base class that provided one Window Proc that would look in a dispatch table (a map) to see if the Windows Message was handled by the Window in question.

Here’s what it looked like. I hope this gets the point across.

static ACLWinMessageMap stcMsgMap[] =
// message sub msg/ctl id message handler method.
//
{ { WM_CREATE, WM_NULL, (PWINMSGHNDLR)&SampleWindow::OnCreate }
, { WM_DESTROY, WM_NULL, (PWINMSGHNDLR)&SampleWindow::OnDestroy }
, { WM_COMMAND, IDM_ABOUT, (PWINMSGHNDLR)&SampleWindow::OnAbout }
, { WM_COMMAND, IDM_EXIT, (PWINMSGHNDLR)&SampleWindow::OnExit }
, { WM_SIZE, WM_NULL, (PWINMSGHNDLR)&SampleWindow::OnResize }
}; // stcMsgMap
LRESULT SampleWindow::OnCreate
(
IN WPARAM wParam,
IN LPARAM lParam
)
{
// Handle creation of other windows and initialization
// of this window
return 0L;
} // ::OnCreate
LRESULT SampleWindow::OnDestroy
(
IN WPARAM wParam,
IN LPARAM lParam
)
{
// Tell the application it's time to go away.
::PostQuitMessage(0);
return 0L;
} // ::OnDestroy
LRESULT SampleWindow::OnAbout
(
IN WPARAM wParam,
IN LPARAM lParam
)
{
AboutDlg about(_hInstance, _hWnd);
about.Show();
return 0L;
} // ::OnAbout
LRESULT SampleWindow::OnExit
(
IN WPARAM wParam,
IN LPARAM lParam
)
{
ACLWinBase::Destroy();
return 0L;
} // ::OnExit

When you implemented a Window Proc class you would derive from a base class and provide it with a message dispatch table (at the top of the above gist.) The dispatch table would direct the base classes Window Proc to call the correct handler for a specific message.

EXTERNC ACLWINCB(LRESULT) ACLWindowDispatchProc
(
IN HWND hWnd,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam
)
{
UNALIGNED ACLWinBase* pWindow = NULL;
PWINMSGHNDLR pHandler = NULL;
LRESULT lResult = 1;
switch (uMsg)
{
case WM_CREATE:
{
// lparam is passed with a pointer to the "this" of our
// object. Grab it here and "attach" it to the window
// proc.
//
pWindow = (ACLWinBase*)(((LPCREATESTRUCT)lParam)->lpCreateParams);
pWindow->SetBoundWindow(hWnd);
break;
}
default:
{
// By default we're going to grab a pointer to the "this" we
// saved at Init time.
//
pWindow = ACLPrivateGetBoundWindow(hWnd);
break;
}
} // switch
if (pWindow)
{
lResult = pWindow->DispatchMessage(uMsg, wParam, lParam);
}
// If we couldn't get a window to dispatch to or the message returned 1
// then go ahead and call DefWindowProc.
//
if (pWindow == NULL || lResult == 1)
{
lResult = ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return lResult;
} // ACLWindowDispatchProc

Notice that this Window Proc is a straight C function. That’s how the Windows API operates. It’s a C based API, but it provides a mechanism to attach user provided data to a Window Procedure. That’s how this operates. When the Windows WM_CREATE message is sent it includes a this pointer to derived Window Class, which can be looked up later based on the HWND identifier.

In the gist above the code that calls pWindow->DispatchMessage knows how to look at the dispatch table and direct the message to the appropriate pointer to a method.

LRESULT ACLWinBase::DispatchMessage
(
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam
)
{
LRESULT lResult(1);
UINT uSubMsg(WM_NULL); // By default this really isn't looked at.
// If we've received a W_COMMAND message or a
// W_NOTIFY message the dive into the sub-messages
// contained in these messages.
//
switch (uMsg)
{
case WM_COMMAND:
{
uSubMsg = LOWORD(wParam);
break;
}
case WM_NOTIFY:
{
LPNMHDR pHDR = (LPNMHDR)lParam;
if (pHDR)
{
// This is the REAL message.
//
uSubMsg = pHDR->code;
}
break;
}
case WM_SYSCOMMAND:
{
uSubMsg = (UINT)wParam;
break;
}
} // Pre-message processing.
bool bail(false);
PWINMSGHNDLR pHandler = NULL;
// Check the Message map for the object, if the message passed
// in here matches one in the map then grab the name of the
// dispatch function and dispatch it!
//
for (ACLWinMessageMapCollection::iterator it = _msgMap.begin();
it != _msgMap.end() && false == bail;
++it)
{
// If the main command(wMsg) and the control ID(uSubMsg)
// match then we've found a command handler for the
// message.
//
if ((uMsg == (*it).uMsg) &&
(uSubMsg == (*it).uSubMsg))
{
pHandler = (*it).pMsgHandler;
bail = true;
}
}
// If we found a matching message handler, then
// dispatch it otherwise return false.
//
if (pHandler)
{
lResult = (this->*pHandler)(wParam, lParam);
}
return lResult;
} // ::DispatchMessage

The code that knows how to send a message to a particular method is (this->*pHandler)(wParam, lParam). This is dereferencing a pointer to a method. All methods have the same signature, which is consistent with how the Windows API works.

Will Code C++ for Food
By the way. This code still builds and runs with up to date versions of Visual Studio on Windows 10. A lot of this code was written in 1995 and has been tweaked over time to keep it up to date with changes to the Windows API.

As I’ve said before, old code never dies.

If you’re feeling really brave you’re welcome to grab my crusty old C++ library and build the simple sample application on your Windows box. Please note, the code is provided as is without warranty, expressed or implied.

Categories
Development

Old Code Never Dies

CheezyPing still worksI happened across some old C++ code I wrote back in 2001. I decided to put it up on GitHub for kicks, might as well put some history up for future generations, right?

I was curious to see if I could build it. The original project was built with Visual Studio 6.0, circa 1998. I have a copy of Visual Studio 2010, so I fired it up pointed it at the original cp.dsw file and was pleasantly surprised it converted. I selected Build All and got a few warnings about some ATL include files I no longer needed, so I removed those. The only other thing that needs correcting are a few warnings related to use of older CRT string copy functions that are not secure. If you’ve upgraded a C or C++ project over the last few years you’ll be familiar with this warning:

warning C4996: '_tcsncpy': This function or variable may be unsafe. Consider using _tcsncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

The code still builds and will execute, but if I were going to use it daily, I’d update it. For now, it’s OK, the code built. Maybe I’ll download a new copy of Visual Studio 2013, get it working with that, and check it in. It would be pretty nifty to create a Mac version by doing an implementation of the HttpIf interface using NSURL and NSURLConnection. This could be a GrilledCheese implementation. Hmmmmmm.

Anywho, once I got a build I hopped out to the command line, and ran it. I was surprised to see, it still works. Amazing.