Best Way to implement Email generation in this scheduled console app - c#

I have never programmatically generated and sent email notifications before. What I need to do is this, if this program fails to move files or encounters an exception to generate an email and (preferably) attach a Log.txt file to the email then send it. This app will be ran from an enterprise scheduler and ran 1/hour to manage the files and folders.
Here is the functional code that I currently have (no email implementation exists yet) I have commented in the locations that I would like to send an email
using System;
using System.Configuration;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Runtime.InteropServices;
namespace FileOrganizer
{
class Program
{
//These are the folder to organize and delimeter that is in the filenames to split off a folder from
static string folder = ConfigurationManager.AppSettings["folder"]; //"C:\Users\MyUserName\Desktop\Organize Me"
static string delim = ConfigurationManager.AppSettings["delim"]; // delim = "__"
//location for the log.txt file
static string logPath = ConfigurationManager.AppSettings["logPath"]; //C:\Users\MyUserName\Desktop\Log.txt
//contact list from the app.config
static string emailTo = ConfigurationManager.AppSettings["notifyEmailAddress"]; //multiple email addresses delimited by a ; example = tom#domain.com;dick#differentDomain.com;harry#domain.com
//for the log.txt reporting
static int filesMoved = 0;
static int filesNOTmoved = 0;
static string fileNamesNOTmoved = "";
static int foldersCreated = 0;
static string message = "";
static string stackTrace = "";
//used for passing all the objects into the log method
static object[] details = new object[6];
static void Main(string[] args)
{
try
{
using (StreamWriter w = File.AppendText(logPath))
{
int total = organizeFolder(delim, folder);
details[0] = filesMoved;
details[1] = filesNOTmoved;
details[2] = fileNamesNOTmoved;
details[3] = foldersCreated;
details[4] = "No Errors Found";
details[5] = "No Errors Found";
Log(details, w);
if (filesNOTmoved > Convert.ToInt32(filesNOTmoved))
{
//generate email to notify of filesNames not moved (prefer to send Log.txt as attachment)
}
}
using (StreamReader r = File.OpenText(logPath))
{
DumpLog(r);
}
}
catch (Exception ex)
{
using (StreamWriter w = File.AppendText(logPath))
{
details[0] = filesMoved;
details[1] = filesNOTmoved;
details[2] = fileNamesNOTmoved;
details[3] = foldersCreated;
details[4] = ex.Message.ToString();
details[5] = ex.StackTrace.ToString();
Log(details, w);
}
//generate email to notify of Error and (prefer to send Log.txt as attachment)
using (StreamReader r = File.OpenText(logPath))
{
DumpLog(r);
}
}
}
/*Takes a folder and organizes it into folder by naming folder whatever
* is in front of the delimeter Example -->(folderName__fileName.txt would be moved
* to a new or existing folder named folderName. Final path for file would
* look like this folderName\folderName__fileName.txt)
*
* It will not move file if it already exists in the new location, instead
* it prompts user that they should rename file and run program again.*/
static int organizeFolder(string delimeter, string rootPath)
{
//counter for total files in Folder
int Count = 0;
int totalMoved = 0;
FileInfo[] fileNames;
DirectoryInfo di = new DirectoryInfo(rootPath);
//set all file names as a string from network directory into an array
fileNames = di.GetFiles("*.*");
Count = fileNames.Length;
//Count = 0;//just to test exception handling
//if no files exist in network folder throw error message.
if (Count == 0)
{
fileNamesNOTmoved = "No Files Were In Directory to Move.";
}
else //files exist in network folder
{
int delimIndex = 0;
string folderName;
for (int i = 0; i < Count; i++)
{
if (fileNames[i].ToString().Contains(delimeter))
{
delimIndex = fileNames[i].ToString().IndexOf(delimeter);
folderName = fileNames[i].ToString().Substring(0, delimIndex);
//if folder doesnt exist create it here.
folderCreation(rootPath, folderName);
if (!File.Exists(rootPath + #"\" + folderName + #"\" + fileNames[i].Name))
{
File.Move(rootPath + #"\" + fileNames[i].ToString(), rootPath + #"\" + folderName + #"\" + fileNames[i].Name);
filesMoved++;
}
else
{
if (fileNamesNOTmoved == "")
fileNamesNOTmoved += "| ";
fileNamesNOTmoved += fileNames[i].Name + " | ";
filesNOTmoved++;
}
totalMoved++;
}
}
}
return totalMoved;
}
//if folder does not exist this method will create it
private static void folderCreation(string strTempPath, string folderName)
{
strTempPath = strTempPath + #"\" + folderName;
if (!Directory.Exists(strTempPath))
{
//Create \folderName
Directory.CreateDirectory(strTempPath);
foldersCreated++;
}
}
//adds entries to the log.txt file
public static void Log(object[] details, TextWriter w)
{
w.Write("\r\nLog Entry : ");
w.WriteLine("{0} {1}", DateTime.Now.ToShortTimeString(), DateTime.Now.ToShortDateString());
w.WriteLine(" :");
w.WriteLine(" :{0}", "Files Moved = " + details[0].ToString());
w.WriteLine(" :{0}", "Folders Created = " + details[3].ToString());
w.WriteLine(" :{0}", "Files Not Moved = " + details[1].ToString());
w.WriteLine(" :{0}", "Files Names Not Moved = {" + details[2].ToString() + "}");
w.WriteLine(" :{0}", "Error Message = " + details[4].ToString());
w.WriteLine(" :{0}", "Error Stack Trace = " + details[5].ToString());
w.WriteLine("---------------------------------------------------------------------------------------");
}
public static void DumpLog(StreamReader r)
{
string line;
while ((line = r.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
}
}
Basically would like the email to say:
Subject: Data Management Error Notification
Body: This is an Auto-Generated email to notify you there
was an error attempting to clean up the Files in the Directory.
Please refer to Attached Log.txt File for more detailed information.
Attachment: Log.txt

As it turns out SMTP was the way to go and Due to restrictions on my local machine I could only access it from the Dev, Qua, & Prd Servers. Once tested as admin from any of those locations.. email sent perfectly!!!
using the App.Config to hold all my To's, Froms, and the SMTP Host info and passing HTML as a string object into the method, here is the method I used to send email.
public static void sendMail(string msg)
{
SmtpClient smtpClient = new SmtpClient();
MailMessage message = new MailMessage();
MailAddress fromAddress = new MailAddress(emailFrom);
smtpClient.Host = smtpHost;
message.From = fromAddress;
message.Subject = "Test Email";
message.IsBodyHtml = true;
message.Body = msg;
message.To.Add(emailTo);
message.Priority = MailPriority.High;
smtpClient.Send(message);
}

Related

My .NET Windows service will not start

I wrote this .NET Windows Service application that is basically a file watcher. The service will monitor incoming .csv files, parse data from them, and add the data to spreadsheets. I installed the service on a server and tried to start it. I was given the warning, "The service started and then stopped. Some services stop automatically if they are not in use by other services or programs." In a debug attempt, I removed all the code and just had a bare service and it started/stopped fine. So I added the file watcher object and it popped the warning again. Next I changed the service to run with a local administrative account instead of the "LocalService" account then it worked. I added the rest of my code and it worked for awhile. I finished development and added the EventLog object and I was right back to the warning. I removed the EventLog object but still got the warning. I just do not know what is causing this to not start. Here is my service:
using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using OfficeOpenXml;
using System.Linq;
using System.ServiceProcess;
using System.Text;
namespace COD_Automation
{
public partial class COD_AUTO : ServiceBase
{
FileSystemWatcher eWatcher;
String remoteSrc;
public static String filePath;
public static String fileName;
public static Boolean fileCheck;
public static String modifiedDT;
public static String remodifiedDT;
public static String SampNum;
public static String SampDate;
public static String AnalysisInitials;
public static int SampResult;
public static double Dilution;
public static FileInfo efile;
public int rowIndex = 8;
public int filterID = 1;
public COD_AUTO()
{
InitializeComponent();
if (!System.Diagnostics.EventLog.SourceExists("COD_Automation"))
{
System.Diagnostics.EventLog.CreateEventSource(
"COD_Automation", "COD Automation Log");
}
serviceLog.Source = "COD_Automation";
serviceLog.Log = "COD Automation Log";
}
protected override void OnStart(string[] args)
{
serviceLog.WriteEntry("COD Automation Service has started.");
//Define the remote folder location to watch
remoteSrc = "\\\\mkfiler01\\ops\\Envcom\\EXEC\\ENVCOM\\LAB\\COD\\Exports\\DataLog";
//Create a new FileSystemWatcher and set its properties
eWatcher = new FileSystemWatcher(remoteSrc, "*.csv");
//Add event handler
eWatcher.Created += new FileSystemEventHandler(eWatcher_Created);
//Begin watching
eWatcher.EnableRaisingEvents = true;
}
protected override void OnStop()
{
serviceLog.WriteEntry("COD Automation Service has stopped.");
eWatcher.EnableRaisingEvents = false;
}
private void eWatcher_Created(object source, FileSystemEventArgs e)
{
filePath = e.FullPath;
fileName = e.Name;
ParseData(filePath);
FileCheck(fileName);
CreateExcelFile(fileCheck);
AddSample(SampNum, SampDate, AnalysisInitials, SampResult, Dilution);
}
public void ParseData(String filePath)
{
//Create a dictionary collections with int keys (rowNums) and String values (each line of Strings)
Dictionary<int, String> eachCSVLine = new Dictionary<int, string>();
//String array that holds the contents of the specified row
String[] lineContent;
int rowNum = 1;
foreach (string line in File.ReadLines(filePath))
{
eachCSVLine.Add(rowNum, line);
rowNum++;
}
//Get the required line and split it by "," into an array
String reqLine = eachCSVLine[5];
lineContent = reqLine.Split(',');
//Get the required values(index 2 for parsed Operator ID, index 4 for parsed Sample Number, index 11 for Sample Result)
AnalysisInitials = lineContent.GetValue(2).ToString();
SampNum = lineContent.GetValue(3).ToString(); //sample number
String result = lineContent.GetValue(11).ToString();
String dilute = lineContent.GetValue(8).ToString();
Dilution = Double.Parse(dilute);
SampResult = Int32.Parse(result); //sample result
}
public void AddSample(String SampleNum, String SampleDate, String AnalysisInitials, int SampleResult, double Diluted)
{
try
{
using (ExcelPackage excelPackage = new ExcelPackage(efile))
{
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[1];
var cell = worksheet.Cells;
//check to see if this is the first sample added --if true, add the first sample --if false, increment rowindex & filterID then add the sample to next available row
if (cell["A8"].Value == null)
{
cell["B5"].Value = SampleDate;
cell["B6"].Value = AnalysisInitials;
cell[rowIndex, 1].Value = filterID; //Filter ID
cell[rowIndex, 2].Value = SampleNum; //Sample Number
cell[rowIndex, 3].Value = Dilution; //Dilution
cell[rowIndex, 4].Value = SampleResult; //Meter Reading
}
else
{
while (!(cell["A8"].Value == null))
{
rowIndex++;
filterID++;
if (cell[rowIndex, 1].Value == null) //ensures that the new row is blank so the loop can break to continue adding the sample
{ break; }
}
//add the sample to the next empty row
cell[rowIndex, 1].Value = filterID; //Filter ID
cell[rowIndex, 2].Value = SampleNum; //Sample Number
cell[rowIndex, 3].Value = Dilution; //Dilution
cell[rowIndex, 4].Value = SampleResult; //Meter Reading
}
excelPackage.Save();
}
}
catch (Exception e)
{
serviceLog.WriteEntry("Sample could not be added to the spreadsheet because of the following error: " + e.Message + ".");
}
}
public Boolean FileCheck(String fileName)
{
//Get the date of the .csv file
String[] fNames = fileName.Split('_');
String fDate = fNames.ElementAt(3);
DateTime dt = Convert.ToDateTime(fDate);
//format the file date into the proper format and convert to a string
modifiedDT = dt.ToString("MMddyy");
//modify the "modifiedDT to get the sample date to insert into spreadsheet
String mdate = modifiedDT.Insert(2, "/");
remodifiedDT = mdate.Insert(5, "/");
SampDate = remodifiedDT; //sample date
//assign an excel filename
String exFile = #"\\mkfiler01\ops\Envcom\EXEC\ENVCOM\LAB\COD\Imports\" + modifiedDT + "COD-" + AnalysisInitials + ".xlsx";
//check for file existence
if (File.Exists(exFile))
{ fileCheck = true; }
else
{ fileCheck = false; }
return fileCheck;
}
public void CreateExcelFile(Boolean fileCheck)
{
if (fileCheck)
{
efile = new FileInfo(#"\\mkfiler01\ops\Envcom\EXEC\ENVCOM\LAB\COD\Imports\" + modifiedDT + "COD-" + AnalysisInitials + ".xlsx");
using (ExcelPackage excelPackage = new ExcelPackage(efile))
{
//Read the existing file to see if the Analysis Initials match the AnalysisInitial variable value
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[1];
String initials = worksheet.Cells["B6"].Value.ToString();
//If initials = AnalysisIntials then assign the existing file the WB variable, else create a new file for the different AnalysisInitials
if (initials.Equals(AnalysisInitials))
{
excelPackage.Save();
}
else
{
try
{
//Excel COD Template to use to create new Excel spreadsheet
FileInfo template = new FileInfo(#"\\mkfiler01\ops\Envcom\EXEC\ENVCOM\LAB\COD\COD TEMPLATE.xlsx");
//The new Excel spreadsheet filename
FileInfo newFile = new FileInfo(#"\\mkfiler01\ops\Envcom\EXEC\ENVCOM\LAB\COD\Imports\" + modifiedDT + "COD-" + AnalysisInitials + ".xlsx");
// Using the template to create the newfile
using (ExcelPackage excelPackage1 = new ExcelPackage(newFile, template))
{
// save the new Excel spreadsheet
excelPackage1.Save();
}
}
catch (Exception ex)
{
serviceLog.WriteEntry("Excel file could not be created because " + ex.Message);
}
}
}
}
else
{
try
{
efile = new FileInfo(#"\\mkfiler01\ops\Envcom\EXEC\ENVCOM\LAB\COD\Imports\" + modifiedDT + "COD-" + AnalysisInitials + ".xlsx");
//Excel COD Template to use to create new Excel spreadsheet
FileInfo template = new FileInfo(#"\\mkfiler01\ops\Envcom\EXEC\ENVCOM\LAB\COD\COD TEMPLATE.xlsx");
//The new Excel spreadsheet filename
FileInfo newFile = new FileInfo(#"\\mkfiler01\ops\Envcom\EXEC\ENVCOM\LAB\COD\Imports\" + modifiedDT + "COD-" + AnalysisInitials + ".xlsx");
// Using the template to create the newfile
using (ExcelPackage excelPackage = new ExcelPackage(newFile, template))
{
// save the new Excel spreadsheet
excelPackage.Save();
}
}
catch (Exception ex)
{
serviceLog.WriteEntry("Excel file could not be created because " + ex.Message);
}
}
}
}
}
My EventViewer was showing details of an ArgumentException in regards to the Source and Log properties of the EventLog object. I changed the following code:
if (!System.Diagnostics.EventLog.SourceExists("COD_Automation"))
{
System.Diagnostics.EventLog.CreateEventSource(
"COD_Automation", "COD Automation Log");
}
serviceLog.Source = "COD_Automation";
serviceLog.Log = "COD Automation Log";
into the following code:
if (!EventLog.SourceExists("COD_Automation"))
{
EventLog.CreateEventSource("COD_Automation", "Application");
}
serviceLog.Source = "COD_Automation";
serviceLog.Log = "Application";
This fixed my problem. I was initially trying to register the COD_Automation source in the COD_Automation Log which doesn't exist. So I set the Log property to the correct log of "Application".
The LocalService account will not have access to a UNC share like \\mkfiler01. My guess is that you are getting an access denied error when trying to access that file share.
Set the service to run under a domain account that has access to that share.

Read credentials in text file for program c#?

This is my program, and it work correctly if i put username and password :
try
{
var url = #"https://mail.google.com/mail/feed/atom";
var User = username;
var Pasw = password;
var encoded = TextToBase64(User + ":" + Pasw);
var myweb = HttpWebRequest.Create(url) as HttpWebRequest;
myweb.Method = "POST";
myweb.ContentLength = 0;
myweb.Headers.Add("Authorization", "Basic " + encoded);
var response = myweb.GetResponse();
var stream = response.GetResponseStream();
textBox1.Text += ("Connection established with" + User + Pasw);
}
catch (Exception ex)
{
textBox1.Text += ("Error connection. Original error: " + ex.Message);
now i want read string of texfile, split them and read username and password like this format: username:password . There is my code at the moment:
Stream myStream = null;
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.InitialDirectory = "c:\\";
openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
openFileDialog1.FilterIndex = 2;
openFileDialog1.RestoreDirectory = true;
string file_name = "";
file_name = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + file_name;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
try
{
if ((myStream = openFileDialog1.OpenFile()) != null)
{
using (StringReader reader = new StringReader(file_name))
{
// Loop over the lines in the string.
int count = 0;
string line;
while ((line = reader.ReadLine()) != null)
{
string[] data = line.Split(':');
string username = data[0].Trim();
string password = data[1].Trim();
count++;
/* Console.WriteLine("Line {0}: {1}", count, line); */
}
reader.Close();
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
You open the file selected by the user, but then try to read from a variable file_name that is not the name of a file but the name of a well kwown folder. Perhaps you want this
try
{
if (openFileDialog1.FileName != string.Empty)
{
using (StreamReader reader = new StreamReader(openFileDialog1.FileName))
{
....
}
}
}
In this same code you use a StringReader, but instead you need a StreamReader to read from a file. StringReader takes the value passed in its constructor and return in the ReadLine call. Then you split the line at the colon but of course this is not the content of your file.
There are other problems in your code. For example, what do you do with the username and password loaded from the line? They are declared as local variables and not used anywhere, so at the next loop they are overwritten and lost.
So, a UserData class could be a possible answer
public class UserData
{
public string UserName {get; set;}
public string Password {get; set;}
}
and declare at the form global level an
List<UserData> data = new List<UserData>
and in your loop
public void button1_Click(object sender, EventArgs e)
{
try
{
if (openFileDialog1.FileName != string.Empty)
{
using (StreamReader reader = new StreamReader(openFileDialog1.FileName))
{
int count = 0;
string line;
while ((line = reader.ReadLine()) != null)
{
UserData d = new UserData();
string[] parts = line.Split(':');
d.UserName = parts[0].Trim();
d.Password = parts[1].Trim();
data.Add(d);
}
// At the loop end you could use the List<UserData> like a normal array
foreach(UserData ud in data)
{
Console.WriteLine("User=" + dd.UserName + " with password=" + dd.Password);
}
}
}
}
}
public void button2_Click(object sender, EventArgs e)
{
try
{
if(data.Count() == 0)
{
MessageBox.Show("Load user info first");
return;
}
var url = #"https://mail.google.com/mail/feed/atom";
var encoded = TextToBase64(data[0].UserName + ":" + data[0].Password);
.....
A warning note. Of course this is just demo code. Remember that in a real scenario saving passwords in clear text is a big security concern. The impact of this is relative to the context of your application but should not be downplayed. A better course of action is to store an hashing of the password values and apply the same hashing function when you need to compare password
You are creating StringReader from file_name varialbe, which is (according to your code)
string file_name = "";
file_name = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + file_name;
and points to nothere.
Also you have stream created for file being selected with open file dialog but you haven't use this stream.

How can I write a console application which can search Users Directory?

I asked guru but I still couldn't solve the problem that I have.
I want to write a console program searching certain files, like xls, doc or *pdf.
I wrote a code like this but when it comes to the say, Users Directory, it cates UnauthorizedAccessException.
How can I write a console application which can search Users Directory?
I set clickonce off and build it with manifest which requireAdministrator.
So, on Vista or 7, it runs as an administrator, with the elevation dialogue.
Here's the full code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication2
{
class Program
{
//
private const string FILE_NAME = "search.txt";
private const string SEARCH_WORDS1 = "*.doc";
private const string SEARCH_WORDS2 = "*.ppt";
private const string SEARCH_WORDS3 = "*.jtd";
private const string SEARCH_WORDS4 = "*.pdf";
private const string END_WORDS = "\r\nSearch is finished.\r\n";
//This funcion echoes the messages.
void FileCheck()
{
string echo_words = "\r\nNow starts searching these files!" + SEARCH_WORDS1 + " "
+ SEARCH_WORDS2 + " " + SEARCH_WORDS3 + " " + SEARCH_WORDS4 + " "
+ "!\r\n";
if (File.Exists(FILE_NAME))
{
Console.WriteLine("{0} is already exists. Replace it to the new one.", FILE_NAME);
Console.WriteLine(echo_words);
File.Delete(FILE_NAME);
using (StreamWriter sw = File.CreateText(FILE_NAME))
{
sw.WriteLine(FILE_NAME + " is already exists. Replace it to the new one.\r\n");
sw.WriteLine(echo_words);
sw.Close();
}
}
else
{
using (StreamWriter sw = File.CreateText(FILE_NAME))
{
Console.WriteLine(echo_words);
sw.WriteLine(echo_words);
sw.Close();
}
}
}
//This function write to a file that search is finished.
void EndMessage()
{
using (StreamWriter sw = File.AppendText(FILE_NAME))
{
Console.WriteLine(END_WORDS);
sw.WriteLine(END_WORDS);
sw.Close();
}
}
//This function searches files given and write to a file.
void DirSearch(string sDir, string SEARCH_WORDS, int row)
{
int i;
i = 0;
string DeviceError = "off";
try
{
foreach (var d in Directory.GetDirectories(sDir))
{
DirectoryInfo di = new DirectoryInfo(d);
if ((di.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) {
//ReparsePoint could not be serached
continue;
}
try
{
foreach (string file in Directory.GetFiles(d, SEARCH_WORDS, SearchOption.AllDirectories))
{
Console.WriteLine(file);
using (StreamWriter sw = File.AppendText(FILE_NAME))
{
sw.WriteLine(file);
sw.Close();
i++;
}
}
}
catch (UnauthorizedAccessException)
{
//Unauthorized
Console.WriteLine(d + " is not allowd to be read !!");
using (StreamWriter sw = File.AppendText(FILE_NAME))
{
sw.WriteLine(d + " is not allowd to be read");
sw.Close();
}
}
}
}
catch (IOException)
{
//Device is not ready
DeviceError = "on";
}
if (DeviceError == "off")
{
if (i > 0)
{
Console.WriteLine(i + "numbers " + SEARCH_WORDS + " Files were found!\r\n");
using (StreamWriter sw = File.AppendText(FILE_NAME))
{
sw.WriteLine(i + "numbers " + SEARCH_WORDS + " Files were found!\r\n");
sw.Close();
}
}
else
{
Console.WriteLine(SEARCH_WORDS + " Files were not found !\r\n");
using (StreamWriter sw = File.AppendText(FILE_NAME))
{
sw.WriteLine(SEARCH_WORDS + " Files were not found !\r\n");
sw.Close();
}
}
}
}
//Main
static void Main(string[] args)
{
Program x = new Program();
string[] drives = Environment.GetLogicalDrives();
int row = drives.GetLength(0);
string my_documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Console.WriteLine("Logical Drives are " + row + ".");
using (StreamWriter sw = File.AppendText(FILE_NAME))
{
sw.WriteLine("Logical Drives are " + row + ".");
sw.Close();
}
int i = 0;
x.FileCheck();
while (row > 0)
{
x.DirSearch(drives[i], SEARCH_WORDS1, row);
x.DirSearch(drives[i], SEARCH_WORDS2, row);
x.DirSearch(drives[i], SEARCH_WORDS3, row);
x.DirSearch(drives[i], SEARCH_WORDS4, row);
row--;
i++;
}
x.EndMessage();
}
}
}
The error you're getting is caused by the file system permissions. The only way around would be to grant the credentials you're using access to the specified folders, run the application as 'Administrator' or run the application as the specific user for each User's folder.

How to keep logs in C#?

This is a WinForm written in C#.
Lets say I'm generating a random named text file in my selected directory. When the button is clicked teh first time, i write the data contained in the textboxes into that text file. If the user wants to do the same thing with different data in the textboxes then the click on the button should write the new data into the text file without losing the old data. It's like keeping logs, is this possible?
My code is like:
private readonly Random setere = new Random();
private const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private string RandomString()
{
char[] buffer = new char[5];
for (int i = 0; i < 5; i++)
{
buffer[i] = chars[setere.Next(chars.Length)];
}
return new string(buffer);
}
private void button1_Click(object sender, EventArgs e)
{
DialogResult dia = MessageBox.Show("Wanna continue?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (dia == DialogResult.Yes)
{
StreamWriter wFile = new StreamWriter("C:\\Users\\Ece\\Documents\\Testings\\" + RandomString() + ".txt");
wFile.WriteLine("Name Surname:" + text1.Text + text2.Text);
wFile.WriteLine("Other:" + text3.Text + text4.Text);
wFile.WriteLine("Money:" + textBox1.Text + " TL.");
wFile.WriteLine("*************************************");
wFile.Close();
}
else
{
return;
}
}
You can append to the text in the file.
See
File.AppendText
using (StreamWriter sw = File.AppendText(pathofFile))
{
sw.WriteLine("This");
sw.WriteLine("is Extra");
sw.WriteLine("Text");
}
where pathofFile is the path to the file to append to.
Have a look at using something like this:
StreamWriter fw = new StreamWriter(#"C:\Logs\MyFile.txt",true);
fw.WriteLine("Some Message" + Environment.Newline);
fw.Flush();
fw.Close();
Hope that helps. See MSDN StreamWriter for more information
Updated: Removed old example
Also if you are trying to create a unique file you can use Path.GetRandomFileName()
Again from the MSDN Books:
The GetRandomFileName method returns a
cryptographically strong, random
string that can be used as either a
folder name or a file name.
UPDATED: Added a Logger class example below
Add a new class to your project and add the following lines (this is 3.0 type syntax so you may have to adjust if creating a 2.0 version)
using System;
using System.IO;
namespace LogProvider
{
//
// Example Logger Class
//
public class Logging
{
public static string LogDir { get; set; }
public static string LogFile { get; set; }
private static readonly Random setere = new Random();
private const string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
public Logging() {
LogDir = null;
LogFile = null;
}
public static string RandomFileName()
{
char[] buffer = new char[5];
for (int i = 0; i < 5; i++)
{
buffer[i] = chars[setere.Next(chars.Length)];
}
return new string(buffer);
}
public static void AddLog(String msg)
{
String tstamp = Convert.ToString(DateTime.Now.Day) + "/" +
Convert.ToString(DateTime.Now.Month) + "/" +
Convert.ToString(DateTime.Now.Year) + " " +
Convert.ToString(DateTime.Now.Hour) + ":" +
Convert.ToString(DateTime.Now.Minute) + ":" +
Convert.ToString(DateTime.Now.Second);
if(LogDir == null || LogFile == null)
{
throw new ArgumentException("Null arguments supplied");
}
String logFile = LogDir + "\\" + LogFile;
String rmsg = tstamp + "," + msg;
StreamWriter sw = new StreamWriter(logFile, true);
sw.WriteLine(rmsg);
sw.Flush();
sw.Close();
}
}
}
Add this to your forms onload event
LogProvider.Logging.LogDir = "C:\\Users\\Ece\\Documents\\Testings";
LogProvider.Logging.LogFile = LogProvider.Logging.RandomFileName();
Now adjust your button click event to be like the following:
DialogResult dia = MessageBox.Show("Wanna continue?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (dia == DialogResult.Yes)
{
StringBuilder logMsg = new StringBuilder();
logMsg.Append("Name Surname:" + text1.Text + text2.Text + Environment.NewLine);
logMsg.Append("Other:" + text3.Text + text4.Text + Environment.NewLine);
logMsg.Append("Money:" + textBox1.Text + " TL." + Environment.NewLine);
logMsg.Append("*************************************" + Environment.NewLine);
LogProvider.Logging.AddLog(logMsg.ToString());
} else
{
return;
}
Now you should only create one file for the entire time that application is running and will log to that one file every time you click your button.
You might want to take a look at log4net and the RollingFileAppender
Sure. Just open the file for appending with something like System.IO.File.AppendText

Deleting files fails as held by and existing process

I'm having issues with a bit of code that I am writing in C#.
I am sending a document using the MailMessage and SMTP components. I copy the files that I wish to send to a temp directory such as c:\temp, loop through the documents and attach them to the email.
The email sends fine, however when I try to delete the files from the temp directory, I get the following error:
The process can not access the file because it is being used by another process
I can't understand why this is happening. Below is the code that processes the documents
public void sendDocument(String email, string barcode, int requestid)
{
string tempDir = #"c:\temp";
//first we get the document information from the database.
Database db = new Database(dbServer, dbName, dbUser, dbPwd);
List<Document> documents = db.getDocumentByID(barcode);
int count = 0;
foreach (Document doc in documents)
{
string tempPath = tempDir + "\\" + doc.getBarcode() + ".pdf";
string sourcePath = doc.getMachineName() + "\\" + doc.getFilePath() + "\\" + doc.getFileName();
//we now copy the file from the source location to the new target location
try
{
//this copies the file to the folder
File.Copy(sourcePath, tempPath, false);
}
catch (IOException ioe)
{
count++;
//the file has failed to copy so we add a number to the file to make it unique and try
//to copy it again.
tempPath = tempDir + "\\" + doc.getBarcode() + "-" + count + ".pdf";
File.Copy(sourcePath, tempPath, false);
}
//we now need to update the filename in the to match the new location
doc.setFileName(doc.getBarcode() + ".pdf");
}
//we now email the document to the user.
this.sendEmail(documents, email, null);
updateSentDocuments(documents, email);
//now we update the request table/
db.updateRequestTable(requestid);
//now we clean up the documents from the temp folder.
foreach (Document doc in documents)
{
string path = #"c:\temp\" + doc.getFileName();
File.Delete(path);
}
}
I would of thought that the this.sendEmail() method would of sent the email before returning to the sendDocument method, as I think it is the smtp object that is causing the deletes to fail.
This is the sendEmail method:
public void sendEmail(List<Document> documents, String email, string division)
{
String SMTPServer = null;
String SMTPUser = null;
String SMTPPwd = null;
String sender = "";
String emailMessage = "";
//first we get all the app setting used to send the email to the users
Database db = new Database(dbServer, dbName, dbUser, dbPwd);
SMTPServer = db.getAppSetting("smtp_server");
SMTPUser = db.getAppSetting("smtp_user");
SMTPPwd = db.getAppSetting("smtp_password");
sender = db.getAppSetting("sender");
emailMessage = db.getAppSetting("bulkmail_message");
DateTime date = DateTime.Now;
MailMessage emailMsg = new MailMessage();
emailMsg.To.Add(email);
if (division == null)
{
emailMsg.Subject = "Document(s) Request - " + date.ToString("dd-MM-yyyy");
}
else
{
emailMsg.Subject = division + " Document Request - " + date.ToString("dd-MM-yyyy");
}
emailMsg.From = new MailAddress(sender);
emailMsg.Body = emailMessage;
bool hasAttachements = false;
foreach (Document doc in documents)
{
String filepath = #"c:\temp\" + doc.getFileName();
Attachment data = new Attachment(filepath);
emailMsg.Attachments.Add(data);
hasAttachements = true;
}
SmtpClient smtp = new SmtpClient(SMTPServer);
//we try and send the email and throw an exception if it all goes tits.
try
{
if (hasAttachements)
{
smtp.Send(emailMsg);
}
}
catch (Exception ex)
{
throw new Exception("EmailFailure");
}
}
How to I get around this problem with a process hogging the file I wish to delete.
I can delete the file(s) once the application finishes.
You're email message isn't being Disposed, try disposing it after you've sent it:
try
{
if (hasAttachements)
{
smtp.Send(emailMsg);
}
}
catch ...
finally
{
emailMsg.Dispose();
}
The first step is to figure out what process is holding onto the file in question. I would grab the SysInternals toolkit and use the handle.exe command to determine what process is holding onto the file.
Without knowing what process has the file open, there is no way to fix this problem.
Sysinternals Link: http://technet.microsoft.com/en-us/sysinternals/default.aspx
Here's a trick I just discoverred, which will hopefully be of use to other folk?
Before adding attachments to a message, create the attachment in a using wrapper. This ensures it gets disposed of correctly, allowing the file to be successfully deleted. I'm not sure if the send also needs to be in this loop; (when I tested, emails didn't get through at first, then half an hour later I got flooded, so decided to leave testing for a time when the network was a little calmer).
using (Attachment attachment = new Attachment(filename))
{
message.Attachments.Add(attachment);
client.SendAsync(message, string.Empty);
}
File.Delete(filename);
Works OK for me anyway :)
Good luck,
JB

Categories