IOException - How to gain file name?

  • Thread starter Thread starter Axel Dahmen
  • Start date Start date
A

Axel Dahmen

Hi,

in our software we're using a couple of File.Copy() or File.Move() statements. The system is supposed to throw an exception if a file already exists. We log the exception into a file then.

Unfortunately, the IOException doesn't seem to provide any means to extract the destination file name that caused the exception. The message only states "Cannot create a file when that file already exists."

Does anyone know a way to retrieve the file name which caused the exception? I don't want to manually track the currently used file name. I don't like the idea to extend the System.IO.File class and IOException class either... Well, as long as I don't need to...

Any help is appreciated!

Best regards,
www.axeldahmen.de
Axel Dahmen
 
Axel said:
in our software we're using a couple of File.Copy() or File.Move()
statements. The system is supposed to throw an exception if a file
already exists. We log the exception into a file then.

Unfortunately, the IOException doesn't seem to provide any means to
extract the destination file name that caused the exception. The
message only states "Cannot create a file when that file already
exists."

Does anyone know a way to retrieve the file name which caused the
exception? I don't want to manually track the currently used file
name. I don't like the idea to extend the System.IO.File class and
IOException class either... Well, as long as I don't need to...

You don't need to (and should not) rely on an exception being raised for
something you can easily check for in the first place:

If Not(File.Exists(dest)) then
File.Copy(src, dest)
Else
' log dest to file
End If

That way you'll only get an IOException if an I/O error occurred.

Andrew
 
You don't need to (and should not) rely on an exception being raised for
something you can easily check for in the first place:

If Not(File.Exists(dest)) then
File.Copy(src, dest)
Else
' log dest to file
End If

That way you'll only get an IOException if an I/O error occurred.

Andrew

While that will work 99.9% of the time, it is possible that the file
could disappear between the File.Exists test and the copy. Also I
suspect there are cases where File.Exists works but the user does not
have permission to open the file for reading.
 
I agree. And that's exactly what the exception is made for.

So I don't want to explicitly avoid such exception but to handle it gracefully, giving a trace leading to the problem file whenever this may happen.

Best regards,
www.axeldahmen.de
Axel Dahmen



----------
 
Hi Axel,

There is no way to extract the destination file name from the IOException if
the file already exists when we call File.Move.

This can be confirmed by digging into the File class's code.

The File.Move class calls MoveFile function in kernel32.dll internally, so
actually the Move method itself doesn't do the Move job. When the MoveFile
function returns, code in Move method checks the return value, if the return
value is zero, which means an error occurred during move, it then calls an
internal helper method to get the last win32 error, converts it to .NET
exception and throws it:

if (!Win32Native.MoveFile(fullPathInternal, dst))
{
__Error.WinIOError();
}

The helper method WinIOError has an overload which can take two parameters,
an error code and a path. But as we can see here, the Move method somehow
doesn't pass in any information to the WinIOError, which causes WinIOError
to pass a string.Empty as the file path:

internal static void WinIOError()
{
WinIOError(Marshal.GetLastWin32Error(), string.Empty);
}

So the information you need from the exception is lost here.

Being different from File.Move, the File.Copy method internally call the
WinIOError this way to throw an Exception:

__Error.WinIOError(errorCode, maybeFullPath);

And if we do something like this (if the target file already exists):

File.Copy(@"C:\Users\jiewan\Desktop\snippet.txt", @"D:\snippet.txt", false);

We'll get an IOException with the following message: The file
'D:\snippet.txt' already exists.

I understand this is inconvenient for your scenario, however it seems to me
that we have to track the file current being used for the log:

try
{
File.Move(srcFile, destPath);
}
catch (IOException ioEx)
{
Log(string.Format("{0} already exists.", destPath));
}

Please kindly let me know if this clarifies your question on File.Move
method.

Regards,

Jie Wang ([email protected], remove 'online.')

Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).
==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: MSDN Managed Newsgroup support offering is for non-urgent issues where
an initial response from the community or a Microsoft Support Engineer
within 2 business days is acceptable. Please note that each follow up
response may take approximately 2 business days as the support professional
working with you may need further investigation to reach the most efficient
resolution. The offering is not appropriate for situations that require
urgent, real-time or phone-based interactions. Issues of this nature are
best handled working with a dedicated Microsoft Support Engineer by
contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Shouldn;t the File.Copy()/File.Move() methods takes "SourcePath" and
"DestinatePath" parameters, should they? So, when your code call these
methoeds, you know what the destination path is. So, if the exception bubles
up to an outer wrapping procedure, you can easily re-wrap the exception at
the method-calling level to incorporate the destination path and rethrow the
exception:


