Skip to content

Commit 3ef70d9

Browse files
committed
Merge branch 'fork/TheBestAstroNOT/master'
2 parents a35685d + d0954b0 commit 3ef70d9

5 files changed

Lines changed: 154 additions & 20 deletions

File tree

source/Reloaded.Mod.Launcher.Lib/Static/Errors.cs

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,68 @@ public static class Errors
1212
/// </summary>
1313
public static void HandleException(Exception ex, string message = "")
1414
{
15-
if (!Debugger.IsAttached)
15+
if (Debugger.IsAttached)
16+
Debugger.Break();
17+
else
1618
{
19+
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
20+
var canDisplayMessageBox = Actions.DisplayMessagebox != null;
21+
var hasSynchronizationContext = Actions.SynchronizationContext != null;
22+
var isUiInitialized = hasSynchronizationContext && canDisplayMessageBox;
23+
// ReSharper restore ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
24+
if (!isUiInitialized) return;
25+
26+
var errorMessage = $"{message}{ex.Message}\n\n{Resources.ErrorViewDetails.Get()}";
27+
bool userWantsToSeeStackTrace;
28+
29+
// Just in case of an error before proper UI init.
1730
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
18-
var isUiInitialized = Actions.SynchronizationContext != null && Actions.DisplayMessagebox != null;
19-
if (isUiInitialized)
31+
if (canDisplayMessageBox)
2032
{
21-
// Just in case of an error before proper UI init.
22-
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
23-
if (Actions.DisplayMessagebox != null)
24-
{
25-
Actions.DisplayMessagebox.Invoke(Resources.ErrorUnknown.Get(), $"{message}{ex.Message}\n{ex.StackTrace}");
26-
}
27-
else
28-
{
29-
MessageBox.Show($"{message}{ex.Message}\n{ex.StackTrace}", Resources.ErrorUnknown.Get());
30-
}
33+
userWantsToSeeStackTrace = Actions.DisplayMessagebox!.Invoke(
34+
Resources.ErrorUnknown.Get(),
35+
errorMessage,
36+
new Actions.DisplayMessageBoxParams
37+
{
38+
Type = Actions.MessageBoxType.OkCancel
39+
});
3140
}
41+
else
42+
{
43+
var result = MessageBox.Show(
44+
errorMessage,
45+
Resources.ErrorUnknown.Get(),
46+
MessageBoxButton.YesNo,
47+
MessageBoxImage.Error);
48+
49+
userWantsToSeeStackTrace = (result == MessageBoxResult.Yes);
50+
}
51+
52+
if (userWantsToSeeStackTrace)
53+
CreateAndOpenLogFile(ex, Path.Combine(Paths.LauncherErrorsPath, $"{DateTime.UtcNow:yyyy-MM-dd HH.mm.ss}.txt"));
3254
}
33-
else
34-
Debugger.Break();
55+
}
56+
57+
/// <summary>
58+
/// Creates and opens a log file with exception details.
59+
/// </summary>
60+
private static void CreateAndOpenLogFile(Exception ex, string logPath)
61+
{
62+
Directory.CreateDirectory(Path.GetDirectoryName(logPath)!);
63+
var text = $"{Resources.ErrorStacktraceTitle.Get()}\n" +
64+
$"{Resources.ErrorStacktraceSubtitle.Get()}\n" +
65+
$"-------------\n" +
66+
$"Exception:\n" +
67+
$"{ex.Message}\n" +
68+
$"Stacktrace:\n" +
69+
$"{ex.StackTrace}";
70+
File.WriteAllText(logPath, text);
71+
72+
ProcessStartInfo logFile = new()
73+
{
74+
FileName = logPath,
75+
UseShellExecute = true
76+
};
77+
Process.Start(logFile);
3578
}
3679
}

