Debugging: Migrating Legacy 32-bit C++ Code to 64-bit

Making 32-bit code build correctly for 64-bit in a single codebase

Truck with Oversize Load

Truck with Oversize Load

HOLLYWOOD, CA (GoshRobin) 2022/08/18 –So we have some 20-year-old Win32 code we’d like to build in Win64. That will take a bit of work. Our 32-bit code needs to be made to fit. Imagine a truck with an oversize load.

Here’s a 64-bit error message from our Visual Studio output:

2>C:\code\gitlab\glaslens\zoom_plus\utility\zoomplus\about.cpp(62,87): error C2065: 'GWL_WNDPROC': undeclared identifier
2>C:\code\gitlab\glaslens\zoom_plus\utility\zoomplus\about.cpp(62,141): warning C4311: 'reinterpret_cast': pointer truncation from 'LRESULT (__cdecl *)(HWND,UINT,WPARAM,LPARAM)' to 'long'
2>C:\code\gitlab\glaslens\zoom_plus\utility\zoomplus\about.cpp(62,141): warning C4302: 'reinterpret_cast': truncation from 'LRESULT (__cdecl *)(HWND,UINT,WPARAM,LPARAM)' to 'long'

And the errant code:

 case WM_INITDIALOG:
{
wndprocOldStatic = (WNDPROC)SetWindowLong( GetDlgItem( hwnd, IDC_MAIL_ME_STATIC ), GWL_WNDPROC, reinterpret_cast<long>( StaticWindowProc ) );

Searching the Internet, onto StackOverflow

Next, we’ll do an Internet search of “GWL_WNDPROC undeclared” to check if anyone else has encountered this issue before. They have. What they say is this is a known issue specific to Win64. Since our legacy Zoom+ source code is so old, it isn’t surprising it has 64-bit issues like this. The code has only ever been built before on Win32.

We make code changes to fix it for Win64, using the tips from StackOverflow:

#ifdef _WIN64
#define GWL_WNDPROC GWLP_WNDPROC
#endif

case WM_INITDIALOG:
{
wndprocOldStatic = (WNDPROC)SetWindowLongPtr( GetDlgItem( hwnd, IDC_MAIL_ME_STATIC ), GWL_WNDPROC, reinterpret_cast<LONG_PTR>( StaticWindowProc ) );
(WNDPROC)SetWindowLongPtr( GetDlgItem( hwnd, IDC_VISIT_ME_STATIC ), GWL_WNDPROC, reinterpret_cast<LONG_PTR>( StaticWindowProc ) );

We used a _WIN64 ifdef, rather than changing GWL_WNDPROC directly to GWLP_WNDPROC, so as to not break our Win32 build.

About.cpp has another Win64 bug:

1>C:\code\gitlab\glaslens\zoom_plus\utility\zoomplus\about.cpp(154,2): error C2664: 'INT_PTR DialogBoxParamA(HINSTANCE,LPCSTR,HWND,DLGPROC,LPARAM)': cannot convert argument 4 from 'overloaded-function' to 'DLGPROC'

void About( HWND hwndParent )
{
ASSERT_VALID_HWND( hwndParent );
DialogBox( g_hInst, MAKEINTRESOURCE( IDD_ABOUTBOX ), hwndParent, About );
}

We won’t need help from StackOverflow with this one. The Visual Studio error is telling us our About function does not match the function type DLGPROC. What is the right function signature? We right-click and use Go to Definition or Go to Declaration to in a few jumps find the right Windows header that defines DLGPROC:

typedef INT_PTR (CALLBACK* DLGPROC)(HWND, UINT, WPARAM, LPARAM);

We notice something misleading about our code. Our About function isn’t calling itself, as a C programmer might imagine. Rather, we have two C++ functions called About. Our callback function has the same name but a different function signature from another function named About. To avoid this function overloading confusion, we rename our About callback AboutProc.

The DLGPROC problem with AboutProc was it didn’t have INT_PTR as the return type. We change that and fix a couple casts that need to be INT_PTR.

We will also clean up 64-bit truncation warnings like this:

1>C:\code\gitlab\glaslens\zoom_plus\utility\zoomplus\about.cpp(94,7): warning C4311: 'type cast': pointer truncation from 'HINSTANCE' to 'UINT'

case IDC_VISIT_ME_STATIC:
if( HIWORD( wParam ) == BN_CLICKED )
{
HINSTANCE h = ShellExecute( NULL, _T("open"), g_szVisitMe.c_str(), NULL, NULL, SW_SHOWNORMAL );
if( (UINT)h > 32 )

What’s happening here is HINSTANCE is a 64-bit handle and UINT is a 32-bit integer. This is an example of why we want to avoid using the Windows hack of specifying bit size in code. The fix is to choose a C++ type that is portable across 32 and 64-bit code. Instead of casting to UINT, which is always 32 bits in Windows, we’ll use the C++ type uintptr_t. This type is 32 bits or 64 bits depending upon the platform.

Porting from Win32 to Win64 provokes a lot of compiler errors and warnings in our legacy code. To work more efficiently, to reduce the clutter of output messages, as we make fixes to About.cpp we have Visual Studio only compile that file rather that trying to build everything. Knock down bugs in each source file one by one. Then do a build all and move on to the next cpp file to fix.

Developer Feedback

Some notes from me to the developer, to review progress.

Glad you are making progress! Congratulations!

Am wondering if you saw my instructions to build Zoom+ for Win32. While I didn’t tell you not to try Win64, I was surprised. It was confusing that your bug ticket didn’t note the reason you encountered errors is you chose to build on Win64, which was not yet supported. To make it easier for you, I had intended you do testing the 32-bit build first. Not a problem that you took the path to complete the Win64 port first, then test.

I fixed all the 64-bit compile errors, but not everything, so as to leave you something to do. There are 37 warnings remaining. Would take me maybe an hour to fix. You may need longer as still learning. Until all these warnings are fixed, expect runtime crashes from pointer truncation. If you get stuck or it seems too hard, let me know?

Once you complete the 64-bit clean build, git commit, then go ahead and test. Expect you will find many user issues, both bugs and feature requests to report. I’ve deliberately not created them, again to leave something for you to do. Hope you will open many tickets, but not more tickets than necessary. We don’t need multiple bug reports for similar bugs that all have the same cause.

Your Agile productivity score today:

1: Tickets opened/touched
0: Git commits

Total score: 1

My score:

2: Tickets opened/touched
1: Git commits

Total score: 3

You’re off to a good start. Appreciate you.

For future, please create a proper MR2 bug report. Don’t expect anyone to have the patience to study screenshots of Visual Studio.

For future, ask me to close tickets when you think they are ready to be closed. Do not close them yourself.

About Robin Rowe

Robin Rowe

I’m Robin Rowe. People call me Robin or Rob, sometimes professor. I work in advanced product design and innovation management.

As UN WHO Augmented Reality Group Manager and XR Games Producer, I developed a medical metaverse, an AR/VR hospital simulation to train doctors worldwide to save lives.

As chief technologist at multi-billion dollar systems integrator SAIC, I was the founding director of their AI research lab and an enterprise manager with P&L responsibility for commercial and defense divisions. I constructed the Chicago NBC-TV studios, advanced national critical infrastructure by adding Smart Cities capability to the U.S. traffic control system, created real-time AI for DoD national defense crisis management, and developed the real-time motion-capture animation system used to create digital stunt doubles in superhero films such as Disney Marvel Spider-Man.

I led the team that won the Novartis Biome innovation prize in 2019, using AI computer vision to analyze skin disease. I’ve taught C++ as a Computer Science professor at the Naval Postgraduate School and the University of Washington. Former DARPA PI for AI and digital video. Former navy research scientist for VR war gaming and vision research. I’ve chaired innovation committees at ANSI/ISO and The CFO Alliance. I’ve developed financial software for Fortune 500 companies and large NGOs. I’m a thesis advisor to graduate and doctoral students in metaverse R&D.

I founded my first start-up, a car company, when I was 16. My family is in real estate and agriculture, owns the largest organic farm in Illinois. Not wishing to run the family business, I went another way. I have 30 years experience in product design and financial systems. And, with trading stocks and now crypto on my own account, I’ve made high ROI year over year.