/*
 ============================================================================
 xoblite -> an alternative shell based on Blackbox for Windows
 Copyright © 2002-2005 Karl-Henrik Henriksson [qwilk]
 Copyright © 2001-2004 The Blackbox for Windows Development Team
 http://xoblite.net/ - #bb4win on irc.freenode.net
 ============================================================================

  Blackbox for Windows is free software, released under the
  GNU General Public License (GPL version 2 or later), with an extension
  that allows linking of proprietary modules under a controlled interface.
  What this means is that plugins etc. are allowed to be released
  under any license the author wishes. Please note, however, that the
  original Blackbox gradient math code used in Blackbox for Windows
  is available under the BSD license.

  http://www.fsf.org/licenses/gpl.html
  http://www.fsf.org/licenses/gpl-faq.html#LinkingOverControlledInterface
  http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  GNU General Public License for more details.

  For additional license information, please read the included license.html

 ============================================================================

  Note: The WM_COPYDATA section of code in TrayManagerWndProc was inspired by
  traymanager.cpp/.h. The LoadShellServiceObjects and UnloadShellServiceObjects
  functions are direct ports from traymanager.cpp/.h. Traymanager.cpp/.h
  are part of the LiteStep shell code base, which is copyright (C) 1997-2002
  The LiteStep Development Team.

  Special thanks go out to Jaykul and Message. Both of them took time out of
  developing their respective shells to help us understand the way the systray
  is handled by Windows.

 ============================================================================
*/

#include "TrayManager.h" 

const char szTrayManagerName[] = "Shell_TrayWnd"; // Window class etc.
const char szTrayNotifyClass[] = "TrayNotifyWnd";

extern Settings *pSettings;
extern TrayManager *pTrayManager;
extern Systembar *pSystembar;

//===========================================================================

#ifndef NIF_STATE 
#define NIF_STATE               0x00000008 
#endif 
#ifndef NIS_HIDDEN 
#define NIS_HIDDEN              0x00000001 
#endif 

//typedef BOOL (WINAPI *LPFN_ISWOW64MESSAGE) (void);
//LPFN_ISWOW64MESSAGE BBIsWow64Message = NULL;

//===========================================================================

TrayManager::TrayManager(HINSTANCE hInstance, bool DisableTray)
{
        hTrayManagerWnd = NULL;
        hTrayNotifyWnd = NULL;

//      memset(this, 0, sizeof(TrayManager));
        ZeroMemory(this, sizeof(TrayManager));

        // At startup, xoblite checks if the -nosystray switch was used or if it
        // is running on top of Explorer (which unfortunately also locks on to a
        // stand alone systray atm), and if so disables the xoblite internal systray
        if (DisableTray)
        {
                TrayManagerDisabled = true;
                return;
        }
        else TrayManagerDisabled = false;

        hTrayManagerInstance = hInstance;

        ZeroMemory(&trayIconList, sizeof(trayIconList));
        ZeroMemory(&ssoIconList, sizeof(ssoIconList));

        TrayManagerReady = false;
        ShowBalloonTooltip = false;

        //====================
/*
        // Check if running under 64-bit Windows Vista... (for future use)
        // ("IsWow64Message determines if the last message read from
        //   the current thread's queue originated from a WOW64 process")
        BBIsWow64Message = (LPFN_ISWOW64MESSAGE)GetProcAddress(GetModuleHandle("user32"),"IsWow64Message");
        if (BBIsWow64Message != NULL) Log("xoblite|TrayManager", "64-bit Windows Vista detected!");
        else Log("xoblite|TrayManager", "32-bit Windows detected!");
*/
        //====================

        hBlackboxWnd = GetBBWnd();

        // Register our window class
        WNDCLASS wc;
        ZeroMemory(&wc,sizeof(wc));
        wc.lpfnWndProc = TrayManagerWndProc;                                    // our window procedure
        wc.hInstance = hTrayManagerInstance;            
        wc.lpszClassName = szTrayManagerName;                                   // our window class name

        if (!RegisterClass(&wc))
        {
                MessageBox(0, "Error registering window class", szTrayManagerName, MB_OK | MB_ICONERROR | MB_TOPMOST);
                Log("TrayManager: Error registering window class", NULL);
                return;
        }

        hTrayManagerWnd = CreateWindowEx(
                WS_EX_TOOLWINDOW,                                                                       // exstyles 
                szTrayManagerName,                                                                      // our window class name
                NULL,                                                                                           // use description for a window title
                WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                0, 0,                                                                                           // position
                0, 0,                                                                                           // width & height of window
                NULL,                                                                                           // parent window
                NULL,                                                                                           // no menu
                hTrayManagerInstance,                                                           // hInstance of DLL
                NULL);

        if (!hTrayManagerWnd)
        {                                                  
                UnregisterClass(szTrayManagerName,hTrayManagerInstance); // unregister window class
                MessageBox(0, "Error creating window", szTrayManagerName, MB_OK | MB_ICONERROR | MB_TOPMOST);
                Log("TrayManager: Error creating window", NULL);
                TrayManagerDisabled = true;
                return;
        }
}

