如何从Windows 10上的高架管理员上下文中启动非高级管理员流程
How to launch non-elevated administrator process from elevated administrator context on Windows 10?
是否有一种简单的方法来从高架管理员流程中创建普通的管理员流程(未升高)? 我正在使用Windows 10 Pro。情况是我正在尝试制造某种部署工具。该工具将使用高架管理员上下文运行,以便将文件写入"程序文件"(并访问其他特权资源)。但是,步骤之一是调用外部程序。该程序在具有提升的管理员特权创建时似乎存在奇怪的问题。我们必须在非高级管理员上下文中启动它。 我尝试了MSDN博客,https://blogs.msdn.microsoft.com/winsdk/2010/05/31/dealing-with-with-with-administrator-and-and-sandard--and-sandard-users-context,它根本不起作用。<<<<<<<</p>
雷蒙德·陈(Raymond Chen)在MSDN上的"旧事物"博客上解决了这个确切的问题:
我如何从高架过程中启动一个未提升的过程,反之亦然?
换句话说更棘手。一方面,要正确删除高程性质确实很难。而另一件事,即使您可以做到,这也不是正确的事情,因为未举报的用户可能与高架用户不同。
...
这里的解决方案是返回Explorer,并要求Explorer为您启动程序。,因为Explorer作为原始未提升的用户(在这种情况下为Web浏览器)运行(在Web浏览器中)将以鲍勃的身份运行。对于您要打开运行的文件的处理程序作为进程扩展而不是单独的过程,这也很重要,因为在这种情况下,由于没有创建新的过程第一位。(如果文件的处理程序试图与现有的未提升本身的副本进行通信,则可能会因为UIPI而失败。)
Raymond使用IShellFolderViewDual
和IShellDispatch2
来完成 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:用户中打开一个未提升的命令提示,最大化。中编辑位图
scratch C:PathToImage.bmp "" "" edit
在未提升的图像编辑器
1 :FindDesktopFolderView()
的实现是在雷蒙德博客上的另一篇文章中:
操纵桌面图标的位置:
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转换为字符串和raii包装器以进行com初始化。
#include <windows.h>
#include <shldisp.h>
#include <shlobj.h>
#include <exdisp.h>
#include <atlbase.h>
#include <stdlib.h>
#include <iostream>
#include <system_error>
template< typename T >
void ThrowIfFailed( HRESULT hr, T&& msg )
{
if( FAILED( hr ) )
throw std::system_error{ hr, std::system_category(), std::forward<T>( 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 {};
}
struct ComInit
{
ComInit() { ThrowIfFailed( ::CoInitialize( nullptr ), "Could not initialize COM" ); }
~ComInit() { ::CoUninitialize(); }
ComInit( ComInit const& ) = delete;
ComInit& operator=( ComInit const& ) = delete;
};
接下来是完成实际工作的功能。这基本上是与雷米·勒博(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()
{
try
{
ComInit init;
ShellExecuteFromExplorer( L"http://www.stackoverflow.com" );
}
catch( std::system_error& e )
{
std::cout << "ERROR: " << e.what() << "n"
<< "Error code: " << e.code() << std::endl;
}
}
其他注释:
使用此方法时,您可能会注意到启动应用程序的窗口并不总是进入前景,尤其是在已经运行的情况下。我的解决方法是在致电ShellExecuteFromExplorer()
之前致电AllowSetForegroundWindow( ASFW_ANY )
,以启用该过程以将自己带到前景(我们无法指定过程ID,因为我们不知道会事先启动的过程)。
这是雷蒙德·陈(Raymond Chen
#include <atlbase.h>
#include <Shlobj.h>
HRESULT FindDesktopFolderView(REFIID riid, void **ppv)
{
HRESULT hr;
CComPtr<IShellWindows> spShellWindows;
hr = spShellWindows.CoCreateInstance(CLSID_ShellWindows);
if(FAILED(hr))
return hr;
CComVariant vtLoc { 0 }; // 0 = CSIDL_DESKTOP
CComVariant vtEmpty;
long lhwnd = 0;
CComPtr<IDispatch> spdisp;
hr = spShellWindows->FindWindowSW(&vtLoc, &vtEmpty, SWC_DESKTOP, &lhwnd, SWFO_NEEDDISPATCH, &spdisp);
if(FAILED(hr))
return hr;
CComQIPtr<IServiceProvider> spProv{ spdisp };
if(!spProv)
return E_NOINTERFACE;
CComPtr<IShellBrowser> spBrowser;
hr = spProv->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&spBrowser));
if(FAILED(hr))
return hr;
CComPtr<IShellView> spView;
hr = spBrowser->QueryActiveShellView(&spView);
if(FAILED(hr))
return hr;
return spView->QueryInterface(riid, ppv);
}
HRESULT GetDesktopAutomationObject(REFIID riid, void **ppv)
{
HRESULT hr;
CComPtr<IShellView> spsv;
hr = FindDesktopFolderView(IID_PPV_ARGS(&spsv));
if(FAILED(hr))
return hr;
if(!spsv)
return E_NOINTERFACE;
CComPtr<IDispatch> spdispView;
hr = spsv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&spdispView));
if(FAILED(hr))
return hr;
return spdispView->QueryInterface(riid, ppv);
}
HRESULT ShellExecuteFromExplorer(PCWSTR pszFile, PCWSTR pszParameters, PCWSTR pszDirectory, PCWSTR pszOperation, int nShowCmd)
{
HRESULT hr;
CComPtr<IShellFolderViewDual> spFolderView;
hr = GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView));
if(FAILED(hr))
return hr;
CComPtr<IDispatch> spdispShell;
hr = spFolderView->get_Application(&spdispShell);
if(FAILED(hr))
return hr;
CComQIPtr<IShellDispatch2> spdispShell2{ spdispShell };
if(!spdispShell2)
return E_NOINTERFACE;
// without this, the launched app is not moved to the foreground
AllowSetForegroundWindow(ASFW_ANY);
return spdispShell2->ShellExecute(
CComBSTR{ pszFile },
CComVariant{ pszParameters ? pszParameters : L"" },
CComVariant{ pszDirectory ? pszDirectory : L"" },
CComVariant{ pszOperation ? pszOperation : L"" },
CComVariant{ nShowCmd } );
}
解决方案有点复杂。也许您无法通过获得一种访问令牌并将其传递给createProcessWithToken直接从高架管理员到非高级管理员,但是您可以朝另一个方向更进一步。您可以从高架管理员到系统帐户,该帐户具有更高的特权。从系统帐户特权中,您应该能够在非高级管理员上下文中启动流程。使用关键字"模仿"搜索可以为您提供很多示例。那么如何从高架管理员到系统?您只能编写系统服务并在高架管理员上下文下创建/启动服务。
- #为""定义宏;静态";针对不同的上下文
- 与互斥锁相比,旋转锁可以保证上下文切换
- 线程,如果else语句,都是错误的上下文切换后,会发生什么
- 为什么我不能使用 EGL 创建无头 OpenGl 上下文?
- Visual Studio(或任何其他工具)能否将地址解释为调用堆栈(boost上下文)的开头
- 为什么在逗号分隔符上下文中将预增量的结果强制转换为void
- 错误"Could not find Boost"(缺少:上下文标头)
- 使用 mod_gsoap 部署服务时,如何在 Gsoap 中更改 soap 上下文的模式?
- 在没有管理员权限的情况下连接到同一网络中的命名管道
- 将有状态的 lambda 传递到 C 样式函数中,而无需上下文参数
- 安排带有上下文的协同程序
- 在没有管理员权限的情况下,在 c++ 中以编程方式将程序添加到启动
- 我的文件无法正常工作,无法从C++文件中读取上下文
- DCMTK 了解"DIMSE 没有有效的演示上下文 ID"错误
- 具有std::initializer_list参数的非成员函数(/non构造函数上下文)的重载解析
- antlr 规则上下文是否可以独立于目标
- OpenGL 调试上下文警告 -"将使用视频内存作为缓冲区异议的来源
- MSVC 在不知道类型的情况下评估上下文(和错误)
- 为什么 Perf 具有如此高的上下文切换?
- 非推导上下文,如标准库中的"boost::mpl::identity:<T>:type"?