<commit_before>/*
* Copyright (c) 2014 <NAME> <<EMAIL>>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

// ThreadedDlg.cpp : implementation file
//

#include "stdafx.h"

#include "SDThread.h"
#include "VirtualKeyboard/VKeyBoardDlg.h"

#include "ThisMfcApp.h"
#include "DboxMain.h"
#include "resource3.h"

#include "core/core.h" // for IDSC_UNKNOWN_ERROR
#include "core/PWSprefs.h"
#include "core/PWPolicy.h"
#include "core/PWCharPool.h" // for CheckPassword()
#include "os/debug.h"

#include "resource.h"

#include <aclapi.h>
#include <tlhelp32.h>
#include <algorithm>

// Following makes debugging SD UI changes feasible
// Of course, remove if/when debugging the Secure Desktop funtionality itself...
#ifdef _DEBUG
#define NO_NEW_DESKTOP
#endif

int iStartTime;  // Start time for SD timer - does get reset by edit changes or mouse clicks (VK)

extern ThisMfcApp app;

extern LRESULT CALLBACK MsgFilter(int code, WPARAM wParam, LPARAM lParam);

template<class T> void  RDLL_LoadAString(HINSTANCE hInstResDLL, T &s, int id)
{
  // Load a String from the Application or Resource DLL (can't use MFC approach
  // or MFC CString class)
  TCHAR *psBuffer;
  int len = LoadString(hInstResDLL, id, reinterpret_cast<LPTSTR>(&psBuffer), 0);

  if (len)
    s = T(psBuffer, len);
  else
    s = T();
}

CSDThread::CSDThread(int iDialogID, GetMasterPhrase *pGMP,
                     HMONITOR hCurrentMonitor, bool bUseSecureDesktop)
  : m_pGMP(pGMP), m_wDialogID((WORD)iDialogID),
  m_hCurrentMonitor(hCurrentMonitor), m_bUseSecureDesktop(bUseSecureDesktop),
  m_passkeyID(-1), m_hNewDesktop(NULL), m_hwndMasterPhraseDlg(NULL), m_pVKeyBoardDlg(NULL),
  m_bDoTimerProcAction(false), m_bMPWindowBeingShown(false), m_bVKWindowBeingShown(false),
  m_iMinutes(-1), m_iSeconds(-1), m_hWaitableTimer(0)
{
  InitInstance();
}

CSDThread::~CSDThread()
{
}

BOOL CSDThread::InitInstance()
{
  // Get us
  m_hInstance = GetModuleHandle(NULL);

  // Only called once the Thread is "Resumed"
  _AFX_THREAD_STATE *pState = AfxGetThreadState();

  if (pState->m_hHookOldMsgFilter) {
    if (!UnhookWindowsHookEx(pState->m_hHookOldMsgFilter)) {
      pws_os::IssueError(_T("UnhookWindowsHookEx"), false);
      ASSERT(0);
    }
    pState->m_hHookOldMsgFilter = NULL;
  }

  m_pGMP->clear();

  m_iUserTimeLimit = PWSprefs::GetInstance()->GetPref(PWSprefs::SecureDesktopTimeout);
  m_hTimer = NULL;

  // Clear progress flags
  xFlags = 0;

  // Get Resource DLL (if any)
  m_hInstResDLL = app.GetResourceDLL();
  if (!m_hInstResDLL)
    m_hInstResDLL = m_hInstance;

  return TRUE;
}

DWORD WINAPI CSDThread::SDThreadProc(LPVOID lpParameter)
{
  CSDThread *self = (CSDThread *)lpParameter;

  return self->ThreadProc();
}

DWORD CSDThread::ThreadProc()
{
  WNDCLASS wc = { 0 };

  StringX sxTemp, sxPrefix;
  DWORD dwError;
  m_dwRC = (DWORD)-1;

  m_pGMP->clear();
  m_hwndVKeyBoard = NULL;

  PWPolicy policy;

  // Get Monitor Images before we create new desktop
  GetMonitorImages();

  // Update Progress
  xFlags |= MONITORIMAGESCREATED;

  // Get uppercase prefix - 1st character for Desktop name, 2nd for Window Class name
  policy.flags = PWPolicy::UseUppercase;
  policy.length = 2;
  policy.upperminlength = 2;

  sxPrefix = policy.MakeRandomPassword();

  // Future use of this is for the next 15 characters of Dekstop & Window Class names
  policy.flags = PWPolicy::UseLowercase | PWPolicy::UseUppercase | PWPolicy::UseDigits;
  policy.length = 15;
  policy.lowerminlength = policy.upperminlength = policy.digitminlength = 1;

#ifndef NO_NEW_DESKTOP
  m_hOriginalDesk = GetThreadDesktop(GetCurrentThreadId());

  // Ensure we don't use an existing Desktop (very unlikely but....)
  do {
    //Create random Desktop name
    sxTemp = sxPrefix.substr(0, 1) + policy.MakeRandomPassword();

    m_sDesktopName = sxTemp.c_str();

    // Check not already there
    CheckDesktop();
  } while (m_bDesktopPresent);

  // Windows starts a copy of ctfmon.exe per Desktop for support of Microsoft Text
  // Services. Ctfmon.exe will also prevent a new Desktop closing after all Windows
  // on it have closed.
  // After the thread thread has switched back to the original desktop (but before
  // the new Desktop is closed), we terminate all processes running on that new
  // Desktop.  This includes ctfmon.exe.

  // Try to create the necessary Security Attributes i.e. only giving the
  // current user Desktop specific access rights and standard access to the Desktop
  // On systems running NVIDIA Display Driver Service (nvsvc), it will normally stop CloseDesktop
  // completing the close.  By setting these Security Asttributes, nvsvc is prevented from
  // grabbing the new Desktop.

  // DO NOT add access DESKTOP_HOOKCONTROL as it will allow other processes to grap the
  // passphrase and/or record mouse clicks on the Virtual Keyboard.
  DWORD dwDesiredAccess = DESKTOP_CREATEWINDOW | DESKTOP_ENUMERATE |
    DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | DESKTOP_SWITCHDESKTOP |
    DELETE | READ_CONTROL | WRITE_DAC;

  SECURITY_ATTRIBUTES sa;
  PSECURITY_DESCRIPTOR pSD(NULL);
  PACL pACL(NULL);
  PSID pCurrentUserSID(NULL);

  bool bSA_Created = CreateSA(sa, pSD, pACL, pCurrentUserSID);

  pws_os::Trace(L"NewDesktop %s\n", m_sDesktopName.c_str());
  m_hNewDesktop = CreateDesktop(m_sDesktopName.c_str(), NULL, NULL, 0, dwDesiredAccess, bSA_Created ? &sa : NULL);

  // Free security data no longer required
  if (pCurrentUserSID)
    FreeSid(pCurrentUserSID);
  if (pACL)
    LocalFree(pACL);
  if (pSD)
    LocalFree(pSD);

  if (m_hNewDesktop == NULL) {
    dwError = pws_os::IssueError(_T("CreateDesktop (new)"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags |= NEWDESKTOCREATED;

  if (!SetThreadDesktop(m_hNewDesktop)) {
    dwError = pws_os::IssueError(_T("SetThreadDesktop to new"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags |= SETTHREADDESKTOP;
#endif

  // Ensure we don't use an existing Window Class Name (very unlikely but....)
  do {
    // Create random Modeless Overlayed Background Window Class Name
    sxTemp = sxPrefix.substr(1, 1) + policy.MakeRandomPassword();

    m_sBkGrndClassName = sxTemp.c_str();

    // Check not already there
    CheckWindow();
  } while (m_bWindowPresent);

  // Register the Window Class Name
  wc.lpfnWndProc = ::DefWindowProc;
  wc.hInstance = m_hInstance;
  wc.lpszClassName = m_sBkGrndClassName.c_str();
  if (!RegisterClass(&wc)) {
    dwError = pws_os::IssueError(_T("RegisterClass - Background Window"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags |= REGISTEREDWINDOWCLASS;

  for (MonitorImageInfoIter it = m_vMonitorImageInfo.begin(); it != m_vMonitorImageInfo.end(); it++) {
    // Window styles: WS_EX_LAYERED for easy painting, WS_EX_TOOLWINDOW so not to appear in Alt+Tab
    HWND hwndBkGrndWindow = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOOLWINDOW,
      m_sBkGrndClassName.c_str(), NULL, WS_POPUP | WS_VISIBLE,
      it->left, it->top, it->width, it->height,
      NULL, NULL, m_hInstance, NULL);

    if (!hwndBkGrndWindow) {
      dwError = pws_os::IssueError(_T("CreateWindowEx - Background"), false);
      ASSERT(0);
      goto BadExit;
    }

    // Save handle to background window
    it->hwndBkGrndWindow = hwndBkGrndWindow;

    HDC hdcScreen = GetDC(0);
    HDC hdc = CreateCompatibleDC(hdcScreen);
    HBITMAP hbmOld = (HBITMAP)SelectObject(hdc, it->hbmDimmendMonitorImage);

    // Get the size of the bitmap
    BITMAP bm;
    GetObject(it->hbmDimmendMonitorImage, sizeof(bm), &bm);
    SIZE sizeBkGnd = {bm.bmWidth, bm.bmHeight};

    // Use the source image's alpha channel for blending
    BLENDFUNCTION bf = {0};
    bf.BlendOp = AC_SRC_OVER;
    bf.SourceConstantAlpha = 255;
    bf.AlphaFormat = AC_SRC_ALPHA;

    POINT ptZero = { 0 ,0 };

    // Paint the window (in the right location) with the alpha-blended bitmap
    UpdateLayeredWindow(hwndBkGrndWindow, NULL, NULL, &sizeBkGnd,
      hdc, &ptZero, RGB(0, 0, 0), &bf, ULW_OPAQUE);

    // Don't allow any action if this is clicked!
    EnableWindow(hwndBkGrndWindow, FALSE);

    // Tidy up GDI resources
    ::ReleaseDC(NULL, hdcScreen);
    ::SelectObject(hdc, hbmOld);
    ::DeleteDC(hdc);
  }

  // Update Progress
  xFlags |= BACKGROUNDWINDOWSCREATED;


#ifndef NO_NEW_DESKTOP
  if (!SwitchDesktop(m_hNewDesktop)) {
    dwError = pws_os::IssueError(_T("SwitchDesktop to new"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags |= SWITCHEDDESKTOP;
#endif

  m_hwndMasterPhraseDlg = CreateDialogParam(m_hInstResDLL, MAKEINTRESOURCE(m_wDialogID),
    HWND_DESKTOP, (DLGPROC)MPDialogProc, reinterpret_cast<LPARAM>(this));

  if (!m_hwndMasterPhraseDlg) {
    dwError = pws_os::IssueError(_T("CreateDialogParam - IDD_SDGETPHRASE"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags |= MASTERPHRASEDIALOGCREATED;

  // Use first monitor's background window as owner for virtual keyboard
  m_pVKeyBoardDlg = new CVKeyBoardDlg(m_hInstResDLL, m_vMonitorImageInfo[0].hwndBkGrndWindow, m_hwndMasterPhraseDlg);

  // Update Progress
  xFlags |= VIRTUALKEYBOARDCREATED;

  ShowWindow(m_hwndMasterPhraseDlg, SW_SHOW);

  MSG msg;
  BOOL brc;

  // Message loop - break out on WM_QUIT or error
  while ((brc = GetMessage(&msg, 0, 0, 0)) != 0) {
    if (brc == -1)
      break;

    // Do not allow Escape to end this dialog
    if ((msg.message == WM_KEYDOWN || msg.message == WM_KEYUP) && msg.wParam == VK_ESCAPE)
      continue;

    if (!IsDialogMessage(m_hwndMasterPhraseDlg, &msg)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }

  // Call DialogProc directly to clear "self".
  // NOTE: - it would NEVER get the WM_QUIT message EVER as this
  // is used to get out of the message loop above(see GetMessage)
  MPDialogProc(NULL, WM_QUIT, NULL, NULL);

  // Update Progress
  xFlags |= MASTERPHRASEDIALOGENDED;

  // Destroy Masterphrase window
  if (!DestroyWindow(m_hwndMasterPhraseDlg)) {
    dwError = pws_os::IssueError(_T("DestroyWindow - IDD_SDGETPHRASE"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags &= ~MASTERPHRASEDIALOGCREATED;

  // Delete Virtual Keyboard instance
  delete m_pVKeyBoardDlg;

  // Update Progress
  xFlags &= ~VIRTUALKEYBOARDCREATED;

  // Destroy background layered windows, images & monitor DCs
  for (MonitorImageInfoIter it = m_vMonitorImageInfo.begin(); it != m_vMonitorImageInfo.end(); it++) {
    if (it->hwndBkGrndWindow != NULL)
      DestroyWindow(it->hwndBkGrndWindow);
    if (it->hbmDimmendMonitorImage)
      ::DeleteObject(it->hbmDimmendMonitorImage);
    if (it->hdcMonitor)
      ::DeleteDC(it->hdcMonitor);
  }

  // Update Progress
  xFlags &= ~(BACKGROUNDWINDOWSCREATED | MONITORIMAGESCREATED);

  // Unregister it
  if (!UnregisterClass(m_sBkGrndClassName.c_str(), m_hInstance)) {
    dwError = pws_os::IssueError(_T("UnregisterClass - Background"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags &= ~REGISTEREDWINDOWCLASS;

#ifndef NO_NEW_DESKTOP
  if (xFlags & SWITCHEDDESKTOP) {
    // Switch back to the initial desktop - due to the possibility that
    // the user may have locked the screen (manually or automatically),
    // or the computer may have gone to sleep/hibernated, we can't switch
    // back to the original Desktop if the MS WinLogon Desktop has control.
    // We have to wait (and keep on trying) until the user's original Desktop
    // is back in control - hence this loop.
    pws_os::Trace(L"ThreadProc SwitchDesktop\n");
    while (!SwitchDesktop(m_hOriginalDesk)) {
      pws_os::Trace(L"SwitchDesktop(m_hOriginalDesk)\n");
      dwError = pws_os::IssueError(_T("SwitchDesktop - back to original"), false);
      if (dwError != ERROR_SUCCESS)
        ASSERT(0);

      ::Sleep(500);
    }

    // Update Progress
    xFlags &= ~SWITCHEDDESKTOP;
  }

  if (xFlags & SETTHREADDESKTOP) {
    // Switch thread back to original Desktop
    if (!SetThreadDesktop(m_hOriginalDesk)) {
      dwError = pws_os::IssueError(_T("SetThreadDesktop - back to original"), false);
      ASSERT(0);
      goto BadExit;
    }
    // Update Progress
    xFlags &= ~SETTHREADDESKTOP;
  }

  // Now that thread is ending - close new Desktop
  if (xFlags & NEWDESKTOCREATED) {
    // Terminate processes still on new Desktop BEFORE we close it
    TerminateProcesses();

    // Note: There can be a good return code even if it does not close
    // due to programs external to PWS keeping it around!
    if (!CloseDesktop(m_hNewDesktop)) {
      dwError = pws_os::IssueError(_T("CloseDesktop (new)"), false);
      ASSERT(0);
    } else {
      // Update Progress
      xFlags &= ~NEWDESKTOCREATED;
    }
  }
#endif

  // Clear variables - just in case someone decides to reuse this instance
  m_pVKeyBoardDlg = NULL;
  m_hwndMasterPhraseDlg = NULL;
  m_sBkGrndClassName.clear();
  m_sDesktopName.clear();
  m_vMonitorImageInfo.clear();

  return m_dwRC;

BadExit:
  // Need to tidy up what was done in reverse order - ignoring what wasn't and ignore errors
  // First clear anything the user has entered
  m_pGMP->clear();

  if (xFlags & VIRTUALKEYBOARDCREATED) {
    // Delete Virtual Keyboard instance
    delete m_pVKeyBoardDlg;

    // Update Progress
    xFlags &= ~VIRTUALKEYBOARDCREATED;
  }

  if (xFlags & MASTERPHRASEDIALOGCREATED) {
    // Destroy master phrase dialog window
    DestroyWindow(m_hwndMasterPhraseDlg);

    // Update Progress
    xFlags &= ~MASTERPHRASEDIALOGCREATED;
  }

  if (xFlags & BACKGROUNDWINDOWSCREATED || xFlags & MONITORIMAGESCREATED) {
    // Destroy background layered window's, images & monitor DCs
    for (MonitorImageInfoIter it = m_vMonitorImageInfo.begin(); it != m_vMonitorImageInfo.end(); it++) {
      if (it->hwndBkGrndWindow)
        DestroyWindow(it->hwndBkGrndWindow);
      if (it->hbmDimmendMonitorImage)
        ::DeleteObject(it->hbmDimmendMonitorImage);
      if (it->hdcMonitor)
        ::DeleteDC(it->hdcMonitor);
    }

    // Update Progress
    xFlags &= ~(BACKGROUNDWINDOWSCREATED | MONITORIMAGESCREATED);
  }

  if (xFlags & REGISTEREDWINDOWCLASS) {
    // Unregister background windows' registered class
    UnregisterClass(m_sBkGrndClassName.c_str(), m_hInstance);

    // Update Progress
    xFlags &= ~REGISTEREDWINDOWCLASS;
  }

  if (xFlags & SWITCHEDDESKTOP) {
    // Switch back to the initial desktop
    while (!SwitchDesktop(m_hOriginalDesk)) {
      dwError = pws_os::IssueError(_T("SwitchDesktop - back to original (bad exit)"), false);
      if (dwError != ERROR_SUCCESS)
        ASSERT(0);

      ::Sleep(500);
    }
    // Update Progress
    xFlags &= ~SWITCHEDDESKTOP;
  }

  if (xFlags & SETTHREADDESKTOP) {
    // Switch thread back to initial desktop
    if (!SetThreadDesktop(m_hOriginalDesk)) {
      dwError = pws_os::IssueError(_T("SetThreadDesktop - back to original (bad exit)"), false);
      ASSERT(0);<commit_msg>Use WinSta0_DesktopSwitch event to reduce impact of loop trying to switch back to original desktop<commit_after>/*
* Copyright (c) 2014 <NAME> <<EMAIL>>.
* All rights reserved. Use of the code is allowed under the
* Artistic License 2.0 terms, as specified in the LICENSE file
* distributed with this code, or available from
* http://www.opensource.org/licenses/artistic-license-2.0.php
*/

