如何在Windows 10中从高级pipe理员上下文启动非高级pipe理员进程?

有没有一个简单的方法来创build一个普通的pipe理员进程(不升级)从一个高架的pipe理员进程? 我正在使用Windows 10专业版。 情况是我正在尝试做一些types的部署工具。 该工具将以高级pipe理员上下文运行,以便将文件写入“程序文件”(并访问其他特权资源)。 但是其中一步就是调用一个外部程序。 这个程序在使用提升的pipe理员权限创build时似乎有些奇怪的问题。 我们必须在非高级pipe理员上下文中启动它。 我尝试了MSDN博客中的方法,它根本不起作用。

解决方案有点复杂。 也许你不能通过获得一种访问令牌直接从高级管理员到非高级管理员,而是将其传递给创建过程,但是你可以向另一个方向前进一步。 您可以从高级管理员转到具有更高权限的系统帐户。 从系统帐户权限,您应该能够在非高级管理员上下文中启动进程。 使用关键字“impersonate”搜索可以给你很多的例子。 那么如何从高管到系统呢? 您只能编写一个系统服务,并在提升的管理员环境下创建/启动该服务。

Raymond Chen在MSDN的“Old New Thing”博客上解答了这个问题:

我怎样才能从我的升级过程中启动一个未升级的过程,反之亦然?

走另一条路是棘手的。 一方面,要想正确移除海拔本质,确实很难。 另一方面,即使你能做到这一点,也不是正确的做法,因为未升级的用户可能与升级的用户不同。

这里的解决方案是回到资源管理器,并要求资源管理器为您启动程序。 由于资源管理器是作为原始未升级的用户运行的,因此程序(在本例中是Web浏览器)将作为Bob运行。 这对于要打开的文件的处理程序作为进程内扩展而不是作为单独的进程运行也是非常重要的,因为在这种情况下,由于没有创建新进程第一个地方。 (如果该文件的处理程序试图与现有的本身未加载的副本进行通信,则可能由于UIPI而失败。)

Raymond使用IShellFolderViewDualIShellDispatch2来完成1

 #define STRICT #include <windows.h> #include <shldisp.h> #include <shlobj.h> #include <exdisp.h> #include <atlbase.h> #include <stdlib.h> // FindDesktopFolderView incorporated by reference 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); } 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)); } 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; } 

打开提升的命令提示符,然后以各种方式运行此程序。

scratch http://www.msn.com/
在用户的默认Web浏览器中打开一个未升级的网页。

scratch cmd.exe "" C:\Users "" 3
在C:\ Users打开一个未升级的命令提示符,最大化。

scratch C:\Path\To\Image.bmp "" "" edit
编辑一个位图在一个unlevated图像编辑器

1FindDesktopFolderView()的实现在Raymond博客的另一篇文章中:

操纵桌面图标的位置 :

 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); } 

这是我的Raymond Chen的代码版本,它通过C ++异常增加了错误处理。

首先我们声明一些帮助函数,用于从HRESULT std::system_error异常,并将GUID转换为字符串。

 #include <windows.h> #include <shldisp.h> #include <shlobj.h> #include <exdisp.h> #include <atlbase.h> #include <stdlib.h> #include <system_error> void ThrowIfFailed( HRESULT hr, const char* msg ) { if( FAILED( hr ) ) throw std::system_error{ hr, std::system_category(), msg }; } void ThrowIfFailed( HRESULT hr, const std::string& msg ) { if( FAILED( hr ) ) throw std::system_error{ hr, std::system_category(), msg }; } template< typename ResultT = std::string > ResultT to_string( REFIID riid ) { LPOLESTR pstr = nullptr; if( SUCCEEDED( ::StringFromCLSID( riid, &pstr ) ) ) { // Use iterator arguments to cast from wchar_t to char if element type of ResultT is char. ResultT result{ pstr, pstr + wcslen( pstr ) }; ::CoTaskMemFree( pstr ); pstr = nullptr; return result; } return {}; } 

其次是执行实际工作的功能。 这与Remy Lebeau的答案基本相同,但增加了错误处理(以及我的个人格式化风格)。

 void FindDesktopFolderView( REFIID riid, void **ppv ) { CComPtr<IShellWindows> spShellWindows; ThrowIfFailed( spShellWindows.CoCreateInstance( CLSID_ShellWindows ), "Could not create instance of IShellWindows" ); CComVariant vtLoc{ CSIDL_DESKTOP }; CComVariant vtEmpty; long lhwnd = 0; CComPtr<IDispatch> spdisp; ThrowIfFailed( spShellWindows->FindWindowSW( &vtLoc, &vtEmpty, SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp ), "Could not find desktop shell window" ); CComQIPtr<IServiceProvider> spProv{ spdisp }; if( ! spProv ) ThrowIfFailed( E_NOINTERFACE, "Could not query interface IServiceProvider" ); CComPtr<IShellBrowser> spBrowser; ThrowIfFailed( spProv->QueryService( SID_STopLevelBrowser, IID_PPV_ARGS(&spBrowser) ), "Could not query service IShellBrowser" ); CComPtr<IShellView> spView; ThrowIfFailed( spBrowser->QueryActiveShellView( &spView ), "Could not query active IShellView" ); ThrowIfFailed( spView->QueryInterface( riid, ppv ), "Could not query interface " + to_string( riid ) + " from IShellView" ); } void GetDesktopAutomationObject( REFIID riid, void **ppv ) { CComPtr<IShellView> spsv; FindDesktopFolderView( IID_PPV_ARGS(&spsv) ); CComPtr<IDispatch> spdispView; ThrowIfFailed( spsv->GetItemObject( SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView) ), "Could not get item object SVGIO_BACKGROUND from IShellView" ); ThrowIfFailed( spdispView->QueryInterface( riid, ppv ), "Could not query interface " + to_string( riid ) + " from ShellFolderView" ); } 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; ThrowIfFailed( spFolderView->get_Application( &spdispShell ), "Could not get application object from IShellFolderViewDual" ); CComQIPtr<IShellDispatch2> spdispShell2{ spdispShell }; if( !spdispShell2 ) ThrowIfFailed( E_NOINTERFACE, "Could not query interface IShellDispatch2" ); ThrowIfFailed( spdispShell2->ShellExecute( CComBSTR{ pszFile }, CComVariant{ pszParameters ? pszParameters : L"" }, CComVariant{ pszDirectory ? pszDirectory : L"" }, CComVariant{ pszOperation ? pszOperation : L"" }, CComVariant{ nShowCmd } ), "ShellExecute failed" ); } 

显示如何处理异常的使用示例:

 int main() { CCoInitialize init; try { ShellExecuteFromExplorer( L"http://www.stackoverflow.com" ); } catch( std::system_error& e ) { std::cout << "ERROR: " << e.what() << "\n" << "Error code: " << e.code() << std::endl; } }