C# File Open frequently, already open exception - c#

Hey so I have a logger class that is called frequently, sometimes when its called fast repeatedly it will throw an exception that the file is already open being used by another application. The only way we found a way around this was to catch the exception then try to open it again... I'm not sure how to handle this properly.
/// <summary>
/// Open a log file based on a date
/// </summary>
/// <param name="date"> Date of the file to open </param>
public static void OpenLogFile(DateTime date)
{
while (true)
{
try
{
logStream = File.Open("Logs/ems." + date.ToString("yyyy-MM-dd") + ".log", FileMode.Append);
break;
}
catch
{
continue;
}
}
}
/// <summary>
/// Writes to a log file the specified input
/// </summary>
/// <param name="input"> Content to write to the log file </param>
public static void Log(string className, string methodName, string input)
{
OpenLogFile(DateTime.Now);
using (StreamWriter s = new StreamWriter(logStream))
{
s.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + '[' + className + '.' + methodName + "] " + input);
}
CloseLogFile();
}
/// <summary>
/// Closes the current log file
/// </summary>
public static void CloseLogFile()
{
if (logStream != null)
{
logStream.Close();
logStream = null;
}
}

Since this is a log file, I think the obvious (and correct) solution, is to leave the file open for the duration of your program's execution.
If you don't want to use an already-implemented 3rd party logger, like Apache's log4net, you could write your own little logger class that uses thread-safe mechanisms for writing lines to the file.
It would probably be a static class, or singleton. Generally, execution at the beginning and end of your program is quite predictable, so it should be clear where to initialize, and close out the object/class.

A bit of order is required, not a disk free-for-all. Store log messages in a queue and have a single thread dequeue and write items.

Related

.NET Framwork Passing information to already running process

I currently have an application (.Net Frameowork) that gets triggered via CMD request. I would like to only have one instance of the application running and I would like to handle the arguments being passed in. Right now I have the application identifying if there is a currently running instance.
Example:
cmd ReceiptApplication.exe -r:blah -c:foo
cmd ReceiptApplication.exe -r:blah2 -c:foo2
The second request would pass blah2 and foo2 to the currently running process of ReceiptApplication.exe and kill the second application.
What is the process or pattern for passing the parameters?
using System.Linq;
using System.Windows;
namespace ReceiptApplication
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
/// <summary>
/// Startup the application,
/// Process:
/// Get the arguments passed in
/// Validate the arguments
/// check to see if there is a running instance of the application
/// if there is a running app
/// Pass the arguments to the running application
/// kill the current application.
/// else
/// process the request
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Application_Startup(object sender, StartupEventArgs e)
{
string receiptID = GetArgument(e.Args, "-r");
string creditUnionFile = GetArgument(e.Args, "-c");
if(receiptID == null || creditUnionFile == null)
{
System.Windows.Forms.Application.Exit();
}
if (AppCurrntlyRunning())
{
//pass arguments to the current running application
Process alreadyRunningApplication = GetOriginalProcess();
var streamWriter = alreadyRunningApplication.StandardInput;
streamWriter.WriteLine("-r:blah -c:yay");
//kill this one
System.Windows.Forms.Application.Exit();
}
MessageBox.Show("Starting UI!");
MainWindow wnd = new MainWindow();
wnd.PullReceipt(receiptID, creditUnionFile);
wnd.Show();
}
}
}
If you want to pass information to your application during runtime you could use the standard input stream. This is how it's done: How to write to stdin in C#
Then in your running application you can read the written data with Console.ReadLine();
On the linked answer on how to write to stdin in C# mind that instead of starting a new application you want to store a reference to your already running one:
//DON'T do this
//var process = new Process();
//process.StartInfo = startInfo;
//process.Start();
//DO this
var processes = Process[] localByName = Process.GetProcessesByName("The Title Of My Application"); //This should give back multiple processes if you start your application for a second time so be mindful about that
Process alreadyRunningApplication;
if(processes.Length > 1)
{
if (processes[0].Id == Process.GetCurrentProcess.Id)
{
alreadyRunningApplication = processes[1];
}
else
{
alreadyRunningApplication = processes[0];
}
var streamWriter = alreadyRunningApplication.StandardInput;
streamWriter.WriteLine("I'm supplying input!");
}
else
{
//If we get into this else, it means this instance is the first running instance so your normal program behavior should go here for example using Console.ReadLine();
}

