C# P/Invoke Attribute - c#

New to C# Compact edition 6.5. I am trying to set the datetime on a file which seems to be off by 5 hours from the actual system time. I am doing only this to create the file:
FileStream fs= File.Create(name);
Just doing this the Created date is 5 hours ahead...if I try and set the CreationTime I get a compile error saying the Attribute is Readonly, seriously?
FileInfo fi = new FileInfo(name);
fi.CreationTime = date;
So my question is since I am new to C# how do you get access to a "readonly" Attribute in the CE framework? I see mentioning of P/Invoke but seems to work on methods only and not attributes. Anyone can given a quick demo on how to do this?
I've tried this solution and still get the file writing UTC even though I send it the current local time

I just ran this:
[MTAThread]
static void Main()
{
var name = "\\foo.txt";
var info = new FileInfo(name);
using (info.Create()) { }
info.Refresh();
var createTime = info.CreationTime;
var now = DateTime.Now;
var delta = now - createTime;
Debug.WriteLine(delta.ToString());
}
And got this output:
00:00:00.0140000
Which seems to be correct to me.

You can't modify the CreationTime of a file. It's set once and only once when the file is created. If you're willing to use P/Invoke to set the time, you can check out this similar question - c# - Change file LastWriteDate in Compact Framework
Instead of hacking the problem, though, you should fix the root cause. If there's an issue with the creation time of the file, I would consider checking your system's time settings (including timezone).

Related

c# SMO Backup - how to use ExpirationDate?