//===========================================================================

TrayManager::~TrayManager()
{
        if (TrayManagerDisabled) return;

        //====================

        // Go through each of the elements in the trayIcons array clearing each one
        for (int i=0; i < (int)trayIconList.size(); i++)
        {
                DestroyIcon(trayIconList[i]->hIcon);
                delete trayIconList[i];
                trayIconList.erase(trayIconList.begin() + i);
        }

        trayIconList.clear();

        if (COMinitialized) UnloadShellServiceObjects();
//      if (!(GetAsyncKeyState(VK_SHIFT) & 0x8000) && COMinitialized) UnloadShellServiceObjects();

        if (hTrayNotifyWnd)
        {
                DestroyWindow(hTrayNotifyWnd); // delete notification window
                UnregisterClass(szTrayNotifyClass, hTrayManagerInstance); // unregister window class
        }
        DestroyWindow(hTrayManagerWnd); // delete our window
        UnregisterClass(szTrayManagerName, hTrayManagerInstance); // unregister window class

        //====================

        if (pSettings->underExplorer) PostMessage(HWND_BROADCAST, RegisterWindowMessage(TEXT("TaskbarCreated")), 0, 0);
}

//===========================================================================

void TrayManager::Initialize()
{
        if (TrayManagerDisabled) return;

        //====================

        // From now on we allow updates to the system tray...
        TrayManagerReady = true;

        // Courtesy of the LiteStep Development Team (introduced in LS 0.24.7 RC3 (?)):
        // "The pre-XP language indicator, internat.exe, checks the existence of
        //  a window with the window class TrayNotifyWnd on startup. If the window
        //  doesn't exist internat.exe terminates. The window needs to be a child
        //  of Shell_TrayWnd. Internat calculates the position of its context menu
        //  from the position of TrayNotifyWnd."

        if (hTrayManagerWnd)
        {
                WNDCLASS wc;
                ZeroMemory(&wc,sizeof(wc));
                wc.lpfnWndProc = TrayManagerWndProc;
                wc.hInstance = hTrayManagerInstance;            
                wc.lpszClassName = szTrayNotifyClass;
                wc.style = CS_DBLCLKS;
           
                if (RegisterClass(&wc))
                {
                        hTrayNotifyWnd = CreateWindowEx(
                        0,
                        szTrayNotifyClass, NULL,
                        WS_CHILD,
                        0, 0, 0, 0,
                        hTrayManagerWnd, NULL,
                        hTrayManagerInstance,
                        NULL);

                        if (!hTrayNotifyWnd)
                        {                                                  
                                UnregisterClass(szTrayNotifyClass, hTrayManagerInstance); // unregister window class
                                MessageBox(0, "Error creating notification window", szTrayManagerName, MB_OK | MB_ICONERROR | MB_TOPMOST);
                                Log("TrayManager: Error creating notification window", NULL);
                        }
                }
        }

        //====================

        // According to MSDN: "With Internet Explorer 4.0 and later, the Shell notifies applications
        // that the taskbar has been created. When the taskbar is created, it registers a message with
        // the TaskbarCreated string and then broadcasts this message to all top-level windows.
        // When your taskbar application receives this message, it should assume that any taskbar icons
        // it added have been removed and add them again."
        PostMessage(HWND_BROADCAST, RegisterWindowMessage(TEXT("TaskbarCreated")), 0, 0);

        // Check which operating system we are running under...
        ZeroMemory(&osInfo, sizeof(osInfo));
        osInfo.dwOSVersionInfoSize = sizeof(osInfo);
        GetVersionEx(&osInfo);
        if ((osInfo.dwMajorVersion >= 5) && (osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)) usingWin2kXP = true;
        else usingWin2kXP = false;

        //====================

        if (usingWin2kXP && !pSettings->underExplorer) LoadShellServiceObjects();

        //====================
/*
        // Add the xoblite icon to the system tray... =]
        NOTIFYICONDATA xobIconData;
        xobIconData.hIcon = LoadIcon(hTrayManagerInstance, MAKEINTRESOURCE(IDI_XOBLITE));
        strcpy(xobIconData.szTip, "xoblite");
        xobIconData.hWnd = GetBBWnd();
        xobIconData.uID = 0;
        xobIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
        AddTrayIcon(&xobIconData);
*/
}

