I am writing a script task in my SSIS package to delete the log file being used by the package if it is older than X number of days. Here is my current code:
if (File.Exists((String)Dts.Variables["ErrorLogLocation"].Value))
{
DateTime logCreatedDate = File.GetCreationTime((String)Dts.Variables["ErrorLogLocation"].Value);
if (logCreatedDate < DateTime.Now.AddDays(-3))
{
try
{
File.Delete((String)Dts.Variables["ErrorLogLocation"].Value);
}
catch (Exception e)
{
using (StreamWriter sw = File.AppendText((String)Dts.Variables["ErrorLogLocation"].Value))
{
sw.WriteLine(DateTime.Now + " : "+ e);
}
}
using (StreamWriter sw = File.AppendText((String)Dts.Variables["ErrorLogLocation"].Value))
{
sw.WriteLine("New log Creation Date: " + File.GetCreationTime((String)Dts.Variables["ErrorLogLocation"].Value));
}
}
}
it seems like it would work, but the issue is when I delete the file, then write to a new file with the same name, the information inside is being wiped as expected, but the file creation date remains the same as before the deletion. This is an issue because i am basing when to delete this file on that datetime. The expected behavior in my mind is it would delete the file, then write to a completely new file with a new created date.
Any idea what may be causing this? Is it due to the fact I am deleting a file then immediately appending to a file with the same name(which i would assume would just create a new file if it doesnt exist?) is this file still being kept in memory or something during that period?
From the documentation:
NTFS-formatted drives may cache information about a file, such as file
creation time, for a short period of time. As a result, it may be
necessary to explicitly set the creation time of a file if you are
overwriting or replacing an existing file.
Based on this information, you must call File.SetCreationTime to make sure it gets the desired timestamp.
Related
Every time I save a file and delete it right away using the function below, I keep getting this error message: "System.IO.IOException: The process cannot access the file because it is being used by another process".
Waiting for a couple of minutes or closing visual studio seems to only unlock the files that you uploaded previously.
public static bool DeleteFiles(List<String> paths)
{ // Returns true on success
try
{
foreach (var path in paths)
{
if (File.Exists(HostingEnvironment.MapPath("~") + path))
File.Delete(HostingEnvironment.MapPath("~") + path);
}
}
catch (Exception ex)
{
return false;
}
return true;
}
I think that the way I'm saving the files may cause them to be locked. This is the code for saving the file:
if (FileUploadCtrl.HasFile)
{
filePath = Server.MapPath("~") + "/Files/" + FileUploadCtrl.FileName;
FileUploadCtrl.SaveAs(filePath)
}
When looking for an answer I've seen someone say that you need to close the streamReader but from what I understand the SaveAs method closes and disposes automatically so I really have no idea whats causing this
After some testing, I found the problem. turns out I forgot about a function I made that was called every time I saved a media file. the function returned the duration of the file and used NAudio.Wave.WaveFileReader and NAudio.Wave.Mp3FileReader methods which I forgot to close after I called them
I fixed these issues by putting those methods inside of a using statement
Here is the working function:
public static int GetMediaFileDuration(string filePath)
{
filePath = HostingEnvironment.MapPath("~") + filePath;
if (Path.GetExtension(filePath) == ".wav")
using (WaveFileReader reader = new WaveFileReader(filePath))
return Convert.ToInt32(reader.TotalTime.TotalSeconds);
else if(Path.GetExtension(filePath) == ".mp3")
using (Mp3FileReader reader = new Mp3FileReader(filePath))
return Convert.ToInt32(reader.TotalTime.TotalSeconds);
return 0;
}
The moral of the story is, to check if you are opening the file anywhere else in your project
I think that the problem is not about streamReader in here.
When you run the program, your program runs in a specific folder. Basically, That folder is locked by your program. In that case, when you close the program, it will be unlocked.
To fix the issue, I would suggest to write/delete/update to different folder.
Another solution could be to check file readOnly attribute and change this attribute which explained in here
Last solution could be using different users. What I mean is that, if you create a file with different user which not admin, you can delete with Admin user. However, I would definitely not go with this solution cuz it is too tricky to manage different users if you are not advance windows user.
In my project I have created a logging system which is basically a shell on top of nLog. I am trying to unittest the archive feature of the logging system. It is currently setup to do rolling archives with a max archive files of 5 (Nlog is setup via code, no configuration file is used):
var myFileTarget = new FileTarget();
LogConfig.AddTarget("file", myFileTarget);
myFileTarget.FileName = LogFile;
myFileTarget.Layout = LogFileLayout;
myFileTarget.AutoFlush = true;
//Archive specifics
var token = "{#}";
var archiveFileName = $"{Path.GetFileNameWithoutExtension(LogFile)}.{token}.{Path.GetExtension(LogFile)}";
myFileTarget.ArchiveFileName = archiveFileName;
myFileTarget.ArchiveNumbering = ArchiveNumberingMode.Rolling;
myFileTarget.ArchiveEvery = toFileArchivePeriod(LogStyle);
myFileTarget.EnableFileDelete = true;
myFileTarget.MaxArchiveFiles = TTL; //Time to Live
myFileTarget.DeleteOldFileOnStartup = true;
To simulate that a lot of logs already exists, I create a range of logs with the same structure as the ArchiveFileName above:
LogFileName = "LogTTLDailyTest.log";
LogStyle = "daily";
LogTTL = 5; //Time To Live
//Arrange old filelogs
for (int i = 0; i < 100; i++)
{
var filename = $"LogTTLDailyTest.{i}.log";
File.WriteAllText(TestLogsDirectory + filename, "UNITTEST");
var creationTime = DateTime.Now.AddDays((i + 1)* -1);
File.SetCreationTime(TestLogsDirectory + filename, creationTime);
File.SetLastWriteTime(TestLogsDirectory + filename, creationTime);
}
But when I write a log to nlog via my Log system it does not see the old log files I created and therefore do not delete them. It does however clean up the old current log file, so deleting files work:
NLog: 2017-07-20 12:54:20.7434 Info Closing old configuration.
NLog: 2017-07-20 12:54:20.7434 Info Found 36 configuration items
NLog: 2017-07-20 12:54:20.7594 Info Found 36 configuration items
NLog: 2017-07-20 12:54:26.9157 Info Deleting old archive file: 'C:\<projectpath>\bin\Debug\unittestlogsea984b05-3c33-4142-9d1a-c900bad89006\LogTTLDailyTest.log'.
My current theory is that the nlog sees the old logs but have some kind of validation process of the contents of the files which I only fill up with "UNITTEST" as content, but I haven't been able to "restart" nlog or force it to see the logs.
Hope you can help me
This is no issue here, just me not setting the test up right. What I forgot is that rolling does not delete, but merely rename all files to the next rolling number. The last file in this rolling manner is deleted. Any other file is ignored by nlog. I also forgot to set my current log file a day back. This meant that nlog saw I had a current file that was not a day old. But because i had DeleteOldFileOnStartup activated it deleted that file.
So, to fix my mistake I made sure I only create as many files as I needed or less and made sure the latest file did not have a rolling number and was a day old. I also removed the DeleteOldFileOnStartup option. The Nlog is now doing what I expect it to do flawlessly.
I have about 5-6 Server Manager programs that write their own configuration file out to a particualr folder, such as C:\ACME. The config files all end with a *ServerConfig.cfg" where * = Program name that created it.
I have a Windows service that has a FileSystemWatcher setup that I want to FTP the configuration files each time the program updates. I've gotten everything to work, but I'm noticing that the different Server Manager programs are behaving differently.
When saving a configuration file, the FileSystemWatcher is picking up two "change" events. This is causing my program to FTP the configuration file twice where I only need it once.
In other instances I'm seeing where it may create 4, 5, or 6 "change" events when saving a configuration file.
What is the best way to handle processing/FTPing these files when they are really done saving only one time.
I really dont want o set something up to poll the directory for filechanges every so often... and like the idea that each time a configuration is saved, I get a duplicate copy along with a date/timestamp appended to the filename copied elsewhere.
I have seen lots of suggestions Googling around and even here on Stackoverflow, but nothing that seems to be all-in-one for me.
I suppose I could put the filename in a queue when a "change" event occurred if it didn't already exist in the queue. Not sure if this is the best approx.
Here is my sample code:
Startup-code:
private DateTime _lastTimeFileWatcherEventRaised = DateTime.Now;
_watcherCFGFiles = new FileSystemWatcher();
_watcherCFGFiles.Path = #"C:\ACME";
_watcherCFGFiles.IncludeSubdirectories = true;
_watcherCFGFiles.Filter = "*ServerConfig.cfg";
_watcherCFGFiles.NotifyFilter = NotifyFilters.Size;
//_watcherCFGFiles.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.FileName;
_watcherCFGFiles.Changed += new FileSystemEventHandler(LogFileSystemChanges);
_watcherCFGFiles.Created += new FileSystemEventHandler(LogFileSystemChanges);
_watcherCFGFiles.Deleted += new FileSystemEventHandler(LogFileSystemChanges);
_watcherCFGFiles.Renamed += new RenamedEventHandler(LogFileSystemRenaming);
_watcherCFGFiles.Error += new ErrorEventHandler(LogBufferError);
_watcherCFGFiles.EnableRaisingEvents = true;
Here is that actual handler for the "change" event. I'm skipping the first "change" event if the second is within 700ms. But this doesn't account for the files that make 3-4 change events...
void LogFileSystemChanges(object sender, FileSystemEventArgs e)
{
string log = string.Format("{0} | {1}", e.FullPath, e.ChangeType);
if( e.ChangeType == WatcherChangeTypes.Changed )
{
if(DateTime.Now.Subtract(_lastTimeFileWatcherEventRaised).TotalMilliseconds < 700)
{
return;
}
_lastTimeFileWatcherEventRaised = DateTime.Now;
LogEvent(log);
// Process file
FTPConfigFileUpdate(e.FullPath);
}
}
I had the exact same issue. I used a HashMap that mapped filenames to times of writes, I then used this as a lookup table for files to check and see if the changed event had been applied very quickly. I defined some epsilon (for me it was about 2 seconds to make sure events were flushed). If the time found in the map was older than that I would put it on a queue to be processed. Essentially all I had to do was keep the HashMap up to date with events and changes and this worked out (although you may want to change your epsilon value depending on your application).
Its normal this behavior because the antivirus system or other programs make more writes when a file change the content. I usually create a (global) HashTable and check if the filename exists, if don't, put the filename in it and start and an asynchronous operation to remove the filename after 3-5 seconds.
This is expected behavior - so you need to figure out how to handle it in your particular case.
The file system does not have a concept of "program done working with this file". I.e. one can write editor that updates (open/write/close) file on every keystroke. File system will report a lot of updates, but from the user point of view there is only one update when the editor is closed.
I have created a text file to save the error in that created file. I have a button which, once pressed, generates an error which should be saved to the file. But if I press the button twice, it will overwrite the first error generated, because the contents of the file are overwritten. I want to generate a another separate file to save the new error. A new file should be generated for each new error.
Thanks in advance
Simple use: FileExists Method and then if it exists pick a new name. Alternatively you could just append to the file.
PSUDO:
public string checkFileName(string fileName){
if(File.Exists(fileName)){
/Pick a new one
newFileName= fileName + DateTime.Now.ToLongDateString()
return checkFileName(newFileName)
}
return fileName
}
This could be the perfect link for you How to Open and Append a log file
You can add time stamp in filename, in this case you would get new file each time.
private void SaveErrorMessage(string errorMessage)
{
string errorFile = null;
for( int x = 0; x < Int32.MaxValue; ++x )
{
errorFile = string.Format(CultureInfo.InvariantCulture, "error-{0}.txt", x);
if( !System.IO.File.Exists(errorFileTest) )
{
break;
}
}
File.WriteAllText(errorFile, errorMessage);
}
This will overwrite the last file after you've had Int32.MaxValue files, but that'll take a while.
An alternative (and probably better) approach would be to simply append to the file, rather than creating a new one.
You may also want to consider using a more robust logging solution, such as log4net.
Creating file in C# is probably what you're looking for.
So you want to generate a unique file name for each error that occurs in your program? Probably the easiest way to accomplish this is to use the date/time when the error occured to name the file. In the function where you are writing to the file you will want to name the file like this:
string filename = LogPath + DateTime.Now.ToString("yyyyMMdd.HHmmss") + ".err";
Where LogPath is the path to the folder you want to write the error files to.
One of our applications allows users to generate an Excel file using an Excel Template (that is installed/deployed using Click Once). This is a VSTO application.
The Excel file is genereted by storing some values in the registry and "calling" the template using the following code (the coluymns are pulled from two different lists, a "required" list and an "optional list":
private void myButtonExport_Click(object sender, EventArgs e)
{
if (CurrentTableRow == null) return;
if (CurrentTemplateRow == null) return;
List<string> requiredColumns = excelTemplateTableDS.GetColumnsList(false);
List<string> myColumns = userTableDS.GetColumnsList(false);
string selectStatement;
try
{
CustomerListColumnSelect f = new CustomerListColumnSelect(myColumns, requiredColumns);
if (f.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
selectStatement = string.Format("SELECT {0} FROM [{1}]", string.Join(", ", f.SelectedColumns.ToArray()), CurrentTableRow.TableName);
Microsoft.Win32.RegistryKey rk = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(#"SOFTWARE\Company\Collection Development");
rk.DeleteValue("TemplateTableName", false);
rk.DeleteValue("TemplateSelectStatement", false);
rk.SetValue("TemplateTableName", CurrentTableRow.TableName, Microsoft.Win32.RegistryValueKind.String);
rk.SetValue("TemplateSelectStatement", selectStatement, Microsoft.Win32.RegistryValueKind.String);
rk.Close();
// Launch Excel
AppLog.WriteEvent("CreateCustomerList", TraceEventType.Information,
string.Format("Creating customer list [{0}] using table [{1}]", CurrentTemplateRow.TemplateName, CurrentTableRow.TableName));
if (Debugger.IsAttached)
{
FileInfo fi = new FileInfo(CurrentTemplateRow.Path);
string newPath = string.Format(#"C:\Documents and Settings\mpetrie\My Documents\Visual Studio 2010\Projects\Collection Development Templates\{0}\bin\Release\{1}", CurrentTemplateRow.TemplateName, fi.Name);
// If running under DEBUG (and app not installed), then launch latest version
Process.Start(newPath);
}
else
{
Process.Start(CurrentTemplateRow.Path);
}
}
f.Dispose();
}
catch (Exception ex)
{
MessageBox.Show(this, CurrentTemplateRow.Path + "\r\n" + ex.Message, "Process.Start", MessageBoxButtons.OK, MessageBoxIcon.Error);
ResetSteps(sender, e);
return;
}
}
Then, each template has "a bunch" of code to load the data (using the values in the Registry) into the spreradsheet. (This part never has an issue, it is AFTER the excel file has been saved and someone tries to open it).
I am new to this application and VSTO in general. What I am a little unclear on is how the file gets saved. I assume when it gets saved, all of the associated "code" from the template does NOT get saved with the file, only the data gets saved...
Ocassionally, our users are unable to open the file. Excel never gives an error, it just "spins" like it is trying to open the file. We have waited up to 30 minutes or longer with no results. It "seems" like the file gets opened, but never "painted" (rendered), but that is just a guess. This is very random. Sometimes, the same file can be opened by other users and sometimes the user that had problems can try again later and the file opens just fine.
We have several "ideas" what could be causing the problem from: AntiVirus to Networking shares. Although, it is such a random issue we don't know how to debug this when it happens (it may not happen again for hours or days). The users report they don't have issues opening any other Excel files, only the files generated by this application.
Does anyone have any ideas what could be causing this??
Shayne
My description of this problem was completely wrong. After seeing the problem first hand today. The issue is the file opens, but isn't visible. I am going to start a new thread that better describes the problem.