从未提升的流程到提升的流程很容易。您可以通过以下方式运行具有高程的进程传递 runas 动词 http://blogs.msdn.com/b/vistacompatteam/archive/2006/09/25/771232.aspx到 ShellExecute 或 ShellExecuteEx。
走另一条路就更棘手了。一方面,很难通过修改你的令牌来正确地消除海拔特性。另一方面,即使您可以做到这一点,但这也不是正确的做法,因为未提升的用户可能与提升的用户不同。
这里的解决方案是返回资源管理器并要求资源管理器为您启动该程序。由于 Explorer 以原始未提升用户身份运行,因此程序(在本例中为 Web 浏览器)将以 Bob 身份运行。如果您要打开的文件的处理程序作为进程内扩展而不是作为单独的进程运行,这一点也很重要,因为在这种情况下,尝试取消提升将是毫无意义的,因为在第一名。 (如果文件的处理程序尝试与自身的现有未提升副本进行通信,则可能会因为 UIPI 的原因而失败。)
好吧,我知道小程序不应该有动力,但我就是控制不住自己。废话够多了。我们来写代码吧。 (请记住,小程序很少或根本不进行错误检查,因为这就是它们的运行方式。)
#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);
}
GetDesktopAutomationObject 函数定位桌面文件夹视图,然后请求该视图的调度对象。然后,我们以调用者请求的形式返回该调度对象。这个调度对象是一个 ShellFolderView,它的 C++ 接口是 IShellFolderViewDual,所以大多数调用者都会要求该接口,但如果你是一个受虐狂,你可以跳过双重接口并直接与 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));
}
ShellExecuteFromExplorer 函数首先获取桌面文件夹自动化对象。我们使用桌面并不是因为它特别有意义,而是因为我们知道它永远存在。
与桌面文件夹视图一样,我们对 ShellFolderView 对象本身并不感兴趣。这对我们来说很有趣,因为该对象驻留在托管桌面视图的进程(这是主 Explorer 进程)中。从 ShellFolderView 中,我们请求 Application 属性,以便可以访问主 Shell.Application 对象,该对象具有 IShellDispatch 接口(及其扩展 IShellDispatch2 到 IShellDispatch6)作为其 C++ 接口。我们真正想要的是 IShellDispatch2::ShellExecute 方法。
我们使用适当的参数调用 IShellDispatch2::ShellExecute。请注意,IShellDispatch2::ShellExecute 的参数与 ShellExecute 的参数顺序不同!
好吧,让我们把它放在一个小程序中。
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;
}
该程序采用强制命令行参数,该参数是要执行的内容,无论是程序、文档还是 URL。可选参数是正在执行的事物的参数、要使用的当前目录、要执行的操作以及如何打开窗口。
打开提升的命令提示符,然后以各种方式运行该程序。
- scratch http://www.msn.com/ http://www.msn.com/
在用户的默认 Web 浏览器中打开未提升的网页。
- 暂存 cmd.exe "" C:\Users "" 3
在 C:\Users 处打开未提升的命令提示符,并将其最大化。
- 从头开始 C:\Path\To\Image.bmp "" "" 编辑
在未提升的图像编辑器中编辑位图。