upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/main_gui.cpp
diff options
context:
space:
mode:
authorBitflash <btf01@proton.me>2026-08-04 10:13:41 -0300
committerGitHub <noreply@github.com>2026-08-04 10:13:41 -0300
commit530f91910a4eceb6705d1212fa0e3ebe8418c6a3 (patch)
treee8834760736e765178413c5925f56293888fbf15 /src/main_gui.cpp
parent1db5f372c36eb701602a70a358de2344726ed734 (diff)
gui: say why the wallet would not open, where the user can see it (#136)
Every fatal startup path wrote to stderr and to printf, and on Windows neither reaches anybody. printf is remapped to OutputDebugStringF, so it lands in debug.log inside the data directory. A -mwindows binary started by double-clicking has no stderr at all, and AttachTerminal() cannot help: it attaches to a parent console, which a double-clicked exe does not have. So Bitflash exited silently. Someone whose wallet.dat needs a newer build -- exactly what the wallet format guard was added to tell them -- double-clicked and watched nothing happen. The guard was correct and mute, which is the failure it was written to prevent. Fatal startup errors now go through one reporter: debug.log as before, stderr as before, and a message box when there is a person in front of the screen. Applied to the three that mean "your data will not open": the block index failing to load, the block index throwing, and wallet.dat failing or throwing. The box is deliberately not shown for a headless node. Those run under scheduled tasks and services, where a modal dialog waits forever for a click nobody is there to make -- a hung process instead of an exit code. Headless keeps stderr and the log. LoadWallet() now also reports why it refused, in a sentence, instead of only logging it. The unsupported-format case is the one that has to be understood by whoever reads it, and "LoadWallet failed" is not that. Verified by breaking a throwaway wallet on purpose and starting both ways. Headless: exit code 1, no dialog, no process left behind. Windowed with no console: a box naming the file, the Berkeley DB error, the usual cause, and where to look for more.
Diffstat (limited to 'src/main_gui.cpp')
-rw-r--r--src/main_gui.cpp87
1 files changed, 77 insertions, 10 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index a2577ca..875988f 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -291,6 +291,50 @@ static void ParseStartupArguments(int argc, char* argv[])
291 } 291 }
292} 292}
293 293
294// Report a startup failure somewhere the user will actually see it.
295//
296// Every fatal path here wrote to stderr and to printf, and on the platform most
297// people use, neither reaches anybody. printf is remapped to OutputDebugStringF,
298// so it lands in debug.log inside the data directory. A -mwindows binary
299// launched by double-clicking has no stderr at all, and AttachTerminal() cannot
300// help: it attaches to a *parent* console, which a double-clicked exe does not
301// have.
302//
303// So the program exited silently. Someone whose wallet.dat needs a newer build
304// -- which is exactly what the wallet format guard was added to tell them --
305// double-clicked Bitflash and watched nothing happen.
306//
307// The box is deliberately not shown for a headless node: those run under
308// scheduled tasks and services, where a modal dialog waits forever for a click
309// nobody is there to make. Headless already has a console when it has one, and
310// its output goes to stderr and the log as before.
311static void FatalStartupError(bool fHeadless, const string& strWhat, const string& strDetail)
312{
313 printf("FATAL: %s\n", strWhat.c_str());
314 if (!strDetail.empty())
315 printf("FATAL: %s\n", strDetail.c_str());
316
317 AttachTerminal();
318 fprintf(stderr, "%s\n", strWhat.c_str());
319 if (!strDetail.empty())
320 fprintf(stderr, "%s\n", strDetail.c_str());
321 fflush(stderr);
322
323#ifdef _WIN32
324 if (!fHeadless && GetConsoleWindow() == NULL)
325 {
326 string strBody = strWhat;
327 if (!strDetail.empty())
328 strBody += "\n\n" + strDetail;
329 strBody += "\n\nNothing was changed. More detail is in debug.log, "
330 "inside the data directory.";
331 MessageBoxA(NULL, strBody.c_str(), "Bitflash", MB_OK | MB_ICONERROR | MB_SETFOREGROUND);
332 }
333#else
334 (void)fHeadless;
335#endif
336}
337
294int main(int argc, char* argv[]) 338int main(int argc, char* argv[])
295{ 339{
296 if (arg(argc,argv,"/help") || arg(argc,argv,"-help") || 340 if (arg(argc,argv,"/help") || arg(argc,argv,"-help") ||
@@ -323,25 +367,47 @@ int main(int argc, char* argv[])
323 // libdb_cxx-6.2.dll -> CDB::CDB(...) -> LoadBlockIndex(...) -> main() 367 // libdb_cxx-6.2.dll -> CDB::CDB(...) -> LoadBlockIndex(...) -> main()
324 // 368 //
325 // The program knew what had gone wrong and threw the reason away. 369 // The program knew what had gone wrong and threw the reason away.
370 // Needed here, long before the headless branch runs, because whether a
371 // startup failure may pop a dialog depends on it.
372#ifdef BITFLASH_NO_GUI
373 bool fHeadlessStartup = true;
374#else
375 bool fHeadlessStartup = arg(argc,argv,"/nogui") || arg(argc,argv,"-nogui") ||
376 arg(argc,argv,"/daemon") || arg(argc,argv,"-daemon");
377#endif
378
326 string strErrors; 379 string strErrors;
327 printf("Loading block index...\n"); 380 printf("Loading block index...\n");
328 try 381 try
329 { 382 {
330 if (!LoadBlockIndex()) { fprintf(stderr, "LoadBlockIndex failed\n"); return 1; } 383 if (!LoadBlockIndex())
384 {
385 FatalStartupError(fHeadlessStartup, "Cannot load the block index.", "");
386 return 1;
387 }
331 } 388 }
332 catch (const std::exception& e) 389 catch (const std::exception& e)
333 { 390 {
334 fprintf(stderr, "Cannot read the block index: %s\n", e.what()); 391 FatalStartupError(fHeadlessStartup,
335 fprintf(stderr, "blkindex.dat and blk0001.dat may be from another machine or " 392 strprintf("Cannot read the block index: %s", e.what()),
336 "incomplete. Deleting both is safe -- they are re-downloaded -- " 393 "blkindex.dat and blk0001.dat may be from another machine or "
337 "but never delete wallet.dat, which holds your keys.\n"); 394 "incomplete. Deleting both is safe -- they are re-downloaded -- "
395 "but never delete wallet.dat, which holds your keys.");
338 return 1; 396 return 1;
339 } 397 }
340 398
341 printf("Loading wallet...\n"); 399 printf("Loading wallet...\n");
342 try 400 try
343 { 401 {
344 if (!LoadWallet()) { fprintf(stderr, "LoadWallet failed\n"); return 1; } 402 if (!LoadWallet())
403 {
404 // LoadWallet() explains itself into strWalletLoadError when it
405 // knows why -- an unsupported wallet format, for one, which is the
406 // case this whole path exists to make visible.
407 FatalStartupError(fHeadlessStartup, "Cannot open wallet.dat.",
408 strWalletLoadError);
409 return 1;
410 }
345 411
346 // After the block index, so there is a chain to compare the wallet 412 // After the block index, so there is a chain to compare the wallet
347 // against, and before anything reports a balance. 413 // against, and before anything reports a balance.
@@ -349,10 +415,11 @@ int main(int argc, char* argv[])
349 } 415 }
350 catch (const std::exception& e) 416 catch (const std::exception& e)
351 { 417 {
352 fprintf(stderr, "Cannot read wallet.dat: %s\n", e.what()); 418 FatalStartupError(fHeadlessStartup,
353 fprintf(stderr, "The file was left untouched. A wallet.dat written by a " 419 strprintf("Cannot read wallet.dat: %s", e.what()),
354 "different platform's Berkeley DB is the usual cause; back it " 420 "The file was left untouched. A wallet.dat written by a "
355 "up before trying anything else.\n"); 421 "different platform's Berkeley DB is the usual cause; back it "
422 "up before trying anything else.");
356 return 1; 423 return 1;
357 } 424 }
358 printf("Height=%d\n", nBestHeight); 425 printf("Height=%d\n", nBestHeight);