| 611 | |
| 612 | |
| 613 | Script::~Script() // Destructor. |
| 614 | { |
| 615 | // MSDN: "Before terminating, an application must call the UnhookWindowsHookEx function to free |
| 616 | // system resources associated with the hook." |
| 617 | AddRemoveHooks(0); // Remove all hooks. |
| 618 | if (mNIC.hWnd) // Tray icon is installed. |
| 619 | Shell_NotifyIcon(NIM_DELETE, &mNIC); // Remove it. |
| 620 | |
| 621 | int i; |
| 622 | // It is safer/easier to destroy the GUI windows prior to the menus (especially the menu bars). |
| 623 | // This is because one GUI window might get destroyed and take with it a menu bar that is still |
| 624 | // in use by an existing GUI window. GuiType::Destroy() adheres to this philosophy by detaching |
| 625 | // its menu bar prior to destroying its window. |
| 626 | GuiType* gui; |
| 627 | while (gui = g_firstGui) // Destroy any remaining GUI windows (due to e.g. circular references). Also: assignment. |
| 628 | gui->Destroy(); |
| 629 | for (i = 0; i < GuiType::sFontCount; ++i) // Now that GUI windows are gone, delete all GUI fonts. |
| 630 | if (GuiType::sFont[i].hfont) |
| 631 | DeleteObject(GuiType::sFont[i].hfont); |
| 632 | // The above might attempt to delete an HFONT from GetStockObject(DEFAULT_GUI_FONT), etc. |
| 633 | // But that should be harmless: |
| 634 | // MSDN: "It is not necessary (but it is not harmful) to delete stock objects by calling DeleteObject." |
| 635 | |
| 636 | // Above: Probably best to have removed icon from tray and destroyed any Gui windows that were |
| 637 | // using it prior to getting rid of the script's custom icon below: |
| 638 | if (mCustomIcon) |
| 639 | { |
| 640 | DestroyIcon(mCustomIcon); |
| 641 | DestroyIcon(mCustomIconSmall); // Should always be non-NULL if mCustomIcon is non-NULL. |
| 642 | } |
| 643 | |
| 644 | // Since they're not associated with a window, we must free the resources for all popup menus. |
| 645 | // Update: Even if a menu is being used as a GUI window's menu bar, see note above for why menu |
| 646 | // destruction is done AFTER the GUI windows are destroyed: |
| 647 | for (UserMenu *m = mFirstMenu; m; m = m->mNextMenu) |
| 648 | m->Dispose(); |
| 649 | |
| 650 | // Since tooltip windows are unowned, they should be destroyed to avoid resource leak: |
| 651 | for (i = 0; i < MAX_TOOLTIPS; ++i) |
| 652 | if (g_hWndToolTip[i] && IsWindow(g_hWndToolTip[i])) |
| 653 | DestroyWindow(g_hWndToolTip[i]); |
| 654 | |
| 655 | if (mOnClipboardChange.Count()) // Remove from viewer chain. |
| 656 | EnableClipboardListener(false); |
| 657 | |
| 658 | // Close any open sound item to prevent hang-on-exit in certain operating systems or conditions. |
| 659 | // If there's any chance that a sound was played and not closed out, or that it is still playing, |
| 660 | // this check is done. Otherwise, the check is avoided since it might be a high overhead call, |
| 661 | // especially if the sound subsystem part of the OS is currently swapped out or something: |
| 662 | if (g_SoundWasPlayed) |
| 663 | { |
| 664 | TCHAR buf[MAX_PATH * 2]; // See "MAX_PATH note" in Line::SoundPlay for comments. |
| 665 | mciSendString(_T("status ") SOUNDPLAY_ALIAS _T(" mode"), buf, _countof(buf), NULL); |
| 666 | if (*buf) // "playing" or "stopped" |
| 667 | mciSendString(_T("close ") SOUNDPLAY_ALIAS, NULL, 0, NULL); |
| 668 | } |
| 669 | |
| 670 | DeleteCriticalSection(&g_CriticalRegExCache); // g_CriticalRegExCache is used elsewhere for thread-safety. |
nothing calls this directly
no test coverage detected