Safe Storage Locations for Any User, C#, Windows
I've got an application for our company that has to store settings in a global fashion so that employees with a multitude of access types can run the program.
Most of our employees access the program using a Public account, which means they don't have to log on - there is a login ID called Public that anyone can use. The Public account, as you can guess, is severely limited!
As a result, I can't seem to find a way to get my application to write data to the PC. The error is always the same Unauthorized Access Exception.
I have tried using the following paths, that all fail:
- C:\ProgSuite\
- Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
- Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)
I do not want to use Isolated Storage, because I do not want to have to configure the application for each individual that logs in onto one of the many PCs in our plant.
Is there a way to say, "Hey, my Application is an administrator. Give me access!" ???
We are currently limited to .NET Framework 3.5.
Edit:
Exception Message:
System.UnauthorizedAccessException: Access to the path 'C:\Documents and Settings\All Users\Application Data\Aaon Coil Products, Inc\AcpConfig.log' is denied.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
at Suite.MdiForm.SaveFormSettings()
at Suite.MdiForm.MdiForm_Closing(Object sender, FormClosingEventArgs e)
at System.Windows.Forms.Form.OnFormClosing(FormClosingEventArgs e)
at System.Windows.Forms.Form.WmClose(Message& m)
at System.Windows.Forms.Form.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
************** Loaded Assemblies **************
mscorlib
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.3603 (GDR.050727-3600)
CodeBase: file:///c:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/mscorlib.dll
----------------------------------------
Suite
Assembly Version: 2.2.8.28978
Win32 Version: 2.2.8
CodeBase: file:///C:/Program%20Files/Aaon%20Coil%20Products,%20Inc/ACP%20Software%20Suite/Suite.exe
----------------------------------------
System.Windows.Forms
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.3053 (netfxsp.050727-3000)
CodeBase: file:///C:/WINDOWS/assembly/GAC_MSIL/System.Windows.Forms/2.0.0.0__b77a5c561934e089/System.Windows.Forms.dll
----------------------------------------
System
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.3053 (netfxsp.050727-3000)
CodeBase: file:///C:/WINDOWS/assembly/GAC_MSIL/System/2.0.0.0__b77a5c561934e089/System.dll
----------------------------------------
System.Drawing
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.3053 (netfxsp.050727-3000)
CodeBase: file:///C:/WINDOWS/assembly/GAC_MSIL/System.Drawing/2.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll
----------------------------------------
System.Core
Assembly Version: 3.5.0.0
Win32 Version: 3.5.30729.1 built by: SP
CodeBase: file:///C:/WINDOWS/assembly/GAC_MSIL/System.Core/3.5.0.0__b77a5c561934e089/System.Core.dll
----------------------------------------
System.Management
Assembly Versio开发者_StackOverflow中文版n: 2.0.0.0
Win32 Version: 2.0.50727.3053 (netfxsp.050727-3000)
CodeBase: file:///C:/WINDOWS/assembly/GAC_MSIL/System.Management/2.0.0.0__b03f5f7f11d50a3a/System.Management.dll
----------------------------------------
System.Data
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.3053 (netfxsp.050727-3000)
CodeBase: file:///C:/WINDOWS/assembly/GAC_32/System.Data/2.0.0.0__b77a5c561934e089/System.Data.dll
----------------------------------------
System.Xml
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.3082 (QFE.050727-3000)
CodeBase: file:///C:/WINDOWS/assembly/GAC_MSIL/System.Xml/2.0.0.0__b77a5c561934e089/System.Xml.dll
----------------------------------------
System.DirectoryServices
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.3053 (netfxsp.050727-3000)
CodeBase: file:///C:/WINDOWS/assembly/GAC_MSIL/System.DirectoryServices/2.0.0.0__b03f5f7f11d50a3a/System.DirectoryServices.dll
----------------------------------------
System.Configuration
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.3053 (netfxsp.050727-3000)
CodeBase: file:///C:/WINDOWS/assembly/GAC_MSIL/System.Configuration/2.0.0.0__b03f5f7f11d50a3a/System.Configuration.dll
----------------------------------------
System.Transactions
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.3053 (netfxsp.050727-3000)
CodeBase: file:///C:/WINDOWS/assembly/GAC_32/System.Transactions/2.0.0.0__b77a5c561934e089/System.Transactions.dll
----------------------------------------
System.EnterpriseServices
Assembly Version: 2.0.0.0
Win32 Version: 2.0.50727.3053 (netfxsp.050727-3000)
CodeBase: file:///C:/WINDOWS/assembly/GAC_32/System.EnterpriseServices/2.0.0.0__b03f5f7f11d50a3a/System.EnterpriseServices.dll
----------------------------------------
AcpSuiteFormHeader
Assembly Version: 1.0.0.0
Win32 Version: 1.0.0
CodeBase: file:///C:/Program%20Files/Aaon%20Coil%20Products,%20Inc/ACP%20Software%20Suite/AcpSuiteFormHeader.DLL
----------------------------------------
I know you said you don't want to use IsolatedStorage because you don't want each user to have to have their own setup.
But have you looked at IsolatedStorage using the MachineStore? If I understand correctly (and I could be wrong, wouldn't be the first time) this should be shared by all users accessing your application.
using(IsolatedStorageFile store = IsolatedStorageFile.GetMachineStoreForApplication())
{
// check if file exists or not
try
{
using(IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(STG_FILE_NAME, FileMode.OpenOrCreate, store)) {
StreamWriter sw = new StreamWriter(isfs);
foreach(string key in m_values.Keys) {
sw.WriteLine(key + "::" + m_values[key]);
} // foreach
sw.Flush();
} // using
} catch(IOException) {
// generally because file is locked by another process...do nothing
}
} // using
Alternatively, you could change your application's config file's permissions to allow everyone access. This could be done by a privileged user the first time your app is run.
http://www.techtalkz.com/c-c-sharp/153732-saving-files-so-any-user-can-access-them.html
FileInfo fileInfo = new FileInfo(path);
FileSecurity fileSecurity = fileInfo.GetAccessControl();
fileSecurity.AddAccessRule(new FileSystemAccessRule(
"Users",
FileSystemRights.FullControl,
AccessControlType.Allow) );
fileInfo.SetAccessControl(fileSecurity);
I use CommonApplicationData and it seems to work fine with one caveat. I found that if an admin creates the subdirectory within the CommonApplicationData path then other users who are 'non-admin' cannot delete that directory. You may need to add a FileSystemAccessRule
to the DirectoryInfo object when you create the sub directory contained within CommonApplicationData. There is an overload to the create method that allows you to specify an object of this type.
Edit: with code snippet.
public static void CreateWithEveryoneFullControlIfAdmin(this DirectoryInfo source)
{
if (IsAdmin())
{
DirectorySecurity directorySecurity = Directory.GetAccessControl(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData));
FileSystemAccessRule accessRule
= new FileSystemAccessRule(@"BUILTIN\Users", FileSystemRights.FullControl,
InheritanceFlags.ObjectInherit | InheritanceFlags.ContainerInherit,
PropagationFlags.None,
AccessControlType.Allow);
bool modified = false;
directorySecurity.ModifyAccessRule(AccessControlModification.Add,
accessRule,
out modified);
if (modified)
{
source.Create(directorySecurity);
}
else
{
source.Create();
}
}
else
{
source.Create();
}
}
public static bool IsAdmin()
{
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
You should be able to write to any folder whose permissions are set to allow Everybody. You can set those permissions inside your installer, or even by hand.
OK, here is my two cents hope it helps you.
If you can hard-code some admin level credentials in your program, you may use impersonation for that part of the code that needs access to folders. Have a look here:
http://www.codeproject.com/KB/cs/cpimpersonation1.aspx
Personally, however, I would favour storing in ApplicationData.
精彩评论