//===========================================================================

LRESULT CALLBACK TrayManagerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        DWORD trayCommand;
        NOTIFYICONDATA *iconData;
        trayIconItem *icon = NULL;
        bool hidden = false;
        bool shared = false;

        switch (message)
        {
                //====================

                case WM_COPYDATA:
                {
                        if (!pTrayManager->TrayManagerReady) break;

                        PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT)lParam;

                        if (cpData->dwData == 1)
                        {
                                trayCommand = *(DWORD *) (((BYTE *)cpData->lpData) + 4);
                                iconData = (NOTIFYICONDATA *) (((BYTE *)cpData->lpData) + 8);

                                //====================

                                icon = pTrayManager->FindTrayIcon(iconData); // = NULL if not found, otherwise pointer to icon

                                //====================

                                if ((iconData->uFlags & NIF_STATE) && (iconData->cbSize >= sizeof(NOTIFYICONDATAV5)))
                                {
                                        NOTIFYICONDATAV5 *iconDataV5 = (NOTIFYICONDATAV5 *)iconData;

                                        if ((iconDataV5->dwState & iconDataV5->dwStateMask) & NIS_HIDDEN)
                                                hidden = true;
                                        if (((iconDataV5->dwState & iconDataV5->dwStateMask) & NIS_SHAREDICON) && (iconDataV5->uID == 1))
                                                return false;
                                }

                                //====================

                                switch (trayCommand)
                                {
                                        case NIM_ADD: // Icon is being added to the system tray...
                                        {
                                                if (hidden) return false;
                                                else if (!icon) return pTrayManager->AddTrayIcon(iconData);
//                                              else if (icon) return pTrayManager->ModifyTrayIcon(icon, iconData);
                                                else return false;
                                        }
                                        break;

                                        case NIM_MODIFY: // Icon in the system tray is being modified...
                                        {
                                                if (hidden && icon) return pTrayManager->RemoveTrayIcon(icon);
//                                              else if (!icon) return pTrayManager->AddTrayIcon(iconData);
                                                else if (icon) return pTrayManager->ModifyTrayIcon(icon, iconData);
                                                else return false;
                                        }
                                        break;

                                        case NIM_DELETE: // Icon is being removed from the system tray...
                                        {
                                                if (icon) return pTrayManager->RemoveTrayIcon(icon);
                                                else return false;
                                        }
                                        break;
                                }
                        }
                }
                break;

                //====================

                default:
                        return DefWindowProc(hwnd,message,wParam,lParam);

        //====================
        }
        return 0;
}

//===========================================================================
// Function: AddTrayIcon
//===========================================================================

