有条件的Windows管理员提示

Conditional Windows administrator prompt

本文关键字:提示 管理员 Windows 有条件      更新时间:2023-10-16

我正在编写一个C++DLL来连接MySQL数据库。除其他外,它试图向Windows防火墙列表中添加一个异常,以便通过TCP(端口3306)连接到远程MySQL服务器。当我尝试添加端口时,我会得到E_ACCESSDENIEDHRESULT,除非我以管理员身份运行该程序。

我想提示用户输入管理员密码,但仅当端口不在异常列表中时。这意味着我不能只创建一个UAC清单,因为据我所知,设置level="requireAdministrator"总是会提示输入管理员密码。我可以有条件地显示管理员提示吗?

根据您的请求,我将根据我的评论做出回答。


正确的方法是让DLL的用户自己处理这种情况。作为一名API开发人员,你的工作肯定不是为防火墙之类的事情而烦恼:要么是API用户和/或最终用户确保了它的工作,要么是你优雅地失败了。正如其他人所说,最终用户无法回答UAC请求的原因有很多(例如,在无头服务器上),因此您不能依赖于在交互式上下文中使用DLL。这根本不是你的责任。


如果你真的必须继续你最初的想法(我再次强调,这是一个坏主意,IMHO),我最好的选择是将你的DLL一分为二:

  • DLL本身没有任何清单,因此它可以在任何用户下运行
  • 和一个单独的.exe,它有一个需要管理员权限的清单,只有在需要时才会由DLL运行

只要要求两者都存储在同一目录中,这样您就可以使用GetModuleFileName轻松地找到DLL(以及.exe)的目录。

其他人指出了runasRunDll(它们是同样有效的答案IMO),但我更喜欢Unix-y类型,因此我建议使用单独的二进制。从长远来看,我发现它更容易维护。


一个"介于两者之间"的解决方案是,你的DLL根本不涉及防火墙(因为它应该),但你提供了一个完全独立的工具(带清单的.exe),可以帮助你的用户在需要时正确设置防火墙。这可能是最好的解决方案:干净的设计(责任分离),并且你仍然为用户提供所有所需的工具。

一旦启动应用程序,就无法提升其权限。在您的情况下,一种解决方案是使用ShellExecuteEx启动具有提升权限的小型外部应用程序,并在其中添加防火墙异常。添加异常后,执行任何必要的清理操作并退出。然后,您可以等待应用程序完成,然后再继续执行。


Cody Gray指出了不应该使用RunDll的原因,以及Raymond Chen的额外博客条目,其中包含了有关RunDll 问题的更多信息,非常感谢他

无论何时获得E_ACCESSDENIED,都可以使用带有"runas"谓词的ShellExecuteEx重新启动程序。像这样的

        wchar_t szPath[MAX_PATH];
        if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)))
        {`enter code here`
            // Launch itself as administrator.
            SHELLEXECUTEINFO sei = { sizeof(sei) };
            sei.lpVerb = L"runas";
            sei.lpFile = szPath;
            sei.lpParameters=L"admin";
            sei.nShow = SW_NORMAL;
            if (!ShellExecuteEx(&sei))
            {
                DWORD dwError = GetLastError();
                if (dwError == ERROR_CANCELLED)
                {
                    ExitProcess(0);
                }
            }
            else
            {
                ExitProcess(0);
            }
        }