1 /* 2 * PROJECT: ReactOS Multimedia Control Panel 3 * FILE: dll/cpl/mmsys/volume.c 4 * PURPOSE: ReactOS Multimedia Control Panel 5 * PROGRAMMER: Thomas Weidenmueller <w3seek@reactos.com> 6 * Johannes Anderwald <janderwald@reactos.com> 7 * Dmitry Chapyshev <dmitry@reactos.org> 8 */ 9 10 #include "mmsys.h" 11 12 #include <shellapi.h> 13 14 15 typedef struct _GLOBAL_DATA 16 { 17 HMIXER hMixer; 18 HICON hIconMuted; 19 HICON hIconUnMuted; 20 HICON hIconNoHW; 21 HICON hIconSpeakImg; 22 23 LONG muteVal; 24 DWORD muteControlID; 25 26 DWORD volumeControlID; 27 DWORD volumeChannels; 28 DWORD volumeMinimum; 29 DWORD volumeMaximum; 30 DWORD volumeStep; 31 32 DWORD maxVolume; 33 PMIXERCONTROLDETAILS_UNSIGNED volumeInitialValues; 34 PMIXERCONTROLDETAILS_UNSIGNED volumePreviousValues; 35 PMIXERCONTROLDETAILS_UNSIGNED volumeCurrentValues; 36 37 } GLOBAL_DATA, *PGLOBAL_DATA; 38 39 40 VOID 41 GetMuteControl(PGLOBAL_DATA pGlobalData) 42 { 43 MIXERLINEW mxln; 44 MIXERCONTROLW mxc; 45 MIXERLINECONTROLSW mxlctrl; 46 47 if (pGlobalData->hMixer == NULL) 48 return; 49 50 mxln.cbStruct = sizeof(MIXERLINEW); 51 mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS; 52 53 if (mixerGetLineInfoW((HMIXEROBJ)pGlobalData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE) 54 != MMSYSERR_NOERROR) 55 return; 56 57 mxlctrl.cbStruct = sizeof(MIXERLINECONTROLSW); 58 mxlctrl.dwLineID = mxln.dwLineID; 59 mxlctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE; 60 mxlctrl.cControls = 1; 61 mxlctrl.cbmxctrl = sizeof(MIXERCONTROLW); 62 mxlctrl.pamxctrl = &mxc; 63 64 if (mixerGetLineControlsW((HMIXEROBJ)pGlobalData->hMixer, &mxlctrl, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE) 65 != MMSYSERR_NOERROR) 66 return; 67 68 pGlobalData->muteControlID = mxc.dwControlID; 69 } 70 71 72 VOID 73 GetMuteState(PGLOBAL_DATA pGlobalData) 74 { 75 MIXERCONTROLDETAILS_BOOLEAN mxcdMute; 76 MIXERCONTROLDETAILS mxcd; 77 78 if (pGlobalData->hMixer == NULL) 79 return; 80 81 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); 82 mxcd.dwControlID = pGlobalData->muteControlID; 83 mxcd.cChannels = 1; 84 mxcd.cMultipleItems = 0; 85 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); 86 mxcd.paDetails = &mxcdMute; 87 88 if (mixerGetControlDetailsW((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE) 89 != MMSYSERR_NOERROR) 90 return; 91 92 pGlobalData->muteVal = mxcdMute.fValue; 93 } 94 95 96 VOID 97 SwitchMuteState(PGLOBAL_DATA pGlobalData) 98 { 99 MIXERCONTROLDETAILS_BOOLEAN mxcdMute; 100 MIXERCONTROLDETAILS mxcd; 101 102 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); 103 mxcd.dwControlID = pGlobalData->muteControlID; 104 mxcd.cChannels = 1; 105 mxcd.cMultipleItems = 0; 106 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); 107 mxcd.paDetails = &mxcdMute; 108 109 mxcdMute.fValue = !pGlobalData->muteVal; 110 if (mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE) 111 != MMSYSERR_NOERROR) 112 return; 113 114 pGlobalData->muteVal = mxcdMute.fValue; 115 } 116 117 118 VOID 119 GetVolumeControl(PGLOBAL_DATA pGlobalData) 120 { 121 MIXERLINEW mxln; 122 MIXERCONTROLW mxc; 123 MIXERLINECONTROLSW mxlc; 124 125 if (pGlobalData->hMixer == NULL) 126 return; 127 128 mxln.cbStruct = sizeof(MIXERLINEW); 129 mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS; 130 if (mixerGetLineInfoW((HMIXEROBJ)pGlobalData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE) 131 != MMSYSERR_NOERROR) 132 return; 133 134 pGlobalData->volumeChannels = mxln.cChannels; 135 136 mxlc.cbStruct = sizeof(MIXERLINECONTROLSW); 137 mxlc.dwLineID = mxln.dwLineID; 138 mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; 139 mxlc.cControls = 1; 140 mxlc.cbmxctrl = sizeof(MIXERCONTROLW); 141 mxlc.pamxctrl = &mxc; 142 if (mixerGetLineControlsW((HMIXEROBJ)pGlobalData->hMixer, &mxlc, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE) 143 != MMSYSERR_NOERROR) 144 return; 145 146 pGlobalData->volumeMinimum = mxc.Bounds.dwMinimum; 147 pGlobalData->volumeMaximum = mxc.Bounds.dwMaximum; 148 pGlobalData->volumeControlID = mxc.dwControlID; 149 pGlobalData->volumeStep = (pGlobalData->volumeMaximum - pGlobalData->volumeMinimum) / (VOLUME_MAX - VOLUME_MIN); 150 151 pGlobalData->volumeInitialValues = HeapAlloc(GetProcessHeap(), 152 0, 153 mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED)); 154 if (pGlobalData->volumeInitialValues == NULL) 155 return; 156 157 pGlobalData->volumePreviousValues = HeapAlloc(GetProcessHeap(), 158 0, 159 mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED)); 160 if (pGlobalData->volumePreviousValues == NULL) 161 return; 162 163 pGlobalData->volumeCurrentValues = HeapAlloc(GetProcessHeap(), 164 0, 165 mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED)); 166 if (pGlobalData->volumeCurrentValues == NULL) 167 return; 168 } 169 170 171 VOID 172 GetVolumeValue( 173 PGLOBAL_DATA pGlobalData, 174 BOOL bInit) 175 { 176 MIXERCONTROLDETAILS mxcd; 177 DWORD i; 178 179 if (pGlobalData->hMixer == NULL) 180 return; 181 182 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); 183 mxcd.dwControlID = pGlobalData->volumeControlID; 184 mxcd.cChannels = pGlobalData->volumeChannels; 185 mxcd.cMultipleItems = 0; 186 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); 187 mxcd.paDetails = pGlobalData->volumePreviousValues; 188 189 if (mixerGetControlDetailsW((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE) 190 != MMSYSERR_NOERROR) 191 return; 192 193 pGlobalData->maxVolume = 0; 194 for (i = 0; i < pGlobalData->volumeChannels; i++) 195 { 196 pGlobalData->volumeCurrentValues[i].dwValue = pGlobalData->volumePreviousValues[i].dwValue; 197 198 if (pGlobalData->volumePreviousValues[i].dwValue > pGlobalData->maxVolume) 199 pGlobalData->maxVolume = pGlobalData->volumePreviousValues[i].dwValue; 200 201 if (bInit) 202 pGlobalData->volumeInitialValues[i].dwValue = pGlobalData->volumeCurrentValues[i].dwValue; 203 } 204 } 205 206 207 VOID 208 SetVolumeValue(PGLOBAL_DATA pGlobalData, 209 DWORD dwPosition) 210 { 211 MIXERCONTROLDETAILS mxcd; 212 DWORD dwVolume, i; 213 214 if (pGlobalData->hMixer == NULL) 215 return; 216 217 if (dwPosition == VOLUME_MIN) 218 dwVolume = pGlobalData->volumeMinimum; 219 else if (dwPosition == VOLUME_MAX) 220 dwVolume = pGlobalData->volumeMaximum; 221 else 222 dwVolume = (dwPosition * pGlobalData->volumeStep) + pGlobalData->volumeMinimum; 223 224 for (i = 0; i < pGlobalData->volumeChannels; i++) 225 { 226 if (pGlobalData->volumePreviousValues[i].dwValue == pGlobalData->maxVolume) 227 { 228 pGlobalData->volumeCurrentValues[i].dwValue = dwVolume; 229 } 230 else 231 { 232 pGlobalData->volumeCurrentValues[i].dwValue = 233 pGlobalData->volumePreviousValues[i].dwValue * dwVolume / pGlobalData->maxVolume; 234 } 235 } 236 237 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); 238 mxcd.dwControlID = pGlobalData->volumeControlID; 239 mxcd.cChannels = pGlobalData->volumeChannels; 240 mxcd.cMultipleItems = 0; 241 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); 242 mxcd.paDetails = pGlobalData->volumeCurrentValues; 243 244 if (mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE) 245 != MMSYSERR_NOERROR) 246 return; 247 } 248 249 250 static 251 VOID 252 RestoreVolumeValue( 253 PGLOBAL_DATA pGlobalData) 254 { 255 MIXERCONTROLDETAILS mxcd; 256 257 if (pGlobalData->hMixer == NULL) 258 return; 259 260 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); 261 mxcd.dwControlID = pGlobalData->volumeControlID; 262 mxcd.cChannels = pGlobalData->volumeChannels; 263 mxcd.cMultipleItems = 0; 264 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); 265 mxcd.paDetails = pGlobalData->volumeInitialValues; 266 267 mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer, 268 &mxcd, 269 MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE); 270 } 271 272 273 static 274 VOID 275 SetSystrayVolumeIconState(BOOL bEnabled) 276 { 277 HWND hwndTaskBar; 278 279 hwndTaskBar = FindWindowW(L"SystemTray_Main", NULL); 280 if (hwndTaskBar == NULL) 281 return; 282 283 SendMessageW(hwndTaskBar, WM_USER + 220, 4, bEnabled); 284 } 285 286 static 287 BOOL 288 GetSystrayVolumeIconState(VOID) 289 { 290 HWND hwndTaskBar; 291 292 hwndTaskBar = FindWindowW(L"SystemTray_Main", NULL); 293 if (hwndTaskBar == NULL) 294 { 295 return FALSE; 296 } 297 298 return (BOOL)SendMessageW(hwndTaskBar, WM_USER + 221, 4, 0); 299 } 300 301 VOID 302 InitVolumeControls(HWND hwndDlg, PGLOBAL_DATA pGlobalData) 303 { 304 UINT NumMixers; 305 MIXERCAPSW mxc; 306 WCHAR szNoDevices[256]; 307 308 LoadStringW(hApplet, IDS_NO_DEVICES, szNoDevices, _countof(szNoDevices)); 309 310 SendDlgItemMessageW(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(VOLUME_MIN, VOLUME_MAX)); 311 SendDlgItemMessageW(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETTICFREQ, VOLUME_TICFREQ, 0); 312 SendDlgItemMessageW(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPAGESIZE, 0, VOLUME_PAGESIZE); 313 314 NumMixers = mixerGetNumDevs(); 315 if (!NumMixers) 316 { 317 EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_TRACKBAR), FALSE); 318 EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_LOW), FALSE); 319 EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_HIGH), FALSE); 320 EnableWindow(GetDlgItem(hwndDlg, IDC_MUTE_CHECKBOX), FALSE); 321 EnableWindow(GetDlgItem(hwndDlg, IDC_ICON_IN_TASKBAR), FALSE); 322 EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED_BTN), FALSE); 323 EnableWindow(GetDlgItem(hwndDlg, IDC_SPEAKER_VOL_BTN), FALSE); 324 EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED2_BTN), FALSE); 325 SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconNoHW); 326 SetDlgItemTextW(hwndDlg, IDC_DEVICE_NAME, szNoDevices); 327 return; 328 } 329 330 if (mixerOpen(&pGlobalData->hMixer, 0, PtrToUlong(hwndDlg), 0, MIXER_OBJECTF_MIXER | CALLBACK_WINDOW) != MMSYSERR_NOERROR) 331 { 332 MessageBoxW(hwndDlg, L"Cannot open mixer", NULL, MB_OK); 333 return; 334 } 335 336 ZeroMemory(&mxc, sizeof(MIXERCAPSW)); 337 if (mixerGetDevCapsW(PtrToUint(pGlobalData->hMixer), &mxc, sizeof(MIXERCAPSW)) != MMSYSERR_NOERROR) 338 { 339 MessageBoxW(hwndDlg, L"mixerGetDevCaps failed", NULL, MB_OK); 340 return; 341 } 342 343 CheckDlgButton(hwndDlg, 344 IDC_ICON_IN_TASKBAR, 345 GetSystrayVolumeIconState() ? BST_CHECKED : BST_UNCHECKED); 346 347 GetMuteControl(pGlobalData); 348 GetMuteState(pGlobalData); 349 if (pGlobalData->muteVal) 350 { 351 SendDlgItemMessageW(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_CHECKED, 0); 352 SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted); 353 } 354 else 355 { 356 SendDlgItemMessageW(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0); 357 SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted); 358 } 359 360 GetVolumeControl(pGlobalData); 361 GetVolumeValue(pGlobalData, TRUE); 362 363 SendDlgItemMessageW(hwndDlg, IDC_DEVICE_NAME, WM_SETTEXT, 0, (LPARAM)mxc.szPname); 364 SendDlgItemMessageW(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pGlobalData->maxVolume - pGlobalData->volumeMinimum) / pGlobalData->volumeStep); 365 } 366 367 VOID 368 SaveData(HWND hwndDlg) 369 { 370 BOOL bShowIcon; 371 372 bShowIcon = (IsDlgButtonChecked(hwndDlg, IDC_ICON_IN_TASKBAR) == BST_CHECKED); 373 374 SetSystrayVolumeIconState(bShowIcon); 375 } 376 377 VOID 378 LaunchSoundControl(HWND hwndDlg) 379 { 380 if ((INT_PTR)ShellExecuteW(NULL, L"open", L"sndvol32.exe", NULL, NULL, SW_SHOWNORMAL) > 32) 381 return; 382 MessageBoxW(hwndDlg, L"Cannot run sndvol32.exe", NULL, MB_OK); 383 } 384 385 /* Volume property page dialog callback */ 386 //static INT_PTR CALLBACK 387 INT_PTR CALLBACK 388 VolumeDlgProc(HWND hwndDlg, 389 UINT uMsg, 390 WPARAM wParam, 391 LPARAM lParam) 392 { 393 static const INT speakImgSize[] = {72, 72}; 394 PGLOBAL_DATA pGlobalData; 395 396 pGlobalData = (PGLOBAL_DATA)GetWindowLongPtrW(hwndDlg, DWLP_USER); 397 398 switch (uMsg) 399 { 400 case MM_MIXM_LINE_CHANGE: 401 { 402 GetMuteState(pGlobalData); 403 if (pGlobalData->muteVal) 404 { 405 SendDlgItemMessageW(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_CHECKED, 0); 406 SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted); 407 } 408 else 409 { 410 SendDlgItemMessageW(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0); 411 SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted); 412 } 413 break; 414 } 415 case MM_MIXM_CONTROL_CHANGE: 416 { 417 GetVolumeValue(pGlobalData, FALSE); 418 SendDlgItemMessageW(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pGlobalData->maxVolume - pGlobalData->volumeMinimum) / pGlobalData->volumeStep); 419 break; 420 } 421 case WM_INITDIALOG: 422 { 423 pGlobalData = (PGLOBAL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GLOBAL_DATA)); 424 SetWindowLongPtrW(hwndDlg, DWLP_USER, (LONG_PTR)pGlobalData); 425 426 pGlobalData->hIconUnMuted = LoadImageW(hApplet, MAKEINTRESOURCEW(IDI_CPLICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR); 427 pGlobalData->hIconMuted = LoadImageW(hApplet, MAKEINTRESOURCEW(IDI_MUTED_ICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR); 428 pGlobalData->hIconNoHW = LoadImageW(hApplet, MAKEINTRESOURCEW(IDI_NO_HW), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR); 429 pGlobalData->hIconSpeakImg = LoadImageW(hApplet, 430 MAKEINTRESOURCEW(IDI_SPEAKIMG), 431 IMAGE_ICON, 432 speakImgSize[0], 433 speakImgSize[1], 434 LR_DEFAULTCOLOR); 435 436 InitVolumeControls(hwndDlg, pGlobalData); 437 break; 438 } 439 440 case WM_DRAWITEM: 441 { 442 LPDRAWITEMSTRUCT lpDrawItem; 443 lpDrawItem = (LPDRAWITEMSTRUCT)lParam; 444 if (lpDrawItem->CtlID == IDC_SPEAKIMG) 445 { 446 LONG left; 447 448 /* Position image in centre of dialog */ 449 left = (lpDrawItem->rcItem.right - speakImgSize[0]) / 2; 450 451 DrawIconEx(lpDrawItem->hDC, 452 left, 453 lpDrawItem->rcItem.top, 454 pGlobalData->hIconSpeakImg, 455 speakImgSize[0], 456 speakImgSize[1], 457 0, 458 NULL, 459 DI_NORMAL); 460 } 461 break; 462 } 463 464 case WM_COMMAND: 465 { 466 switch (LOWORD(wParam)) 467 { 468 case IDC_MUTE_CHECKBOX: 469 if (HIWORD(wParam) == BN_CLICKED) 470 { 471 SwitchMuteState(pGlobalData); 472 if (pGlobalData->muteVal) 473 { 474 SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted); 475 } 476 else 477 { 478 SendDlgItemMessageW(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted); 479 } 480 481 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 482 } 483 break; 484 485 case IDC_ICON_IN_TASKBAR: 486 if (HIWORD(wParam) == BN_CLICKED) 487 { 488 PropSheet_Changed(GetParent(hwndDlg), hwndDlg); 489 } 490 break; 491 492 case IDC_ADVANCED_BTN: 493 LaunchSoundControl(hwndDlg); 494 break; 495 496 case IDC_SPEAKER_VOL_BTN: 497 SpeakerVolume(hwndDlg); 498 break; 499 } 500 break; 501 } 502 503 case WM_HSCROLL: 504 { 505 HWND hVolumeTrackbar = GetDlgItem(hwndDlg, IDC_VOLUME_TRACKBAR); 506 if (hVolumeTrackbar == (HWND)lParam) 507 { 508 switch (LOWORD(wParam)) 509 { 510 case TB_THUMBPOSITION: 511 break; 512 513 case TB_ENDTRACK: 514 PlaySoundW((LPCWSTR)SND_ALIAS_SYSTEMDEFAULT, NULL, SND_ALIAS_ID | SND_ASYNC); 515 break; 516 517 default: 518 SetVolumeValue(pGlobalData, 519 (DWORD)SendDlgItemMessageW(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_GETPOS, 0, 0)); 520 break; 521 } 522 } 523 break; 524 } 525 526 case WM_DESTROY: 527 if (pGlobalData) 528 { 529 HeapFree(GetProcessHeap(), 0, pGlobalData->volumeCurrentValues); 530 HeapFree(GetProcessHeap(), 0, pGlobalData->volumePreviousValues); 531 HeapFree(GetProcessHeap(), 0, pGlobalData->volumeInitialValues); 532 mixerClose(pGlobalData->hMixer); 533 534 if (pGlobalData->hIconSpeakImg) 535 DestroyIcon(pGlobalData->hIconSpeakImg); 536 537 if (pGlobalData->hIconNoHW) 538 DestroyIcon(pGlobalData->hIconNoHW); 539 540 if (pGlobalData->hIconMuted) 541 DestroyIcon(pGlobalData->hIconMuted); 542 543 if (pGlobalData->hIconUnMuted) 544 DestroyIcon(pGlobalData->hIconUnMuted); 545 546 HeapFree(GetProcessHeap(), 0, pGlobalData); 547 } 548 break; 549 550 case WM_NOTIFY: 551 switch (((LPNMHDR)lParam)->code) 552 { 553 case PSN_APPLY: 554 SaveData(hwndDlg); 555 break; 556 557 case PSN_RESET: 558 RestoreVolumeValue(pGlobalData); 559 break; 560 } 561 return TRUE; 562 } 563 564 return FALSE; 565 } 566