bool TrayManager::AddTrayIcon(NOTIFYICONDATA *iconData)
{
        CleanTray();

        trayIconItem *icon;

        if (!iconData) return false;

        //====================

//      if ((iconData->uFlags & NIF_ICON) && (iconData->uFlags & NIF_MESSAGE))
        if ((iconData->uFlags & NIF_MESSAGE)) // Temporarily skipping the NIF_ICON requirement since Azureus forget to include it...
        {
                if (iconData->uFlags & ~NIF_TIP)
                        PostMessage(iconData->hWnd, iconData->uCallbackMessage, iconData->uID, WM_MOUSEMOVE);

                icon = new trayIconItem;

                icon->uID = iconData->uID;
                icon->hWnd = iconData->hWnd;
                icon->uCallbackMessage = iconData->uCallbackMessage;

                icon->hIcon = CopyIcon(iconData->hIcon);
/*
                if (iconData->hIcon) icon->hIcon = CopyIcon(iconData->hIcon);
                else
                {
                        // If an icon was not specified, we extract a "default" icon
                        // from the HWND just like we do for the taskbar icons... (e.g. Offline Files)
                        HICON tempIcon;
                    SendMessageTimeout(icon->hWnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG|SMTO_BLOCK, 200, (PDWORD_PTR)&tempIcon);
                        if (!tempIcon) tempIcon = (HICON)GetClassLongPtr(icon->hWnd, GCLP_HICONSM);
                        if (!tempIcon) SendMessageTimeout(icon->hWnd, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG|SMTO_BLOCK, 200, (PDWORD_PTR)&tempIcon);
                        if (!tempIcon) tempIcon = (HICON)GetClassLongPtr(icon->hWnd, GCLP_HICON);
                        icon->hIcon = CopyIcon(tempIcon);
                }
*/
                ConvertTip(icon->szTip, (WCHAR *)iconData->szTip);

                //====================

                if (pSettings->systembarXobloonNotifications && (iconData->uFlags & NIF_INFO))
                        CreateXobloonNotification(icon, iconData);

                //====================

                trayIconList.push_back(icon);

                if (!pSettings->accessLock)
                {
                        if (!pSystembar->SysTrayDisabled)
                        {
                                if (!pSettings->systrayHidden)
                                {
                                        // Redraw systray...
                                        pSystembar->cachedTaskButtonsExist = false;
                                        InvalidateRect(pSystembar->hSystembarWnd, NULL, false);
                                }
                                pSystembar->UpdateToolTipPositions(); // Update tooltips and their x/y positions...
                        }
                }

                //====================

//              SendMessage(hBlackboxWnd, BB_TRAYUPDATE, (WPARAM)icon, (LPARAM)TRAYICON_ADDED);
                SendMessage(hBlackboxWnd, BB_TRAYUPDATE, 0, (LPARAM)TRAYICON_ADDED);
        }

        //====================

        return true;
}

//===========================================================================
// Function: ModifyTrayIcon
//===========================================================================

bool TrayManager::ModifyTrayIcon(trayIconItem *icon, NOTIFYICONDATA *iconData)
{
        CleanTray();

        if (!icon || !iconData) return false;

        //====================

        if ((iconData->uFlags & NIF_MESSAGE) && iconData->uCallbackMessage)
        {
                if (icon->uCallbackMessage != iconData->uCallbackMessage)
                {
                        icon->uCallbackMessage = iconData->uCallbackMessage;
                }
        }

        //====================

        if ((iconData->uFlags & NIF_ICON) && iconData->hIcon)
        {
                if (!iconData->hIcon) return false;

                if (icon->hIcon != iconData->hIcon)
                {
                        if (icon->hIcon) DestroyIcon(icon->hIcon);
                        icon->hIcon = CopyIcon(iconData->hIcon);

                        if (!pSettings->accessLock)
                        {
                                // Redraw the systray to display the updated icon...
                                if (!pSystembar->SysTrayDisabled && !pSettings->systrayHidden)
                                {
                                        InvalidateRect(pSystembar->hSystembarWnd, &icon->iconRect, false);
                                }
                        }
                }

                SendMessage(hBlackboxWnd, BB_TRAYUPDATE, (WPARAM)icon, (LPARAM)TRAYICON_MODIFIED);
        }

        //====================

        if ((iconData->uFlags & NIF_TIP) && iconData->szTip)
        {
                char newTip[256];
                ConvertTip(newTip, (WCHAR *)iconData->szTip);
                if (stricmp(icon->szTip, newTip)) strcpy(icon->szTip, newTip);

                if (!pSettings->accessLock)
                {
                        // Update the tooltip...
                        if (!pSystembar->SysTrayDisabled)
                        {
                                pSystembar->UpdateToolTipPositions();
                        }
                }

                SendMessage(hBlackboxWnd, BB_TRAYUPDATE, (WPARAM)icon, (LPARAM)TRAYICON_MODIFIED);
        }

        //====================

        if (pSettings->systembarXobloonNotifications && (iconData->uFlags & NIF_INFO))
                CreateXobloonNotification(icon, iconData);

        //====================

        return true;
}

