diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..7581738 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,70 @@ +name: Build and Release + +on: + push: + tags: + - 'v*' + pull_request: + branches: + - main + +jobs: + build: + runs-on: windows-latest + permissions: + contents: write + id-token: write + attestations: write + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: UCRT64 + update: true + install: >- + mingw-w64-ucrt-x86_64-clang + mingw-w64-ucrt-x86_64-cmake + mingw-w64-ucrt-x86_64-ninja + + - name: Build with LLVM/Clang + shell: msys2 {0} + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_BUILD_TYPE=MinSizeRel + cmake --build build + + - name: Attest Build Provenance + if: startsWith(github.ref, 'refs/tags/v') + uses: actions/attest@v4 + with: + subject-path: build/umpdc.dll + + - name: Upload Artifact + uses: actions/upload-artifact@v7 + with: + name: umpdc-dll + path: build/umpdc.dll + + release: + needs: build + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Download Artifact + uses: actions/download-artifact@v8 + with: + name: umpdc-dll + + - name: Create Release + uses: softprops/action-gh-release@v3 + with: + files: umpdc.dll + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Build.cmd b/Build.cmd deleted file mode 100644 index cbe84e2..0000000 --- a/Build.cmd +++ /dev/null @@ -1,7 +0,0 @@ -@echo off -cd "%~dp0/src" - -rd /q /s "bin" -md "bin" - -gcc.exe -Oz -Wl,--gc-sections,--exclude-all-symbols -municode -shared -nostdlib -s "Library.c" -lntdll -lwtsapi32 -lkernel32 -luser32 -ladvapi32 -lshell32 -o "bin\umpdc.dll" \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9476d51 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.10) +project(NoSteamWebHelper C) + +add_library(umpdc SHARED src/Library.c) + +# Extreme size optimization & no stdlib +if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(umpdc PRIVATE + -Oz + -municode + -fno-stack-protector + -fno-ident + -fno-asynchronous-unwind-tables + -ffunction-sections + -fdata-sections + ) + target_link_options(umpdc PRIVATE + -nostdlib + -Wl,--gc-sections + -Wl,--exclude-all-symbols + -Wl,--no-insert-timestamp + -s + ) + target_link_libraries(umpdc PRIVATE ntdll kernel32 user32 advapi32 shell32) +elseif(MSVC) + target_compile_options(umpdc PRIVATE /O1 /GS- /Zl) + target_link_options(umpdc PRIVATE + /NODEFAULTLIB + /ENTRY:DllMainCRTStartup + /OPT:REF + /OPT:ICF + /MERGE:.rdata=.text + ) + target_link_libraries(umpdc PRIVATE ntdll.lib kernel32.lib user32.lib advapi32.lib shell32.lib) +endif() diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..6588168 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,46 @@ +# NoSteamWebHelper - Instructions & Context + +## Project Overview +NoSteamWebHelper is a high-performance Win32 utility (C-based) that dynamically disables Steam's Chromium Embedded Framework (CEF) / WebHelper when a game is running. It aims to replace the removed `-no-browser` flag, freeing system resources while keeping Steam functional. + +### Architecture +- **Language**: Pure C (Win32 API), optimized for binary size (`nostdlib`). +- **Core Component**: `umpdc.dll`, designed to be loaded via DLL side-loading/injection (placed next to `steam.exe`). +- **Mechanics**: + - **Event Hooking**: Uses `SetWinEventHook` to detect Steam window creations. + - **Registry Monitoring**: Asynchronously monitors `HKCU\SOFTWARE\Valve\Steam\RunningAppID` to detect game state changes. + - **Process Management**: Uses `Toolhelp32` snapshotting to identify and terminate `steamwebhelper.exe` processes spawned by Steam. + - **Thread Safety**: Employs `Interlocked` atomic operations for race-condition-free initialization of the monitoring thread. + - **UI**: Provides a minimalist system tray icon for manual toggling and status feedback. + +## Building and Pipeline +The project uses a modern CMake-based build system optimized for size and performance. + +### Build Requirements +- **MSYS2** (UCRT64 environment) +- **LLVM/Clang** (preferred) or GCC +- **CMake** & **Ninja** + +### Compilation Commands +```bash +# Configuration for maximum size optimization +cmake -S . -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=MinSizeRel + +# Build +cmake --build build +``` +Output binary: `build/umpdc.dll` + +### CI/CD Pipeline +- **Platform**: GitHub Actions (`.github/workflows/release.yml`) +- **Features**: + - **Validation**: Compiles the project on every Pull Request to `main`. + - **Deployment**: On version tags (`v*`), performs a production build using **LLVM/Clang 22**. + - **Immutability & Trust**: Generates **GitHub Artifact Attestations** (SLSA) for every release, ensuring cryptographic proof of provenance. + +## Development Conventions +- **Minimalism**: Avoid C Standard Library (`nostdlib`). Use direct Win32/NT APIs. +- **Size Optimization**: Use aggressive compiler flags (`-Oz`, `-fno-stack-protector`, `-fno-asynchronous-unwind-tables`). +- **Nomenclature**: Use descriptive constants (defined in `Library.c`) instead of magic strings or numbers. +- **Safety**: Always close Handles and use atomic operations when managing shared state between the Main thread and the Registry Monitor thread. +- **Provenance**: All production binaries must be built through the CI pipeline to maintain "Immutable" status. \ No newline at end of file diff --git a/README.md b/README.md index a44ccbc..b4e6446 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,46 @@ -> [!CAUTION] -> This project is now deprecated. - # NoSteamWebHelper - A program that disables Steam's CEF/Chromium Embedded Framework. +A high-performance utility that disables Steam's CEF (Chromium Embedded Framework) / Steam WebHelper while gaming. ## Aim -This program was created with the intent of replacing of Steam's command-line parameter `-no-browser` which was [removed.](https://steamcommunity.com/groups/SteamClientBeta/discussions/3/3710433479207750727/?ctp=42) - - -## How does NoSteamWebHelper kill or disable the CEF/Chromium Embedded Framework? - -The dynamic link library toggles the CEF depending if an is app running or not. - -- If an app is running then the CEF is disabled. - -- If an app is not running then the CEF is enabled. - -This way, Steam is still accessible to use. - -# Usage +This program replaces Steam's removed `-no-browser` command-line parameter. It dynamically toggles the Steam WebHelper based on your activity to free up system resources without losing access to your library. + +- **Game running**: Steam WebHelper is disabled (processes terminated and suppressed). +- **No game running**: Steam WebHelper is enabled (interface restored). + +## Key Features & Refactoring (2026) +This project has been fully refactored for maximum performance and reliability: +- **Ultra-Lightweight**: Written in pure C with `nostdlib`. The resulting `umpdc.dll` is only a few KB. +- **Modern Build System**: Migrated to **CMake** with support for **LLVM/Clang 22** for state-of-the-art optimizations. +- **Zero-Leak Architecture**: + - Uses `Interlocked` atomic operations for thread-safe monitor initialization. + - Replaced `wtsapi32` dependency with `Toolhelp32` for better compatibility and smaller footprint. + - Asynchronous registry monitoring using Win32 Events to prevent UI/Hook freezes. +- **Immutable Releases**: Automated CI/CD pipeline via GitHub Actions with cryptographic build provenance (SLSA). + +## Usage 1. Download the latest release from [GitHub Releases](https://github.com/Aetopia/NoSteamWebHelper/releases). - -2. Place `umpdc.dll` in your Steam installation directory where `steam.exe` is located. - -3. Make sure Steam is fully closed and launch a new instance of Steam. - -4. Start up an app and the CEF will be toggled accordingly. - -> [!NOTE] -> - You may also manually toggle the CEF via a tray icon. -> - To prevent the CEF from automatically showing when restored, pass `-silent` to Steam. - -## Build -1. Install & update [MSYS2](https://www.msys2.org): - - ```bash - pacman -Syu --noconfirm - ``` - -3. Install [GCC](https://gcc.gnu.org) & [MinHook](https://github.com/TsudaKageyu/minhook): - - ```bash - pacman -Syu mingw-w64-ucrt-x86_64-gcc --noconfirm - ``` - - -3. Start MSYS2's `UCRT64` environment & run `Build.cmd`. - - - +2. Place `umpdc.dll` in your Steam installation directory (same folder as `steam.exe`). +3. Ensure Steam is fully closed, then launch Steam. +4. The CEF will now toggle automatically when you launch/close a game. + +> [!TIP] +> - You can manually toggle the state via the tray icon. +> - Launch Steam with `-silent` to prevent the CEF from popping up unexpectedly when restored. + +## Build from Source +The project uses **MSYS2** with the **UCRT64** environment. + +1. **Install Prerequisites**: + ```bash + pacman -Syu mingw-w64-ucrt-x86_64-clang mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja + ``` + +2. **Compile**: + ```bash + cmake -S . -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=MinSizeRel + cmake --build build + ``` + The optimized `umpdc.dll` will be located in the `build/` directory. + +## CI/CD +This project uses GitHub Actions for automated builds. Every tagged release (`v*`) triggers a Clang-optimized build and generates a **Verified Provenance Attestation**, ensuring the binary's integrity. diff --git a/src/Library.c b/src/Library.c index 616b644..93ef1dc 100644 --- a/src/Library.c +++ b/src/Library.c @@ -1,132 +1,242 @@ #include -#include +#include #include -static DWORD WINAPI ThreadProc(LPVOID lpParameter); +#ifndef NT_SUCCESS +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#endif -static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +#define STEAM_REGISTRY_KEY L"SOFTWARE\\Valve\\Steam" +#define RUNNING_APP_ID_VALUE L"RunningAppID" +#define STEAM_WEB_HELPER_EXE L"steamwebhelper.exe" +#define VGUI_POPUP_WINDOW_CLASS L"vguiPopupWindow" +#define TRAY_ICON_TOOLTIP L"Steam WebHelper" + +static DWORD WINAPI MainThreadProc(LPVOID lpParameter); +static DWORD WINAPI RegistryMonitorThreadProc(LPVOID lpParameter); +static LRESULT CALLBACK TrayWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +static VOID CALLBACK WinEventProc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime); + +static NOTIFYICONDATAW g_TrayIconData = {0}; +static UINT g_TaskbarCreatedMsg = WM_NULL; +static HWINEVENTHOOK g_hEventHook = NULL; +static volatile LONG g_MonitorThreadStarted = 0; + +static void KillSteamWebHelperProcesses(void) +{ + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot == INVALID_HANDLE_VALUE) + return; + + PROCESSENTRY32W pe32; + pe32.dwSize = sizeof(PROCESSENTRY32W); + + if (Process32FirstW(hSnapshot, &pe32)) + { + do + { + if (CompareStringOrdinal(pe32.szExeFile, -1, STEAM_WEB_HELPER_EXE, -1, TRUE) == CSTR_EQUAL) + { + HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE, FALSE, pe32.th32ProcessID); + if (hProcess) + { + PROCESS_BASIC_INFORMATION pbi = {0}; + if (NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL))) + { + if (pbi.InheritedFromUniqueProcessId == (ULONG_PTR)GetCurrentProcessId()) + { + TerminateProcess(hProcess, EXIT_SUCCESS); + } + } + CloseHandle(hProcess); + } + } + } while (Process32NextW(hSnapshot, &pe32)); + } + CloseHandle(hSnapshot); +} + +static DWORD WINAPI RegistryMonitorThreadProc(LPVOID lpParameter) +{ + DWORD dwEventThread = (DWORD)(ULONG_PTR)lpParameter; + HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, dwEventThread); + if (!hThread) + { + InterlockedExchange(&g_MonitorThreadStarted, 0); + return EXIT_FAILURE; + } + + HKEY hKey = NULL; + if (RegOpenKeyExW(HKEY_CURRENT_USER, STEAM_REGISTRY_KEY, 0, KEY_NOTIFY | KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) + { + CloseHandle(hThread); + InterlockedExchange(&g_MonitorThreadStarted, 0); + return EXIT_FAILURE; + } + + HANDLE hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + if (!hEvent) + { + RegCloseKey(hKey); + CloseHandle(hThread); + InterlockedExchange(&g_MonitorThreadStarted, 0); + return EXIT_FAILURE; + } + + BOOL isAppRunning = FALSE; + DWORD dataSize = sizeof(BOOL); + if (RegGetValueW(hKey, NULL, RUNNING_APP_ID_VALUE, RRF_RT_REG_DWORD, NULL, &isAppRunning, &dataSize) == ERROR_SUCCESS) + { + if (isAppRunning) + { + SuspendThread(hThread); + KillSteamWebHelperProcesses(); + } + } + + while (TRUE) + { + if (RegNotifyChangeKeyValue(hKey, FALSE, REG_NOTIFY_CHANGE_LAST_SET, hEvent, TRUE) != ERROR_SUCCESS) + break; + + if (WaitForSingleObject(hEvent, INFINITE) != WAIT_OBJECT_0) + break; + + isAppRunning = FALSE; + dataSize = sizeof(BOOL); + if (RegGetValueW(hKey, NULL, RUNNING_APP_ID_VALUE, RRF_RT_REG_DWORD, NULL, &isAppRunning, &dataSize) == ERROR_SUCCESS) + { + if (isAppRunning) + { + SuspendThread(hThread); + KillSteamWebHelperProcesses(); + } + else + { + ResumeThread(hThread); + } + } + } + + CloseHandle(hEvent); + RegCloseKey(hKey); + CloseHandle(hThread); + InterlockedExchange(&g_MonitorThreadStarted, 0); + return EXIT_SUCCESS; +} + +static VOID CALLBACK WinEventProc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime) { - static NOTIFYICONDATAW _ = {.cbSize = sizeof(NOTIFYICONDATAW), - .uCallbackMessage = WM_USER, - .uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP, - .szTip = L"Steam WebHelper"}; - static UINT $ = WM_NULL; - - switch (uMsg) - { - case WM_CREATE: - $ = RegisterWindowMessageW(L"TaskbarCreated"); - _.hWnd = hWnd; - _.hIcon = LoadIconW(NULL, IDI_APPLICATION); - Shell_NotifyIconW(NIM_ADD, &_); - break; - - case WM_USER: - if (lParam == WM_RBUTTONDOWN) - { - HMENU hMenu = CreatePopupMenu(); - AppendMenuW(hMenu, MF_STRING, FALSE, L"On"); - AppendMenuW(hMenu, MF_STRING, TRUE, L"Off"); - SetForegroundWindow(hWnd); - - POINT _ = {}; - GetCursorPos(&_); - RegSetKeyValueW( - HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam", L"RunningAppID", REG_DWORD, - &((DWORD){TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, _.x, _.y, - 0, hWnd, NULL) - ? TRUE - : FALSE}), - sizeof(DWORD)); - - DestroyMenu(hMenu); - } - break; - - default: - if (uMsg == $) - Shell_NotifyIconW(NIM_ADD, &_); - break; - } - return DefWindowProcW(hWnd, uMsg, wParam, lParam); + WCHAR szClassName[16] = {0}; + if (!GetClassNameW(hwnd, szClassName, sizeof(szClassName) / sizeof(WCHAR))) + return; + + if (CompareStringOrdinal(VGUI_POPUP_WINDOW_CLASS, -1, szClassName, -1, FALSE) != CSTR_EQUAL || GetWindowTextLengthW(hwnd) < 1) + return; + + if (InterlockedCompareExchange(&g_MonitorThreadStarted, 1, 0) == 0) + { + HANDLE hThread = CreateThread(NULL, 0, RegistryMonitorThreadProc, (LPVOID)(ULONG_PTR)dwEventThread, 0, NULL); + if (hThread) + { + CloseHandle(hThread); + } + else + { + InterlockedExchange(&g_MonitorThreadStarted, 0); + } + } } -static VOID CALLBACK WinEventProc(HWINEVENTHOOK hWinEventHook, DWORD event, HWND hwnd, LONG idObject, LONG idChild, - DWORD dwEventThread, DWORD dwmsEventTime) +static void ShowContextMenu(HWND hWnd) { - WCHAR szClassName[16] = {}; - GetClassNameW(hwnd, szClassName, 16); - - if (CompareStringOrdinal(L"vguiPopupWindow", -1, szClassName, -1, FALSE) != CSTR_EQUAL || - GetWindowTextLengthW(hwnd) < 1) - return; - - CloseHandle(CreateThread(NULL, 0, ThreadProc, (LPVOID)FALSE, 0, NULL)); - - HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, dwEventThread); - HKEY hKey = NULL; - RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam", REG_OPTION_NON_VOLATILE, KEY_NOTIFY | KEY_QUERY_VALUE, - &hKey); - - while (!RegNotifyChangeKeyValue(hKey, FALSE, REG_NOTIFY_CHANGE_LAST_SET, NULL, FALSE)) - { - BOOL _ = FALSE; - RegGetValueW(hKey, NULL, L"RunningAppID", RRF_RT_REG_DWORD, NULL, (PVOID)&_, &((DWORD){sizeof(BOOL)})); - (_ ? SuspendThread : ResumeThread)(hThread); - if (_) - { - WTS_PROCESS_INFOW *pProcessInfo = {}; - DWORD $ = {}; - - WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pProcessInfo, &$); - for (DWORD _ = {}; _ < $; _++) - if (CompareStringOrdinal(pProcessInfo[_].pProcessName, -1, L"steamwebhelper.exe", -1, TRUE) == - CSTR_EQUAL) - { - HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE, FALSE, - pProcessInfo[_].ProcessId); - - PROCESS_BASIC_INFORMATION _ = {}; - NtQueryInformationProcess(hProcess, ProcessBasicInformation, &_, sizeof(PROCESS_BASIC_INFORMATION), - NULL); - - if (_.InheritedFromUniqueProcessId == GetCurrentProcessId()) - TerminateProcess(hProcess, EXIT_SUCCESS); - - CloseHandle(hProcess); - } - WTSFreeMemory(pProcessInfo); - } - } + HMENU hMenu = CreatePopupMenu(); + if (hMenu) + { + AppendMenuW(hMenu, MF_STRING, FALSE, L"On"); + AppendMenuW(hMenu, MF_STRING, TRUE, L"Off"); + SetForegroundWindow(hWnd); + + POINT pt = {0}; + GetCursorPos(&pt); + BOOL bOff = TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD, pt.x, pt.y, 0, hWnd, NULL); + + DWORD val = bOff ? TRUE : FALSE; + RegSetKeyValueW(HKEY_CURRENT_USER, STEAM_REGISTRY_KEY, RUNNING_APP_ID_VALUE, REG_DWORD, &val, sizeof(DWORD)); + + DestroyMenu(hMenu); + } } -static DWORD WINAPI ThreadProc(LPVOID lpParameter) +static LRESULT CALLBACK TrayWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - if (lpParameter) - SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE, NULL, WinEventProc, GetCurrentProcessId(), (DWORD){}, - WINEVENT_OUTOFCONTEXT); - else - CreateWindowExW( - WS_EX_LEFT | WS_EX_LTRREADING, - (LPCWSTR)(ULONG_PTR)RegisterClassW(&((WNDCLASSW){ - .lpszClassName = L" ", .hInstance = GetModuleHandleW(NULL), .lpfnWndProc = (WNDPROC)WndProc})), - NULL, WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL, GetModuleHandleW(NULL), NULL); - - MSG _ = {}; - while (GetMessageW(&_, NULL, (UINT){}, (UINT){})) - { - TranslateMessage(&_); - DispatchMessageW(&_); - } - return EXIT_SUCCESS; + switch (uMsg) + { + case WM_CREATE: + g_TaskbarCreatedMsg = RegisterWindowMessageW(L"TaskbarCreated"); + g_TrayIconData.cbSize = sizeof(NOTIFYICONDATAW); + g_TrayIconData.hWnd = hWnd; + g_TrayIconData.uID = 1; + g_TrayIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + g_TrayIconData.uCallbackMessage = WM_USER; + g_TrayIconData.hIcon = LoadIconW(NULL, IDI_APPLICATION); + lstrcpynW(g_TrayIconData.szTip, TRAY_ICON_TOOLTIP, sizeof(g_TrayIconData.szTip) / sizeof(WCHAR)); + Shell_NotifyIconW(NIM_ADD, &g_TrayIconData); + break; + + case WM_USER: + if (lParam == WM_RBUTTONDOWN) + { + ShowContextMenu(hWnd); + } + break; + + case WM_DESTROY: + Shell_NotifyIconW(NIM_DELETE, &g_TrayIconData); + PostQuitMessage(0); + break; + + default: + if (uMsg == g_TaskbarCreatedMsg && g_TaskbarCreatedMsg != WM_NULL) + { + Shell_NotifyIconW(NIM_ADD, &g_TrayIconData); + } + break; + } + return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +static DWORD WINAPI MainThreadProc(LPVOID lpParameter) +{ + g_hEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE, NULL, WinEventProc, GetCurrentProcessId(), 0, WINEVENT_OUTOFCONTEXT); + + WNDCLASSW wc = {0}; + wc.lpszClassName = L"NoSteamWebHelperTray"; + wc.hInstance = GetModuleHandleW(NULL); + wc.lpfnWndProc = TrayWindowProc; + RegisterClassW(&wc); + + CreateWindowExW(WS_EX_LEFT | WS_EX_LTRREADING, wc.lpszClassName, L"", WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL, wc.hInstance, NULL); + + MSG msg = {0}; + while (GetMessageW(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + if (g_hEventHook) + UnhookWinEvent(g_hEventHook); + + return EXIT_SUCCESS; } BOOL WINAPI DllMainCRTStartup(HINSTANCE hLibModule, DWORD dwReason, LPVOID lpReserved) { - if (dwReason == DLL_PROCESS_ATTACH) - { - DisableThreadLibraryCalls(hLibModule); - CloseHandle(CreateThread(NULL, 0, ThreadProc, (LPVOID)TRUE, 0, NULL)); - } - return TRUE; + if (dwReason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(hLibModule); + CloseHandle(CreateThread(NULL, 0, MainThreadProc, NULL, 0, NULL)); + } + return TRUE; } \ No newline at end of file