try
{
FileCopy(source, destination);
}
catch (IOException ex)
{
//re-throw the exception with destination file name added
//You may create your own custom Exception, insteadof using generic
ApplicationException
throw new ApplicationException("Copying \"" + destination "\" failed: "
+ ex.Message);
}
catch(Exception e)
{
throw new ApplicationException("Copying \"" + destination "\" failed: "
+ ex.Message);
}

Then, in your Excpetion logging level, the re-thrown exceptpion will be
caught with destination file information included.


Hi,

in our software we're using a couple of File.Copy() or File.Move()
statements. The system is supposed to throw an exception if a file already
exists. We log the exception into a file then.

Unfortunately, the IOException doesn't seem to provide any means to extract
the destination file name that caused the exception. The message only states
"Cannot create a file when that file already exists."

Does anyone know a way to retrieve the file name which caused the exception?
I don't want to manually track the currently used file name. I don't like
the idea to extend the System.IO.File class and IOException class either...
Well, as long as I don't need to...

Any help is appreciated!

Best regards,
www.axeldahmen.de
Axel Dahmen
 
Why do you need IOException to tell you what the destination file was? You
should already have it.

string sourcePath = "Some File";
string destPath = "Another Location";
try {
File.Copy(sourcePath, destPath);
} catch (IOException e)
{
Debug.WriteLine("Error copying {0} to {1}", sourcePath, destPath);
}

Andrew Faust
 
Why do you need IOException to tell you what the destination file was? You
should already have it.

Anywhere within the program? File.Copy()/File.Move() isn't just at one single place. It's used anywhere. Our global try-catch is used to detect program errors and invalid program conditions. The fun part of exception programming is that you *don't* anticipate any exception but handle them at *one single* place - gracefully.

Regards,
www.axeldahmen.de
Axel Dahmen
 
Thanks, Norman,

but doing this would contradict using exceptions at all. As I've also replied to Andrew, the fun part of exception programming is that you *don't* anticipate any exception but handle them at *one single* place - gracefully.

We're using a factory that creates classes and implements a global try-catch to be able to log any errors that occurred. Adding a manual try-catch to each and every system function call would not be what's usually known as structured programming.

So I guess we'll have to make our way in creating our own File class, aggregating the original one and extending its functions by local try-catch-rethrow wrappers. :/

Thanks for all your replies, guys.
www.axeldahmen.de
Axel Dahmen
 
Thanks, Jie, for your informative response.

I've already responded to Andrew and Norman, and both my reactions apply to your answer as well, I guess..

So it seems we'll have to make our way in creating our own File class, aggregating the original one and extending its functions by local try-catch-rethrow wrappers. :/

Best regards,
www.axeldahmen.de
Axel Dahmen
 
Axel,

The fine name is always in the method.
It becomes something different as you don't catch the errors where it has to
be, but do a kind of general catch.
Then you can of course give only a general answer like "There has been an
errror"

Cor

Axel Dahmen said:
Why do you need IOException to tell you what the destination file was? You
should already have it.

Anywhere within the program? File.Copy()/File.Move() isn't just at one
single place. It's used anywhere. Our global try-catch is used to detect
program errors and invalid program conditions. The fun part of exception
programming is that you *don't* anticipate any exception but handle them at
*one single* place - gracefully.

Regards,
www.axeldahmen.de
Axel Dahmen
 
Axel,

Do you make a general copy class as well, without the file name supplied
that has to be copied as well.

All your replies fits to that as well.

Cor

Thanks, Jie, for your informative response.

I've already responded to Andrew and Norman, and both my reactions apply to
your answer as well, I guess..

So it seems we'll have to make our way in creating our own File class,
aggregating the original one and extending its functions by local
try-catch-rethrow wrappers. :/

Best regards,
www.axeldahmen.de
Axel Dahmen
 
The fine name is always in the method.
It becomes something different as you don't catch the errors where it has to
be, but do a kind of general catch.
Then you can of course give only a general answer like "There has been an
errror"

Hi Cor,

