ObjectDisposed Exception on second XPS export - c#

I have an application that is used to open files in a document viewer for the purposes of 1: not allowing users to alter the files, and 2: to track the files that are opened and how long they are open for.
That said, I have the files they select (either Word Docs or Excel Workbooks) converted into XPS files and placed in a DocumentViewer in a WPF project.
The first time a document is opened, it works as intended. However, as soon as a second file is attempted to be opened, I get a
System.ObjectDisposedException: Package object was closed and disposed, so cannot carry out operations on this object or any stream opened on a part of this package.
I have been searching for hours now and have no idea what is going on.
Here is the relevant code:
class DocumentViewerFileGenerator
{
/// <summary>
/// Generates a docuemnt, of the IDocumentPaginatorSource type to be used by the document viewer in the
/// view. By looking at the extension type, decides on which interop to use.
/// </summary>
/// <param name="filePath">Path of the file that is to be converted</param>
/// <param name="extension">Extension of the file. Makes it easier for the if's</param>
/// <returns>A converted IDocumentPaginatorSource version of the file to be viewed.</returns>
public XpsDocument GenerateDocumentForViewer(string filePath, string extension)
{
string tempOutputPath = Environment.CurrentDirectory + #"\temp.xps";
ClearOldTemp(tempOutputPath);
XpsDocument xpsDocument;
if (extension == ".doc" || extension == ".docx")
{
ConvertWordToXps(filePath, tempOutputPath);
}
if (extension == ".xls" || extension == ".xlsx")
{
ConvertExcelToXps(filePath, tempOutputPath);
}
xpsDocument = MakeFixedDocument(tempOutputPath);
return xpsDocument;
}
/// <summary>
/// Just clears out the old temp path
/// </summary>
/// <param name="tempOutputPath"></param>
private void ClearOldTemp(string tempOutputPath)
{
if (File.Exists(tempOutputPath))
{
File.Delete(tempOutputPath);
}
}
/// <summary>
/// Converts the file selected, through Word, into an XPS for conversion purposes.
/// </summary>
/// <param name="filePath">The file to be converted. Full path needed.</param>
private void ConvertWordToXps(string filePath, string tempOutputPath)
{
Word.Application word = new Word.Application();
word.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;
word.Visible = false;
Word.Document document = word.Documents.Open(filePath);
document.SaveAs2(tempOutputPath, FileFormat: Word.WdSaveFormat.wdFormatXPS);
document.Close();
word.Quit();
Marshal.ReleaseComObject(document);
Marshal.ReleaseComObject(word);
}
/// <summary>
/// Converts the file selected, through Excel, into an XPS for conversion purposes.
/// </summary>
/// <param name="filePath">The file to be converted. Full path needed.</param>
private void ConvertExcelToXps(string filename, string tempOutputPath)
{
Excel.Application excel = new Excel.Application();
excel.Visible = false;
excel.DisplayAlerts = false;
Excel.Workbook workbook = excel.Workbooks.Open(filename);
workbook.ExportAsFixedFormat(Excel.XlFixedFormatType.xlTypeXPS, tempOutputPath);
workbook.Close();
excel.Quit();
Marshal.ReleaseComObject(workbook);
Marshal.ReleaseComObject(excel);
excel = null;
}
/// <summary>
///
/// </summary>
/// <param name="tempOutputPath"></param>
/// <returns></returns>
private XpsDocument MakeFixedDocument(string tempOutputPath)
{
return new XpsDocument(tempOutputPath, FileAccess.Read);
}
}
}
This is the ViewModel for displaying the document:
public FileViewerViewModel(string fileName, string exentsion)
{
DocumentViewerFileGenerator generator = new DocumentViewerFileGenerator();
try
{
FileToDisplay = generator.GenerateDocumentForViewer(fileName, exentsion);
FileToShow = FileToDisplay.GetFixedDocumentSequence();
IsMainEnabled.Instance.IsWindowVisible = System.Windows.Visibility.Hidden;
}
catch (Exception log)
{
//Error handeling
}
/// <summary>
/// The file that is to be shown on the view model.
/// </summary>
private IDocumentPaginatorSource fileToShow;
public IDocumentPaginatorSource FileToShow
{
get { return fileToShow; }
set {
if (value == fileToShow)
{ return; }
fileToShow = value;
OnPropertyChanged();
}
}
private XpsDocument FileToDisplay
{
get;
set;
}
/// <summary>
/// Our good, generic PropertyChanged handler. Glorious.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void ClosingWindow()
{
if (AutoLogoutTimer.Instance.IsLogOff != true)
{
UnlockXPSFile(FileToDisplay);
IsMainEnabled.Instance.IsWindowVisible = System.Windows.Visibility.Visible;
}
}
private void UnlockXPSFile(XpsDocument fileToUnlock)
{
Package xpsPackage = PackageStore.GetPackage(fileToUnlock.Uri);
xpsPackage.Close();
}
}
As I said before, the first time this runs through, it works fine.
However, the second time it goes to make a new xps file at this line:
private XpsDocument MakeFixedDocument(string tempOutputPath)
{
return new XpsDocument(tempOutputPath, FileAccess.Read);
}
The exception gets thrown.
What am I missing here?
Thank you.

Well after doing quite of a bit of head scratching, more Googling and banging my face against the keyboard, it dawned upon me what I was missing...
private void UnlockXPSFile(XpsDocument fileToUnlock)
{
Package xpsPackage = PackageStore.GetPackage(fileToUnlock.Uri);
xpsPackage.Close();
PackageStore.RemovePackage(fileToUnlock.Uri); //This line right here
}
While I was closing the package, I wasn't actually removing it from the package store. So, the application was looking for the same package store to store the new XPS folder in, but since it was closed and dumped, it had no where to go.

Related

FileSystemWatcher with a background thread is missing to move files

