You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
390 lines
11 KiB
390 lines
11 KiB
#include "StartupSound.h" |
|
|
|
#include <Shlwapi.h> |
|
#pragma comment(lib, "Shlwapi.lib") |
|
#include <stdio.h> |
|
#include <strsafe.h> |
|
#pragma comment(lib, "Winmm.lib") |
|
#include <Wtsapi32.h> |
|
#pragma comment(lib, "Wtsapi32.lib") |
|
#include <tchar.h> |
|
#include <wrl/client.h> |
|
#include <wil/result_macros.h> |
|
|
|
#include "def.h" |
|
|
|
BOOL AreLogonLogoffShutdownSoundsEnabled() |
|
{ |
|
#if 0 |
|
DWORD dwValue = 0; |
|
DWORD dwSize = sizeof(dwValue); |
|
RegGetValueW(HKEY_CURRENT_USER, _T(REGPATH), L"LogonLogoffShutdownSounds", RRF_RT_DWORD, nullptr, &dwValue, &dwSize); |
|
return dwValue != 0; |
|
#else |
|
return FALSE; |
|
#endif |
|
} |
|
|
|
DWORD GetLastErrorError() |
|
{ |
|
DWORD result = GetLastError(); |
|
return result == ERROR_SUCCESS ? 1 : result; |
|
} |
|
|
|
HRESULT HRESULTFromLastErrorError() |
|
{ |
|
DWORD error = GetLastError(); |
|
if (error != ERROR_SUCCESS && (int)error <= 0) |
|
return (HRESULT)GetLastErrorError(); |
|
else |
|
return (HRESULT)((GetLastErrorError() & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000); |
|
} |
|
|
|
DWORD PlaySoundFileThreadProc(LPVOID pvData) |
|
{ |
|
PlaySoundW((LPCWSTR)pvData, nullptr, SND_NODEFAULT | SND_MEMORY | SND_SYSTEM); |
|
LocalFree(pvData); |
|
return 0; |
|
} |
|
|
|
HRESULT PlaySoundFile(HANDLE* phThread, const WCHAR* pszPath) |
|
{ |
|
HRESULT hr; |
|
|
|
void* pvData = nullptr; |
|
HANDLE hFile = CreateFileW( |
|
pszPath, |
|
GENERIC_READ, |
|
FILE_SHARE_READ | FILE_SHARE_DELETE, |
|
nullptr, |
|
OPEN_EXISTING, |
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, |
|
nullptr |
|
); |
|
if (hFile != INVALID_HANDLE_VALUE) |
|
{ |
|
DWORD dwSize = GetFileSize(hFile, nullptr); |
|
hr = E_OUTOFMEMORY; |
|
if (dwSize != (DWORD)-1 && dwSize) |
|
{ |
|
if (dwSize < 0x400000) |
|
{ |
|
pvData = LocalAlloc(0, dwSize); |
|
if (pvData) |
|
{ |
|
DWORD dwRead; |
|
if (ReadFile(hFile, pvData, dwSize, &dwRead, nullptr)) |
|
hr = dwSize == dwRead ? S_OK : HRESULT_FROM_WIN32(ERROR_IO_PENDING); |
|
else |
|
hr = HRESULTFromLastErrorError(); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
hr = HRESULTFromLastErrorError(); |
|
} |
|
CloseHandle(hFile); |
|
} |
|
else |
|
{ |
|
hr = HRESULTFromLastErrorError(); |
|
} |
|
if (SUCCEEDED(hr)) |
|
{ |
|
HANDLE hThread = CreateThread(nullptr, 0, PlaySoundFileThreadProc, pvData, 0, nullptr); |
|
if (hThread) |
|
{ |
|
if (phThread) |
|
*phThread = hThread; |
|
else |
|
CloseHandle(hThread); |
|
return hr; |
|
} |
|
hr = HRESULTFromLastErrorError(); |
|
} |
|
if (pvData) |
|
LocalFree(pvData); |
|
return hr; |
|
} |
|
|
|
typedef enum LOGONOFFSOUNDTYPE |
|
{ |
|
LOGONOFFSOUNDTYPE_LOGON, |
|
LOGONOFFSOUNDTYPE_LOGOFF, |
|
LOGONOFFSOUNDTYPE_EXIT, |
|
} LOGONOFFSOUNDTYPE; |
|
|
|
HRESULT PlayLogonLogoffSound(HANDLE* phThread, LOGONOFFSOUNDTYPE type) |
|
{ |
|
const WCHAR* szEventName; |
|
switch (type) |
|
{ |
|
case LOGONOFFSOUNDTYPE_LOGON: |
|
szEventName = L"WindowsLogon"; |
|
break; |
|
case LOGONOFFSOUNDTYPE_LOGOFF: |
|
szEventName = L"WindowsLogoff"; |
|
break; |
|
default: |
|
szEventName = L"SystemExit"; |
|
break; |
|
} |
|
|
|
WCHAR szSubKey[MAX_PATH]; |
|
HRESULT hr = StringCchPrintfW(szSubKey, ARRAYSIZE(szSubKey), L"AppEvents\\Schemes\\Apps\\.Default\\%ws\\.Current", szEventName); |
|
if (FAILED(hr)) |
|
return hr; |
|
|
|
WCHAR szPath[MAX_PATH]; |
|
DWORD cbData = sizeof(szPath); |
|
LSTATUS lStat = RegGetValueW(HKEY_CURRENT_USER, szSubKey, nullptr, REG_EXPAND_SZ, nullptr, szPath, &cbData); |
|
if (lStat != ERROR_SUCCESS) |
|
return HRESULT_FROM_WIN32(lStat); |
|
|
|
return PlaySoundFile(phThread, szPath); |
|
} |
|
|
|
// https://stackoverflow.com/a/59810748 |
|
bool IsSessionLocked() |
|
{ |
|
WTSINFOEXW* pInfo = NULL; |
|
WTS_INFO_CLASS wtsic = WTSSessionInfoEx; |
|
LPTSTR ppBuffer = NULL; |
|
DWORD dwBytesReturned = 0; |
|
LONG sessionFlags = WTS_SESSIONSTATE_UNKNOWN; |
|
|
|
DWORD dwSessionID = WTSGetActiveConsoleSessionId(); |
|
|
|
if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, dwSessionID, wtsic, &ppBuffer, &dwBytesReturned)) |
|
{ |
|
if (dwBytesReturned > 0) |
|
{ |
|
pInfo = (WTSINFOEXW*)ppBuffer; |
|
if (pInfo->Level == 1) |
|
{ |
|
sessionFlags = pInfo->Data.WTSInfoExLevel1.SessionFlags; |
|
} |
|
} |
|
WTSFreeMemory(ppBuffer); |
|
ppBuffer = NULL; |
|
} |
|
|
|
return (sessionFlags == WTS_SESSIONSTATE_LOCK); |
|
} |
|
|
|
HRESULT (*CLogonSound_PlayIfNecessaryFunc)(void* _this, LOGON_SOUND_CLIENT client); |
|
HRESULT CLogonSound_PlayIfNecessaryHook(void* _this, LOGON_SOUND_CLIENT client) |
|
{ |
|
HRESULT hr = CLogonSound_PlayIfNecessaryFunc(_this, client); |
|
if (hr != S_OK && client == LSC_EXPLORER) |
|
{ |
|
if (!IsSessionLocked()) |
|
PlayLogonLogoffSound(nullptr, LOGONOFFSOUNDTYPE_LOGON); |
|
} |
|
return hr; |
|
} |
|
|
|
HRESULT HookLogonSound() |
|
{ |
|
RETURN_IF_FAILED(CoInitialize(nullptr)); |
|
|
|
Microsoft::WRL::ComPtr<IAuthUILogonSound> logonSound; |
|
RETURN_IF_FAILED(CoCreateInstance(__uuidof_AuthUILogonSound, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&logonSound))); |
|
|
|
void** vtable = *(void***)logonSound.Get(); |
|
DWORD flOldProtect; |
|
RETURN_HR_IF(E_FAIL, !VirtualProtect(&vtable[3], sizeof(void*), PAGE_EXECUTE_READWRITE, &flOldProtect)); |
|
|
|
CLogonSound_PlayIfNecessaryFunc = (decltype(CLogonSound_PlayIfNecessaryFunc))vtable[3]; |
|
vtable[3] = (void*)CLogonSound_PlayIfNecessaryHook; |
|
VirtualProtect(&vtable[3], sizeof(void*), flOldProtect, &flOldProtect); |
|
|
|
return S_OK; |
|
} |
|
|
|
LRESULT SHDefWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
if (IsWindowUnicode(hwnd)) |
|
{ |
|
return DefWindowProcW(hwnd, uMsg, wParam, lParam); |
|
} |
|
else |
|
{ |
|
return DefWindowProcA(hwnd, uMsg, wParam, lParam); |
|
} |
|
} |
|
|
|
HWND g_hwndSound; |
|
|
|
class CSoundWnd |
|
{ |
|
public: |
|
CSoundWnd(); |
|
|
|
BOOL Init(); |
|
DWORD Release(); |
|
|
|
protected: |
|
static LRESULT CALLBACK s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); |
|
LRESULT v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); |
|
|
|
private: |
|
static DWORD s_CreateWindow(void* pvParam); |
|
static DWORD s_ThreadProc(void* pvParam); |
|
|
|
LONG m_refCount; |
|
HWND m_hwnd; |
|
HANDLE m_thread; |
|
}; |
|
|
|
CSoundWnd::CSoundWnd() |
|
: m_refCount(1) |
|
, m_hwnd(nullptr) |
|
, m_thread(nullptr) |
|
{ |
|
} |
|
|
|
BOOL CSoundWnd::Init() |
|
{ |
|
SHCreateThread(s_ThreadProc, this, CTF_THREAD_REF | CTF_COINIT_STA | CTF_REF_COUNTED | CTF_NOADDREFLIB, s_CreateWindow); |
|
g_hwndSound = m_hwnd; |
|
return m_hwnd != nullptr; |
|
} |
|
|
|
DWORD CSoundWnd::Release() |
|
{ |
|
LONG refCount = InterlockedDecrement(&m_refCount); |
|
if (refCount == 0 && this) |
|
operator delete(this); |
|
return refCount; |
|
} |
|
|
|
LRESULT CSoundWnd::s_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
CSoundWnd* pThis = (CSoundWnd*)GetWindowLongPtrW(hwnd, 0); |
|
if (pThis) |
|
return pThis->v_WndProc(hwnd, uMsg, wParam, lParam); |
|
else |
|
return SHDefWindowProc(hwnd, uMsg, wParam, lParam); |
|
} |
|
|
|
LRESULT CSoundWnd::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) |
|
{ |
|
switch (uMsg) |
|
{ |
|
case WM_QUERYENDSESSION: |
|
{ |
|
if ((lParam & ENDSESSION_CRITICAL) == 0) |
|
{ |
|
WCHAR sz[256]; |
|
LoadStringW(GetModuleHandleW(nullptr), 731, sz, ARRAYSIZE(sz)); // Playing logoff sound... |
|
ShutdownBlockReasonCreate(m_hwnd, sz); |
|
PlayLogonLogoffSound(&m_thread, (lParam & ENDSESSION_LOGOFF) != 0 ? LOGONOFFSOUNDTYPE_LOGOFF : LOGONOFFSOUNDTYPE_EXIT); |
|
if (m_thread) |
|
{ |
|
WaitForSingleObject(m_thread, INFINITE); // @MOD |
|
CloseHandle(m_thread); // @MOD |
|
} |
|
} |
|
return 1; |
|
} |
|
case WM_ENDSESSION: |
|
{ |
|
/*if (wParam && (lParam & ENDSESSION_CRITICAL) == 0 && m_thread) // @MOD This doesn't work |
|
{ |
|
WaitForSingleObject(m_thread, INFINITE); |
|
CloseHandle(m_thread); |
|
}*/ |
|
DestroyWindow(m_hwnd); |
|
break; |
|
} |
|
case WM_NCDESTROY: |
|
{ |
|
SetWindowLongW(hwnd, 0, 0); |
|
g_hwndSound = nullptr; |
|
m_hwnd = nullptr; |
|
PostQuitMessage(0); |
|
return 0; |
|
} |
|
} |
|
return SHDefWindowProc(hwnd, uMsg, wParam, lParam); |
|
} |
|
|
|
extern "C" HWND (__stdcall *explorerframe_SHCreateWorkerWindowFunc)( |
|
WNDPROC wndProc, |
|
HWND hWndParent, |
|
DWORD dwExStyle, |
|
DWORD dwStyle, |
|
HMENU hMenu, |
|
LONG_PTR wnd_extra |
|
); |
|
|
|
DWORD CSoundWnd::s_CreateWindow(void* pvParam) |
|
{ |
|
CSoundWnd* pThis = (CSoundWnd*)pvParam; |
|
InterlockedIncrement(&pThis->m_refCount); |
|
pThis->m_hwnd = explorerframe_SHCreateWorkerWindowFunc(s_WndProc, nullptr, 0, 0, nullptr, (LONG_PTR)pThis); |
|
return 0; |
|
} |
|
|
|
DWORD CSoundWnd::s_ThreadProc(void* pvParam) |
|
{ |
|
CSoundWnd* pThis = (CSoundWnd*)pvParam; |
|
if (pThis->m_hwnd) |
|
{ |
|
MSG Msg; |
|
while (GetMessageW(&Msg, nullptr, 0, 0)) |
|
{ |
|
TranslateMessage(&Msg); |
|
DispatchMessageW(&Msg); |
|
} |
|
} |
|
pThis->Release(); |
|
return 0; |
|
} |
|
|
|
BOOL InitSoundWindow() |
|
{ |
|
BOOL bSuccess = FALSE; |
|
CSoundWnd* soundWnd = new CSoundWnd(); |
|
if (soundWnd) |
|
{ |
|
bSuccess = soundWnd->Init(); |
|
soundWnd->Release(); |
|
} |
|
return bSuccess; |
|
} |
|
|
|
void TermSoundWindow() |
|
{ |
|
if (g_hwndSound) |
|
{ |
|
PostMessageW(g_hwndSound, WM_CLOSE, 0, 0); |
|
g_hwndSound = nullptr; |
|
} |
|
} |
|
|
|
HRESULT SHPlaySound(LPCWSTR pszSound, DWORD dwFlags) |
|
{ |
|
HRESULT hr; |
|
BOOL bDefault = (dwFlags & 1) != 0; |
|
BOOL bSecondAttempt = FALSE; |
|
while (true) |
|
{ |
|
WCHAR szKey[MAX_PATH]; |
|
hr = StringCchPrintfW(szKey, MAX_PATH, L"AppEvents\\Schemes\\Apps\\%s\\%s\\.current", bDefault ? L".Default" : L"Explorer", pszSound); |
|
if (SUCCEEDED(hr)) |
|
{ |
|
WCHAR pvData[MAX_PATH]; |
|
DWORD cbData = sizeof(pvData); |
|
if (SHGetValueW(HKEY_CURRENT_USER, szKey, nullptr, nullptr, pvData, &cbData) == ERROR_SUCCESS && cbData && pvData[0]) |
|
hr = PlaySoundW(pszSound, nullptr, (!bDefault ? 0x400000 : 0) | (SND_ASYNC | SND_NODEFAULT | SND_NOSTOP | SND_NOWAIT | SND_ALIAS | SND_SENTRY | SND_SYSTEM)) ? S_OK : S_FALSE; |
|
} |
|
if (hr == S_OK || (dwFlags & 2) == 0 || bSecondAttempt) |
|
break; |
|
bDefault = !bDefault; |
|
bSecondAttempt = TRUE; |
|
} |
|
return hr; |
|
}
|
|
|