//===========================================================================
// Function: RemoveTrayIcon
//===========================================================================

bool TrayManager::RemoveTrayIcon(trayIconItem *icon)
{
        CleanTray();

        //====================

        // Traverse the trayIcons array looking for the icon...
        for (int i=0; i < (int)trayIconList.size(); i++)
        {
                if (trayIconList[i]->hWnd == icon->hWnd && trayIconList[i]->uID == icon->uID)
                {
                        DestroyIcon(trayIconList[i]->hIcon);
                        delete trayIconList[i];
                        trayIconList.erase(trayIconList.begin() + i);

                        if (!pSettings->accessLock)
                        {
                                if (!pSystembar->SysTrayDisabled)
                                {
                                        if (!pSettings->systrayHidden)
                                        {
                                                pSystembar->cachedTaskButtonsExist = false;
                                                InvalidateRect(pSystembar->hSystembarWnd, NULL, false);
                                        }
                                        pSystembar->UpdateToolTipPositions(); // Update tooltip x/y positions...
                                }
                        }

//                      SendMessage(hBlackboxWnd, BB_TRAYUPDATE, (WPARAM)trayIconList[i], (LPARAM)TRAYICON_REMOVED);
                        SendMessage(hBlackboxWnd, BB_TRAYUPDATE, 0, (LPARAM)TRAYICON_REMOVED);

                        return true;
                }
        }

        //====================

        return false;
}

//===========================================================================
// Function: FindTrayIcon
//===========================================================================

trayIconItem* TrayManager::FindTrayIcon(NOTIFYICONDATA *iconData)
{
        // Traverse the trayIcons array..
        for (int i=0; i < (int)trayIconList.size(); i++)        
        {
                // If the window and uID match an entry in the trayIcons array, return its position...
                if (trayIconList[i]->hWnd == iconData->hWnd && trayIconList[i]->uID == iconData->uID)
                {
                        return trayIconList[i];
                }
        }

        return NULL;
}


//===========================================================================
// Function: CreateXobloonNotification
//===========================================================================

