/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    taskman.c

Abstract:

    This file contains the source for the windows Task Manager.
    Taskman basically is a dialog box, which enumerates active windows
    keep in the user window manager, then sets active focus to the selected
    dialog box element(ie active window).

--*/

#define UNICODE

#include "taskman.h"
#include <port1632.h>
#include <shellapi.h>
#include <shlapip.h>

//LATER find correct define for NT
#if !defined(NTWIN) && !defined(DOSWIN32) && !defined(WIN16)
#define NTWIN   1
#endif

#define ARRAYSIZE(x)    (sizeof(x)/sizeof(x[0]))

#define MAXPATHFIELD 260

#define INIT_MAX_FILES 4
#define FILES_KEY  L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Program Manager\\Recent File List"
#define MAXFILES_ENTRY L"Max Files"
#define FILE_ENTRY L"File%lu"

#define WM_CONTENTSCHANGED (WM_USER+5)

TCHAR szPathField[MAXPATHFIELD];
TCHAR szDirField[MAXPATHFIELD];
TCHAR szTitle[MAXPATHFIELD];
TCHAR szMessage[MAXMSGBOXLEN];

TCHAR szUserHomeDir[MAXPATHFIELD];
TCHAR szWindowsDirectory[MAXPATHFIELD];

TCHAR szOOMExitMsg[64] = TEXT("Close an application and try again."); // 64
TCHAR szOOMExitTitle[32] = TEXT("Extremely Low on Memory"); // 32

TCHAR szNoRun[] = TEXT("NoRun");
// registry key for groups

HANDLE hInst;

BOOL bChangedDefaultButton;

INT_PTR APIENTRY TaskmanDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam);

WORD APIENTRY ExecProgram(LPTSTR lpszPath,LPTSTR lpDir,LPTSTR lpTitle);
void APIENTRY SaveRecentFileList (HWND, LPTSTR);

VOID GetPathInfo(PTSTR szPath,PTSTR *pszFileName,PTSTR *pszExt,WORD *pich,BOOL *pfUnc);
VOID GetFilenameFromPath(PTSTR szPath, PTSTR szFilename);
VOID GetDirectoryFromPath(PTSTR szFilePath, PTSTR szDir);
BOOL IsUserAdmin();
BOOL OKToExec();
BOOL TestTokenForAdmin(HANDLE Token);
VOID SetDefButton(HWND hwndDlg, INT  idButton);

WINUSERAPI VOID SwitchToThisWindow(HWND, BOOL);
INT MyMessageBox(HWND hWnd,WORD idTitle,WORD idMessage,PWSTR psz,WORD wStyle);

INT MyX = 0;
INT MyY = 0;
INT dxTaskman;
INT dyTaskman;
INT dxScreen;
INT dyScreen;

HWND ghwndDialog;
BOOL fExecOK = TRUE;
BOOL fMsgBox = FALSE;


PVOID Alloc(
    DWORD   Bytes)
{
    HANDLE  hMem;
    PVOID   Buffer;

    hMem = LocalAlloc(LMEM_MOVEABLE, Bytes + sizeof(hMem));

    if (hMem == NULL) {
        return(NULL);
    }

    Buffer = LocalLock(hMem);
    if (Buffer == NULL) {
        LocalFree(hMem);
        return(NULL);
    }

    *((PHANDLE)Buffer) = hMem;

    return (PVOID)(((PHANDLE)Buffer)+1);
}


BOOL Free(
    PVOID   Buffer)
{
    HANDLE  hMem;

    hMem = *(((PHANDLE)Buffer) - 1);

    LocalUnlock(hMem);

    return(LocalFree(hMem) == NULL);
}


