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

C# 如何解决需要完全控制/所有者权限的LocalFileSettingsProvider问题,c#,.net,fileshare,settings,roaming-profile,C#,.net,Fileshare,Settings,Roaming Profile,在.NET客户端应用程序中,我使用默认设置提供程序,范围=用户,漫游=True。这在大多数环境中都可以正常工作,无论是客户端还是终端服务器,但具有Citrix终端服务器场的客户除外。无论何时属性。调用Settings.Default.Save(),将引发以下异常: System.UnauthorizedAccessException: Attempted to perform an unauthorized operation. at System.Security.AccessContr

在.NET客户端应用程序中,我使用默认设置提供程序,范围=用户,漫游=True。这在大多数环境中都可以正常工作,无论是客户端还是终端服务器,但具有Citrix终端服务器场的客户除外。无论何时
属性。调用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都是内部的。当然,我不想破坏当前使用的设置,因此,仅仅用“注释掉一行”(设置文件的所有者)就可以重建所有内容,这看起来是一个荒谬的代码量

你知道我还能试什么吗


客户无法更改其文件共享权限中的任何内容…

我在Citrix上遇到过类似问题-AppData被重定向到“更改并读取”网络共享(而不是“完全控制”-它在“完全控制”下工作)。在第一次运行时,我们的应用程序将在第一次Save()调用中创建user.config,但在任何后续Save()调用中抛出UnauthorizedAccessException

答案似乎是在调用Save()之前删除user.config文件(如果存在)

我们目前正在与我们的客户测试这一点-我会更新我的答案,当我有具体的结果

更新:在调用Save()之前,您需要“触摸”Settings.Default中的每个设置,因为临时文件实际上已与现有user.config合并。通过在调用Save()之前调用以下方法,每次都会正确地重新创建user.config(不会引发UnauthorizedAccessException)


备注:此应用程序的目标是.NET 2/3.5 CLR。以.NET 4.0/4.5为目标会产生相同的异常,只是堆栈跟踪缺少几行,因此升级到较新的.NET版本没有帮助。@realmarkuscmidt您能找到解决问题的方法吗?在我的情况下,它在正常的应用程序数据文件夹下工作正常,但在应用程序数据文件夹(即重定向)的情况下出现问题。我得到相同的异常。我尝试在保存配置文件之前删除配置文件,但它给我一个异常,即“配置文件已被其他程序更改”
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());
    }
}