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

发布于 2024-11-07 20:38:48 字数 2339 浏览 9 评论 0原文

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

例如;

原来的: C:\Program Files (x86)\Age of Wonders\Save

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

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

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

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

我应该在 Vista 上使用 VirtualStore 解决方案吗?

创建启用 FS 虚拟化的进程

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

吻泪 2024-11-14 20:38:48

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

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

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.

゛清羽墨安 2024-11-14 20:38:48

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

您可以将您的应用程序编译为 x86 吗?如果是这样,那应该可以解决问题。

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.

猫弦 2024-11-14 20:38:48

仅供参考,我想出了这个 - 它确实达到了我的要求;

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

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
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文