I followed roughly this example to backup a database with Microsofts SMO API and the code crashed with an exception telling invalid parameter ExpirationDate. I checked the documentation which does not contain details on how to set the parameter and my intuition told me it should be in the future, right? I was curious and tested some values:
DateTime.Today.AddDays(10) -> InvalidDataException
DateTime.Today.AddDays(-10) -> works fine
DateTime.Today.AddDays(-5) -> works fine
DateTime.Today.AddDays(-4) -> works fine
DateTime.Today.AddDays(-3) -> InvalidDataException
DateTime.Today.AddDays(-1) -> InvalidDataException
DateTime.Today.AddDays(100) -> InvalidDataException
DateTime.Today.AddDays(500) -> InvalidDataException
DateTime.Today.AddDays(1000) -> works fine
Reading this 5 year-old post it could be that the internal parameter is actually not of the type DateTime? But then it would be a bug, right?
These errors are likely the result of the locale of where the Backup.ExpirationDate property is being set from. Depending on the culture this is being executed in, the DateTime.AddDays method may increment the month instead of the day as expected, leading to the inconsistent results you saw. Of the values that you tested only the negative ones should cause errors, as the range of days for a backup expiration date is 0 - 99999, with 0 indicating that the backup will never expire as stated in the documentation. Try using the CultureInfo class to define a new locale then set the expiration date. This will require a reference to the System.Globalization namespace. Running the following code gave me no errors in setting the expiration date in a backup operation using the US (en-US) culture. Just make sure that the date in the culture you convert this to matches the date you expect it to in your timezone.
using System.Globalization;
string folderPath = #"C:\YourFolder\";
Server serv = new Server(#"YourServer");
Backup bkup = new Backup();
bkup.Database = "YourDatabase";
string bkupFilePath = folderPath + bkup.Database.ToString() + ".bak";
bkup.Action = BackupActionType.Database;
bkup.Devices.AddDevice(bkupFilePath, DeviceType.File);
bkup.BackupSetName = "YourDatabase Full Backup";
bkup.BackupSetDescription = "Full backup of YourDatabase";
DateTime today = DateTime.Now;
//define current date representation with en-US culture
string newLocale = today.ToString(new CultureInfo("en-US"));
//set Backup.ExpirationDate to use new culture
bkup.ExpirationDate = Convert.ToDateTime(newLocale);
bkup.ExpirationDate.AddDays(10);
bkup.ExpirationDate.AddDays(100);
bkup.ExpirationDate.AddDays(500);
bkup.ExpirationDate.AddDays(1000);
bkup.SqlBackup(serv);
edit I am super confused. I thought this solved my issue:
My issue was that I called backup.ExpirationDate.AddDays(X) without assigning it to anything. Therefore, the software was basically using "DateTime.Now".
Solution:
backup.ExpirationDate = backup.ExpirationDate.AddDays(X);
But it didn't completely. I still get the exception if I do this:
backup.ExpirationDate = backup.ExpirationDate.AddDays(1);
No idea why this code is wrong.

File creation time in C#

I need to get when a file was created - I have tried using:
FileInfo fi = new FileInfo(FilePath);
var creationTime = fi.CreationTimeUtc;
and
var creationTime = File.GetCreationTimeUtc(FilePath);
Both methods generally return the wrong creation time - I guess it is being cached somewhere.
The file is deleted and re-created with the same name and I need to know when/if it has been re-created (by checking if the created date/time has changed) - I had planned to do this by seeing it the file creation time had changed but I have found this to be inaccurate.
I'm working on Win 7 and if I check File Explorer it shows the new file creation time correctly.
I have also tried using the FileSystemWatcher but it doesn't entirely work for my use case. E.g. if my program is not running, the FileSystemWatcher is not running, so when my program starts up again I don't know if the file has been deleted and recreated or not.
I've seen MSDN http://msdn.microsoft.com/en-us/library/system.io.file.getcreationtime.aspx where it says:
This method may return an inaccurate value, because it uses native functions whose values may not be continuously updated by the operating system.
But I have also tried using their alternative suggestion and setting the SetCreationDate after creating a new file but I also find that this doesn't work. See test below:
[Test]
public void FileDateTimeCreatedTest()
{
var binPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
var fullFilePath = Path.Combine(binPath, "Resources", "FileCreatedDatetimeTest.txt");
var fullFilePathUri = new Uri(fullFilePath);
var dateFormatted = "2013-08-17T15:31:29.0000000Z"; // this is a UTC string
DateTime expectedResult = DateTime.MinValue;
if (DateTime.TryParseExact(dateFormatted, "o", CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal, out expectedResult)) // we expect the saved datetime to be in UTC.
{
}
File.Create(fullFilePathUri.LocalPath);
Thread.Sleep(1000); // give the file creation a chance to release any lock
File.SetCreationTimeUtc(fullFilePathUri.LocalPath, expectedResult); // physically check what time this puts on the file. It should get the local time 16:31:29 local
Thread.Sleep(2000);
var actualUtcTimeFromFile = File.GetCreationTimeUtc(fullFilePathUri.LocalPath);
Assert.AreEqual(expectedResult.ToUniversalTime(), actualUtcTimeFromFile.ToUniversalTime());
// clean up
if (File.Exists(fullFilePathUri.LocalPath))
File.Delete(fullFilePathUri.LocalPath);
}
Any help much appreciated.
You need to use Refresh:
FileSystemInfo.Refresh takes a snapshot of the file from the current
file system. Refresh cannot correct the underlying file system even if
the file system returns incorrect or outdated information. This can
happen on platforms such as Windows 98.
Calls must be made to Refresh before attempting to get the attribute
information, or the information will be outdated.
The key bits from MSDN indicate that it takes a snapshot and attribute information..will be outdated.
Try using FileInfo and Refresh method of it
fileInfo.Refresh();
var created = fileInfo.CreationTime;
this should work
File.Create(fullFilePathUri.LocalPath);
Thread.Sleep(1000); // give the file creation a chance to release any lock
That is not how you do it. File.Create creates stream writer which should be closed to release the lock without any waiting. If you find yourself using Thread.Sleep, you will often find that you are doing something wrong.
If the file described in the path parameter does not exist, this method returns 12:00 midnight, January 1, 1601 A.D. (C.E.) Coordinated Universal Time (UTC), adjusted to local time.
https://learn.microsoft.com/en-us/dotnet/api/system.io.file.getcreationtime?view=netframework-4.8

Delete files older than a date

I am currently working on a c# program where I check the creation time of a file and delete it if the file is older than 2 days. I have the following code snippet that should be achieving this.
DateTime creationTime = file.CreationTime.Date;
if (creationTime < DateTime.Now.AddDays(-logAge) && file.Name != currentLog)
{
File.Delete(string.Format("{0}/{1}", directory, file));
}
While my program is running it is constantly creating new files and a separate thread checks that the files are no older than say 2 days. If I have my PC's date set to the 24th April the files are created and kept as expected, if I then change the PC's date to the 25th April I would expect the files to remain as they are not older than 2 days, however, this is not the case as they are being deleted.
Log age is set to so I wouldn't have expected files to be deleted until after I had changed the date to be the 26th April.
What am I doing wrong, I've looked at many examples including another question on Stackoverflow Delete files older than 3 months old in a directory using .NET but its not doing what I would expect it to.
You forced to consider only the date part of the creation time-stamp then condition is satisfied and file will be deleted (earlier) anyway I suggest a few modifications to that code:
static class Helpers {
public static void DeleteOldFiles(string folderPath, uint maximumAgeInDays,
params string[] filesToExclude) {
DateTime minimumDate = DateTime.Now.AddDays(-maximumAgeInDays);
var filesToDelete = Directory.EnumerateFiles(folderPath)
.Where(x => !IsExcluded(x, filesToExclude));
foreach (var eligibleFileToDelete in filesToDelete)
DeleteFileIfOlderThan(eligibleFileToDelete, minimumDate);
}
private const int RetriesOnError = 3;
private const int DelayOnRetry = 1000;
private static bool IsExcluded(string item, string[] exclusions) {
return exclusions.Contains(item, StringComparer.CurrentCultureIgnoreCase);
}
private static void DeleteFileIfOlderThan(string path, DateTime date)
{
for (int i = 0; i < RetriesOnError; ++i) {
try {
var file = new FileInfo(path);
if (file.CreationTime < date)
file.Delete();
}
catch (IOException) {
System.Threading.Thread.Sleep(DelayOnRetry);
}
catch (UnauthorizedAccessException) {
System.Threading.Thread.Sleep(DelayOnRetry);
}
}
}
}
Notes
I'm still using DateTime.Now, I guess for this kind of operations you do not need any precision measurement (and you're talking about days so your thread may have a scheduled time of hours).
If your application uses multiple log files you can specify them all as parameters and they'll be ignored.
If you call DeleteOldFiles with 0 for maximumAgeInDays then you'll delay all log files not in use (as specified in the exclusion list).
Sometimes files can be in use (even if this should happen seldom in your case). The DeleteFileIfOlderThan function will retry to delete them after a short delay (it mimics Explorer.exe behavior).
You can call this function in this way:
Helpers.DeleteOldFiles(#"c:\mypath\", logAge, currentLog);
Few more notes:
This code doesn't combine path and file name but if you have to do it you should use Path.Combine(), I guess you do not want to reinvent the wheel each time to check if a path ends with a trailing backslash or not.
I/O operations can fail! Always check for exceptions.
file.Delete does make more sense than File.Delete(path) and Path.Combine() makes a lot more sense than using string.Format.
I've stumbled across this answer, don't know why I didn't find it before hand after spending ages on google, but this appears to have fixed the problem. DateTime.Compare how to check if a date is less than 30 days old?. The other problem was that I was using the file creation time but for my scenario it made more sense to use lastWriteTime.date.
I guess an additional problem must be in
File.Delete(string.Format("{0}/{1}", directory, file));
Your file is of type FileSystemInfo. Maybe you wanted to use file.Name.
Example: let's say directory is "c:\" and file points to "c:\myfile.log", your code will try to delete "c:/c:\myfile.log". It's hard for me to guess what exactly you have in these variables.
Correct replacement is suggested by #HenkHolterman:
file.Delete();

Sharepoint web.config changes using SPWebConfigModification

I've written a sharepoint application that needs to change web.config
I have a feature that is supposed to make all these configurations. The code for that feature is like this:
SPSite site = properties.Feature.Parent as SPSite;
List<SPWebConfigModification> modifications = new List<SPWebConfigModification>();
modifications.AddRange(CustomErrorsModeConfig.Modifications);
webConfigModificationHelper.AddWebConfigModifications(site.WebApplication, modifications);
CustomErrorsModeConfig.Modifications property contains this code:
public static SPWebConfigModification[] Modifications = {
new SPWebConfigModification()
{
Owner = WebConfigModificationOwner,
Name = "mode",
Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute,
Path = "system.web/customErrors",
Sequence = 0,
Value = "Off"
}
};
Then finally the webConfigModificationHelper.AddWebConfigModifications method:
foreach (SPWebConfigModification modification in modifications)
{
webApp.WebConfigModifications.Add(modification);
}
webApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
webApp.Update();
The problem is that I keep getting this error:
Name cannot begin with the ''' character, hexadecimal value 0x27. Line 1, position 1453
Could this be a problem with the web.config before I try to apply my changes ?
Could the SPWebConfigModification property be incorrectly defined ?
Is there some glitch in my code that leads to this error ?
Might there be some property I am missing (e.g. web.AllowUnsafeUpdates) ?
Some sharepoint site configuration ?
I've been trying to solve this issue for some time now with no luck :( Any ideas ?
I can recommend using stsadmwebconfig for making changes to web.config files. I've implemented this in many features and it has always been a pain, especially while developing. Using this tool makes it a lot easier.
Ive seen this before when the file format is not correctly set between the file and the declaration.
Open the web.config file into a advanced text editor (Notepad++ or Visual Studio) and manually force the file type to match what is specified. Usually its going to be UTF-8.
For more info:
http://www.dumpsterdoggy.com/tutorials/?xmlexception-name-cannot-begin-with
Try taking the List template and for loop out and set the property using straightforward syntax. Here's a post for setting the property in your example, see if you can get this to work and then progress to building up a more generic solution with a List and iteration over the items in the list.
http://www.sharepointkings.com/2008/05/how-to-modify-webconfig-file-in.html

Directory.GetFiles keeping the last access time

It appears that Directory.GetFiles() in C# modifies the Last access date of a file.
I've googled for hours and can't seem to find a work around for this issue. Is there anyway to keep all the MAC (Modified, Accessed, Created) attributes of a file?
I'm using Directory.GetDirectories(), Directory.GetFiles(), and FileInfo.
Also, the fi.LastAccessTime is giving strange results -- the date is correct, however, the time is off by 2 minutes, or a few hours.
Time of function execution: 10/31/2008 8:35 AM
Program Shows As Last Access Time
0_PDFIndex.html - 10/31/2008 8:17:24 AM
AdvancedArithmetic.pdf - 10/31/2008 8:31:05 AM
AdvancedControlStructures.pdf - 10/30/2008 1:18:00 PM
AoAIX.pdf - 10/30/2008 1:18:00 PM
AoATOC.pdf - 10/30/2008 12:29:51 PM
AoATOC2.pdf - 10/30/2008 1:18:00 PM
Actual Last Access Time
0_PDFIndex.html - 10/31/2008 8:17 AM
AdvancedArithmetic.pdf - 10/30/2008 12:29 PM
AdvancedControlStructures.pdf - 10/30/2008 12:29 PM
AoAIX.pdf - 10/30/2008 12:29 PM
AoATOC.pdf - 10/30/2008 12:29 PM
AoATOC2.pdf - 10/30/2008 12:29 PM
Below is the method I'm using. If you require more information, please let me know.
Thanks!
public void PopulateTreeView(string directoryValue, ref TreeNode parentNode)
{
string[] directoryArray = Directory.GetDirectories(directoryValue);
string[] fileArray = Directory.GetFiles(directoryValue, "*.*", SearchOption.AllDirectories);
try
{
#region Directories
if (directoryArray.Length != 0)
{
foreach (string directory in directoryArray)
{
DirectoryInfo di = new DirectoryInfo(directory);
TreeNode dirNode = parentNode.Nodes.Add(di.Name);
FileNode fn = new FileNode();
fn.bIsDir = true;
fn.dir = di;
dirNode.Tag = fn;
PopulateTreeView(directory, ref dirNode);
Application.DoEvents();
}
}
#endregion
#region Files
if (fileArray.Length != 0)
{
foreach (string file in fileArray)
{
FileInfo fi = new FileInfo(file);
TreeNode fileNode = parentNode.Nodes.Add(fi.Name);
FileNode fn = new FileNode();
fn.bIsDir = false;
fn.file = fi;
fileNode.Tag = fn;
fileNode.ImageIndex = 1;
Console.WriteLine(fi.Name + " - " + fi.LastAccessTime);
}
}
#endregion
}
catch (UnauthorizedAccessException)
{
parentNode.Nodes.Add("Access denied");
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
Application.DoEvents();
}
}
i know the differences between the attributes. What i need is for the file to remain exactly the same all attributes and meta-data, as if my program never touched the file; this includes the last access date.
I know this is far from ideal, but u can use fsutil (provided with Windows) to disable last access time writing:
fsutil behavior set disablelastaccess 1
Presumably you'd set it back to 0 once done. You can invoke this using Process.Start from C#, but there must be a better programmatic way (calling into Windows API).
Process.Start("fsutil", "behavior set disablelastaccess 1").WaitForExit();
Do note that this is a global Windows setting and would also affect disk access from outside your app...
Access times are different from last write times. If you use fi.LastWriteTime I think you will find that the times are the same displayed in explorer or cmd window.
Of course the last access and last write could be the same, but they are not necessarily the same.
(Reposting this as a response rather than a comment...)
I've just run this snippet of code here, and it's left the last access time alone - I can't reproduce the problem you're seeing, so Directory.GetFiles isn't broken 100% of the time.
Filemon can check whether some other app is doing this: http://technet.microsoft.com/en-us/sysinternals/bb896642.aspx
If you're doing forensics and you don't want the drive to be modified, why are you mounting it in a writable mode? You should be accessing it read-only to guarantee that you aren't accidentally changing something. Also, I would hope that you're not running your program in the OS of the person who's disk you're examining... you have just added the disk to a machine you control, right?
Not sure if this is related or not, but from MSDN:
When first called, FileSystemInfo
calls Refresh and returns the cached
information on APIs to get attributes
and so on. On subsequent calls, you
must call Refresh to get the latest
copy of the information.
BTW, "LastAccessTime" basically tells you the last time you "looked at" the file. In the absence of stale data, this would always be "now"... Not particularly useful, IMHO.
Access time would show a read only marker, last write would show the file being modified.
I haven't tried this, but Google suggests:
Disable the NTFS Last Access Time Stamp
It's a system-wide change, so be aware of that...
If you're accessing the disk for forensic purposes then you really should be doing it with the entire hard disk write-protected at the hardware level (and hence this isn't really a programming question).
A Google search for hdd "write protect" will reveal plenty of potential solutions.

Categories