I see you point, but I'm afraid I have to disagree to your statement "where it has to be"... Putting a try-catch in the method itself is the opposite of a proper place to put try-catch statements. According to structured programming principles try-catch statements have been invented for method "clients" to catch method "server" errors. Catching an error is usually be done far away from where the error occurs. So putting a try-catch around each and every system method call is usually not what's been the idea behind exception handling. If they wanted to follow your path, they'd have stuck with HRESULT codes.

Best regards,
www.axeldahmen.de
Axel Dahmen
 
Hi, Cor,

I'd create a class similar to this (don't know if I can actually derive from any of the system classes, but here's just an idea yet):

namespace MyNS
{
public class IOException : System.IO.IOException
{
public string FileName;

public IOException(System.IO.IOException baseEx, string string fileName) : base(baseEx)
{
FileName = fileName;
}
}


public class File : System.IO.File
{
public static void Move(string sourceFileName, string destFileName)
{
try
{
base.Move(sourceFileName, destFileName);
}
catch (IOException io)
{
throw new IOExeption(io, destFileName);
}
}
}
}

In our program we would subsequently replace System.IO.File with MyNS.File then...

Regards,
www.axeldahmen.de
Axel Dahmen
 
Since you want to have a more generic try...catch..., that is why I re-throw
the exception with more descriptive message. If you want specific exception
information, you can only get by catching that specific exception. You can
decide where you really want to catch an exception, but you need to do
something to get the interexception's message. So, if you do not use the way
I suggested, you can drill down into the multiple level deep into
InnerException to find out. In your case, even you drilled down to the
IOException caused by FileCopy(), you still do not have the desination file
name, if you do not do as I suggested.


Thanks, Norman,

but doing this would contradict using exceptions at all. As I've also
replied to Andrew, the fun part of exception programming is that you *don't*
anticipate any exception but handle them at *one single* place - gracefully.

We're using a factory that creates classes and implements a global try-catch
to be able to log any errors that occurred. Adding a manual try-catch to
each and every system function call would not be what's usually known as
structured programming.

So I guess we'll have to make our way in creating our own File class,
aggregating the original one and extending its functions by local
try-catch-rethrow wrappers. :/

Thanks for all your replies, guys.
www.axeldahmen.de
Axel Dahmen



-----------
 
Axel Dahmen said:
Hi Cor,

I see you point, but I'm afraid I have to disagree to your statement
"where it has to be"... Putting a try-catch in the method itself is the
opposite > of a proper place to put try-catch statements. According to
structured programming principles try-catch statements have been invented
for
method "clients" to catch method "server" errors. Catching an error is
usually be done far away from where the error occurs. So putting a try-
catch around each and every system method call is usually not what's been
the idea behind exception handling. If they wanted to follow your
path, they'd have stuck with HRESULT codes.

I'm afraid I have to disagree. Try..Catch was invented to provide an error
handling mechanism. It's up to the application designer on how it best fits
their applications purposes. In some cases the best method may be to not
catch the exception where it's generated, but let it get thrown up. In other
cases it may be to catch and handle immediately. In yet more cases you may
want to catch the exception then rethrow it. You may also want to capture
the exception and wrap it in a new exception then throw that.

It sounds like the way you have your application designed, the last case may
be your best bet. I'd recommend catching the exception, and then wrapping it
in a new exception that contains the data you need then throw that.
Something like this:

public class CustomIOException : IOException
{
public string SourcePath { get; set; }
public string DestPath { get; set; }
public CustomIOException(string sourcePath, string destPath, IOException
e) : base(e)
{
SourcePath = sourcePath;
DestPath = destPath;
}
}

......

string sourcePath =""
string destPath = "";

try
{
File.Copy(sourcePath, destPath);
} catch (IOException e)
{
throw new CustomIOException(sourcePath, destPath, e);
}

Andrew Faust
 
Hi Axel,

The System.IO.File class is a static class (sealed class in .NET 1.1) which
cannot be inherited. So you might want to make your own File class without
inheriting the System.IO.File class, but you can call System.IO.File inside
your own class and wrap up the "catch original and rethrow" job.

I think this is the easist and cleanest way to go.

Regards,

Jie Wang ([email protected], remove 'online.')

Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 2 business days is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions. Issues of this
nature are best handled working with a dedicated Microsoft Support Engineer
by contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
public class CustomIOException : IOException
{
public string SourcePath { get; set; }
public string DestPath { get; set; }
public CustomIOException(string sourcePath, string destPath, IOException
e) : base(e)
{
SourcePath = sourcePath;
DestPath = destPath;
}
}