VOID
HideWindow(HWND hwnd)
{
   if (!fMsgBox) {

      if (fExecOK) {
         SetDlgItemText(ghwndDialog, IDD_PATH, TEXT(""));
      }

      // redundant?  why do they do the reverse twice for show below?
      ShowWindow(ghwndDialog, SW_HIDE);

      SetWindowPos(ghwndDialog, HWND_NOTOPMOST, 0, 0, 0, 0,
         SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
   }

   // Erase dark border from depressed pushbuttons
   SendMessage(GetDlgItem(hwnd, IDCANCEL), // IDCANCEL
      BM_SETSTYLE, BS_PUSHBUTTON, MAKELPARAM(TRUE, 0));
   SendMessage(GetDlgItem(hwnd, IDD_TERMINATE),
      BM_SETSTYLE, BS_PUSHBUTTON, MAKELPARAM(TRUE, 0));
   SendMessage(GetDlgItem(hwnd, IDD_CASCADE),
      BM_SETSTYLE, BS_PUSHBUTTON, MAKELPARAM(TRUE, 0));
   SendMessage(GetDlgItem(hwnd, IDD_TILE),
      BM_SETSTYLE, BS_PUSHBUTTON, MAKELPARAM(TRUE, 0));
   SendMessage(GetDlgItem(hwnd, IDD_ARRANGEICONS),
      BM_SETSTYLE, BS_PUSHBUTTON, MAKELPARAM(TRUE, 0));
}

/*
 * We call HideTasklist() when we want to remove the tasklist window
 * from the screen but not select another window (ie. when we're about
 * to select another app.  We call ShowWindow(SW_HIDE) directly when
 * we're doing something like tiling or cascading so a window other than
 * the tasklist will become the foreground window.
 */
VOID HideTasklist(VOID)
{
    if (fExecOK) {
       SetDlgItemText(ghwndDialog, IDD_PATH, TEXT(""));
    }

    SetWindowPos(ghwndDialog, HWND_TOP, 0, 0, 0, 0, SWP_HIDEWINDOW |
            SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);

}


VOID ShowTasklist(
   POINT pt)
{
    /*
     * Retract the drop down listbox.
     */

     if (fExecOK) {
        SendDlgItemMessage(ghwndDialog, IDD_PATH,
                           CB_SHOWDROPDOWN,0,0);
     }

    SetWindowPos(ghwndDialog, HWND_TOPMOST, pt.x, pt.y, 0, 0,
       SWP_NOSIZE | SWP_NOACTIVATE );
    SetForegroundWindow(ghwndDialog);

    ShowWindow(ghwndDialog, SW_NORMAL);
}


/*** ActivateSelectedWindow --         Calls user, to set active window, selected
 *                                                                 by the user.
 *
 *
 * ActivateSelectedWindow(HWND hwndLB)
 *
 * ENTRY -         HWND hwndLB - handle to window, which is to become the active
 *                                                  window, with focus.
 * EXIT  -
 * SYNOPSIS -  This function takes the hwnd passed into it, calls user
 *                                to set active focus to that window.
 * WARNINGS -
 * EFFECTS  -
 *
 */

VOID ActivateSelectedWindow(
    HWND hwndLB)
{
   INT nIndex;
   HWND hwndT;
   HWND hwndLastActive;
   DWORD lTemp;

    /*
     * Get the hwnd of the item which was selected.
     */
    nIndex = (int)SendMessage(hwndLB, LB_GETCURSEL, 0, 0);
    hwndT = (HWND)SendMessage(hwndLB, LB_GETITEMDATA, nIndex, 0);

    if (!IsWindow(hwndT)) {
        /*
         * We gotta make sure the window is valid before doing stuff with it.
         * An app may terminate itself in the background rendering these
         * window handles invalid.
         */
        goto Beep;
    }

    /*
     * Switch to that task.
     * HACK! Activate the window in the hwndLastActive field of the WndStruct.
     */
    hwndLastActive = GetLastActivePopup(hwndT);

    if (!IsWindow(hwndLastActive)) {
        goto Beep;
    }

    /*
     * But only if it isn't disabled.
     */
    lTemp = GetWindowLong(hwndLastActive, GWL_STYLE);
    if (!(lTemp & WS_DISABLED)) {
        /*
         * HACK!! Use SwitchToThisWindow() to bring dialog parents as well.
         */
        SwitchToThisWindow(hwndLastActive, TRUE);

    } else {
Beep:
        MessageBeep(0);
    }
}

#ifdef NTWIN
/*** DoEndTask --
 *
 * void DoEndTask( HWND hwnd )
 */
VOID DoEndTask(
   HWND hwnd )
{
   TCHAR szMsgBoxText[MAXMSGBOXLEN];
   TCHAR szTempField[MAXTASKNAMELEN];
   INT nch;


   if (!EndTask(hwnd, FALSE, FALSE)) {
      /* App does not want to close, ask user if
       * he wants to blow it away
       */

       InternalGetWindowText(hwnd, (LPTSTR)szTempField, MAXTASKNAMELEN);

       /* Load the message box string, it is very long (greater than 255 chars
        * which is why we load it in two pieces
        */
        nch = LoadString(NULL, IDS_MSGBOXSTR1, szMsgBoxText, MAXMSGBOXLEN);
        LoadString(NULL, IDS_MSGBOXSTR2, &szMsgBoxText[nch], MAXMSGBOXLEN-nch);

        if( MessageBox( NULL, szMsgBoxText, szTempField,
                MB_SETFOREGROUND | MB_SYSTEMMODAL | MB_YESNO ) == IDYES) {
            EndTask(hwnd, FALSE, TRUE);
        }
    }
}


/*** CallEndTask --                 A separate thread to instigate EndTask
 *
 * CallEndTask( HWND hwnd );
 *
 * ENTRY -      HWND hwnd - window handle for the task to be killed
 * EXIT  -
 * SYNOPSIS -  This function calls EndTask on the given window to kill the
 *              task that owns that window.
 *
 * WARNINGS -
 * EFFECTS  -   Kills the task that owns hwnd.
 *
 */

DWORD CallEndTask(
    HWND hwnd)
{
    DoEndTask(hwnd);

    ExitThread(0);
    return 0; /* placate compiler */
}
#endif


/*** TaskmanDlgProc --         Dialog Procedure for Taskman Window
 *
 *
 *
 * TaskmanDlgProc(HWND hDlg, WORD wMSG, DWORD wParam, LPARAM lParam)
 *
 * ENTRY -         HWND hhDlg                 - handle to dialog box.
 *                        WORD wMsg                  - message to be acted upon.
 *                        DWORD wParam        - value specific to wMsg.
 *                        LPARAM lParam                - value specific to wMsg.
 *
 * EXIT  -           True if success, False if not.
 * SYNOPSIS -  Dialog box message processing function.
 *
 * WARNINGS -
 * EFFECTS  -
 *
 */

INT_PTR TaskmanDlgProc(
    HWND hwnd,
    UINT wMsg,
    WPARAM wParam,
    LPARAM lParam)
{
    int nIndex;
    RECT rc;
    HWND hwndLB;
    HWND hwndNext;
    TCHAR szTempField[MAXTASKNAMELEN];
    POINT pt;
    HKEY  hKey;
    DWORD dwDisp;
    DWORD dwDataType, dwMaxFiles=INIT_MAX_FILES, dwMaxFilesSize, dwCount;
    TCHAR szFileEntry[20];
    TCHAR szFullPath[MAXPATHFIELD];
#ifndef NTWIN
    LONG lTemp;
#endif

    hwndLB = GetDlgItem(hwnd, IDD_TASKLISTBOX);

    switch (wMsg) {

    case WM_INITDIALOG:
        /*
         * call private api to mark task man as a system app. This causes
         * it to be killed after all other non-system apps during shutdown.
         */
//      MarkProcess(MP_SYSTEMAPP);
        GetWindowRect(hwnd, &rc);
        dxTaskman = rc.right - rc.left;
        dyTaskman = rc.bottom - rc.top;
        dxScreen = GetSystemMetrics(SM_CXSCREEN);
        dyScreen = GetSystemMetrics(SM_CYSCREEN);

        pt.x = (dxScreen - dxTaskman) / 2;
        pt.y = (dyScreen - dyTaskman) / 2;

        SetWindowPos(hwnd, HWND_NOTOPMOST, pt.x, pt.y, 0, 0,
           SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);

        SendDlgItemMessage(hwnd, IDD_PATH, EM_LIMITTEXT, MAXPATHFIELD-4, 0L);
        szPathField[0] = TEXT('\0');

        bChangedDefaultButton = FALSE;

        return FALSE;

    case WM_SHOWWINDOW:
        /*
         * If we're being shown fill in the listbox.  We do this here
         * rather than in WM_ACTIVATE process so we can do it while the
         * dialog is still invisible.
         */
        if (wParam != 0) {
            DWORD pidTaskMan = GetCurrentProcessId();

            /*
             * First delete any previous entries.
             */
            while ((int)SendMessage(hwndLB, LB_DELETESTRING, 0, 0) != LB_ERR);

            /*
             * Search the window list for enabled top level windows.
             */
            hwndNext = GetWindow(hwnd, GW_HWNDFIRST);
            while (hwndNext) {

                /*
                 * Only add non-owned, visible, non-Taskman, Top Level Windows.
                 */
                if ((hwndNext != hwnd) && (IsWindowVisible(hwndNext)) &&
                        (!GetWindow(hwndNext, GW_OWNER))) {
                    DWORD pidNext;
                    GetWindowThreadProcessId(hwndNext, &pidNext);
                    if (pidNext != pidTaskMan) {
                        if (InternalGetWindowText(hwndNext, (LPTSTR)szTempField,
                                MAXTASKNAMELEN)) {
                            nIndex = (int)SendMessage(hwndLB, LB_ADDSTRING, 0,
                                    (DWORD_PTR)(LPTSTR)szTempField);
                            SendMessage(hwndLB, LB_SETITEMDATA, nIndex,
                                    (DWORD_PTR)hwndNext);
                        }
                    }
                }

                hwndNext = GetWindow(hwndNext, GW_HWNDNEXT);
            }
            SendMessage(hwndLB, LB_SETCURSEL, 0, 0);

            //
            // Set the default button to "Switch To"
            //

            SetDefButton(hwnd,IDD_SWITCH);

            //
            // Load the combobox with the recently used files.
            //

            if (GetDlgItem(hwnd, IDD_PATH)) {

                //
                // FIrst empty the combo box from the last time.
                //

                SendDlgItemMessage (hwnd, IDD_PATH,
                                    CB_RESETCONTENT, 0, 0);


                //
                // Load the combobox with recently used files from the registry.
                //
                // Query the max number of files first.
                //

                if (RegCreateKeyEx (HKEY_CURRENT_USER, FILES_KEY, 0, 0,
                                    REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
                                    NULL, &hKey, &dwDisp) == ERROR_SUCCESS) {

                    if (dwDisp == REG_OPENED_EXISTING_KEY) {

                        //
                        //  Query the max number of entries
                        //

                        dwMaxFilesSize = sizeof (DWORD);

                        if (RegQueryValueEx (hKey, MAXFILES_ENTRY, NULL, &dwDataType,
                                       (LPBYTE)&dwMaxFiles, &dwMaxFilesSize) == ERROR_SUCCESS) {

                            //
                            //  Now Query each entry and add it to the list box.
                            //

                            for (dwCount=0; dwCount < dwMaxFiles; dwCount++) {

                                wsprintf (szFileEntry, FILE_ENTRY, dwCount);
                                dwMaxFilesSize = MAXPATHFIELD+1;

                                if (RegQueryValueEx (hKey, szFileEntry, NULL, &dwDataType,
                                                 (LPBYTE) szFullPath, &dwMaxFilesSize) == ERROR_SUCCESS) {

                                    //
                                    // Found an entry.  Add it to the combo box.
                                    //

                                    SendDlgItemMessage (hwnd, IDD_PATH,
                                                        CB_ADDSTRING, 0,
                                                        (LPARAM)szFullPath);

                                } else {
                                    break;
                                }
                            }
                        }
                    } else {
                        //
                        // We are working with a new key, so we need to
                        // set the default number of files.
                        //

                        RegSetValueEx (hKey, MAXFILES_ENTRY, 0, REG_DWORD,
                                       (CONST BYTE *) &dwMaxFiles, sizeof (DWORD));
                    }

                    //
                    //  Close the registry key
                    //

                    RegCloseKey (hKey);

                }
            }

            //
            // Disable the Run button and set the focus to the
            // listbox.
            //

            EnableWindow(GetDlgItem(hwnd, IDD_RUN), FALSE);
            SetFocus(hwndLB);
        }
        break;

    case WM_ACTIVATE:
        /*
         * If we're being deactivated clear the listbox so we
         * can fill it in afresh when we're re-activated.
         */
        if (wParam == 0) {
            /*
             * If we're not already invisible, hide ourself.
             */
            if (IsWindowVisible(hwnd)) {
                HideWindow(hwnd);
            }
        }

        if (!bChangedDefaultButton) {
           SetDefButton(hwnd,IDD_SWITCH);
        }

        break;

    case WM_ACTIVATEAPP:
        if (wParam)
            return FALSE;

        /*
         * If we are not visible when we get this message it is because
         * we are already in the process of terminating.  If we don't
         * ignore this we get into a weird race condition and the frame
         * of the window being activated doesn't get fully drawn.  (BG)
         */
        if (IsWindowVisible(hwnd)) {
            HideWindow(hwnd);
        }
        break;

#ifdef JAPAN // bug fix
    //
    // Do nothing. Let the progman main thread do the work.
    //
#else // not JAPAN
    case WM_WININICHANGE:
        //
        // Check if the user's environment variables have changed, if so
        // regenerate the environment, so that new apps started from
        // taskman will have the latest environment.
        //
        if (lParam && (!lstrcmpi((LPTSTR)lParam, (LPTSTR) TEXT("Environment")))) {
            PVOID pEnv;

            RegenerateUserEnvironment(&pEnv, TRUE);
            break;
        }
        else {
            return FALSE;
        }
#endif // JAPAN

    case WM_CONTENTSCHANGED:
       if (fExecOK) {
          if (GetDlgItemText(hwnd, IDD_PATH, (LPTSTR)szPathField, MAXPATHFIELD)) {
             EnableWindow(GetDlgItem(hwnd, IDD_RUN), TRUE);
             if (!bChangedDefaultButton) {
                SetDefButton(hwnd,IDD_RUN);
                bChangedDefaultButton = TRUE;
             }
          } else {
             EnableWindow(GetDlgItem(hwnd, IDD_RUN), FALSE);
             if (bChangedDefaultButton) {
                SetDefButton(hwnd,IDD_SWITCH);
                bChangedDefaultButton = FALSE;
             }
          }
       }

       break;


    case WM_COMMAND:
        switch(LOWORD(wParam)) {

        case IDD_TASKLISTBOX:

            switch(HIWORD(wParam)) {

            case LBN_DBLCLK:
                HideTasklist();
                ActivateSelectedWindow(hwndLB);
                break;

            default:
                return FALSE;
            }
            break;

        case IDD_PATH:
           PostMessage (hwnd, WM_CONTENTSCHANGED, 0, 0);
           break;


        case IDOK:
           if (!bChangedDefaultButton) {
              goto Switchem;
           }

        case IDD_RUN:
           if (fExecOK) {
              TCHAR szFilename[MAXPATHFIELD+4];
              WORD ret;

              GetDlgItemText(hwnd, IDD_PATH, szPathField, MAXPATHFIELD);
              DoEnvironmentSubst(szPathField, MAXPATHFIELD);
              GetDirectoryFromPath(szPathField, szDirField);
              if (*szDirField) {
                  // Convert path into a .\foo.exe style thing.
                  lstrcpy(szFilename, L".\\");
                  // Tag the filename and params on to the end of the dot slash.
                  GetFilenameFromPath(szPathField, szFilename+2);
                  if (*(szFilename+2) == L'"' ) {
                      SheRemoveQuotes(szFilename+2);
                      CheckEscapes(szFilename, ARRAYSIZE(szFilename));
                  }
              }
              else {
                  GetFilenameFromPath(szPathField, szFilename);
              }

              ret = ExecProgram(szFilename, szDirField, szFilename);

              if (ret) {
                 fMsgBox = TRUE;
                 MyMessageBox( hwnd, IDS_EXECERRTITLE, ret, szPathField,
                    MB_SYSTEMMODAL | MB_OK | MB_ICONEXCLAMATION );
                 fMsgBox = FALSE;

                 SetFocus(GetDlgItem(hwnd, IDD_PATH));
              } else {
                 GetDlgItemText(hwnd, IDD_PATH, szPathField, MAXPATHFIELD);
                 SaveRecentFileList (hwnd, szPathField);
                 HideWindow(hwnd);
              }

           }
           break;

Switchem:

        case IDD_SWITCH:
            HideTasklist();
            ActivateSelectedWindow(hwndLB);
            break;

        case IDCANCEL:
           HideWindow(hwnd);
           break;

        case IDD_TERMINATE:
            /*
             * Get the hwnd of the item which was selected.
             */
            nIndex = (int)SendMessage(hwndLB, LB_GETCURSEL, 0, 0);
            hwndNext = (HWND)SendMessage(hwndLB, LB_GETITEMDATA, nIndex, 0);

            if (!IsWindow(hwndNext)) {
                HideWindow(hwnd);
                MessageBeep(0);
                break;
            }

#ifndef NTWIN   /* Since NTWIN uses WM_ENDSESSION to kill the app,
                 * It is OK to kill it when it is disabled
                 */
            /*
             * Test if the toplevel window is disabled. If it is
             * diabled, we don't want to send a WM_CLOSE message to
             * the parent. This is because the app could have a dialog
             * box up and it's not expecting a CLOSE message...
             * Nasty rips can happen... Instead, active the window so
             * that the user can dismis any dialog box or fix whatever
             * is causing the top level window to be disabled...
             */
            lTemp = GetWindowLong(hwndNext, GWL_STYLE);
            if (lTemp & WS_DISABLED) {
                HideTasklist();
                MessageBeep(0);
                ActivateSelectedWindow(hwndLB);

            } else
#endif
            {
                /* Always activate the window first.  This prevents
                 * apps from going to Beep mode.  Failing to do this
                 * can cause re-entrancy problems in the app if we
                 * do this again before activating the app.
                 *
                 * However, don't do this if it is a old app task.
                 */

#ifdef WIN16    /* if NTWIN, then always do this, as is no winoldapp */
                if (!IsWinoldapTask(GetTaskFromHwnd(hwndNext)))
#endif
                HideWindow(hwnd);
                ActivateSelectedWindow(hwndLB);
#ifdef NTWIN
                {
                    DWORD idt;
                    HANDLE hThread;

                    hThread = CreateThread(NULL, 0,
                             (LPTHREAD_START_ROUTINE)CallEndTask,
                             (LPVOID)hwndNext, 0,
                             &idt);

                    if (hThread == NULL) {
                        /*
                         * Can not create thread, just call EndTask
                         * syncronously
                         */
                        DoEndTask( hwndNext );
                    } else {
                        CloseHandle(hThread);
                    }
                }
#else
                EndTask(hwndNext, FALSE, FALSE);
#endif
            }

            break;

        case IDD_TILE:
        case IDD_CASCADE:
            {
                HWND hwndDesktop;

                HideWindow(hwnd);

                hwndDesktop = GetDesktopWindow();

                if (wParam == IDD_CASCADE) {
                    CascadeChildWindows(hwndDesktop, 0);

                } else {
                    /*
                     * If shift is down, tile vertically, else horizontally.
                     */
                    TileChildWindows(hwndDesktop, ((GetKeyState(VK_SHIFT) &
                            0x8000) ? MDITILE_HORIZONTAL : MDITILE_VERTICAL));
                }
                break;
            }

        case IDD_ARRANGEICONS:
            /*
             * Let's restore the saved bits before ArrangeIcons
             * FIX for Bug #4884; --SANKAR-- 10-02-89
             */
            HideWindow(hwnd);
            ArrangeIconicWindows(GetDesktopWindow());
            break;
        }

        break;


    case WM_CLOSE:
        /*
         * If wParam != 0, this is a shutdown request, so exit.
         */
        if (wParam != 0)
            ExitProcess(0);
        return FALSE;
        break;

    case WM_HOTKEY:
        if (wParam == 1) {
            pt.x = (dxScreen - dxTaskman) / 2;
            pt.y = (dyScreen - dyTaskman) / 2;
            ShowTasklist(pt);
        }
        break;

    case WM_LOGOFF:
        PostQuitMessage(0);
        break;

    default:
        return FALSE;
    }

    return TRUE;

    lParam;
}

//*************************************************************
//
//  SetDefButton()
//
//  Purpose:    Sets the default button
//
//  Parameters: HWND hDlg     - Window handle of dialog box
//              INT  idButton - ID of button
//
//  Return:     void
//
//*************************************************************

VOID SetDefButton(HWND hwndDlg, INT  idButton)
{
    LRESULT lr;

    if (HIWORD(lr = SendMessage(hwndDlg, DM_GETDEFID, 0, 0)) == DC_HASDEFID)
    {
        HWND hwndOldDefButton = GetDlgItem(hwndDlg, LOWORD(lr));

        SendMessage (hwndOldDefButton,
                     BM_SETSTYLE,
                     MAKEWPARAM(BS_PUSHBUTTON, 0),
                     MAKELPARAM(TRUE, 0));
    }

    SendMessage( hwndDlg, DM_SETDEFID, idButton, 0L );
    SendMessage( GetDlgItem(hwndDlg, idButton),
                 BM_SETSTYLE,
                 MAKEWPARAM( BS_DEFPUSHBUTTON, 0 ),
                 MAKELPARAM( TRUE, 0 ));

}


/*** SaveRecentFileList --    Save the list of recently used files
 *
 * void APIENTRY SaveRecentFileList (HWND hwnd, LPTSTR szCurrentFile);
 *
 *
 *
 * ENTRY - HWND   hwnd            - handle to dialog box.
 *         LPTSTR szCurrentFile   - pointer to selected filename
 *
 * EXIT  -
 * SYNOPSIS -
 *
 * WARNINGS -
 * EFFECTS  -
 *
 */


void APIENTRY SaveRecentFileList (HWND hwnd, LPTSTR szCurrentFile)
{
    HKEY  hKey;
    DWORD dwDisp;
    DWORD dwDataType, dwMaxFiles=INIT_MAX_FILES, dwMaxFilesSize, dwCount;
    TCHAR szFileEntry[20];
    DWORD dwEnd=0;
    DWORD dwFileNum=0;
    DWORD dwDup;
    static TCHAR szRecentFilePath[MAXPATHFIELD+1];

    //
    // Open registry key
    //

    if ( RegCreateKeyEx (HKEY_CURRENT_USER, FILES_KEY, 0, 0,
                             REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,
                             NULL, &hKey, &dwDisp) != ERROR_SUCCESS) {
        return;
    }


    //
    // Query the max number of files to save first.
    //

    dwMaxFilesSize = sizeof (DWORD);

    RegQueryValueEx (hKey, MAXFILES_ENTRY, NULL, &dwDataType,
                    (LPBYTE)&dwMaxFiles, &dwMaxFilesSize);

    //
    // If the user request 0 entries, then exit now.
    //
    if (dwMaxFiles == 0) {
        RegCloseKey (hKey);
        return;
    }

    //
    // Find out how many items are in the list box.
    //

    dwEnd = (DWORD)SendDlgItemMessage (hwnd, IDD_PATH, CB_GETCOUNT, 0, 0);

    //
    // If the max number of items we want to save is less than the
    // number of entries, then change the ending point.
    //

    if (dwMaxFiles < dwEnd) {
        dwEnd = dwMaxFiles;
    }

    //
    // Add the first entry (the current file)
    //

    wsprintf (szFileEntry, FILE_ENTRY, dwFileNum++);
    dwMaxFilesSize = MAXPATHFIELD+1;

    RegSetValueEx (hKey, szFileEntry, 0, REG_SZ, (CONST BYTE *)szCurrentFile,
                   sizeof (TCHAR) * (lstrlen (szCurrentFile)+1));


    //
    // Check for a duplicate string.
    //

    dwDup = (DWORD)SendDlgItemMessage (hwnd, IDD_PATH, CB_FINDSTRING,
                                (WPARAM) -1, (LPARAM) szCurrentFile);

    //
    // If we already have dwMaxFiles in the list and we don't have any
    // duplicates, then we only want to save dwMaxFiles - 1 entries
    // (drop the last entry).
    //
    //

    if ( (dwEnd == dwMaxFiles) && (dwDup == CB_ERR) ) {
        dwEnd--;
    }

    //
    // Now loop through the remaining entries
    //

    for (dwCount=0; dwCount < dwEnd; dwCount++) {

        //
        // Check to see if we are at the duplicate entry.  If
        // so skip on to the next item.
        //

        if ((dwDup != CB_ERR) && (dwCount == dwDup)) {
            continue;
        }

        //
        // Get an entry out of the listbox.
        //

        SendDlgItemMessage (hwnd, IDD_PATH, CB_GETLBTEXT, (WPARAM) dwCount,
                            (LPARAM) szRecentFilePath);

        //
        // If we get a NULL string, break out of the loop.
        //

        if (!(*szRecentFilePath) || !szRecentFilePath) {
            break;
        }

        //
        // Build the entry name
        //

        wsprintf (szFileEntry, FILE_ENTRY, dwFileNum);
        dwMaxFilesSize = MAXPATHFIELD+1;

        //
        // Save the entry
        //

        RegSetValueEx (hKey, szFileEntry, 0, REG_SZ,(CONST BYTE *) szRecentFilePath,
                       sizeof (TCHAR) * (lstrlen (szRecentFilePath)+1));

        //
        // Increment our current file number
        //

        dwFileNum++;
    }

    //
    // Close the key
    //

    RegCloseKey (hKey);

}



/*** Main --         Program entry point (was WinMain).
 *
 *
 *
 * Main(int argc, char *argv[], char *envp[])
 *
 * ENTRY -         int argc                - argument count.
 *                        char *argv[]        - argument list.
 *                        char *envp[]        - environment.
 *
 * EXIT  -           TRUE if success, FALSE if not.
 * SYNOPSIS -  Parses command line, for position to place dialog box, if no
 *                                position (came from ctl/esc) then center on screen.
 *                                Also make sure only one instance of taskman.
 *
 * WARNINGS -
 * EFFECTS  -
 */

INT __cdecl main(
   INT argc,
   CHAR *argv[],
   CHAR *envp[])
{
   MSG msg;

   /*
    * First set the priority of taskman so it is higher than foreground apps
    * that spin in loops - this way it'll always come up when you hit
    * ctrl-esc.
    */
   hInst = GetModuleHandle(NULL);

   SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);

   {
       //
       // Set the working set size to 200k.
       //

       QUOTA_LIMITS QuotaLimits;
       NTSTATUS status;

       status = NtQueryInformationProcess( NtCurrentProcess(),
                                           ProcessQuotaLimits,
                                           &QuotaLimits,
                                           sizeof(QUOTA_LIMITS),
                                           NULL );
       if (NT_SUCCESS(status)) {
           QuotaLimits.MinimumWorkingSetSize = 300 * 1024;
           QuotaLimits.MaximumWorkingSetSize = 372 * 1024;

           NtSetInformationProcess( NtCurrentProcess(),
                                    ProcessQuotaLimits,
                                    &QuotaLimits,
                                    sizeof(QUOTA_LIMITS) );
       }
   }

   /*
    * Taskman will work in the windows directory, and switch to the
    * original directory (home directory) before execing programs.
    * This is to prevent weird popups if a UNC original directory is
    * disconnected.
    */

   GetCurrentDirectory(MAXPATHFIELD, szUserHomeDir);
   GetWindowsDirectory(szWindowsDirectory, MAXPATHFIELD);
   SetCurrentDirectory(szWindowsDirectory);

   fExecOK = OKToExec();
   if (!IsUserAdmin() && !fExecOK) {
      ghwndDialog = CreateDialog(hInst, MAKEINTRESOURCE(WMPTASKMANDLG), NULL,
         TaskmanDlgProc);
   } else {
      ghwndDialog = CreateDialog(hInst, MAKEINTRESOURCE(PWRTASKMANDLG), NULL,
         TaskmanDlgProc);
   }

   if (ghwndDialog == NULL)
       return 0;

   LoadString(hInst, IDS_OOMEXITTITLE, szOOMExitTitle, 32);
   LoadString(hInst, IDS_OOMEXITMSG, szOOMExitMsg, 64);

   if (!RegisterHotKey(ghwndDialog, 1, MOD_CONTROL, VK_ESCAPE) ||
           !RegisterTasklist(ghwndDialog)) {
       goto exit;
   }

   while (GetMessage(&msg, (HWND)NULL, (UINT)0, (UINT)0)) {
       if (!IsDialogMessage(ghwndDialog, &msg)) {
          if ((msg.message == WM_SYSCOMMAND) && (msg.wParam == SC_TASKLIST)) {
             POINT pt;

             GetCursorPos(&pt);
             pt.x = max(pt.x - (dyTaskman / 2), 0);
             pt.x = min(pt.x, dxScreen - dxTaskman);
             pt.y = max(pt.y - (GetSystemMetrics(SM_CYCAPTION) * 2), 0);
             pt.y = min(pt.y, dyScreen - dyTaskman);

             ShowTasklist(pt);
          } else {

              //
              //  We need to have a regular message loop in order
              //  to handle the DDE messages generated by spawning
              //  an application via an association.
              //

              TranslateMessage (&msg);
              DispatchMessage (&msg);
          }

       }
   }

exit:
    DestroyWindow(ghwndDialog);
    return 0;

    argc;
    argv;
    envp;
}


