// dirtytalk.cpp - Win32 keyboard monitoring service // Runs as a Windows service (no window, no console, not in taskbar). // Monitors keyboard activity: counts key presses and measures speed. // When a threshold is reached, plays a random effect WAV file. #define WIN32_LEAN_AND_MEAN #include #include #include #include #pragma comment(lib, "winmm.lib") // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- // Number of available effect WAV files (effect1.wav … effectN.wav) const int EFFECT_FILE_COUNT = 5; // Threshold for "fast" typing: ~50 rapid key presses trigger the effect const int FAST_PRESS_THRESHOLD = 50; // Threshold for "slow" typing: ~300 key presses trigger the effect const int SLOW_PRESS_THRESHOLD = 300; // A key press is considered "fast" when the inter-key interval is below this const DWORD FAST_INTERVAL_MS = 200; // --------------------------------------------------------------------------- // Globals // --------------------------------------------------------------------------- // currentValue records keyboard activity as a running correlation between // the number of keys pressed and their typing speed. // Higher speed contributions accumulate the value more quickly, allowing fast // typists to hit FAST_PRESS_THRESHOLD and slow typists SLOW_PRESS_THRESHOLD. double currentValue = 0.0; // Service infrastructure static SERVICE_STATUS g_ServiceStatus = {}; static SERVICE_STATUS_HANDLE g_StatusHandle = NULL; static HANDLE g_StopEvent = INVALID_HANDLE_VALUE; static HHOOK g_KeyboardHook = NULL; // Keyboard tracking state static DWORD g_LastKeyTime = 0; static int g_PressCount = 0; // --------------------------------------------------------------------------- // Helper: resolve an effect file path from the service's own directory // --------------------------------------------------------------------------- // Determines the full path of 'filename' by using the module's location as // the base directory (stored implicitly in the executable path variable). static void GetEffectFilePath(const wchar_t *filename, wchar_t *outPath, DWORD outSize) { wchar_t modulePath[MAX_PATH] = {}; GetModuleFileNameW(NULL, modulePath, MAX_PATH); // Trim to directory portion wchar_t *lastSep = wcsrchr(modulePath, L'\\'); if (lastSep) *(lastSep + 1) = L'\0'; // keep trailing backslash else modulePath[0] = L'\0'; // no directory component _snwprintf_s(outPath, outSize, _TRUNCATE, L"%s%s", modulePath, filename); } // --------------------------------------------------------------------------- // Helper: play a randomly chosen effect WAV file // --------------------------------------------------------------------------- static void PlayRandomEffect() { int index = (rand() % EFFECT_FILE_COUNT) + 1; wchar_t filename[64]; swprintf_s(filename, L"effect%d.wav", index); wchar_t fullPath[MAX_PATH]; GetEffectFilePath(filename, fullPath, MAX_PATH); PlaySoundW(fullPath, NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT); } // --------------------------------------------------------------------------- // Helper: monitor keyboard activity // Updates currentValue and triggers the effect when a threshold is reached. // --------------------------------------------------------------------------- static void MonitorKeyboardActivity() { DWORD now = GetTickCount(); DWORD interval = (g_LastKeyTime > 0) ? (now - g_LastKeyTime) : FAST_INTERVAL_MS; g_LastKeyTime = now; // Speed factor: fast presses contribute more than 1.0, slow ones contribute 1.0. // Guard against division by near-zero by adding 1 ms. double speedFactor = (interval < FAST_INTERVAL_MS) ? (double)FAST_INTERVAL_MS / (double)(interval + 1) : 1.0; g_PressCount++; currentValue += speedFactor; // correlates press count with speed // Determine whether to trigger the effect bool isFast = (speedFactor > 1.0); bool triggered = (isFast && g_PressCount >= FAST_PRESS_THRESHOLD) || (!isFast && g_PressCount >= SLOW_PRESS_THRESHOLD); if (triggered) { // Reset tracking state before playing so the hook remains responsive g_PressCount = 0; currentValue = 0.0; PlayRandomEffect(); } } // --------------------------------------------------------------------------- // Low-level keyboard hook procedure // --------------------------------------------------------------------------- static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HC_ACTION && wParam == WM_KEYDOWN) MonitorKeyboardActivity(); return CallNextHookEx(NULL, nCode, wParam, lParam); } // --------------------------------------------------------------------------- // Service worker thread: installs the hook and runs a message pump // --------------------------------------------------------------------------- static DWORD WINAPI ServiceWorkerThread(LPVOID /*lpParam*/) { srand((unsigned int)time(NULL)); g_KeyboardHook = SetWindowsHookExW(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandleW(NULL), 0); if (g_KeyboardHook == NULL) return GetLastError(); // WH_KEYBOARD_LL requires a message loop on the installing thread MSG msg; while (WaitForSingleObject(g_StopEvent, 0) == WAIT_TIMEOUT) { while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } Sleep(10); } UnhookWindowsHookEx(g_KeyboardHook); g_KeyboardHook = NULL; return ERROR_SUCCESS; } // --------------------------------------------------------------------------- // Service control handler // --------------------------------------------------------------------------- static VOID WINAPI ServiceCtrlHandler(DWORD ctrlCode) { if (ctrlCode == SERVICE_CONTROL_STOP && g_ServiceStatus.dwCurrentState == SERVICE_RUNNING) { g_ServiceStatus.dwControlsAccepted = 0; g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwCheckPoint = 4; SetServiceStatus(g_StatusHandle, &g_ServiceStatus); SetEvent(g_StopEvent); } } // --------------------------------------------------------------------------- // ServiceMain // --------------------------------------------------------------------------- static VOID WINAPI ServiceMain(DWORD /*argc*/, LPWSTR * /*argv*/) { g_StatusHandle = RegisterServiceCtrlHandlerW(L"DirtyTalk", ServiceCtrlHandler); if (!g_StatusHandle) return; ZeroMemory(&g_ServiceStatus, sizeof(g_ServiceStatus)); g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; g_ServiceStatus.dwControlsAccepted = 0; SetServiceStatus(g_StatusHandle, &g_ServiceStatus); g_StopEvent = CreateEventW(NULL, TRUE, FALSE, NULL); if (!g_StopEvent) { g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; g_ServiceStatus.dwWin32ExitCode = GetLastError(); SetServiceStatus(g_StatusHandle, &g_ServiceStatus); return; } g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwCheckPoint = 0; SetServiceStatus(g_StatusHandle, &g_ServiceStatus); HANDLE hThread = CreateThread(NULL, 0, ServiceWorkerThread, NULL, 0, NULL); if (!hThread) { CloseHandle(g_StopEvent); g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; g_ServiceStatus.dwWin32ExitCode = GetLastError(); SetServiceStatus(g_StatusHandle, &g_ServiceStatus); return; } WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(g_StopEvent); g_ServiceStatus.dwControlsAccepted = 0; g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwCheckPoint = 3; SetServiceStatus(g_StatusHandle, &g_ServiceStatus); } // --------------------------------------------------------------------------- // Entry point — no console window, not in taskbar (WinMain, not main) // --------------------------------------------------------------------------- int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/) { SERVICE_TABLE_ENTRYW serviceTable[] = { { const_cast(L"DirtyTalk"), reinterpret_cast(ServiceMain) }, { NULL, NULL } }; StartServiceCtrlDispatcherW(serviceTable); return 0; }