有没有API调用来提示用户进行UAC提升?

7
我的应用程序需要写入一个可能受保护的 \ProgramData 文件。这只会在安装后发生一次。
是否有 API 函数可以接受 ACL 信息并提示用户授权应用程序访问文件?换句话说,应用程序将要求 Windows 提示用户确认和授权。这应该是交互式的,并允许应用程序在授权访问文件后恢复执行。应用程序以标准用户身份运行,不需要管理员权限。
文件将使用 CreateFile() 打开。
编辑:我的问题与其他被认为是重复的问题之间存在微妙差别。我正在请求访问一个特定的对象,即一个文件。其他人则要求提升整个过程的权限。无论如何,我感谢所有包含解决方案的回答。

但是...有没有一些GetNamedSecurityInfo,AllocateAndInitializeSid,SetEntriesInAcl,SetNamedSecurityInfo等的组合可以让我仅访问那个文件?我不想整个进程都以管理员身份运行。CreateFile()引用了一个SECURITY_ATTRIBUTES东西。看起来我可以获取一个SECURITY_ATTRIBUTES对象,将其传递给CreateFile,然后就完成了! - Pierre
根据CreateFile()文档: "*一个指向SECURITY_ATTRIBUTES结构的指针,该结构包含两个独立但相关的数据成员:一个可选的安全描述符和一个布尔值,用于确定返回的句柄是否可以被子进程继承。... 当打开现有文件或设备时,CreateFile忽略lpSecurityDescriptor成员,但继续使用bInheritHandle成员。*" - Remy Lebeau
完全没有区别。这是一个重复的问题很多次了。如果您不同意,那么您仍然不理解UAC。重新开放投票者显然也不理解这一点。 - David Heffernan
我只是重新打开它,以便我可以发布我的解决方案,然后我立即关闭了它。请向我展示另一个具有相同解决方案的Stackoverflow帖子,包括ShellExecuteEx,lpFile =“cmd”和双命令。 - Pierre
20020450 不算,它没有提到UAC,并且只有在你已经找到解决方案之后才能找到它。 - Pierre
3个回答

21

如果您不想提升整个应用程序的权限,有几个选择:

  1. 创建一个单独的提升进程来访问文件。使用带有 runas 副词的 ShellExecute/Ex() 或者 CreateProcessElevated() 方法运行第二个你的应用程序副本,或者另一个辅助程序,并通过命令行参数告诉它该怎么做。主进程可以在需要时等待第二个进程退出。

  2. 创建一个 COM 对象来访问文件,然后使用 COM 提升代理 以提升状态运行 COM 对象。

  3. 使用 CredUIPromptForCredentials() 或者 CredUIPromptForWindowsCredentials() 来提示用户输入凭据(有关详细信息,请参见请求用户凭证),然后使用 LogonUser() 登录指定账户以获取令牌,使用 ImpersonateLoggedOnUser() 模拟该令牌,如需访问文件,然后使用RevertToSelf() 停止模拟,并使用 CloseHandle() 关闭令牌。


#3非常巧妙,但如果我理解正确,它需要应用程序知道管理员密码?恐怕大多数用户不会同意这样做。我将尝试#2。(我的COM很基础,需要一些时间)。谢谢。 - Pierre
#1 更容易实现,因为所有内容都可以保留在单个exe文件中。对于#3,用户需要知道管理员密码,因为对话框将要求输入用户名和密码,然后您的应用程序将使用这些信息登录到该帐户。这与UAC提升提示标准用户以获取管理员凭据没有太大区别。如果管理员正在运行您的应用程序,则他们可能已经可以访问该文件。因此,您应该尝试访问该文件一次,然后仅在出现错误代码(例如“ERROR_ACCESS_DENIED”或“ERROR_ELEVATION_REQUIRED”)时再次请求凭据并重试。 - Remy Lebeau
我想我会尝试#1,因为它是最简单的。非常感谢。 - Pierre
我认为#3不会起作用,因为您没有模拟特权。(另外,LogonUser默认会给您一个受限令牌,但您可以通过使用“批处理登录”选项来更正。) - Harry Johnston

7
感谢@Remy提供的ShellExecuteEx建议,以下是详细情况。请注意使用“cmd”和双命令,以便用户只需要回复一次。此外,[1]必须等待进程完成,否则您可能会在文件被删除之前创建该文件,而[2]如果进程失败,则不要等待进程。
// delete file with Admin privilege
// 'file_name' is path of file to be deleted
SHELLEXECUTEINFO shex;
char param[512];
char *cmd = "/C \"attrib -H \"%s\" && del /F /Q \"%s\"\""; // double command

_snprintf(param, sizeof(param), cmd, file_name, file_name);
ZeroMemory(&shex, sizeof(shex));
shex.cbSize = sizeof(shex);
shex.lpVerb = "runas";  // runas, open
shex.lpFile = "cmd";    // not 'del'
shex.lpParameters = param;
shex.nShow = SW_HIDE;
shex.fMask = SEE_MASK_NOCLOSEPROCESS;
BOOL retshx = ShellExecuteEx(&shex);
// wait otherwise could return before completed
if(retshx)
{   time_t st = clock();
    DWORD exitCode;
    do
    {   if(!GetExitCodeProcess(shex.hProcess, &exitCode))
            break;
        if(clock() - st > CLOCKS_PER_SEC * 5)       // max 5 seconds
            break;
    } while(exitCode != STATUS_WAIT_0); // STILL_ACTIVE
    CloseHandle(shex.hProcess);
}

2
为什么要用“-1”?这段代码花费了好几个小时才调试正确。我希望当我在寻找答案时,有人能够发布像这样的内容。我希望它能帮助遇到同样问题的人。使用“-1”如何帮助社区呢? - Pierre

5
进程只能使用提升的令牌启动,无法在启动后获得。因此,您可以使用命令行参数重新启动应用程序并提升权限(简单解决方案),或者实现一个独立的COM服务器,您可以创建提升的COM对象并向其传递指令(更难)。
第三种解决方案是利用内置的UAC支持IFileOperation接口,但这只允许复制而不是读写。因此,您可以复制需要修改的文件,修改副本,然后使用IFileOperation将临时文件复制到原始文件上。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接