I have developed an application that loaded many images in a listview using ImageList in c# .net framework 4. The images are also compressed. When many many images are loaded and compressed then it takes a long time. So I call the method in backgroundworker. In the backgroundworker I had to add images to ImageList and add ImageList to ListView. So I have used safeinvoke() method listView1.SafeInvoke(d=>d.Items.Add(item)).
Everything works fine. Images are displayed one by one in the listview.
But the release of the application doesn’t work properly in some pc and properly works in some other pc. Doesn’t work properly means, If 100 images are browsed using OpenFileDialog to load then some images are loaded and added to listview and then the loading is automatically stopped without adding all images to the listview and no exception shows.
I have spent many times to solve this problem but couldn’t figure out the problem. . Where is the problem? Can anybody help me?
private void bgwLoading_DoWork(object sender, DoWorkEventArgs e)
{
ArrayList a = (ArrayList)e.Argument;
string[] fileNames = (string[])a[0];
this.loadMultiImages(fileNames);
}
private void loadMultiImages(string[] fileNames)
{
int i = 1;
int totalFiles = fileNames.Count();
foreach (string flName in fileNames)
{
if (!flName.Contains("Thumbs.db"))
{
Bitmap newBtmap = (Bitmap)Image.FromFile(flName);
FileInfo fi = new FileInfo(flName);
long l = fi.Length;
if (l > compressSize)
{
newBtmap = resizeImage(newBtmap, 1024,768) ;
newBtmap = saveJpeg(IMAGE_PATH + (SCANNING_NUMBER +
) + ".jpg", newBtmap, IMAGE_QUALITY);
}
else
{
File.Copy(flName, TEMP_IMAGE_PATH + (SCANNING_NUMBER + 1) + ".jpg");
}
if (!bgwLoading.CancellationPending)
{
CommonInformation.SCANNING_NUMBER++;
this.SafeInvoke(d => d.addItemToLvImageContainer(newBtmap));
bgwLoading.ReportProgress((int)Math.Round((double)i / (double)
(totalFiles) * 100));
i++;
}
}
}
}
}
public void addItemToLvImageContainer(Bitmap newBtmap)
{
imageList.Images.Add(newBtmap);
ListViewItem item;
item = new ListViewItem();
item.ImageIndex = SCANNING_NUMBER - 1;
item.Text = SCANNING_NUMBER.ToString();
lvImageContainer.Items.Add(item);
lvImageContainer.Items[item.ImageIndex].Focused = true;
}
To find out the error I have modified the code as follows:
I have commented the two lines
//newBtmap = resizeImage(newBtmap, 1024, 768);
// newBtmap = saveJpeg(IMAGE_PATH + scanning_number + ".jpg", newBtmap, Image_Quality );
and added try-catch as follows:
try
{
Bitmap newBtmap = (Bitmap)Image.FromFile(flName);
File.Copy(flName, CommonInformation.TEMP_IMAGE_PATH +
(CommonInformation.SCANNING_NUMBER + 1) + ".jpg");
if (!bgwLoading.CancellationPending)
{
this.SafeInvoke(d => d.imageList.Images.Add(newBtmap));
ListViewItem item;
item = new ListViewItem();
CommonInformation.SCANNING_NUMBER++;
item.ImageIndex = CommonInformation.SCANNING_NUMBER - 1;
item.Text = CommonInformation.SCANNING_NUMBER.ToString();
this.SafeInvoke(d => d.lvImageContainer.Items.Add(item));
bgwLoading.ReportProgress((int)Math.Round((double)i /
(double)(totalFiles) * 100));
this.safeInvoke(d=>d.addItemImageContainer(newBtmap))
catch (Exception ex)
{
MessageBox.Show( ex.Message);
}
It shows the error message after loading some images as "OutOfMemoryException"
Most probably the following line creates the exception:
Bitmap newBtmap = (Bitmap)Image.FromFile(flName);
But the image files are not corrupted and their file extension is .JPG.
How to get rid of this problem?
I have no answer, but i have some suggestions:
Check .NET framework version on computers with problems
Check, if you have permissions for files you trying to read
Use "try-catch" when you accessing files
And questions:
Is this project written in older version of .NET and migrated/upgraded to .NET 4.0?
Are you using any non-built-in assemblies or external dll's for image processing?
Related
Windows 8.1 Pro, Visual Studio 2015 Update 3, C#, .NET Framework 4.5. Ghostscript.NET (latest), GhostScript 9.20.
I'm converting a PDF to a PDF. Hah. Well, I'm making an "editable" PDF "hard" PDF that can't be edited and is of lower quality. The process is I take the editable PDF, save it out as x-pages of PNG files, convert those PNG files to a multipage TIFF, and then convert the multipage TIFF to the PDF I need.
This worked just fine with Visual Studio 2012, one version earlier of GhostScript .NET and GS 9.10.
public static Tuple<string, List<string>> CreatePNGFromPDF(string inputFile, string outputfile)
{
Tuple<string, List<string>> t = null;
List<string> fileList = new List<string>();
string message = "Success";
string outputFileName = string.Empty;
int desired_x_dpi = 96;
int desired_y_dpi = 96;
try
{
using (GhostscriptViewer gsViewer = new GhostscriptViewer())
{
gsViewer.Open(inputFile);
using (GhostscriptRasterizer rasterizer = new GhostscriptRasterizer(gsViewer))
{
for (int pageNumber = 1; pageNumber <= rasterizer.PageCount; pageNumber++)
{
using (System.Drawing.Image img = rasterizer.GetPage(desired_x_dpi, desired_y_dpi, pageNumber))
{
outputFileName = outputfile.Replace(".png", string.Empty) + "_page_" + pageNumber.ToString() + ".png";
img.Save(outputFileName, ImageFormat.Png);
if (!fileList.Contains(outputFileName))
{
fileList.Add(outputFileName);
}
}
}
}
}
}
catch (Exception ex)
{
message = ex.Message;
}
t = new Tuple<string, List<string>>(message, fileList);
return t;
}
This now fails on this line:
using (System.Drawing.Image img = rasterizer.GetPage(desired_x_dpi, desired_y_dpi, pageNumber))
when processing the second page. The first page works okay.
I downloaded the source for GhostScript.NET, added it to my solution, debugged, etc., and spent a good long while trying to figure this out.
I then decided to separate out the functionality and make the bare minimum available for me to examine further in a simple Console application:
static void Main(string[] args)
{
int xDpi = 96;
int yDpi = 96;
string pdfFile = #"Inputfilenamehere.pdf";
GhostscriptVersionInfo gsVersionInfo = GhostscriptVersionInfo.GetLastInstalledVersion(GhostscriptLicense.GPL | GhostscriptLicense.AFPL, GhostscriptLicense.GPL);
List<GhostscriptVersionInfo> gsVersionInfoList = GhostscriptVersionInfo.GetInstalledVersions(GhostscriptLicense.GPL | GhostscriptLicense.AFPL);
try
{
using (GhostscriptViewer gsViewer = new GhostscriptViewer())
{
gsViewer.Open(pdfFile);
using (GhostscriptRasterizer gsRasterizer = new GhostscriptRasterizer(gsViewer))
{
int pageCount = gsRasterizer.PageCount;
for (int i = 0; i < pageCount; i++)
{
Image img = gsRasterizer.GetPage(xDpi, yDpi, i + 1);
}
}
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Lo and behold, no problems. The difference is that I'm not putting declaration of my Image in the using statement.
I always try to be a good boy developer and use a using statement whenever the class implements IDisposable.
So, I removed the use of the using and I get the lower-quality PDF's that I've always desired. My life is good now.
using (GhostscriptViewer gsViewer = new GhostscriptViewer())
{
gsViewer.Open(inputFile);
using (GhostscriptRasterizer rasterizer = new GhostscriptRasterizer(gsViewer))
{
for (int pageNumber = 1; pageNumber <= rasterizer.PageCount; pageNumber++)
{
System.Drawing.Image img = rasterizer.GetPage(desired_x_dpi, desired_y_dpi, pageNumber);
outputFileName = outputfile.Replace(".png", string.Empty) + "_page_" + pageNumber.ToString() + ".png";
img.Save(outputFileName, ImageFormat.Png);
if (!fileList.Contains(outputFileName))
{
fileList.Add(outputFileName);
}
}
}
}
Note that if I call img.Dispose() at the end of the for loop, I get the same error again!
My best guess is that my issue is not a GhostScript or GhostScript.NET issue. Am I being a bonehead for insisting on blindly using "using" statements if the class implements IDisposable? I've always understood that it's best practice to wrap anything that implements IDisposable with a using statement to forgo leaks, etc.
Hence, my question: Any ideas why I get the "Parameter is invalid" exception when I initialize the System.Drawing.Image class within the using statement but not when I don't? I'd love to understand this more.
Better yet, if anyone knows how I can get this functionality and also ensure I'm properly disposing my object, that would be the best.
I didn't find much about this particular topic when I searched for information. I did find one other StackOverflow post about someone using a graphic object in a using statement with the same error. I wonder if there is a relationship. I also note that I should be using Dispose(), but that appears to be causing the problem, and I need this to work.
FYI, for anyone interested, the actual error occurs here in GhostscriptInterprester.cs in the GhostScript.NET code:
Method: public void Run(string str)
str is "Page pdfshowpage_init pdfshowpage_finish"
// GSAPI: run the string
int rc_run = _gs.gsapi_run_string(_gs_instance, str, 0, out exit_code);
I found the root cause of my failure at least. My GhostscriptRasterizer object had a value of '0' set for the height points and width points.
var rasterizer = new GhostscriptRasterizer();
rasterizer.CustomSwitches.Add("-dDEVICEWIDTHPOINTS=" + widthPoints);
rasterizer.CustomSwitches.Add("-dDEVICEHEIGHTPOINTS=" + heightPoints);
Once I set both height and width to a valid non-zero value, the issue got fixed.
I'm working on an app that uses Bing's API to search and download images.
Bing's API provides a set of image links and I iterate over them and download each one.
The problem that I'm having is that sometimes the downloaded file size is 0Kb.
I assume that happens because WebClient first creates the filename and then tries to write to it. So when it can't write to it for some reason this happens. The problem is that it happens without throwing an exception so my 'Catch' statement can't catch this and delete the file.
public void imageFetcher(string performerName, int maxNumberOfImages, RichTextBox richTextBox)
{
string performersDirPath = Environment.CurrentDirectory + #"\Performers\";
string performerPath = performersDirPath + performerName + #"\";
if (!Directory.Exists(performersDirPath))
{
Directory.CreateDirectory(performersDirPath);
}
if (!Directory.Exists(performerPath))
{
Directory.CreateDirectory(performerPath);
}
// Searching for Images using bing api
IEnumerable<Bing.ImageResult> bingSearch = bingImageSearch(performerName);
int i = 0;
foreach (var result in bingSearch)
{
downloadImage(result.MediaUrl, performerPath + performerName + i + ".jpg",richTextBox);
i++;
if (i == maxNumberOfImages)
{
break;
}
}
}
The download method:
public void downloadImage(string imgUrl, string saveDestination, RichTextBox richTextBox)
{
if (File.Exists(saveDestination))
{
richTextBox.ForeColor = System.Drawing.Color.Red;
richTextBox.AppendText("The File: " + saveDestination + "Already exists");
}
else
{
try
{
using (WebClient client = new WebClient())
{
client.DownloadFileCompleted += new AsyncCompletedEventHandler(((sender, e) => downloadFinished(sender, e, saveDestination , richTextBox)));
Uri imgURI = new Uri(imgUrl, UriKind.Absolute);
client.DownloadFileAsync(imgURI, saveDestination);
}
}
catch (Exception e)
{
richTextBox.AppendText("There was an exception downloading the file" + imgUrl);
richTextBox.AppendText("Deleteing" + saveDestination);
File.Delete(saveDestination);
richTextBox.AppendText("File deleted!");
}
}
}
This happens also when I try to wait for the client to finish using:
client.DownloadFileAsync(imgURI, saveDestination);
while (client.IsBusy)
{
}
Can anyone please tell me what I'm doing wrong?
In other simular question the solution was to keep the Webclient instance open until download is finished.. I'm doing this with this loop:
while (client.IsBusy){}
Yet the results are the same.
Update:
I resorted to not use webclient, instead I used this code:
try
{
byte[] lnBuffer;
byte[] lnFile;
using (BinaryReader lxBR = new BinaryReader(stream))
{
using (MemoryStream lxMS = new MemoryStream())
{
lnBuffer = lxBR.ReadBytes(1024);
while (lnBuffer.Length > 0)
{
lxMS.Write(lnBuffer, 0, lnBuffer.Length);
lnBuffer = lxBR.ReadBytes(1024);
}
lnFile = new byte[(int)lxMS.Length];
lxMS.Position = 0;
lxMS.Read(lnFile, 0, lnFile.Length);
}
using (System.IO.FileStream lxFS = new FileStream(saveDestination, FileMode.Create))
{
lxFS.Write(lnFile, 0, lnFile.Length);
}
This solves the problem almost complelty, there are still one or two 0KB files but I assume it's because of network errors.
To see possible exceptions - try changing DownloadFileAsync to just DownloadFile - my problem was "Can not create SSL/TLS secure channel". Hope this will help someone.
I am trying to load pictures that are in a certain folder (camera) into my application using a listview and pictureList. For some reason the files are loaded but do not appear in the listview.
This is the code I have so far:
try
{
listView1.View = View.LargeIcon;
imageList1.ImageSize = new Size(32, 32);
listView1.LargeImageList = imageList1;
DirectoryInfo directory = new DirectoryInfo(#"C:\");
FileInfo[] Archives = directory.GetFiles("*.JPG");
foreach (FileInfo fileinfo in Archives)
{
imageList1.Images.Add(Image.FromFile(fileinfo.FullName));
}
listView1.Update();
MessageBox.Show("I found " + imageList1.Images.Count.ToString() + " images!");
}
catch
{
MessageBox.Show("Something went wrong!");
}
Note that the messagebox is showing me the correct number of files, so I suppose I have some part right. Any clues what might be wrong?
I have a block of code which reads powerpoint slides and creates xml for them.Everything is working fine on my local machine.but on server,when second slide is read.I Get the exception:
EXCEPTION:The message filter indicated that the application is busy. (Exception from HRESULT: 0x8001010A (RPC_E_SERVERCALL_RETRYLATER)) for Powerpoint Interop
Function Throwing Error:
public string AddPPTPages(long templateid, long pptFileId)
{
string strPptFilePath = "";
string strSuccess = "";
using (var dc = new DataContext())
{
var template = dc.Templates.GetByID(templateid);
template.ExtendedData = "<template><pptfileid>" + pptFileId + "</pptfileid></template>";
template.Save();
dc.SubmitChanges();
var file = dc.FileHandles.GetByID(Convert.ToInt64(pptFileId));
file.EnsureUrlFiles();
strPptFilePath = file.GetPhysicalPath(file.FileName);//get path of original ppt file
}
try
{
using (new Impersonator(Installs.Current.PPTUser, null, Installs.Current.PPTPassword))
{
PowerPoint.Application PowerPoint_App = new PowerPoint.Application();//Open PowerPoint app/process
PowerPoint.Presentation presentation = null;//initialize presentation to null
try
{
PowerPoint_App.Visible = MsoTriState.msoTrue;//set app visibility to true
presentation = PowerPoint_App.Presentations.Open(strPptFilePath, Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoTrue);//open powerpoint presentation using path strPptFilePath
templateID = templateid;//required for readslide function
/////////ERROR is THROWN FOR BELOW LINE//////////////////
for (int i = 0; i < presentation.Slides.Count; i++)
{
ReadSlides(presentation, i);//call to read current slide
}
using (var dc = new DataContext())
{
var template = dc.Templates.GetByID(templateID);
template.FixPageIndexes();
template.Save();
dc.SubmitChanges();
}
presentation.Close();//close presentation
PowerPoint_App.Quit();//quit opened powerpoint app/process
}
catch (Exception ex)
{
strSuccess = ex.ToString();
}
finally
{
while (Marshal.FinalReleaseComObject(presentation) != 0) { }
presentation = null;
while (Marshal.FinalReleaseComObject(PowerPoint_App) != 0) { }
PowerPoint_App = null;
GC.Collect();
GC.WaitForPendingFinalizers();
KillPPTProcess();//find ppt process in taskmanager and kill it
}
}
}
catch (Exception e)
{
strSuccess = e.ToString();
MindMatrix.Libraries.Entities.ExceptionMessage.HandleException(e, null);
Loggers.HandleException2(e);
}
return strSuccess;
}
private void ReadSlides(PowerPoint.Presentation presentation, int i)
{
try
{
string strPptXml = "";
//get number of objects(text and image) present in current slide
foreach (var item in presentation.Slides[i + 1].Shapes)
{
var shape = (PowerPoint.Shape)item;
strPptXml += ReadShape(shape);//read object and add it to xml
}
int height = ConvertToPixel(presentation.Slides[i + 1].Master.Height);//get height of current slide
int width = ConvertToPixel(presentation.Slides[i + 1].Master.Width);//get width of current slide
strFileImage = Installs.Current.GetTempDirectory(DirectoryType.PPT);//get the temporary folder path for current loggedin user in machine
if (System.IO.Directory.Exists(strFileImage) == false)
{
System.IO.Directory.CreateDirectory(strFileImage);
}
strFileImage = strFileImage + "\\" + (i + 1) + ".png";//create image path for slide snapshot
presentation.Slides[i + 1].Export(strFileImage, "png", width, height);//create snapshot as png image to temp folder
strPptXml = "<slides datasourceid='0' repeaterid = '0' id='" + presentation.Slides[i + 1].SlideID + "' >" + strPptXml + "</slides>";//create slide xml using slideid and ppt xml(contains text and image objects of slide)
MemoryStream ms = new MemoryStream();
System.Drawing.Image imageIn;
imageIn = System.Drawing.Image.FromFile(strFileImage);//Creates an Image from location strFileImage.
imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
using (var dc = new DataContext())
{
var template = dc.Templates.GetByID(templateID);
//template.createPptPage(strPptXml, height, width, ms);//call to create ppt page for current slide
template.createPptPage(RemoveTroublesomeCharacters(strPptXml), height, width, ms);//call to create ppt page for current slide
dc.SubmitChanges();
}
}
catch (Exception e)
{
Loggers.HandleException2(e);
}
}
Any help guys??
My guess is that ReadSlides is changing the value of presentation.Slides.Count. This would happen if you were adding slides to, or removing slides from your presentation within ReadSlides.
I would pull this out into it's own variable and then use this variable in your for loop, like so:
var slideCount = presentation.Slides.Count;
for (int i = 0; i < slideCount; i++)
{
//etc etc
Have you tried setting DisplayAlert to false?
PowerPoint_App.DisplayAlerts = Powerpoint.PpAlertLevel.ppAlertsNone
You normally get this exception when Office opens a dialogue, and your application is unable to continue.
Quoting from your question:
Everything is working fine on my local machine.but on server,when second slide is read.I Get the exception
If your app is truly running in an unattended server environment, be aware that Microsoft specifically does not support COM for Office. Their warnings are fairly explicit. Here's a snippet:
If you use an Office application from a server-side solution, the application will lack many of the necessary capabilities to run successfully. Additionally, you will be taking risks with the stability of your overall solution.
My C# winforms 4.0 application has been using a thread-safe streamwriter to do internal, debug logging information. When my app opens, it deletes the file, and recreates it. When the app closes, it saves the file.
What I'd like to do is modify my application so that it does appending instead of replacing. This is a simple fix.
However, here's my question:
I'd like to keep my log file AROUND 10 megabytes maximum. My constraint would be simple. When you go to close the file, if the file is greater than 10 megabytes, trim out the first 10%.
Is there a 'better' way then doing the following:
Close the file
Check if the file is > 10 meg
If so, open the file
Parse the entire thing
Cull the first 10%
Write the file back out
Close
Edit: well, I ended up rolling my own (shown following) the suggestion to move overt to Log4Net is a good one, but the time it woudl take to learn the new library and move all my log statements (thousands) over isn't time effective for the small enhancement I was trying to make.
private static void PerformFileTrim(string filename)
{
var FileSize = Convert.ToDecimal((new System.IO.FileInfo(filename)).Length);
if (FileSize > 5000000)
{
var file = File.ReadAllLines(filename).ToList();
var AmountToCull = (int)(file.Count * 0.33);
var trimmed = file.Skip(AmountToCull).ToList();
File.WriteAllLines(filename, trimmed);
}
}
I researched this once and never came up with anything, but I can offer you plan B here:
I use the selection below to keep a maximum of 3 log files. At first, log file 1 is created and appended to. When it exceeds maxsize, log 2 and later log 3 are created. When log 3 is too large, log 1 is deleted and the remaining logs get pushed down the stack.
string[] logFileList = Directory.GetFiles(Path.GetTempPath(), "add_all_*.log", SearchOption.TopDirectoryOnly);
if (logFileList.Count() > 1)
{
Array.Sort(logFileList, 0, logFileList.Count());
}
if (logFileList.Any())
{
string currFilePath = logFileList.Last();
string[] dotSplit = currFilePath.Split('.');
string lastChars = dotSplit[0].Substring(dotSplit[0].Length - 3);
ctr = Int32.Parse(lastChars);
FileInfo f = new FileInfo(currFilePath);
if (f.Length > MaxLogSize)
{
if (logFileList.Count() > MaxLogCount)
{
File.Delete(logFileList[0]);
for (int i = 1; i < MaxLogCount + 1; i++)
{
Debug.WriteLine(string.Format("moving: {0} {1}", logFileList[i], logFileList[i - 1]));
File.Move(logFileList[i], logFileList[i - 1]); // push older log files back, in order to pop new log on top
}
}
else
{
ctr++;
}
}
}
The solutions here did not really work for me. I took user3902302's answer, which again was based on bigtech's answer and wrote a complete class. Also, I am NOT using StreamWriter, you can change the one line (AppendAllText against the StreamWrite aequivalent).
There is little error handling (e. g. re-try access when it is failing, though the lock should catch all internal concurrent access).
This might be enough for some people who had to use a big solution like log4net or nlog before. (And log4net RollingAppender is not even thread-safe, this one is. :) )
public class RollingLogger
{
readonly static string LOG_FILE = #"c:\temp\logfile.log";
readonly static int MaxRolledLogCount = 3;
readonly static int MaxLogSize = 1024; // 1 * 1024 * 1024; <- small value for testing that it works, you can try yourself, and then use a reasonable size, like 1M-10M
public static void LogMessage(string msg)
{
lock (LOG_FILE) // lock is optional, but.. should this ever be called by multiple threads, it is safer
{
RollLogFile(LOG_FILE);
File.AppendAllText(LOG_FILE, msg + Environment.NewLine, Encoding.UTF8);
}
}
private static void RollLogFile(string logFilePath)
{
try
{
var length = new FileInfo(logFilePath).Length;
if (length > MaxLogSize)
{
var path = Path.GetDirectoryName(logFilePath);
var wildLogName = Path.GetFileNameWithoutExtension(logFilePath) + "*" + Path.GetExtension(logFilePath);
var bareLogFilePath = Path.Combine(path, Path.GetFileNameWithoutExtension(logFilePath));
string[] logFileList = Directory.GetFiles(path, wildLogName, SearchOption.TopDirectoryOnly);
if (logFileList.Length > 0)
{
// only take files like logfilename.log and logfilename.0.log, so there also can be a maximum of 10 additional rolled files (0..9)
var rolledLogFileList = logFileList.Where(fileName => fileName.Length == (logFilePath.Length + 2)).ToArray();
Array.Sort(rolledLogFileList, 0, rolledLogFileList.Length);
if (rolledLogFileList.Length >= MaxRolledLogCount)
{
File.Delete(rolledLogFileList[MaxRolledLogCount - 1]);
var list = rolledLogFileList.ToList();
list.RemoveAt(MaxRolledLogCount - 1);
rolledLogFileList = list.ToArray();
}
// move remaining rolled files
for (int i = rolledLogFileList.Length; i > 0; --i)
File.Move(rolledLogFileList[i - 1], bareLogFilePath + "." + i + Path.GetExtension(logFilePath));
var targetPath = bareLogFilePath + ".0" + Path.GetExtension(logFilePath);
// move original file
File.Move(logFilePath, targetPath);
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
}
edit:
Since I just noticed that you asked a slightly different question: should your lines vary greatly in size, this would be a variation (, that in 90% of cases does not improve over yours, though, and might be very slightly faster, also introduced a new unhandled error (\n not being present)):
private static void PerformFileTrim(string filename)
{
var fileSize = (new System.IO.FileInfo(filename)).Length;
if (fileSize > 5000000)
{
var text = File.ReadAllText(filename);
var amountToCull = (int)(text.Length * 0.33);
amountToCull = text.IndexOf('\n', amountToCull);
var trimmedText = text.Substring(amountToCull + 1);
File.WriteAllText(filename, trimmedText);
}
}
This is derived from bigtech's answer:
private static string RollLogFile()
{
string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string appName = Path.GetFileNameWithoutExtension(Environment.GetCommandLineArgs()[0]);
string wildLogName = string.Format("{0}*.log",appName);
int fileCounter = 0;
string[] logFileList = Directory.GetFiles(path, wildLogName, SearchOption.TopDirectoryOnly);
if (logFileList.Length > 0)
{
Array.Sort(logFileList, 0, logFileList.Length);
fileCounter = logFileList.Length - 1;
//Make sure we apply the MaxLogCount (but only once to reduce the delay)
if (logFileList.Length > MaxLogCount)
{
//Too many files - remove one and rename the others
File.Delete(logFileList[0]);
for (int i = 1; i < logFileList.Length; i++)
{
File.Move(logFileList[i], logFileList[i - 1]);
}
--fileCounter;
}
string currFilePath = logFileList[fileCounter];
FileInfo f = new FileInfo(currFilePath);
if (f.Length < MaxLogSize)
{
//still room in the current file
return currFilePath;
}
else
{
//need another filename
++fileCounter;
}
}
return string.Format("{0}{1}{2}{3:00}.log", path, Path.DirectorySeparatorChar, appName, fileCounter);
}
Usage:
string logFileName = RollLogFile();
using (StreamWriter sw = new StreamWriter(logFileName, true))
{
sw.AutoFlush = true;
sw.WriteLine(string.Format("{0:u} {1}", DateTime.Now, message));
}
This function will allow you to rotate your log based on weekdays. First time y our application will launch on Monday, will check for any existing entry for Monday Date, if not already initialized for today will discard old entries and reinitialize new file. Onwards for whole of that day, file will keep appending the text to same log file.
So, total 7 log files will be created.
debug-Mon.txt, debog-Tue.txt...
it will also add the method name which actually logged the message along with date time. very useful for general purpose use.
private void log(string text)
{
string dd = DateTime.Now.ToString("yyyy-MM-dd");
string mm = DateTime.Now.ToString("ddd");
if (File.Exists("debug-" + mm + ".txt"))
{
String contents = File.ReadAllText("debug-" + mm + ".txt");
if (!contents.Contains("Date: " + dd))
{
File.Delete("debug-" + mm + ".txt");
}
}
File.AppendAllText("debug-" + mm + ".txt", "\r\nDate: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:s") + " =>\t" + new System.Diagnostics.StackFrame(1, true).GetMethod().Name + "\t" + text);
}
I liked greggorob64's solution but also wanted to zip the old file. This has everything you need other than the part of compressing the old file to a zip, which you can find here: Create zip file in memory from bytes (text with arbitrary encoding)
static int iMaxLogLength = 2000; // Probably should be bigger, say 200,000
static int KeepLines = 5; // minimum of how much of the old log to leave
public static void ManageLogs(string strFileName)
{
try
{
FileInfo fi = new FileInfo(strFileName);
if (fi.Length > iMaxLogLength) // if the log file length is already too long
{
int TotalLines = 0;
var file = File.ReadAllLines(strFileName);
var LineArray = file.ToList();
var AmountToCull = (int)(LineArray.Count - KeepLines);
var trimmed = LineArray.Skip(AmountToCull).ToList();
File.WriteAllLines(strFileName, trimmed);
string archiveName = strFileName + "-" + DateTime.Now.ToString("MM-dd-yyyy") + ".zip";
File.WriteAllBytes(archiveName, Compression.Zip(string.Join("\n", file)));
}
}
catch (Exception ex)
{
Console.WriteLine("Failed to write to logfile : " + ex.Message);
}
}
I have this as part of the initialization / reinitialization section of my application, so it gets run a few times a day.
ErrorLogging.ManageLogs("Application.log");
I was looking through the win32 api, and I'm not even sure it's possible to do this with native win32 vfs calls, nevermind through .Net.
About the only solution I would have would be to use memory-mapped files and move the data manually, which .Net seems to support as of .Net 4.0.
Memory Mapped Files