You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
641 lines
18 KiB
641 lines
18 KiB
#include "stdafx.h"
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <wtsapi32.h>
|
|
#include <faxreg.h>
|
|
#include "systray.h"
|
|
|
|
#ifndef FAX_SYS_TRAY_DLL
|
|
#define FAX_SYS_TRAY_DLL TEXT("fxsst.dll") // Fax notification bar DLL (loaded by STObject.dll)
|
|
#define IS_FAX_MSG_PROC "IsFaxMessage" // Fax message handler (used by GetProcAddress)
|
|
typedef BOOL (*PIS_FAX_MSG_PROC)(PMSG); // IsFaxMessage type
|
|
#define FAX_MONITOR_SHUTDOWN_PROC "FaxMonitorShutdown"// Fax monitor shutdown (used by GetProcAddress)
|
|
typedef BOOL (*PFAX_MONITOR_SHUTDOWN_PROC)(); // FaxMonitorShutdown type
|
|
#endif
|
|
|
|
|
|
// Global instance handle of this application.
|
|
HINSTANCE g_hInstance;
|
|
|
|
DWORD g_uiShellHook; //shell hook window message
|
|
|
|
// Global handle to VxDs
|
|
HANDLE g_hPCCARD = INVALID_HANDLE_VALUE;
|
|
|
|
static UINT g_uEnabledSvcs = 0;
|
|
|
|
// Context sensitive help array used by the WinHelp engine.
|
|
extern const DWORD g_ContextMenuHelpIDs[];
|
|
|
|
UINT g_msg_winmm_devicechange = 0;
|
|
|
|
DWORD g_msgTaskbarCreated;
|
|
LRESULT CALLBACK SysTrayWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);
|
|
|
|
HMODULE g_hFaxLib = NULL;
|
|
PIS_FAX_MSG_PROC g_pIsFaxMessage = NULL;
|
|
PFAX_MONITOR_SHUTDOWN_PROC g_pFaxMonitorShutdown = NULL;
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
* Turns the specified service on or off depending upon the value in
|
|
* fEnable and writes the new value to the registry.
|
|
*
|
|
* PARAMETERS:
|
|
* (returns), Mask of all currently enabled services.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
UINT EnableService(UINT uNewSvcMask, BOOL fEnable)
|
|
{
|
|
HKEY hk;
|
|
UINT uCurSvcMask;
|
|
DWORD cb;
|
|
uCurSvcMask = STSERVICE_ALL; // Enable all standard serivces
|
|
|
|
// Disable volume on clean install
|
|
uCurSvcMask &= ~STSERVICE_VOLUME;
|
|
|
|
if (RegCreateKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_SYSTRAY, 0, NULL,
|
|
REG_OPTION_NON_VOLATILE, KEY_READ | KEY_SET_VALUE, NULL, &hk, NULL) == ERROR_SUCCESS)
|
|
{
|
|
cb = sizeof(uCurSvcMask);
|
|
RegQueryValueEx(hk, REGSTR_VAL_SYSTRAYSVCS, NULL, NULL, (LPBYTE)&uCurSvcMask, &cb);
|
|
|
|
if (uNewSvcMask)
|
|
{
|
|
if (fEnable)
|
|
{
|
|
uCurSvcMask |= uNewSvcMask;
|
|
}
|
|
else
|
|
{
|
|
uCurSvcMask &= ~uNewSvcMask;
|
|
}
|
|
|
|
RegSetValueEx(hk, REGSTR_VAL_SYSTRAYSVCS, 0, REG_DWORD, (LPSTR)&uCurSvcMask, sizeof(uCurSvcMask));
|
|
}
|
|
|
|
RegCloseKey(hk);
|
|
}
|
|
|
|
return(uCurSvcMask & STSERVICE_ALL);
|
|
}
|
|
|
|
|
|
//
|
|
// Closes file handles IFF the global variable != INVALID_HANDLE_VALUE
|
|
//
|
|
void CloseIfOpen(LPHANDLE lph)
|
|
{
|
|
if (*lph != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(*lph);
|
|
*lph = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
|
|
// From stobject.cpp
|
|
void StartNetShell();
|
|
void StopNetShell();
|
|
|
|
// if lpCmdLine contains an integer value then we'll enable that service
|
|
|
|
STDAPI_(int) SysTrayMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpszCmdLine, int nCmdShow)
|
|
{
|
|
HWND hExistWnd = FindWindow(SYSTRAY_CLASSNAME, NULL);
|
|
UINT iEnableServ = StrToInt(lpszCmdLine);
|
|
|
|
CoInitializeEx (NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED);
|
|
|
|
g_hInstance = hInstance;
|
|
g_uiShellHook = 0;
|
|
g_msg_winmm_devicechange = RegisterWindowMessage(TEXT("winmm_devicechange"));
|
|
|
|
if (hExistWnd)
|
|
{
|
|
// NOTE: Send an enable message even if the command line parameter
|
|
// is 0 to force us to re-check for all enabled services.
|
|
SendMessage(hExistWnd, STWM_ENABLESERVICE, iEnableServ, TRUE);
|
|
}
|
|
else
|
|
{
|
|
WNDCLASSEX wc;
|
|
|
|
// Register a window class for the Battery Meter. This is done so that
|
|
// the power control panel applet has the ability to detect us and turn us
|
|
// off if we're running.
|
|
|
|
wc.cbSize = sizeof(wc);
|
|
wc.style = CS_GLOBALCLASS;
|
|
wc.lpfnWndProc = SysTrayWndProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = DLGWINDOWEXTRA;
|
|
wc.hInstance = hInstance;
|
|
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_BATTERYPLUG));
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = SYSTRAY_CLASSNAME;
|
|
wc.hIconSm = NULL;
|
|
|
|
if (RegisterClassEx(&wc))
|
|
{
|
|
MSG Msg;
|
|
// Create the Battery Meter and get this thing going!!!
|
|
HWND hWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_BATTERYMETER), NULL, NULL);
|
|
|
|
g_msgTaskbarCreated = RegisterWindowMessage(L"TaskbarCreated");
|
|
|
|
// Ensure we're always running the CSC "service" on Win2000.
|
|
// CSC won't work without it.
|
|
//
|
|
//
|
|
// Ensure we're always running the hotplug "service" on Win2000.
|
|
//
|
|
iEnableServ |= (STSERVICE_CSC | STSERVICE_HOTPLUG);
|
|
|
|
// create the timer that will delay the startup of the fax code.
|
|
SetTimer( hWnd, FAX_STARTUP_TIMER_ID, 20 * 1000, NULL );
|
|
|
|
// create the timer that will delay the startup of the print tray code.
|
|
SetTimer( hWnd, PRINT_STARTUP_TIMER_ID, 20 * 1000, NULL );
|
|
|
|
//
|
|
// This message will initialize all existing services if iEnableServ
|
|
// is 0, so it's used to do the general initialization as well as to
|
|
// enable a new service via the command line.
|
|
//
|
|
SendMessage(hWnd, STWM_ENABLESERVICE, iEnableServ, TRUE);
|
|
|
|
|
|
// Whistler runs NETSHELL in the thread of the systray
|
|
StartNetShell();
|
|
|
|
while (GetMessage(&Msg, NULL, 0, 0))
|
|
{
|
|
if(g_pIsFaxMessage && g_pIsFaxMessage(&Msg))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!IsDialogMessage(hWnd, &Msg) &&
|
|
!CSC_MsgProcess(&Msg))
|
|
{
|
|
TranslateMessage(&Msg);
|
|
DispatchMessage(&Msg);
|
|
}
|
|
}
|
|
// Whistler runs NETSHELL in the thread of the systray
|
|
StopNetShell();
|
|
}
|
|
CloseIfOpen(&g_hPCCARD);
|
|
}
|
|
CoUninitialize();
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* UpdateServices
|
|
*
|
|
* DESCRIPTION:
|
|
* Enables or disables all services specified by the uEnabled mask.
|
|
*
|
|
* PARAMETERS:
|
|
* (returns), TRUE if any service wants to remain resident.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
BOOL UpdateServices(HWND hWnd, UINT uEnabled)
|
|
{
|
|
BOOL bAnyEnabled = FALSE;
|
|
|
|
g_uEnabledSvcs = uEnabled;
|
|
bAnyEnabled |= CSC_CheckEnable(hWnd, uEnabled & STSERVICE_CSC);
|
|
bAnyEnabled |= Power_CheckEnable(hWnd, uEnabled & STSERVICE_POWER);
|
|
bAnyEnabled |= HotPlug_CheckEnable(hWnd, uEnabled & STSERVICE_HOTPLUG);
|
|
bAnyEnabled |= Volume_CheckEnable(hWnd, uEnabled & STSERVICE_VOLUME);
|
|
bAnyEnabled |= USBUI_CheckEnable(hWnd, uEnabled & STSERVICE_USBUI);
|
|
|
|
//
|
|
// now check accessibility features
|
|
//
|
|
|
|
bAnyEnabled |= StickyKeys_CheckEnable(hWnd);
|
|
bAnyEnabled |= MouseKeys_CheckEnable(hWnd);
|
|
bAnyEnabled |= FilterKeys_CheckEnable(hWnd);
|
|
|
|
// register to listen for SHChangeNotify events, so if somebody prints a job
|
|
// we start the print tray code before the kick off timer.
|
|
Print_SHChangeNotify_Register(hWnd);
|
|
|
|
return(bAnyEnabled);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* SysTrayWndProc
|
|
*
|
|
* DESCRIPTION:
|
|
* Callback procedure for the BatteryMeter window.
|
|
*
|
|
* PARAMETERS:
|
|
* hWnd, handle of BatteryMeter window.
|
|
* Message,
|
|
* wParam,
|
|
* lParam,
|
|
* (returns),
|
|
*
|
|
*******************************************************************************/
|
|
|
|
LRESULT CALLBACK SysTrayWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
|
|
if (g_uiShellHook && Message == g_uiShellHook) // NT5: 406505 shellhook for MouseKeys
|
|
{
|
|
switch (wParam)
|
|
{
|
|
case HSHELL_ACCESSIBILITYSTATE:
|
|
switch (lParam)
|
|
{
|
|
case ACCESS_STICKYKEYS:
|
|
StickyKeys_CheckEnable(hWnd);
|
|
break;
|
|
|
|
case ACCESS_MOUSEKEYS:
|
|
MouseKeys_CheckEnable(hWnd);
|
|
break;
|
|
// Since we only enable the shellhook when MouseKeys or StickKeys is on, we should only get that msg
|
|
// case ACCESS_FILTERKEYS:
|
|
// FilterKeys_CheckEnable(hWnd);
|
|
// break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
if (Message == g_msg_winmm_devicechange)
|
|
{
|
|
if (g_uEnabledSvcs & STSERVICE_VOLUME)
|
|
{
|
|
Volume_WinMMDeviceChange(hWnd);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
switch (Message)
|
|
{
|
|
case WM_CREATE:
|
|
WTSRegisterSessionNotification(hWnd, NOTIFY_FOR_THIS_SESSION);
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
Power_OnCommand(hWnd, wParam, lParam);
|
|
break;
|
|
|
|
case STWM_NOTIFYPOWER:
|
|
Power_Notify(hWnd, wParam, lParam);
|
|
break;
|
|
|
|
case STWM_NOTIFYUSBUI:
|
|
USBUI_Notify(hWnd, wParam, lParam);
|
|
break;
|
|
|
|
case STWM_NOTIFYHOTPLUG:
|
|
HotPlug_Notify(hWnd, wParam, lParam);
|
|
break;
|
|
|
|
case STWM_NOTIFYSTICKYKEYS:
|
|
StickyKeys_Notify(hWnd, wParam, lParam);
|
|
break;
|
|
|
|
case STWM_NOTIFYMOUSEKEYS:
|
|
MouseKeys_Notify(hWnd, wParam, lParam);
|
|
break;
|
|
|
|
case STWM_NOTIFYFILTERKEYS:
|
|
FilterKeys_Notify(hWnd, wParam, lParam);
|
|
break;
|
|
|
|
case STWM_NOTIFYVOLUME:
|
|
Volume_Notify(hWnd, wParam, lParam);
|
|
break;
|
|
|
|
case STWM_ENABLESERVICE:
|
|
UpdateServices(hWnd, EnableService((UINT)wParam, (BOOL)lParam));
|
|
break;
|
|
|
|
case STWM_GETSTATE:
|
|
return((BOOL)(g_uEnabledSvcs & (UINT)wParam));
|
|
|
|
case MM_MIXM_CONTROL_CHANGE:
|
|
Volume_ControlChange(hWnd, (HMIXER)wParam, (DWORD)lParam);
|
|
break;
|
|
|
|
case MM_MIXM_LINE_CHANGE:
|
|
Volume_LineChange(hWnd, (HMIXER)wParam, (DWORD)lParam);
|
|
break;
|
|
|
|
case WM_ACTIVATE:
|
|
if (Power_OnActivate(hWnd, wParam, lParam))
|
|
{
|
|
break;
|
|
}
|
|
return DefWindowProc(hWnd, Message, wParam, lParam);
|
|
|
|
case WM_TIMER:
|
|
switch (wParam)
|
|
{
|
|
|
|
case VOLUME_TIMER_ID:
|
|
Volume_Timer(hWnd);
|
|
break;
|
|
|
|
case POWER_TIMER_ID:
|
|
Power_Timer(hWnd);
|
|
break;
|
|
|
|
case HOTPLUG_TIMER_ID:
|
|
HotPlug_Timer(hWnd);
|
|
break;
|
|
|
|
case USBUI_TIMER_ID:
|
|
USBUI_Timer(hWnd);
|
|
break;
|
|
|
|
case HOTPLUG_DEVICECHANGE_TIMERID:
|
|
HotPlug_DeviceChangeTimer(hWnd);
|
|
break;
|
|
case FAX_STARTUP_TIMER_ID:
|
|
KillTimer(hWnd, FAX_STARTUP_TIMER_ID);
|
|
if (NULL == g_hFaxLib)
|
|
{
|
|
g_hFaxLib = LoadLibrary(FAX_SYS_TRAY_DLL);
|
|
|
|
g_pIsFaxMessage = NULL;
|
|
g_pFaxMonitorShutdown = NULL;
|
|
if(g_hFaxLib)
|
|
{
|
|
g_pIsFaxMessage = (PIS_FAX_MSG_PROC)GetProcAddress(g_hFaxLib, IS_FAX_MSG_PROC);
|
|
g_pFaxMonitorShutdown = (PFAX_MONITOR_SHUTDOWN_PROC)GetProcAddress(g_hFaxLib, FAX_MONITOR_SHUTDOWN_PROC);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case PRINT_STARTUP_TIMER_ID:
|
|
KillTimer(hWnd, PRINT_STARTUP_TIMER_ID);
|
|
Print_TrayInit();
|
|
break;
|
|
|
|
case FAX_SHUTDOWN_TIMER_ID:
|
|
{
|
|
if (g_hFaxLib)
|
|
{
|
|
if (g_pFaxMonitorShutdown)
|
|
{
|
|
g_pFaxMonitorShutdown();
|
|
}
|
|
FreeLibrary (g_hFaxLib);
|
|
g_hFaxLib = NULL;
|
|
g_pIsFaxMessage = NULL;
|
|
g_pFaxMonitorShutdown = NULL;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Handle SC_CLOSE to hide the window without destroying it. This
|
|
// happens when we display the window and the user "closes" it.
|
|
// Don't pass SC_CLOSE to DefWindowProc since that causes a
|
|
// WM_CLOSE which destroys the window.
|
|
//
|
|
// Note that CSysTray::DestroySysTrayWindow must send WM_CLOSE
|
|
// to destroy the window. It can't use DestroyWindow since it's
|
|
// typically on a different thread and DestroyWindow fails.
|
|
//
|
|
case WM_SYSCOMMAND:
|
|
if (SC_CLOSE != (wParam & ~0xf))
|
|
return DefWindowProc(hWnd, Message, wParam, lParam);
|
|
ShowWindow(hWnd, SW_HIDE);
|
|
break;
|
|
|
|
case WM_POWERBROADCAST:
|
|
Power_OnPowerBroadcast(hWnd, wParam, lParam);
|
|
Volume_HandlePowerBroadcast(hWnd, wParam, lParam);
|
|
break;
|
|
|
|
case WM_DEVICECHANGE:
|
|
Power_OnDeviceChange(hWnd, wParam, lParam);
|
|
|
|
if (g_uEnabledSvcs & STSERVICE_VOLUME)
|
|
{
|
|
Volume_DeviceChange(hWnd, wParam, lParam);
|
|
}
|
|
|
|
HotPlug_DeviceChange(hWnd, wParam, lParam);
|
|
break;
|
|
|
|
case WM_ENDSESSION:
|
|
if (g_uEnabledSvcs & STSERVICE_VOLUME)
|
|
{
|
|
Volume_Shutdown(hWnd);
|
|
}
|
|
break;
|
|
|
|
case WM_WTSSESSION_CHANGE:
|
|
HotPlug_SessionChange(hWnd, wParam, wParam);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
WTSUnRegisterSessionNotification(hWnd);
|
|
UpdateServices(hWnd, 0); // Force all services off
|
|
Volume_WmDestroy(hWnd);
|
|
Power_WmDestroy(hWnd);
|
|
HotPlug_WmDestroy(hWnd);
|
|
Print_SHChangeNotify_Unregister();
|
|
Print_TrayExit();
|
|
StopNetShell();
|
|
if (g_hFaxLib)
|
|
{
|
|
if (g_pFaxMonitorShutdown)
|
|
{
|
|
g_pFaxMonitorShutdown();
|
|
}
|
|
FreeLibrary (g_hFaxLib);
|
|
g_hFaxLib = NULL;
|
|
g_pIsFaxMessage = NULL;
|
|
g_pFaxMonitorShutdown = NULL;
|
|
}
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
case WM_HELP:
|
|
WinHelp(((LPHELPINFO)lParam)->hItemHandle, NULL, HELP_WM_HELP,
|
|
(ULONG_PTR)(LPSTR)g_ContextMenuHelpIDs);
|
|
break;
|
|
|
|
case WM_CONTEXTMENU:
|
|
WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
|
|
(ULONG_PTR)(LPSTR) g_ContextMenuHelpIDs);
|
|
break;
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
StickyKeys_CheckEnable(hWnd);
|
|
FilterKeys_CheckEnable(hWnd);
|
|
MouseKeys_CheckEnable(hWnd);
|
|
break;
|
|
|
|
|
|
case WM_SETTINGCHANGE:
|
|
switch(wParam)
|
|
{
|
|
case SPI_SETSTICKYKEYS:
|
|
StickyKeys_CheckEnable(hWnd);
|
|
break;
|
|
case SPI_SETFILTERKEYS:
|
|
FilterKeys_CheckEnable(hWnd);
|
|
break;
|
|
case SPI_SETMOUSEKEYS:
|
|
MouseKeys_CheckEnable(hWnd);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_PRINT_NOTIFY:
|
|
Print_Notify(hWnd, Message, wParam, lParam);
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// if Taskbar Created notification renenable all shell notify icons.
|
|
//
|
|
|
|
if (Message == g_msgTaskbarCreated)
|
|
{
|
|
UpdateServices(hWnd, EnableService(0, TRUE));
|
|
break;
|
|
}
|
|
|
|
|
|
return DefWindowProc(hWnd, Message, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Loads the specified string ID and executes it.
|
|
|
|
void SysTray_RunProperties(UINT RunStringID)
|
|
{
|
|
LPTSTR pszRunCmd = LoadDynamicString(RunStringID);
|
|
if (pszRunCmd)
|
|
{
|
|
TCHAR szRunDllPath[MAX_PATH];
|
|
TCHAR szRunDll[] = TEXT("rundll32.exe");
|
|
|
|
if (GetSystemDirectory(szRunDllPath, ARRAYSIZE(szRunDllPath)) &&
|
|
PathAppend(szRunDllPath, szRunDll))
|
|
{
|
|
ShellExecute(NULL, TEXT("open"), szRunDllPath, pszRunCmd, NULL, SW_SHOWNORMAL);
|
|
}
|
|
DeleteDynamicString(pszRunCmd);
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* SysTray_NotifyIcon
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* PARAMETERS:
|
|
* hWnd, handle of BatteryMeter window.
|
|
* Message,
|
|
* hIcon,
|
|
* lpTip,
|
|
*
|
|
*******************************************************************************/
|
|
|
|
VOID SysTray_NotifyIcon(HWND hWnd, UINT uCallbackMessage, DWORD Message, HICON hIcon, LPCTSTR lpTip)
|
|
{
|
|
NOTIFYICONDATA nid = {0};
|
|
|
|
nid.cbSize = sizeof(nid);
|
|
nid.uID = uCallbackMessage;
|
|
nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
|
|
nid.uCallbackMessage = uCallbackMessage;
|
|
|
|
nid.hWnd = hWnd;
|
|
nid.hIcon = hIcon;
|
|
if (lpTip)
|
|
{
|
|
StrCpyN(nid.szTip, lpTip, ARRAYSIZE(nid.szTip));
|
|
}
|
|
else
|
|
{
|
|
nid.szTip[0] = 0;
|
|
}
|
|
|
|
Shell_NotifyIcon(Message, &nid);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* DESCRIPTION:
|
|
* Wrapper for the FormatMessage function that loads a string from our
|
|
* resource table into a dynamically allocated buffer, optionally filling
|
|
* it with the variable arguments passed.
|
|
*
|
|
* BE CAREFUL in 16-bit code to pass 32-bit quantities for the variable
|
|
* arguments.
|
|
*
|
|
* PARAMETERS:
|
|
* StringID, resource identifier of the string to use.
|
|
* (optional), parameters to use to format the string message.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
LPTSTR CDECL LoadDynamicString(UINT StringID, ...)
|
|
{
|
|
TCHAR Buffer[256];
|
|
LPTSTR pStr=NULL;
|
|
va_list Marker;
|
|
|
|
// va_start is a macro...it breaks when you use it as an assign
|
|
va_start(Marker, StringID);
|
|
|
|
LoadString(g_hInstance, StringID, Buffer, ARRAYSIZE(Buffer));
|
|
|
|
FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
|
(void *) (LPTSTR) Buffer, 0, 0, (LPTSTR) (LPTSTR *) &pStr, 0, &Marker);
|
|
|
|
return pStr;
|
|
}
|
|
|
|
|
|
|
|
VOID SetIconFocus(HWND hwnd, UINT uiIcon)
|
|
{
|
|
NOTIFYICONDATA nid = {0};
|
|
|
|
nid.cbSize = sizeof(NOTIFYICONDATA);
|
|
nid.hWnd = hwnd;
|
|
nid.uID = uiIcon;
|
|
|
|
Shell_NotifyIcon(NIM_SETFOCUS, &nid);
|
|
}
|