I have written a windows service that periodically calls an ExcelReader.exe( this exe reads data from an excel). Now the problem is that when i'm running ExcelReader.exe directly it is able to read data from excel but when i am calling it through windows service, i was able to call but ReadExcel was not performing full job. Since my service does not have any GUI and even ExcelReader.exe is console program do i need to enable service to "interact with desktop"
Here is my code:
protected override void OnStart(string[] args)
{
AddToFile(path, "starting service");
//set time interval for watch
timer.Interval = 6 * 1000;
timer.Enabled = true;
// Add handler for Elapsed event
timer.Elapsed += new System.Timers.ElapsedEventHandler(OnElapsedTime);
}
protected override void OnStop()
{
timer.Enabled = false;
AddToFile(path, "stop service");
}
private void OnElapsedTime(object source, ElapsedEventArgs e)
{
AddToFile(path,"Add another entry");
Process p = new Process();
p.StartInfo.FileName = #"D:\ExcelReader.exe";
p.Start();
}
Here is ExcelReader code:
namespace ExcelReader
{
class Program
{
public static string path = #"D:\\Excel Reader log .txt";
public static BindingList<myTeam> TeamList = new BindingList<myTeam>();
static void Main(string[] args)
{
write(path, "console program");
readexcel();
}
public static void readexcel()
{
MyExcel dailystatus = new MyExcel();
IList<myTeam> datasource = (BindingList<myTeam>) TeamList;
MyExcel.InitializeExcel();
datasource = MyExcel.ReadMyExcel();
foreach (myTeam member in datasource)
{
string memberdetails = member.Name + " " + member.TaskPerformedToday + " " + member.Progress;
write(path, memberdetails);
//(path, memberdetails);
}
//CloseExcel();
}
public static void write(string path, string contents)
{
//set up a filestream
FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
//set up a streamwriter for adding text
StreamWriter sw = new StreamWriter(fs);
//find the end of the underlying filestream
sw.BaseStream.Seek(0, SeekOrigin.End);
//add the text
sw.WriteLine(contents);
//add the text to the underlying filestream
sw.Flush();
//close the writer
sw.Close();
}
}
}
output of ExcelReader.exe (when executed directly):
console program
Name Task Performed Today Progress
sanj windows service not completed
kumar service completed
output when service call the same function:
starting service
console program
so why is it that windows service is unable to perform all ExcelReader.exe instructions.
Related
I have been using fileSystemWatcher for c# to track when a zip file has been downloaded, the problem is that the name of the file never gets output to result.Name. Why is this??
public ResultsDetails ExportCallsToCsv()
{
var downloadsDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + #"\Downloads";
ExportToCsvButton.Click();
using (var watcher = new FileSystemWatcher(downloadsDirectory))
{
watcher.Filter = "*.zip";
var result = watcher.WaitForChanged(WatcherChangeTypes.Created, 10000);
CsvFilePath = $#"{downloadsDirectory}\{result.Name}".Replace("_", "-");
}
return new ResultsDetails(ActionResult.Passed, "Csv file created succesfully.");
}
Why does result.Name never get filled? I've attempted to put an endless while loop inside the using block which breaks when result.Name gets filled, but to still no avail. It just halts forever when I do that.
FileSystemWatcher.WaitForChanged is a synchronous/blocking method and is unable to capture the file change that was triggered on the same thread.
Here's an example of your code in a small test program. This will not work (as you pointed out).
static void Main(string[] args)
{
var directory = "C:\\test";
File.Copy("C:\\test\\source.txt", "C:\\test\\new_file.txt");
using (var watcher = new FileSystemWatcher(directory))
{
watcher.Filter = "*.txt";
var result = watcher.WaitForChanged(WatcherChangeTypes.Created, 10000);
if (result.Name is null)
{
// This is always printed.
Console.WriteLine("No name");
}
}
Console.ReadLine();
}
Here's a small sample program that does work. This version handles the FileSystemWatcher events asynchronously.
static void Main(string[] args)
{
var directory = "C:\\test";
using (var watcher = new FileSystemWatcher(directory, "*.txt"))
{
watcher.EnableRaisingEvents = true; // *** Be sure to include this!
watcher.Created += Watcher_Created; // *** This is different, too.
File.Delete("C:\\test\\new_file.txt");
File.Copy("C:\\test\\source.txt", "C:\\test\\new_file.txt");
}
Console.WriteLine("All done.");
Console.ReadLine();
}
private static void Watcher_Created(object sender, FileSystemEventArgs e)
{
Console.WriteLine($"Detected {e.Name} is {e.ChangeType}.");
}
I have a windows service, written in c# and I need to run cmd in this service to get remote server's system time.
This is my code to obtain system time from remote server:
public static void GetNetworkTime()
{
//To disable automatic update from time server
RegistryKey key = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\services\\W32Time\\Parameters", true);
key.SetValue("Type", "NoSync");
key.Close();
//Run cmd.exe with "net time \\\\192.168.2.147 /set /yes" command
Process proc = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
proc.StartInfo.RedirectStandardOutput = true;
startInfo.FileName = "cmd";
startInfo.Arguments = "/c net time \\\\192.168.2.147 /set /yes";
startInfo.UseShellExecute = false;
//Get client time
DateTime clientDate = DateTime.Now;
proc.StartInfo = startInfo;
proc.Start();
proc.WaitForExit();
//Get server time from updated time of client
DateTime serverDate = DateTime.Now;
//Write info to log file
StreamWriter sw = null;
try
{
sw = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + "\\LogFile.txt", true);
sw.WriteLine("Client time is: " + clientDate);
sw.WriteLine("Server time is: " + serverDate);
sw.Flush();
sw.Close();
}
catch
{
}
}
Also, this is my service code. It supposes to check server time in every 30 seconds and update client time. Service is working but it is not updating time of my computer according to server time.
namespace NetTimeService
{
public partial class Service1 : ServiceBase
{
private Timer timer1 = null;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
timer1 = new Timer();
this.timer1.Interval = 30000;
this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Tick);
timer1.Enabled = true;
Library.WriteErrorLog("Test windows service started");
}
private void timer1_Tick(object sender, ElapsedEventArgs e)
{
Library.GetNetworkTime();
Library.WriteErrorLog("Client time has been set");
Library.WriteErrorLog("Timer ticked!");
}
protected override void OnStop()
{
timer1.Enabled = false;
Library.WriteErrorLog("Test windows service stopped");
}
}
}
I am trying to write a console application that acts as a "job manager" by running processes in the background. These processes would be running JScript files with arguments passed in. This console application will be distributed across many machines, and will pull from a centralized source (ie. database) to get jobs. The purpose of this application is to eliminate the need for individualized batch files on all of these machines.
I am having trouble keeping the application alive. In the code that I included, you can see in my main function that I am making an initial call to the JobManger's StartNewJobs() method. After this initial call to this method, I'd like my application to then be event-driven, only waking up and running when a process has exited, allowing me to start a new process. The problem I am running into is that once the main() function finishes (when the initial StartNewJobs() method finishes) the console closes and the program ends.
My question is what is the proper way to keep my console application alive and allow it to be event-driven rather than procedural? I know I can probably throw in a while(true) at the end of the main function, but that seems sloppy and incorrect.
Batch file we are trying to replace:
C:\Windows\SysWOW64\cscript.exe c:\temp\somejscriptfile.js 49f1bdd8-5e6b-40cc-92bc-eb20c237a959
C:\Windows\SysWOW64\cscript.exe c:\temp\somejscriptfile.js 654e3783-a1b6-43be-8027-c7d060bf131f
...
Program.cs:
using DistributedJobs.Data;
using DistributedJobs.Logging;
using DistributedJobs.Models;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using System;
namespace DistributedJobs
{
class Program
{
static void Main(string[] args)
{
//Get intial objects/settings
ILogger logger = new Logger(Properties.Settings.Default.LoggingLevel, EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>());
IDataProvider dataProvider = new SQLDataProvider();
DMSPollingJobType availableJobTypes = DMSPollingJobType.FlatFile;
if (Properties.Settings.Default.SupportsVPN)
{
availableJobTypes |= DMSPollingJobType.VPN;
}
String executableLocation = Properties.Settings.Default.ExecutableLocation;
String jsLocation = Properties.Settings.Default.JSLocation;
Int32 maxProcesses = Properties.Settings.Default.MaxProcesses;
//Create job manager and start new processes/jobs
DMSJobManager jobManager = new DMSJobManager(logger, dataProvider, availableJobTypes, executableLocation, jsLocation, maxProcesses);
jobManager.StartNewJobs();
}
}
}
JobManager.cs:
using DistributedJobs.Models;
using System.Diagnostics;
using System;
using System.Collections.Generic;
using DistributedJobs.Logging;
namespace DistributedJobs.Data
{
public class JobManager
{
private IDataProvider DataProvider;
private ILogger Logger;
private Dictionary<Job, Process> RunningProcesses;
private JobType AvailableJobTypes;
private String ExecutableLocation;
private String JSLocation;
private Int32 MaxProcesses;
public Boolean CanStartNewJob
{
get
{
Boolean canStartNewJob = false;
if (RunningProcesses.Count < MaxProcesses)
{
canStartNewJob = true;
}
foreach (KeyValuePair<Job, Process> entry in RunningProcesses)
{
if (entry.Key.JobType != JobType.FlatFile)
{
canStartNewJob = false;
break;
}
}
return canStartNewJob;
}
}
public JobManager(ILogger logger, IDataProvider dataProvider, JobType availableJobTypes, String executableLocation, String jsLocation, Int32 maxProcesses)
{
Logger = logger;
DataProvider = dataProvider;
RunningProcesses = new Dictionary<Job, Process>();
AvailableJobTypes = availableJobTypes;
ExecutableLocation = executableLocation;
JSLocation = jsLocation;
MaxProcesses = maxProcesses;
}
public void StartNewJobs()
{
while (CanStartNewJob)
{
Job newJob = DataProvider.GetNextScheduledJob(AvailableJobTypes);
if (newJob != null)
{
Process newProcess = CreateNewProcess(newJob);
RunningProcesses.Add(newJob, newProcess);
newProcess.Start();
}
}
}
public Process CreateNewProcess(Job job)
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = ExecutableLocation;
startInfo.Arguments = JSLocation + " " + job.JobID.ToString();
startInfo.UseShellExecute = false;
Process retProcess = new Process()
{
StartInfo = startInfo,
EnableRaisingEvents = true
};
retProcess.Exited += new EventHandler(JobFinished);
return retProcess;
}
public void JobFinished(object sender, EventArgs e)
{
Job finishedJob = null;
foreach (KeyValuePair<Job, Process> entry in RunningProcesses)
{
if ((Process)sender == entry.Value)
{
finishedJob = entry.Key;
break;
}
}
if (finishedJob != null)
{
RunningProcesses.Remove(finishedJob);
StartNewJobs();
}
}
}
}
You could try using Application.Run()(System.Windows.Forms). This will start a standard message loop.
So at the end of your Main method just add a Application.Run():
static void Main(string[] args)
{
//Get intial objects/settings
ILogger logger = new Logger(Properties.Settings.Default.LoggingLevel, EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>());
IDataProvider dataProvider = new SQLDataProvider();
DMSPollingJobType availableJobTypes = DMSPollingJobType.FlatFile;
if (Properties.Settings.Default.SupportsVPN)
{
availableJobTypes |= DMSPollingJobType.VPN;
}
String executableLocation = Properties.Settings.Default.ExecutableLocation;
String jsLocation = Properties.Settings.Default.JSLocation;
Int32 maxProcesses = Properties.Settings.Default.MaxProcesses;
//Create job manager and start new processes/jobs
DMSJobManager jobManager = new DMSJobManager(logger, dataProvider, availableJobTypes, executableLocation, jsLocation, maxProcesses);
jobManager.StartNewJobs();
// start message loop
Application.Run();
}
I am trying to add a logfile to a program I am making, but when I tell the main thread to exit (using Application.ExitThread()), logstrmWriter is suddenly null before it ever gets there. This is a very simple script.
private static FileStream appLogStream;
internal static string logFile;
internal static StreamWriter logstrmWriter;
public static void Main()
{
logFile = Application.StartupPath + #"\Archiver.log";
appLogStream = new FileStream(logFile, FileMode.Append, FileAccess.Write, FileShare.Read);
TextWriter logtxtWriter = Console.Out;
StreamWriter logstrmWriter = new StreamWriter(appLogStream);
if(!console) Console.SetOut(logstrmWriter);
Application.ApplicationExit += new EventHandler(OnApplicationExit);
Application.Run();
}
internal static void OnApplicationExit(object sender, EventArgs e)
{
active = false; Console.WriteLine("Main thread is shutting down. Sending Interrupt...");
Archiver.Stop(); Console.WriteLine("Shutdown. Log and Exit");
Console.WriteLine();
logstrmWriter.Flush();
logstrmWriter.Close();
logstrmWriter.Dispose();
}
You're creating a local variable that hides the static variable in question. As such, you never initialize logstrmWriter at all.
TextWriter logtxtWriter = Console.Out;
/* StreamWriter */ logstrmWriter = new StreamWriter(appLogStream); // Remove the redeclaration here!
if(!console) Console.SetOut(logstrmWriter);
I have a slight problem. What my application is supose to do, is to watch a folder for any newly copied file with the extention '.XSD' open the file and assign the lines to an array. After that the data from the array should be inserted into a MySQL database, then move the used file to another folder if it's done.
The problem is that the application works fine with the first file, but as soon as the next file is copied to the folder I get this exception for example: 'The process cannot access the file 'C:\inetpub\admission\file2.XPD' because it is being used by another process'.
If two files on the onther hand is copied at the same time there's no problem at all.
The following code is on the main window:
public partial class Form1 : Form
{
static string folder = specified path;
static FileProcessor processor;
public Form1()
{
InitializeComponent();
processor = new FileProcessor();
InitializeWatcher();
}
static FileSystemWatcher watcher;
static void InitializeWatcher()
{
watcher = new FileSystemWatcher();
watcher.Path = folder;
watcher.Created += new FileSystemEventHandler(watcher_Created);
watcher.EnableRaisingEvents = true;
watcher.Filter = "*.XPD";
}
static void watcher_Created(object sender, FileSystemEventArgs e)
{
processor.QueueInput(e.FullPath);
}
}
As you can see the file's path is entered into a queue for processing which is on another class called FileProcessor:
class FileProcessor
{
private Queue<string> workQueue;
private Thread workerThread;
private EventWaitHandle waitHandle;
public FileProcessor()
{
workQueue = new Queue<string>();
waitHandle = new AutoResetEvent(true);
}
public void QueueInput(string filepath)
{
workQueue.Enqueue(filepath);
if (workerThread == null)
{
workerThread = new Thread(new ThreadStart(Work));
workerThread.Start();
}
else if (workerThread.ThreadState == ThreadState.WaitSleepJoin)
{
waitHandle.Set();
}
}
private void Work()
{
while (true)
{
string filepath = RetrieveFile();
if (filepath != null)
ProcessFile(filepath);
else
waitHandle.WaitOne();
}
}
private string RetrieveFile()
{
if (workQueue.Count > 0)
return workQueue.Dequeue();
else
return null;
}
private void ProcessFile(string filepath)
{
string xName = Path.GetFileName(filepath);
string fName = Path.GetFileNameWithoutExtension(filepath);
string gfolder = specified path;
bool fileInUse = true;
string line;
string[] itemArray = null;
int i = 0;
#region Declare Db variables
//variables for each field of the database is created here
#endregion
#region Populate array
while (fileInUse == true)
{
FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read,
FileShare.ReadWrite);
StreamReader reader = new StreamReader(fs);
itemArray = new string[75];
while (!reader.EndOfStream == true)
{
line = reader.ReadLine();
itemArray[i] = line;
i++;
}
fs.Flush();
reader.Close();
reader.Dispose();
i = 0;
fileInUse = false;
}
#endregion
#region Assign Db variables
//here all the variables get there values from the array
#endregion
#region MySql Connection
//here the connection to mysql is made and the variables are inserted into the db
#endregion
#region Test and Move file
if (System.IO.File.Exists(gfolder + xName))
{
System.IO.File.Delete(gfolder + xName);
}
Directory.Move(filepath, gfolder + xName);
#endregion
}
}
The problem I get occurs in the Populate array region. I read alot of other threads and was lead to believe that by flushing the file stream would help...
I am also thinking of adding a try..catch for if the file process was successful, the file is moved to gfolder and if it failed, moved to bfolder
Any help would be awesome
Tx
You're not disposing of your FileStream instance, so a lock remains on the file. Change your code to use using blocks:
using (var fileStream = new FileStream(...))
{
using (var reader = new StreamReader(fileStream))
{
}
}
These using blocks will ensure the instances are correctly disposed of.
Also, why are you calling Flush on the file stream? You're not writing anything with it...
I would suggest :
1° use the using syntax on StreamReader
2° use the using syntax on FileStream