bool TrayManager::CreateXobloonNotification(trayIconItem *icon, NOTIFYICONDATA *iconData)
{
        // The xoblite style balloon tooltips replacement - xobloon notifications! =]

        if (iconData->cbSize >= sizeof(NOTIFYICONDATAV5))
        {
                // Are we already showing an xBalloon tooltip?
                if (pTrayManager->ShowBalloonTooltip)
                {
                        // If so, we dismiss it and inform the parent application that
                        // the balloon tooltip has been dismissed because of timing out...
                        // (balloon tooltips should ideally be queued, but the chance/risk
                        // of two balloon tooltips popping up at the same time is low,
                        // and I am lazy atm... =] )

                        KillTimer(pSystembar->hSystembarWnd, SYSTEMBAR_XOBLOON_TIMER);
                        if (pTrayManager->BalloonTooltipItem.hWnd != NULL)
                        {
                                PostMessage(pTrayManager->BalloonTooltipItem.hWnd, pTrayManager->BalloonTooltipItem.uCallbackMessage, pTrayManager->BalloonTooltipItem.uID, NIN_BALLOONTIMEOUT);
                        }
                }

                //====================

                NOTIFYICONDATAV5 *iconDataV5 = (NOTIFYICONDATAV5 *)iconData;

                pTrayManager->ConvertTip(icon->szInfo, (WCHAR *)iconDataV5->szInfo);
                pTrayManager->ConvertTip(icon->szInfoTitle, (WCHAR *)iconDataV5->szInfoTitle);
                icon->uTimeout = iconDataV5->uTimeout;
                icon->dwInfoFlags = iconDataV5->dwInfoFlags;

                // Filter out any "empty" tooltips...
                if (!strlen(icon->szInfo) && !strlen(icon->szInfoTitle)) return false;

                pTrayManager->BalloonTooltipItem.uID = icon->uID;
                pTrayManager->BalloonTooltipItem.hWnd = icon->hWnd;
                pTrayManager->BalloonTooltipItem.uCallbackMessage = icon->uCallbackMessage;

                if (icon->dwInfoFlags == NIIF_ERROR) pTrayManager->BalloonTooltipIcon = IDI_ERROR;
                else if (icon->dwInfoFlags == NIIF_WARNING) pTrayManager->BalloonTooltipIcon = IDI_WARNING;
                else pTrayManager->BalloonTooltipIcon = IDI_INFORMATION;

                // Show xobloon notification...
//              if (pSettings->systembarHidden) PostMessage(GetBBWnd(), BB_TOGGLESYSTEMBAR, 0, 0);
                sprintf(pTrayManager->BalloonTooltipText, "xobloon notification -> %s -> %s", icon->szInfoTitle, icon->szInfo);
                pTrayManager->ShowBalloonTooltip = true;
                if (!pSettings->accessLock) InvalidateRect(pSystembar->hSystembarWnd, NULL, false);

                // Set xobloon timeout...
                SetTimer(pSystembar->hSystembarWnd, SYSTEMBAR_XOBLOON_TIMER, icon->uTimeout, (TIMERPROC)NULL);
        }

        return true;
}

//===========================================================================
// Function: CleanTray
//===========================================================================

void TrayManager::CleanTray()
{
        // Go through each of the elements in the trayIcons array
        for (int i=0; i < (int)trayIconList.size(); i++)
        { 
                // If the icon does not have a valid window handle,
                // reset everything except the x and y values of the icon
                if (!IsWindow(trayIconList[i]->hWnd))
                {
                        DestroyIcon(trayIconList[i]->hIcon);
                        delete trayIconList[i];
                        trayIconList.erase(trayIconList.begin() + i);
                }
        }
}

//===========================================================================
// Function: GetTraySize
//===========================================================================

int TrayManager::GetTraySize()
{
        return trayIconList.size();
}

//===========================================================================
// Function: GetTrayIcon
//===========================================================================

trayIconItem* TrayManager::GetTrayIcon(int pointer)
{
        return trayIconList[pointer];
}

//===========================================================================
// Function: ConvertTip
//===========================================================================

bool TrayManager::ConvertTip(char *destination, WCHAR *tip)
{
        if (usingWin2kXP)
        {
                // Convert from unicode
//              UINT nChars = min( UINT( wcslen( tip ) + 1 ), UINT(256) );
//              WideCharToMultiByte(CP_ACP, 0, tip, nChars, destination, nChars, NULL, NULL);
                destination[0] = '\0';
                WideCharToMultiByte(CP_ACP, 0, tip, -1, destination, 256, NULL, NULL);
                destination[255] = '\0';
        }
        else
        {
//              UINT nChars = min(strlen((char *)tip), 256);
//              strncpy(destination, (char *)tip, nChars);
                strncpy(destination, (char *)tip, 256);
                destination[255] = '\0';
        }

        return true;
}

//===========================================================================
// Function: LoadShellServiceObjects
// Purpose: Initializes COM, then starts all ShellServiceObjects listed in
//          the ShellServiceObjectDelayLoad registry key, e.g. Volume,
//          Power Management, PCMCIA, etc.
// In: void
// Out: void
//===========================================================================