Its about the copying image files from one directory to another

I have 3 directories. These are images,good, bad. I'm getting images from images directory and I'm sending it the other directories. My question is, if good or bad directory have these images how can I change the name of images such as "I have car.jpg and if i copy it again , its name changes to car(1).jpg. I hope you understand my question..
I use code like this
internal static partial class IOUtilities
{
/// <summary>
/// Move a file to an archive folder
/// </summary>
/// <remarks>Renames file if necessary to avoid collision.
/// See <see cref="File.Move"/> for exceptions</remarks>
/// <param name="file">path to file to move</param>
/// <param name="targetFolder">folder to move file to</param>
public static void ArchiveFile(string file, string targetFolder) {
if (file == null)
throw new ArgumentNullException("file");
if (targetFolder == null)
throw new ArgumentNullException("targetFolder");
string targetFilename = Path.Combine(targetFolder, Path.GetFileName(file));
File.Move(file, FindAvailableFilename(targetFilename));
}
/// <summary>
/// Archive file in the same folder
/// </summary>
/// <remarks>Renames file by adding first free "(n)" suffix
/// See <see cref="File.Move"/> for exceptions</remarks>
/// <param name="file">path to file to archive</param>
public static void ArchiveFile(string file) {
if (file == null)
throw new ArgumentNullException("file");
File.Move(file, FindAvailableFilename(file));
}
/// <summary>
/// Find a "free" filename by adding (2),(3)...
/// </summary>
/// <param name="targetFilename">Complete path to target filename</param>
/// <returns>First available filename</returns>
private static string FindAvailableFilename(string targetFilename) {
if (!File.Exists(targetFilename))
return targetFilename;
string filenameFormat = Path.GetFileNameWithoutExtension(targetFilename) + "({0})" + Path.GetExtension(targetFilename);
string format = Path.Combine(Path.GetDirectoryName(targetFilename), filenameFormat);
for (int ii = 2;; ++ii) {
// until we find a filename that doesn't exist
string newFilename = string.Format(CultureInfo.InvariantCulture, format, ii);
if (!File.Exists(newFilename)) // returns false on illegal paths, security problems etc
return newFilename;
}
}
}
You can try out the sample code here:
http://www.codeproject.com/Questions/212217/increment-filename-if-file-exists-using-csharp

Loop through set of files and check if it is comma delimited in C#

I need to loop through set of files and check if it is comma delimited or not in C#.I am very new to C#.Please help me with this.
Thanks in advance.
As someone already pointed out it is not going to be easy solution.
It could be very easy if every comma delimited file had a specified extension (for example: csv). If not the following algorigthm should work:
Retrieve all names (paths + names) of files in specified directory. If needed filter only those that may be of interest. Hint: take a look at System.IO.Directory and System.IO.File and System.IO.DirectoryInfo and System.IO.FileInfo
You have to examine every file, and check if it is comma delimited or not. This is going to be tricky part. You could build a regular expression, that will check each line of the file and and tell you if it is comma delimited or not.
Regular expressions are a bit hard to learn at the beginning but it should pay back after some time.
Here's a quick Console app that will take a directory, scan the directory for all files, then iterate through them and return a percentage of lines containing commas -vs- the total lines in the file. As has been pointed out, there are CSV libraries you can validate against. This is just a quick example to get you started.
To use this, create a new Console App project in Visual Studio and name it "TestStub" then copy and past this into the "Program.cs" file.
namespace TestStub
{
using System;
using System.IO;
using System.Text;
public class Program
{
private static char[] CSV = { ',', ',' };
private static bool csvFound = false;
/// <summary>
/// This is the console program entry point
/// </summary>
/// <param name="args">A list of any command-line args passed to this application when started</param>
public static void Main(string[] args)
{
// Change this to use args[0] if you like
string myInitialPath = #"C:\Temp";
string[] myListOfFiles;
try
{
myListOfFiles = EnumerateFiles(myInitialPath);
foreach (string file in myListOfFiles)
{
Console.WriteLine("\nFile {0} is comprised of {1}% CSV delimited lines.",
file,
ScanForCSV(file));
}
Console.WriteLine("\n\nPress any key to exit.");
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(
"Error processing {0} for CSV content: {1} :: {2}",
myInitialPath,
ex.Message,
ex.InnerException.Message);
}
}
/// <summary>
/// Get a list of all files for the specified path
/// </summary>
/// <param name="path">Directory path</param>
/// <returns>String array of files (with full path)</returns>
public static string[] EnumerateFiles(string path)
{
string[] arrItems = new string[1];
try
{
arrItems = Directory.GetFiles(path);
return arrItems;
}
catch (Exception ex)
{
throw new System.IO.IOException("EnumerateFilesAndFolders() encountered an error:", ex);
}
}
/// <summary>
/// Determines if the supplied file has comma separated values
/// </summary>
/// <param name="filename">Path and filename</param>
/// <returns>Percentage of lines containing CSV elements -vs- those without</returns>
public static float ScanForCSV(string filename)
{
//
// NOTE: You should look into one of the many CSV libraries
// available. This method will not carefully scruitinize
// the file to see if there's a combination of delimeters or
// even if it's a plain-text (e.g. a newspaper article)
// It just looks for the presence of commas on multiple lines
// and calculates a percentage of them with and without
//
float totalLines = 0;
float linesCSV = 0;
try
{
using (StreamReader sReader = new StreamReader(filename))
{
int elements = 0;
string line = string.Empty;
string[] parsed = new string[1];
while (!sReader.EndOfStream)
{
++totalLines;
line = sReader.ReadLine();
parsed = line.Split(CSV);
elements = parsed.Length;
if (elements > 1)
{
++linesCSV;
}
}
}
}
catch (Exception ex)
{
throw new System.IO.IOException(string.Format("Problem accessing [{0}]: {1}", filename, ex.Message), ex);
}
return (float)((linesCSV / totalLines) * 100);
}
}
}
}

