Help with Windows Visa / 7 UAC file system virtualisation
I am working on a program that is designed to operate in conjunction with an old C++ application. A game called Age of Wonders. When running under Windows Vista or Windows 7 (with the UAC enabled) the game save files are written to the virtualisation path instead of the real path.
So for example;
Original: C:\Program Files (x86)\Age of Wonders\Save
Virtualized: C:\Users\UserName\AppData\Local\VirtualStore\Program Files (x86)\Age of Wonders\Save
In my .Net application I download files from an email server and place them in the Save folder, if I try to write to the original path I receive an Unauthorised Access Exception when the UAC is enabled. Windows doesn’t automatically convert it to the Virtualised path for me. I’ve been working around this by getting my users to run the application as administrator. However I would like to make a more elegant solution.
I could write some code to handle the exception and write to the virtualised path in my code, but I think a better solution would be to somehow switch my program into a mode so that this is done by Windows itself and not in my code. This I feel would be a better long term solution for future versions of Windows.
I have spent time searching on the internet and I have found other people who are talking about this, but no one provides any help which is actually usable. Here are the links I have looked at;
Should I use a VirtualStore solution on Vista?
Create Process with FS Virtualization Enabled
http://us.generation-nt.com/answer/using-settokeninformation-control-file-system-virtualization-vista-help-37057472.html
I need to solution to not involve the user having to modify their system settings or create any accounts to run the process under etc.
So, below I have the code for a simple Windows Forms app. It has one button and one check box, the check box is intended to switch the virtualisation mode and the button writes a small text file to 开发者_开发问答the a Program Files folder. I want to be able to use this to test the virtualisation behaviour. So I would expect the File.txt to be written to the virtualised path when the check box is checked etc.
If anyone can help me fill in the blank function I would be extremely grateful. Thanks in advance.
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
string testText = "Dave was here";
File.WriteAllText("C:\\Program Files\\DaveTest\\File.txt", testText);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
SetVirtualization(checkBox1.Checked);
}
private void SetVirtualization(bool enabled)
{
//What code do I need here, please provide examples than can be used
}
}
If you want virtualization you need a 32 bit process without a manifest. You appear to already have a 32 bit process so you need to get rid of the manifest.
I expect this will be inconvenient for your nice shiny WinForms app since you'd be abandoning the modern themed look. A simple workaround would be to code this up in a separate process for handling just the part of app that needs virtualization. The added benefit of this is that the rest of your process wouldn't have to be virtualized.
Looks like your .Net application is running as a 64 bits app. Virtualization (file/registry) is disabled for such applications.
Can you compile your app as x86? If so that should do the trick.
FYI all, I have come up with this - which does do what I wanted;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace VirtualizationTest
{
public class FileVirtualizationHelper
{
#region Win32 API routines
enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
MaxTokenInfoClass // MaxTokenInfoClass should always be the last enum
}
const UInt32 MAXIMUM_ALLOWED = 0x2000000;
[DllImport("kernel32.dll", SetLastError = true)]
static extern Boolean CloseHandle(IntPtr hSnapshot);
[DllImport("advapi32", SetLastError = true), System.Security.SuppressUnmanagedCodeSecurityAttribute]
static extern Boolean OpenProcessToken(IntPtr ProcessHandle, // handle to process
UInt32 DesiredAccess, // desired access to process
ref IntPtr TokenHandle); // handle to open access token
[DllImport("advapi32.dll", SetLastError = true)]
static extern Boolean SetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, ref UInt32 TokenInformation, UInt32 TokenInformationLength);
#endregion
#region Public Methods
public static bool Enable()
{
return SetVirtualization(true);
}
public static bool Disable()
{
return SetVirtualization(false);
}
#endregion
#region Private Methods
private static bool SetVirtualization(bool DoEnable)
{
IntPtr Token = (IntPtr)0;
UInt32 EnableValue = DoEnable ? (UInt32)1 : (UInt32)0;
UInt32 EnableValueSize = sizeof(UInt32);
if (!OpenProcessToken(Process.GetCurrentProcess().Handle, MAXIMUM_ALLOWED, ref Token))
{
return false;
}
if (!SetTokenInformation(Token, (TOKEN_INFORMATION_CLASS)24, ref EnableValue, EnableValueSize))
{
CloseHandle(Token);
return false;
}
CloseHandle(Token);
return true;
}
#endregion
}
}
精彩评论