WORD APIENTRY
ExecProgram(
    LPTSTR lpszPath,
    LPTSTR lpDir,
    LPTSTR lpTitle
    )
{
  WORD      ret;
  HCURSOR   hCursor;
  LPTSTR     lpP;
  TCHAR cSeparator;
  TCHAR lpReservedFormat[] = TEXT("dde.%d,hotkey.%d");
  TCHAR lpReserved[100];  // used for DDE request of icons from console apps
                         // add for passing the hotkey associated with an item.
  HANDLE hProcess;

  ret = 0;

  /*
   * Set the current directory to the user's home directory if possible.
   */
  SetCurrentDirectory(szUserHomeDir);

  hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));

  /* Don't mess with the mouse state; unless we're on a mouseless system.
   */
  if (!GetSystemMetrics(SM_MOUSEPRESENT))
      ShowCursor(TRUE);

  /* skip leading spaces
   */
  while (*lpszPath == TEXT(' '))
      lpszPath++;

  /* skip past path
   */
  lpP = lpszPath;
  if (*lpszPath == TEXT('"')) {
     cSeparator = TEXT('"');
     lpP++;
  }
  else {
     cSeparator = TEXT(' ');
  }

  for (; *lpP && *lpP != cSeparator; lpP = CharNext(lpP))
      ;
  if (*lpP == TEXT('"')) {
     lpP++;
  }

  /* if stuff on end, separate it
   */
  if (*lpP)
      *lpP++ = 0;


  /* Try to exec 'szCommandLine'. */

  /*changed order, since wPendINstance is a 32b HANDLE, and ret is WORD*/
    if (!lpP)
        lpP = TEXT("");

    wsprintf(lpReserved, lpReservedFormat, 0, 0);

    ret = (WORD)RealShellExecute(ghwndDialog, NULL, lpszPath, lpP,
                            lpDir, NULL, lpTitle, lpReserved,
                            (WORD)SW_SHOWNORMAL, &hProcess);

  /*BUG BUG these are DOS exec function return codes, no map yet to NT return codes!*/
  switch (ret) {
      case 0:
      case SE_ERR_OOM:    // 8
          ret = IDS_NOMEMORYMSG;
          break;

      case SE_ERR_FNF:    // 2
          ret = IDS_FILENOTFOUNDMSG;
          break;

      case SE_ERR_PNF:    // 3
          ret = IDS_BADPATHMSG;
          break;

      case 4:
          ret = IDS_MANYOPENFILESMSG;
          break;

      case 5:
          ret = IDS_ACCESSDENIED;
          break;

      case 10:
          ret = IDS_NEWWINDOWSMSG;
          break;

      case 12:
          ret = IDS_OS2APPMSG;
          break;

      case 15:
          /* KERNEL has already put up a messagebox for this one. */
          ret = 0;
          break;

      case 16:
          ret = IDS_MULTIPLEDSMSG;
          break;

      case 18:
          ret = IDS_PMODEONLYMSG;
          break;

      case 19:
          ret = IDS_COMPRESSEDEXE;
          break;

      case 20:
          ret = IDS_INVALIDDLL;
          break;

      case SE_ERR_SHARE:
          ret = IDS_SHAREERROR;
          break;

      case SE_ERR_ASSOCINCOMPLETE:
          ret = IDS_ASSOCINCOMPLETE;
          break;

      case SE_ERR_DDETIMEOUT:
      case SE_ERR_DDEFAIL:
      case SE_ERR_DDEBUSY:
          ret = IDS_DDEFAIL;
          break;

      case SE_ERR_NOASSOC:
          ret = IDS_NOASSOCMSG;
          break;

      default:
          ret = 0;
          break;
  }

  if (!GetSystemMetrics(SM_MOUSEPRESENT)) {
      /*
       * We want to turn the mouse off here on mouseless systems, but
       * the mouse will already have been turned off by USER if the
       * app has GP'd so make sure everything's kosher.
       */
      if (ShowCursor(FALSE) != -1)
          ShowCursor(TRUE);
  }

  SetCursor(hCursor);

  /*
   * Reset the working directory to the windows directory.
   */
  SetCurrentDirectory(szWindowsDirectory);

  return(ret);
}


