The easiest way is to have 2 processes. One of them is a regular user, and he starts the advanced / administration process. The administration process can then use IPC to ask the normal user process to do something.
Moving from an unselected process to an elevated process is easy. You can start the process with elevation to pass the verb runas to ShellExecute or ShellExecuteEx.
Going the other way is harder. Firstly, it is very difficult to execute a marker in order to properly remove nature. And, among other things, even if you can do this, it is not, because the user cannot have a different user.
The solution here is to return to Explorer and ask Explorer to run the program for you. Since Explorer works as the original non-powered user, the program (in this case, a web browser) will work like Bob. This is also important if the handler for the file you want to open is launched as an internal extension of the process, and not as a separate process, because in this case the attempt to unelevate will be meaningless, because the first place in the process of creating a new process. (And if the file handler tries to contact an existing unrestored copy of itself, everything may fail due to UIPI.)
Well, I know that Small programs should not have motivation, but I could not help myself. Enough jabber. Let write the code. (Remember that in "Small Programs" there is little or no error checking because they roll.)
#define STRICT #include <windows.h> #include <shldisp.h> #include <shlobj.h> #include <exdisp.h> #include <atlbase.h> #include <stdlib.h> void FindDesktopFolderView(REFIID riid, void **ppv) { CComPtr<IShellWindows> spShellWindows; spShellWindows.CoCreateInstance(CLSID_ShellWindows); CComVariant vtLoc(CSIDL_DESKTOP); CComVariant vtEmpty; long lhwnd; CComPtr<IDispatch> spdisp; spShellWindows->FindWindowSW( &vtLoc, &vtEmpty, SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp); CComPtr<IShellBrowser> spBrowser; CComQIPtr<IServiceProvider>(spdisp)-> QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&spBrowser)); CComPtr<IShellView> spView; spBrowser->QueryActiveShellView(&spView); spView->QueryInterface(riid, ppv); } void GetDesktopAutomationObject(REFIID riid, void **ppv) { CComPtr<IShellView> spsv; FindDesktopFolderView(IID_PPV_ARGS(&spsv)); CComPtr<IDispatch> spdispView; spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView)); spdispView->QueryInterface(riid, ppv); }
The GetDesktopAutomationObject function finds the view of the desktop folder, and then queries the send object for the view. Then we return this dispatch object in the form requested by the caller. This send object is ShellFolderView, and for the C ++ interface it is IShellFolderViewDual, so most callres will request this interface, but if you are a masochist, you can skip the dual interface and talk directly to IDispatch.
void ShellExecuteFromExplorer( PCWSTR pszFile, PCWSTR pszParameters = nullptr, PCWSTR pszDirectory = nullptr, PCWSTR pszOperation = nullptr, int nShowCmd = SW_SHOWNORMAL) { CComPtr<IShellFolderViewDual> spFolderView; GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView)); CComPtr<IDispatch> spdispShell; spFolderView->get_Application(&spdispShell); CComQIPtr<IShellDispatch2>(spdispShell) ->ShellExecute(CComBSTR(pszFile), CComVariant(pszParameters ? pszParameters : L""), CComVariant(pszDirectory ? pszDirectory : L""), CComVariant(pszOperation ? pszOperation : L""), CComVariant(nShowCmd)); }
The ShellExecuteFromExplorer function begins by retrieving the desktop folder automation object. We use the desktop not because it is especially significant, but because we know that it will always be there.
As in the desktop folder view, the ShellFolderView object is not interesting to us. This is interesting for us, because the object is in the process that hosts the desktop (which is the main Explorer process). From the ShellFolderView, we request the Application property so that we can move on to the main Shell.Application object, which has the IShellDispatch interface (and its extensions IShellDispatch2 via IShellDispatch6) as its C ++ interfaces. And it is this method of IShellDispatch2 :: ShellExecute that we really need.
And we call IShellDispatch2 :: ShellExecute with the appropriate parameters. Note that the parameters of IShellDispatch2 :: ShellExecute are in a different order from the parameters to ShellExecute!
Well, let it be inside a small program.
int __cdecl wmain(int argc, wchar_t **argv) { if (argc < 2) return 0; CCoInitialize init; ShellExecuteFromExplorer( argv[1], argc >= 3 ? argv[2] : L"", argc >= 4 ? argv[3] : L"", argc >= 5 ? argv[4] : L"", argc >= 6 ? _wtoi(argv[5]) : SW_SHOWNORMAL); return 0; }
A program takes a mandatory command line argument that must be executed, whether it be a program or a document or a URL. Optional parameters are the parameters of the thing being done, the current directory to use, the operation being performed, and the way the window opens.
Open an elevated command prompt, and then run this program in various ways.
- scratch http://www.msn.com/
Open the unleaded web page in a custom web browser. - scratch cmd.exe "C: \ Users" "3
Open a command prompt without change in C: \ Users, maximized. - scratch C: \ Path \ To \ Image.bmp "" "" change
Change the bitmap in the unleaded image editor.