├── .gitignore ├── README.md ├── build.bat ├── messagebox-win.exe ├── messagebox.cpp └── messagebox.exe /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore debugging and intermediate files. 2 | *.obj 3 | messagebox/* 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MessageBox()/MessageBeep() Windows API Command-Line Utility 2 | =========================================================== 3 | 4 | A complete, robust command-line utility to construct highly customized calls to the MessageBox() and MessageBeep() Windows APIs. Released under a MIT or LGPL license. 5 | 6 | This project is intended primarily for use from batch files (.bat) and other scripts to display a modal message dialog. If it can be done with MessageBox(), it can be done with this command-line program. 7 | 8 | Why would you need this? To easily display native Windows message boxes from command-line scripts that also returns the user's response (i.e. which button was pressed). Useful for handling critical application/script failures. 9 | 10 | [![Donate](https://cubiclesoft.com/res/donate-shield.png)](https://cubiclesoft.com/donate/) [![Discord](https://img.shields.io/discord/777282089980526602?label=chat&logo=discord)](https://cubiclesoft.com/product-support/github/) 11 | 12 | Features 13 | -------- 14 | 15 | * Command-line action! 16 | * Verbose mode tells you exactly how MessageBox() or MessageBeep() will be called. No more guessing! 17 | * Pre-built binaries using Visual Studio (statically linked C++ runtime, minimal file size of ~85K, direct Win32 API calls). 18 | * Windows subsystem variant. 19 | * Unicode support. 20 | * Offers almost everything MessageBox() and MessageBeep() offers. 21 | * Has a liberal open source license. MIT or LGPL, your choice. 22 | * Sits on GitHub for all of that pull request and issue tracker goodness to easily submit changes and ideas respectively. 23 | 24 | Useful Information 25 | ------------------ 26 | 27 | Running the command with the `/?` option will display the options: 28 | 29 | ``` 30 | (C) 2021 CubicleSoft. All Rights Reserved. 31 | 32 | Syntax: messagebox.exe [options] [Text [Caption/Title]] 33 | 34 | Options: 35 | /v 36 | Verbose mode. 37 | 38 | /p 39 | Parse Text for special escape sequences. 40 | Only '\n' and '\\' are supported. 41 | 42 | /f=Buttons 43 | Sets the buttons of the message box. 44 | The 'Buttons' can be one of: 45 | MB_ABORTRETRYIGNORE 46 | MB_CANCELTRYCONTINUE 47 | MB_OK (Default) 48 | MB_OKCANCEL 49 | MB_RETRYCANCEL 50 | MB_YESNO 51 | MB_YESNOCANCEL 52 | 53 | /f=Icon 54 | Sets the icon of the message box. 55 | The 'Icon' can be one of: 56 | MB_ICONERROR 57 | MB_ICONWARNING 58 | MB_ICONINFORMATION 59 | MB_ICONQUESTION 60 | 61 | /f=DefaultButton 62 | Sets the default button for the message box. 63 | The 'DefaultButton' can be one of: 64 | MB_DEFBUTTON1 (Default) 65 | MB_DEFBUTTON2 66 | MB_DEFBUTTON3 67 | MB_DEFBUTTON4 68 | 69 | /f=Modality 70 | Sets the modality for the message box. 71 | The 'Modality' can be one of: 72 | MB_APPLMODAL (Default) 73 | MB_SYSTEMMODAL 74 | MB_TASKMODAL 75 | 76 | /f=MiscFlag 77 | Sets the miscellaneous flags for the message box. 78 | Multiple /f options can be specified. 79 | Each 'MiscFlag' can be one of: 80 | MB_SIMPLEBEEP (Only when Title is not used) 81 | MB_HELP (Probably won't work) 82 | MB_DEFAULT_DESKTOP_ONLY 83 | MB_RIGHT 84 | MB_RTLREADING 85 | MB_SETFOREGROUND 86 | MB_TOPMOST 87 | MB_SERVICE_NOTIFICATION 88 | 89 | /w=Milliseconds 90 | The amount of time, in milliseconds, to wait. 91 | The default behavior is to wait indefinitely. 92 | This feature relies on an undocumented Windows API. 93 | ``` 94 | 95 | Example with verbose output: 96 | 97 | ``` 98 | C:\>messagebox.exe /v /f=MB_OKCANCEL /f=MB_ICONWARNING "The application crashed. See the log file for details. Press OK to continue or Cancel to exit now." "Oh Dear!" 99 | Arguments: 100 | argv[0] = messagebox.exe 101 | argv[1] = /v 102 | argv[2] = /f=MB_OKCANCEL 103 | argv[3] = /f=MB_ICONWARNING 104 | argv[4] = The application crashed. See the log file for details. Press OK to continue or Cancel to exit now. 105 | argv[5] = Oh Dear! 106 | 107 | MessageBox( 108 | hWnd = 0x008F0D4A, 109 | lpText = The application crashed. See the log file for details. Press OK to continue or Cancel to exit now., 110 | lpCaption = Oh Dear!, 111 | uType = MB_OKCANCEL | MB_ICONWARNING | MB_DEFBUTTON1 | MB_APPLMODAL 112 | ); 113 | ``` 114 | 115 | Which waits for the message box to be closed and returns the return code of the selected button. 116 | 117 | Windows Subsystem Variant 118 | ------------------------- 119 | 120 | While `messagebox.exe` is intended for use with console apps, `messagebox-win.exe` is intended for detached console and GUI applications. Starting `messagebox.exe` in certain situations will briefly flash a console window before displaying the error message. Calling `messagebox-win.exe` instead will no longer show the console window. 121 | 122 | Why not just use `messagebox-win.exe`? Since `messagebox-win.exe` starts as a Windows GUI application, there is the tendency for it to be run in the background and the message box that displays may not behave as a modal dialog. The software is a little bit trickier to work with as a result. It's also a few KB larger than `messagebox.exe`. 123 | 124 | There is one additional option specifically for `messagebox-win.exe` called `/attach` which attempts to attach to the console of the parent process (if any). 125 | 126 | Sources 127 | ------- 128 | 129 | The MessageBox() and MessageBeep() APIs on MSDN Library have the intimate details on each option: 130 | 131 | * https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-messagebox 132 | * https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-messagebeep 133 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cls 3 | 4 | cl /Ox messagebox.cpp -D_USING_V110_SDK71_ -DSUBSYSTEM_CONSOLE /link /FILEALIGN:512 /OPT:REF /OPT:ICF /INCREMENTAL:NO /subsystem:console,5.01 user32.lib /out:messagebox.exe 5 | cl /Ox messagebox.cpp -D_USING_V110_SDK71_ -DSUBSYSTEM_WINDOWS /link /FILEALIGN:512 /OPT:REF /OPT:ICF /INCREMENTAL:NO /subsystem:windows,5.01 user32.lib shell32.lib /out:messagebox-win.exe 6 | -------------------------------------------------------------------------------- /messagebox-win.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cubiclesoft/messagebox-windows/33be748fa41d01b75161c592d72815bc6ed26a7c/messagebox-win.exe -------------------------------------------------------------------------------- /messagebox.cpp: -------------------------------------------------------------------------------- 1 | // A simple program whose sole job is to execute modal MessageBox()/MessageBeep() API calls and returning the result. 2 | // Useful for notifying the user and/or asking Yes/No/FileNotFound questions. Limited to what MessageBox() can do. 3 | // 4 | // (C) 2019 CubicleSoft. All Rights Reserved. 5 | 6 | // Implemented as a single file compilation unit. 7 | 8 | #define UNICODE 9 | #define _UNICODE 10 | #define _CRT_SECURE_NO_WARNINGS 11 | 12 | #ifdef _MBCS 13 | #undef _MBCS 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | typedef int (__stdcall *MessageBoxTimeoutFunc)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType, WORD wLanguageId, DWORD dwMilliseconds); 22 | 23 | #ifdef SUBSYSTEM_WINDOWS 24 | // If the caller is a console application and is waiting for this application to complete, then attach to the console. 25 | void InitVerboseMode(void) 26 | { 27 | if (::AttachConsole(ATTACH_PARENT_PROCESS)) 28 | { 29 | if (::GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) 30 | { 31 | freopen("CONOUT$", "w", stdout); 32 | setvbuf(stdout, NULL, _IONBF, 0); 33 | } 34 | 35 | if (::GetStdHandle(STD_ERROR_HANDLE) != INVALID_HANDLE_VALUE) 36 | { 37 | freopen("CONOUT$", "w", stderr); 38 | setvbuf(stderr, NULL, _IONBF, 0); 39 | } 40 | } 41 | } 42 | #endif 43 | 44 | void DumpSyntax(TCHAR *currfile) 45 | { 46 | #ifdef SUBSYSTEM_WINDOWS 47 | InitVerboseMode(); 48 | #endif 49 | 50 | _tprintf(_T("(C) 2021 CubicleSoft. All Rights Reserved.\n\n")); 51 | 52 | _tprintf(_T("Syntax: %s [options] [Text [Caption/Title]]\n\n"), currfile); 53 | 54 | _tprintf(_T("Options:\n")); 55 | 56 | _tprintf(_T("\t/v\n\ 57 | \tVerbose mode.\n\ 58 | \n\ 59 | \t/p\n\ 60 | \tParse Text for special escape sequences.\n\ 61 | \tOnly '\\n' and '\\\\' are supported.\n\ 62 | \n\ 63 | \t/f=Buttons\n\ 64 | \tSets the buttons of the message box.\n\ 65 | \tThe 'Buttons' can be one of:\n\ 66 | \tMB_ABORTRETRYIGNORE\n\ 67 | \tMB_CANCELTRYCONTINUE\n\ 68 | \tMB_OK (Default)\n\ 69 | \tMB_OKCANCEL\n\ 70 | \tMB_RETRYCANCEL\n\ 71 | \tMB_YESNO\n\ 72 | \tMB_YESNOCANCEL\n\ 73 | \n\ 74 | \t/f=Icon\n\ 75 | \tSets the icon of the message box.\n\ 76 | \tThe 'Icon' can be one of:\n\ 77 | \tMB_ICONERROR\n\ 78 | \tMB_ICONWARNING\n\ 79 | \tMB_ICONINFORMATION\n\ 80 | \tMB_ICONQUESTION\n\ 81 | \n\ 82 | \t/f=DefaultButton\n\ 83 | \tSets the default button for the message box.\n\ 84 | \tThe 'DefaultButton' can be one of:\n\ 85 | \tMB_DEFBUTTON1 (Default)\n\ 86 | \tMB_DEFBUTTON2\n\ 87 | \tMB_DEFBUTTON3\n\ 88 | \tMB_DEFBUTTON4\n\ 89 | \n\ 90 | \t/f=Modality\n\ 91 | \tSets the modality for the message box.\n\ 92 | \tThe 'Modality' can be one of:\n\ 93 | \tMB_APPLMODAL (Default)\n\ 94 | \tMB_SYSTEMMODAL\n\ 95 | \tMB_TASKMODAL\n\ 96 | \n\ 97 | \t/f=MiscFlag\n\ 98 | \tSets the miscellaneous flags for the message box.\n\ 99 | \tMultiple /f options can be specified.\n\ 100 | \tEach 'MiscFlag' can be one of:\n\ 101 | \tMB_SIMPLEBEEP (Only when Title is not used)\n\ 102 | \tMB_HELP (Probably won't work)\n\ 103 | \tMB_DEFAULT_DESKTOP_ONLY\n\ 104 | \tMB_RIGHT\n\ 105 | \tMB_RTLREADING\n\ 106 | \tMB_SETFOREGROUND\n\ 107 | \tMB_TOPMOST\n\ 108 | \tMB_SERVICE_NOTIFICATION\n\ 109 | \n\ 110 | \t/w=Milliseconds\n\ 111 | \tThe amount of time, in milliseconds, to wait.\n\ 112 | \tThe default behavior is to wait indefinitely.\n\ 113 | \tThis feature relies on an undocumented Windows API.\n\n")); 114 | 115 | #ifdef SUBSYSTEM_WINDOWS 116 | _tprintf(_T("\t/attach\n")); 117 | _tprintf(_T("\t\tAttempt to attach to a parent console if it exists.\n\n")); 118 | #endif 119 | } 120 | 121 | int _tmain(int argc, TCHAR **argv) 122 | { 123 | bool verbose = false; 124 | UINT buttons = MB_OK; 125 | UINT icon = 0; 126 | UINT defbutton = MB_DEFBUTTON1; 127 | UINT modality = MB_APPLMODAL; 128 | bool simplebeep = false; 129 | bool parsetext = false; 130 | UINT flags = 0; 131 | HMODULE modulehandle = NULL; 132 | DWORD waitamount = INFINITE; 133 | 134 | // Process command-line options. 135 | int x; 136 | for (x = 1; x < argc; x++) 137 | { 138 | if (!_tcsicmp(argv[x], _T("/v"))) verbose = true; 139 | else if (!_tcsicmp(argv[x], _T("/?")) || !_tcsicmp(argv[x], _T("/h"))) 140 | { 141 | DumpSyntax(argv[0]); 142 | 143 | return 0; 144 | } 145 | else if (!_tcsicmp(argv[x], _T("/p"))) parsetext = true; 146 | else if (!_tcsicmp(argv[x], _T("/f=MB_ABORTRETRYIGNORE"))) buttons = MB_ABORTRETRYIGNORE; 147 | else if (!_tcsicmp(argv[x], _T("/f=MB_CANCELTRYCONTINUE"))) buttons = MB_CANCELTRYCONTINUE; 148 | else if (!_tcsicmp(argv[x], _T("/f=MB_OK"))) buttons = MB_OK; 149 | else if (!_tcsicmp(argv[x], _T("/f=MB_OKCANCEL"))) buttons = MB_OKCANCEL; 150 | else if (!_tcsicmp(argv[x], _T("/f=MB_RETRYCANCEL"))) buttons = MB_RETRYCANCEL; 151 | else if (!_tcsicmp(argv[x], _T("/f=MB_YESNO"))) buttons = MB_YESNO; 152 | else if (!_tcsicmp(argv[x], _T("/f=MB_YESNOCANCEL"))) buttons = MB_YESNOCANCEL; 153 | else if (!_tcsicmp(argv[x], _T("/f=MB_ICONERROR"))) icon = MB_ICONERROR; 154 | else if (!_tcsicmp(argv[x], _T("/f=MB_ICONWARNING"))) icon = MB_ICONWARNING; 155 | else if (!_tcsicmp(argv[x], _T("/f=MB_ICONINFORMATION"))) icon = MB_ICONINFORMATION; 156 | else if (!_tcsicmp(argv[x], _T("/f=MB_ICONQUESTION"))) icon = MB_ICONQUESTION; 157 | else if (!_tcsicmp(argv[x], _T("/f=MB_DEFBUTTON1"))) defbutton = MB_DEFBUTTON1; 158 | else if (!_tcsicmp(argv[x], _T("/f=MB_DEFBUTTON2"))) defbutton = MB_DEFBUTTON2; 159 | else if (!_tcsicmp(argv[x], _T("/f=MB_DEFBUTTON3"))) defbutton = MB_DEFBUTTON3; 160 | else if (!_tcsicmp(argv[x], _T("/f=MB_DEFBUTTON4"))) defbutton = MB_DEFBUTTON4; 161 | else if (!_tcsicmp(argv[x], _T("/f=MB_APPLMODAL"))) modality = MB_APPLMODAL; 162 | else if (!_tcsicmp(argv[x], _T("/f=MB_SYSTEMMODAL"))) modality = MB_SYSTEMMODAL; 163 | else if (!_tcsicmp(argv[x], _T("/f=MB_TASKMODAL"))) modality = MB_TASKMODAL; 164 | else if (!_tcsicmp(argv[x], _T("/f=MB_SIMPLEBEEP"))) simplebeep = true; 165 | else if (!_tcsicmp(argv[x], _T("/f=MB_HELP"))) flags |= MB_HELP; 166 | else if (!_tcsicmp(argv[x], _T("/f=MB_DEFAULT_DESKTOP_ONLY"))) flags |= MB_DEFAULT_DESKTOP_ONLY; 167 | else if (!_tcsicmp(argv[x], _T("/f=MB_RIGHT"))) flags |= MB_RIGHT; 168 | else if (!_tcsicmp(argv[x], _T("/f=MB_RTLREADING"))) flags |= MB_RTLREADING; 169 | else if (!_tcsicmp(argv[x], _T("/f=MB_SETFOREGROUND"))) flags |= MB_SETFOREGROUND; 170 | else if (!_tcsicmp(argv[x], _T("/f=MB_TOPMOST"))) flags |= MB_TOPMOST; 171 | else if (!_tcsicmp(argv[x], _T("/f=MB_SERVICE_NOTIFICATION"))) flags |= MB_SERVICE_NOTIFICATION; 172 | else if (!_tcsncicmp(argv[x], _T("/w="), 3)) waitamount = _tstoi(argv[x] + 3); 173 | else if (!_tcsicmp(argv[x], _T("/attach"))) 174 | { 175 | #ifdef SUBSYSTEM_WINDOWS 176 | // For the Windows subsystem only, attempt to attach to a parent console if it exists. 177 | InitVerboseMode(); 178 | #endif 179 | } 180 | else 181 | { 182 | // Probably reached the command to execute portion of the arguments. 183 | break; 184 | } 185 | } 186 | 187 | if (verbose) 188 | { 189 | #ifdef SUBSYSTEM_WINDOWS 190 | InitVerboseMode(); 191 | #endif 192 | 193 | _tprintf(_T("Arguments:\n")); 194 | for (int x2 = 0; x2 < argc; x2++) 195 | { 196 | _tprintf(_T("\targv[%d] = %s\n"), x2, argv[x2]); 197 | } 198 | _tprintf(_T("\n")); 199 | } 200 | 201 | // Display a message box or play a system beep sound. 202 | int result; 203 | if (x == argc) 204 | { 205 | if (simplebeep) icon = 0xFFFFFFFF; 206 | 207 | if (verbose) 208 | { 209 | _tprintf(_T("MessageBeep(\n")); 210 | _tprintf(_T("\tuType = ")); 211 | if (icon == MB_OK) _tprintf(_T("MB_OK")); 212 | else if (icon == MB_ICONERROR) _tprintf(_T("MB_ICONERROR")); 213 | else if (icon == MB_ICONWARNING) _tprintf(_T("MB_ICONWARNING")); 214 | else if (icon == MB_ICONINFORMATION) _tprintf(_T("MB_ICONINFORMATION")); 215 | else if (icon == MB_ICONQUESTION) _tprintf(_T("MB_ICONQUESTION")); 216 | else _tprintf(_T("0xFFFFFF (MB_SIMPLEBEEP)")); 217 | _tprintf(_T("\n")); 218 | _tprintf(_T(");\n")); 219 | } 220 | 221 | result = (int)::MessageBeep(icon); 222 | } 223 | else 224 | { 225 | LPCTSTR maintext = argv[x]; 226 | LPCTSTR caption = (x + 1 < argc ? argv[x + 1] : NULL); 227 | 228 | // Handle easier parsing of newlines in the text. 229 | if (parsetext) 230 | { 231 | LPTSTR maintext2 = (LPTSTR)malloc(_tcslen(maintext) * sizeof(TCHAR)); 232 | 233 | size_t x3 = 0; 234 | 235 | for (size_t x2 = 0; maintext[x2]; x2++) 236 | { 237 | if (maintext[x2] == _T('\\') && maintext[x2 + 1] == _T('n')) 238 | { 239 | x2++; 240 | 241 | maintext2[x3++] = _T('\n'); 242 | } 243 | else if (maintext[x2] == _T('\\') && maintext[x2 + 1] == _T('\\')) 244 | { 245 | x2++; 246 | 247 | maintext2[x3++] = _T('\\'); 248 | } 249 | else 250 | { 251 | maintext2[x3++] = maintext[x2]; 252 | } 253 | } 254 | 255 | maintext2[x3] = _T('\0'); 256 | 257 | maintext = maintext2; 258 | } 259 | 260 | if (verbose) 261 | { 262 | _tprintf(_T("MessageBox(\n")); 263 | _tprintf(_T("\thWnd = 0x%p,\n"), (void *)::GetConsoleWindow()); 264 | _tprintf(_T("\tlpText = %s,\n"), maintext); 265 | _tprintf(_T("\tlpCaption = %s,\n"), (caption != NULL ? caption : _T("NULL"))); 266 | _tprintf(_T("\tuType = ")); 267 | if (buttons == MB_ABORTRETRYIGNORE) _tprintf(_T("MB_ABORTRETRYIGNORE")); 268 | else if (buttons == MB_CANCELTRYCONTINUE) _tprintf(_T("MB_CANCELTRYCONTINUE")); 269 | else if (buttons == MB_OK) _tprintf(_T("MB_OK")); 270 | else if (buttons == MB_OKCANCEL) _tprintf(_T("MB_OKCANCEL")); 271 | else if (buttons == MB_RETRYCANCEL) _tprintf(_T("MB_RETRYCANCEL")); 272 | else if (buttons == MB_YESNO) _tprintf(_T("MB_YESNO")); 273 | else if (buttons == MB_YESNOCANCEL) _tprintf(_T("MB_YESNOCANCEL")); 274 | 275 | if (icon == MB_ICONERROR) _tprintf(_T(" | MB_ICONERROR")); 276 | else if (icon == MB_ICONWARNING) _tprintf(_T(" | MB_ICONWARNING")); 277 | else if (icon == MB_ICONINFORMATION) _tprintf(_T(" | MB_ICONINFORMATION")); 278 | else if (icon == MB_ICONQUESTION) _tprintf(_T(" | MB_ICONQUESTION")); 279 | 280 | if (defbutton == MB_DEFBUTTON1) _tprintf(_T(" | MB_DEFBUTTON1")); 281 | else if (defbutton == MB_DEFBUTTON2) _tprintf(_T(" | MB_DEFBUTTON2")); 282 | else if (defbutton == MB_DEFBUTTON3) _tprintf(_T(" | MB_DEFBUTTON3")); 283 | else if (defbutton == MB_DEFBUTTON4) _tprintf(_T(" | MB_DEFBUTTON4")); 284 | 285 | if (modality == MB_APPLMODAL) _tprintf(_T(" | MB_APPLMODAL")); 286 | else if (modality == MB_SYSTEMMODAL) _tprintf(_T(" | MB_SYSTEMMODAL")); 287 | else if (modality == MB_TASKMODAL) _tprintf(_T(" | MB_TASKMODAL")); 288 | 289 | if (flags & MB_HELP) _tprintf(_T(" | MB_HELP")); 290 | if (flags & MB_DEFAULT_DESKTOP_ONLY) _tprintf(_T(" | MB_DEFAULT_DESKTOP_ONLY")); 291 | if (flags & MB_RIGHT) _tprintf(_T(" | MB_RIGHT")); 292 | if (flags & MB_RTLREADING) _tprintf(_T(" | MB_RTLREADING")); 293 | if (flags & MB_SETFOREGROUND) _tprintf(_T(" | MB_SETFOREGROUND")); 294 | if (flags & MB_TOPMOST) _tprintf(_T(" | MB_TOPMOST")); 295 | if (flags & MB_SERVICE_NOTIFICATION) _tprintf(_T(" | MB_SERVICE_NOTIFICATION")); 296 | _tprintf(_T("\n")); 297 | _tprintf(_T(");\n")); 298 | } 299 | 300 | bool displayed = false; 301 | 302 | if (waitamount != INFINITE) 303 | { 304 | HMODULE modulehandle = ::LoadLibrary(_T("user32.dll")); 305 | 306 | #ifdef UNICODE 307 | MessageBoxTimeoutFunc TempMBTPtr = (MessageBoxTimeoutFunc)::GetProcAddress(modulehandle, "MessageBoxTimeoutW"); 308 | #else 309 | MessageBoxTimeoutFunc TempMBTPtr = (MessageBoxTimeoutFunc)::GetProcAddress(modulehandle, "MessageBoxTimeoutA"); 310 | #endif 311 | 312 | if (TempMBTPtr != NULL) 313 | { 314 | result = TempMBTPtr(::GetConsoleWindow(), maintext, caption, buttons | icon | defbutton | modality | flags, 0, waitamount); 315 | 316 | displayed = true; 317 | } 318 | } 319 | 320 | if (!displayed) result = ::MessageBox(::GetConsoleWindow(), maintext, caption, buttons | icon | defbutton | modality | flags); 321 | } 322 | 323 | if (!result) 324 | { 325 | #ifdef SUBSYSTEM_WINDOWS 326 | InitVerboseMode(); 327 | #endif 328 | 329 | DWORD errnum = ::GetLastError(); 330 | LPTSTR errmsg = NULL; 331 | 332 | ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errmsg, 0, NULL); 333 | if (x == argc) _tprintf(_T("An error occurred while calling MessageBeep():\n")); 334 | else _tprintf(_T("An error occurred while calling MessageBox():\n")); 335 | if (errmsg == NULL) _tprintf(_T("%d - Unknown error\n\n"), errnum); 336 | else 337 | { 338 | _tprintf(_T("%d - %s\n"), errnum, errmsg); 339 | ::LocalFree(errmsg); 340 | } 341 | } 342 | 343 | if (modulehandle != NULL) ::FreeLibrary(modulehandle); 344 | 345 | return result; 346 | } 347 | 348 | #ifdef SUBSYSTEM_WINDOWS 349 | #ifndef UNICODE 350 | // Swiped from: https://stackoverflow.com/questions/291424/canonical-way-to-parse-the-command-line-into-arguments-in-plain-c-windows-api 351 | LPSTR* CommandLineToArgvA(LPSTR lpCmdLine, INT *pNumArgs) 352 | { 353 | int retval; 354 | retval = ::MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, lpCmdLine, -1, NULL, 0); 355 | if (!SUCCEEDED(retval)) return NULL; 356 | 357 | LPWSTR lpWideCharStr = (LPWSTR)malloc(retval * sizeof(WCHAR)); 358 | if (lpWideCharStr == NULL) return NULL; 359 | 360 | retval = ::MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, lpCmdLine, -1, lpWideCharStr, retval); 361 | if (!SUCCEEDED(retval)) 362 | { 363 | free(lpWideCharStr); 364 | 365 | return NULL; 366 | } 367 | 368 | int numArgs; 369 | LPWSTR* args; 370 | args = ::CommandLineToArgvW(lpWideCharStr, &numArgs); 371 | free(lpWideCharStr); 372 | if (args == NULL) return NULL; 373 | 374 | int storage = numArgs * sizeof(LPSTR); 375 | for (int i = 0; i < numArgs; i++) 376 | { 377 | BOOL lpUsedDefaultChar = FALSE; 378 | retval = ::WideCharToMultiByte(CP_ACP, 0, args[i], -1, NULL, 0, NULL, &lpUsedDefaultChar); 379 | if (!SUCCEEDED(retval)) 380 | { 381 | ::LocalFree(args); 382 | 383 | return NULL; 384 | } 385 | 386 | storage += retval; 387 | } 388 | 389 | LPSTR* result = (LPSTR *)::LocalAlloc(LMEM_FIXED, storage); 390 | if (result == NULL) 391 | { 392 | ::LocalFree(args); 393 | 394 | return NULL; 395 | } 396 | 397 | int bufLen = storage - numArgs * sizeof(LPSTR); 398 | LPSTR buffer = ((LPSTR)result) + numArgs * sizeof(LPSTR); 399 | for (int i = 0; i < numArgs; ++ i) 400 | { 401 | BOOL lpUsedDefaultChar = FALSE; 402 | retval = ::WideCharToMultiByte(CP_ACP, 0, args[i], -1, buffer, bufLen, NULL, &lpUsedDefaultChar); 403 | if (!SUCCEEDED(retval)) 404 | { 405 | ::LocalFree(result); 406 | ::LocalFree(args); 407 | 408 | return NULL; 409 | } 410 | 411 | result[i] = buffer; 412 | buffer += retval; 413 | bufLen -= retval; 414 | } 415 | 416 | ::LocalFree(args); 417 | 418 | *pNumArgs = numArgs; 419 | return result; 420 | } 421 | #endif 422 | 423 | int CALLBACK WinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, LPSTR lpCmdLine, int /* nCmdShow */) 424 | { 425 | int argc; 426 | TCHAR **argv; 427 | int result; 428 | 429 | #ifdef UNICODE 430 | argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 431 | #else 432 | argv = CommandLineToArgvA(lpCmdLine, &argc); 433 | #endif 434 | 435 | if (argv == NULL) return 0; 436 | 437 | result = _tmain(argc, argv); 438 | 439 | ::LocalFree(argv); 440 | 441 | return result; 442 | } 443 | #endif 444 | -------------------------------------------------------------------------------- /messagebox.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cubiclesoft/messagebox-windows/33be748fa41d01b75161c592d72815bc6ed26a7c/messagebox.exe --------------------------------------------------------------------------------