// ThreadedDlg.cpp : implementation file
//

#include "stdafx.h"

#include "SDThread.h"
#include "VirtualKeyboard/VKeyBoardDlg.h"

#include "ThisMfcApp.h"
#include "DboxMain.h"
#include "resource3.h"

#include "core/core.h" // for IDSC_UNKNOWN_ERROR
#include "core/PWSprefs.h"
#include "core/PWPolicy.h"
#include "core/PWCharPool.h" // for CheckPassword()
#include "os/debug.h"

#include "resource.h"

#include <aclapi.h>
#include <tlhelp32.h>
#include <algorithm>

// Following makes debugging SD UI changes feasible
// Of course, remove if/when debugging the Secure Desktop funtionality itself...
#ifdef _DEBUG
#define NO_NEW_DESKTOP
#endif

int iStartTime;  // Start time for SD timer - does get reset by edit changes or mouse clicks (VK)

extern ThisMfcApp app;

extern LRESULT CALLBACK MsgFilter(int code, WPARAM wParam, LPARAM lParam);

template<class T> void  RDLL_LoadAString(HINSTANCE hInstResDLL, T &s, int id)
{
  // Load a String from the Application or Resource DLL (can't use MFC approach
  // or MFC CString class)
  TCHAR *psBuffer;
  int len = LoadString(hInstResDLL, id, reinterpret_cast<LPTSTR>(&psBuffer), 0);

  if (len)
    s = T(psBuffer, len);
  else
    s = T();
}

