Skip to content

Commit 045bde9

Browse files
committed
CEF reworks #3: Miscellaneous and refactors
1 parent d91aeb8 commit 045bde9

File tree

3 files changed

+129
-110
lines changed

3 files changed

+129
-110
lines changed

Client/ceflauncher/Main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ struct HINSTANCE__;
2727
using HINSTANCE = HINSTANCE__*;
2828
using LPSTR = char*;
2929

30-
[[nodiscard("InitCEF return value must be used")]] __declspec(dllimport) auto InitCEF() -> int;
30+
extern "C" [[nodiscard("InitCEF return value must be used")]] __declspec(dllimport) int __cdecl InitCEF();
3131

3232
// Users are faced with vague crashes in CEFLauncher.exe, so rather than over-engineering all this is intended
3333
// Do note that CEFLauncher.exe ends up hosting any GPU rendering processes of CEF

Client/ceflauncher_DLL/Main.cpp

Lines changed: 127 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -9,111 +9,170 @@
99
*
1010
*****************************************************************************/
1111

12+
// #define CEF_ENABLE_SANDBOX
13+
14+
#include <atomic>
15+
1216
#define WIN32_NO_STATUS
1317
#define WIN32_LEAN_AND_MEAN
1418
#include <Windows.h>
19+
1520
#undef WIN32_NO_STATUS
1621
#include <ntstatus.h>
17-
#include <winnt.h>
1822
#include <winternl.h>
1923
#include <delayimp.h>
24+
2025
#include "CCefApp.h"
21-
#include <string>
22-
#include <cef3/cef/include/cef_sandbox_win.h>
23-
#include <SharedUtil.h>
26+
#include "SharedUtil.h"
2427

25-
// #define CEF_ENABLE_SANDBOX
2628
#ifdef CEF_ENABLE_SANDBOX
27-
#pragma comment(lib, "cef_sandbox.lib")
29+
#include <cef3/cef/include/cef_sandbox_win.h>
30+
#pragma comment(lib, "cef_sandbox.lib")
2831
#endif
2932

30-
DWORD WINAPI CheckParentProcessAliveness(LPVOID);
33+
// Return codes
34+
inline constexpr int CEF_INIT_SUCCESS = 0;
35+
inline constexpr int CEF_INIT_ERROR_NO_BASE_DIR = -1;
36+
inline constexpr int CEF_INIT_ERROR_DLL_LOAD_FAILED = -2;
3137

