The Problem (Q-score 6, ranked #47th of 95 in the VBA Core archive)
The scenario as originally posted in 2009
I am wrapping up an office application (VBA) that makes a call to a C# console application to perform some of the heavy lifting for the application (large simulation program). I would like to be able to have the VBA application wait for the console application to complete as well as retreive the exit code from the console application. I have been able to do the former, but have yet to be able to retrieve the exit code from the application. Is there any way that I can use something like
Diagnostics.Process.Start(filePath)
I have seen this in VB but not sure about VBA. Otherwise, any other suggestions?
Why the Win32 API declaration is fragile here
This problem involves a Declare statement, which means 32-bit vs 64-bit compatibility is in play. Office 64-bit requires the PtrSafe keyword and LongPtr data types for any handles — the most common root cause of the exact symptom described.
The Verified Solution — solid answer (above median) (+10)
45-line VBA Core pattern (copy-ready)
Have a look at WaitForSingleObject and GetExitCodeProcess functions.
Example Usage:
Private Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Long, lpExitCode As Long) As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" (ByVal dwFlags As Long, lpSource As Any, ByVal dwMessageId As Long, ByVal dwLanguageId As Long, ByVal lpBuffer As String, ByVal nSize As Long, Arguments As Long) As Long
Public Const INFINITE = &HFFFF
Public Const PROCESS_ALL_ACCESS = &H1F0FFF
Sub RunApplication(ByVal Cmd as String)
lTaskID = Shell(Cmd, vbNormalFocus)
//Get process handle
lPID = OpenProcess(PROCESS_ALL_ACCESS, True, lTaskID)
If lPID Then
//Wait for process to finish
Call WaitForSingleObject(lPID, INFINITE)
//Get Exit Process
If GetExitCodeProcess(lPID, lExitCode) Then
//Received value
MsgBox "Successfully returned " & lExitCode, vbInformation
Else
MsgBox "Failed: " & DLLErrorText(Err.LastDllError), vbCritical
End If
Else
MsgBox "Failed: " & DLLErrorText(Err.LastDllError), vbCritical
End If
lTaskID = CloseHandle(lPID)
End Sub
Public Function DLLErrorText(ByVal lLastDLLError As Long) As String
Dim sBuff As String * 256
Dim lCount As Long
Const FORMAT_MESSAGE_ALLOCATE_BUFFER = &H100, FORMAT_MESSAGE_ARGUMENT_ARRAY = &H2000
Const FORMAT_MESSAGE_FROM_HMODULE = &H800, FORMAT_MESSAGE_FROM_STRING = &H400
Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000, FORMAT_MESSAGE_IGNORE_INSERTS = &H200
Const FORMAT_MESSAGE_MAX_WIDTH_MASK = &HFF
lCount = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS, 0, lLastDLLError, 0&, sBuff, Len(sBuff), ByVal 0)
If lCount Then
DLLErrorText = Left$(sBuff, lCount - 2) \Remove line feeds
End If
End Function
Error-handling details to lift with the snippet
This answer wires error flow through MsgBox / Err.Description. Keep that intact: stripping it to “make it cleaner” removes the signal you’ll need when the macro fails silently on a user machine.
When to Use It — vintage (14+ years old, pre-2013)
Ranked #47th in its category — specialized fit
This pattern sits in the 92% tail relative to the top answer. Reach for it when your scenario closely matches the question title; otherwise browse the VBA Core archive for a higher-consensus alternative.
What changed between 2009 and 2026
The answer is 17 years old. The VBA Core object model has been stable across Office 2013, 2016, 2019, 2021, 365, and 2024/2026 LTSC, so the pattern still compiles. Changes that might affect you: 64-bit API declarations (use PtrSafe), blocked macros in downloaded files (Mark-of-the-Web), and the shift toward Office Scripts for web-first workflows.