I've written several applications that require access to protected resources like enumerating the files and directories in the Master File Table or using the NTFS USN Journal or accessing WMI classes and objects that require admin rights.
My first attempts at writing applications that would automatically elevate was frustrating.
Facts:
You cannot elevate a running process. Process elevation happens at the moment a process is started. You cannot change its elevation level after it has been started.
Level Description
asInvoker Process does not require elevation
highestAvailable Requests the highest available privilege tokens of its parent
process.
requireAdministrator Requires elevation to full administrator privileges.
The uiAccess attribute determines whether the applicaton requires access to any protected ui elements such as system dialog boxes or higher level processes. Only signed applications may do this. That is why the value defaults to 'false'.
When you write a UAC enabled process in Visual Studio and select 'Debug>Start Debugging', Visual Studio does not start the application in a way that UAC elevation takes effect.
That said, here is a little console application that accesses the directory 'c:\Program Files' which we all know to be protected. If I try to access 'c:\program files' in a Vista machine I'll get 'Access Denied'.
I've simply created a 'WPF Application' and taken the defaults. In the xaml code, I've added a button that when clicked will attempt to enumerate the files below 'c:\Program Files'.
Here is the xaml code:
<Window x:Class="ElevateProcessProject.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<Button Name="_btnProgramFiles" Click="_btnProgramFiles_Click">
<ListBox Name="_lbFiles"/>
</Button>
</StackPanel>
</Window>
The only thing I need to do to change the User Account Control level is to replace the 'requestedExecutionLevel' node and request 'requireAdministrator' level access. To do this, I right click on the project in Solution Explorer and select 'Add>New Item...> to get the 'Add New Item' dialog. The User Account Control level is specified in the application's manifest file. So select 'Application Manifest File' and click the 'Add' button.
This will add the app.manifest file to the project that looks like:
<?xml version="1.0" encoding="utf-8"?>
<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">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
If you want to utilize File and Registry Virtualization for backward
compatibility then delete the requestedExecutionLevel node.
-->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</asmv1:assembly>
Replace the line '<requestedExecutionLevel level="asInvoker" uiAccess="false" />' with '<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />'. This is what your app.manifest file should look like.
<?xml version="1.0" encoding="utf-8"?>
<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">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
If you want to utilize File and Registry Virtualization for backward
compatibility then delete the requestedExecutionLevel node.
-->
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</asmv1:assembly>
Here is the C# code in the file Window1.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Security.Principal;
using System.Threading;
namespace ElevateProcessProject
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void _btnProgramFiles_Click(object sender, RoutedEventArgs e)
{
DirectoryInfo di = new DirectoryInfo(@"c:\Program Files");
_lbFiles.ItemsSource = di.GetFiles("*.*", SearchOption.AllDirectories);
}
}
}
However, when you attempt to launch your program with Visual Studio's 'Debug>Start Debugging' you'll notice that you do not get the UAC evelation dialog and when your application runs, it will fail with an 'Access Denied' exception. It doesn't happen immediately, give it a few seconds. I get the error 'UnauthorizedAccessException was unhandled', 'Access to the path 'c:\Program Files\Google\CrashReports' is denied'.
Now close Visual Studio and restart it using the 'Run As Administrator' option. Right click on Visual Studio in the start menu and select the 'Run as administrator' menu item. Now open your project and try to run it. It will work but no UAC dialog will appear because Visual Studio is running under Admin credentials. When VS starts an application/process it starts it under the user credentials of its process or the user credentials under which it was started and bypasses the UAC code.
So I added another button to the UI which when clicked launches another copy of my exe file such that the UAC does get invoked.
Here is the new Window1.xaml file:
<Window x:Class="ElevateProcessProject.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<Button Name="_btnLaunchExe" Click="_btnLaunchExe_Click">
Launch Another Copy of the exe
</Button>
<Button Name="_btnProgramFiles" Click="_btnProgramFiles_Click">
<ListBox Name="_lbFiles"/>
</Button>
</StackPanel>
</Window>
and the new Window1.xaml.cs file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Security.Principal;
using System.Threading;
using System.Diagnostics;
namespace ElevateProcessProject
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void _btnProgramFiles_Click(object sender, RoutedEventArgs e)
{
DirectoryInfo di = new DirectoryInfo(@"c:\Program Files");
_lbFiles.ItemsSource = di.GetFiles("*.*", SearchOption.AllDirectories);
}
private void _btnLaunchExe_Click(object sender, RoutedEventArgs e)
{
try
{
ProcessStartInfo proc = new ProcessStartInfo();
proc.UseShellExecute = true;
proc.WorkingDirectory = Directory.GetCurrentDirectory();
proc.FileName = "ElevateProcessProject.exe";
proc.Verb = "runas";
Process.Start(proc);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
Notice, I also added a using statment for the System.Diagnostics namespace so I could reference the Process and ProcessStartInfo classes.
Now when you press the 'Launch Another Copy...' button, you get the UAC dialog.
Also note that if you simply use Windows Explorer to navigate to your project>bin>Debug directory and start ElevateProcessProject.exe by clicking on it, you also get the UAC dialog asking for permission to elevate your process.





MultiQuote



|