windows-nt/Source/XPSP1/NT/shell/osshell/accessib/utilman/umandlg/umdialog.cpp
2020-09-26 16:20:57 +08:00

1362 lines
39 KiB
C++

// UMDialog.cpp : implementation file
// Author: J. Eckhardt, ECO Kommunikation
// Copyright (c) 1997-1999 Microsoft Corporation
//
// History:
// Changes
// Yuri Khramov
// 01-jun-99: DisplayName key used in the Dialog (Localization)
// 11-jun-99: DlgHasClosed code changed to work with app closure
// 15-jun-99: Timer delay increased 1000ms
//
// Bug fixes and Changes Anil Kumar 1999
//---------------------------------------------------------------------
#include <afxwin.h> // MFC core and standard components
#include <afxext.h> // MFC extensions
#include "UManDlg.h"
#include "UMDialog.h"
#include "UMAbout.h"
#include "_UMDlg.h"
#include "_UMClnt.h"
#include "_UMTool.h"
#include "UMS_Ctrl.h"
#include "w95trace.h"
#include <WinSvc.h>
#include <htmlhelp.h>
#include <initguid.h>
#include <ole2.h>
#include "deskswitch.c"
#include "ManageShellLinks.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// --------------------------------------------
// constants
#define IDC_ABOUT 10
#define UPDATE_CLIENT_LIST_TIMER 1
// --------------------------------------------
// variables
static DWORD g_cClients = 0;
static umclient_tsp g_rgClients = NULL;
static DWORD s_dwStartMode = START_BY_OTHER;
static BOOL s_fShowWarningAgain = TRUE;
extern CUMDlgApp theApp;
// --------------------------------------------
// C prototypes
static BOOL InitClientData(void);
static BOOL StartClientsOnShow();
static BOOL WriteClientData(BOOL fRunningSecure);
static BOOL IsStartAuto();
static BOOL CantStopClient(umclient_tsp client);
static int GetClientNameFromAccelerator(WPARAM wVK);
extern "C" BOOL StartAppAsUser( LPCTSTR appPath,
LPTSTR cmdLine,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation);
// Help ID's for context sensitive help
DWORD g_rgHelpIds[] = {
IDC_NAME_STATUS, 3,
IDC_START, 1001,
IDC_STOP, 1002,
IDC_START_AT_LOGON, 1003, // TODO UE needs to update CS help
IDC_START_WITH_UM, 1004,
IDC_START_ON_LOCK, 1005, // TODO UE needs to add to CS help
IDOK, 1100,
IDCANCEL, 1200,
ID_HELP, 1300,
};
// ---------------------------------------------------------------
extern "C"{
//--------------------------------
HWND g_hWndDlg = NULL;
HWND aboutWnd = NULL;
static HANDLE s_hDlgThread = NULL;
static HDESK s_hdeskSave = 0;
static HDESK s_hdeskInput = 0;
// UnassignDesktop gets called after the thread has exited to
// close desktop handles opened in AssignDesktop.
inline void UnassignDesktop()
{
if (s_hdeskInput)
{
CloseDesktop(s_hdeskInput);
s_hdeskInput = 0;
}
}
BOOL AssignDesktop(DWORD dwThreadId)
{
s_hdeskSave = GetThreadDesktop(dwThreadId);
s_hdeskInput = OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED);
if (!s_hdeskInput)
{
s_hdeskInput = OpenDesktop(_TEXT("Winlogon"),0,FALSE,MAXIMUM_ALLOWED);
}
if (s_hdeskInput)
{
BOOL fSet = SetThreadDesktop(s_hdeskInput);
}
return (s_hdeskInput)?TRUE:FALSE;
}
//--------------------------------
DWORD UManDlgThread(LPVOID /* UNUSED */ in)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
UMDialog dlg;
// assign thread to the input desktop (have to do
// this here so it works on the winlogon desktop)
if (AssignDesktop(GetCurrentThreadId()))
{
// initialize COM *after* assign to input desktop
// because CoInitialize creates a hidden window on
// the current desktop.
CoInitialize(NULL);
InitCommonControls();
if (InitClientData())
{
Sleep(10);
dlg.DoModal();
if (g_rgClients)
{
VirtualFree(g_rgClients,0,MEM_RELEASE);
g_rgClients = NULL;
}
g_cClients = 0;
g_hWndDlg = NULL;
s_hDlgThread = NULL;
}
CoUninitialize(); // uninitialize COM
}
return 1;
}
void StopDialog()
{
if (aboutWnd)
{
EndDialog(aboutWnd,0);
aboutWnd = NULL;
Sleep(10);
}
if (g_hWndDlg)
{
::PostMessage(g_hWndDlg, WM_CLOSE, 0, 0);
g_hWndDlg = NULL;
Sleep(10);
UnassignDesktop();
}
if (g_rgClients)
{
VirtualFree(g_rgClients,0,MEM_RELEASE);
g_rgClients = NULL;
}
g_cClients = 0;
}
//--------------------------------
#if defined(_X86_)
__declspec (dllexport)
#endif
// UManDlg - Opens or closes the utilman dialog.
//
// fShowDlg - TRUE if dialog should be shown, FALSE if dialog should be closed
// fWaitForDlgClose - TRUE if the function should not return until the dialog
// is closed or a desktop switch happens else FALSE.
// dwVersion - The utilman version
//
// returns TRUE if the dialog was opened or closed
// returns FALSE if the dialog could not be opened or it wasn't open
//
BOOL UManDlg(BOOL fShowDlg, BOOL fWaitForDlgClose, DWORD dwVersion)
{
BOOL fRv = FALSE;
if (dwVersion != UMANDLG_VERSION)
return FALSE;
AFX_MANAGE_STATE(AfxGetStaticModuleState());
if (fShowDlg)
{
if (!s_hDlgThread)
{
s_hDlgThread = CreateThread(NULL, 0, UManDlgThread, NULL, 0, NULL);
}
else
{
SetForegroundWindow((aboutWnd)?aboutWnd:g_hWndDlg);
}
if (s_hDlgThread && fWaitForDlgClose)
{
// This code is executed on the default desktop for the following cases:
//
// 1. Utilman #1 run from the start menu
// 2. Utilman #2 run from the start menu (utilman #1 is SYSTEM)
// 3. Utilman #2 run by utilman #1 in user's context
//
// Wait for either the dialog to close or a desktop switch then return.
// This will end this instance of utilman. If there is a utilman
// running as SYSTEM it will bring up the dialog on the other desktop.
HANDLE rghEvents[2];
rghEvents[0] = s_hDlgThread;
rghEvents[1] = OpenEvent(SYNCHRONIZE, FALSE, __TEXT("WinSta0_DesktopSwitch"));
while (TRUE)
{
DWORD dwObj = MsgWaitForMultipleObjects(2, rghEvents, FALSE, INFINITE, QS_ALLINPUT );
switch (dwObj)
{
case WAIT_OBJECT_0 + 1: // the desktop is changing; close the dialog
StopDialog();
// intentional fall thru to cleanup code
case WAIT_OBJECT_0: // the thread exited; clean up and return
CloseHandle(s_hDlgThread);
s_hDlgThread = 0;
CloseHandle(rghEvents[1]);
return TRUE;
break;
default: // process messages
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
break;
}
}
}
}
else
{
// This code is executed when utilman is running on the secure desktop. In
// that case, utilman brings up the dialog as a thread from its process.
// When the desktop switch is detected utilman calls this function to close
// the dialog. It will be restarted again on the new desktop.
fRv = (g_hWndDlg && s_hDlgThread);
StopDialog();
}
return fRv;
}
BOOL IsDialogUp()
{
return (g_hWndDlg && s_hDlgThread)?TRUE:FALSE;
}
}//extern "C"
/////////////////////////////////////////////////////////////////////////////
// CWarningDlg dialog
CWarningDlg::CWarningDlg(CWnd* pParent /*=NULL*/)
: CDialog(CWarningDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CWarningDlg)
m_fDontWarnAgain = TRUE;
//}}AFX_DATA_INIT
}
void CWarningDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CWarningDlg)
DDX_Check(pDX, IDC_CHK_WARN, m_fDontWarnAgain);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CWarningDlg, CDialog)
//{{AFX_MSG_MAP(CWarningDlg)
// NOTE: the ClassWizard will add message map macros here
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CWarningDlg message handlers
/////////////////////////////////////////////////////////////////////////////
// UMDialog dialog
// --------------------------------------------
UMDialog::UMDialog(CWnd* pParent /*=NULL*/)
: CDialog(UMDialog::IDD, pParent)
, m_fRunningSecure(FALSE)
{
m_szUMStr.LoadString(IDS_UM);
//{{AFX_DATA_INIT(UMDialog)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
UMDialog::~UMDialog()
{
m_lbClientList.Detach();
}
// --------------------------------------------
void UMDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(UMDialog)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
// --------------------------------------------
BEGIN_MESSAGE_MAP(UMDialog, CDialog)
//{{AFX_MSG_MAP(UMDialog)
ON_WM_CLOSE()
ON_BN_CLICKED(IDC_START, OnStart)
ON_BN_CLICKED(IDC_STOP, OnStop)
ON_WM_TIMER()
ON_WM_HELPINFO()
ON_COMMAND( ID_HELP, OnHelp )
ON_LBN_SELCHANGE(IDC_NAME_STATUS, OnSelchangeNameStatus)
ON_BN_CLICKED(IDC_START_AT_LOGON, OnStartAtLogon)
ON_BN_CLICKED(IDC_START_WITH_UM, OnStartWithUm)
ON_WM_CONTEXTMENU()
ON_MESSAGE(WM_SYSCOMMAND,OnSysCommand)
ON_BN_CLICKED(IDC_START_ON_LOCK, OnStartOnLock)
ON_WM_SHOWWINDOW()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// UMDialog message handlers
BOOL UMDialog::PreTranslateMessage(MSG* pMsg)
{
// Override that allows use of function keys to launch applets when on
// the logon desktop. Only pay attention to key up to avoid dup calls.
if (m_fRunningSecure && WM_KEYUP == pMsg->message)
{
int iClient = GetClientNameFromAccelerator(pMsg->wParam);
if (iClient >= 0)
{
m_lbClientList.SelectString(-1, g_rgClients[iClient].machine.DisplayName);
OnStart();
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
BOOL UMDialog::OnInitDialog()
{
CDialog::OnInitDialog();
// set the flag indicating if we are running in secure mode
desktop_ts desktop;
QueryCurrentDesktop(&desktop, TRUE);
m_fRunningSecure = RunSecure(desktop.type);
if (s_fShowWarningAgain && s_dwStartMode == START_BY_MENU)
{
CWarningDlg dlgWarn;
dlgWarn.m_fDontWarnAgain = !s_fShowWarningAgain;
dlgWarn.DoModal();
s_fShowWarningAgain = !dlgWarn.m_fDontWarnAgain;
}
g_hWndDlg = m_hWnd;
// change system menu
CMenu *hSysMenu = GetSystemMenu(FALSE);
if (hSysMenu)
{
CString str;
hSysMenu->AppendMenu(MF_SEPARATOR);
str.LoadString(IDS_ABOUT_STRING);
hSysMenu->AppendMenu(MF_STRING,IDC_ABOUT,LPCTSTR(str));
}
// handle any "start when utility manager starts" applets
StartClientsOnShow();
// attach ListBox to member data and populate w/list of applications
m_lbClientList.Attach(GetDlgItem(IDC_NAME_STATUS)->m_hWnd);
ListClients();
// Disable Help button if we are at WinLogon because the help
// dialog supports "Jump to URL..." exposing security risk.
// The m_fRunningSecure variable is TRUE if UI shouldn't expose help.
if (m_fRunningSecure)
{
EnableDlgItem(ID_HELP, FALSE, IDOK);
}
// Disable "Start when UtilMan starts" unless user is an admin
// and we are running in non-secure mode.
if (s_dwStartMode != START_BY_MENU)
{
GetDlgItem(IDC_START_WITH_UM)->EnableWindow(IsAdmin() && !m_fRunningSecure);
}
// Bring dialog to top and center on desktop window
RECT rectUmanDlg,rectDesktop;
GetDesktopWindow()->GetWindowRect(&rectDesktop);
GetWindowRect(&rectUmanDlg);
long lDlgWidth = rectUmanDlg.right - rectUmanDlg.left;
long lDlgHieght = rectUmanDlg.bottom - rectUmanDlg.top;
if (!m_fRunningSecure)
{
rectUmanDlg.left = (rectDesktop.right - lDlgWidth)/2;
rectUmanDlg.top = (rectDesktop.bottom - lDlgHieght)/2;
} else
{
rectUmanDlg.left = rectDesktop.left + (long)(lDlgWidth/10);
rectUmanDlg.top = rectDesktop.bottom - lDlgHieght - (long)(lDlgHieght/10);
}
// This looks a bit odd (SetForegroundWindow should also be activating
// the window) but if you don't call SetActiveWindow on the secure
// desktop then the second, etc... WinKey+U will bring up UM hidden
// behind the welcome "screen".
SetActiveWindow();
SetForegroundWindow();
SetWindowPos(&wndTopMost,rectUmanDlg.left,rectUmanDlg.top,0,0,SWP_NOSIZE);
if (!m_fRunningSecure)
{
// on default desktop the above SetWindowPos makes the dialog initially
// on top and this call allows other apps to then be on top.
SetWindowPos(&wndNoTopMost,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
}
// start checking every so often to see if we need to update our display
SetTimer(UPDATE_CLIENT_LIST_TIMER, 3000, NULL);
return TRUE; // return TRUE unless you set the focus to a control
}
// -----------------------------------------------------
void UMDialog::OnSysCommand(UINT nID,LPARAM lParam)
{
if (nID == IDC_ABOUT)
{
UMAbout dlg;
dlg.DoModal();
aboutWnd = NULL;
}
else
{
CDialog::OnSysCommand(nID,lParam);
}
}//UMDialog::OnSysCommand
// ------------------------------------
// --------------------------------------------
// CanStartOnLockedDesktop - returns TRUE if applets can
// be configured to be auto-started on the secure desktop
//
inline BOOL CanStartOnLockedDesktop(int iWhichClient, BOOL fRunningSecure)
{
// user can ask for auto start on secure desktop if they are logged on, the
// applet is OK'd to run on the secure desktop and the machine isn't using
// fast user switching (FUS) (w/FUS Ctrl+Alt+Del disconnects the user session
// rather than switching desktops).
BOOL fCanStartOnLockedDesktop =
(
!fRunningSecure &&
g_rgClients[iWhichClient].user.fCanRunSecure &&
CanLockDesktopWithoutDisconnect()
)?TRUE:FALSE;
return fCanStartOnLockedDesktop;
}
// --------------------------------------------
// OnSelchangeNameStatus is called when the user navigates the list
// box items by clicking with the mouse or using up/down arrows.
//
void UMDialog::OnSelchangeNameStatus()
{
// Get the currently selected item and update the controls for
// the currently selected item.
int iSel;
if (GetSelectedClient((int)g_cClients, iSel))
{
// Group box label
CString str(g_rgClients[iSel].machine.DisplayName);
CString optStr;
optStr.Format(IDS_OPTIONS, str);
GetDlgItem(IDC_OPTIONS)->SetWindowText(optStr);
// only enable options when started via WinKey+U
if (s_dwStartMode != START_BY_MENU)
{
// Disable "start at logon" at secure desktop to avoid mischief
if (!m_fRunningSecure)
{
GetDlgItem(IDC_START_AT_LOGON)->EnableWindow(TRUE);
} else
{
// this may be set in an upgrade situation; clear it
g_rgClients[iSel].user.fStartAtLogon = FALSE;
GetDlgItem(IDC_START_AT_LOGON)->EnableWindow(FALSE);
}
// Enable "start on locked desktop" if at default desktop and
// when applet can run on secure desktop
if (CanStartOnLockedDesktop(iSel, m_fRunningSecure))
{
GetDlgItem(IDC_START_ON_LOCK)->EnableWindow(TRUE);
} else
{
// this may be set in an upgrade situation; clear it
g_rgClients[iSel].user.fStartOnLockDesktop = FALSE;
GetDlgItem(IDC_START_ON_LOCK)->EnableWindow(FALSE);
}
// Start option checkboxes
CheckDlgButton(IDC_START_AT_LOGON, (g_rgClients[iSel].user.fStartAtLogon)?TRUE:FALSE);
CheckDlgButton(IDC_START_ON_LOCK, (g_rgClients[iSel].user.fStartOnLockDesktop)?TRUE:FALSE);
CheckDlgButton(IDC_START_WITH_UM, (g_rgClients[iSel].user.fStartWithUtilityManager)?TRUE:FALSE);
} else
{
GetDlgItem(IDC_START_AT_LOGON)->EnableWindow(FALSE);
GetDlgItem(IDC_START_ON_LOCK)->EnableWindow(FALSE);
GetDlgItem(IDC_START_WITH_UM)->EnableWindow(FALSE);
CheckDlgButton(IDC_START_AT_LOGON, FALSE);
CheckDlgButton(IDC_START_ON_LOCK, FALSE);
CheckDlgButton(IDC_START_WITH_UM, FALSE);
}
// Start and stop buttons
DWORD dwState = g_rgClients[iSel].state;
if ((dwState == UM_CLIENT_RUNNING)
&& (g_rgClients[iSel].runCount >= g_rgClients[iSel].machine.MaxRunCount))
EnableDlgItem(IDC_START, FALSE, IDC_NAME_STATUS);
else
EnableDlgItem(IDC_START, TRUE, IDC_NAME_STATUS);
if ((dwState == UM_CLIENT_NOT_RUNNING) || CantStopClient(&g_rgClients[iSel]))
EnableDlgItem(IDC_STOP, FALSE, IDC_NAME_STATUS);
else
EnableDlgItem(IDC_STOP, TRUE, IDC_NAME_STATUS);
}// else ignore selections not in a valid range
}
// --------------------------------------------
void UMDialog::OnClose()
{
// behave like cancel
CDialog::OnClose();
}//UMDialog::OnClose
// --------------------------------------------
// OnStart is called when the Start button is clicked. It starts
// the client ap then lets the timer update saved state.
//
void UMDialog::OnStart()
{
int iSel;
if (GetSelectedClient((int)g_cClients, iSel))
{
if (StartClient(m_hWnd, &g_rgClients[iSel]))
{
KillTimer(UPDATE_CLIENT_LIST_TIMER);
EnableDlgItem(IDC_STOP, TRUE, IDC_NAME_STATUS);
ListClients();
SetTimer(UPDATE_CLIENT_LIST_TIMER, 3000, NULL);
if (g_rgClients[iSel].runCount+1 >= g_rgClients[iSel].machine.MaxRunCount)
EnableDlgItem(IDC_START, FALSE, IDC_STOP);
}
else if (g_rgClients[iSel].runCount < g_rgClients[iSel].machine.MaxRunCount)
{
// Unable to start
CString str;
str.LoadString((m_fRunningSecure)?IDS_SECUREMODE:IDS_ERRSTART);
MessageBox(str, m_szUMStr, MB_OK);
}
}
}
// --------------------------------------------
// OnStop is called when the Stop button is clicked. It stops
// the client ap then lets the timer update saved state.
//
void UMDialog::OnStop()
{
int iSel;
if (GetSelectedClient((int)g_cClients, iSel))
{
if (StopClient(&g_rgClients[iSel]))
{
KillTimer(UPDATE_CLIENT_LIST_TIMER);
GetDlgItem(IDC_START)->EnableWindow(TRUE);
ListClients();
EnableDlgItem(IDC_STOP, FALSE, IDOK);
SetTimer(UPDATE_CLIENT_LIST_TIMER, 3000, NULL);
}
else
{
// Unable to stop
CString str;
str.LoadString(IDS_ERRSTOP);
MessageBox(str, m_szUMStr, MB_OK);
}
}
}
void UMDialog::SaveCurrentState()
{
int iSel;
if (GetSelectedClient((int)g_cClients, iSel))
{
g_rgClients[iSel].user.fStartAtLogon
= (IsDlgButtonChecked(IDC_START_AT_LOGON))?TRUE:FALSE;
g_rgClients[iSel].user.fStartWithUtilityManager
= (IsDlgButtonChecked(IDC_START_WITH_UM))?TRUE:FALSE;
g_rgClients[iSel].user.fStartOnLockDesktop
= (IsDlgButtonChecked(IDC_START_ON_LOCK))?TRUE:FALSE;
}
}
// --------------------------------------------
// OnOK is called when the user clicks the OK button to
// dismiss the UtilMan dialog.
//
void UMDialog::OnOK()
{
SaveCurrentState();
WriteClientData(m_fRunningSecure);
CDialog::OnOK();
}//UMDialog::OnOK
// ----------------------------------------------------------------------------
// OnTimer is called to check the status of client apps that are displayed
// in the UI. This keeps the UI consistent with the running client app's.
//
void UMDialog::OnTimer(UINT nIDEvent)
{
if (nIDEvent == UPDATE_CLIENT_LIST_TIMER)
{
UINT uiElapsed = 3000;
KillTimer(UPDATE_CLIENT_LIST_TIMER);
// get current status and pick up new apps
if (CheckStatus(g_rgClients, g_cClients))
{
ListClients(); // something has changed - update the UI
uiElapsed = 500;
}
SetTimer(UPDATE_CLIENT_LIST_TIMER, uiElapsed, NULL);
}
CDialog::OnTimer(nIDEvent);
}
// --------------------------------------------
// OnHelpInfo provides context sensitive help. It only does
// this if not on the WinLogon desktop.
//
BOOL UMDialog::OnHelpInfo(HELPINFO* pHelpInfo)
{
if (m_fRunningSecure)
return FALSE;
if ( pHelpInfo->iCtrlId == IDC_OPTIONS )
return TRUE;
::WinHelp((HWND)pHelpInfo->hItemHandle, __TEXT("utilmgr.hlp"), HELP_WM_HELP,
(DWORD_PTR) (LPSTR) g_rgHelpIds);
return TRUE;
}
// --------------------------------------------
// OnHelpInfo provides context sensitive help when the user
// right-clicks the dialog. It only does this if not on the
// WinLogon desktop.
//
void UMDialog::OnContextMenu(CWnd* pWnd, CPoint point)
{
if (m_fRunningSecure)
return;
::WinHelp(pWnd->m_hWnd, __TEXT("utilmgr.hlp"), HELP_CONTEXTMENU
, (DWORD_PTR) (LPSTR) g_rgHelpIds);
}
// --------------------------------------------
// OnHelp provides standard help. It only does this if not
// on the WinLogon desktop.
//
void UMDialog::OnHelp()
{
if (m_fRunningSecure)
return;
::HtmlHelp(m_hWnd , TEXT("utilmgr.chm"), HH_DISPLAY_TOPIC, 0);
}
// ----------------------------------
void UMDialog::EnableDlgItem(DWORD dwEnableMe, BOOL fEnable, DWORD dwFocusHere)
{
// when disabling a control that currently has focs switch it to dwFocusHere
if (!fEnable && (GetFocus() == GetDlgItem(dwEnableMe)))
GetDlgItem(dwFocusHere)->SetFocus();
GetDlgItem(dwEnableMe)->EnableWindow(fEnable);
}
void UMDialog::SetStateStr(int iClient)
{
switch (g_rgClients[iClient].state)
{
case UM_CLIENT_NOT_RUNNING:
m_szStateStr.Format(IDS_NOT_RUNNING, g_rgClients[iClient].machine.DisplayName);
break;
case UM_CLIENT_RUNNING:
m_szStateStr.Format(IDS_RUNNING, g_rgClients[iClient].machine.DisplayName);
break;
case UM_CLIENT_NOT_RESPONDING:
m_szStateStr.Format(IDS_NOT_RESPONDING, g_rgClients[iClient].machine.DisplayName);
break;
default:
m_szStateStr.Empty();
break;
}
}
// --------------------------------------------
void UMDialog::ListClients()
{
// Re-do the client list box with latest state info
int iCurSel = m_lbClientList.GetCurSel();
if (iCurSel == LB_ERR)
iCurSel = 0;
m_lbClientList.ResetContent();
for (DWORD i = 0; i < g_cClients; i++)
{
SetStateStr(i);
if (!m_szStateStr.IsEmpty())
{
m_lbClientList.AddString(m_szStateStr);
}
}
m_lbClientList.SetCurSel(iCurSel);
// Refresh button states in case they've changed
// (this happens on desktop switch)
OnSelchangeNameStatus();
}
// --------------------------------------------
// UpdateClientState updates the client list box with the current state
// of the application (running, not running, not responding)
//
void UMDialog::UpdateClientState(int iSel)
{
SetStateStr(iSel);
m_lbClientList.DeleteString(iSel);
m_lbClientList.InsertString(iSel, m_szStateStr);
m_lbClientList.SetCurSel(iSel);
}
// --------------------------------------------
// OnStartAtLogon updates the state of the client in memory when the
// Start with Windows checkbox is checked or unchecked.
//
void UMDialog::OnStartAtLogon()
{
SaveCurrentState();
}
// --------------------------------------------
// OnStartWithUm updates the state of the client in memory when the
// Start with Utility Manager checkbox is checked or unchecked.
//
void UMDialog::OnStartWithUm()
{
SaveCurrentState();
}
// --------------------------------------------
// OnStartOnLock updates the state of the client in memory when the
// Start when I lock my desktop checkbox is checked or unchecked.
//
void UMDialog::OnStartOnLock()
{
SaveCurrentState();
}
// --------------------------------------------
// OnShowWindow
// This was added for a timing problem where utilman came up running in the system context on the users desktop
// this code here makes sure that that cannot happen by cheching right when the dialog is about to appear
//
void UMDialog::OnShowWindow(BOOL bShow, UINT nStatus)
{
HDESK hdesk;
SID *desktopSID = NULL;
hdesk = OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED);
// this is expected to fail for the winlogon desktop and thats ok
if (hdesk)
{
TCHAR desktopName[NAME_LEN];
DWORD nl, SIDLen = 0;
if (!GetUserObjectInformation(hdesk, UOI_NAME, desktopName, NAME_LEN, &nl))
goto StopDialog;
if (!GetUserObjectInformation(hdesk, UOI_USER_SID, desktopSID, 0, &SIDLen))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
goto StopDialog;
}
if (SIDLen > 0 && !lstrcmpi(desktopName, TEXT("Default")))
{
desktopSID = (SID*)new BYTE[SIDLen];
if (!desktopSID)
goto StopDialog;
if (!GetUserObjectInformation(hdesk, UOI_USER_SID, desktopSID, SIDLen, &SIDLen))
goto StopDialog;
BOOL fError;
HANDLE hUserToken = GetUserAccessToken(TRUE, &fError);
if (fError)
goto StopDialog;
// We get a token only if there is a logged on user.
// If there is not then we can come up as system with no worries.
if (!hUserToken)
goto LetDialogComeup;
BOOL fStatus = FALSE;
BOOL fIsInteractiveUser = FALSE;
PSID psidInteractive = InteractiveUserSid(TRUE);
if (!psidInteractive)
goto StopDialog;
fStatus = CheckTokenMembership(hUserToken, psidInteractive, &fIsInteractiveUser);
//If the logged on user is the interactive user and we are running as system then it is a
// security risk to show UI. This can happen when rappidly switching desktops.
if ( fStatus && fIsInteractiveUser && IsSystem())
goto StopDialog;
}
}
LetDialogComeup:
if (desktopSID)
delete [] desktopSID;
return;
StopDialog:
StopDialog();
if (desktopSID)
delete [] desktopSID;
}
/////////////////////////////////////////////////////////////////////////////
// C code
//-----------------------------------------------------------------
__inline void ReplaceDisplayName(LPTSTR szName, int iRID)
{
TCHAR szBuf[MAX_APPLICATION_NAME_LEN];
if (LoadString(AfxGetInstanceHandle(), iRID, szBuf, MAX_APPLICATION_NAME_LEN))
lstrcpy(szName, szBuf);
}
void SetLocalizedDisplayName()
{
// Make localization easier; don't require them to localize registry entries.
// Instead, replace our copy with the localized version. This appears to be
// duplicate code to that in umanrun.c however, that part of the code always
// runs as system and therefore the DisplayName is set to the default system
// language. This part of the code (the UI) runs as the logged on user when
// there is one so these resources will be the user's language. The resources
// and associated code should be removed from utilman.exe and this code and
// resources (from umandlg.dll) should be used.
for (DWORD i=0;i<g_cClients;i++)
{
if ( lstrcmp( g_rgClients[i].machine.ApplicationName, TEXT("Magnifier") ) == 0 )
{
ReplaceDisplayName(g_rgClients[i].machine.DisplayName, IDS_DISPLAY_NAME_MAGNIFIER);
}
else if ( lstrcmp( g_rgClients[i].machine.ApplicationName, TEXT("Narrator") ) == 0 )
{
ReplaceDisplayName(g_rgClients[i].machine.DisplayName, IDS_DISPLAY_NAME_NARRATOR);
}
else if ( lstrcmp( g_rgClients[i].machine.ApplicationName, TEXT("On-Screen Keyboard") ) == 0 )
{
ReplaceDisplayName(g_rgClients[i].machine.DisplayName, IDS_DISPLAY_NAME_OSK);
}
}
}
static BOOL InitClientData(void)
{
BOOL fRv = TRUE;
// On initial run allocate and initialize client array
if (!g_rgClients || !g_cClients)
{
umc_header_tsp pHdr = 0;
umclient_tsp c = 0;
DWORD_PTR accessID,accessID2;
g_cClients = 0;
g_rgClients = NULL;
fRv = FALSE;
pHdr = (umc_header_tsp)AccessIndependentMemory(
UMC_HEADER_FILE,
sizeof(umc_header_ts),
FILE_MAP_READ,
&accessID);
if (!pHdr)
{
goto Cleanup;
}
s_dwStartMode = pHdr->dwStartMode; // capture the Utilman start mode
s_fShowWarningAgain = pHdr->fShowWarningAgain; // and warning dialog flag
if (!pHdr->numberOfClients)
{
goto Cleanup;
}
c = (umclient_tsp)AccessIndependentMemory(
UMC_CLIENT_FILE,
sizeof(umclient_ts)*pHdr->numberOfClients,
FILE_MAP_READ,
&accessID2);
if (!c)
{
goto Cleanup;
}
g_rgClients = (umclient_tsp)VirtualAlloc(NULL, sizeof(umclient_ts)*pHdr->numberOfClients, MEM_RESERVE,PAGE_READWRITE);
if (!g_rgClients)
{
goto Cleanup;
}
if (!VirtualAlloc(g_rgClients, sizeof(umclient_ts)*pHdr->numberOfClients, MEM_COMMIT, PAGE_READWRITE))
{
goto Cleanup;
}
fRv = TRUE;
g_cClients = pHdr->numberOfClients;
memcpy(g_rgClients,c,sizeof(umclient_ts)*pHdr->numberOfClients);
SetLocalizedDisplayName();
Cleanup:
if (pHdr)
{
UnAccessIndependentMemory(pHdr, accessID);
}
if (c)
{
UnAccessIndependentMemory(c, accessID2);
}
if (!fRv && g_rgClients)
{
VirtualFree(g_rgClients, 0, MEM_RELEASE);
g_rgClients = NULL;
g_cClients = 0;
}
}
// "Start when I log on" is per-user setting so get that
// each time the dialog is brought up
CManageShellLinks CManageLinks(STARTUP_FOLDER);
for (DWORD i=0;i<g_cClients;i++)
{
g_rgClients[i].user.fStartAtLogon
= CManageLinks.LinkExists(g_rgClients[i].machine.ApplicationName);
}
return fRv;
}
// RegSetUMDwordValue - helper function to set a DWORD string value creating it if necessary
//
BOOL RegSetUMDwordValue(HKEY hKey, LPCTSTR pszKey, LPCTSTR pszString, DWORD dwNewValue)
{
HKEY hSubkey;
int iRv;
DWORD dwValue = dwNewValue;
iRv = RegCreateKeyEx(
hKey
, pszKey
, 0, NULL
, REG_OPTION_NON_VOLATILE
, KEY_ALL_ACCESS
, NULL, &hSubkey, NULL);
if (iRv == ERROR_SUCCESS)
{
RegSetValueEx(
hSubkey
, pszString
, 0, REG_DWORD
, (BYTE *)&dwValue
, sizeof(DWORD));
RegCloseKey(hSubkey);
}
return (iRv == ERROR_SUCCESS)?TRUE:FALSE;
}
void WriteUserRegData(HKEY hKeyCU, BOOL fDoAppletData)
{
HKEY hkey;
DWORD dwRv = RegCreateKeyEx(hKeyCU
, UM_HKCU_REGISTRY_KEY
, 0 , NULL
, REG_OPTION_NON_VOLATILE
, KEY_ALL_ACCESS, NULL
, &hkey, NULL);
if (dwRv == ERROR_SUCCESS)
{
dwRv = RegSetValueEx(
hkey
, UMR_VALUE_SHOWWARNING
, 0, REG_DWORD
, (BYTE *)&s_fShowWarningAgain
, sizeof(DWORD));
if (fDoAppletData)
{
for (DWORD i = 0; i < g_cClients; i++)
{
RegSetUMDwordValue(
hkey
, g_rgClients[i].machine.ApplicationName
, UMR_VALUE_STARTLOCK
, g_rgClients[i].user.fStartOnLockDesktop);
}
}
RegCloseKey(hkey);
}
}
// --------------------------------------------
static BOOL CopyClientData()
{
umclient_tsp c;
DWORD_PTR accessID;
if (!g_cClients || !g_rgClients)
return TRUE;
c = (umclient_tsp)AccessIndependentMemory(
UMC_CLIENT_FILE,
sizeof(umclient_ts)*g_cClients,
FILE_MAP_READ|FILE_MAP_WRITE,
&accessID);
if (!c)
return FALSE;
memcpy(c,g_rgClients,sizeof(umclient_ts)*g_cClients);
UnAccessIndependentMemory(c, accessID);
return TRUE;
}
static void CopyHeaderData()
{
umc_header_tsp pHdr;
DWORD_PTR accessID;
pHdr = (umc_header_tsp)AccessIndependentMemory(
UMC_HEADER_FILE,
sizeof(umc_header_ts),
FILE_MAP_READ|FILE_MAP_WRITE,
&accessID);
if (pHdr)
{
pHdr->fShowWarningAgain = s_fShowWarningAgain;
UnAccessIndependentMemory(pHdr, accessID);
}
}
// ----------------------------------
// WriteClientData - save settings to the registry
//
static BOOL WriteClientData(BOOL fRunningSecure)
{
// It only makes sense to do this if there are any applets being managed
// (shouldn't get here) and if there is a logged on user (otherwise the
// settings cannot be changed)
if (!g_cClients || !g_rgClients || fRunningSecure)
return TRUE;
// The SYSTEM instance of utilman needs to be updated in case the user
// changed any options. This is so subsequent instances of the UI will
// get the correct options without having to read the registry.
CopyHeaderData();
CopyClientData();
//
// Write utilman settings data. Put "Start when UtilMan starts" in HKLM,
// "Start when I lock desktop" in HKCU, and "Start when I log on" into
// a startup link in the logged on user's shell folder.
//
// "Start when UtilMan starts" settings... (only for admins)
DWORD i;
if (IsWindowEnabled(GetDlgItem(g_hWndDlg, IDC_START_WITH_UM)))
{
HKEY hHKLM;
DWORD dwRv = RegCreateKeyEx(HKEY_LOCAL_MACHINE
, UM_REGISTRY_KEY
, 0 , NULL
, REG_OPTION_NON_VOLATILE
, KEY_ALL_ACCESS, NULL
, &hHKLM, NULL);
if (dwRv == ERROR_SUCCESS)
{
for (i = 0; i < g_cClients; i++)
{
RegSetUMDwordValue(
hHKLM
, g_rgClients[i].machine.ApplicationName
, UMR_VALUE_STARTUM
, g_rgClients[i].user.fStartWithUtilityManager);
}
RegCloseKey(hHKLM);
}
}
//
// "Start when I lock my desktop" settings... (any logged on user)
// and Don't show me the warning anymore setting
//
WriteUserRegData(HKEY_CURRENT_USER, IsWindowEnabled(GetDlgItem(g_hWndDlg, IDC_START_ON_LOCK)));
//
// manage shell folder link updates (logged on user only)
//
if (IsWindowEnabled(GetDlgItem(g_hWndDlg, IDC_START_AT_LOGON)))
{
CManageShellLinks CManageLinks(STARTUP_FOLDER);
for (i = 0; i < g_cClients; i++)
{
LPTSTR pszAppName = g_rgClients[i].machine.ApplicationName;
BOOL fLinkExists = CManageLinks.LinkExists(pszAppName);
// if should start at logon and there isn't a link then create one
// and if shouldn't start at logon and there is a link then delete it
if (g_rgClients[i].user.fStartAtLogon && !fLinkExists)
{
TCHAR pszAppPath[MAX_PATH];
LPTSTR pszApp = 0;
// Following is TRUE *only* if pszAppPath is non-null string value
if (GetClientApplicationPath(pszAppName , pszAppPath , MAX_PATH))
{
TCHAR pszFullPath[MAX_PATH*2+1]; // path + filename
TCHAR pszStartIn[MAX_PATH];
int ctch, ctchAppPath = lstrlen(pszAppPath);
// if pszAppPath is just base name and extension then prepend system path
if (wcscspn(pszAppPath, TEXT("\\")) != (size_t)ctchAppPath
|| wcscspn(pszAppPath, TEXT(":")) != (size_t)ctchAppPath)
{
TCHAR szDrive[_MAX_DRIVE], szDir[_MAX_DIR];
_wsplitpath(pszAppPath, szDrive, szDir, NULL, NULL);
lstrcpy(pszStartIn, szDrive);
lstrcat(pszStartIn, szDir);
pszApp = pszAppPath;
} else
{
ctch = GetSystemDirectory(pszStartIn, MAX_PATH);
lstrcpy(pszFullPath, pszStartIn); // save path to build full path
if (ctch + ctchAppPath + 2 > MAX_PATH*2)
{
DBPRINTF(TEXT("WriteClientData: Path is too short!\r\n"));
} else
{
if (*(pszFullPath + ctch - 1) != '\\')
lstrcat(pszFullPath, TEXT("\\"));
lstrcat(pszFullPath, pszAppPath);
pszApp = pszFullPath;
}
}
if (pszApp)
{
// remove ending '\' from StartIn path
ctch = lstrlen(pszStartIn) - 1;
if (*(pszStartIn + ctch) == '\\')
*(pszStartIn + ctch) = 0;
CManageLinks.CreateLink(
pszAppName
, pszApp
, pszStartIn
, g_rgClients[i].machine.DisplayName
, TEXT("/UM"));
}
}
} else if (!g_rgClients[i].user.fStartAtLogon && fLinkExists)
{
CManageLinks.RemoveLink(pszAppName);
}
}
}
return TRUE;
}
// --------------------------------------------
static BOOL CantStopClient(umclient_tsp client)
{
switch (client->machine.ApplicationType)
{
case APPLICATION_TYPE_APPLICATION:
break;
case APPLICATION_TYPE_SERVICE:
{
SERVICE_STATUS ssStatus;
SC_HANDLE hService;
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (!hSCM)
return TRUE;
hService = OpenService(hSCM, client->machine.ApplicationName, SERVICE_ALL_ACCESS);
CloseServiceHandle(hSCM);
if (!hService)
return TRUE;
if (!QueryServiceStatus(hService, &ssStatus) ||
!(ssStatus.dwControlsAccepted & SERVICE_ACCEPT_STOP))
{
CloseServiceHandle(hService);
return TRUE;
}
CloseServiceHandle(hService);
break;
}
}
return FALSE;
}//CantStopClient
// We don't want UtilMan to startType to be Automatic
// It should only be made Automatic if it is required, When the user
// selects "Start when NT starts" through the GUI :a-anilk
static BOOL IsStartAuto()
{
#ifdef NEVER // MICW Don't start service anymore at logon because of TS
DWORD nClient;
for(nClient = 0; nClient < g_cClients; nClient++)
{
if ( g_rgClients[nClient].user.fStartAtLogon == TRUE )
return TRUE;
}
#endif
return FALSE;
}
static int GetClientNameFromAccelerator(WPARAM wVK)
{
for (int i=0;i<(int)g_cClients;i++)
if (g_rgClients[i].machine.AcceleratorKey == wVK)
return i;
return -1;
}
static BOOL StartClientsOnShow()
{
BOOL fOK = TRUE;
for (int i=0;i<(int)g_cClients;i++)
{
if ( g_rgClients[i].user.fStartWithUtilityManager
&& !StartClient(g_hWndDlg, &g_rgClients[i]))
fOK = FALSE;
}
return fOK;
}