32-
int _declspec(dllexport) InitCEF()
33-
{
34-
// Get MTA base directory and set DLL directory to MTA folder
35-
const SString strBaseDir = SharedUtil::GetMTAProcessBaseDir();
36-
37-
if (strBaseDir.empty())
38-
{
39-
// Unable to determine base directory - CEF cannot initialize
40-
return -1;
41-
}
42-
43-
const SString strMTADir = SharedUtil::PathJoin(strBaseDir, "MTA");
44-
45-
SetDllDirectoryW(SharedUtil::FromUTF8(strMTADir));
38+
inline constexpr DWORD CEF_PARENT_CHECK_INTERVAL = 1000;
39+
inline constexpr const char* CEF_DLL_NAME = "libcef.dll";
40+
inline constexpr const char* CEF_MTA_SUBDIR = "MTA";
4641

47-
// Load libcef.dll from the DLL directory
48-
assert(SUCCEEDED(__HrLoadAllImportsForDll("libcef.dll")));
42+
inline constexpr DWORD PARENT_CHECK_ERROR_NO_QUERY_FUNC = 1;
43+
inline constexpr DWORD PARENT_CHECK_ERROR_QUERY_FAILED = 2;
44+
inline constexpr DWORD PARENT_CHECK_ERROR_OPEN_FAILED = 3;
4945

50-
// Load CEF
51-
CefMainArgs mainArgs(GetModuleHandle(NULL));
52-
CefRefPtr<CCefApp> app{new CCefApp};
46+
using NtQueryInformationProcessFunc = NTSTATUS(NTAPI*)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
5347

54-
void* sandboxInfo = nullptr;
55-
#ifdef CEF_ENABLE_SANDBOX
56-
CefScopedSandboxInfo scopedSandbox;
57-
sandboxInfo = scopedSandbox.sandbox_info();
58-
#endif
48+
// Safe parent monitor thread shutdown
49+
std::atomic<bool> g_bShouldTerminateMonitor{false};
50+
std::atomic<HANDLE> g_hMonitorThread{nullptr};
5951

60-
const HANDLE parentCheckThread = CreateThread(nullptr, 0, CheckParentProcessAliveness, nullptr, 0, nullptr);
52+
namespace
53+
{
54+
[[nodiscard]] auto GetNtQueryInformationProcess() noexcept -> NtQueryInformationProcessFunc
55+
{
56+
const auto ntdll = GetModuleHandleW(L"ntdll.dll");
57+
if (!ntdll)
58+
return nullptr;
59+
60+
const auto procAddr = GetProcAddress(ntdll, "NtQueryInformationProcess");
61+
if (!procAddr)
62+
return nullptr;
63+
64+
return reinterpret_cast<NtQueryInformationProcessFunc>(procAddr);
65+
}
6166

62-
const int exitCode = CefExecuteProcess(mainArgs, app, sandboxInfo);
67+
[[nodiscard]] auto GetParentProcessId(NtQueryInformationProcessFunc queryFunc) noexcept -> DWORD
68+
{
69+
PROCESS_BASIC_INFORMATION info{};
70+
ULONG returnLength = 0;
71+
72+
if (const auto status = queryFunc(GetCurrentProcess(), ProcessBasicInformation, &info, sizeof(info), &returnLength);
73+
!NT_SUCCESS(status) || returnLength < sizeof(PROCESS_BASIC_INFORMATION))
74+
{
75+
return 0;
76+
}
6377

64-
if (parentCheckThread != nullptr)
78+
return static_cast<DWORD>(reinterpret_cast<ULONG_PTR>(info.Reserved3));
79+
}
80+
81+
void MonitorParentProcess(HANDLE parentProcess) noexcept
6582
{
66-
TerminateThread(parentCheckThread, 0);
67-
CloseHandle(parentCheckThread);
83+
while (!g_bShouldTerminateMonitor.load(std::memory_order_acquire))
84+
{
85+
const DWORD result = WaitForSingleObject(parentProcess, CEF_PARENT_CHECK_INTERVAL);
86+
87+
if (result == WAIT_OBJECT_0)
88+
{
89+
DWORD exitCode = 0;
90+
if (GetExitCodeProcess(parentProcess, &exitCode))
91+
ExitProcess(exitCode);
92+
else
93+
ExitProcess(0);
94+
}
95+
else if (result == WAIT_FAILED)
96+
{
97+
// Wine/Proton compatibility: Exit thread instead of terminating process
98+
// Wine's handle implementation dont support all wait operations reliably
99+
break;
100+
}
101+
}
68102
}
103+
} // namespace
69104

70-
return exitCode;
71-
}
105+
DWORD WINAPI CheckParentProcessAliveness(LPVOID) noexcept;
72106

73-
static DWORD WINAPI CheckParentProcessAliveness(LPVOID)
107+
BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, [[maybe_unused]] LPVOID lpReserved)
74108
{
75-
NTSTATUS(NTAPI * queryInformation)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG) = nullptr;
76-
77-
if (auto ntdll = GetModuleHandleW(L"ntdll.dll"); ntdll != nullptr)
109+
if (dwReason == DLL_PROCESS_ATTACH)
78110
{
79-
auto procAddr = GetProcAddress(ntdll, "NtQueryInformationProcess");
80-
queryInformation = reinterpret_cast<decltype(queryInformation)>(reinterpret_cast<void*>(procAddr));
111+
DisableThreadLibraryCalls(hModule);
112+
g_bShouldTerminateMonitor.store(false, std::memory_order_relaxed);
113+
g_hMonitorThread.store(nullptr, std::memory_order_relaxed);
81114
}
115+
else if (dwReason == DLL_PROCESS_DETACH)
116+
{
117+
g_bShouldTerminateMonitor.store(true, std::memory_order_release);
118+
}
119+
120+
return TRUE;
121+
}
82122

