66#include < endpointvolume.h>
77#include < mmdeviceapi.h>
88#include < tlhelp32.h>
9+ #include < wrl/client.h>
910
1011#include < algorithm>
1112#include < cwctype>
1213#include < iterator>
14+ #include < memory>
15+ #include < optional>
1316#include < string>
1417#include < string_view>
1518#include < thread>
2124
2225namespace {
2326
27+ using Microsoft::WRL::ComPtr;
2428using HotkeyAction = void (*)();
2529
30+ class ComInitializer {
31+ public:
32+ ComInitializer () {
33+ hr_ = CoInitialize (nullptr );
34+ initialized_ = SUCCEEDED (hr_) || hr_ == RPC_E_CHANGED_MODE;
35+ should_uninit_ = (hr_ == S_OK || hr_ == S_FALSE);
36+ }
37+ ~ComInitializer () {
38+ if (should_uninit_) {
39+ CoUninitialize ();
40+ }
41+ }
42+ ComInitializer (const ComInitializer&) = delete ;
43+ ComInitializer& operator =(const ComInitializer&) = delete ;
44+
45+ [[nodiscard]] bool IsInitialized () const { return initialized_; }
46+
47+ private:
48+ HRESULT hr_ = E_FAIL;
49+ bool initialized_ = false ;
50+ bool should_uninit_ = false ;
51+ };
52+
2653// Static variables for internal use
2754bool is_hide = false ;
2855std::vector<HWND> hwnd_list;
29- std::unordered_map<DWORD , bool > original_mute_states;
56+ std::unordered_map<std::wstring , bool > original_mute_states;
3057
3158#define MOD_NOREPEAT 0x4000
3259
@@ -80,68 +107,116 @@ std::vector<DWORD> GetAppPids() {
80107 return pids;
81108}
82109
110+ std::optional<std::wstring> GetSessionKey (IAudioSessionControl2* session2) {
111+ if (!session2) {
112+ return std::nullopt ;
113+ }
114+ LPWSTR session_id = nullptr ;
115+ if (SUCCEEDED (session2->GetSessionInstanceIdentifier (&session_id)) &&
116+ session_id) {
117+ std::wstring key (session_id);
118+ CoTaskMemFree (session_id);
119+ return key;
120+ }
121+ return std::nullopt ;
122+ }
123+
124+ void ProcessAudioSession (IAudioSessionControl* session,
125+ const std::vector<DWORD>& pids,
126+ bool set_mute,
127+ bool save_mute_state) {
128+ ComPtr<IAudioSessionControl2> session2;
129+ if (FAILED (session->QueryInterface (IID_PPV_ARGS (&session2)))) {
130+ return ;
131+ }
132+
133+ DWORD session_pid = 0 ;
134+ if (FAILED (session2->GetProcessId (&session_pid))) {
135+ return ;
136+ }
137+
138+ auto it = std::find (pids.begin (), pids.end (), session_pid);
139+ if (it == pids.end ()) {
140+ return ;
141+ }
142+
143+ ComPtr<ISimpleAudioVolume> volume;
144+ if (FAILED (session2->QueryInterface (IID_PPV_ARGS (&volume)))) {
145+ return ;
146+ }
147+
148+ auto session_key = GetSessionKey (session2.Get ());
149+
150+ if (save_mute_state && session_key) {
151+ BOOL is_muted = FALSE ;
152+ if (SUCCEEDED (volume->GetMute (&is_muted))) {
153+ original_mute_states[*session_key] = (is_muted == TRUE );
154+ }
155+ }
156+
157+ if (set_mute) {
158+ volume->SetMute (TRUE , nullptr );
159+ } else {
160+ // Unmute logic:
161+ // - If we have recorded state for this session, respect it
162+ // - If session is new (not in our records), unmute it
163+ // (it was likely created after hide, so it should be unmuted)
164+ bool should_unmute = true ;
165+ if (session_key) {
166+ auto state_it = original_mute_states.find (*session_key);
167+ if (state_it != original_mute_states.end ()) {
168+ should_unmute = !state_it->second ; // unmute only if was not muted
169+ }
170+ }
171+ if (should_unmute) {
172+ volume->SetMute (FALSE , nullptr );
173+ }
174+ }
175+ }
176+
83177void MuteProcess (const std::vector<DWORD>& pids,
84178 bool set_mute,
85179 bool save_mute_state = false ) {
86- CoInitialize (nullptr );
87- IMMDeviceEnumerator* enumerator = nullptr ;
88- IMMDevice* device = nullptr ;
89- IAudioSessionManager2* manager = nullptr ;
90- IAudioSessionEnumerator* session_enumerator = nullptr ;
91-
92- CoCreateInstance (__uuidof (MMDeviceEnumerator), nullptr , CLSCTX_ALL,
93- IID_PPV_ARGS (&enumerator));
94- enumerator->GetDefaultAudioEndpoint (eRender, eMultimedia, &device);
95- device->Activate (__uuidof (IAudioSessionManager2), CLSCTX_ALL, nullptr ,
96- (void **)&manager);
97- manager->GetSessionEnumerator (&session_enumerator);
180+ ComInitializer com;
181+ if (!com.IsInitialized ()) {
182+ return ;
183+ }
184+
185+ ComPtr<IMMDeviceEnumerator> enumerator;
186+ if (FAILED (CoCreateInstance (__uuidof (MMDeviceEnumerator), nullptr , CLSCTX_ALL,
187+ IID_PPV_ARGS (&enumerator)))) {
188+ return ;
189+ }
190+
191+ ComPtr<IMMDevice> device;
192+ if (FAILED (
193+ enumerator->GetDefaultAudioEndpoint (eRender, eMultimedia, &device))) {
194+ return ;
195+ }
196+
197+ ComPtr<IAudioSessionManager2> manager;
198+ if (FAILED (
199+ device->Activate (__uuidof (IAudioSessionManager2), CLSCTX_ALL, nullptr ,
200+ reinterpret_cast <void **>(manager.GetAddressOf ())))) {
201+ return ;
202+ }
203+
204+ ComPtr<IAudioSessionEnumerator> session_enumerator;
205+ if (FAILED (manager->GetSessionEnumerator (&session_enumerator))) {
206+ return ;
207+ }
98208
99209 int session_count = 0 ;
100- session_enumerator->GetCount (&session_count);
210+ if (FAILED (session_enumerator->GetCount (&session_count))) {
211+ return ;
212+ }
213+
101214 for (int i = 0 ; i < session_count; ++i) {
102- IAudioSessionControl* session = nullptr ;
103- session_enumerator->GetSession (i, &session);
104- IAudioSessionControl2* session2 = nullptr ;
105- if (SUCCEEDED (session->QueryInterface (__uuidof (IAudioSessionControl2),
106- (void **)&session2))) {
107- DWORD session_pid = 0 ;
108- session2->GetProcessId (&session_pid);
109-
110- for (DWORD pid : pids) {
111- if (session_pid == pid) {
112- ISimpleAudioVolume* volume = nullptr ;
113- if (SUCCEEDED (session2->QueryInterface (__uuidof (ISimpleAudioVolume),
114- (void **)&volume))) {
115- if (save_mute_state) {
116- BOOL is_muted;
117- volume->GetMute (&is_muted);
118- original_mute_states[pid] = (is_muted == TRUE );
119- }
120-
121- if (set_mute) {
122- volume->SetMute (TRUE , nullptr );
123- } else {
124- // Only unmute if the original state was not muted beforehand
125- auto it = original_mute_states.find (pid);
126- if (it != original_mute_states.end () && !it->second ) {
127- volume->SetMute (FALSE , nullptr );
128- }
129- }
130- volume->Release ();
131- }
132- break ;
133- }
134- }
135- session2->Release ();
215+ ComPtr<IAudioSessionControl> session;
216+ if (SUCCEEDED (session_enumerator->GetSession (i, &session)) && session) {
217+ ProcessAudioSession (session.Get (), pids, set_mute, save_mute_state);
136218 }
137- session->Release ();
138219 }
139-
140- session_enumerator->Release ();
141- manager->Release ();
142- device->Release ();
143- enumerator->Release ();
144- CoUninitialize ();
145220}
146221
147222void HideAndShow () {
@@ -162,6 +237,7 @@ void HideAndShow() {
162237 }
163238 hwnd_list.clear ();
164239 MuteProcess (chrome_pids, false );
240+ original_mute_states.clear ();
165241 }
166242 is_hide = !is_hide;
167243}
0 commit comments