#include #include #include #include #include #include "resource.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; } 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("Service doesn't exist: %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); print("Bridge service started successfully\n"); } 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); } void ShowLicenseDialog() { HMODULE hModule = GetModuleHandle(NULL); HRSRC hRes = FindResource(hModule, MAKEINTRESOURCE(IDR_LICENSE_TXT), RT_RCDATA); if (!hRes) { MessageBox(NULL, "Resource not found", "Error", MB_OK | MB_ICONERROR); return; } HGLOBAL hResData = LoadResource(NULL, hRes); if (!hResData) { MessageBox(NULL, "Resource failed to load", "Error", MB_OK | MB_ICONERROR); return; } DWORD resSize = SizeofResource(NULL, hRes); void *pRes = LockResource(hResData); if (!pRes) { MessageBox(NULL, "Resource failed to lock", "Error", MB_OK | MB_ICONERROR); return; } char *licenseText = (char *)malloc(resSize + 1); if (!licenseText) { MessageBox(NULL, "Memory allocation failed", "Error", MB_OK | MB_ICONERROR); return; } memcpy(licenseText, pRes, resSize); licenseText[resSize] = '\0'; MessageBoxA(hwnd, licenseText, "About", MB_OK); free(licenseText); } 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; case IDM_VIEW_LOG: ShellExecute(NULL, "open", "C:\\windows\\notepad.exe", "C:\\windows\\logs\\bridge.log", NULL, SW_SHOW); break; case IDM_HELP_DOCUMENTATION: ShellExecute(NULL, "open", "https://enderice2.github.io/rpc-bridge/index.html", NULL, NULL, SW_SHOWNORMAL); break; case IDM_HELP_LICENSE: ShowLicenseDialog(); break; case IDM_HELP_ABOUT: { char msg[256]; snprintf(msg, sizeof(msg), "rpc-bridge v%s\n" " branch: %s\n" " commit: %s\n\n" "Simple bridge that allows you to use Discord Rich Presence with Wine games/software.\n\n" "Created by EnderIce2\n\n" "Licensed under the MIT License", VER_VERSION_STR, GIT_BRANCH, GIT_COMMIT); MessageBox(NULL, msg, "About", MB_OK); 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 | WS_TABSTOP; *btnRemoveStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP; *btnInstallStyle = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP; // 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); HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAINMENU)); SetMenu(hwnd, hMenu); 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) { if (!IsDialogMessage(hwnd, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } void CreateGUI() { ShowWindow(GetConsoleWindow(), SW_MINIMIZE); ExitProcess(__WinMain(GetModuleHandle(NULL), NULL, GetCommandLine(), SW_SHOWNORMAL)); }