最近做了C#软件,支持开机启动,但是发现在win7里面即使我的程序给它添加了权限小盾牌,正常运行软件是会弹出一个小筐提示获取管理员权限,但是再刚开机的时候是无法申请到管理员权限的,所以开机启动的程序未申请到管理员权限就未启动。
首先我们看正常情况下简单获取管理员权限的方法——app.manifest(该方法的程序不能注册开机启动):
为项目添加文件app.manifest,简单添加方法是右键项目属性在安全性选项里面有个“启动clickonce安全设置”复选框,勾选上后项目会自动生成app.manifest文件.
该文件如下:
1 <?xml version="1.0" encoding="utf-8"?>
2 <asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
3 <assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
4 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
5 <security>
6 <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
7 <!-- UAC 清单选项
8 如果希望更改 Windows 用户帐户控制级别,请用以下节点之一替换
9 requestedExecutionLevel 节点。
10
11 <requestedExecutionLevel level="asInvoker" uiAccess="false" />
12 <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
13 <requestedExecutionLevel level="highestAvailable" uiAccess="false" />
14
15 如果您希望利用文件和注册表虚拟化提供
16 向后兼容性,请删除 requestedExecutionLevel 节点。
17 -->
18 <requestedExecutionLevel level="asInvoker" uiAccess="false" />
19 </requestedPrivileges>
20 <applicationRequestMinimum>
21 <defaultAssemblyRequest permissionSetReference="Custom" />
22 <PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
23 </applicationRequestMinimum>
24 </security>
25 </trustInfo>
26 </asmv1:assembly>
然后修改18行的:<requestedExecutionLevel level="asInvoker" uiAccess="false" /> 为:<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
最后编译项目生成的EXE文件就会带有小盾牌,只要运行是确认管理员权限就行了。
但是问题就出在程序在开机的时候申请不到管理员权限,那该怎么办呢,其实方法也很简单,就是通过过一段时间动态获取管理员权限来解决。
下面就是我对该模块开机启动进行的设计代码:
if (isRunning("小馒头管家"))
{
IntPtr hTray = FindWindowA(null, "小馒头管家"); //根据窗口标题查找窗口
ShowWindow(hTray, 5); //如果窗口是隐藏,则恢复显示窗口
SetForegroundWindow(hTray); //如果程序界面不显示在最前面,则将程序显示在最前端
}
else
{
//获得当前登录的Windows用户标示
System.Security.Principal.WindowsIdentity identity = System.Security.Principal.WindowsIdentity.GetCurrent();
//创建Windows用户主题
Application.EnableVisualStyles();
System.Security.Principal.WindowsPrincipal principal = new System.Security.Principal.WindowsPrincipal(identity);
//判断当前登录用户是否为管理员
if (principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator))
{
//如果是管理员,则直接运行
Application.EnableVisualStyles();
Application.Run(new Form1());
}
else
{
//最多延时尝试5次
for (int i = 0; i < 5; i++)
{
if (isRunningOne("小馒头管家"))
{
break;
}
//创建启动对象
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
//设置运行文件
startInfo.FileName = System.Windows.Forms.Application.ExecutablePath;
//设置启动动作,确保以管理员身份运行
startInfo.Verb = "runas";
//如果不是管理员,则启动UAC
System.Diagnostics.Process.Start(startInfo);
//System.Windows.Forms.Application.Exit();
Thread.Sleep(2000);
}
}
}
}
//根据进程名称判断是否有同名进程超过两个正在运行中
private static bool isRunning(string processName)
{
return (Process.GetProcessesByName(processName).Length > 2) ? true : false;
}
//根据进程名称判断是否有同名进程正在运行中
private static bool isRunningOne(string processName)
{
return (Process.GetProcessesByName(processName).Length > 1) ? true : false;
}
这段代码添加上以后app.manifest就没必要要了,就是程序不需要带有小盾牌了,管理员权限在运行的时候自动申请,由于程序刚开始启动时不需要管理员权限所以开机可以自启动,并且在启动后重新以管理员身份启动程序,这样就解决了开机不能自动运行的问题。开机一段时间后才允许应用程序申请管理员权限的限制就可以通过上面的代码绕过,并且上面还保证了应用程序运行一次,至于为啥大家可以分析下,不过这也是我的程序需求,并且正常情况下我的程序可能不是显现的,所以采用了ShowWindow(hTray, 5); //如果窗口是隐藏,则恢复显示窗口 这段代码,要是想以其他形式显示已经运行的程序窗口,大家可以去查ShowWindow(hTray, 5);这个函数,第二个参数可以进行各种显示控制。
开机自启动的源码我也分享下:
public static bool runAtStart()
{
string starupPath = Application.ExecutablePath;
//class Micosoft.Win32.RegistryKey. 表示Window注册表中项级节点,此类是注册表装.
RegistryKey loca = Registry.LocalMachine;
RegistryKey run = loca.OpenSubKey(@"SOFTWARE/Microsoft/Windows/CurrentVersion/Run", true);
if (run == null)
{
run = loca.CreateSubKey(@"SOFTWARE/Microsoft/Windows/CurrentVersion/Run");
}
try
{
run.SetValue("小馒头管家", "/"" + starupPath + "/"");
loca.Close();
run.Close();
return true;
}
catch (Exception ee)
{
return false;
}
}
至此,该程序已经可以以管理员的权限再开机的时候自启动了,在此与大家分享下。