diff --git a/Dalamud.Boot/Dalamud.Boot.vcxproj b/Dalamud.Boot/Dalamud.Boot.vcxproj
index dd3f57632..ab68c1ec0 100644
--- a/Dalamud.Boot/Dalamud.Boot.vcxproj
+++ b/Dalamud.Boot/Dalamud.Boot.vcxproj
@@ -199,4 +199,4 @@
-
\ No newline at end of file
+
diff --git a/Dalamud.Boot/dllmain.cpp b/Dalamud.Boot/dllmain.cpp
index cf31b7016..e6aa9c4ac 100644
--- a/Dalamud.Boot/dllmain.cpp
+++ b/Dalamud.Boot/dllmain.cpp
@@ -9,7 +9,7 @@
HMODULE g_hModule;
HINSTANCE g_hGameInstance = GetModuleHandleW(nullptr);
-DWORD WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
+HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
g_startInfo.from_envvars();
std::string jsonParseError;
@@ -114,7 +114,7 @@ DWORD WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
logging::I("Calling InitializeClrAndGetEntryPoint");
void* entrypoint_vfn;
- int result = InitializeClrAndGetEntryPoint(
+ const auto result = InitializeClrAndGetEntryPoint(
g_hModule,
g_startInfo.BootEnableEtw,
runtimeconfig_path,
@@ -124,7 +124,7 @@ DWORD WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
L"Dalamud.EntryPoint+InitDelegate, Dalamud",
&entrypoint_vfn);
- if (result != 0)
+ if (FAILED(result))
return result;
using custom_component_entry_point_fn = void (CORECLR_DELEGATE_CALLTYPE*)(LPVOID, HANDLE);
@@ -156,7 +156,7 @@ DWORD WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
entrypoint_fn(lpParam, hMainThreadContinue);
logging::I("Done!");
- return 0;
+ return S_OK;
}
extern "C" DWORD WINAPI Initialize(LPVOID lpParam) {
diff --git a/Dalamud.Boot/pch.h b/Dalamud.Boot/pch.h
index 6dda9d03b..a09882c74 100644
--- a/Dalamud.Boot/pch.h
+++ b/Dalamud.Boot/pch.h
@@ -26,6 +26,9 @@
// MSVC Compiler Intrinsic
#include
+// COM
+#include
+
// C++ Standard Libraries
#include
#include
diff --git a/Dalamud.Boot/rewrite_entrypoint.cpp b/Dalamud.Boot/rewrite_entrypoint.cpp
index a47254701..6ece3665c 100644
--- a/Dalamud.Boot/rewrite_entrypoint.cpp
+++ b/Dalamud.Boot/rewrite_entrypoint.cpp
@@ -1,8 +1,9 @@
#include "pch.h"
#include "logging.h"
+#include "utils.h"
-DWORD WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue);
+HRESULT WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue);
struct RewrittenEntryPointParameters {
char* pEntrypoint;
@@ -102,6 +103,7 @@ void read_process_memory_or_throw(HANDLE hProcess, void* pAddress, T& data) {
void write_process_memory_or_throw(HANDLE hProcess, void* pAddress, const void* data, size_t len) {
SIZE_T written = 0;
+ const utils::memory_tenderizer tenderizer(hProcess, pAddress, len, PAGE_EXECUTE_READWRITE);
if (!WriteProcessMemory(hProcess, pAddress, data, len, &written))
throw std::runtime_error("WriteProcessMemory failure");
if (written != len)
@@ -227,15 +229,18 @@ void* get_mapped_image_base_address(HANDLE hProcess, const std::filesystem::path
/// @brief Rewrite target process' entry point so that this DLL can be loaded and executed first.
/// @param hProcess Process handle.
/// @param pcwzPath Path to target process.
-/// @param pcszLoadInfo JSON string to be passed to Initialize.
-/// @return 0 if successful; nonzero if unsuccessful
+/// @param pcwzLoadInfo JSON string to be passed to Initialize.
+/// @return null if successful; memory containing wide string allocated via GlobalAlloc if unsuccessful
///
/// When the process has just been started up via CreateProcess (CREATE_SUSPENDED), GetModuleFileName and alikes result in an error.
/// Instead, we have to enumerate through all the files mapped into target process' virtual address space and find the base address
/// of memory region corresponding to the path given.
///
-extern "C" DWORD WINAPI RewriteRemoteEntryPointW(HANDLE hProcess, const wchar_t* pcwzPath, const wchar_t* pcwzLoadInfo) {
+extern "C" HRESULT WINAPI RewriteRemoteEntryPointW(HANDLE hProcess, const wchar_t* pcwzPath, const wchar_t* pcwzLoadInfo) {
+ std::wstring last_operation;
+ SetLastError(ERROR_SUCCESS);
try {
+ last_operation = L"get_mapped_image_base_address";
const auto base_address = static_cast(get_mapped_image_base_address(hProcess, pcwzPath));
IMAGE_DOS_HEADER dos_header{};
@@ -244,21 +249,34 @@ extern "C" DWORD WINAPI RewriteRemoteEntryPointW(HANDLE hProcess, const wchar_t*
IMAGE_NT_HEADERS64 nt_header64{};
};
+ last_operation = L"read_process_memory_or_throw(base_address)";
read_process_memory_or_throw(hProcess, base_address, dos_header);
+
+ last_operation = L"read_process_memory_or_throw(base_address + dos_header.e_lfanew)";
read_process_memory_or_throw(hProcess, base_address + dos_header.e_lfanew, nt_header64);
const auto entrypoint = base_address + (nt_header32.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
? nt_header32.OptionalHeader.AddressOfEntryPoint
: nt_header64.OptionalHeader.AddressOfEntryPoint);
- auto standalone_rewrittenentrypoint = thunks::create_standalone_rewrittenentrypoint(get_path_from_local_module(g_hModule));
+ last_operation = L"get_path_from_local_module(g_hModule)";
+ auto local_module_path = get_path_from_local_module(g_hModule);
+
+ last_operation = L"thunks::create_standalone_rewrittenentrypoint(local_module_path)";
+ auto standalone_rewrittenentrypoint = thunks::create_standalone_rewrittenentrypoint(local_module_path);
+
+ last_operation = L"thunks::create_entrypointreplacement()";
auto entrypoint_replacement = thunks::create_entrypointreplacement();
+ last_operation = L"unicode::convert(pcwzLoadInfo)";
auto load_info = unicode::convert(pcwzLoadInfo);
load_info.resize(load_info.size() + 1); //ensure null termination
- std::vector buffer(sizeof(RewrittenEntryPointParameters) + entrypoint_replacement.size() + load_info.size() + standalone_rewrittenentrypoint.size());
+ const auto bufferSize = sizeof(RewrittenEntryPointParameters) + entrypoint_replacement.size() + load_info.size() + standalone_rewrittenentrypoint.size();
+ last_operation = std::format(L"std::vector alloc({}b)", bufferSize);
+ std::vector buffer(bufferSize);
// Allocate buffer in remote process, which will be used to fill addresses in the local buffer.
+ last_operation = std::format(L"VirtualAllocEx({}b)", bufferSize);
const auto remote_buffer = static_cast(VirtualAllocEx(hProcess, nullptr, buffer.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
auto& params = *reinterpret_cast(buffer.data());
@@ -266,24 +284,51 @@ extern "C" DWORD WINAPI RewriteRemoteEntryPointW(HANDLE hProcess, const wchar_t*
params.pEntrypoint = entrypoint;
// Backup original entry point.
+ last_operation = std::format(L"read_process_memory_or_throw(entrypoint, {}b)", entrypoint_replacement.size());
read_process_memory_or_throw(hProcess, entrypoint, &buffer[sizeof params], entrypoint_replacement.size());
memcpy(&buffer[sizeof params + entrypoint_replacement.size()], load_info.data(), load_info.size());
+ last_operation = L"thunks::fill_placeholders(EntryPointReplacement)";
thunks::fill_placeholders(standalone_rewrittenentrypoint.data(), remote_buffer);
memcpy(&buffer[sizeof params + entrypoint_replacement.size() + load_info.size()], standalone_rewrittenentrypoint.data(), standalone_rewrittenentrypoint.size());
// Write the local buffer into the buffer in remote process.
+ last_operation = std::format(L"write_process_memory_or_throw(remote_buffer, {}b)", buffer.size());
write_process_memory_or_throw(hProcess, remote_buffer, buffer.data(), buffer.size());
+ last_operation = L"thunks::fill_placeholders(RewrittenEntryPoint_Standalone::pRewrittenEntryPointParameters)";
thunks::fill_placeholders(entrypoint_replacement.data(), remote_buffer + sizeof params + entrypoint_replacement.size() + load_info.size());
+
// Overwrite remote process' entry point with a thunk that will load our DLLs and call our trampoline function.
+ last_operation = std::format(L"write_process_memory_or_throw(entrypoint={:X}, {}b)", reinterpret_cast(entrypoint), buffer.size());
write_process_memory_or_throw(hProcess, entrypoint, entrypoint_replacement.data(), entrypoint_replacement.size());
- return 0;
+ return S_OK;
} catch (const std::exception& e) {
- OutputDebugStringA(std::format("RewriteRemoteEntryPoint failure: {} (GetLastError: {})\n", e.what(), GetLastError()).c_str());
- return 1;
+ const auto err = GetLastError();
+ const auto hr = err == ERROR_SUCCESS ? E_FAIL : HRESULT_FROM_WIN32(err);
+ auto formatted = std::format(
+ L"{}: {} ({})",
+ last_operation,
+ unicode::convert(e.what()),
+ utils::format_win32_error(err));
+ OutputDebugStringW((formatted + L"\r\n").c_str());
+
+ ICreateErrorInfoPtr cei;
+ if (FAILED(CreateErrorInfo(&cei)))
+ return hr;
+ if (FAILED(cei->SetSource(const_cast(L"Dalamud.Boot"))))
+ return hr;
+ if (FAILED(cei->SetDescription(const_cast(formatted.c_str()))))
+ return hr;
+
+ IErrorInfoPtr ei;
+ if (FAILED(cei.QueryInterface(IID_PPV_ARGS(&ei))))
+ return hr;
+
+ (void)SetErrorInfo(0, ei);
+ return hr;
}
}
@@ -300,8 +345,9 @@ static void AbortRewrittenEntryPoint(DWORD err, const std::wstring& clue) {
nullptr);
if (MessageBoxW(nullptr, std::format(
- L"Failed to load Dalamud. Load game without Dalamud(yes) or abort(no)?\n\nError: 0x{:08X} {}\n\n{}",
- err, pwszMsg ? pwszMsg : L"", clue).c_str(),
+ L"Failed to load Dalamud. Load game without Dalamud(yes) or abort(no)?\n\n{}\n\n{}",
+ utils::format_win32_error(err),
+ clue).c_str(),
L"Dalamud.Boot", MB_OK | MB_YESNO) == IDNO)
ExitProcess(-1);
}
@@ -309,21 +355,62 @@ static void AbortRewrittenEntryPoint(DWORD err, const std::wstring& clue) {
/// @brief Entry point function "called" instead of game's original main entry point.
/// @param params Parameters set up from RewriteRemoteEntryPoint.
extern "C" void WINAPI RewrittenEntryPoint_AdjustedStack(RewrittenEntryPointParameters & params) {
- const auto pOriginalEntryPointBytes = reinterpret_cast(¶ms) + sizeof(params);
- const auto pLoadInfo = pOriginalEntryPointBytes + params.entrypointLength;
+ HANDLE hMainThreadContinue = nullptr;
+ auto hr = S_OK;
+ std::wstring last_operation;
+ std::wstring exc_msg;
+ SetLastError(ERROR_SUCCESS);
- // Restore original entry point.
- // Use WriteProcessMemory instead of memcpy to avoid having to fiddle with VirtualProtect.
- if (SIZE_T written; !WriteProcessMemory(GetCurrentProcess(), params.pEntrypoint, pOriginalEntryPointBytes, params.entrypointLength, &written))
- return AbortRewrittenEntryPoint(GetLastError(), L"WriteProcessMemory(entrypoint restoration)");
+ try {
+ const auto pOriginalEntryPointBytes = reinterpret_cast(¶ms) + sizeof(params);
+ const auto pLoadInfo = pOriginalEntryPointBytes + params.entrypointLength;
- const auto hMainThreadContinue = CreateEventW(nullptr, true, false, nullptr);
- if (!hMainThreadContinue)
- return AbortRewrittenEntryPoint(GetLastError(), L"CreateEventW");
+ // Restore original entry point.
+ // Use WriteProcessMemory instead of memcpy to avoid having to fiddle with VirtualProtect.
+ last_operation = L"restore original entry point";
+ write_process_memory_or_throw(GetCurrentProcess(), params.pEntrypoint, pOriginalEntryPointBytes, params.entrypointLength);
- if (const auto result = InitializeImpl(pLoadInfo, hMainThreadContinue))
- return AbortRewrittenEntryPoint(result, L"InitializeImpl");
+ hMainThreadContinue = CreateEventW(nullptr, true, false, nullptr);
+ last_operation = L"hMainThreadContinue = CreateEventW";
+ if (!hMainThreadContinue)
+ throw std::runtime_error("CreateEventW");
+
+ last_operation = L"InitializeImpl";
+ hr = InitializeImpl(pLoadInfo, hMainThreadContinue);
+ } catch (const std::exception& e) {
+ if (hr == S_OK) {
+ const auto err = GetLastError();
+ hr = err == ERROR_SUCCESS ? E_FAIL : HRESULT_FROM_WIN32(err);
+ }
+
+ ICreateErrorInfoPtr cei;
+ IErrorInfoPtr ei;
+ if (SUCCEEDED(CreateErrorInfo(&cei))
+ && SUCCEEDED(cei->SetDescription(const_cast(unicode::convert(e.what()).c_str())))
+ && SUCCEEDED(cei.QueryInterface(IID_PPV_ARGS(&ei)))) {
+ (void)SetErrorInfo(0, ei);
+ }
+ }
+
+ if (FAILED(hr)) {
+ const _com_error err(hr);
+ auto desc = err.Description();
+ if (desc.length() == 0)
+ desc = err.ErrorMessage();
+ if (MessageBoxW(nullptr, std::format(
+ L"Failed to load Dalamud. Load game without Dalamud(yes) or abort(no)?\n\n{}\n{}",
+ last_operation,
+ desc.GetBSTR()).c_str(),
+ L"Dalamud.Boot", MB_OK | MB_YESNO) == IDNO)
+ ExitProcess(-1);
+ if (hMainThreadContinue) {
+ CloseHandle(hMainThreadContinue);
+ hMainThreadContinue = nullptr;
+ }
+ }
+
+ if (hMainThreadContinue)
+ WaitForSingleObject(hMainThreadContinue, INFINITE);
- WaitForSingleObject(hMainThreadContinue, INFINITE);
VirtualFree(¶ms, 0, MEM_RELEASE);
}
diff --git a/Dalamud.Boot/utils.cpp b/Dalamud.Boot/utils.cpp
index 9dc296c5f..65018add4 100644
--- a/Dalamud.Boot/utils.cpp
+++ b/Dalamud.Boot/utils.cpp
@@ -408,14 +408,20 @@ utils::signature_finder::result utils::signature_finder::find_one() const {
return find(1, 1, false).front();
}
-utils::memory_tenderizer::memory_tenderizer(const void* pAddress, size_t length, DWORD dwNewProtect) : m_data(reinterpret_cast(const_cast(pAddress)), length) {
+utils::memory_tenderizer::memory_tenderizer(const void* pAddress, size_t length, DWORD dwNewProtect)
+ : memory_tenderizer(GetCurrentProcess(), pAddress, length, dwNewProtect) {
+}
+
+utils::memory_tenderizer::memory_tenderizer(HANDLE hProcess, const void* pAddress, size_t length, DWORD dwNewProtect)
+: m_process(hProcess)
+, m_data(static_cast(const_cast(pAddress)), length) {
try {
- for (auto pCoveredAddress = &m_data[0];
- pCoveredAddress < &m_data[0] + m_data.size();
- pCoveredAddress = reinterpret_cast(m_regions.back().BaseAddress) + m_regions.back().RegionSize) {
+ for (auto pCoveredAddress = m_data.data();
+ pCoveredAddress < m_data.data() + m_data.size();
+ pCoveredAddress = static_cast(m_regions.back().BaseAddress) + m_regions.back().RegionSize) {
MEMORY_BASIC_INFORMATION region{};
- if (!VirtualQuery(pCoveredAddress, ®ion, sizeof region)) {
+ if (!VirtualQueryEx(hProcess, pCoveredAddress, ®ion, sizeof region)) {
throw std::runtime_error(std::format(
"VirtualQuery(addr=0x{:X}, ..., cb={}) failed with Win32 code 0x{:X}",
reinterpret_cast(pCoveredAddress),
@@ -423,7 +429,7 @@ utils::memory_tenderizer::memory_tenderizer(const void* pAddress, size_t length,
GetLastError()));
}
- if (!VirtualProtect(region.BaseAddress, region.RegionSize, dwNewProtect, ®ion.Protect)) {
+ if (!VirtualProtectEx(hProcess, region.BaseAddress, region.RegionSize, dwNewProtect, ®ion.Protect)) {
throw std::runtime_error(std::format(
"(Change)VirtualProtect(addr=0x{:X}, size=0x{:X}, ..., ...) failed with Win32 code 0x{:X}",
reinterpret_cast(region.BaseAddress),
@@ -436,7 +442,7 @@ utils::memory_tenderizer::memory_tenderizer(const void* pAddress, size_t length,
} catch (...) {
for (auto& region : std::ranges::reverse_view(m_regions)) {
- if (!VirtualProtect(region.BaseAddress, region.RegionSize, region.Protect, ®ion.Protect)) {
+ if (!VirtualProtectEx(hProcess, region.BaseAddress, region.RegionSize, region.Protect, ®ion.Protect)) {
// Could not restore; fast fail
__fastfail(GetLastError());
}
@@ -448,7 +454,7 @@ utils::memory_tenderizer::memory_tenderizer(const void* pAddress, size_t length,
utils::memory_tenderizer::~memory_tenderizer() {
for (auto& region : std::ranges::reverse_view(m_regions)) {
- if (!VirtualProtect(region.BaseAddress, region.RegionSize, region.Protect, ®ion.Protect)) {
+ if (!VirtualProtectEx(m_process, region.BaseAddress, region.RegionSize, region.Protect, ®ion.Protect)) {
// Could not restore; fast fail
__fastfail(GetLastError());
}
@@ -644,3 +650,25 @@ std::wstring utils::escape_shell_arg(const std::wstring& arg) {
}
return res;
}
+
+std::wstring utils::format_win32_error(DWORD err) {
+ wchar_t* pwszMsg = nullptr;
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ err,
+ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
+ reinterpret_cast(&pwszMsg),
+ 0,
+ nullptr);
+ if (pwszMsg) {
+ std::wstring result = std::format(L"Win32 error ({}=0x{:X}): {}", err, err, pwszMsg);
+ while (!result.empty() && std::isspace(result.back()))
+ result.pop_back();
+ LocalFree(pwszMsg);
+ return result;
+ }
+
+ return std::format(L"Win32 error ({}=0x{:X})", err, err);
+}
diff --git a/Dalamud.Boot/utils.h b/Dalamud.Boot/utils.h
index 85509cf38..f10e277c0 100644
--- a/Dalamud.Boot/utils.h
+++ b/Dalamud.Boot/utils.h
@@ -111,10 +111,13 @@ namespace utils {
};
class memory_tenderizer {
+ HANDLE m_process;
std::span m_data;
std::vector m_regions;
public:
+ memory_tenderizer(HANDLE hProcess, const void* pAddress, size_t length, DWORD dwNewProtect);
+
memory_tenderizer(const void* pAddress, size_t length, DWORD dwNewProtect);
template&& std::is_standard_layout_v>>
@@ -273,4 +276,6 @@ namespace utils {
void wait_for_game_window();
std::wstring escape_shell_arg(const std::wstring& arg);
+
+ std::wstring format_win32_error(DWORD err);
}
diff --git a/Dalamud.Injector.Boot/main.cpp b/Dalamud.Injector.Boot/main.cpp
index 741505d08..7fc44f5e1 100644
--- a/Dalamud.Injector.Boot/main.cpp
+++ b/Dalamud.Injector.Boot/main.cpp
@@ -23,7 +23,7 @@ int wmain(int argc, wchar_t** argv)
// =========================================================================== //
void* entrypoint_vfn;
- int result = InitializeClrAndGetEntryPoint(
+ const auto result = InitializeClrAndGetEntryPoint(
GetModuleHandleW(nullptr),
false,
runtimeconfig_path,
@@ -33,15 +33,15 @@ int wmain(int argc, wchar_t** argv)
L"Dalamud.Injector.EntryPoint+MainDelegate, Dalamud.Injector",
&entrypoint_vfn);
- if (result != 0)
+ if (FAILED(result))
return result;
- typedef void (CORECLR_DELEGATE_CALLTYPE* custom_component_entry_point_fn)(int, wchar_t**);
+ typedef int (CORECLR_DELEGATE_CALLTYPE* custom_component_entry_point_fn)(int, wchar_t**);
custom_component_entry_point_fn entrypoint_fn = reinterpret_cast(entrypoint_vfn);
logging::I("Running Dalamud Injector...");
- entrypoint_fn(argc, argv);
+ const auto ret = entrypoint_fn(argc, argv);
logging::I("Done!");
- return 0;
+ return ret;
}
diff --git a/Dalamud.Injector/EntryPoint.cs b/Dalamud.Injector/EntryPoint.cs
index f839d9656..9e2b95657 100644
--- a/Dalamud.Injector/EntryPoint.cs
+++ b/Dalamud.Injector/EntryPoint.cs
@@ -31,89 +31,100 @@ namespace Dalamud.Injector
///
/// Count of arguments.
/// char** string arguments.
- public delegate void MainDelegate(int argc, IntPtr argvPtr);
+ /// Return value (HRESULT).
+ public delegate int MainDelegate(int argc, IntPtr argvPtr);
///
/// Start the Dalamud injector.
///
/// Count of arguments.
/// byte** string arguments.
- public static void Main(int argc, IntPtr argvPtr)
+ /// Return value (HRESULT).
+ public static int Main(int argc, IntPtr argvPtr)
{
- List args = new(argc);
-
- unsafe
+ try
{
- var argv = (IntPtr*)argvPtr;
- for (var i = 0; i < argc; i++)
- args.Add(Marshal.PtrToStringUni(argv[i]));
- }
+ List args = new(argc);
- Init(args);
- args.Remove("-v"); // Remove "verbose" flag
-
- if (args.Count >= 2 && args[1].ToLowerInvariant() == "launch-test")
- {
- Environment.Exit(ProcessLaunchTestCommand(args));
- return;
- }
-
- DalamudStartInfo startInfo = null;
- if (args.Count == 1)
- {
- // No command defaults to inject
- args.Add("inject");
- args.Add("--all");
-
-#if !DEBUG
- args.Add("--warn");
-#endif
-
- }
- else if (int.TryParse(args[1], out var _))
- {
- // Assume that PID has been passed.
- args.Insert(1, "inject");
-
- // If originally second parameter exists, then assume that it's a base64 encoded start info.
- // Dalamud.Injector.exe inject [pid] [base64]
- if (args.Count == 4)
+ unsafe
{
- startInfo = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(Convert.FromBase64String(args[3])));
- args.RemoveAt(3);
+ var argv = (IntPtr*)argvPtr;
+ for (var i = 0; i < argc; i++)
+ args.Add(Marshal.PtrToStringUni(argv[i]));
+ }
+
+ Init(args);
+ args.Remove("-v"); // Remove "verbose" flag
+
+ if (args.Count >= 2 && args[1].ToLowerInvariant() == "launch-test")
+ {
+ return ProcessLaunchTestCommand(args);
+ }
+
+ DalamudStartInfo startInfo = null;
+ if (args.Count == 1)
+ {
+ // No command defaults to inject
+ args.Add("inject");
+ args.Add("--all");
+
+ #if !DEBUG
+ args.Add("--warn");
+ #endif
+
+ }
+ else if (int.TryParse(args[1], out var _))
+ {
+ // Assume that PID has been passed.
+ args.Insert(1, "inject");
+
+ // If originally second parameter exists, then assume that it's a base64 encoded start info.
+ // Dalamud.Injector.exe inject [pid] [base64]
+ if (args.Count == 4)
+ {
+ startInfo = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(Convert.FromBase64String(args[3])));
+ args.RemoveAt(3);
+ }
+ }
+
+ startInfo = ExtractAndInitializeStartInfoFromArguments(startInfo, args);
+ // Remove already handled arguments
+ args.Remove("--console");
+ args.Remove("--msgbox1");
+ args.Remove("--msgbox2");
+ args.Remove("--msgbox3");
+ args.Remove("--etw");
+ args.Remove("--veh");
+ args.Remove("--veh-full");
+ args.Remove("--no-plugin");
+ args.Remove("--no-3rd-plugin");
+ args.Remove("--crash-handler-console");
+ args.Remove("--no-exception-handlers");
+
+ var mainCommand = args[1].ToLowerInvariant();
+ if (mainCommand.Length > 0 && mainCommand.Length <= 6 && "inject"[..mainCommand.Length] == mainCommand)
+ {
+ return ProcessInjectCommand(args, startInfo);
+ }
+ else if (mainCommand.Length > 0 && mainCommand.Length <= 6 &&
+ "launch"[..mainCommand.Length] == mainCommand)
+ {
+ return ProcessLaunchCommand(args, startInfo);
+ }
+ else if (mainCommand.Length > 0 && mainCommand.Length <= 4 &&
+ "help"[..mainCommand.Length] == mainCommand)
+ {
+ return ProcessHelpCommand(args, args.Count >= 3 ? args[2] : null);
+ }
+ else
+ {
+ throw new CommandLineException($"\"{mainCommand}\" is not a valid command.");
}
}
-
- startInfo = ExtractAndInitializeStartInfoFromArguments(startInfo, args);
- // Remove already handled arguments
- args.Remove("--console");
- args.Remove("--msgbox1");
- args.Remove("--msgbox2");
- args.Remove("--msgbox3");
- args.Remove("--etw");
- args.Remove("--veh");
- args.Remove("--veh-full");
- args.Remove("--no-plugin");
- args.Remove("--no-3rd-plugin");
- args.Remove("--crash-handler-console");
- args.Remove("--no-exception-handlers");
-
- var mainCommand = args[1].ToLowerInvariant();
- if (mainCommand.Length > 0 && mainCommand.Length <= 6 && "inject"[..mainCommand.Length] == mainCommand)
+ catch (Exception e)
{
- Environment.Exit(ProcessInjectCommand(args, startInfo));
- }
- else if (mainCommand.Length > 0 && mainCommand.Length <= 6 && "launch"[..mainCommand.Length] == mainCommand)
- {
- Environment.Exit(ProcessLaunchCommand(args, startInfo));
- }
- else if (mainCommand.Length > 0 && mainCommand.Length <= 4 && "help"[..mainCommand.Length] == mainCommand)
- {
- Environment.Exit(ProcessHelpCommand(args, args.Count >= 3 ? args[2] : null));
- }
- else
- {
- throw new CommandLineException($"\"{mainCommand}\" is not a valid command.");
+ Log.Error(e, "Operation failed.");
+ return e.HResult;
}
}
@@ -189,6 +200,7 @@ namespace Dalamud.Injector
CullLogFile(logPath, 1 * 1024 * 1024);
Log.Logger = new LoggerConfiguration()
+ .WriteTo.Console(standardErrorFromLevel: LogEventLevel.Debug)
.WriteTo.File(logPath, fileSizeLimitBytes: null)
.MinimumLevel.ControlledBy(levelSwitch)
.CreateLogger();
@@ -800,12 +812,8 @@ namespace Dalamud.Injector
{
var startInfo = AdjustStartInfo(dalamudStartInfo, gamePath);
Log.Information("Using start info: {0}", JsonConvert.SerializeObject(startInfo));
- if (RewriteRemoteEntryPointW(p.Handle, gamePath, JsonConvert.SerializeObject(startInfo)) != 0)
- {
- Log.Error("[HOOKS] RewriteRemoteEntryPointW failed");
- throw new Exception("RewriteRemoteEntryPointW failed");
- }
-
+ Marshal.ThrowExceptionForHR(
+ RewriteRemoteEntryPointW(p.Handle, gamePath, JsonConvert.SerializeObject(startInfo)));
Log.Verbose("RewriteRemoteEntryPointW called!");
}
},
diff --git a/lib/CoreCLR/boot.cpp b/lib/CoreCLR/boot.cpp
index e3db99c4f..54276aad1 100644
--- a/lib/CoreCLR/boot.cpp
+++ b/lib/CoreCLR/boot.cpp
@@ -27,7 +27,7 @@ void ConsoleTeardown()
std::optional g_clr;
-int InitializeClrAndGetEntryPoint(
+HRESULT InitializeClrAndGetEntryPoint(
void* calling_module,
bool enable_etw,
std::wstring runtimeconfig_path,
@@ -76,7 +76,7 @@ int InitializeClrAndGetEntryPoint(
if (result != 0)
{
logging::E("Unable to get RoamingAppData path (err={})", result);
- return result;
+ return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
}
std::filesystem::path fs_app_data(_appdata);
@@ -92,7 +92,7 @@ int InitializeClrAndGetEntryPoint(
if (!std::filesystem::exists(dotnet_path))
{
logging::E("Error: Unable to find .NET runtime path");
- return 1;
+ return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
}
get_hostfxr_parameters init_parameters
@@ -137,12 +137,12 @@ int InitializeClrAndGetEntryPoint(
entrypoint_delegate_type_name.c_str(),
nullptr, entrypoint_fn)) != 0)
{
- logging::E("Failed to load module (err={})", result);
+ logging::E("Failed to load module (err=0x{:X})", static_cast(result));
return result;
}
logging::I("Done!");
// =========================================================================== //
- return 0;
+ return S_OK;
}
diff --git a/lib/CoreCLR/boot.h b/lib/CoreCLR/boot.h
index f75077edd..33bc58bbf 100644
--- a/lib/CoreCLR/boot.h
+++ b/lib/CoreCLR/boot.h
@@ -1,7 +1,7 @@
void ConsoleSetup(const std::wstring console_name);
void ConsoleTeardown();
-int InitializeClrAndGetEntryPoint(
+HRESULT InitializeClrAndGetEntryPoint(
void* calling_module,
bool enable_etw,
std::wstring runtimeconfig_path,