Marshalling C# com items when using recursion

I am using the SourceSafe COM object (SourceSafeTypeLib) from C# to automate a SourceSafe recursive get (part of a larger build process). The recursive function is shown below. How do I ensure that all the COM objects created in the foreach loop get released correctly?
/// <summary>
/// Recursively gets files/projects from SourceSafe (this is a recursive function).
/// </summary>
/// <param name="vssItem">The VSSItem to get</param>
private void GetChangedFiles(VSSItem vssItem)
{
// 'If the object is a file perform the diff,
// 'If not, it is a project, so use recursion to go through it
if(vssItem.Type == (int)VSSItemType.VSSITEM_FILE)
{
bool bDifferent = false; //file is different
bool bNew = false; //file is new
//Surround the diff in a try-catch block. If a file is new(doesn't exist on
//the local filesystem) an error will be thrown. Catch this error and record it
//as a new file.
try
{
bDifferent = vssItem.get_IsDifferent(vssItem.LocalSpec);
}
catch
{
//File doesn't exist
bDifferent = true;
bNew = true;
}
//If the File is different(or new), get it and log the message
if(bDifferent)
{
if(bNew)
{
clsLog.WriteLine("Getting " + vssItem.Spec);
}
else
{
clsLog.WriteLine("Replacing " + vssItem.Spec);
}
string strGetPath = vssItem.LocalSpec;
vssItem.Get(ref strGetPath, (int)VSSFlags.VSSFLAG_REPREPLACE);
}
}
else //Item is a project, recurse through its sub items
{
foreach(VSSItem fileItem in vssItem.get_Items(false))
{
GetChangedFiles(fileItem);
}
}
}
If it is a short running program and there is nothing to "commit" on the COM side, it is ok to let them go, believe it or not. The GC will come and properly release the interfaces when it needs to.
If it is a long running program (like a server component or takes hours and hours to complete), or you need to "commit" or "save" changes the best bet would be to release them as you would any VSSItem right after your call to GetChangedFiles(fileItem); in your foreach loop.
Example:
foreach (VSSItem fileItem in vssItem.get_Items(false))
{
GetChangedFiles(fileItem);
// fileItem.Release(); or fileItem.Dispose();
// or even Marshal.ReleaseComObject(fileItem);
}

Is there a way to automate turning a BizTalk Receive Location on or off through code?

