Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
3679108829
|
|||
77c3dabf79
|
|||
a6a12720ad
|
|||
231658ff2b
|
|||
6e99e11250
|
|||
8cd44da368
|
|||
ac590912bc
|
|||
1ba2faeb25
|
|||
bedd99a593
|
|||
e9f6b969b6
|
|||
5d0e6c4026
|
|||
418e53e7f7
|
|||
03d5e8dbc8
|
|||
e5a09024ff
|
|||
aeda375cba
|
|||
7af098ab26
|
|||
7461479cbb
|
46
.github/workflows/build-deploy.yml
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
name: Build and Deploy docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: dependencies
|
||||
run: sudo apt-get update && sudo apt-get install gcc-mingw-w64 make
|
||||
- name: make
|
||||
run: make
|
||||
- name: artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bridge
|
||||
path: build
|
||||
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Configure Git Credentials
|
||||
run: |
|
||||
git config user.name github-actions[bot]
|
||||
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.x
|
||||
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
key: mkdocs-material-${{ env.cache_id }}
|
||||
path: .cache
|
||||
restore-keys: |
|
||||
mkdocs-material-
|
||||
- run: pip install mkdocs-material
|
||||
- run: mkdocs gh-deploy --force
|
24
.github/workflows/c-cpp.yml
vendored
@ -1,24 +0,0 @@
|
||||
name: C/C++ CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: dependencies
|
||||
run: sudo apt-get update && sudo apt-get install gcc-mingw-w64 make
|
||||
- name: make
|
||||
run: make
|
||||
- name: artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bridge
|
||||
path: build
|
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
*.o
|
||||
*.exe
|
||||
*.res
|
||||
|
2
.vscode/c_cpp_properties.json
vendored
@ -8,7 +8,7 @@
|
||||
"defines": [],
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "gnu++17",
|
||||
"intelliSenseMode": "windows-gcc-x86"
|
||||
"intelliSenseMode": "windows-gcc-x64"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
|
7
.vscode/settings.json
vendored
@ -1,8 +1,3 @@
|
||||
{
|
||||
"C_Cpp.default.compilerPath": "/usr/bin/i686-w64-mingw32-gcc",
|
||||
"files.associations": {
|
||||
"*.su": "tsv",
|
||||
"windows.h": "c",
|
||||
"namedpipeapi.h": "c"
|
||||
}
|
||||
"C_Cpp.default.compilerPath": "/usr/bin/x86_64-w64-mingw32-gcc",
|
||||
}
|
2
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2023 EnderIce2
|
||||
Copyright (c) 2024 EnderIce2
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
11
Makefile
@ -2,23 +2,20 @@ C_SOURCES = $(shell find ./ -type f -name '*.c')
|
||||
C_OBJECTS = $(C_SOURCES:.c=.o)
|
||||
|
||||
CFLAGS = -std=c17 -Wno-int-conversion
|
||||
LFLAGS =
|
||||
LFLAGS = -lgdi32
|
||||
|
||||
# DBGFLAGS = -Wl,--export-all-symbols -g -O0 -ggdb3 -Wall
|
||||
|
||||
all: build
|
||||
|
||||
# This is valid only if this directory is a subdirectory of drive_c
|
||||
install: build
|
||||
cp build/bridge.exe ../bridge.exe
|
||||
|
||||
build: $(C_OBJECTS)
|
||||
$(info Linking)
|
||||
x86_64-w64-mingw32-gcc $(C_OBJECTS) $(LFLAGS) $(DBGFLAGS) -o build/bridge.exe
|
||||
x86_64-w64-mingw32-windres bridge.rc -O coff -o bridge.res
|
||||
x86_64-w64-mingw32-gcc $(C_OBJECTS) bridge.res $(LFLAGS) $(DBGFLAGS) -o build/bridge.exe
|
||||
|
||||
%.o: %.c
|
||||
$(info Compiling $<)
|
||||
x86_64-w64-mingw32-gcc $(CFLAGS) $(DBGFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(C_OBJECTS) build/bridge.exe
|
||||
rm -f $(C_OBJECTS) build/bridge.exe bridge.res
|
||||
|
107
README.md
@ -1,8 +1,8 @@
|
||||
# Discord RPC Bridge for Wine
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
Simple bridge that allows you to use Discord Rich Presence with Wine games/software.
|
||||
|
||||
@ -12,121 +12,54 @@ This bridge takes advantage of the Windows service implementation in Wine, elimi
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Discord RPC Bridge for Wine](#discord-rpc-bridge-for-wine)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Installation \& Usage](#installation--usage)
|
||||
- [Installing inside a prefix](#installing-inside-a-prefix)
|
||||
- [Wine (~/.wine)](#wine-wine)
|
||||
- [Lutris](#lutris)
|
||||
- [Steam](#steam)
|
||||
- [If you use Flatpak](#if-you-use-flatpak)
|
||||
- [Run without installing the service](#run-without-installing-the-service)
|
||||
- [About macOS](#about-macos)
|
||||
- [Compiling from source](#compiling-from-source)
|
||||
- [Command line arguments](#command-line-arguments)
|
||||
- [Debugging](#debugging)
|
||||
- [Demo](#demo)
|
||||
- [Credits](#credits)
|
||||
|
||||
## Installation & Usage
|
||||
|
||||
Installation will place `bridge.exe` to `C:\bridge.exe` (if it's not already there) and create a Windows service. This service will automatically start when the prefix is used.
|
||||
|
||||
If you prefer not to use the service for any reason, please refer to the [Run without installing the service](#run-without-installing-the-service) section.
|
||||
Installation will copy itself to `C:\windows\bridge.exe` and create a Windows service.
|
||||
Logs are stored in `C:\windows\logs\bridge.log`.
|
||||
|
||||
#### Installing inside a prefix
|
||||
|
||||
##### Wine (~/.wine)
|
||||
|
||||
- Open terminal in `build`
|
||||
- Run `$ wine cmd` and `C:\> install.bat`
|
||||
- 
|
||||
- To remove, run `C:\> remove.bat`
|
||||
- 
|
||||
- Note: Copying files are not required here.
|
||||
- Double click `bridge.exe` and click `Install`.
|
||||
- 
|
||||
- To remove, the same process can be followed, but click `Remove` instead.
|
||||
|
||||
##### Lutris
|
||||
|
||||
- Right click on the game and select `Browse files`
|
||||
- 
|
||||
- Copy contents of `build` to the game's prefix `drive_c`
|
||||
- 
|
||||
- To install open the console
|
||||
- 
|
||||
- And run `C:\> install.bat` (make sure you are in `C:\`!)
|
||||
- 
|
||||
- Click on a game and select `Run EXE inside Wine prefix`.
|
||||
- 
|
||||
- The same process can be followed as in Wine.
|
||||
|
||||
##### Steam
|
||||
|
||||
- Open [Protontricks](https://github.com/Matoking/protontricks) and select the game you want to install the bridge to
|
||||
- Select `Select the default wineprefix`
|
||||
- Select `Browse files` and copy contents of `build` to the game's prefix `drive_c`
|
||||
- Select `Run a Wine cmd shell` and run `C:\> install.bat`
|
||||
- If you are not in `C:\`, type `c:` and press enter
|
||||
- 
|
||||
- Right click on the game and select `Properties`.
|
||||
- Under `Set Launch Options`, add the following:
|
||||
- 
|
||||
- The `bridge.sh` script must be in the same directory as `bridge.exe`.
|
||||
|
||||
#### If you use Flatpak
|
||||
|
||||
- If you are running Steam, Lutris, etc. in a Flatpak, you will need to allow the bridge to access the `/run/user/1000/discord-ipc-0` file.
|
||||
- If you are running Steam, Lutris, etc in a Flatpak, you will need to allow the bridge to access the `/run/user/1000/discord-ipc-0` file.
|
||||
- ##### By using [Flatseal](https://flathub.org/apps/details/com.github.tchx84.Flatseal)
|
||||
- Add `xdg-run/discord-ipc-0` under `Filesystems` category
|
||||
- 
|
||||
- 
|
||||
- ##### By using the terminal
|
||||
- Per application
|
||||
- `flatpak override --filesystem=xdg-run/discord-ipc-0 <flatpak app name>`
|
||||
- Globally
|
||||
- `flatpak override --user --filesystem=xdg-run/discord-ipc-0`
|
||||
|
||||
## Run without installing the service
|
||||
## macOS
|
||||
|
||||
If you prefer not to use the service, you can manually run `bridge.exe` within the Wine prefix.
|
||||
This method is compatible with both Wine and Lutris.
|
||||
|
||||
In Lutris, you can achieve this by adding the path to `bridge.exe` in the `Executable` field under `Game options`. In `Arguments` field, be sure to include the _Windows_ path to the game's executable.
|
||||
Example:
|
||||
- Without bridge:
|
||||
- Executable `/mnt/games/lutris/league-of-legends/drive_c/Riot Games/League of Legends/LeagueClient.exe`
|
||||
- Arguments `--locale=en_US --launch-product=league_of_legends --launch-patchline=live`
|
||||
- With bridge:
|
||||
- Executable `/mnt/games/lutris/league-of-legends/drive_c/bridge.exe`
|
||||
- Arguments `"C:\Riot Games\League of Legends\LeagueClient.exe" --locale=en_US --launch-product=league_of_legends --launch-patchline=live`
|
||||
|
||||
In Wine, all you need to do is run `bridge.exe`.
|
||||
|
||||
- When running the program manually without providing any arguments, it will simply initiate the bridge and wait indefinitely until it's closed.
|
||||
|
||||
## About macOS
|
||||
|
||||
The bridge works similarly on macOS as it does on Linux, but it can't be registered as a service due to TMPDIR limitations. macOS users must manually run `bridge.exe` when needed.
|
||||
|
||||
To run `bridge.exe` on macOS, navigate to its directory in the terminal and execute `wine bridge.exe`. (or double click it in the Finder)
|
||||
On macOS, follow [these instructions](https://enderice2.github.io/rpc-bridge/installation.html#macos).
|
||||
|
||||
## Compiling from source
|
||||
|
||||
- Install the `wine`, `x86_64-w64-mingw32-gcc` and `make` packages.
|
||||
- Install the `wine`, `gcc-mingw-w64` and `make` packages.
|
||||
- Open a terminal in the directory that contains this file and run `make`.
|
||||
- The compiled executable will be located in `build/bridge.exe`.
|
||||
|
||||
## Command line arguments
|
||||
|
||||
- `--install` - Installs the service
|
||||
- `--remove` - Removes the service
|
||||
- `--service` - Reserved for the service
|
||||
- `[Target Executable]` - Starts the bridge and the game
|
||||
- Example: `bridge.exe "C:\Riot Games\League of Legends\LeagueClient.exe" --locale=en_US --launch-product=league_of_legends --launch-patchline=live`
|
||||
- Note: The game executable must be enclosed in quotes. The rest of the arguments are passed to the target executable.
|
||||
|
||||
## Debugging
|
||||
|
||||
The bridge will write the logs in `C:\bridge.log`.
|
||||
|
||||
## Demo
|
||||
|
||||

|
||||

|
||||
|
||||
## Credits
|
||||
|
||||
This project is inspired by [wine-discord-ipc-bridge](https://github.com/0e4ef622/wine-discord-ipc-bridge).
|
||||
|
89
bridge.c
@ -63,6 +63,8 @@ LPTSTR GetErrorMessage();
|
||||
extern BOOL RunningAsService;
|
||||
BOOL RetryNewConnection;
|
||||
BOOL IsLinux;
|
||||
HANDLE hOut = NULL;
|
||||
HANDLE hIn = NULL;
|
||||
|
||||
static force_inline int linux_syscall(int num,
|
||||
int arg1, int arg2, int arg3,
|
||||
@ -162,37 +164,50 @@ static inline int sys_connect(int s, caddr_t name, socklen_t namelen)
|
||||
return darwin_syscall(__darwin_connect, s, name, namelen, 0, 0, 0);
|
||||
}
|
||||
|
||||
void *environStr = NULL;
|
||||
char *native_getenv(const char *name)
|
||||
{
|
||||
static char lpBuffer[512];
|
||||
DWORD ret = GetEnvironmentVariable("BRIDGE_RPC_PATH", lpBuffer, sizeof(lpBuffer));
|
||||
if (ret != 0)
|
||||
return lpBuffer;
|
||||
|
||||
if (!IsLinux)
|
||||
{
|
||||
char *value = getenv(name);
|
||||
if (value == NULL)
|
||||
{
|
||||
print("Failed to get environment variable: %s\n", name);
|
||||
return NULL;
|
||||
|
||||
/* Use GetEnvironmentVariable as a last resort */
|
||||
DWORD ret = GetEnvironmentVariable(name, lpBuffer, sizeof(lpBuffer));
|
||||
if (ret == 0)
|
||||
{
|
||||
print("GetEnvironmentVariable(\"%s\", ...) failed: %d\n", name, ret);
|
||||
return NULL;
|
||||
}
|
||||
return lpBuffer;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/* I hope the 0x10000 is okay */
|
||||
void *environStr = sys_mmap(0x10000, 4096, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
|
||||
if (environStr == MAP_FAILED)
|
||||
/* I hope the 0x20000 is okay */
|
||||
if (environStr == NULL)
|
||||
{
|
||||
print("Failed to allocate environ string: %d\n", (intptr_t)environStr);
|
||||
return NULL;
|
||||
environStr = sys_mmap(0x20000, 4096, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);
|
||||
print("Allocated 4096 bytes at %#lx\n", environStr);
|
||||
}
|
||||
|
||||
if ((uintptr_t)environStr > 0x7effffff)
|
||||
print("Warning: environStr %p is above 2GB\n", environStr);
|
||||
print("Warning: environStr %#lx is above 2GB\n", environStr);
|
||||
|
||||
const char *linux_environ = "/proc/self/environ";
|
||||
memcpy(environStr, linux_environ, strlen(linux_environ) + 1);
|
||||
|
||||
int fd = sys_open(environStr, O_RDONLY, 0);
|
||||
|
||||
sys_munmap(environStr, 4096);
|
||||
// sys_munmap(environStr, 4096);
|
||||
if (fd < 0)
|
||||
{
|
||||
print("Failed to open /proc/self/environ: %d\n", fd);
|
||||
@ -304,6 +319,7 @@ void PipeBufferInThread(LPVOID lpParam)
|
||||
bridge_thread *bt = (bridge_thread *)lpParam;
|
||||
print("In thread started using fd %d and pipe %#x\n",
|
||||
bt->fd, bt->hPipe);
|
||||
int EOFCount = 0;
|
||||
while (TRUE)
|
||||
{
|
||||
char buffer[1024];
|
||||
@ -317,12 +333,22 @@ void PipeBufferInThread(LPVOID lpParam)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (EOFCount > 4)
|
||||
{
|
||||
print("EOF count exceeded\n");
|
||||
RetryNewConnection = TRUE;
|
||||
TerminateThread(hOut, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (unlikely(read == 0))
|
||||
{
|
||||
print("EOF\n");
|
||||
Sleep(1000);
|
||||
EOFCount++;
|
||||
continue;
|
||||
}
|
||||
EOFCount = 0;
|
||||
|
||||
print("Reading %d bytes from unix pipe: \"", read);
|
||||
for (int i = 0; i < read; i++)
|
||||
@ -440,17 +466,6 @@ void PipeBufferOutThread(LPVOID lpParam)
|
||||
|
||||
void CreateBridge()
|
||||
{
|
||||
static void(CDECL * wine_get_host_version)(const char **sysname, const char **release);
|
||||
HMODULE hntdll = GetModuleHandle("ntdll.dll");
|
||||
wine_get_host_version = (void *)GetProcAddress(hntdll, "wine_get_host_version");
|
||||
|
||||
assert(wine_get_host_version);
|
||||
const char *__sysname;
|
||||
const char *__release;
|
||||
wine_get_host_version(&__sysname, &__release);
|
||||
IsLinux = strcmp(__sysname, "Linux") == 0;
|
||||
print("Running on %s\n", __sysname);
|
||||
|
||||
LPCTSTR lpszPipename = TEXT("\\\\.\\pipe\\discord-ipc-0");
|
||||
|
||||
NewConnection:
|
||||
@ -461,8 +476,11 @@ NewConnection:
|
||||
print("Pipe already exists: %s\n",
|
||||
GetErrorMessage());
|
||||
if (!RunningAsService)
|
||||
{
|
||||
MessageBox(NULL, GetErrorMessage(),
|
||||
NULL, MB_OK | MB_ICONSTOP);
|
||||
"Pipe already exists",
|
||||
MB_OK | MB_ICONSTOP);
|
||||
}
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
@ -477,8 +495,11 @@ NewConnection:
|
||||
print("Failed to create pipe: %s\n",
|
||||
GetErrorMessage());
|
||||
if (!RunningAsService)
|
||||
{
|
||||
MessageBox(NULL, GetErrorMessage(),
|
||||
NULL, MB_OK | MB_ICONSTOP);
|
||||
"Failed to create pipe",
|
||||
MB_OK | MB_ICONSTOP);
|
||||
}
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
@ -523,21 +544,27 @@ NewConnection:
|
||||
|
||||
bridge_thread bt = {fd, hPipe};
|
||||
|
||||
HANDLE hIn = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE)PipeBufferInThread,
|
||||
(LPVOID)&bt,
|
||||
0, NULL);
|
||||
hIn = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE)PipeBufferInThread,
|
||||
(LPVOID)&bt,
|
||||
0, NULL);
|
||||
print("Created in thread %#lx\n", hIn);
|
||||
|
||||
HANDLE hOut = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE)PipeBufferOutThread,
|
||||
(LPVOID)&bt,
|
||||
0, NULL);
|
||||
hOut = CreateThread(NULL, 0,
|
||||
(LPTHREAD_START_ROUTINE)PipeBufferOutThread,
|
||||
(LPVOID)&bt,
|
||||
0, NULL);
|
||||
print("Created out thread %#lx\n", hOut);
|
||||
|
||||
if (hIn == NULL || hOut == NULL)
|
||||
{
|
||||
print("Failed to create threads: %s\n", GetErrorMessage());
|
||||
if (!RunningAsService)
|
||||
MessageBox(NULL, GetErrorMessage(), NULL, MB_OK | MB_ICONSTOP);
|
||||
{
|
||||
MessageBox(NULL, GetErrorMessage(),
|
||||
"Failed to create threads",
|
||||
MB_OK | MB_ICONSTOP);
|
||||
}
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
|
BIN
bridge.ico
Normal file
After Width: | Height: | Size: 16 KiB |
32
bridge.manifest
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
version="1.0.0.0"
|
||||
processorArchitecture="amd64"
|
||||
name="EnderIce2.rpc-bridge"
|
||||
type="win32"
|
||||
/>
|
||||
<description>Simple bridge that allows you to use Discord Rich Presence with Wine games/software.</description>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel
|
||||
level="asInvoker"
|
||||
uiAccess="false"
|
||||
/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
44
bridge.rc
Normal file
@ -0,0 +1,44 @@
|
||||
#include <windef.h>
|
||||
#include <winuser.h>
|
||||
#include <winresrc.h>
|
||||
|
||||
#define VER_VERSION 1,1,0,0
|
||||
#define VER_VERSION_STR "1.1\0"
|
||||
|
||||
#ifndef DEBUG
|
||||
#define VER_DEBUG 0
|
||||
#else
|
||||
#define VER_DEBUG VS_FF_DEBUG
|
||||
#endif
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION VER_VERSION
|
||||
PRODUCTVERSION VER_VERSION
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904E4"
|
||||
BEGIN
|
||||
VALUE "FileDescription", "Simple bridge that allows you to use Discord Rich Presence with Wine games/software."
|
||||
VALUE "FileVersion", VER_VERSION_STR
|
||||
VALUE "InternalName", "bridge"
|
||||
VALUE "LegalCopyright", "Copyright (c) 2024 EnderIce2"
|
||||
VALUE "OriginalFilename", "bridge.exe"
|
||||
VALUE "ProductName", "rpc-bridge"
|
||||
VALUE "ProductVersion", VER_VERSION_STR
|
||||
END
|
||||
END
|
||||
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1252
|
||||
END
|
||||
END
|
||||
|
||||
IDI_ICON_128 ICON "bridge.ico"
|
||||
|
||||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST bridge.manifest
|
32
build/bridge.sh
Executable file
@ -0,0 +1,32 @@
|
||||
#! /bin/sh
|
||||
|
||||
# This script is used to run Steam Play with the bridge.
|
||||
# Usage: /path/to/bridge.sh %command%
|
||||
# Original script: https://github.com/0e4ef622/wine-discord-ipc-bridge/blob/master/winediscordipcbridge-steam.sh
|
||||
# As requested by https://github.com/EnderIce2/rpc-bridge/issues/2
|
||||
|
||||
# Exporting BRIDGE_PATH to provide the bridge with its location.
|
||||
export BRIDGE_PATH="$(dirname "$0")/bridge.exe"
|
||||
|
||||
# The "--steam" option prevents the game from
|
||||
# hanging as "running" in Steam after it is closed.
|
||||
# This is done by creating a dummy service with
|
||||
# startup type SERVICE_DEMAND_START so this service
|
||||
# is only started when we use this script.
|
||||
BRIDGE_CMD="$BRIDGE_PATH --steam"
|
||||
|
||||
# Linux
|
||||
TEMP_PATH="$XDG_RUNTIME_DIR"
|
||||
# macOS but Steam Play is not supported on macOS https://github.com/ValveSoftware/Proton/issues/1344
|
||||
TEMP_PATH=${TEMP_PATH:-"$TMPDIR"}
|
||||
|
||||
VESSEL_PATH="$BRIDGE_PATH"
|
||||
IPC_PATHS="$TEMP_PATH /run/user/$UID $TEMP_PATH/snap.discord $TEMP_PATH/app/com.discordapp.Discord"
|
||||
for discord_ipc in $IPC_PATHS; do
|
||||
if [ -S "$discord_ipc"/discord-ipc-0 ]; then
|
||||
VESSEL_PATH="$BRIDGE_PATH:$(echo "$discord_ipc"/discord-ipc-0)"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
PROTON_REMOTE_DEBUG_CMD="$BRIDGE_CMD" PRESSURE_VESSEL_FILESYSTEMS_RW="$VESSEL_PATH:$PRESSURE_VESSEL_FILESYSTEMS_RW" "$@"
|
BIN
docs/assets/favicon.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
BIN
docs/assets/gui.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
docs/assets/lutris.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 160 KiB |
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 203 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
BIN
docs/assets/steam_script.png
Normal file
After Width: | Height: | Size: 28 KiB |
33
docs/index.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Discord RPC Bridge for Wine
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
Simple bridge that allows you to use Discord Rich Presence with Wine games/software on Linux/macOS.
|
||||
|
||||
[Download latest release](https://github.com/EnderIce2/rpc-bridge/releases/latest/download/bridge.zip "Recommended"){ .md-button .md-button--primary }
|
||||
[Download latest pre-release](https://github.com/EnderIce2/rpc-bridge/releases "Unstable builds with experimental features"){ .md-button }
|
||||
<!-- [Download latest build](https://github.com/EnderIce2/rpc-bridge/actions "Builds from the latest commits, here be dragons!"){ .md-button } -->
|
||||
|
||||
Works by running a small program in the background that creates a [named pipe](https://learn.microsoft.com/en-us/windows/win32/ipc/named-pipes) `\\.\pipe\discord-ipc-0` inside the prefix and forwards all data to the pipe `/run/user/1000/discord-ipc-0`.
|
||||
|
||||
This bridge takes advantage of the Windows service implementation in Wine, eliminating the need to run it manually.
|
||||
|
||||
These docs are for the latest stable release.
|
||||
For v1.0, see [the original README](https://github.com/EnderIce2/rpc-bridge/blob/v1.0/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
{ width="600" }
|
||||
|
||||
{ width="600" }
|
||||
|
||||
## Credits
|
||||
|
||||
This project is inspired by [wine-discord-ipc-bridge](https://github.com/0e4ef622/wine-discord-ipc-bridge).
|
||||
|
||||
---
|
109
docs/installation.md
Normal file
@ -0,0 +1,109 @@
|
||||
# Installation
|
||||
|
||||
Installation will copy itself to `C:\windows\bridge.exe` and create a Windows service.
|
||||
Logs are stored in `C:\windows\logs\bridge.log`.
|
||||
|
||||
## Installing inside a prefix
|
||||
|
||||
### Wine (~/.wine)
|
||||
|
||||
- Double click `bridge.exe` and click `Install`.
|
||||
- 
|
||||
- To remove, the same process can be followed, but click `Remove` instead.
|
||||
|
||||
### Lutris
|
||||
|
||||
- Click on a game and select `Run EXE inside Wine prefix`.
|
||||
- 
|
||||
- The same process can be followed as in Wine.
|
||||
|
||||
### Steam
|
||||
|
||||
There are two ways to install the bridge on Steam.
|
||||
|
||||
#### Using bridge.sh[^1]
|
||||
|
||||
This method is recommended because it's easier to manage.
|
||||
|
||||
- Right click on the game and select `Properties`.
|
||||
- Under `Set Launch Options`, add the following:
|
||||
- 
|
||||
<sup><sub>Of course, you need to replace `/path/to/bridge.sh` with the actual path to the script.</sub></sup>
|
||||
|
||||
!!! info "Note"
|
||||
|
||||
`bridge.sh` must be in the same directory as `bridge.exe`.
|
||||
|
||||
#### Using Protontricks
|
||||
|
||||
- Open [Protontricks](https://github.com/Matoking/protontricks) and select the game you want to install the bridge to.
|
||||
- Select `Select the default wineprefix`
|
||||
- Select `Browse files` and copy contents of `build` to the game's prefix `drive_c`
|
||||
- Select `Run a Wine cmd shell` and run `C:\> install.bat`
|
||||
- If you are not in `C:\`, type `c:` and press enter
|
||||
|
||||
, use it instead!")
|
||||
|
||||
!!! warning "If you use Flatpak"
|
||||
|
||||
If you are running Steam, Lutris, etc in a Flatpak, you will need to allow the bridge to access the `/run/user/1000/discord-ipc-0` file.
|
||||
|
||||
You can do this by using [Flatseal](https://flathub.org/apps/details/com.github.tchx84.Flatseal) or the terminal.
|
||||
|
||||
=== "Flatseal"
|
||||
|
||||
Add `xdg-run/discord-ipc-0` under `Filesystems` category
|
||||

|
||||
|
||||
=== "Terminal"
|
||||
|
||||
- Per application
|
||||
- `flatpak override --filesystem=xdg-run/discord-ipc-0 <flatpak app name>`
|
||||
- Globally
|
||||
- `flatpak override --user --filesystem=xdg-run/discord-ipc-0`
|
||||
|
||||
## macOS
|
||||
|
||||
If using the default Wine prefix (`~/.wine`), you can follow the same steps as in Wine.
|
||||
|
||||
Registering as a service [is not supported](https://github.com/EnderIce2/rpc-bridge/issues/1#issuecomment-2103423242 "Bridge can't get $TMPDIR unless is set with --rpc. See note below").
|
||||
|
||||
!!! info "Note"
|
||||
|
||||
Since macOS doesn't have `/proc/self/environ` and `$TMPDIR` is neither always set nor the same after reboots, so you will need to [add `--rpc "$TMPDIR"` to the `bridge.exe` arguments](https://github.com/EnderIce2/rpc-bridge/issues/1#issuecomment-2104797235).
|
||||
If you don't encounter any issues, you can ignore this step.
|
||||
|
||||
## Run without installing the service
|
||||
|
||||
If you prefer not to use the service, you can manually run `bridge.exe` within the Wine prefix.
|
||||
This method is compatible with both Wine and Lutris.
|
||||
|
||||
In Lutris, you can achieve this by adding the path to `bridge.exe` in the `Executable` field under `Game options`. In `Arguments` field, be sure to include the _Windows_ path to the game's executable.
|
||||
|
||||
=== "Without bridge"
|
||||
|
||||
```
|
||||
Executable
|
||||
/mnt/games/lutris/league-of-legends/drive_c/Riot Games/League of Legends/LeagueClient.exe
|
||||
Arguments
|
||||
--locale=en_US --launch-product=league_of_legends --launch-patchline=live
|
||||
```
|
||||
|
||||
=== "With bridge"
|
||||
|
||||
```
|
||||
Executable
|
||||
/mnt/games/lutris/league-of-legends/drive_c/bridge.exe
|
||||
Arguments
|
||||
"C:\Riot Games\League of Legends\LeagueClient.exe" --locale=en_US --launch-product=league_of_legends --launch-patchline=live
|
||||
```
|
||||
|
||||
In Wine, all you need to do is run `bridge.exe` and select `Start`.
|
||||
|
||||
## Compiling from source
|
||||
|
||||
- Install the `wine`, `gcc-mingw-w64` and `make` packages.
|
||||
- Open a terminal in the directory that contains this file and run `make`.
|
||||
- The compiled executable will be located in `build/bridge.exe`.
|
||||
|
||||
[^1]: As requested [here](https://github.com/EnderIce2/rpc-bridge/issues/2).
|
32
docs/usage.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Usage
|
||||
|
||||
## GUI
|
||||
|
||||
- When running the program manually without providing any arguments it will show a GUI.
|
||||

|
||||
- `Start` will start the service without installing itself.
|
||||
- `Install` will install the service.
|
||||
- `Remove` will uninstall the service.
|
||||
|
||||
## Commands
|
||||
|
||||
- `--help` Show help message
|
||||
- This will show the help message
|
||||
|
||||
- `--install` Install the service
|
||||
- This will copy the binary to `C:\windows\bridge.exe` and register it as a service
|
||||
|
||||
- `--uninstall` Uninstall the service
|
||||
- This will remove the service and delete `C:\windows\bridge.exe`
|
||||
|
||||
- `--steam` Reserved for Steam
|
||||
- This will start the service and exit (used with `bridge.sh`)
|
||||
|
||||
- `--no-service` Do not run as service
|
||||
- (only for `--steam`)
|
||||
|
||||
- `--service` Reserved for service
|
||||
- Reserved
|
||||
|
||||
- `--rpc <dir>` Set RPC_PATH environment variable
|
||||
- This is used to specify the directory where `discord-ipc-0` is located
|
9
game.c
@ -55,7 +55,8 @@ void LaunchGame(int argc, char **argv)
|
||||
if (!GetBinaryType(argv[1], &dwBinaryType))
|
||||
{
|
||||
MessageBox(NULL, GetErrorMessage(),
|
||||
NULL, MB_OK | MB_ICONSTOP);
|
||||
"GetBinaryType",
|
||||
MB_OK | MB_ICONSTOP);
|
||||
ExitProcess(1);
|
||||
}
|
||||
print("Executable type: %d\n", dwBinaryType);
|
||||
@ -104,7 +105,8 @@ void LaunchGame(int argc, char **argv)
|
||||
if (!CreateProcess(gamePath, gameArgs, NULL, NULL, FALSE,
|
||||
0, NULL, NULL, &game_si, &game_pi))
|
||||
{
|
||||
MessageBox(NULL, GetErrorMessage(), NULL, MB_OK | MB_ICONSTOP);
|
||||
MessageBox(NULL, GetErrorMessage(),
|
||||
"CreateProcess", MB_OK | MB_ICONSTOP);
|
||||
ExitProcess(1);
|
||||
}
|
||||
LocalFree(gameArgs);
|
||||
@ -136,7 +138,8 @@ void LaunchGame(int argc, char **argv)
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox(NULL, GetErrorMessage(), NULL, MB_OK | MB_ICONSTOP);
|
||||
MessageBox(NULL, GetErrorMessage(),
|
||||
"CreateToolhelp32Snapshot", MB_OK | MB_ICONSTOP);
|
||||
ExitProcess(0);
|
||||
}
|
||||
|
||||
|
302
gui.c
Normal file
@ -0,0 +1,302 @@
|
||||
#include <windowsx.h>
|
||||
#include <windows.h>
|
||||
#include <winuser.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* The entire code could be better written, but at least it works.
|
||||
*
|
||||
* This will make installation and removal of the bridge WAY easier.
|
||||
*/
|
||||
|
||||
LPTSTR GetErrorMessage();
|
||||
void print(char const *fmt, ...);
|
||||
void InstallService(int ServiceStartType, LPCSTR Path);
|
||||
void RemoveService();
|
||||
void CreateBridge();
|
||||
extern BOOL IsLinux;
|
||||
|
||||
HWND hwnd = NULL;
|
||||
HANDLE hBridge = NULL;
|
||||
extern HANDLE hOut;
|
||||
extern HANDLE hIn;
|
||||
|
||||
BOOL IsAlreadyRunning = FALSE;
|
||||
VOID HandleStartButton(BOOL Silent)
|
||||
{
|
||||
if (IsAlreadyRunning)
|
||||
{
|
||||
HWND item = GetDlgItem(hwnd, 4);
|
||||
SetWindowText(item, "Do you want to start, install or remove the bridge?");
|
||||
RedrawWindow(item, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
|
||||
item = GetDlgItem(hwnd, /* Start Button */ 1);
|
||||
Button_SetText(item, "Start");
|
||||
EnableWindow(item, FALSE);
|
||||
RedrawWindow(item, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);
|
||||
|
||||
print("Killing %#x, %#lx and waiting for %#lx\n", hIn, hOut, hBridge);
|
||||
if (hIn != NULL)
|
||||
TerminateThread(hIn, 0);
|
||||
if (hOut != NULL)
|
||||
TerminateThread(hOut, 0);
|
||||
WaitForSingleObject(hBridge, INFINITE);
|
||||
|
||||
EnableWindow(item, TRUE);
|
||||
IsAlreadyRunning = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsLinux)
|
||||
{
|
||||
hBridge = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CreateBridge,
|
||||
NULL, 0, NULL);
|
||||
|
||||
HWND item = GetDlgItem(hwnd, /* Start Button */ 1);
|
||||
Button_SetText(item, "Stop");
|
||||
item = GetDlgItem(hwnd, 4);
|
||||
SetWindowText(item, "Bridge is running...");
|
||||
IsAlreadyRunning = TRUE;
|
||||
ShowWindow(hwnd, SW_MINIMIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
||||
if (hSCManager == NULL)
|
||||
{
|
||||
print("OpenSCManager failed: %s\n", GetErrorMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
SC_HANDLE schService = OpenService(hSCManager, "rpc-bridge",
|
||||
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_START);
|
||||
if (schService == NULL)
|
||||
{
|
||||
// print("OpenService failed: %s\n", GetErrorMessage());
|
||||
|
||||
/* Service doesn't exist; running without any service */
|
||||
|
||||
hBridge = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CreateBridge,
|
||||
NULL, 0, NULL);
|
||||
|
||||
HWND item = GetDlgItem(hwnd, /* Start Button */ 1);
|
||||
Button_SetText(item, "Stop");
|
||||
item = GetDlgItem(hwnd, 4);
|
||||
SetWindowText(item, "Bridge is running...");
|
||||
IsAlreadyRunning = TRUE;
|
||||
ShowWindow(hwnd, SW_MINIMIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD dwBytesNeeded;
|
||||
QueryServiceConfig(schService, NULL, 0, &dwBytesNeeded);
|
||||
LPQUERY_SERVICE_CONFIG lpqsc = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LPTR, dwBytesNeeded);
|
||||
if (lpqsc == NULL)
|
||||
{
|
||||
print("LocalAlloc failed: %s\n", GetErrorMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!QueryServiceConfig(schService, lpqsc, dwBytesNeeded, &dwBytesNeeded))
|
||||
{
|
||||
print("QueryServiceConfig failed: %s\n", GetErrorMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
if (StartService(schService, 0, NULL) == FALSE)
|
||||
{
|
||||
if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING)
|
||||
return;
|
||||
print("StartService failed: %s\n", GetErrorMessage());
|
||||
}
|
||||
|
||||
LocalFree(lpqsc);
|
||||
CloseServiceHandle(schService);
|
||||
CloseServiceHandle(hSCManager);
|
||||
if (Silent == FALSE)
|
||||
MessageBox(NULL, "Bridge service started successfully", "Info", MB_OK);
|
||||
}
|
||||
|
||||
VOID HandleInstallButton()
|
||||
{
|
||||
char filename[MAX_PATH];
|
||||
GetModuleFileName(NULL, filename, MAX_PATH);
|
||||
CopyFile(filename, "C:\\windows\\bridge.exe", FALSE);
|
||||
InstallService(SERVICE_AUTO_START, "C:\\windows\\bridge.exe --service");
|
||||
MessageBox(NULL, "Bridge installed successfully", "Info", MB_OK);
|
||||
HandleStartButton(TRUE);
|
||||
ExitProcess(0);
|
||||
}
|
||||
|
||||
VOID HandleRemoveButton()
|
||||
{
|
||||
RemoveService();
|
||||
MessageBox(NULL, "Bridge removed successfully", "Info", MB_OK);
|
||||
ExitProcess(0);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
case WM_COMMAND:
|
||||
{
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case 1:
|
||||
HandleStartButton(FALSE);
|
||||
break;
|
||||
case 2:
|
||||
HandleInstallButton();
|
||||
break;
|
||||
case 3:
|
||||
HandleRemoveButton();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_CLOSE:
|
||||
DestroyWindow(hwnd);
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
ExitProcess(0);
|
||||
break;
|
||||
case WM_CTLCOLORSTATIC:
|
||||
{
|
||||
HDC hdcStatic = (HDC)wParam;
|
||||
SetBkMode(hdcStatic, TRANSPARENT);
|
||||
return (INT_PTR)(HBRUSH)GetStockObject(NULL_BRUSH);
|
||||
}
|
||||
default:
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
VOID SetButtonStyles(INT *btnStartStyle, INT *btnRemoveStyle, INT *btnInstallStyle)
|
||||
{
|
||||
*btnStartStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;
|
||||
*btnRemoveStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;
|
||||
*btnInstallStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;
|
||||
|
||||
if (!IsLinux)
|
||||
{
|
||||
*btnInstallStyle |= WS_DISABLED;
|
||||
*btnRemoveStyle |= WS_DISABLED;
|
||||
return;
|
||||
}
|
||||
|
||||
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
||||
SC_HANDLE schService = OpenService(hSCManager, "rpc-bridge", SERVICE_START | SERVICE_QUERY_STATUS);
|
||||
|
||||
if (schService != NULL)
|
||||
{
|
||||
*btnInstallStyle |= WS_DISABLED;
|
||||
|
||||
SERVICE_STATUS_PROCESS ssStatus;
|
||||
DWORD dwBytesNeeded;
|
||||
assert(QueryServiceStatusEx(schService, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssStatus,
|
||||
sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded));
|
||||
|
||||
if (ssStatus.dwCurrentState == SERVICE_RUNNING ||
|
||||
ssStatus.dwCurrentState == SERVICE_START_PENDING)
|
||||
*btnStartStyle |= WS_DISABLED;
|
||||
}
|
||||
else
|
||||
*btnRemoveStyle |= WS_DISABLED;
|
||||
|
||||
CloseServiceHandle(schService);
|
||||
CloseServiceHandle(hSCManager);
|
||||
}
|
||||
|
||||
int WINAPI __WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
||||
LPSTR lpCmdLine, int nCmdShow)
|
||||
{
|
||||
INT btnStartStyle, btnRemoveStyle, btnInstallStyle;
|
||||
SetButtonStyles(&btnStartStyle, &btnRemoveStyle, &btnInstallStyle);
|
||||
|
||||
const char szClassName[] = "BridgeWindowClass";
|
||||
|
||||
WNDCLASSEX wc;
|
||||
wc.cbSize = sizeof(WNDCLASSEX);
|
||||
wc.style = 0;
|
||||
wc.lpfnWndProc = WndProc;
|
||||
wc.cbClsExtra = 0;
|
||||
wc.cbWndExtra = 0;
|
||||
wc.hInstance = hInstance;
|
||||
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
|
||||
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
||||
wc.lpszMenuName = NULL;
|
||||
wc.lpszClassName = szClassName;
|
||||
wc.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
|
||||
|
||||
assert(RegisterClassEx(&wc));
|
||||
|
||||
hwnd = CreateWindowEx(WS_EX_WINDOWEDGE,
|
||||
szClassName,
|
||||
"Discord RPC Bridge",
|
||||
WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME,
|
||||
(GetSystemMetrics(SM_CXSCREEN) - 400) / 2,
|
||||
(GetSystemMetrics(SM_CYSCREEN) - 150) / 2,
|
||||
400, 150,
|
||||
NULL, NULL, hInstance, NULL);
|
||||
|
||||
HICON hIcon = LoadIcon(hInstance, "IDI_ICON_128");
|
||||
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
|
||||
|
||||
HWND hLbl4 = CreateWindowEx(WS_EX_TRANSPARENT,
|
||||
"STATIC", "Do you want to start, install or remove the bridge?",
|
||||
WS_CHILD | WS_VISIBLE | SS_CENTER,
|
||||
0, 15, 400, 25,
|
||||
hwnd, (HMENU)4, hInstance, NULL);
|
||||
|
||||
HWND hbtn1 = CreateWindow("BUTTON", "Start",
|
||||
btnStartStyle,
|
||||
40, 60, 100, 30,
|
||||
hwnd, (HMENU)1, hInstance, NULL);
|
||||
|
||||
HWND hbtn2 = CreateWindow("BUTTON", "Install",
|
||||
btnInstallStyle,
|
||||
150, 60, 100, 30,
|
||||
hwnd, (HMENU)2, hInstance, NULL);
|
||||
|
||||
HWND hbtn3 = CreateWindow("BUTTON", "Remove",
|
||||
btnRemoveStyle,
|
||||
260, 60, 100, 30,
|
||||
hwnd, (HMENU)3, hInstance, NULL);
|
||||
|
||||
HDC hDC = GetDC(hwnd);
|
||||
int nHeight = -MulDiv(11, GetDeviceCaps(hDC, LOGPIXELSY), 72);
|
||||
|
||||
HFONT hFont = CreateFont(nHeight, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET,
|
||||
OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
|
||||
DEFAULT_PITCH | FF_DONTCARE, TEXT("Segoe UI"));
|
||||
ReleaseDC(hwnd, hDC);
|
||||
|
||||
SendMessage(hwnd, WM_SETFONT, hFont, TRUE);
|
||||
SendMessage(hLbl4, WM_SETFONT, hFont, TRUE);
|
||||
SendMessage(hbtn1, WM_SETFONT, hFont, TRUE);
|
||||
SendMessage(hbtn2, WM_SETFONT, hFont, TRUE);
|
||||
SendMessage(hbtn3, WM_SETFONT, hFont, TRUE);
|
||||
|
||||
ShowWindow(hwnd, nCmdShow);
|
||||
UpdateWindow(hwnd);
|
||||
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, NULL, 0, 0) > 0)
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
return msg.wParam;
|
||||
}
|
||||
|
||||
void CreateGUI()
|
||||
{
|
||||
ShowWindow(GetConsoleWindow(), SW_MINIMIZE);
|
||||
ExitProcess(__WinMain(GetModuleHandle(NULL), NULL, GetCommandLine(), SW_SHOWNORMAL));
|
||||
}
|
Before Width: | Height: | Size: 321 KiB |
Before Width: | Height: | Size: 264 KiB |
Before Width: | Height: | Size: 609 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 541 KiB |
Before Width: | Height: | Size: 193 KiB |
265
main.c
@ -6,11 +6,14 @@
|
||||
FILE *g_logFile = NULL;
|
||||
BOOL RunningAsService = FALSE;
|
||||
|
||||
void CreateGUI();
|
||||
void CreateBridge();
|
||||
void LaunchGame(int argc, char **argv);
|
||||
void ServiceMain(int argc, char *argv[]);
|
||||
void InstallService();
|
||||
void InstallService(int ServiceStartType, LPCSTR Path);
|
||||
char *native_getenv(const char *name);
|
||||
void RemoveService();
|
||||
extern BOOL IsLinux;
|
||||
|
||||
LPTSTR GetErrorMessage()
|
||||
{
|
||||
@ -45,7 +48,12 @@ void DetectWine()
|
||||
{
|
||||
HMODULE hNTdll = GetModuleHandle("ntdll.dll");
|
||||
if (!hNTdll)
|
||||
{
|
||||
MessageBox(NULL, "Failed to load ntdll.dll",
|
||||
GetErrorMessage(), MB_OK | MB_ICONERROR);
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
if (!GetProcAddress(hNTdll, "wine_get_version"))
|
||||
{
|
||||
MessageBox(NULL, "This program is only intended to run under Wine.",
|
||||
@ -69,6 +77,9 @@ void DetectWine()
|
||||
else if (result == IDNO)
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
IsLinux = strcmp(__sysname, "Linux") == 0;
|
||||
printf("Running on %s\n", __sysname);
|
||||
}
|
||||
|
||||
void print(char const *fmt, ...)
|
||||
@ -82,10 +93,219 @@ void print(char const *fmt, ...)
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void HandleArguments(int argc, char *argv[])
|
||||
{
|
||||
if (strcmp(argv[1], "--service") == 0)
|
||||
{
|
||||
RunningAsService = TRUE;
|
||||
print("Running as service\n");
|
||||
|
||||
SERVICE_TABLE_ENTRY ServiceTable[] =
|
||||
{
|
||||
{"rpc-bridge", (LPSERVICE_MAIN_FUNCTION)ServiceMain},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
|
||||
{
|
||||
print("Service failed to start\n");
|
||||
ExitProcess(1);
|
||||
}
|
||||
}
|
||||
else if (strcmp(argv[1], "--steam") == 0)
|
||||
{
|
||||
/* All this mess just so when you close the game,
|
||||
it automatically closes the bridge and Steam
|
||||
will not say that the game is still running. */
|
||||
|
||||
print("Running as Steam\n");
|
||||
if (IsLinux == FALSE)
|
||||
CreateBridge();
|
||||
|
||||
if (argc > 2)
|
||||
{
|
||||
if (strcmp(argv[2], "--no-service") == 0)
|
||||
CreateBridge();
|
||||
}
|
||||
|
||||
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
||||
if (hSCManager == NULL)
|
||||
{
|
||||
print("(Steam) OpenSCManager: %s\n", GetErrorMessage());
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
SC_HANDLE schService = OpenService(hSCManager, "rpc-bridge",
|
||||
SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_START);
|
||||
if (schService == NULL)
|
||||
{
|
||||
if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
|
||||
{
|
||||
print("(Steam) OpenService: %s\n", GetErrorMessage());
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
print("(Steam) Service does not exist, registering...\n");
|
||||
|
||||
WCHAR *(CDECL * wine_get_dos_file_name)(LPCSTR str) =
|
||||
(void *)GetProcAddress(GetModuleHandleA("KERNEL32"),
|
||||
"wine_get_dos_file_name");
|
||||
|
||||
char *unixPath = native_getenv("BRIDGE_PATH");
|
||||
if (unixPath == NULL)
|
||||
{
|
||||
print("(Steam) BRIDGE_PATH not set\n");
|
||||
ExitProcess(1);
|
||||
}
|
||||
WCHAR *dosPath = wine_get_dos_file_name(unixPath);
|
||||
LPSTR asciiPath = (LPSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH);
|
||||
WideCharToMultiByte(CP_ACP, 0, dosPath, -1, asciiPath, MAX_PATH, NULL, NULL);
|
||||
|
||||
strcat_s(asciiPath, MAX_PATH, " --service");
|
||||
print("(Steam) Binary path: %s\n", asciiPath);
|
||||
|
||||
InstallService(SERVICE_DEMAND_START, asciiPath);
|
||||
HeapFree(GetProcessHeap(), 0, asciiPath);
|
||||
|
||||
/* Create handle for StartService below */
|
||||
print("(Steam) Service registered, opening handle...\n");
|
||||
/* FIXME: For some reason here it freezes??? */
|
||||
schService = OpenService(hSCManager, "rpc-bridge", SERVICE_START);
|
||||
if (schService == NULL)
|
||||
{
|
||||
print("(Steam) Cannot open service after creation: %s\n", GetErrorMessage());
|
||||
ExitProcess(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD dwBytesNeeded;
|
||||
QueryServiceConfig(schService, NULL, 0, &dwBytesNeeded);
|
||||
LPQUERY_SERVICE_CONFIG lpqsc = (LPQUERY_SERVICE_CONFIG)LocalAlloc(LPTR, dwBytesNeeded);
|
||||
if (lpqsc == NULL)
|
||||
{
|
||||
print("(Steam) LocalAlloc: %s\n", GetErrorMessage());
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
if (!QueryServiceConfig(schService, lpqsc, dwBytesNeeded, &dwBytesNeeded))
|
||||
{
|
||||
print("(Steam) QueryServiceConfig: %s\n", GetErrorMessage());
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
WCHAR *(CDECL * wine_get_dos_file_name)(LPCSTR str) =
|
||||
(void *)GetProcAddress(GetModuleHandleA("KERNEL32"),
|
||||
"wine_get_dos_file_name");
|
||||
|
||||
char *unixPath = native_getenv("BRIDGE_PATH");
|
||||
if (unixPath == NULL)
|
||||
{
|
||||
print("(Steam) BRIDGE_PATH not set\n");
|
||||
ExitProcess(1);
|
||||
}
|
||||
WCHAR *dosPath = wine_get_dos_file_name(unixPath);
|
||||
LPSTR asciiPath = (LPSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH);
|
||||
WideCharToMultiByte(CP_ACP, 0, dosPath, -1, asciiPath, MAX_PATH, NULL, NULL);
|
||||
|
||||
strcat_s(asciiPath, MAX_PATH, " --service");
|
||||
print("(Steam) Binary path: %s\n", asciiPath);
|
||||
|
||||
if (strcmp(lpqsc->lpBinaryPathName, asciiPath) != 0)
|
||||
{
|
||||
print("(Steam) Service binary path is not correct, updating...\n");
|
||||
ChangeServiceConfig(schService, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
|
||||
asciiPath, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
else
|
||||
print("(Steam) Service binary path is correct\n");
|
||||
HeapFree(GetProcessHeap(), 0, asciiPath);
|
||||
}
|
||||
|
||||
print("(Steam) Starting service...\n");
|
||||
if (StartService(schService, 0, NULL) == FALSE)
|
||||
{
|
||||
if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING)
|
||||
{
|
||||
print("(Steam) Service is already running\n");
|
||||
CloseServiceHandle(schService);
|
||||
CloseServiceHandle(hSCManager);
|
||||
ExitProcess(0);
|
||||
}
|
||||
|
||||
print("StartService: %s\n", GetErrorMessage());
|
||||
MessageBox(NULL, GetErrorMessage(),
|
||||
"StartService",
|
||||
MB_OK | MB_ICONSTOP);
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
print("(Steam) Service started successfully, exiting...\n");
|
||||
CloseServiceHandle(schService);
|
||||
CloseServiceHandle(hSCManager);
|
||||
ExitProcess(0);
|
||||
}
|
||||
else if (strcmp(argv[1], "--install") == 0)
|
||||
{
|
||||
char filename[MAX_PATH];
|
||||
GetModuleFileName(NULL, filename, MAX_PATH);
|
||||
CopyFile(filename, "C:\\windows\\bridge.exe", FALSE);
|
||||
|
||||
InstallService(SERVICE_AUTO_START, "C:\\windows\\bridge.exe --service");
|
||||
ExitProcess(0);
|
||||
}
|
||||
else if (strcmp(argv[1], "--uninstall") == 0)
|
||||
{
|
||||
RemoveService();
|
||||
ExitProcess(0);
|
||||
}
|
||||
else if (strcmp(argv[1], "--rpc") == 0)
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
print("No directory provided\n");
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
SetEnvironmentVariable("BRIDGE_RPC_PATH", argv[2]);
|
||||
print("BRIDGE_RPC_PATH has been set to \"%s\"\n", argv[2]);
|
||||
CreateBridge();
|
||||
ExitProcess(0);
|
||||
}
|
||||
else if (strcmp(argv[1], "--help") == 0)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf(" %s [args]\n\n", argv[0]);
|
||||
|
||||
printf("Arguments:\n");
|
||||
printf(" --help Show this help\n\n");
|
||||
|
||||
printf(" --install Install service\n");
|
||||
printf(" This will copy the binary to C:\\windows\\bridge.exe and register it as a service\n\n");
|
||||
|
||||
printf(" --uninstall Uninstall service\n");
|
||||
printf(" This will remove the service and delete C:\\windows\\bridge.exe\n\n");
|
||||
|
||||
printf(" --steam Reserved for Steam\n");
|
||||
printf(" This will start the service and exit (used with bridge.sh)\n\n");
|
||||
|
||||
printf(" --no-service Do not run as service\n");
|
||||
printf(" (only for --steam)\n\n");
|
||||
|
||||
printf(" --service Reserved for service\n\n");
|
||||
|
||||
printf(" --rpc <dir> Set RPC_PATH environment variable\n");
|
||||
printf(" This is used to specify the directory where 'discord-ipc-0' is located\n\n");
|
||||
|
||||
printf("Note: If no arguments are provided, the GUI will be shown instead\n");
|
||||
ExitProcess(0);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
DetectWine();
|
||||
char *logFilePath = "C:\\bridge.log";
|
||||
char *logFilePath = "C:\\windows\\logs\\bridge.log";
|
||||
g_logFile = fopen(logFilePath, "w");
|
||||
if (g_logFile == NULL)
|
||||
{
|
||||
@ -95,39 +315,9 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
if (strcmp(argv[1], "--service") == 0)
|
||||
{
|
||||
RunningAsService = TRUE;
|
||||
print("Running as service\n");
|
||||
|
||||
SERVICE_TABLE_ENTRY ServiceTable[] =
|
||||
{
|
||||
{"rpc-bridge", (LPSERVICE_MAIN_FUNCTION)ServiceMain},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
|
||||
{
|
||||
print("Service failed to start\n");
|
||||
return GetLastError();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(argv[1], "--install") == 0)
|
||||
InstallService();
|
||||
else if (strcmp(argv[1], "--uninstall") == 0)
|
||||
RemoveService();
|
||||
else if (strcmp(argv[1], "--help") == 0)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf(" %s [args]\n", argv[0]);
|
||||
printf(" --install - Install service\n");
|
||||
printf(" --uninstall - Uninstall service\n");
|
||||
printf(" --help - Show this help\n");
|
||||
ExitProcess(0);
|
||||
}
|
||||
}
|
||||
HandleArguments(argc, argv);
|
||||
else
|
||||
CreateGUI();
|
||||
|
||||
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CreateBridge,
|
||||
NULL, 0, NULL);
|
||||
@ -137,3 +327,8 @@ int main(int argc, char *argv[])
|
||||
fclose(g_logFile);
|
||||
ExitProcess(0);
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
||||
{
|
||||
return main(__argc, __argv);
|
||||
}
|
||||
|
76
mkdocs.yml
Normal file
@ -0,0 +1,76 @@
|
||||
site_name: rpc-bridge
|
||||
repo_url: https://github.com/EnderIce2/rpc-bridge
|
||||
repo_name: EnderIce2/rpc-bridge
|
||||
theme:
|
||||
name: material
|
||||
features:
|
||||
- content.code.copy
|
||||
- content.tabs.link
|
||||
- navigation.tabs
|
||||
- navigation.top
|
||||
- navigation.footer
|
||||
- toc.integrate
|
||||
- content.tooltips
|
||||
palette:
|
||||
- media: "(prefers-color-scheme)"
|
||||
toggle:
|
||||
icon: material/weather-sunny
|
||||
name: Switch to light mode
|
||||
- media: "(prefers-color-scheme: dark)"
|
||||
scheme: slate
|
||||
primary: black
|
||||
accent: indigo
|
||||
toggle:
|
||||
icon: material/brightness-4
|
||||
name: Switch to system preference
|
||||
- media: "(prefers-color-scheme: light)"
|
||||
scheme: default
|
||||
primary: indigo
|
||||
accent: indigo
|
||||
toggle:
|
||||
icon: material/brightness-7
|
||||
name: Switch to dark mode
|
||||
font:
|
||||
text: Roboto
|
||||
code: Roboto Mono
|
||||
favicon: assets/favicon.png
|
||||
logo: assets/favicon.png
|
||||
icon:
|
||||
logo: logo
|
||||
admonition:
|
||||
note: octicons/tag-16
|
||||
abstract: octicons/checklist-16
|
||||
info: octicons/info-16
|
||||
tip: octicons/squirrel-16
|
||||
success: octicons/check-16
|
||||
question: octicons/question-16
|
||||
warning: octicons/alert-16
|
||||
failure: octicons/x-circle-16
|
||||
danger: octicons/zap-16
|
||||
bug: octicons/bug-16
|
||||
example: octicons/beaker-16
|
||||
quote: octicons/quote-16
|
||||
markdown_extensions:
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
line_spans: __span
|
||||
pygments_lang_class: true
|
||||
- admonition
|
||||
- pymdownx.details
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets
|
||||
- footnotes
|
||||
- attr_list
|
||||
- pymdownx.critic
|
||||
- pymdownx.caret
|
||||
- pymdownx.keys
|
||||
- pymdownx.mark
|
||||
- pymdownx.tilde
|
||||
- pymdownx.tabbed:
|
||||
alternate_style: true
|
||||
plugins:
|
||||
- offline
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Installation: installation.md
|
||||
- Usage: usage.md
|
72
service.c
@ -8,6 +8,7 @@ SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
|
||||
void print(char const *fmt, ...);
|
||||
void CreateBridge();
|
||||
LPTSTR GetErrorMessage();
|
||||
extern BOOL IsLinux;
|
||||
|
||||
void WINAPI ServiceCtrlHandler(DWORD CtrlCode)
|
||||
{
|
||||
@ -83,66 +84,47 @@ void ServiceMain(DWORD argc, LPTSTR *argv)
|
||||
return;
|
||||
}
|
||||
|
||||
void DetectDarwin()
|
||||
void InstallService(int ServiceStartType, LPCSTR Path)
|
||||
{
|
||||
static void(CDECL * wine_get_host_version)(const char **sysname, const char **release);
|
||||
wine_get_host_version = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"),
|
||||
"wine_get_host_version");
|
||||
const char *__sysname;
|
||||
const char *__release;
|
||||
wine_get_host_version(&__sysname, &__release);
|
||||
if (strcmp(__sysname, "Darwin") == 0)
|
||||
print("Registering service\n");
|
||||
|
||||
if (IsLinux == FALSE)
|
||||
{
|
||||
/* FIXME: I don't know how to get the TMPDIR without getenv */
|
||||
MessageBox(NULL, "Registering as a service is not supported on macOS at the moment.",
|
||||
"Unsupported", MB_OK | MB_ICONINFORMATION);
|
||||
ExitProcess(1);
|
||||
}
|
||||
}
|
||||
|
||||
void InstallService()
|
||||
{
|
||||
print("Registering to run on startup\n");
|
||||
|
||||
DetectDarwin();
|
||||
|
||||
SC_HANDLE schSCManager, schService;
|
||||
DWORD dwTagId;
|
||||
|
||||
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
|
||||
SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
|
||||
if (schSCManager == NULL)
|
||||
{
|
||||
print("Failed to open service manager\n");
|
||||
MessageBox(NULL, GetErrorMessage(),
|
||||
NULL, MB_OK | MB_ICONSTOP);
|
||||
"OpenSCManager",
|
||||
MB_OK | MB_ICONSTOP);
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
schService =
|
||||
CreateService(schSCManager,
|
||||
"rpc-bridge", "Wine RPC Bridge",
|
||||
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
|
||||
SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
|
||||
"C:\\bridge.exe --service",
|
||||
NULL, &dwTagId, NULL, NULL, NULL);
|
||||
DWORD dwTagId;
|
||||
SC_HANDLE schService = CreateService(schSCManager,
|
||||
"rpc-bridge", "Wine RPC Bridge",
|
||||
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
|
||||
ServiceStartType, SERVICE_ERROR_NORMAL,
|
||||
Path, NULL, &dwTagId, NULL, NULL, NULL);
|
||||
|
||||
if (schService == NULL)
|
||||
{
|
||||
print("Failed to create service\n");
|
||||
MessageBox(NULL, GetErrorMessage(),
|
||||
NULL, MB_OK | MB_ICONSTOP);
|
||||
"CreateService",
|
||||
MB_OK | MB_ICONSTOP);
|
||||
ExitProcess(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
char filename[MAX_PATH];
|
||||
GetModuleFileName(NULL, filename, MAX_PATH);
|
||||
CopyFile(filename, "C:\\bridge.exe", FALSE);
|
||||
|
||||
print("Service installed successfully\n");
|
||||
CloseServiceHandle(schService);
|
||||
}
|
||||
|
||||
print("Service installed successfully\n");
|
||||
CloseServiceHandle(schService);
|
||||
CloseServiceHandle(schSCManager);
|
||||
ExitProcess(0);
|
||||
}
|
||||
|
||||
void RemoveService()
|
||||
@ -152,11 +134,12 @@ void RemoveService()
|
||||
SC_HANDLE schSCManager, schService;
|
||||
SERVICE_STATUS ssSvcStatus;
|
||||
|
||||
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
||||
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
|
||||
if (schSCManager == NULL)
|
||||
{
|
||||
MessageBox(NULL, GetErrorMessage(),
|
||||
NULL, MB_OK | MB_ICONSTOP);
|
||||
"OpenSCManager",
|
||||
MB_OK | MB_ICONSTOP);
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
@ -166,7 +149,8 @@ void RemoveService()
|
||||
if (schService == NULL)
|
||||
{
|
||||
MessageBox(NULL, GetErrorMessage(),
|
||||
NULL, MB_OK | MB_ICONSTOP);
|
||||
"OpenService",
|
||||
MB_OK | MB_ICONSTOP);
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
@ -195,13 +179,13 @@ void RemoveService()
|
||||
if (!DeleteService(schService))
|
||||
{
|
||||
MessageBox(NULL, GetErrorMessage(),
|
||||
NULL, MB_OK | MB_ICONSTOP);
|
||||
"DeleteService",
|
||||
MB_OK | MB_ICONSTOP);
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
DeleteFile("C:\\bridge.exe");
|
||||
DeleteFile("C:\\windows\\bridge.exe");
|
||||
print("Service removed successfully\n");
|
||||
CloseServiceHandle(schService);
|
||||
CloseServiceHandle(schSCManager);
|
||||
ExitProcess(0);
|
||||
}
|
||||
|