source/Reloaded.Mod.Launcher.Lib/Static/Resources.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,9 @@ public static void Init(IDictionaryResourceProvider provider)
214214
public static IDictionaryResource<string> ProblematicPathAppDescription { get; set; }
215215
public static IDictionaryResource<string> ProblematicPathReloadedDescription { get; set; }
216216
public static IDictionaryResource<string> ProblematicPathModsDescription { get; set; }
217+
218+
// Update 1.29.0: Launcher Error Reporting
219+
public static IDictionaryResource<string> ErrorViewDetails { get; set; }
220+
public static IDictionaryResource<string> ErrorStacktraceTitle { get; set; }
221+
public static IDictionaryResource<string> ErrorStacktraceSubtitle { get; set; }
217222
}

source/Reloaded.Mod.Launcher/Assets/Languages/en-GB.xaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
1+
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
22
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
33
xmlns:sys="clr-namespace:System;assembly=mscorlib"
44
xml:space="preserve">
@@ -726,4 +726,8 @@ For more info, refer to the tutorial. Don't forget to set correct Publish target
726726
<sys:String x:Key="ProblematicPathReloadedDescription">Reloaded-II has detected it is installed in a folder that is managed by OneDrive or contains non-ASCII (special) characters.&#x0a;It is recommended to move your Reloaded-II folder to a different path, such as "C:\Reloaded-II\".&#x0a;Keeping Reloaded-II in an unsupported path may result in mods not working.</sys:String>
727727
<sys:String x:Key="ProblematicPathModsDescription">Reloaded-II has detected your mods folder is managed by OneDrive or contains non-ASCII (special) characters.&#x0a;It is recommended to move your mods folder to a different path.&#x0a;Keeping mods in an unsupported path may result in problems running mods.</sys:String>
728728

729+
<!-- Update 1.29.0: Launcher Error Reporting -->
730+
<sys:String x:Key="ErrorViewDetails">Do you want to view the error details?</sys:String>
731+
<sys:String x:Key="ErrorStacktraceTitle">This is a Reloaded-II launcher error</sys:String>
732+
<sys:String x:Key="ErrorStacktraceSubtitle">Report issues to https://github.com/Reloaded-Project/Reloaded-II/issues/new</sys:String>
729733
</ResourceDictionary>

source/Reloaded.Mod.Loader.Bootstrapper/LoaderConfig.cpp

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
#include <codecvt>
88
#include "ReloadedPaths.h"
99

10+
// Maximum path length supported by Windows extended paths
11+
constexpr DWORD MAX_PATH_BUFFER_SIZE = 32767;
12+
1013
LoaderConfig::LoaderConfig()
1114
{
1215
// Get path to AppData
13-
char_t buffer[32767]; // Max Windows10+ path.
16+
char_t buffer[MAX_PATH_BUFFER_SIZE]; // Max Windows10+ path.
1417
BOOL result = SHGetSpecialFolderPath(nullptr, buffer, CSIDL_APPDATA, false);
1518

1619
if (!result)
@@ -28,14 +31,79 @@ LoaderConfig::LoaderConfig()
2831
config = json::parse(configFile);
2932
}
3033

