mirror of
https://github.com/reactos/reactos.git
synced 2024-11-02 12:53:33 +00:00
380 lines
9.5 KiB
C++
380 lines
9.5 KiB
C++
/*
|
|
* PROJECT: ReactOS system libraries
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: dll/shellext/stobject/csystray.cpp
|
|
* PURPOSE: Systray shell service object implementation
|
|
* PROGRAMMERS: David Quintana <gigaherz@gmail.com>
|
|
* Shriraj Sawant a.k.a SR13 <sr.official@hotmail.com>
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#include <regstr.h>
|
|
#include <undocshell.h>
|
|
#include <shellutils.h>
|
|
|
|
SysTrayIconHandlers_t g_IconHandlers [] = {
|
|
{ VOLUME_SERVICE_FLAG, Volume_Init, Volume_Shutdown, Volume_Update, Volume_Message },
|
|
{ HOTPLUG_SERVICE_FLAG, Hotplug_Init, Hotplug_Shutdown, Hotplug_Update, Hotplug_Message },
|
|
{ POWER_SERVICE_FLAG, Power_Init, Power_Shutdown, Power_Update, Power_Message }
|
|
};
|
|
const int g_NumIcons = _countof(g_IconHandlers);
|
|
|
|
CSysTray::CSysTray() : dwServicesEnabled(0)
|
|
{
|
|
wm_SHELLHOOK = RegisterWindowMessageW(L"SHELLHOOK");
|
|
wm_DESTROYWINDOW = RegisterWindowMessageW(L"CSysTray_DESTROY");
|
|
}
|
|
|
|
CSysTray::~CSysTray()
|
|
{
|
|
}
|
|
|
|
VOID CSysTray::GetServicesEnabled()
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwSize;
|
|
|
|
/* Enable power, volume and hotplug by default */
|
|
this->dwServicesEnabled = POWER_SERVICE_FLAG | VOLUME_SERVICE_FLAG | HOTPLUG_SERVICE_FLAG;
|
|
|
|
if (RegCreateKeyExW(HKEY_CURRENT_USER, REGSTR_PATH_SYSTRAY,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_READ,
|
|
NULL,
|
|
&hKey,
|
|
NULL) == ERROR_SUCCESS)
|
|
{
|
|
dwSize = sizeof(DWORD);
|
|
RegQueryValueExW(hKey,
|
|
L"Services",
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)&this->dwServicesEnabled,
|
|
&dwSize);
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
VOID CSysTray::EnableService(DWORD dwServiceFlag, BOOL bEnable)
|
|
{
|
|
HKEY hKey;
|
|
|
|
if (bEnable)
|
|
this->dwServicesEnabled |= dwServiceFlag;
|
|
else
|
|
this->dwServicesEnabled &= ~dwServiceFlag;
|
|
|
|
if (RegCreateKeyExW(HKEY_CURRENT_USER,
|
|
L"Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\SysTray",
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hKey,
|
|
NULL) == ERROR_SUCCESS)
|
|
{
|
|
RegSetValueExW(hKey,
|
|
L"Services",
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&this->dwServicesEnabled,
|
|
sizeof(DWORD));
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
BOOL CSysTray::IsServiceEnabled(DWORD dwServiceFlag)
|
|
{
|
|
return (this->dwServicesEnabled & dwServiceFlag);
|
|
}
|
|
|
|
HRESULT CSysTray::InitNetShell()
|
|
{
|
|
HRESULT hr = CoCreateInstance(CLSID_ConnectionTray, 0, 1u, IID_PPV_ARG(IOleCommandTarget, &pctNetShell));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
return pctNetShell->Exec(&CGID_ShellServiceObject,
|
|
OLECMDID_NEW,
|
|
OLECMDEXECOPT_DODEFAULT, NULL, NULL);
|
|
}
|
|
|
|
HRESULT CSysTray::ShutdownNetShell()
|
|
{
|
|
if (!pctNetShell)
|
|
return S_FALSE;
|
|
HRESULT hr = pctNetShell->Exec(&CGID_ShellServiceObject,
|
|
OLECMDID_SAVE,
|
|
OLECMDEXECOPT_DODEFAULT, NULL, NULL);
|
|
pctNetShell.Release();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSysTray::InitIcons()
|
|
{
|
|
TRACE("Initializing Notification icons...\n");
|
|
for (int i = 0; i < g_NumIcons; i++)
|
|
{
|
|
if (this->dwServicesEnabled & g_IconHandlers[i].dwServiceFlag)
|
|
{
|
|
HRESULT hr = g_IconHandlers[i].pfnInit(this);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
MouseKeys_Init(this);
|
|
|
|
return InitNetShell();
|
|
}
|
|
|
|
HRESULT CSysTray::ShutdownIcons()
|
|
{
|
|
TRACE("Shutting down Notification icons...\n");
|
|
for (int i = 0; i < g_NumIcons; i++)
|
|
{
|
|
if (this->dwServicesEnabled & g_IconHandlers[i].dwServiceFlag)
|
|
{
|
|
this->dwServicesEnabled &= ~g_IconHandlers[i].dwServiceFlag;
|
|
HRESULT hr = g_IconHandlers[i].pfnShutdown(this);
|
|
FAILED_UNEXPECTEDLY(hr);
|
|
}
|
|
}
|
|
|
|
MouseKeys_Shutdown(this);
|
|
|
|
return ShutdownNetShell();
|
|
}
|
|
|
|
HRESULT CSysTray::UpdateIcons()
|
|
{
|
|
TRACE("Updating Notification icons...\n");
|
|
for (int i = 0; i < g_NumIcons; i++)
|
|
{
|
|
if (this->dwServicesEnabled & g_IconHandlers[i].dwServiceFlag)
|
|
{
|
|
HRESULT hr = g_IconHandlers[i].pfnUpdate(this);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CSysTray::ProcessIconMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
|
|
{
|
|
for (int i = 0; i < g_NumIcons; i++)
|
|
{
|
|
HRESULT hr = g_IconHandlers[i].pfnMessage(this, uMsg, wParam, lParam, lResult);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (hr == S_OK)
|
|
return hr;
|
|
}
|
|
|
|
// Not handled by anyone, so return accordingly.
|
|
return S_FALSE;
|
|
}
|
|
|
|
/*++
|
|
* @name NotifyIcon
|
|
*
|
|
* Basically a Shell_NotifyIcon wrapper.
|
|
* Based on the parameters provided, it changes the current state of the notification icon.
|
|
*
|
|
* @param code
|
|
* Determines whether to add, delete or modify the notification icon (represented by uId).
|
|
* @param uId
|
|
* Represents the particular notification icon.
|
|
* @param hIcon
|
|
* A handle to an icon for the notification object.
|
|
* @param szTip
|
|
* A string for the tooltip of the notification.
|
|
* @param dwstate
|
|
* Determines whether to show or hide the notification icon.
|
|
*
|
|
* @return The error code.
|
|
*
|
|
*--*/
|
|
HRESULT CSysTray::NotifyIcon(INT code, UINT uId, HICON hIcon, LPCWSTR szTip, DWORD dwstate)
|
|
{
|
|
NOTIFYICONDATA nim = { 0 };
|
|
|
|
TRACE("NotifyIcon code=%d, uId=%d, hIcon=%p, szTip=%S\n", code, uId, hIcon, szTip);
|
|
|
|
nim.cbSize = sizeof(nim);
|
|
nim.uFlags = NIF_MESSAGE | NIF_ICON | NIF_STATE | NIF_TIP;
|
|
nim.hIcon = hIcon;
|
|
nim.uID = uId;
|
|
nim.uCallbackMessage = uId;
|
|
nim.dwState = dwstate;
|
|
nim.dwStateMask = NIS_HIDDEN;
|
|
nim.hWnd = m_hWnd;
|
|
nim.uVersion = NOTIFYICON_VERSION;
|
|
if (szTip)
|
|
StringCchCopy(nim.szTip, _countof(nim.szTip), szTip);
|
|
else
|
|
nim.szTip[0] = 0;
|
|
BOOL ret = Shell_NotifyIcon(code, &nim);
|
|
return ret ? S_OK : E_FAIL;
|
|
}
|
|
|
|
DWORD WINAPI CSysTray::s_SysTrayThreadProc(PVOID param)
|
|
{
|
|
CSysTray * st = (CSysTray*) param;
|
|
return st->SysTrayThreadProc();
|
|
}
|
|
|
|
HRESULT CSysTray::SysTrayMessageLoop()
|
|
{
|
|
BOOL ret;
|
|
MSG msg;
|
|
|
|
while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0)
|
|
{
|
|
if (ret < 0)
|
|
break;
|
|
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CSysTray::SysTrayThreadProc()
|
|
{
|
|
WCHAR strFileName[MAX_PATH];
|
|
GetModuleFileNameW(g_hInstance, strFileName, MAX_PATH);
|
|
HMODULE hLib = LoadLibraryW(strFileName);
|
|
|
|
CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED);
|
|
|
|
Create(NULL);
|
|
|
|
HRESULT ret = SysTrayMessageLoop();
|
|
|
|
CoUninitialize();
|
|
|
|
Release();
|
|
FreeLibraryAndExitThread(hLib, ret);
|
|
}
|
|
|
|
HRESULT CSysTray::CreateSysTrayThread()
|
|
{
|
|
TRACE("CSysTray Init TODO: Initialize tray icon handlers.\n");
|
|
AddRef();
|
|
|
|
HANDLE hThread = CreateThread(NULL, 0, s_SysTrayThreadProc, this, 0, NULL);
|
|
|
|
CloseHandle(hThread);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CSysTray::DestroySysTrayWindow()
|
|
{
|
|
if (!DestroyWindow())
|
|
{
|
|
// Window is from another thread, ask it politely to destroy itself:
|
|
SendMessage(wm_DESTROYWINDOW);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// *** IOleCommandTarget methods ***
|
|
HRESULT STDMETHODCALLTYPE CSysTray::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE CSysTray::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
|
|
{
|
|
if (!IsEqualGUID(*pguidCmdGroup, CGID_ShellServiceObject))
|
|
return E_FAIL;
|
|
|
|
switch (nCmdID)
|
|
{
|
|
case OLECMDID_NEW: // init
|
|
return CreateSysTrayThread();
|
|
case OLECMDID_SAVE: // shutdown
|
|
return DestroySysTrayWindow();
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL CSysTray::ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult, DWORD dwMsgMapID)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (hWnd != m_hWnd)
|
|
return FALSE;
|
|
|
|
if (wm_DESTROYWINDOW && uMsg == wm_DESTROYWINDOW)
|
|
{
|
|
return DestroyWindow();
|
|
}
|
|
|
|
if (wm_SHELLHOOK && uMsg == wm_SHELLHOOK)
|
|
{
|
|
if (wParam == HSHELL_ACCESSIBILITYSTATE && lParam == ACCESS_MOUSEKEYS)
|
|
{
|
|
MouseKeys_Update(this);
|
|
}
|
|
lResult = 0L;
|
|
return TRUE;
|
|
}
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_NCCREATE:
|
|
case WM_NCDESTROY:
|
|
return FALSE;
|
|
|
|
case WM_CREATE:
|
|
GetServicesEnabled();
|
|
InitIcons();
|
|
SetTimer(1, 2000, NULL);
|
|
RegisterShellHookWindow(hWnd);
|
|
return TRUE;
|
|
|
|
case WM_TIMER:
|
|
if (wParam == 1)
|
|
UpdateIcons();
|
|
else
|
|
ProcessIconMessage(uMsg, wParam, lParam, lResult);
|
|
return TRUE;
|
|
|
|
case WM_SETTINGCHANGE:
|
|
if (wParam == SPI_SETMOUSEKEYS)
|
|
{
|
|
MouseKeys_Update(this);
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
KillTimer(1);
|
|
DeregisterShellHookWindow(hWnd);
|
|
ShutdownIcons();
|
|
PostQuitMessage(0);
|
|
return TRUE;
|
|
}
|
|
|
|
TRACE("SysTray message received %u (%08p %08p)\n", uMsg, wParam, lParam);
|
|
|
|
hr = ProcessIconMessage(uMsg, wParam, lParam, lResult);
|
|
if (FAILED(hr))
|
|
return FALSE;
|
|
|
|
return (hr == S_OK);
|
|
}
|