CSDThread::CSDThread(int iDialogID, GetMasterPhrase *pGMP,
                     HMONITOR hCurrentMonitor, bool bUseSecureDesktop)
  : m_pGMP(pGMP), m_wDialogID((WORD)iDialogID),
  m_hCurrentMonitor(hCurrentMonitor), m_bUseSecureDesktop(bUseSecureDesktop),
  m_passkeyID(-1), m_hNewDesktop(NULL), m_hwndMasterPhraseDlg(NULL), m_pVKeyBoardDlg(NULL),
  m_bDoTimerProcAction(false), m_bMPWindowBeingShown(false), m_bVKWindowBeingShown(false),
  m_iMinutes(-1), m_iSeconds(-1), m_hWaitableTimer(0)
{
  InitInstance();
}

CSDThread::~CSDThread()
{
}

BOOL CSDThread::InitInstance()
{
  // Get us
  m_hInstance = GetModuleHandle(NULL);

  // Only called once the Thread is "Resumed"
  _AFX_THREAD_STATE *pState = AfxGetThreadState();

  if (pState->m_hHookOldMsgFilter) {
    if (!UnhookWindowsHookEx(pState->m_hHookOldMsgFilter)) {
      pws_os::IssueError(_T("UnhookWindowsHookEx"), false);
      ASSERT(0);
    }
    pState->m_hHookOldMsgFilter = NULL;
  }

  m_pGMP->clear();

  m_iUserTimeLimit = PWSprefs::GetInstance()->GetPref(PWSprefs::SecureDesktopTimeout);
  m_hTimer = NULL;

  // Clear progress flags
  xFlags = 0;

  // Get Resource DLL (if any)
  m_hInstResDLL = app.GetResourceDLL();
  if (!m_hInstResDLL)
    m_hInstResDLL = m_hInstance;

  return TRUE;
}

