0

对所有人来说,我被困在一个地方,希望大家对此事提出建议。

我的应用程序包含 79 个表单,其中许多地方需要管理员权限。程序在 windows 启动时运行,因此不能使用默认的管理令牌 (asAdministator)。Lazarus 无法创建 ActiveX dll,所以还有什么其他选择对我有利,我在 lazarus 几乎完成了我的大部分项目,所以没有回头路了。

4

1 回答 1

1

如果您的应用程序确实做了一些需要管理员访问的事情

这是一个非常大的如果;几乎没有人需要它

那么最好的技术是启动你自己的提升副本,传递一个命令行开关来说明你想要采取的行动。

假设您要启动一项服务。您创建一个带有 UAC 屏蔽的按钮:

ss

然后在单击事件中,您启动自己的提升副本,并传递/StartService命令行参数:

procedure TForm1.StartService(Sender: TObject);
begin
   if IsUserAnAdmin then
   begin
      //no need to elevate, we're already an admin
      StartService();
      Exit;
   end;

   //We're a standard user, relaunch elevated to start the service
   RunAsAdmin(0, ParamStr(0), '/StartService');
end;

使用辅助函数来检查我们是否具有管理权限:

///This function tells us if we're running with administrative permissions.
function IsUserAdmin: Boolean;
var
    b: BOOL;
    AdministratorsGroup: PSID;
begin
    {
        This function returns true if you are currently running with admin privelages.
        In Vista and later, if you are non-elevated, this function will return false (you are not running with administrative privelages).
        If you *are* running elevated, then IsUserAdmin will return true, as you are running with admin privelages.

    }
    b := AllocateAndInitializeSid(
            SECURITY_NT_AUTHORITY,
            2, //2 sub-authorities
            SECURITY_BUILTIN_DOMAIN_RID,    //sub-authority 0
            DOMAIN_ALIAS_RID_ADMINS,        //sub-authority 1
            0, 0, 0, 0, 0, 0,               //sub-authorities 2-7 not passed
            AdministratorsGroup);
    if (b) then
    begin
        if not CheckTokenMembership(0, AdministratorsGroup, b) then
            b := False;
        FreeSid(AdministratorsGroup);
    end;

    Result := b;
end;

以管理员身份启动应用程序的技巧是将ShellExecuterunas动词一起使用。我曾经创建过一个方便的包装器:

function RunAsAdmin(hWnd: HWND; filename: string; Parameters: string): Boolean;
{
    See Step 3: Redesign for UAC Compatibility (UAC)
    http://msdn.microsoft.com/en-us/library/bb756922.aspx
}
var
    sei: TShellExecuteInfo;
begin
    ZeroMemory(@sei, SizeOf(sei));
    sei.cbSize := SizeOf(TShellExecuteInfo);
    sei.Wnd := hwnd;
    sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
    sei.lpVerb := PChar('runas');
    sei.lpFile := PChar(Filename); // PAnsiChar;
    if parameters <> '' then
        sei.lpParameters := PChar(parameters); // PAnsiChar;
    sei.nShow := SW_SHOWNORMAL; //Integer;

    Result := ShellExecuteEx(@sei);
end;

现在您只需要注意/StartService应用程序启动时的命令行开关。在这个快速而肮脏的代码中,我把它放在 FormActivate 中。实际上,您可以在 Application.Run 之前将其放入项目文件中:

procedure TForm1.FormActivate(Sender: TObject);
begin
    if FindCmdLineSwitch('startService', True) then
    begin
        //If we're not an admin, then use ShellExecute to launch ourselves as one
        if not IsUserAdmin then
        begin
            //Relaunch ourselves as an admin
            Toolkit.RunAsAdmin(0, ParamStr(0), '/StartService'); //don't forget to pass the command option
            Application.Terminate;
            Exit;
        end;

        //We are an admin; do the updates.
        StartOurService();
        MessageDlg('Service started!', mtInformation, [mbOk], 0);

        Application.Terminate;
        Exit;
    end;
end;

注意:任何发布到公共领域的代码。无需归属。

于 2014-01-03T03:28:16.640 回答