VOID
GetDirectoryFromPath(
   PTSTR szFilePath,
   PTSTR szDir)
{
   PTSTR pFileName;
   PTSTR pExt;
   WORD ich;
   BOOL fUnc;

   *szDir = TEXT('\0');

   /* Get info about file path. */
   GetPathInfo(szFilePath, &pFileName, &pExt, &ich, &fUnc);

   /* UNC paths don't (conceptually to Progman) have a directory component. */
   if (fUnc)
      return;

   /* Does it have a directory component ? */
   if (pFileName != szFilePath) { // Yep.
      /* copy path to temp. */
      if (*szFilePath == TEXT('"')) {
         szFilePath++;
      }
      lstrcpy(szDir, szFilePath);
      /* check path style. */
      if (ich <= 3 && *(szDir+1) == TEXT(':')) {
         /*
          * The path is "c:\foo.c" or "c:foo.c" style.
          * Don't remove the last slash/colon, just the filename.
          */
         szDir[pFileName-szFilePath] = TEXT('\0');
      }

      else if (ich == 1) {
         /*
          * something like "\foo.c"
          * Don't remove the last slash/colon, just the filename.
          */
          szDir[pFileName-szFilePath] = TEXT('\0');
       }
       else {
          /*
           * The filepath is a full normal path.
           * Could be something like "..\foo.c" or ".\foo.c" though.
           * Stomp on the last slash to get just the path.
           */
           szDir[pFileName-szFilePath-1] = TEXT('\0');
       }
    }

    /* else just a filename with no path. */
}