DWORD WINAPI CSDThread::SDThreadProc(LPVOID lpParameter)
{
  CSDThread *self = (CSDThread *)lpParameter;

  return self->ThreadProc();
}

DWORD CSDThread::ThreadProc()
{
  WNDCLASS wc = { 0 };

  StringX sxTemp, sxPrefix;
  DWORD dwError;
  m_dwRC = (DWORD)-1;

  m_pGMP->clear();
  m_hwndVKeyBoard = NULL;

  PWPolicy policy;

  // Get Monitor Images before we create new desktop
  GetMonitorImages();

  // Update Progress
  xFlags |= MONITORIMAGESCREATED;

  // Get uppercase prefix - 1st character for Desktop name, 2nd for Window Class name
  policy.flags = PWPolicy::UseUppercase;
  policy.length = 2;
  policy.upperminlength = 2;

  sxPrefix = policy.MakeRandomPassword();

  // Future use of this is for the next 15 characters of Dekstop & Window Class names
  policy.flags = PWPolicy::UseLowercase | PWPolicy::UseUppercase | PWPolicy::UseDigits;
  policy.length = 15;
  policy.lowerminlength = policy.upperminlength = policy.digitminlength = 1;

#ifndef NO_NEW_DESKTOP
  m_hOriginalDesk = GetThreadDesktop(GetCurrentThreadId());

  // Ensure we don't use an existing Desktop (very unlikely but....)
  do {
    //Create random Desktop name
    sxTemp = sxPrefix.substr(0, 1) + policy.MakeRandomPassword();

    m_sDesktopName = sxTemp.c_str();

    // Check not already there
    CheckDesktop();
  } while (m_bDesktopPresent);

  // Windows starts a copy of ctfmon.exe per Desktop for support of Microsoft Text
  // Services. Ctfmon.exe will also prevent a new Desktop closing after all Windows
  // on it have closed.
  // After the thread thread has switched back to the original desktop (but before
  // the new Desktop is closed), we terminate all processes running on that new
  // Desktop.  This includes ctfmon.exe.

  // Try to create the necessary Security Attributes i.e. only giving the
  // current user Desktop specific access rights and standard access to the Desktop
  // On systems running NVIDIA Display Driver Service (nvsvc), it will normally stop CloseDesktop
  // completing the close.  By setting these Security Asttributes, nvsvc is prevented from
  // grabbing the new Desktop.

  // DO NOT add access DESKTOP_HOOKCONTROL as it will allow other processes to grap the
  // passphrase and/or record mouse clicks on the Virtual Keyboard.
  DWORD dwDesiredAccess = DESKTOP_CREATEWINDOW | DESKTOP_ENUMERATE |
    DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS | DESKTOP_SWITCHDESKTOP |
    DELETE | READ_CONTROL | WRITE_DAC;

  SECURITY_ATTRIBUTES sa;
  PSECURITY_DESCRIPTOR pSD(NULL);
  PACL pACL(NULL);
  PSID pCurrentUserSID(NULL);

  bool bSA_Created = CreateSA(sa, pSD, pACL, pCurrentUserSID);

  pws_os::Trace(L"NewDesktop %s\n", m_sDesktopName.c_str());
  m_hNewDesktop = CreateDesktop(m_sDesktopName.c_str(), NULL, NULL, 0, dwDesiredAccess, bSA_Created ? &sa : NULL);

  // Free security data no longer required
  if (pCurrentUserSID)
    FreeSid(pCurrentUserSID);
  if (pACL)
    LocalFree(pACL);
  if (pSD)
    LocalFree(pSD);

  if (m_hNewDesktop == NULL) {
    dwError = pws_os::IssueError(_T("CreateDesktop (new)"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags |= NEWDESKTOCREATED;

  if (!SetThreadDesktop(m_hNewDesktop)) {
    dwError = pws_os::IssueError(_T("SetThreadDesktop to new"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags |= SETTHREADDESKTOP;
#endif

  // Ensure we don't use an existing Window Class Name (very unlikely but....)
  do {
    // Create random Modeless Overlayed Background Window Class Name
    sxTemp = sxPrefix.substr(1, 1) + policy.MakeRandomPassword();

    m_sBkGrndClassName = sxTemp.c_str();

    // Check not already there
    CheckWindow();
  } while (m_bWindowPresent);

  // Register the Window Class Name
  wc.lpfnWndProc = ::DefWindowProc;
  wc.hInstance = m_hInstance;
  wc.lpszClassName = m_sBkGrndClassName.c_str();
  if (!RegisterClass(&wc)) {
    dwError = pws_os::IssueError(_T("RegisterClass - Background Window"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags |= REGISTEREDWINDOWCLASS;

  for (MonitorImageInfoIter it = m_vMonitorImageInfo.begin(); it != m_vMonitorImageInfo.end(); it++) {
    // Window styles: WS_EX_LAYERED for easy painting, WS_EX_TOOLWINDOW so not to appear in Alt+Tab
    HWND hwndBkGrndWindow = CreateWindowEx(WS_EX_LAYERED | WS_EX_TOOLWINDOW,
      m_sBkGrndClassName.c_str(), NULL, WS_POPUP | WS_VISIBLE,
      it->left, it->top, it->width, it->height,
      NULL, NULL, m_hInstance, NULL);

    if (!hwndBkGrndWindow) {
      dwError = pws_os::IssueError(_T("CreateWindowEx - Background"), false);
      ASSERT(0);
      goto BadExit;
    }

    // Save handle to background window
    it->hwndBkGrndWindow = hwndBkGrndWindow;

    HDC hdcScreen = GetDC(0);
    HDC hdc = CreateCompatibleDC(hdcScreen);
    HBITMAP hbmOld = (HBITMAP)SelectObject(hdc, it->hbmDimmendMonitorImage);

    // Get the size of the bitmap
    BITMAP bm;
    GetObject(it->hbmDimmendMonitorImage, sizeof(bm), &bm);
    SIZE sizeBkGnd = {bm.bmWidth, bm.bmHeight};

    // Use the source image's alpha channel for blending
    BLENDFUNCTION bf = {0};
    bf.BlendOp = AC_SRC_OVER;
    bf.SourceConstantAlpha = 255;
    bf.AlphaFormat = AC_SRC_ALPHA;

    POINT ptZero = { 0 ,0 };

    // Paint the window (in the right location) with the alpha-blended bitmap
    UpdateLayeredWindow(hwndBkGrndWindow, NULL, NULL, &sizeBkGnd,
      hdc, &ptZero, RGB(0, 0, 0), &bf, ULW_OPAQUE);

    // Don't allow any action if this is clicked!
    EnableWindow(hwndBkGrndWindow, FALSE);

    // Tidy up GDI resources
    ::ReleaseDC(NULL, hdcScreen);
    ::SelectObject(hdc, hbmOld);
    ::DeleteDC(hdc);
  }

  // Update Progress
  xFlags |= BACKGROUNDWINDOWSCREATED;


#ifndef NO_NEW_DESKTOP
  if (!SwitchDesktop(m_hNewDesktop)) {
    dwError = pws_os::IssueError(_T("SwitchDesktop to new"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags |= SWITCHEDDESKTOP;
#endif

  m_hwndMasterPhraseDlg = CreateDialogParam(m_hInstResDLL, MAKEINTRESOURCE(m_wDialogID),
    HWND_DESKTOP, (DLGPROC)MPDialogProc, reinterpret_cast<LPARAM>(this));

  if (!m_hwndMasterPhraseDlg) {
    dwError = pws_os::IssueError(_T("CreateDialogParam - IDD_SDGETPHRASE"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags |= MASTERPHRASEDIALOGCREATED;

  // Use first monitor's background window as owner for virtual keyboard
  m_pVKeyBoardDlg = new CVKeyBoardDlg(m_hInstResDLL, m_vMonitorImageInfo[0].hwndBkGrndWindow, m_hwndMasterPhraseDlg);

  // Update Progress
  xFlags |= VIRTUALKEYBOARDCREATED;

  ShowWindow(m_hwndMasterPhraseDlg, SW_SHOW);

  MSG msg;
  BOOL brc;

  // Message loop - break out on WM_QUIT or error
  while ((brc = GetMessage(&msg, 0, 0, 0)) != 0) {
    if (brc == -1)
      break;

    // Do not allow Escape to end this dialog
    if ((msg.message == WM_KEYDOWN || msg.message == WM_KEYUP) && msg.wParam == VK_ESCAPE)
      continue;

    if (!IsDialogMessage(m_hwndMasterPhraseDlg, &msg)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }

  // Call DialogProc directly to clear "self".
  // NOTE: - it would NEVER get the WM_QUIT message EVER as this
  // is used to get out of the message loop above(see GetMessage)
  MPDialogProc(NULL, WM_QUIT, NULL, NULL);

  // Update Progress
  xFlags |= MASTERPHRASEDIALOGENDED;

  // Destroy Masterphrase window
  if (!DestroyWindow(m_hwndMasterPhraseDlg)) {
    dwError = pws_os::IssueError(_T("DestroyWindow - IDD_SDGETPHRASE"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags &= ~MASTERPHRASEDIALOGCREATED;

  // Delete Virtual Keyboard instance
  delete m_pVKeyBoardDlg;

  // Update Progress
  xFlags &= ~VIRTUALKEYBOARDCREATED;

  // Destroy background layered windows, images & monitor DCs
  for (MonitorImageInfoIter it = m_vMonitorImageInfo.begin(); it != m_vMonitorImageInfo.end(); it++) {
    if (it->hwndBkGrndWindow != NULL)
      DestroyWindow(it->hwndBkGrndWindow);
    if (it->hbmDimmendMonitorImage)
      ::DeleteObject(it->hbmDimmendMonitorImage);
    if (it->hdcMonitor)
      ::DeleteDC(it->hdcMonitor);
  }

  // Update Progress
  xFlags &= ~(BACKGROUNDWINDOWSCREATED | MONITORIMAGESCREATED);

  // Unregister it
  if (!UnregisterClass(m_sBkGrndClassName.c_str(), m_hInstance)) {
    dwError = pws_os::IssueError(_T("UnregisterClass - Background"), false);
    ASSERT(0);
    goto BadExit;
  }

  // Update Progress
  xFlags &= ~REGISTEREDWINDOWCLASS;

#ifndef NO_NEW_DESKTOP
  if (xFlags & SWITCHEDDESKTOP) {
    // Switch back to the initial desktop - due to the possibility that
    // the user may have locked the screen (manually or automatically),
    // or the computer may have gone to sleep/hibernated, we can't switch
    // back to the original Desktop if the MS WinLogon Desktop has control.
    // We ask to be informed when there is a desktop switch and so we can try again.
    // However, for example if the user switches to another account, it might
    // not be for us and so we wait for the next notification etc.
    // Eventually, it should allow us to switch back.

    HANDLE hDesktopSwitch = OpenEvent(SYNCHRONIZE, false, _T("WinSta0_DesktopSwitch"));
    if (!hDesktopSwitch)
      dwError = pws_os::IssueError(_T("OpenEvent - SYNCHRONIZE - WinSta0_DesktopSwitch"), false);

    pws_os::Trace(L"ThreadProc SwitchDesktop\n");
    while (!SwitchDesktop(m_hOriginalDesk)) {
      pws_os::Trace(L"SwitchDesktop(m_hOriginalDesk)\n");
      dwError = pws_os::IssueError(_T("SwitchDesktop - back to original"), false);
      if (dwError != ERROR_SUCCESS)
        ASSERT(0);

      if (hDesktopSwitch) {
        WaitForSingleObject(hDesktopSwitch, INFINITE);
        pws_os::Trace(_T("Wait for WinSta0_DesktopSwitch signalled\n"));
      }

      // Try again soon
      ::Sleep(500);
    }

    // Close Handle
    if (hDesktopSwitch)
      CloseHandle(hDesktopSwitch);

    // Update Progress
    xFlags &= ~SWITCHEDDESKTOP;
  }

  if (xFlags & SETTHREADDESKTOP) {
    // Switch thread back to original Desktop
    if (!SetThreadDesktop(m_hOriginalDesk)) {
      dwError = pws_os::IssueError(_T("SetThreadDesktop - back to original"), false);
      ASSERT(0);
      goto BadExit;
    }
    // Update Progress
    xFlags &= ~SETTHREADDESKTOP;
  }

  // Now that thread is ending - close new Desktop
  if (xFlags & NEWDESKTOCREATED) {
    // Terminate processes still on new Desktop BEFORE we close it
    TerminateProcesses();

    // Note: There can be a good return code even if it does not close
    // due to programs external to PWS keeping it around!
    if (!CloseDesktop(m_hNewDesktop)) {
      dwError = pws_os::IssueError(_T("CloseDesktop (new)"), false);
      ASSERT(0);
    } else {
      // Update Progress
      xFlags &= ~NEWDESKTOCREATED;
    }
  }
#endif

  // Clear variables - just in case someone decides to reuse this instance
  m_pVKeyBoardDlg = NULL;
  m_hwndMasterPhraseDlg = NULL;
  m_sBkGrndClassName.clear();
  m_sDesktopName.clear();
  m_vMonitorImageInfo.clear();

  return m_dwRC;

BadExit:
  // Need to tidy up what was done in reverse order - ignoring what wasn't and ignore errors
  // First clear anything the user has entered
  m_pGMP->clear();

  if (xFlags & VIRTUALKEYBOARDCREATED) {
    // Delete Virtual Keyboard instance
    delete m_pVKeyBoardDlg;

    // Update Progress
    xFlags &= ~VIRTUALKEYBOARDCREATED;
  }

  if (xFlags & MASTERPHRASEDIALOGCREATED) {
    // Destroy master phrase dialog window
    DestroyWindow(m_hwndMasterPhraseDlg);

    // Update Progress
    xFlags &= ~MASTERPHRASEDIALOGCREATED;
  }

  if (xFlags & BACKGROUNDWINDOWSCREATED || xFlags & MONITORIMAGESCREATED) {
    // Destroy background layered window's, images & monitor DCs
    for (MonitorImageInfoIter it = m_vMonitorImageInfo.begin(); it != m_vMonitorImageInfo.end(); it++) {
      if (it->hwndBkGrndWindow)
        DestroyWindow(it->hwndBkGrndWindow);
      if (it->hbmDimmendMonitorImage)
        ::DeleteObject(it->hbmDimmendMonitorImage);
      if (it->hdcMonitor)
        ::DeleteDC(it->hdcMonitor);
    }

    // Update Progress
    xFlags &= ~(BACKGROUNDWINDOWSCREATED | MONITORIMAGESCREATED);
  }

  if (xFlags & REGISTEREDWINDOWCLASS) {
    // Unregister background windows' registered class
    UnregisterClass(m_sBkGrndClassName.c_str(), m_hInstance);

    // Update Progress
    xFlags &= ~REGISTEREDWINDOWCLASS;
  }

  if (xFlags & SWITCHEDDESKTOP) {
    // Switch back to the initial desktop - see comments above
    HANDLE hDesktopSwitch = OpenEvent(SYNCHRONIZE, false, _T("WinSta0_DesktopSwitch"));
    if (!hDesktopSwitch)
      dwError = pws_os::IssueError(_T("OpenEvent - SYNCHRONIZE - WinSta0_DesktopSwitch (bad exit)"), false);

    while (!SwitchDesktop(m_hOriginalDesk)) {
      dwError = pws_os::IssueError(_T("SwitchDesktop - back to original (bad exit)"), false);
      if (dwError != ERROR_SUCCESS)
        ASSERT(0);

      if (hDesktopSwitch) {
        WaitForSingleObject(hDesktopSwitch, INFINITE);
        pws_os::Trace(_T("Wait for WinSta0_DesktopSwitch signalled (bad exit)\n"));
      }

      // Try again soon
      ::Sleep(500);
    }

    // Close Handle
    if (hDesktopSwitch)
      CloseHandle(hDesktopSwitch);

    // Update Progress
    xFlags &= ~SWITCHEDDESKTOP;
  }

  if (xFlags & SETTHREADDESKTOP) {
    // Switch thread back to initial desktop
    if (!SetThreadDesktop(m_hOriginalDesk)) {
      dwError = pws_os::IssueError(_T("SetThreadDesktop - back to original (bad exit)"), false);
      ASSERT(0);
{'fraction_non_alphanumeric': 0.1076429682786651, 'fraction_numerical': 0.0046041977582319885, 'mean_word_length': 3.6740872662511133, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 75, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5420747', 'n_tokens_mistral': 11083, 'n_tokens_neox': 10449, 'n_words': 2738}