Sunday, June 19, 2011

GetShellWindow vs. GetDesktopWindow

In his post about running a process as a standard user from an elevated process, Aaron Margosis uses a technique that gets the access token of the shell process (typically explorer.exe) and uses that token to launch the new process (Sasha Goldshtein also blogged about that).
The first thing his code does is try to locate the shell process id. One way is to look for “explorer.exe” in the list of processes, but that’s a bit limiting, as there may be a different shell, or it may have been renamed for whatever reason. His code calls GetShellWindow to get the shell window handle, followed by GetWindowThreadProcessId that returns the window’s creator thread (and optionally its parent process). A similar looking function is GetDesktopWindow, which seems at first glance as a better candidate, or at least an equivalent. Let’s test that:

HWND hShell = ::GetShellWindow(); 
HWND hDesktop = ::GetDesktopWindow(); 
cout << "Shell window: " << hShell << endl; 
cout << "Desktop window: " << hDesktop << endl; 
DWORD idShell, idDesktop; 
::GetWindowThreadProcessId(hShell, &idShell); 
::GetWindowThreadProcessId(hDesktop, &idDesktop); 
TCHAR filename[MAX_PATH]; 
if(GetImageFileName(idShell, filename, MAX_PATH)) 
    wcout << "Shell process: " << filename << endl; 
if(GetImageFileName(idDesktop, filename, MAX_PATH)) 
    wcout << "Desktop process: " << filename << endl;
GetImageFileName is a simple helper for getting the process image name:
BOOL GetImageFileName(DWORD pid, TCHAR* name, DWORD size) { 
    HANDLE hProces = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); 
    if(!hProces) return FALSE; 
    DWORD sz = size;

Read more: Pavel's Blog