I have a simple requirement of moving files from source folder to destination folder. The files are about 5mb in size and arrive three at a time (every 5 seconds).
The current mechanism I have in place seems to move files however if the destination folder is not accessible for a few seconds, the files to process from the source directory does not queue up but gets missed.
My question is how do I create a queue with all the files which are created at source directory and move the files to destination? Do i need to use the background thread?
My watcher code looks like this.
public sealed class Watcher
{
int eventCount = 0;
#region Private Members
/// <summary>
/// File system watcher variable.
/// </summary>
private FileSystemWatcher fsw = null;
/// <summary>
/// Destination path to use.
/// </summary>
private string destination = #"c:\temp\doc2\";
/// <summary>
/// Source path to monitor.
/// </summary>
private string source = #"c:\temp\doc\";
/// <summary>
/// Default filter type is all files.
/// </summary>
private string filter = "*.bmp";
/// <summary>
/// Monitor all sub directories in the source folder.
/// </summary>
private bool includeSubdirectories = true;
/// <summary>
/// Background worker which will Move files.
/// </summary>
private BackgroundWorker bgWorker = null;
/// <summary>
/// Toggle flag to enable copying files and vice versa.
/// </summary>
private bool enableCopyingFiles = false;
/// <summary>
/// File System watcher lock.
/// </summary>
private object fswLock = new object();
private static Watcher watcherInstance;
#endregion
#region Public Properties
public static Watcher WatcherInstance
{
get
{
if (watcherInstance == null)
{
watcherInstance = new Watcher();
}
return watcherInstance;
}
}
public string Source
{
get
{
return source;
}
set
{
source = value;
}
}
public string Destination
{
get
{
return destination;
}
set
{
destination = value;
}
}
public string Filter
{
get
{
return filter;
}
set
{
filter = value;
}
}
public bool MonitorSubDirectories
{
get
{
return includeSubdirectories;
}
set
{
includeSubdirectories = value;
}
}
public bool EnableCopyingFiles
{
get
{
return enableCopyingFiles;
}
set
{
enableCopyingFiles = value;
}
}
public FileSystemWatcher FSW
{
get
{
return fsw;
}
set
{
fsw = value;
}
}
#endregion
#region Construction
/// <summary>
/// Constructor.
/// </summary>
public Watcher()
{
// Intentionally left blank.
}
#endregion
#region Public Methods
/// <summary>
/// Method which will initialise the required
/// file system watcher objects to starting watching.
/// </summary>
public void InitialiseFSW()
{
fsw = new FileSystemWatcher();
bgWorker = new BackgroundWorker();
}
/// <summary>
/// Method which will start watching.
/// </summary>
public void StartWatch()
{
if (fsw != null)
{
fsw.Path = source;
fsw.Filter = filter;
fsw.IncludeSubdirectories = includeSubdirectories;
fsw.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastAccess;
// Setup events.
fsw.Created += fsw_Created;
// Important to set the below otherwise no events will be raised.
fsw.EnableRaisingEvents = enableCopyingFiles;
bgWorker.DoWork += bgWorker_DoWork;
}
else
{
Trace.WriteLine("File System Watcher is not initialised. Setting ISS Fault Alarm Bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
}
}
/// <summary>
/// Method to stop watch.
/// </summary>
public void StopWatch()
{
// Stop Watcher.
if (bgWorker != null)
{
bgWorker.DoWork -= bgWorker_DoWork;
}
if (fsw != null)
{
fsw.Created -= fsw_Created;
}
}
#endregion
#region Private Methods
/// <summary>
/// Method which will do the work on the background thread.
/// Currently Move files from source to destination and
/// monitor disk capacity.
/// </summary>
/// <param name="sender">Object Sender</param>
/// <param name="e">Event Arguments</param>
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
Trace.WriteLine("ZZZZZ..Event Count:" + eventCount.ToString());
// Extract the file names form the arguments.
FileSystemEventArgs fsea = (FileSystemEventArgs)e.Argument;
// Form the correct filename.
// An assumption has been made that there will always be an '&' symbol in the filename.
// An assumption has been made that the batch code will be added before the first '&' symbol.
string newFileName = string.Empty;
//// First character we are looking for has been found.
//// Sanity checks.
//if (CommonActions.ExtDictionary != null)
//{
// // Add the batch Code.
// // newFileName = fnParts[i] + "_" + CommonActions.ExtDictionary["BatchID"] + "_" + "&";
// // Add the batch code before the filename for easy sorting in windows explorer.
// newFileName = CommonActions.ExtDictionary["BatchID"] + "_" + fsea.Name;
//}
//else
//{
// Batch Code not found. So prefix with hardcoded text.
newFileName = "BatchCode" + "_" + fsea.Name;
//newFileName = fsea.Name;
//}
// We should now have the fully formed filename now.
// Move the file to the new location
string destPath = destination + #"\" + newFileName;
var fi = new FileInfo(fsea.FullPath);
// TODO Check if the file exist.
if (File.Exists(Path.Combine(Source, fsea.Name)))
{
// Check if the file is accessiable.
if (IsAccessible(fi, FileMode.Open, FileAccess.Read))
{
if (!File.Exists(destPath))
{
try
{
// Copy the file.
//File.Copy(fsea.FullPath, destPath);
// Move the file.
//File.Move(fsea.FullPath, destPath);
File.Copy(fsea.FullPath, destPath);
File.SetAttributes(destPath, FileAttributes.ReadOnly);
//Stopwatch sWatch = Stopwatch.StartNew();
//TimeSpan fileDropTimeout = new TimeSpan(0, 0, 10);
//bool fileActionSuccess = false;
//do
//{
// // Copy the file.
// //File.Copy(fsea.FullPath, destPath);
// // Move the file.
// //File.Move(fsea.FullPath, destPath);
// File.Copy(fsea.FullPath, destPath);
// File.SetAttributes(destPath, FileAttributes.ReadOnly);
// fileActionSuccess = true;
//} while (sWatch.Elapsed < fileDropTimeout);
//if(!fileActionSuccess)
//{
// Trace.WriteLine("File Move or File Attribute settings failed.");
// throw new Exception();
//}
// Wait before checking for the file exists at destination.
Thread.Sleep(Convert.ToInt32(ConfigurationManager.AppSettings["ExistsAtDestination"]));
// Check if the file has actually been moved to dest.
if (!File.Exists(destPath))
{
// TODO Raise alarms here.
Trace.WriteLine("Failed to Move. File does not exist in destination. Setting ISS Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
// Notify HMI about the error type.
Trace.WriteLine("Failed to Move. File does not exist in destination. Setting ISS File Move Fault Alarm bit");
}
}
catch (Exception ex)
{
// TODO log the exception and Raise alarm?
Trace.WriteLine("Failed to Move or set attributes on file. Setting ISS Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
// Notify HMI about the error type.
Trace.WriteLine("Failed to Move or set attributes on file. Setting ISS File Move Fault Alarm bit");
}
}
else
{
Trace.WriteLine("File Move failed as the file: " + newFileName + " already exists in the destination folder");
}
}
else
{
Trace.WriteLine("File Move failed. File is not accessible");
}
}
}
/// <summary>
/// Event which is raised when a file is created in the folder which is being watched.
/// </summary>
/// <param name="sender">Object sender</param>
/// <param name="e">Event arguments</param>
private void fsw_Created(object sender, FileSystemEventArgs e)
{
lock (fswLock)
{
eventCount++;
// Start the background worker.
// Check whether if the background worker is busy if not continue.
if (!bgWorker.IsBusy)
{
bgWorker.RunWorkerAsync(e);
}
else
{
Trace.WriteLine("An attempt to use background worker for concurrent tasks has been encountered ");
// Worker thread is busy.
int busyCount = 0;
while (busyCount < 4)
{
// Wait for 500ms and try again.
Thread.Sleep(500);
if (!bgWorker.IsBusy)
{
bgWorker.RunWorkerAsync(e);
break;
}
else
{
Trace.WriteLine("An attempt to use background worker for concurrent tasks has been encountered, attempt " + busyCount);
busyCount++;
}
}
}
}
}
/// <summary>
/// Extension method to check if a file is accessible.
/// </summary>
/// <param name="fi">File Info</param>
/// <param name="mode">File Mode</param>
/// <param name="access">File Access</param>
/// <returns>Attempts three times. True if the file is accessible</returns>
private bool IsAccessible(FileInfo fi, FileMode mode, FileAccess access)
{
bool hasAccess = false;
int i = 0;
while (!hasAccess)
{
try
{
using (var fileStream = File.Open(fi.FullName, mode, access))
{
}
hasAccess = true;
i = 1;
break;
}
catch (Exception ex)
{
if (i < 4)
{
// We will swallow the exception, wait and try again.
Thread.Sleep(500);
// Explicitly set hasaccess flag.
hasAccess = false;
i++;
}
else
{
i = 0;
Trace.WriteLine("Failed to Move. File is not accessable. " + ex.ToString());
// Notify HMI
Trace.WriteLine("Failed to Move. File is not accessable.. Setting ISS Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
// Notify HMI about the error type.
Trace.WriteLine("Failed to Move. File is not accessable.. Setting ISS File Move Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFileCopyFault);
// Explicitly set hasaccess flag.
hasAccess = false;
break;
}
}
}
// return hasAccess;
return true;
}
#endregion
}
I will start watching for file as below
private void CopyFiles(bool enableCopy)
{
if (enableCopy)
{
// Initialise watcher.
Watcher.WatcherInstance.InitialiseFSW();
// Set Source.
Watcher.WatcherInstance.Source = ConfigurationManager.AppSettings["Source"];
// Set Destination.
Watcher.WatcherInstance.Destination = ConfigurationManager.AppSettings["Destination"];
//Trace.WriteLine("FTP Destination is set to:" + Watcher.WatcherInstance.Destination);
// Set Filter
Watcher.WatcherInstance.Filter = "*.bmp";
// Watch subdirectories?
Watcher.WatcherInstance.MonitorSubDirectories = true;
// Enable events.
Watcher.WatcherInstance.EnableCopyingFiles = enableCopy;
// Start Watch.
Watcher.WatcherInstance.StartWatch();
}
else
{
//if (wchr != null && wchr.FSW != null)
//{
// Stop Watcher.
Watcher.WatcherInstance.StopWatch();
// Stop copying files. As the batch is stopped.
Watcher.WatcherInstance.FSW.EnableRaisingEvents = enableCopy;
//}
}
}
These are my findings and finally seem to move files without missing any files.
Background worker is not the correct thing to use
Create a task for each file and let it do its thing.
The updated watcher class looks like this.
public sealed class Watcher
{
int eventCount = 0;
#region Private Members
/// <summary>
/// File system watcher variable.
/// </summary>
private FileSystemWatcher fsw = null;
/// <summary>
/// Destination path to use.
/// </summary>
private string destination = #"c:\temp\doc2\";
/// <summary>
/// Source path to monitor.
/// </summary>
private string source = #"c:\temp\doc\";
/// <summary>
/// Default filter type is all files.
/// </summary>
private string filter = "*.bmp";
/// <summary>
/// Monitor all sub directories in the source folder.
/// </summary>
private bool includeSubdirectories = true;
/// <summary>
/// Background worker which will Move files.
/// </summary>
private BackgroundWorker bgWorker = null;
/// <summary>
/// Toggle flag to enable copying files and vice versa.
/// </summary>
private bool enableCopyingFiles = false;
/// <summary>
/// File System watcher lock.
/// </summary>
private object fswLock = new object();
private static Watcher watcherInstance;
#endregion
#region Public Properties
public static Watcher WatcherInstance
{
get
{
if (watcherInstance == null)
{
watcherInstance = new Watcher();
}
return watcherInstance;
}
}
public string Source
{
get
{
return source;
}
set
{
source = value;
}
}
public string Destination
{
get
{
return destination;
}
set
{
destination = value;
}
}
public string Filter
{
get
{
return filter;
}
set
{
filter = value;
}
}
public bool MonitorSubDirectories
{
get
{
return includeSubdirectories;
}
set
{
includeSubdirectories = value;
}
}
public bool EnableCopyingFiles
{
get
{
return enableCopyingFiles;
}
set
{
enableCopyingFiles = value;
}
}
public FileSystemWatcher FSW
{
get
{
return fsw;
}
set
{
fsw = value;
}
}
#endregion
#region Construction
/// <summary>
/// Constructor.
/// </summary>
public Watcher()
{
// Intentionally left blank.
}
#endregion
#region Public Methods
/// <summary>
/// Method which will initialise the required
/// file system watcher objects to starting watching.
/// </summary>
public void InitialiseFSW()
{
fsw = new FileSystemWatcher();
bgWorker = new BackgroundWorker();
}
/// <summary>
/// Method which will start watching.
/// </summary>
public void StartWatch()
{
if (fsw != null)
{
fsw.Path = source;
fsw.Filter = filter;
fsw.IncludeSubdirectories = includeSubdirectories;
fsw.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastAccess;
// Setup events.
fsw.Created += fsw_Created;
// Important to set the below otherwise no events will be raised.
fsw.EnableRaisingEvents = enableCopyingFiles;
bgWorker.DoWork += bgWorker_DoWork;
}
else
{
Trace.WriteLine("File System Watcher is not initialised. Setting ISS Fault Alarm Bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
}
}
/// <summary>
/// Method to stop watch.
/// </summary>
public void StopWatch()
{
// Stop Watcher.
if (bgWorker != null)
{
bgWorker.DoWork -= bgWorker_DoWork;
}
if (fsw != null)
{
fsw.Created -= fsw_Created;
}
}
#endregion
#region Private Methods
/// <summary>
/// Method which will do the work on the background thread.
/// Currently Move files from source to destination and
/// monitor disk capacity.
/// </summary>
/// <param name="sender">Object Sender</param>
/// <param name="e">Event Arguments</param>
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
}
private void PerformFileActions(string sourcePath)
{
string extractedFileName = Path.GetFileName(sourcePath);
string newFileName = string.Empty;
newFileName = "BatchCode" + "_" + extractedFileName;
// We should now have the fully formed filename now.
// Move the file to the new location
string destPath = destination + #"\" + newFileName;
var fi = new FileInfo(sourcePath);
// TODO Check if the file exist.
if (File.Exists(Path.Combine(Source, extractedFileName)))
{
// Check if the file is accessiable.
if (IsAccessible(fi, FileMode.Open, FileAccess.Read))
{
if (!File.Exists(destPath))
{
try
{
File.Copy(sourcePath, destPath);
File.SetAttributes(destPath, FileAttributes.ReadOnly);
// Wait before checking for the file exists at destination.
Thread.Sleep(Convert.ToInt32(ConfigurationManager.AppSettings["ExistsAtDestination"]));
// Check if the file has actually been moved to dest.
if (!File.Exists(destPath))
{
// TODO Raise alarms here.
Trace.WriteLine("Failed to Move. File does not exist in destination. Setting ISS Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
// Notify HMI about the error type.
Trace.WriteLine("Failed to Move. File does not exist in destination. Setting ISS File Move Fault Alarm bit");
}
}
catch (Exception ex)
{
// TODO log the exception and Raise alarm?
Trace.WriteLine("Failed to Move or set attributes on file. Setting ISS Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
// Notify HMI about the error type.
Trace.WriteLine("Failed to Move or set attributes on file. Setting ISS File Move Fault Alarm bit");
}
}
else
{
Trace.WriteLine("File Move failed as the file: " + newFileName + " already exists in the destination folder");
}
}
else
{
Trace.WriteLine("File Move failed. File is not accessible");
}
}
}
/// <summary>
/// Event which is raised when a file is created in the folder which is being watched.
/// </summary>
/// <param name="sender">Object sender</param>
/// <param name="e">Event arguments</param>
private void fsw_Created(object sender, FileSystemEventArgs e)
{
lock (fswLock)
{
DateTime lastRead = DateTime.MinValue;
DateTime lastWriteTime = File.GetCreationTime(e.FullPath);
if (lastWriteTime != lastRead)
{
eventCount++;
// Start a new task and forget.
Task.Factory.StartNew(() => {
PerformFileActions(e.FullPath);
});
lastRead = lastWriteTime;
}
}
}
/// <summary>
/// Extension method to check if a file is accessible.
/// </summary>
/// <param name="fi">File Info</param>
/// <param name="mode">File Mode</param>
/// <param name="access">File Access</param>
/// <returns>Attempts three times. True if the file is accessible</returns>
private bool IsAccessible(FileInfo fi, FileMode mode, FileAccess access)
{
bool hasAccess = false;
int i = 0;
while (!hasAccess)
{
try
{
using (var fileStream = File.Open(fi.FullName, mode, access))
{
}
hasAccess = true;
i = 1;
break;
}
catch (Exception ex)
{
if (i < 4)
{
// We will swallow the exception, wait and try again.
Thread.Sleep(500);
// Explicitly set hasaccess flag.
hasAccess = false;
i++;
}
else
{
i = 0;
Trace.WriteLine("Failed to Move. File is not accessable. " + ex.ToString());
// Notify HMI
Trace.WriteLine("Failed to Move. File is not accessable.. Setting ISS Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFault);
// Notify HMI about the error type.
Trace.WriteLine("Failed to Move. File is not accessable.. Setting ISS File Move Fault Alarm bit");
//CommonActions.SetAlarmBit(ApplicationConstants.tag_AlarmTag, ApplicationConstants.bit_ISSFileCopyFault);
// Explicitly set hasaccess flag.
hasAccess = false;
break;
}
}
}
// return hasAccess;
return true;
}
#endregion
}

C#: Write hight speed data(50M/s) to local disk in real time

My application has to writing received UDP data to disk continuously, and the speed is about 40M/s - 50M/s. My current logic is that all data is cached in a queue and Create a BinaryWriter to write queue data to local disk. It works well most time, but sometimes the writing speed will be very slow and causes cached data too large, then my application crushes.
Is there any method to improve writing file speed and keep a stable writing speed? Or i have to adjust my hardware?
Add more info:
The hareware could sustain my application write, I ran application for 1 hours many times, application only crushes occasionally.
This is my class to write file:
class BufferedFileStorageManager : IFileStorageManager
{
private BinaryWriter writer;
private ConcurrentQueue<byte[]> buffer;
private bool isWriting;
private bool isClosed;
public void Open(string filePath)
{
buffer = new ConcurrentQueue<byte[]>();
writer = new BinaryWriter(new FileStream(filePath, FileMode.Create));
isWriting = false;
isClosed = false;
}
/// <summary>
/// Writes the specified data syncronizly. Method will return after writing finished.
/// </summary>
/// <param name="data">The data.</param>
public void Write(byte[] data)
{
writer.Write(data);
}
/// <summary>
/// Write the data asyncronizly.
/// </summary>
/// <param name="data">The data.</param>
/// <returns></returns>
public async Task WriteAsync(byte[] data)
{
if (isClosed)
{
var closedTask = Task.Factory.StartNew(() => LogManager.Warn("File is closed, skip writing data."));
await closedTask;
}
else
{
buffer.Enqueue(data);
var writeTask = Task.Factory.StartNew(WriteInQueue);
await writeTask;
}
}
private void WriteInQueue()
{
if (isWriting)
return;
isWriting = true;
byte[] data;
while (buffer.TryDequeue(out data))
{
writer.Write(data);
}
isWriting = false;
}
/// <summary>
/// Close file. Method will return until all pending data is written to file
/// </summary>
public void Close()
{
WriteInQueue();
while (isWriting)
{
Thread.Sleep(1);
}
writer.Close();
}
public async Task CloseAsync()
{
isClosed = true;
var closeTask = new Task(Close);
closeTask.Start();
await closeTask;
writer.Dispose();
writer = null;
}
}

Microsoft Office Interop Excel is not closing on windows 2008 64bit and office 2007 32bit

I created a excel helper class, to interact excel interop service.
But i noticed that the excel.exe is not getting closed on the server.
(windows 2008 64bit Japanese OS and office 2007 32bit).
When i checked with process explorer it shows tooltip like:
Path:[Error opening process]
I did excel.Quit() and Marshal.FinalReleaseComObject(_xlApp) but nothing works as expected, then tried to kill the process by processID, still not killing the process.
uint processID = 0;
GetWindowThreadProcessId((IntPtr)_hWnd, out processID);
if (processID != 0)
{
System.Diagnostics.Process.GetProcessById((int)processID).Kill();
}
Then i tried below both method, but it close all manually opened excel documents.
System.Diagnostics.Process[] procs = System.Diagnostics.Process.GetProcessesByName("EXCEL");
foreach (System.Diagnostics.Process p in procs)
{
int baseAdd = p.MainModule.BaseAddress.ToInt32();
if (baseAdd == _xlApp.Hinstance)
{
p.Kill();
}
}
System.Diagnostics.Process[] procs = System.Diagnostics.Process.GetProcessesByName("EXCEL");
foreach (System.Diagnostics.Process p in procs)
{
if (p.MainWindowTitle.Length == 0)
{
p.Kill();
}
}
Any idea about how to deal with this case ?
To get the processId is a little bit more complicated.
Try this...
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Excel = Microsoft.Office.Interop.Excel;
using Word = Microsoft.Office.Interop.Word;
/// <summary>
/// Gets an Interop.Application object and its associated processId
/// </summary>
/// <returns>Excel.Application or Word.Application depending on _isExcel</returns>
private object ApplicationFactory()
{
object application = null;
string processName = (_isExcel) ? "excel" : "winword";
Process[] beforeProcesses = null;
Process[] afterProcesses = null;
int i = 0;
while (i < 3)
{ // ourProcess = afterList - beforeList
beforeProcesses = Process.GetProcessesByName(processName);
application = (_isExcel) ? (object)new Excel.Application() : (object)new Word.Application();
afterProcesses = Process.GetProcessesByName(processName);
if ((afterProcesses.Length - beforeProcesses.Length) == 1)
{ // OK. Just a single new process
break;
}
else
{ // Two or more processes, we cannot get our processId
// therefore quit while we can and try again
if (_isExcel)
((Excel._Application)application).Quit();
else
((Word._Application)application).Quit();
int indexReferences = 1;
do
{
indexReferences = System.Runtime.InteropServices.Marshal.ReleaseComObject(application);
}
while (indexReferences > 0);
application = null;
System.Threading.Thread.Sleep(150);
i++;
}
}
if (application == null)
{
throw new ApplicationException("Unable to create Excel Application and get its processId");
}
List<int> processIdList = new List<int>(afterProcesses.Length);
foreach (Process procDesp in afterProcesses)
{
processIdList.Add(procDesp.Id);
}
foreach (Process proc in beforeProcesses)
{
processIdList.Remove(proc.Id);
}
_processId = processIdList[0];
return application;
}
/// <summary>
/// Kills _processId process if exists
/// </summary>
private void ProcessKill()
{
Process applicationProcess = null;
if (_processId != 0)
{
try
{
applicationProcess = Process.GetProcessById(_processId);
applicationProcess.Kill();
}
catch
{ // no Process with that processId
}
}
}
That said, violence is just the last resourse ;-)
You need to kill because there are some COM objects not released. (See
MS Support: Office application does not close)
Try reference your COM objects always, put them in a Stack and release them after use with
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
then, a simple application.Quit();application = null will make the trick.
Hope it helps.
EDIT:
- Always means: "whenever you use two points (_xlApp.Application., _xlWorkbook.Worksheets,...)
Add to stack means stack.push(_xlApp.Application)
Release means stack.pop()
I include mi helperStack
using System.Collections.Generic;
namespace OfficeUtils.Stack
{
/// <summary>
/// Stack of COM objects to be released
/// </summary>
public abstract class ComObjectsStack
{
private Stack<object> comObjects = new Stack<object>();
private int mark = 0;
/// <summary>
/// Releases all the remaining COM objects
/// </summary>
~ComObjectsStack()
{
if (comObjects.Count > 0)
ReleaseAll();
comObjects = null;
}
/// <summary>
/// Add a new object to the stack to be released
/// </summary>
/// <param name="obj">Nuevo objeto a liberar</param>
public void Push(object obj)
{
comObjects.Push(obj);
}
/// <summary>
/// Release the last object in the stack
/// </summary>
public void Pop()
{
Release(1);
}
/// <summary>
/// Mark for future use of ReleaseUpToMark
/// </summary>
public void Mark()
{
mark = comObjects.Count;
}
/// <summary>
/// Release up to mark
/// </summary>
/// <returns>Number of released objects</returns>
public int ReleaseUpToMark()
{
int numberObjects = comObjects.Count - mark;
if (numberObjects > 0)
{
Release(numberObjects);
return numberObjects;
}
else
{
return 0;
}
}
/// <summary>
/// Release all the objects in the stack
/// </summary>
public void ReleaseAll()
{
if (comObjects != null)
Release(comObjects.Count);
}
/// <summary>
/// Release the last numberObjects objects in stack
/// </summary>
/// <param name="numberObjects">Number of objects to release</param>
private void Release(int numberObjects)
{
for (int j = 0; j < numberObjects; j++)
{
object obj = comObjects.Pop();
int i = 1;
do
{
i = System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
}
while (i > 0);
obj = null;
}
}
}
}
I found by using the following I was able to remove Excel from the task manager:
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;
/// <summary>
/// Excel application
/// </summary>
private ApplicationClass m_xlApp = null;
/// <summary>
/// Reference to the workbook.
/// </summary>
private Workbook m_book = null;
/// <summary>
/// Reference to the worksheet.
/// </summary>
private Worksheet m_sheet = null;
/// <summary>
/// Close the workbook.
/// </summary>
public void Close()
{
if (m_book != null)
m_book.Close(Missing.Value, Missing.Value, Missing.Value);
if (m_xlApp != null)
{
m_xlApp.Workbooks.Close();
m_xlApp.Quit();
}
GC.Collect();
GC.WaitForPendingFinalizers();
// Release the objects
Marshal.FinalReleaseComObject(m_sheet);
Marshal.FinalReleaseComObject(m_book);
Marshal.FinalReleaseComObject(m_xlApp);
m_sheet = null;
m_book = null;
m_xlApp = null;
}

Loading default values from .INI Files

A bit of background:
I am currently working on an application that allows novice computer users to test their ping without having to go into the command prompt.
My application works, but I would very much like to take the application to the next level and feed in default form values from a locally stored .INI file.
I can give people the existing code, but I stress that this application works - I am just interested in advancing the code so I can read in default form values.
using System;
using System.Collections.Generic;
using System.Net;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.NetworkInformation;
namespace Ping_Application
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void pingButton_Click(object sender, EventArgs e)
{
if (pingAddressTextBox.Text != "")
{
DataTable resultsList = new DataTable();
resultsList.Columns.Add("Time", typeof(int));
resultsList.Columns.Add("Status", typeof(string));
for (int indexVariable = 1; indexVariable <= timesToPing.Value; indexVariable++)
{
string stat = "";
Ping pinger = new Ping();
PingReply reply = pinger.Send(pingAddressTextBox.Text);
if (reply.Status.ToString() != "Success")
stat = "Failed";
else
stat = reply.RoundtripTime.ToString();
pinger.Dispose();
resultsList.Rows.Add(Convert.ToInt32(reply.RoundtripTime), reply.Status.ToString());
}
resultsGrid.DataSource = resultsList;
minPing.Text = resultsList.Compute("MIN(time)", "").ToString();
maxPing.Text = resultsList.Compute("MAX(time)", "").ToString();
avgPing.Text = resultsList.Compute("AVG(time)", "").ToString();
}
else
{
MessageBox.Show("You are required to enter an address.");
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}
I am uncertain how to go about it? Where would the default.ini file be stored for my application?
Also any comments on the existing code are welcomed.
If anyone can help I would be grateful.
Many Thanks,
J
You can store your default values in ini file (i.e config file), this default file will be stored in your system D or C folder...
and from that file you can get those default values from the ini file by the following method
/// <summary>
/// This will read config.ini file and return the specific value
/// </summary>
/// <param name="MainSection">Main catergory name</param>
/// <param name="key">name of the key in main catergory</param>
/// <param name="defaultValue">if key is not in the section, then default value</param>
/// <returns></returns>
public static string getIniValue(string MainSection, string key, string defaultValue)
{
IniFile inif = new IniFile(AppDataPath() + #"\config.ini");
string value = "";
value = (inif.IniReadValue(MainSection, key, defaultValue));
return value;
}
public static string AppDataPath()
{
gCommonAppDataPath = #"c:\" + gCompanyName + #"\" + gProductName; // your config file location path
return gCommonAppDataPath;
}
make a class like this INifile.cs and place the below code in ini.cs
public class IniFile
{
public string path;
[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string section,string key,string val,string filePath);
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section,string key,string def,StringBuilder retVal,int size,string filePath);
/// <summary>
/// INIFile Constructor.
/// </summary>
/// <param name="INIPath"></param>
public IniFile(string INIPath)
{
path = INIPath;
}
/// <summary>
/// Write Data to the INI File
/// </summary>
/// <param name="Section"></param>
/// Section name
/// <param name="Key"></param>
/// Key Name
/// <param name="Value"></param>
/// Value Name
public void IniWriteValue(string Section,string Key,string Value)
{
WritePrivateProfileString(Section,Key,Value,this.path);
}
/// <summary>
/// Read Data Value From the Ini File
/// </summary>
/// <param name="Section"></param>
/// <param name="Key"></param>
/// <param name="Path"></param>
/// <returns></returns>
public string IniReadValue(string Section,string Key,string Default)
{
StringBuilder temp = new StringBuilder(255);
int i = GetPrivateProfileString(Section,Key,Default,temp,255,this.path);
return temp.ToString();
}
public void IniWriteString(string Section, string Key, string Value)
{
WritePrivateProfileString(Section, Key, Value, this.path);
}
public string IniReadString(string Section, string Key, string Default)
{
StringBuilder temp = new StringBuilder(255);
int i = GetPrivateProfileString(Section, Key, Default, temp, 255, this.path);
return temp.ToString();
}
}
and the values in config file look like this ....
[System]
GroupCode=xx
SiteCode=1234
MemberPrefix=xxx
AutoStart=no
EnablePosButton=yes....
you can get this values like by using
string a = getIniValue("System", "Sitecode", "");
you will get the value like this 1234
pls let me know if this is unclear to understand
i hope it will helps you......
If you are using Visual Studio 2005/2008 or 2010, using INI might not be a good choice. Instead you can use the off-the-shelf tools provided by Visual Studio, by clicking:
Project> Properties > Settings Tab
where you can add user settings and bind it to your GUI form. Visual Studio takes care of most of the stuff for you, and when you want to reference that variable, use the below syntax:
string LocalString=Properties.Settings.Default.YourSettings;
Moreover, a single call helps to save all the staff to archives.
Properties.Settings.Default.Save();
For even more details, please refer to the book Windows Form 2.0 Data Binding.

WatiN unreliable for repeated downloads?

WatiN seems to not handle repeated download dialogs consistently:
foreach (string file in lstFiles)
{
// continue if no download link
if (!ie.Element([search criteria]).Exists) continue;
var btnDownload = ie.Element([search criteria]);
string fullFilename = workingDir + "\\" + file;
FileDownloadHandler download = new FileDownloadHandler(fullFilename);
using (new UseDialogOnce(ie.DialogWatcher, download))
{
btnDownload.ClickNoWait();
download.WaitUntilFileDownloadDialogIsHandled(30);
download.WaitUntilDownloadCompleted(150);
ie.RemoveDialogHandler(download);
}
}
Basically, I loop through a list of filenames that I expect to be available and click the download button. This usually works, but after so many downloads (it varies, sometimes everything that's available downloads, sometimes nothing) it will hang while waiting to handle the dialog. The button's identified correctly, the download dialog appears, it just isn't detected and handled. It isn't site-specific as similar methods on other sites are also met with variable success. Anyone encounter this before and know of a resolution?
edit: Repeated downloads do not work whatsoever in Server 2008. In Win7, this happens randomly after one or more successful repeated downloads.
The problem appears because IE File Download Dialog consist of 2 windows. WatiN DialogWatcher gets all the system windows and tries to handle them in foreach loop. After handling first correct dialog window DialogWatcher gets the next window wich has the same properties and is a valid Download Dialog. DialogWatcher starts waiting until this window is visible but it closes immediately after previos window is handled.
My solution is to return from foreach loop after any dialog is handled:
#region WatiN Copyright (C) 2006-2011 Jeroen van Menen
//Copyright 2006-2011 Jeroen van Menen
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion Copyright
using System;
using System.Collections.Generic;
using System.Threading;
using WatiN.Core.Exceptions;
using WatiN.Core.Interfaces;
using WatiN.Core.Logging;
using WatiN.Core.Native.InternetExplorer;
using WatiN.Core.Native.Windows;
using WatiN.Core.UtilityClasses;
namespace WatiN.Core.DialogHandlers
{
/// <summary>
/// This class handles alert/popup dialogs. Every second it checks if a dialog
/// is shown. If so, it stores it's message in the alertQueue and closses the dialog
/// by clicking the close button in the title bar.
/// </summary>
public class DialogWatcher : IDisposable
{
private static IList<DialogWatcher> dialogWatchers = new List<DialogWatcher>();
private bool _keepRunning = true;
private readonly IList<IDialogHandler> _handlers;
private readonly Thread _watcherThread;
private bool _closeUnhandledDialogs = Settings.AutoCloseDialogs;
public Window MainWindow { get; private set; }
/// <summary>
/// Gets the dialog watcher for the specified (main) internet explorer window.
/// It creates new instance if no dialog watcher for the specified window exists.
/// </summary>
/// <param name="mainWindowHwnd">The (main) internet explorer window.</param>
/// <returns></returns>
public static DialogWatcher GetDialogWatcher(IntPtr mainWindowHwnd)
{
var window = new Window(mainWindowHwnd);
Logger.LogDebug("GetDialogWatcher mainhwnd: " + window.Hwnd + ", " + window.Title);
var toplevelWindow = window.ToplevelWindow;
Logger.LogDebug("GetDialogWatcher mainhwnd: " + toplevelWindow.Hwnd + ", " + toplevelWindow.Title);
CleanupDialogWatcherCache();
var dialogWatcher = GetDialogWatcherFromCache(toplevelWindow);
// If no dialogwatcher exists for the ieprocessid then
// create a new one, store it and return it.
if (dialogWatcher == null)
{
dialogWatcher = new DialogWatcher(toplevelWindow);
dialogWatchers.Add(dialogWatcher);
}
return dialogWatcher;
}
public static DialogWatcher GetDialogWatcherFromCache(Window mainWindow)
{
// Loop through already created dialogwatchers and
// return a dialogWatcher if one exists for the given processid
foreach (var dialogWatcher in dialogWatchers)
{
if (dialogWatcher.MainWindow.Equals(mainWindow))
{
return dialogWatcher;
}
}
return null;
}
public static void CleanupDialogWatcherCache()
{
var cleanedupDialogWatcherCache = new List<DialogWatcher>();
foreach (var dialogWatcher in dialogWatchers)
{
if (!dialogWatcher.IsRunning)
{
dialogWatcher.Dispose();
}
else
{
cleanedupDialogWatcherCache.Add(dialogWatcher);
}
}
dialogWatchers = cleanedupDialogWatcherCache;
}
/// <summary>
/// Initializes a new instance of the <see cref="DialogWatcher"/> class.
/// You are encouraged to use the Factory method <see cref="DialogWatcher.GetDialogWatcherFromCache"/>
/// instead.
/// </summary>
/// <param name="mainWindow">The main window handle of internet explorer.</param>
public DialogWatcher(Window mainWindow)
{
MainWindow = mainWindow;
_handlers = new List<IDialogHandler>();
// Create thread to watch windows
_watcherThread = new Thread(Start);
// Start the thread.
_watcherThread.Start();
}
/// <summary>
/// Increases the reference count of this DialogWatcher instance with 1.
/// </summary>
public void IncreaseReferenceCount()
{
ReferenceCount++;
}
/// <summary>
/// Decreases the reference count of this DialogWatcher instance with 1.
/// When reference count becomes zero, the Dispose method will be
/// automatically called. This method will throw an <see cref="ReferenceCountException"/>
/// if the reference count is zero.
/// </summary>
public void DecreaseReferenceCount()
{
if (ReferenceCount > 0)
{
ReferenceCount--;
}
else
{
throw new ReferenceCountException();
}
if (ReferenceCount == 0)
{
Dispose();
}
}
/// <summary>
/// Adds the specified handler.
/// </summary>
/// <param name="handler">The handler.</param>
public void Add(IDialogHandler handler)
{
lock (this)
{
_handlers.Add(handler);
}
}
/// <summary>
/// Removes the specified handler.
/// </summary>
/// <param name="handler">The handler.</param>
public void Remove(IDialogHandler handler)
{
lock (this)
{
_handlers.Remove(handler);
}
}
/// <summary>
/// Removes all instances that match <paramref name="handler"/>.
/// This method determines equality by calling Object.Equals.
/// </summary>
/// <param name="handler">The object implementing IDialogHandler.</param>
/// <example>
/// If you want to use RemoveAll with your custom dialog handler to
/// remove all instances of your dialog handler from a DialogWatcher instance,
/// you should override the Equals method in your custom dialog handler class
/// like this:
/// <code>
/// public override bool Equals(object obj)
/// {
/// if (obj == null) return false;
///
/// return (obj is YourDialogHandlerClassNameGoesHere);
/// }
/// </code>
/// You could also inherit from <see cref="BaseDialogHandler"/> instead of implementing
/// <see cref="IDialogHandler"/> in your custom dialog handler. <see cref="BaseDialogHandler"/> provides
/// overrides for Equals and GetHashCode that work with RemoveAll.
/// </example>
public void RemoveAll(IDialogHandler handler)
{
while (Contains(handler))
{
Remove(handler);
}
}
/// <summary>
/// Removes all registered dialog handlers.
/// </summary>
public void Clear()
{
lock (this)
{
_handlers.Clear();
}
}
/// <summary>
/// Determines whether this <see cref="DialogWatcher"/> contains the specified dialog handler.
/// </summary>
/// <param name="handler">The dialog handler.</param>
/// <returns>
/// <c>true</c> if [contains] [the specified handler]; otherwise, <c>false</c>.
/// </returns>
public bool Contains(IDialogHandler handler)
{
lock (this)
{
return _handlers.Contains(handler);
}
}
/// <summary>
/// Gets the count of registered dialog handlers.
/// </summary>
/// <value>The count.</value>
public int Count
{
get
{
lock (this)
{
return _handlers.Count;
}
}
}
/// <summary>
/// Gets or sets a value indicating whether unhandled dialogs should be closed automaticaly.
/// The initial value is set to the value of <cref name="Settings.AutoCloseDialogs" />.
/// </summary>
/// <value>
/// <c>true</c> if unhandled dialogs should be closed automaticaly; otherwise, <c>false</c>.
/// </value>
public bool CloseUnhandledDialogs
{
get
{
lock (this)
{
return _closeUnhandledDialogs;
}
}
set
{
lock (this)
{
_closeUnhandledDialogs = value;
}
}
}
/// <summary>
/// Gets the (main) internet explorer window handle this dialog watcher watches.
/// </summary>
/// <value>The process id.</value>
public IntPtr MainWindowHwnd
{
get { return MainWindow.Hwnd; }
}
/// <summary>
/// Called by the constructor to start watching popups
/// on a separate thread.
/// </summary>
private void Start()
{
while (_keepRunning)
{
if (MainWindow.Exists())
{
var winEnumerator = new WindowsEnumerator();
var windows = winEnumerator.GetWindows(win => true);
foreach (var window in windows)
{
if (!_keepRunning) return;
if(HandleWindow(window))
break;
}
// Keep DialogWatcher responsive during 1 second sleep period
var count = 0;
while (_keepRunning && count < 5)
{
Thread.Sleep(200);
count++;
}
}
else
{
_keepRunning = false;
}
}
}
public bool IsRunning
{
get { return _watcherThread.IsAlive; }
}
public int ReferenceCount { get; private set; }
/// <summary>
/// Get the last stored exception thrown by a dialog handler while
/// calling the <see cref="IDialogHandler.HandleDialog"/> method of the
/// dialog handler.
/// </summary>
/// <value>The last exception.</value>
public Exception LastException { get; private set; }
/// <summary>
/// If the window is a dialog and visible, it will be passed to
/// the registered dialog handlers. I none if these can handle
/// it, it will be closed if <see cref="CloseUnhandledDialogs"/>
/// is <c>true</c>.
/// </summary>
/// <param name="window">The window.</param>
/// <returns>
/// <c>true</c> if dialog is handled by one of handlers; otherwise, <c>false</c>.
/// </returns>
public bool HandleWindow(Window window)
{
if (!window.IsDialog()) return false;
if (!HasDialogSameProcessNameAsBrowserWindow(window)) return false;
// This is needed otherwise the window Style will return a "wrong" result.
WaitUntilVisibleOrTimeOut(window);
// Lock the thread and see if a handler will handle
// this dialog window
lock (this)
{
foreach (var dialogHandler in _handlers)
{
try
{
if (dialogHandler.CanHandleDialog(window, MainWindow.Hwnd))
{
if (dialogHandler.HandleDialog(window)) return true;
}
}
catch (Exception e)
{
LastException = e;
Logger.LogAction((LogFunction log) => { log("Exception was thrown while DialogWatcher called HandleDialog: {0}",e.ToString()); });
}
}
// If no handler handled the dialog, see if the dialog
// should be closed automatically.
if (!CloseUnhandledDialogs || !MainWindow.Equals(window.ToplevelWindow)) return false;
Logger.LogAction((LogFunction log) => { log("Auto closing dialog with title: '{0}', text: {1}, style: ", window.Title, window.Message, window.StyleInHex); });
window.ForceClose();
}
return false;
}
private bool HasDialogSameProcessNameAsBrowserWindow(Window window)
{
var comparer = new Comparers.StringComparer(window.ProcessName, true);
return comparer.Compare(MainWindow.ProcessName);
}
private static void WaitUntilVisibleOrTimeOut(Window window)
{
// Wait untill window is visible so all properties
// of the window class (like Style and StyleInHex)
// will return valid values.
var tryActionUntilTimeOut = new TryFuncUntilTimeOut(TimeSpan.FromSeconds(Settings.WaitForCompleteTimeOut));
var success = tryActionUntilTimeOut.Try(() => window.Visible);
if (!success)
{
Logger.LogAction((LogFunction log) => { log("Dialog with title '{0}' not visible after {1} seconds.", window.Title, Settings.WaitForCompleteTimeOut); });
}
}
#region IDisposable Members
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or
/// resetting unmanaged resources.
/// </summary>
public void Dispose()
{
lock (this)
{
_keepRunning = false;
}
if (IsRunning)
{
_watcherThread.Join();
}
Clear();
}
#endregion
}
}
My team ran into this as well while automating IE8 with WatiN. The problem seems to be with IE, possibly doing some time consuming house-cleaning. The work-around we ultimately used was to invoke a new instance of IE within the outer loop, disposing of it each iteration and then waiting for 5 seconds for whatever was going on in the background to resolve. It was a hack but got the job done.
foreach (var file in lstFiles)
{
string fullFilename = workingDir + "\\" + file;
using (var browser = new IE(fullFilename))
{
//page manipulations...
FileDownloadHandler download = new FileDownloadHandler(fullFilename);
using (new UseDialogOnce(browser.DialogWatcher, download))
{ //lnkFile.ClickNoWait();
download.WaitUntilFileDownloadDialogIsHandled(15);
download.WaitUntilDownloadCompleted(150);
}
}
Thread.Sleep(5000);
}

Categories