373 lines
11 KiB
C++
373 lines
11 KiB
C++
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved
|
|
|
|
//**********************************************************************
|
|
//
|
|
// IMPORTANT NOTE!
|
|
// For this program to have the privileges to dismiss the desktop, it
|
|
// must be signed and run from Program Files or Windows directory.
|
|
//
|
|
//**********************************************************************
|
|
|
|
// Includes
|
|
#include <windows.h>
|
|
#include <shobjidl.h>
|
|
#include <objbase.h>
|
|
#include <sal.h>
|
|
|
|
#include <stdarg.h>
|
|
#include <wchar.h>
|
|
|
|
// Defines
|
|
#define MAX_RETRIES 5
|
|
#define VK_D 0x44
|
|
|
|
// Static variables
|
|
HANDLE g_hFile = nullptr;
|
|
BOOL g_fOutputToFile = FALSE;
|
|
|
|
// Forward declarations and typedefs
|
|
typedef struct _ENUMDISPLAYDATA {
|
|
BOOL fAppVisible;
|
|
IAppVisibility *pAppVisible;
|
|
} ENUMDISPLAYDATA;
|
|
|
|
void ShowDesktop();
|
|
BOOL CALLBACK MonitorEnumProc (_In_ HMONITOR hMonitor, _In_ HDC, _In_ LPRECT, _In_ LPARAM dwData);
|
|
BOOL IsAppVisible();
|
|
BOOL WaitForDesktop();
|
|
BOOL HasUIAccess();
|
|
BOOL OutputString(_In_z_ _Null_terminated_ WCHAR *pszFormatString, ...);
|
|
|
|
//**********************************************************************
|
|
//
|
|
// Sends Win + D to toggle to the desktop
|
|
//
|
|
//**********************************************************************
|
|
void ShowDesktop()
|
|
{
|
|
OutputString(L"Sending 'Win-D'\r\n");
|
|
INPUT inputs[4];
|
|
ZeroMemory(inputs, sizeof(inputs));
|
|
|
|
inputs[0].type = INPUT_KEYBOARD;
|
|
inputs[0].ki.wVk = VK_LWIN;
|
|
|
|
inputs[1].type = INPUT_KEYBOARD;
|
|
inputs[1].ki.wVk = VK_D;
|
|
|
|
inputs[2].type = INPUT_KEYBOARD;
|
|
inputs[2].ki.wVk = VK_D;
|
|
inputs[2].ki.dwFlags = KEYEVENTF_KEYUP;
|
|
|
|
inputs[3].type = INPUT_KEYBOARD;
|
|
inputs[3].ki.wVk = VK_LWIN;
|
|
inputs[3].ki.dwFlags = KEYEVENTF_KEYUP;
|
|
|
|
UINT uSent = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
|
|
if (uSent != ARRAYSIZE(inputs))
|
|
{
|
|
OutputString(L"SendInput failed: 0x%x\n", HRESULT_FROM_WIN32(GetLastError()));
|
|
}
|
|
}
|
|
|
|
//**********************************************************************
|
|
//
|
|
// Callback for EnumWindows in IsAppVisible
|
|
//
|
|
//**********************************************************************
|
|
|
|
BOOL CALLBACK MonitorEnumProc (_In_ HMONITOR hMonitor, _In_ HDC, _In_ LPRECT, _In_ LPARAM dwData)
|
|
{
|
|
ENUMDISPLAYDATA *pData = reinterpret_cast<ENUMDISPLAYDATA *>(dwData);
|
|
MONITOR_APP_VISIBILITY monitorAppVisibility = MAV_UNKNOWN;
|
|
|
|
HRESULT hr = pData->pAppVisible->GetAppVisibilityOnMonitor(hMonitor, &monitorAppVisibility);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
OutputString(L"\tMonitor app visibility:\t\t");
|
|
switch (monitorAppVisibility)
|
|
{
|
|
case MAV_UNKNOWN:
|
|
OutputString(L"UNKNOWN\r\n");
|
|
break;
|
|
case MAV_NO_APP_VISIBLE:
|
|
OutputString(L"NO APP VISIBLE\r\n");
|
|
break;
|
|
case MAV_APP_VISIBLE:
|
|
OutputString(L"APP VISIBLE\r\n");
|
|
pData->fAppVisible = TRUE;
|
|
break;
|
|
default:
|
|
OutputString(L"UNDEFINED\r\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//**********************************************************************
|
|
//
|
|
// Checks to see if any apps or the Start menu is visible
|
|
//
|
|
//**********************************************************************
|
|
BOOL IsAppVisible()
|
|
{
|
|
OutputString(L"Checking for apps\r\n");
|
|
BOOL fAppVisible = FALSE;
|
|
|
|
ENUMDISPLAYDATA data = {0};
|
|
|
|
HRESULT hr = CoCreateInstance(CLSID_AppVisibility, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&data.pAppVisible));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = data.pAppVisible->IsLauncherVisible(&fAppVisible);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
OutputString(L"\tIsLauncherVisible:\t%s\r\n", fAppVisible ? L"TRUE" : L"FALSE");
|
|
|
|
if (!fAppVisible)
|
|
{
|
|
if (0 == EnumDisplayMonitors(nullptr, nullptr, MonitorEnumProc, reinterpret_cast<LPARAM>(&data)))
|
|
{
|
|
OutputString(L"EnumDisplayMonitors failed.\r\n");
|
|
}
|
|
else
|
|
{
|
|
fAppVisible = data.fAppVisible;
|
|
}
|
|
}
|
|
}
|
|
|
|
data.pAppVisible->Release();
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
OutputString(L"IsAppVisible failed: 0x%x\r\n", hr);
|
|
}
|
|
|
|
OutputString(L"\tApps:\t\t%s\r\n", fAppVisible ? L"FOUND" : L"NOT FOUND");
|
|
return fAppVisible;
|
|
}
|
|
|
|
//*********************************************************************
|
|
//
|
|
// Waits for apps or Start menu to be dismissed
|
|
// Returns true if apps and Start menu are no longer visible
|
|
//
|
|
//*********************************************************************
|
|
BOOL WaitForDesktop()
|
|
{
|
|
BOOL fAppVisible = IsAppVisible();
|
|
|
|
for (int i = 0; i < MAX_RETRIES && fAppVisible; i++)
|
|
{
|
|
Sleep(1000);
|
|
fAppVisible = IsAppVisible();
|
|
}
|
|
|
|
return !fAppVisible;
|
|
}
|
|
|
|
//***********************************************************************
|
|
//
|
|
// Checks UIAccess
|
|
//
|
|
//***********************************************************************
|
|
BOOL HasUIAccess()
|
|
{
|
|
BOOL fUIAccess = FALSE;
|
|
HANDLE hToken = nullptr;
|
|
|
|
// Get process token in order to check for UIAccess
|
|
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
|
|
{
|
|
DWORD dwAccessInfo = 0;
|
|
DWORD dwInfoSize = 0;
|
|
|
|
// Verify that the process has UIAccess
|
|
if (GetTokenInformation(hToken, TokenUIAccess, reinterpret_cast<LPVOID>(&dwAccessInfo), sizeof(dwAccessInfo), &dwInfoSize))
|
|
{
|
|
fUIAccess = (dwAccessInfo > 0);
|
|
}
|
|
else
|
|
{
|
|
OutputString(L"GetTokenInformation error:%d\n", GetLastError());
|
|
}
|
|
|
|
CloseHandle(hToken);
|
|
hToken = nullptr;
|
|
}
|
|
else
|
|
{
|
|
OutputString(L"Failed to open the token.\n");
|
|
}
|
|
|
|
return fUIAccess;
|
|
}
|
|
|
|
//***********************************************************************
|
|
//
|
|
// Outputs a string, either to a log file or to the console
|
|
//
|
|
//***********************************************************************
|
|
BOOL OutputString(_In_z_ _Null_terminated_ WCHAR *pszFormatString, ...)
|
|
{
|
|
BOOL fRetValue = TRUE;
|
|
|
|
// Make the string for the variadic function
|
|
va_list args;
|
|
WCHAR szOutputBuffer[MAX_PATH] = {0};
|
|
va_start(args, pszFormatString);
|
|
|
|
int nCharWritten = _vsnwprintf_s(szOutputBuffer, ARRAYSIZE(szOutputBuffer), pszFormatString, args);
|
|
if (nCharWritten > 0)
|
|
{
|
|
// Output
|
|
if (g_fOutputToFile)
|
|
{
|
|
CHAR szMultibyteOut[MAX_PATH];
|
|
int nBytesWritten = WideCharToMultiByte(CP_UTF8, 0, szOutputBuffer, nCharWritten, szMultibyteOut, sizeof(szMultibyteOut), nullptr, nullptr);
|
|
if (nBytesWritten == 0)
|
|
{
|
|
fRetValue = FALSE;
|
|
}
|
|
else
|
|
{
|
|
fRetValue = WriteFile(g_hFile, reinterpret_cast<LPCVOID>(szMultibyteOut), nBytesWritten, nullptr, nullptr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wprintf(szOutputBuffer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fRetValue = FALSE;
|
|
}
|
|
|
|
return fRetValue;
|
|
}
|
|
|
|
//***********************************************************************
|
|
//
|
|
// ShowDesktop entry point
|
|
//
|
|
//***********************************************************************
|
|
int __cdecl wmain(_In_ int argc, _In_reads_(argc) WCHAR *argv[], _In_reads_(argc) WCHAR *[])
|
|
{
|
|
int nResult = ERROR_SUCCESS;
|
|
|
|
// Check input args for flags
|
|
BOOL fOverwrite = FALSE;
|
|
BOOL fIgnoreUIAccess = FALSE;
|
|
WCHAR *pszfileName = nullptr;
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
// Flag to give help
|
|
if (_wcsicmp(argv[i], L"-?") == 0)
|
|
{
|
|
wprintf(L"Command line options:\n"
|
|
L"-o <file path>: Writes output to a the specified log file instead of the console\n"
|
|
L"-f: Forces the file specified in -o flag to be overwritten if the file exists\n"
|
|
L"-i: Ignores the return value of the check for UIAccess\n");
|
|
}
|
|
|
|
// Flag for force overwrite
|
|
if (_wcsicmp(argv[i], L"-f") == 0)
|
|
{
|
|
fOverwrite = TRUE;
|
|
}
|
|
|
|
// Flag for output file path
|
|
if (_wcsicmp(argv[i - 1], L"-o") == 0)
|
|
{
|
|
pszfileName = argv[i];
|
|
g_fOutputToFile = TRUE;
|
|
}
|
|
|
|
// Flag to ignore the check for UIAccess
|
|
if (_wcsicmp(argv[i], L"-i") == 0)
|
|
{
|
|
fIgnoreUIAccess = TRUE;
|
|
}
|
|
}
|
|
|
|
// Open the specified file for write
|
|
if (g_fOutputToFile && pszfileName != nullptr)
|
|
{
|
|
DWORD dwCreationDisposition = fOverwrite ? CREATE_ALWAYS: CREATE_NEW;
|
|
g_hFile = CreateFile(pszfileName, GENERIC_WRITE, FILE_SHARE_READ, nullptr, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
|
|
if (g_hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
nResult = GetLastError();
|
|
wprintf(L"Cannot write to the specified file, error: 0x%x\r\n", nResult);
|
|
}
|
|
}
|
|
|
|
if (nResult == ERROR_SUCCESS)
|
|
{
|
|
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (fIgnoreUIAccess || HasUIAccess())
|
|
{
|
|
if (fIgnoreUIAccess)
|
|
{
|
|
OutputString(L"Ignoring the check for UIAccess.\r\n");
|
|
}
|
|
else
|
|
{
|
|
OutputString(L"UI automation access is allowed.\r\n");
|
|
}
|
|
|
|
if (IsAppVisible())
|
|
{
|
|
ShowDesktop();
|
|
|
|
// Wait for the dismiss animation to complete and for the desktop to be visible.
|
|
Sleep(1000);
|
|
if (WaitForDesktop())
|
|
{
|
|
nResult = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
nResult = ERROR_CAN_NOT_COMPLETE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// System is already in Desktop--no need to send Win + D
|
|
nResult = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutputString(L"UI automation access is NOT allowed.\r\n");
|
|
nResult = ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
CoUninitialize();
|
|
}
|
|
else
|
|
{
|
|
OutputString(L"CoInitializeEx failed: 0x%x\r\n", hr);
|
|
}
|
|
|
|
if (g_hFile != nullptr)
|
|
{
|
|
CloseHandle(g_hFile);
|
|
}
|
|
}
|
|
|
|
return nResult;
|
|
} |