1

I'm working on an application that has to make specific decisions based on files that are placed into a folder being watched by a file watcher.

Part of this decision making process involves renaming files before moving them off to another folder to be processed.

Since I'm working with files of all different sizes I created an object that checks the file in a seperate thread to verify that it is "available" and when it is it fires an event.

When I run the rename code from inside this available event it works.

public void RenameFile_Test()
{
    string psFilePath = @"C:\File1.xlsx";
    tgt_File target = new FileObject(psFilePath);
    target.FileAvailable += new FileEventHandler(OnFileAvailable);
    target.FileUnAvailable += new FileEventHandler(OnFileUnavailable);
}

private void OnFileAvailable(object source, FileEventArgs e)
{
    ((FileObject)source).RenameFile(@"C:\File2.xlsx");
}

The problem I'm running into is that when the extensions are different from the source file and the rename to file I am making a call to a conversion factory that returns a factory object based on the type of conversion and then converts the file accordingly before doing the rename. When I run that particular piece of code in unit test it works, the factory object is returned, and the conversion happens correctly.

But when I run it within the process I get up to the...

        moExcelApp = new Application();

part of converting an .xls or .xlsx to a .csv and i get a "Thread was being Aborted" error.

Any thoughts?

Update:

There is a bit more information and a bit of map of how the application works currently.

  • Client Application running FSW
  • On File Created event Creates a FileObject passing in the path of the file.
  • On construction the file is validated: if file exists is true then,

    Thread toAvailableCheck = new Thread(new ThreadStart(AvailableCheck));
    toAvailableCheck.Start();
    
  • The AvailableCheck Method repeatedly tries to open a streamreader to the file until the reader is either created or the number of attempts times out. If the reader is opened, it fires the FileAvailable event, if not it fires the FileUnAvailable event, passing back itself in the event.

  • The client application is wired to catch those events from inside the Oncreated event of the FSW.

  • the OnFileAvailable method then calls the rename functionality which contains the excel interop call.
  • If the file is being renamed (not converted, extensions stay the same) it does a move to change the name from the old file name to the new, and if its a conversion it runs a conversion factory object which returns the correct type of conversion based on the extensions of the source file and the destination file name.
  • If it is a simple rename it works w/o a problem. If its a conversion (which is the XLS to CSV object that is returned as a part of the factory) the very first thing it does is create a new application object. That is where the application bombs.

When i test the factory and conversion/rename process outside of the thread and in its own unit test the process works w/o a problem.

Update:

I tested the Excel Interop inside a thread by doing this:

[TestMethod()]
public void ExcelInteropTest()
{
    Thread toExcelInteropThreadTest = new Thread(new ThreadStart(Instantiate_App));
    toExcelInteropThreadTest.Start();
}

private void Instantiate_App()
{
    Application moExcelApp = new Application();
    moExcelApp.Quit();
}

And on the line where the application is instatntiated I got the 'A first chance exception of type 'System.Threading.ThreadAbortException' error.

So I added;

toExcelInteropThreadTest.SetApartmentState(ApartmentState.MTA);

after the thread instantiation and before the thread start call and still got the same error. I'm getting the notion that I'm going to have to reconsider the design.

Patrick
  • 7,512
  • 7
  • 39
  • 50

1 Answers1

2

Somebody is calling Thread.Abort(). That could be the CLR, trying to shut your program down because of an unhandled exception. The reason you might see the ThreadAbortException instead of the real exception is because you are using a COM server (like Excel) in a thread that isn't a single threaded apartment. Check the docs for Thread.SetApartmentState(). Threadpool threads like the ones that FileSystemWatcher uses to raise events cannot be STA.

Also check the Output window for debugger notifications and use Debug + Exceptions, Thrown box to make the debugger stop on the first exception.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • There are no unhandled exceptions. there are try-catches around everything. the output from the debug shows "A first chance exception of type 'System.Threading.ThreadAbortException' occured.' – Patrick Dec 21 '10 at 13:09
  • Now my question is... since I'm doing ALL of the work inside an event that is called by the file system watcher, are you saying that I won't be able to load the excel application because it is COM because the default apartment state for the FSW is MTA? – Patrick Dec 21 '10 at 13:10
  • Excel requires an STA. If you don't provide one, COM will create a thread for it to give it a hospitable home. You cannot catch any exceptions in that thread, you didn't create it. – Hans Passant Dec 21 '10 at 13:33
  • so the issue itself then is probably because i'm trying to create a new thread inside another thread based on the events? I'm going to update my question to show the flow of the application threads and events. – Patrick Dec 21 '10 at 13:43
  • I take my previous comment back... It's not a problem with the threads itself... its a problem with the Microsoft Interop COM object. It simply cannot be called inside another thread? – Patrick Dec 21 '10 at 14:58
  • COM and Excel support this. But it can have side-effects. I think you found one. – Hans Passant Dec 21 '10 at 15:11
  • So considering all of this... If i create a queue of objects and then process the objects in the queue based on flags that are set, and I'm checking these items based on a timer event... I would still run into the exact same issue? – Patrick Dec 21 '10 at 15:59
  • It is always a good idea to use a queue, a file often is still locked when FSW generates an event. That queue could be emptied by an STA thread. Check this answer for the code you need: http://stackoverflow.com/questions/4269800/c-webbrowser-control-in-a-new-thread/4271581#4271581 – Hans Passant Dec 21 '10 at 16:22
  • I may have found the solution.... if am able to run the process from a timer event! so what I can do is build a queue object... add each file object to the queue as they are created, then use a timer to check each object in the queue and do something to them based on flags... thanks for putting the ideas in my head Hans! – Patrick Dec 21 '10 at 16:24
  • and as to the "a file often is still locked when FSW generates and event" yes, that is why i created my own custom object... the first thing it does is runs a threaded call to a method called isAvailable, which checks to make sure that the file can be opened... if it can it marks the object as available and throws an event... – Patrick Dec 21 '10 at 16:25
  • the queue will allow me to go back and recycle through file objects that are marked as unavailable and check their availability again as well. then when the files are available I can determine what the next step is... rename, conversion, move, delete, etc... – Patrick Dec 21 '10 at 16:26