83-
if (queryInformation == nullptr)
84-
return 1;
123+
extern "C" [[nodiscard]] __declspec(dllexport) auto InitCEF() noexcept -> int
124+
{
125+
const auto baseDir = SharedUtil::GetMTAProcessBaseDir();
126+
if (baseDir.empty())
127+
return CEF_INIT_ERROR_NO_BASE_DIR;
128+
129+
const auto mtaDir = SharedUtil::PathJoin(baseDir, CEF_MTA_SUBDIR);
130+
SetDllDirectoryW(SharedUtil::FromUTF8(mtaDir));
85131

86-
PROCESS_BASIC_INFORMATION info{};
132+
if (FAILED(__HrLoadAllImportsForDll(CEF_DLL_NAME)))
133+
return CEF_INIT_ERROR_DLL_LOAD_FAILED;
87134

88-
ULONG returnLength = 0;
89-
NTSTATUS status = queryInformation(GetCurrentProcess(), ProcessBasicInformation, &info, sizeof(info), &returnLength);
135+
const CefMainArgs mainArgs(GetModuleHandleW(nullptr));
136+
const CefRefPtr<CCefApp> app{new CCefApp};
90137

91-
if (!NT_SUCCESS(status) || returnLength < sizeof(PROCESS_BASIC_INFORMATION))
92-
return 2;
138+
void* sandboxInfo = nullptr;
139+
#ifdef CEF_ENABLE_SANDBOX
140+
const CefScopedSandboxInfo scopedSandbox;
141+
sandboxInfo = scopedSandbox.sandbox_info();
142+
#endif
93143

94-
const auto parentProcessId = static_cast<DWORD>(reinterpret_cast<ULONG_PTR>(info.Reserved3));
95-
const HANDLE parentProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, parentProcessId);
144+
const auto hThread = CreateThread(nullptr, 0, CheckParentProcessAliveness, nullptr, 0, nullptr);
145+
if (hThread)
146+
g_hMonitorThread.store(hThread, std::memory_order_release);
96147

97-
if (parentProcess == nullptr)
98-
{
99-
if (GetLastError() == ERROR_INVALID_PARAMETER)
100-
ExitProcess(0);
148+
return CefExecuteProcess(mainArgs, app, sandboxInfo);
149+
}
101150

102-
return 3;
103-
}
151+
static auto WINAPI CheckParentProcessAliveness([[maybe_unused]] LPVOID) noexcept -> DWORD
152+
{
153+
const auto queryFunc = GetNtQueryInformationProcess();
154+
if (!queryFunc)
155+
return PARENT_CHECK_ERROR_NO_QUERY_FUNC;
104156

105-
while (true)
106-
{
107-
DWORD exitCode{};
157+
const auto parentProcessId = GetParentProcessId(queryFunc);
158+
if (parentProcessId == 0)
159+
return PARENT_CHECK_ERROR_QUERY_FAILED;
108160

109-
if (!GetExitCodeProcess(parentProcess, &exitCode) || exitCode != STILL_ACTIVE)
161+
const auto parentProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, false, parentProcessId);
162+
if (!parentProcess)
163+
{
164+
// Wine/Proton fallback: PROCESS_QUERY_LIMITED_INFORMATION may not be implemented
165+
const auto parentProcessFallback = OpenProcess(SYNCHRONIZE, false, parentProcessId);
166+
if (parentProcessFallback)
110167
{
111-
CloseHandle(parentProcess);
112-
ExitProcess(exitCode);
168+
MonitorParentProcess(parentProcessFallback);
169+
CloseHandle(parentProcessFallback);
170+
return CEF_INIT_SUCCESS;
113171
}
114-
115-
Sleep(1000);
172+
return PARENT_CHECK_ERROR_OPEN_FAILED;
116173
}
117174

118-
return 0;
175+
MonitorParentProcess(parentProcess);
176+
CloseHandle(parentProcess);
177+
return CEF_INIT_SUCCESS;
119178
}

Client/cefweb/CWebCore.cpp

Lines changed: 1 addition & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,9 @@ bool CWebCore::Initialise(bool gpuEnabled)
6666

6767
m_bGPUEnabled = gpuEnabled;
6868

69-
// Log current working directory at entry
70-
std::array<wchar_t, MAX_PATH> cwdBefore{};
71-
GetCurrentDirectoryW(static_cast<DWORD>(cwdBefore.size()), cwdBefore.data());
72-
AddReportLog(8010, SString("CWebCore::Initialise - CWD at entry: %s", *SharedUtil::ToUTF8(cwdBefore.data())));
73-
7469
// Get MTA base directory
7570
SString strBaseDir = SharedUtil::GetMTAProcessBaseDir();
7671

