帮助Windows Vista / 7 UAC文件系统虚拟化

时间:2011-05-16 13:54:53

标签: windows windows-7 windows-vista uac

我正在开发一个旨在与旧的C ++应用程序一起运行的程序。一款名为Age of Wonders的游戏。在Windows Vista或Windows 7(启用UAC)下运行时,游戏保存文件将写入虚拟化路径而不是真实路径。

所以例如;

原件: C:\ Program Files(x86)\ Won of Wonders \ Save

虚拟化: C:\ Users \ UserName \ AppData \ Local \ VirtualStore \ Program Files(x86)\ Age of Wonders \ Save

在我的.Net应用程序中,我从电子邮件服务器下载文件并将它们放在Save文件夹中,如果我尝试写入原始路径,则在启用UAC时会收到未经授权的访问异常。 Windows不会自动将其转换为虚拟化路径。我一直在努力让我的用户以管理员身份运行应用程序。但是我想提出一个更优雅的解决方案。

我可以编写一些代码来处理异常并在我的代码中写入虚拟化路径,但我认为更好的解决方案是以某种方式将我的程序切换到一个模式,这样就可以由Windows本身完成,而不是在我的码。对于未来版本的Windows,我认为这将是一个更好的长期解决方案。

我花时间在互联网上搜索,我发现其他人正在谈论这个,但没有人提供任何实际可用的帮助。这是我看过的链接;

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

我需要解决方案,不要让用户不得不修改他们的系统设置或创建任何帐户来运行等等的过程。

所以,下面我有一个简单的Windows窗体应用程序的代码。它有一个按钮和一个复选框,复选框用于切换虚拟化模式,按钮将一个小文本文件写入Program Files文件夹。我希望能够使用它来测试虚拟化行为。因此,我希望在选中复选框等时将File.txt写入虚拟化路径。

如果有人可以帮助我填写空白功能,我将非常感激。提前谢谢。

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
    }
}

3 个答案:

答案 0 :(得分:2)

如果您想要虚拟化,则需要一个没有清单的32位进程。您似乎已经有32位进程,因此您需要删除清单。

我希望这对你漂亮的WinForms应用程序来说不方便,因为你会放弃现代主题外观。一个简单的解决方法是在一个单独的流程中对其进行编码,以便仅处理需要虚拟化的应用程序部分。这样做的另一个好处是,您的过程的其余部分不必虚拟化。

答案 1 :(得分:1)

您的.Net应用程序看起来像64位应用程序一样运行。此类应用程序禁用虚拟化(文件/注册表)。

你能将你的应用程序编译为x86吗?如果是这样,应该做的伎俩。

答案 2 :(得分:0)

仅供参考,我已经想到了 - 这确实做了我想要的事情;

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
    }
}