VOID
GetFilenameFromPath(
   PTSTR szPath,
   PTSTR szFilename)
{
   DWORD dummy;
   PTSTR pFileName;
   BOOL fUNC;

   GetPathInfo(szPath, &pFileName, (PTSTR*) &dummy, (WORD*) &dummy,
      &fUNC);

   /* If it's a UNC then the 'filename' part is the whole thing. */
   if (fUNC || (szPath == pFileName))
      lstrcpy(szFilename, szPath);
   else {
      if (*szPath == TEXT('"')) {
         *szFilename++ = TEXT('"');
      }
      lstrcpy(szFilename, pFileName);
   }
}


VOID
GetPathInfo(
   PTSTR szPath,
   PTSTR *pszFileName,
   PTSTR *pszExt,
   WORD *pich,
   BOOL *pfUnc)
{
   TCHAR *pch;          // Temp variable.
   WORD ich = 0;       // Temp.
   BOOL InQuotes;

   *pszExt = NULL;         // If no extension, return NULL.
   *pszFileName = szPath;  // If no seperate filename component, return path.
   *pich = 0;
   *pfUnc = FALSE;         // Default to not UNC style.

   //
   // Check if path is in quotes.
   //
   if (InQuotes = (*szPath == TEXT('"'))) {
      szPath++;
   }

   // Check for UNC style paths.
   if (*szPath == TEXT('\\') && *(szPath+1) == TEXT('\\'))
      *pfUnc = TRUE;

   // Search forward to find the last backslash or colon in the path.
   // While we're at it, look for the last dot.
   for (pch = szPath; *pch; pch = CharNext(pch)) {

      if ((*pch == TEXT(' ')) && (!InQuotes)) {
         // Found a space - stop here.
         break;
      }
      if (*pch == TEXT('"')) {
         // Found a the second quote - stop here.
         pch++;
         break;
      }
      if (*pch == TEXT('\\') || *pch == TEXT(':')) {
         // Found it, record ptr to it and it's index.
         *pszFileName = pch+1;
         *pich = ich + (WORD)1;
      }
      if (*pch == TEXT('.')) {
         // Found a dot.
         *pszExt = pch;
      }
      ich++;
   }

   /* Check that the last dot is part of the last filename. */
   if (*pszExt < *pszFileName)
      *pszExt = NULL;
}