void TrayManager::LoadShellServiceObjects()
{
        HKEY hkeyServices;
        LONG lErrorCode;
        int i = 0;

        // CoInitializeEx "initializes the COM library for use by the calling thread, sets the thread's
        // concurrency model, and creates a new apartment for the thread if one is required."
        HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE | COINIT_SPEED_OVER_MEMORY);
        //HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
        if (FAILED(hr)) COMinitialized = false;
        else COMinitialized = true;

        if (!COMinitialized)
        {
                MessageBox(GetDesktopWindow(), "COM initialization failed", "Tray", MB_OK | MB_ICONERROR | MB_TOPMOST);
                Log("Tray: COM initialization failed", NULL);
                return;
        }
        else
        {
                // Enumerate the registry key values (one indexed value name
                // and data block is copied each time RegOpenKeyEx is called)
                lErrorCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
                        "Software\\Microsoft\\Windows\\CurrentVersion\\ShellServiceObjectDelayLoad",
                        0, KEY_READ, &hkeyServices);

                while (lErrorCode == ERROR_SUCCESS)
                {
                        char szValueName[32];
                        char szData[40];
                        DWORD cbValueName = 32;
                        DWORD cbData = 40;
                        DWORD dwDataType;

                        lErrorCode = RegEnumValue(hkeyServices, i,
                                szValueName, &cbValueName, 0,
                                &dwDataType, (LPBYTE)szData, &cbData);

                        //====================
/*
                        // ##### FOR DEBUG PURPOSES ONLY! #####

                        if (pSettings->filterSSO && lErrorCode == ERROR_SUCCESS)
                        {
                                char msg[MAX_LINE_LENGTH];
                                sprintf(msg, "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\ShellServiceObjectDelayLoad\\%s   \n%s   \n\nDo you want to execute this ShellServiceObjectDelayLoad item?   ", szValueName, szData);

                                if (MessageBox(GetDesktopWindow(), msg, "xoblite", MB_YESNO | MB_ICONQUESTION | MB_SETFOREGROUND | MB_TOPMOST) == IDNO)
                                {
                                        i++;
                                        continue;
                                }
                        }
*/
                        //====================

                        if (lErrorCode == ERROR_SUCCESS)
                        {
                                WCHAR wszCLSID[40];
                                CLSID clsid;
                                IOleCommandTarget *pCmdTarget;

                                MultiByteToWideChar(CP_ACP, 0, szData, cbData, wszCLSID, 40);

                                CLSIDFromString(wszCLSID, &clsid);

                                hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
                                        IID_IOleCommandTarget, (void **) & pCmdTarget);

                                if (SUCCEEDED(hr))
                                {
                                        // Open ShellServiceObject...
                                        pCmdTarget->Exec(&CGID_ShellServiceObject,
                                                2, // start
                                                0,
                                                NULL, NULL);

                                        ssoIconList.push_back(pCmdTarget);
                                }
                        }
                        i++;
                }

                RegCloseKey(hkeyServices);
        }
}

//===========================================================================
// Function: UnloadShellServiceObjects
// Purpose: Stops each of the ShellServiceObjects. Once that's complete,
//          it clears the array and unitializes COM.
// In: void
// Out: void
//===========================================================================

void TrayManager::UnloadShellServiceObjects()
{
        // Go through each element of the array and stop it...
        for (int i = 0; i < (int)ssoIconList.size(); i++)
        {
                ssoIconList[i]->Exec(&CGID_ShellServiceObject,
                        3, // stop
                        0,
                        NULL, NULL);
                ssoIconList[i]->Release();
        }

        // CoUninitialize "closes the COM library on the current thread, unloads all DLLs loaded by the thread,
        // frees any other resources that the thread maintains, and forces all RPC connections on the thread to close."
        if (COMinitialized) CoUninitialize();

        COMinitialized = false;
}

//===========================================================================





syntax highlighting by

w e b c p p
web c plus plus