Your Excel macros worked perfectly in 32-bit Excel 2010. Then someone upgraded to 64-bit Microsoft 365 and everything broke. Or you're converting .xls files to .xlsm and need to make sure the VBA code inside actually runs in modern Excel. Either way, you need a systematic approach to migrating VBA macros — and ideally a tool that catches the problems before your users do. This guide covers what breaks, why it breaks, and how to fix it at scale.
When you convert an .xls file to .xlsm, the VBA code is copied into the new file format. The code itself doesn't change — it's the same VBA source text. So what goes wrong?
The issues fall into three categories:
Declare statements was written for 32-bit Excel. In 64-bit Excel, these declarations need the PtrSafe keyword and certain data types need to change from Long to LongPtr. Without these changes, 64-bit Excel refuses to compile the VBA project at all — every macro in the workbook fails, not just the ones using API calls.CommandBars behavior changes, the removal of certain Application.FileSearch methods, and changes to how Workbook.SaveAs handles format arguments.Of these three, the 64-bit issue is by far the most common and the most disruptive. Let's dig into it.
This is the number-one reason Excel macros break after migration, and it's worth understanding in detail.
In 32-bit VBA, you can call Windows APIs like this:
Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Declare Function FindWindow Lib "user32" _
Alias "FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
These declarations tell VBA how to call functions in external DLLs. They've worked since Excel 97 and they work perfectly in 32-bit Excel today.
In 64-bit Excel, these same declarations cause a compile error: "The code in this project must be updated for use on 64-bit systems." When this error fires, it doesn't just affect the macros that use API calls — it prevents the entire VBA project from compiling. Every macro in the workbook becomes unusable.
64-bit VBA requires two changes to API declarations:
PtrSafe keyword after DeclareLong parameter that represents a pointer or handle to LongPtrThe corrected versions look like this:
Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
Private Declare PtrSafe Function FindWindow Lib "user32" _
Alias "FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As LongPtr
Notice that GetTickCount returns a tick count (a number), so it stays as Long. But FindWindow returns a window handle (a pointer), so it changes to LongPtr. The distinction matters: LongPtr is 4 bytes on 32-bit systems and 8 bytes on 64-bit systems. Using Long for a pointer on 64-bit systems truncates the value, which causes crashes or silent data corruption.
PtrSafe to every declaration without also fixing the pointer types, the code will compile and appear to work — until it encounters a handle value that exceeds 2^31. Then it crashes with no useful error message. This can happen intermittently, making it extremely difficult to diagnose.
If your workbook needs to run on both 32-bit and 64-bit Excel (common in organizations with mixed environments), use conditional compilation:
#If VBA7 Then
Private Declare PtrSafe Function FindWindow Lib "user32" _
Alias "FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As LongPtr
#Else
Private Declare Function FindWindow Lib "user32" _
Alias "FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
#End If
VBA7 is true in Excel 2010 and later (both 32-bit and 64-bit). Win64 is true only in 64-bit environments. For most cases, checking VBA7 and using PtrSafe with LongPtr is sufficient — LongPtr automatically resolves to the correct size on both platforms.
LegacyLeaps scans every VBA module in your workbooks and flags every Declare statement that needs PtrSafe, every Long that should be LongPtr, and every deprecated API call — across your entire file inventory.
Try the Free ScanThese are the API functions we see most often in legacy Excel VBA code, along with the changes needed for 64-bit compatibility:
| API Function | Used For | Key Change for 64-bit |
|---|---|---|
FindWindow | Finding application windows | Return type: Long to LongPtr |
SendMessage | Sending messages to windows | hwnd param and return: LongPtr |
GetWindowLong | Getting window properties | Replace with GetWindowLongPtr |
SetWindowLong | Setting window properties | Replace with SetWindowLongPtr |
ShellExecute | Opening files/URLs | hwnd param: LongPtr |
GetTickCount | Timing operations | Add PtrSafe only (no type changes) |
Sleep | Pausing execution | Add PtrSafe only (no type changes) |
SHGetPathFromIDList | Folder browsing | pidl param: LongPtr |
CoTaskMemFree | Memory management | pv param: LongPtr |
The general rule: any parameter or return value that represents a handle (hwnd, hInstance, hIcon), a pointer, or a memory address must be LongPtr in 64-bit VBA. Parameters that represent counts, sizes, flags, or other numeric values stay as Long.
Application.FileSearch was removed in Office 2007. If your macros use it, they'll fail with "Object not found" at runtime. The replacement is Dir() for simple cases or the FileSystemObject from the Scripting Runtime for more complex searches.
Macros that modify CommandBars to add custom menus or toolbars still run, but the results are different in the Ribbon-based Office interface. Custom CommandBars items appear in the Add-Ins tab rather than in the main menu. If your macro relies on specific menu positions, it needs to be rewritten to use the Ribbon XML model or IRibbonUI callbacks.
Some legacy ActiveX controls (particularly third-party ones like MSFlexGrid, MSChart, or calendar controls) are 32-bit only and don't work in 64-bit Excel. If your forms use these controls, you'll need to replace them with modern equivalents. The built-in MSForms controls (TextBox, ComboBox, ListBox, etc.) work fine on both platforms.
If your VBA project has references to type libraries that don't exist on the target machine — common with DAO 3.6, older ADODB versions, or third-party COM libraries — the project won't compile. Open the VBA editor, go to Tools > References, and look for any reference marked "MISSING." Update these to their modern equivalents or switch to late binding.
For a small number of files (under 10), manual review is feasible. Here's the process:
Declare to find API calls, and FileSearch, CommandBars, and other known problem patternsThis works, but it's slow. A single complex workbook with 20+ VBA modules can take an hour or more to review. For 50 or 100 workbooks, you need a different approach.
A good macro migration tool should do the following:
Long in a Declare statement needs to become LongPtr — only the ones representing handles or pointers. A good tool knows the Windows API well enough to make this distinction automatically.LegacyLeaps does exactly this. The free scan phase reads every VBA module in your .xls and .xlsm files and produces a detailed compatibility report — which files have issues, which specific lines need attention, and which fixes can be applied automatically. You see the full picture before any code is changed.
For organizations with large VBA codebases — and we've seen companies with thousands of lines of VBA spread across hundreds of workbooks — this saves days or weeks of manual code review.
After fixing compatibility issues, you need to test. Here's a practical testing strategy:
Open each .xlsm file, press Alt+F11, then Debug > Compile. If it compiles without errors, the syntax-level issues are resolved. This catches missing PtrSafe declarations, bad type references, and syntax errors. It does not catch runtime bugs.
Run each macro's primary function once with typical input data. Does it produce the expected output? Does it complete without errors? This catches most runtime issues — broken API calls, missing references, changed object model behavior.
For critical macros, test with empty data, large datasets, and boundary conditions. The 64-bit pointer issue is intermittent — it only manifests when a handle value happens to exceed 2^31, which may not happen in a quick test but will happen eventually in production.
Have the actual users of each workbook verify that their workflows work correctly. They know the macros' behavior better than anyone and will catch issues that a developer might miss — things like "the macro used to put the output in column G but now it's in column H."
Download LegacyLeaps and scan your VBA-heavy workbooks for free. Get a complete compatibility report — every PtrSafe issue, every deprecated API, every missing reference — before you change a single line of code.
Download Free ScannerMost VBA macros work without changes after converting from .xls to .xlsm. The main exception is macros that use Windows API calls (Declare statements) — these need PtrSafe declarations and may need LongPtr types to work correctly in 64-bit Excel. Pure VBA code that doesn't call external APIs typically migrates cleanly.
PtrSafe is a keyword required on all Declare statements in 64-bit VBA. It tells the VBA runtime that the API declaration has been reviewed for 64-bit compatibility. Without it, 64-bit Excel refuses to compile the VBA project. You also need to change Long parameters that represent handles or pointers to LongPtr.
LegacyLeaps scans VBA code for 64-bit compatibility issues — missing PtrSafe keywords, incorrect pointer types, deprecated API calls — and flags exactly what needs to change. For many common patterns, it can apply the fixes automatically. This saves hours of manual code review on large codebases.
Practical fixes for legacy Excel and Access problems. No spam.