BOOL
IsUserAdmin()
{
    BOOL UserIsAdmin = FALSE;
    HANDLE Token;
    PSID    AdminAliasSid = NULL;
    SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;

    //
    // Get the token of the current process.
    //

    if (!OpenProcessToken(
          GetCurrentProcess(),
          TOKEN_QUERY,
          &Token) ) {
       return(FALSE);
    }

    UserIsAdmin = TestTokenForAdmin(Token);


    CloseHandle(Token);

    return(UserIsAdmin);
}

BOOL
TestTokenForAdmin(
   HANDLE Token
   )
{
   NTSTATUS    Status;
   DWORD       InfoLength;
   PTOKEN_GROUPS TokenGroupList;
   DWORD       GroupIndex;
   PSID        AdminSid;
   BOOL        FoundAdmin;
   SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;

   //
   // Get a list of groups in the token
   //

   Status = NtQueryInformationToken(
      Token,                    // Handle
      TokenGroups,              // TokenInformationClass
      NULL,                     // TokenInformation
      0,                        // TokenInformationLength
      &InfoLength               // ReturnLength
   );

   if ((Status != STATUS_SUCCESS) && (Status != STATUS_BUFFER_TOO_SMALL)) {
      return(FALSE);
   }

   TokenGroupList = Alloc(InfoLength);

   if (TokenGroupList == NULL) {
      return(FALSE);
   }

   Status = NtQueryInformationToken(
      Token,                    // Handle
      TokenGroups,              // TokenInformationClass
      TokenGroupList,           // TokenInformation
      InfoLength,               // TokenInformationLength
      &InfoLength               // ReturnLength
   );

   if (!NT_SUCCESS(Status)) {
      LocalFree(TokenGroupList);
      return(FALSE);
   }

   //
   // Create the admin sid
   //
   Status = RtlAllocateAndInitializeSid(
      &SystemSidAuthority, 2,
      SECURITY_BUILTIN_DOMAIN_RID,
      DOMAIN_ALIAS_RID_ADMINS,
      0, 0, 0, 0, 0, 0,
      &AdminSid);

   if (!NT_SUCCESS(Status)) {
      Free(TokenGroupList);
      return(FALSE);
   }

   //
   // Search group list for admin alias
   //
   FoundAdmin = FALSE;

   for (GroupIndex=0; GroupIndex < TokenGroupList->GroupCount; GroupIndex++ ) {
      if (RtlEqualSid(TokenGroupList->Groups[GroupIndex].Sid, AdminSid)) {
         FoundAdmin = TRUE;
         break;
      }
   }

   //
   // Tidy up
   //

   RtlFreeSid(AdminSid);
   Free(TokenGroupList);

   return(FoundAdmin);
}