.....

string sourcePath =""
string destPath = "";

try
{
File.Copy(sourcePath, destPath);
} catch (IOException e)
{
throw new CustomIOException(sourcePath, destPath, e);
}
-----------

OK, I see we don't get any further here.

Just to finish this discussion from my side: So if I understand your and Jie's suggestion correctly, then actually you suggest the following:

class A
{
public void someFunc1()
{
string sourcePath ="", destPath = "";

try
{
File.Copy(sourcePath, destPath);
} catch (IOException e)
{
throw new CustomIOException(sourcePath, destPath, e);
}
}
}

class B
{
public void someFunc2()
{
string sourcePath ="", destPath = "";

try
{
File.Copy(sourcePath, destPath);
} catch (IOException e)
{
throw new CustomIOException(sourcePath, destPath, e);
}
}
}

class C
{
public void someFunc3()
{
string sourcePath ="", destPath = "";

try
{
File.Copy(sourcePath, destPath);
} catch (IOException e)
{
throw new CustomIOException(sourcePath, destPath, e);
}
}
}

class D
{
public void someFunc4()
{
string sourcePath ="", destPath = "";

try
{
File.Copy(sourcePath, destPath);
} catch (IOException e)
{
throw new CustomIOException(sourcePath, destPath, e);
}
}
}

class E
{
public void someFunc5()
{
string sourcePath ="", destPath = "";

try
{
File.Copy(sourcePath, destPath);
} catch (IOException e)
{
throw new CustomIOException(sourcePath, destPath, e);
}
}
}

and and and .........

instead of just:

class A
{
public void someFunc1()
{
MyFile.Copy(sourcePath, destPath);
}
}

class B
{
public void someFunc2()
{
MyFile.Copy(sourcePath, destPath);
}
}

class C
{
public void someFunc3()
{
MyFile.Copy(sourcePath, destPath);
}
}

class D
{
public void someFunc4()
{
MyFile.Copy(sourcePath, destPath);
}
}

class E
{
public void someFunc5()
{
MyFile.Copy(sourcePath, destPath);
}
}

Come on, guys, where's your (sense of) object oriented design?

www.axeldahmen.de
Axel Dahmen
 
Hi Axel,

I think there might be some misunderstanding. We were focusing on how to
get the filename when an IOException occurs. Your original post says "I
don't like the idea to extend the System.IO.File class and IOException
class either... Well, as long as I don't need to...", however, now we know
this is very hard to do, we have to extend the IOException class and use
try...catch to get the original IOException and rethrow a extended version
of IOException with the filename. Until this point, I was focusing on the
single method itself.

Widening the view to the overall design of your class library or
application, I totally agree with you that we need to reuse the code
instead of spreading the same piece of code all over the classes.

So the possible solutions would be:

1. Create a base class which has a Copy method for class A to E to inherit.
Or...
2. Create a helper class which implements the Copy method, so class A to E
can call the helper.

I believe everyone in this thread knows how to make OO design, but just
like I said, our different focuses - that's natural because we just saw a
leaf of your project but you certainly got the whole forest - caused a
little bit misunderstanding.

Anyway, I'm glad to see now the solution to the original problem is settled.

And thanks to everyone sharing ideas in this thread.

Best regards,

Jie Wang ([email protected], remove 'online.')

Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 2 business days is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions. Issues of this
nature are best handled working with a dedicated Microsoft Support Engineer
by contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Come on, guys, where's your (sense of) object oriented design?

You don't need to start getting rude. I disagree with your earlier
statements of how exception handling is "supposed" to be done. Rather than
starting an argument about it, I provided an implementation that I felt
would meet your needs based on my limited knowledge of your application
architecture.

If you'll think about my recommendation of using a custom exception class
you'll see that it is in no way contradictory to your MyFile.Copy method. If
you have your MyFile.Copy method throws the new custom exception then you
only need to write my Try...catch block once. This gives you the benefit of
reusing code and still having an exception which can provide you more
detailed information anywhere you happen to need it.

I hope you find an implementation that will work out for you. However, I'm
done with this thread. I have better things to do than be insulted by
someone I'm trying to help.

Andrew Faust
 
Back
Top