77-
AddReportLog(8011, SString("CWebCore::Initialise - GetMTAProcessBaseDir returned: '%s'", strBaseDir.c_str()));
78-
7972
if (strBaseDir.empty())
8073
{
8174
g_pCore->GetConsole()->Printf("CEF initialization skipped - Unable to determine MTA base directory");
@@ -99,17 +92,6 @@ bool CWebCore::Initialise(bool gpuEnabled)
9992
// Read GTA path from registry to pass to CEF subprocess
10093
int iRegistryResult = 0;
10194
const SString strGTAPath = GetCommonRegistryValue("", "GTA:SA Path", &iRegistryResult);
102-
g_pCore->GetConsole()->Printf("DEBUG: Registry read result=%d, path='%s'", iRegistryResult, strGTAPath.c_str());
103-
AddReportLog(8017, SString("DEBUG: Registry read result=%d, path='%s'", iRegistryResult, strGTAPath.c_str()));
104-
105-
if (!strGTAPath.empty())
106-
{
107-
AddReportLog(8015, SString("Read GTA path from registry: %s", *strGTAPath));
108-
}
109-
else
110-
{
111-
AddReportLog(8016, "Failed to read GTA path from registry");
112-
}
11395

11496
// Check if process is running with elevated privileges
11597
// CEF subprocesses may have communication issues when running elevated
@@ -134,14 +116,9 @@ bool CWebCore::Initialise(bool gpuEnabled)
134116
AddReportLog(8021, "WARNING: Process is running with elevated privileges (Administrator)");
135117
AddReportLog(8022, "CEF browser features may not work correctly when running as Administrator");
136118
AddReportLog(8023, "Consider running MTA without Administrator privileges for full browser functionality");
137-
g_pCore->GetConsole()->Printf("^3WARNING: Running as Administrator - browser features may be limited");
119+
g_pCore->GetConsole()->Printf("WARNING: Running as Administrator - browser features may be limited");
138120
}
139121

140-
// Log current working directory before CEF initialization
141-
std::array<wchar_t, MAX_PATH> cwdBeforeCef{};
142-
GetCurrentDirectoryW(static_cast<DWORD>(cwdBeforeCef.size()), cwdBeforeCef.data());
143-
AddReportLog(8012, SString("CWebCore::Initialise - CWD before CefInitialize: %s", *SharedUtil::ToUTF8(cwdBeforeCef.data())));
144-
145122
if (!FileExists(strLauncherPath))
146123
{
147124
g_pCore->GetConsole()->Printf("CEF initialization skipped - CEFLauncher not found: %s", *strLauncherPath);
@@ -164,11 +141,6 @@ bool CWebCore::Initialise(bool gpuEnabled)
164141
return false;
165142
}
166143

167-
// Proceed with CEF initialization
168-
AddReportLog(8018, SString("Pre-CEF Init: Launcher path: %s", *strLauncherPath));
169-
AddReportLog(8019, SString("Pre-CEF Init: Cache path: %s", *strCachePath));
170-
AddReportLog(8020, SString("Pre-CEF Init: Locales path: %s", *strLocalesPath));
171-
172144
// Use std::filesystem for CWD management with RAII scope guard
173145
namespace fs = std::filesystem;
174146
std::error_code ec;
@@ -189,10 +161,6 @@ bool CWebCore::Initialise(bool gpuEnabled)
189161
~CwdGuard() {
190162
std::error_code restoreEc;
191163
fs::current_path(savedPath, restoreEc);
192-
if (!restoreEc)
193-
{
194-
AddReportLog(8027, SString("Restored original CWD: %s", savedPath.string().c_str()));
195-
}
196164
}
197165
} cwdGuard(savedCwd);
198166

@@ -205,7 +173,6 @@ bool CWebCore::Initialise(bool gpuEnabled)
205173
m_bInitialised = false;
206174
return false;
207175
}
208-
AddReportLog(8026, SString("Temporarily changed CWD to MTA dir for CEF init: %s", *strMTADir));
209176

210177
CefMainArgs mainArgs;
211178
void* sandboxInfo = nullptr;
@@ -249,18 +216,11 @@ bool CWebCore::Initialise(bool gpuEnabled)
249216
}
250217

251218
// CWD will be restored by cwdGuard destructor when this function returns
252-
253-
// Log CWD after CEF initialization
254-
std::array<wchar_t, MAX_PATH> cwdAfterCef{};
255-
GetCurrentDirectoryW(static_cast<DWORD>(cwdAfterCef.size()), cwdAfterCef.data());
256-
AddReportLog(8013, SString("CWebCore::Initialise - CWD after CefInitialize: %s", *SharedUtil::ToUTF8(cwdAfterCef.data())));
257219

258220
if (m_bInitialised)
259221
{
260222
// Register custom scheme handler factory only if initialization succeeded
261223
CefRegisterSchemeHandlerFactory("http", "mta", app);
262-
g_pCore->GetConsole()->Printf("CEF initialized successfully");
263-
AddReportLog(8000, "CEF initialized successfully");
264224
}
265225
else
266226
{

0 commit comments

Comments
 (0)