BOOL
OKToExec()
{
   TCHAR szRestrict[]        = TEXT("Restrictions");
   TCHAR szProgramManager[]  = TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Program Manager");
   HKEY hkeyProgramManager  = NULL;  // progman.ini key
   HKEY hkeyPMRestrict      = NULL;
   DWORD cbData, dwType;
   BOOL fNoRun = FALSE;

  /*
   * Create/Open the registry keys corresponding to progman.ini sections.
   */
  if (!RegCreateKeyEx(HKEY_CURRENT_USER, szProgramManager, 0, szProgramManager, 0,
                         KEY_READ | KEY_WRITE, NULL, &hkeyProgramManager, NULL)) {

      RegCreateKeyEx(hkeyProgramManager, szRestrict, 0, szProgramManager, 0,
                         KEY_READ, NULL, &hkeyPMRestrict, NULL);

  } else {
    return(FALSE);
  }

  if (hkeyPMRestrict) {
      cbData = sizeof(fNoRun);
      RegQueryValueEx(hkeyPMRestrict, szNoRun, 0, &dwType, (LPBYTE)&fNoRun,
         &cbData);
  }

  if (hkeyPMRestrict) {
      RegCloseKey(hkeyPMRestrict);
      hkeyPMRestrict = NULL;
  }

  RegCloseKey(hkeyProgramManager);

  return(!fNoRun);

}