34+
namespace
35+
{
36+
bool TryGetCurrentModulePath(string_t& outPath)
37+
{
38+
const auto raw_path_buffer = std::make_unique<wchar_t[]>(MAX_PATH_BUFFER_SIZE);
39+
40+
// Get current module handle using GetModuleHandleEx with GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
41+
// to get the module that contains this function's code address
42+
HMODULE hModule = nullptr;
43+
if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
44+
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
45+
reinterpret_cast<LPCWSTR>(TryGetCurrentModulePath),
46+
&hModule))
47+
{
48+
return false;
49+
}
50+
51+
// Get module filename with extended path support
52+
const DWORD result = GetModuleFileNameW(hModule, raw_path_buffer.get(), MAX_PATH_BUFFER_SIZE);
53+
if (result == 0 || result == MAX_PATH_BUFFER_SIZE)
54+
return false;
55+
56+
// Normally we would rely on GetModuleFileNameW, but there's this thing called 'Xbox Game Pass', which has some
57+
// legacy DRM mechanisms inherited from UWP; it pretends files are in a virtualized location. We must ask Windows
58+
// for the real path of a file.
59+
const auto final_path_buffer = std::make_unique<wchar_t[]>(MAX_PATH_BUFFER_SIZE);
60+
string_t result_path;
61+
62+
// Open the file to get a handle
63+
const HANDLE file_handle = CreateFileW(
64+
raw_path_buffer.get(),
65+
0,
66+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
67+
nullptr,
68+
OPEN_EXISTING,
69+
FILE_ATTRIBUTE_NORMAL,
70+
nullptr
71+
);
72+
73+
if (file_handle != INVALID_HANDLE_VALUE)
74+
{
75+
// Get the canonical path if the file handle is valid
76+
// final_result has length of string.
77+
const DWORD final_result = GetFinalPathNameByHandleW(file_handle, final_path_buffer.get(), MAX_PATH_BUFFER_SIZE, FILE_NAME_NORMALIZED);
78+
CloseHandle(file_handle);
79+
80+
// Check that the returned string is in_bounds, if not, use the raw path
81+
// Note: It should always be in bounds unless Windows extends file path limits beyond NTFS limits.
82+
if (final_result > 0 && final_result < MAX_PATH_BUFFER_SIZE)
83+
result_path = final_path_buffer.get();
84+
else
85+
result_path = raw_path_buffer.get();
86+
}
87+
else
88+
result_path = raw_path_buffer.get();
89+
90+
// Remove the extended path prefix if present
91+
const string_t prefix = L"\\\\?\\";
92+
if (result_path.rfind(prefix, 0) == 0)
93+
result_path = result_path.substr(prefix.length());
94+
95+
outPath = result_path;
96+
return true;
97+
}
98+
}
99+
31100
string_t LoaderConfig::get_loader_path()
32101
{
33102
#if _WIN64
34103
const std::string stringLoaderPath = config["LoaderPath64"];
35104
#else
36105
const std::string stringLoaderPath = config["LoaderPath32"];
37106
#endif
38-
39107

40108
/* std::string is non-wide and will not handle unicode characters on Windows.
41109
* Need to convert back to wide characters.
@@ -45,8 +113,17 @@ string_t LoaderConfig::get_loader_path()
45113
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
46114
const string_t loaderPath = converter.from_bytes(stringLoaderPath);
47115

48-
if (!Utilities::file_exists(loaderPath))
49-
throw std::exception("Reloaded Mod Loader DLL has not been found.");
116+
if (!Utilities::file_exists(loaderPath)) {
117+
std::string errorMessage = "Reloaded-II Loader DLL has not been found.\nTo fix this, start the Reloaded launcher.";
118+
119+
string_t modulePath;
120+
if (TryGetCurrentModulePath(modulePath)) {
121+
std::string dllFolderPath = converter.to_bytes(modulePath);
122+
errorMessage = "Reloaded-II Loader DLL has not been found.\nTo fix this, start the Reloaded launcher.\n\nIf you intended to uninstall Reloaded, delete:\n\n" + dllFolderPath;
123+
}
124+
125+
throw std::exception(errorMessage.c_str());
126+
}
50127

51128
return loaderPath;
52129
}

source/Reloaded.Mod.Loader.IO/Paths.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ public class Paths
4545
/// </summary>
4646
public static readonly string ArchivedLogPath = Path.Combine(LogPath, "OldLogs.zip");
4747

48+
/// <summary>
49+
/// Location of the launcher errors folder, used to store launcher error logs.
50+
/// </summary>
51+
public static readonly string LauncherErrorsPath = Path.Combine(ConfigFolder, "LauncherErrors");
52+
4853
// MACHINE SPECIFIC DATA //
4954

5055
/// <summary>

0 commit comments

Comments
 (0)