如何解决LocalFileSettingsProvider需要完全控制/所有权的问题

6

在我的.NET客户端应用程序中,我使用默认的设置提供程序,范围为User且Roaming=True。在大多数环境下,无论是客户端还是终端服务器,都可以正常工作,但对于一个Citrix终端服务器群集的客户来说,情况并非如此。每当调用Properties.Settings.Default.Save()时,就会抛出以下异常:

System.UnauthorizedAccessException: Attempted to perform an unauthorized operation.
   at System.Security.AccessControl.Win32.SetSecurityInfo(ResourceType type, String name, SafeHandle handle, SecurityInfos securityInformation, SecurityIdentifier owner, SecurityIdentifier group, GenericAcl sacl, GenericAcl dacl)
   at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, SafeHandle handle, AccessControlSections includeSections, Object exceptionContext)
   at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, AccessControlSections includeSections, Object exceptionContext)
   at System.Security.AccessControl.FileSystemSecurity.Persist(String fullPath)
   at System.Configuration.Internal.WriteFileContext.DuplicateTemplateAttributes (String source, String destination)
   at System.Configuration.Internal.WriteFileContext.DuplicateFileAttributes(String source, String destination)
   at System.Configuration.Internal.WriteFileContext.Complete(String filename, Boolean success)
   at System.Configuration.Internal.InternalConfigHost.StaticWriteCompleted(String streamName, Boolean success, Object writeContext, Boolean assertPermissions)
   at System.Configuration.Internal.DelegatingConfigHost.WriteCompleted(String streamName, Boolean success, Object writeContext, Boolean assertPermissions)
   at System.Configuration.ClientSettingsStore.ClientSettingsConfigurationHost.WriteCompleted(String streamName, Boolean success, Object writeContext)
   at System.Configuration.UpdateConfigHost.WriteCompleted(String streamName, Boolean success, Object writeContext)
   at System.Configuration.MgmtConfigurationRecord.SaveAs(String filename, ConfigurationSaveMode saveMode, Boolean forceUpdateAll)
   at System.Configuration.ClientSettingsStore.WriteSettings(String sectionName, Boolean isRoaming, IDictionary newSettings)
   at System.Configuration.LocalFileSettingsProvider.SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection values)
   at System.Configuration.SettingsBase.SaveCore()
   at System.Configuration.SettingsBase.Save()

这个异常的原因是:
  • System.Configuration.Internal.WriteFileContext 在用户的漫游配置文件中写入用户设置的新副本 (...newcfg)。然后,DuplicateTemplateAttributes 尝试修改此文件的 ACL,并将所有权显式设置为当前用户。
  • 在这位客户的情况下,这会失败,因为漫游配置文件存储在文件共享上,而用户只有“读取”和“更改”权限,没有“完全控制”权限。他们可能在 NTFS 上拥有完全控制 (因为默认情况下,您是创建的所有文件的“所有者”,作为所有者,您可以对文件执行任何操作,无论是否显式设置了“完全控制”),但似乎在 SMB 共享级别被阻止。

这种行为对我来说毫无意义:鉴于 LocalFileSystemProvider 总是使用当前用户的私有配置文件夹 (本地或漫游),我们可以安全地假设用户已经是所有者了。

由于 WriteFileContext 捕获了异常、删除了临时的 .newcfg 文件,然后重新抛出异常,所以没有办法在我的代码中捕获异常并重命名文件或以某种方式获取其内容,因为当异常抛出时该文件已经被删除。

我找不到任何简单的方法来解决此问题,除了实现自己的设置提供程序。为此,似乎我甚至必须重建像序列化部分这样的所有 System.Configuration 东西,因为它们都是内部的。当然,我不想破坏当前使用的设置,所以看起来需要大量的代码才能以与“注释掉一行”相同的方式重新构建所有内容 (设置文件的所有者)。

还有其他什么方法可以尝试吗?

没有办法让客户更改其文件共享权限...


备注:此应用程序的目标为 .NET 2/3.5 CLR。升级到较新版本的 .NET 并不会有所帮助,因为针对 .NET 4.0/4.5 的目标将产生相同的异常,只是堆栈跟踪少了几行。 - realMarkusSchmidt
@realMarkusScmidt,你找到解决问题的方法了吗?在我的情况下,正常应用程序数据文件夹可以正常工作,但是在应用程序数据文件夹即重定向的情况下会出现问题。我遇到了相同的异常。 - V K
1个回答

1
我在Citrix上遇到了类似的问题 - AppData被重定向到“更改和读取”网络共享(不是“完全控制” - 它可以在“完全控制”下工作)。第一次运行时,我们的应用程序将在第一次Save()调用时创建user.config,但在任何后续的Save()调用中都会抛出UnauthorizedAccessException。
答案似乎是在调用Save()之前删除user.config文件(如果存在)。
我们目前正在与客户进行测试 - 当我有具体结果时,我会更新我的答案。
更新:在调用Save()之前,您需要“触碰”Settings.Default中的每个设置,因为临时文件实际上与现有的user.config合并。通过在调用Save()之前调用以下方法,每次都可以正确重新创建user.config(不会抛出UnauthorizedAccessException)。
public static void ClearUserConfigFile()
{
    //Touch each setting
    foreach (SettingsProperty property in Settings.Default.Properties)
    {
        if (property.DefaultValue != Settings.Default[property.Name])
            Settings.Default[property.Name] = Settings.Default[property.Name];
    }

    //Delete the user.config file
    var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoaming);
    var userConfigPath = config.FilePath;
    try
    {
        if (File.Exists(userConfigPath) == true)
            File.Delete(userConfigPath);
    }
    catch (Exception ex)
    {
        _log.ErrorFormat("Exception thrown while deleting user.config : {0}", ex.ToString());
    }
}

我在保存配置文件之前尝试删除它,但是它报出了异常:“配置文件已被另一个程序更改”。 - V K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接