INT
MyMessageBox(
   HWND hWnd,
   WORD idTitle,
   WORD idMessage,
   PWSTR psz,
   WORD wStyle)
{
    WCHAR    szTempField[MAXMSGBOXLEN];
    INT     iMsgResult;

    if (!LoadString(hInst, idTitle, szTitle, ARRAYSIZE(szTitle))){
        goto MessageBoxOOM;
    }
    if (idMessage < 32){
        if (!LoadString(hInst, IDS_UNKNOWNMSG, szTempField, ARRAYSIZE(szTempField))){
            goto MessageBoxOOM;
        }
        wsprintf(szMessage, szTempField, idMessage);
    }
    else{
        if (!LoadString(hInst, idMessage, szTempField, ARRAYSIZE(szTempField)))
            goto MessageBoxOOM;

        if (psz) {
            wsprintf(szMessage, szTempField, (LPTSTR)psz);
        }
        else
            lstrcpy(szMessage, szTempField);
    }

    iMsgResult = MessageBox(hWnd, szMessage, szTitle, wStyle );

    if (iMsgResult == -1){

MessageBoxOOM:
        MessageBox(hWnd, szOOMExitMsg, szOOMExitTitle, MB_SYSTEMMODAL | MB_ICONHAND | MB_OK);
    }

    return(iMsgResult);
}