Is there a way to automate the turning on or off of a Receive Location in BizTalk? It seems like there should be some kind of API or some such for this kind of thing. I would prefer to work in C#, but WMI or some kind of script would work too.
Besides ExplorerOM, as you've found out, you can also enable/disable receive locations (and control send ports) using WMI.
I have a sample PowerShell script that shows how to do those things here, if you're interested.
I found a solution. It appears that the Microsoft.BizTalk.ExplorerOM.dll is what I wanted. Here is an excerpt from the BizTalk documentation that should get anyone else started:
using System;
using Microsoft.BizTalk.ExplorerOM;
public static void EnumerateOrchestrationArtifacts()
{
// Connect to the local BizTalk Management database
BtsCatalogExplorer catalog = new BtsCatalogExplorer();
catalog.ConnectionString = "Server=.;Initial Catalog=BizTalkMgmtDb;Integrated Security=SSPI;";
// Enumerate all orchestrations and their ports/roles
Console.WriteLine("ORCHESTRATIONS: ");
foreach(BtsAssembly assembly in catalog.Assemblies)
{
foreach(BtsOrchestration orch in assembly.Orchestrations)
{
Console.WriteLine(" Name:{0}\r\n Host:{1}\r\n Status:{2}",
orch.FullName, orch.Host.Name, orch.Status);
// Enumerate ports and operations
foreach(OrchestrationPort port in orch.Ports)
{
Console.WriteLine("\t{0} ({1})",
port.Name, port.PortType.FullName);
foreach(PortTypeOperation operation in port.PortType.Operations)
{
Console.WriteLine("\t\t" + operation.Name);
}
}
// Enumerate used roles
foreach(Role role in orch.UsedRoles)
{
Console.WriteLine("\t{0} ({1})",
role.Name, role.ServiceLinkType);
foreach(EnlistedParty enlistedparty in role.EnlistedParties)
{
Console.WriteLine("\t\t" + enlistedparty.Party.Name);
}
}
// Enumerate implemented roles
foreach(Role role in orch.ImplementedRoles)
{
Console.WriteLine("\t{0} ({1})",
role.Name, role.ServiceLinkType);
}
}
}
}
One caveat, apparently this dll does not support 64 bit. Since I am only writing a simple utility it's not a big deal for me (just compiling as 32-bit), but it is something to be aware of.
Glad to see that you seem to have found a solution.
Wanted to mention a similar alternative which is also using Powershell, ExplorerOM, and the BizTalk API to set BizTalk artifacts to various statuses.
Receive Locations being one of them.
The script accepts XML configuration files, where you list the artifacts and what status you would like to set them to.
The script has been published to Microsoft Script Center:
http://gallery.technet.microsoft.com/scriptcenter/Set-Artifact-Status-270f43a0
In response to Alhambraeidos comment. Here's is some excerpts of code I used in a Windows app to disable a Receive Location remotely:
/// <summary>
/// Gets or sets the biz talk catalog.
/// </summary>
/// <value>The biz talk catalog.</value>
private BtsCatalogExplorer BizTalkCatalog { get; set; }
/// <summary>
/// Initializes the biz talk artifacts.
/// </summary>
private void InitializeBizTalkCatalogExplorer()
{
// Connect to the local BizTalk Management database
BizTalkCatalog = new BtsCatalogExplorer();
BizTalkCatalog.ConnectionString = "server=BiztalkDbServer;database=BizTalkMgmtDb;integrated security=true";
}
/// <summary>
/// Gets the location from biz talk.
/// </summary>
/// <param name="locationName">Name of the location.</param>
/// <returns></returns>
private ReceiveLocation GetLocationFromBizTalk(string locationName)
{
ReceivePortCollection receivePorts = BizTalkCatalog.ReceivePorts;
foreach (ReceivePort port in receivePorts)
{
foreach (ReceiveLocation location in port.ReceiveLocations)
{
if (location.Name == locationName)
{
return location;
}
}
}
throw new ApplicationException("The following receive location could not be found in the BizTalk Database: " + locationName);
}
/// <summary>
/// Turns the off receive location.
/// </summary>
/// <param name="vendorName">Name of the vendor.</param>
public void TurnOffReceiveLocation(string vendorName)
{
ReceiveLocation location = Locations[vendorName].ReceiveLocation;
location.Enable = false;
BizTalkCatalog.SaveChanges();
}
You'll notice that there is some I left out, like I was creating a dictionary of receive locations called "Locations", but you should be able to get the idea. The pattern pretty much holds true for any BizTalk object you want to interact with.

Categories

Resources