SaveFileDialog locks the file?

P

Peter Duniho

I searched using Google, on the web and in the newsgroups, and found nothing
on this topic. Hopefully that means I just don't understand what I'm
supposed to be doing here. :)

The problem:

I am trying to use the SaveFileDialog class to get a filename, which is
subsequently opened for writing (write access, read sharing, but using
read/write sharing doesn't make the problem go away anyway). Sometimes, on
the statement where I actually open the file (using the FileName string from
the SaveFileDialog instance) I get an IOException complaining that the file
is in use by another process (says it can't open "...because it is being
used by another process").

Because this error occurs more often the more quickly I click through the
UI, it suggested strongly that there was some uncollected resource somewhere
causing problems. However, I have been unable to fix the problem using the
mechanisms I know of for addressing those issues ("using" or calling
GC.Collect(), for example).

I have narrowed the problem down to the SaveFileDialog itself. That is, if
I instead hard-code the filename as a string and repeatedly run the save
command, I never get the error, no matter how quickly I resave the file over
and over. If, however, I include the SaveFileDialog, the error occurs quite
often, sometimes even on the very first access of the file during a given
program execution.

Not that I think this sort of thing should be necessary, but I've tried two
different possible work-arounds, neither of which fixed the problem:

string szFile = null;

using (SaveFileDialog sfd = new SaveFileDialog())
{
if (sfd.ShowDialog(this) == DialogResult.OK)
{
szFile = sfd.FileName;
}
}

if (szFile != null)
{
// open file here...this is where the exception occurs
}

And, alternatively:

SaveFileDialog sfd = new SaveFileDialog();

if (sfd.ShowDialog(this) == DialogResult.OK)
{
string szFile = sfd.FileName;

sfd = null;
GC.Collect();

// open file here...this is where the exception occurs
}

So, any ideas? Has anyone seen this before? As I mentioned, if I don't use
the SaveFileDialog and instead just set the filename variable directly in
code, the problem completely disappears. It seems obvious to me that the
SaveFileDialog is somehow keeping the file open, but I don't understand why,
nor do I understand how I can get it to release the file so that I can open
it myself.

Much thanks,
Pete
 
B

Ben Voigt

Peter Duniho said:
I searched using Google, on the web and in the newsgroups, and found
nothing on this topic. Hopefully that means I just don't understand what
I'm supposed to be doing here. :)

The problem:

I am trying to use the SaveFileDialog class to get a filename, which is
subsequently opened for writing (write access, read sharing, but using
read/write sharing doesn't make the problem go away anyway). Sometimes,
on the statement where I actually open the file (using the FileName string
from the SaveFileDialog instance) I get an IOException complaining that
the file is in use by another process (says it can't open "...because it
is being used by another process").
Tried using SaveFileDialog.OpenFile instead of the FileName property?
 
P

Peter Duniho

Ben Voigt said:
Tried using SaveFileDialog.OpenFile instead of the FileName property?

I may look at that just to see whether it also has the problem, so I can
provide a more complete bug report. But the OpenFile method doesn't provide
any parameters for specifying file access, buffer size, etc. and so is
unsuitable in my particular situation (ie, the default open behavior isn't
sufficient). Even if it makes the problem go away, it's not a solution for
this particular case. :(

Thanks,
Pete
 
E

Emby

Hi Pete,

Sorry, I don't have a solution, but I will confirm that I've seen this
behavior. I've not correlated it to the SF dialog; it has always seemed
infrequent and inconsistent. I just figured it was yet another of VS8's many
little bugs ... :(
... so where is that SP1??

I'll try "clicking faster" and see what happens.

Emby
 
P

Peter Duniho

Emby said:
Hi Pete,

Sorry, I don't have a solution, but I will confirm that I've seen this
behavior. I've not correlated it to the SF dialog; it has always seemed
infrequent and inconsistent. I just figured it was yet another of VS8's
many little bugs ... :(
... so where is that SP1??

I'll try "clicking faster" and see what happens.

Well, I'm glad to hear I'm not entirely insane. :)

I have been doing a little more investigation. I don't think I've come up
with too much in the way of insight, but here's what new I've learned about
the issue:

* A suitable workaround appears to be to wrap a try/catch in a loop with
a counter, and to repeatedly attempt to open the file. I call
Thread.Sleep(1000) for each failure, and only try five times. Doing this,
about half the time it would take two tries to open the file, but never more
than that. It's ugly, but it does work.

* Another suitable workaround appears to be to write to a temp file
first, and then move the file over. This is a common enough technique
generally and one I was implementing for other reasons anyway, and it
appears to allow for enough time for the file to become unlocked before you
have to delete it (so that the rename/move can happen). Note: this doesn't
really avoid the problem per se...it just shifts things around enough that
it becomes a lot more rare (ie, this isn't a suitable workaround for those
"must work absolutely 100% of the time" situations).

On that second item, I will also note that I was in fact still able to
reproduce a problem once. However, it occurred in an even more bizarre way:
I got an "Access Denied" exception, but the target file (which I was
overwriting) got deleted anyway, and the File.Move failed. For all I know,
this was an entirely different race condition bug, in which the delete did
occur but the OS didn't finish it in time for the File.Move call to succeed.

I'm not doing any async i/o, so there *shouldn't* be any race conditions at
all. But it does appear that even though I'm doing everything sequentially
and synchronously, there is other stuff going on behind my back (maybe
within .NET, and maybe just in Windows...I can't tell the difference with
the information I have).

Finally, one interesting tidbit:

* The file that I'm working with for testing is a DivX AVI file. I've
noticed that every time I run the SaveFileDialog, the little DivX notify
icon shows up. I suspect this happens as the SaveFileDialog (or more
likely, the built-in Windows common file dialog...it'd be kind of silly for
..NET to completely reinvent that wheel rather than just calling Windows to
do it) generates thumbnails to present in the dialog. I have a suspicion
that this is somehow related to whatever is causing the file to be locked.

Of course, it is .NET that is calling the common dialog, and it is the
common dialog that is running the multimedia code that calls DivX to
generate a thumbnail. I have no doubt that a lot of finger-pointing may be
in the offing when it comes to trying to actually *fix* the problem. But
hopefully, the various teams involved can coordinate and come up with a
solution.

Thanks for your input,
Pete
 
W

Willy Denoyette [MVP]

|I searched using Google, on the web and in the newsgroups, and found
nothing
| on this topic. Hopefully that means I just don't understand what I'm
| supposed to be doing here. :)
|
| The problem:
|
| I am trying to use the SaveFileDialog class to get a filename, which is
| subsequently opened for writing (write access, read sharing, but using
| read/write sharing doesn't make the problem go away anyway). Sometimes,
on
| the statement where I actually open the file (using the FileName string
from
| the SaveFileDialog instance) I get an IOException complaining that the
file
| is in use by another process (says it can't open "...because it is being
| used by another process").
|
| Because this error occurs more often the more quickly I click through the
| UI, it suggested strongly that there was some uncollected resource
somewhere
| causing problems. However, I have been unable to fix the problem using
the
| mechanisms I know of for addressing those issues ("using" or calling
| GC.Collect(), for example).
|
| I have narrowed the problem down to the SaveFileDialog itself. That is,
if
| I instead hard-code the filename as a string and repeatedly run the save
| command, I never get the error, no matter how quickly I resave the file
over
| and over. If, however, I include the SaveFileDialog, the error occurs
quite
| often, sometimes even on the very first access of the file during a given
| program execution.
|
| Not that I think this sort of thing should be necessary, but I've tried
two
| different possible work-arounds, neither of which fixed the problem:
|
| string szFile = null;
|
| using (SaveFileDialog sfd = new SaveFileDialog())
| {
| if (sfd.ShowDialog(this) == DialogResult.OK)
| {
| szFile = sfd.FileName;
| }
| }
|
| if (szFile != null)
| {
| // open file here...this is where the exception occurs
| }
|
| And, alternatively:
|
| SaveFileDialog sfd = new SaveFileDialog();
|
| if (sfd.ShowDialog(this) == DialogResult.OK)
| {
| string szFile = sfd.FileName;
|
| sfd = null;
| GC.Collect();
|
| // open file here...this is where the exception occurs
| }
|
| So, any ideas? Has anyone seen this before? As I mentioned, if I don't
use
| the SaveFileDialog and instead just set the filename variable directly in
| code, the problem completely disappears. It seems obvious to me that the
| SaveFileDialog is somehow keeping the file open, but I don't understand
why,
| nor do I understand how I can get it to release the file so that I can
open
| it myself.
|
| Much thanks,
| Pete
|

The SaveFileDialog does not open the selected file unless you open the file
by calling SaveFileDialog.OpenFile. Note that I don't quite understand why
you are using SaveFileDialog instead of the OpenFileDialog, you aren't using
any functionality of this class other than select a file.

What version of the framework are you running?


Willy.
 
E

Emby

Hi Willy,

I am using framework v2 (I think Pete is as well).

I won't speak for Pete, but I'm using the File Save Dialog because, well,
the user is specifying a file to which they are saving data. This has a
somewhat different action than the Open file dialog. For example, Open has
props like "ShowReadOnly" and "MultiSelect", while Save has "CreatePrompt"
and "OverwritePrompt".

Both of the dialogs select files, but the FS dialog selects a file to write
to, while the OF dialog selects a file to read from.

Emby
 
P

Peter Duniho

Willy Denoyette said:
The SaveFileDialog does not open the selected file unless you open the
file
by calling SaveFileDialog.OpenFile.

It obviously does so. Whether it's the .NET portion of SaveFileDialog
that's doing that or not, I can't say. For all I know, this is a general
problem that exists in the regular Win32 common dialog implementations,
inhereted by .NET. But invoking the dialog results in the file being
locked, that much I am sure.
Note that I don't quite understand why
you are using SaveFileDialog instead of the OpenFileDialog, you aren't
using
any functionality of this class other than select a file.

Um, because I am prompting the user to *save* the file. It is simply not
true that I am not "using any functionality of this class other than select
a file". There are genuine and subtle differences between the way the Open
and Save dialogs work, and it's important for an application to use the
appropriate one in the appropriate situation to preserve these subtle
differences.

Is it your assertion that it's my fault the problem I am having because I'm
using the SaveFileDialog instead of the OpenFileDialog? If not, I'm at a
loss as to why you would even mention it anyway (even assuming it was a
legitimate complaint of my code, when it's obviously not).
What version of the framework are you running?

..NET 2.0.

Pete
 
W

Willy Denoyette [MVP]

| Hi Willy,
|
| I am using framework v2 (I think Pete is as well).
|
| I won't speak for Pete, but I'm using the File Save Dialog because, well,
| the user is specifying a file to which they are saving data. This has a
| somewhat different action than the Open file dialog. For example, Open has
| props like "ShowReadOnly" and "MultiSelect", while Save has "CreatePrompt"
| and "OverwritePrompt".
|
| Both of the dialogs select files, but the FS dialog selects a file to
write
| to, while the OF dialog selects a file to read from.
|

Right, but you should know that the way you use this common control, you
opened a can of worms. The FileDialog is wrapping a native common control
(as you rightfully concluded yourself), which integrates with the shell
(explorer.exe), When selecting a file and accepting the selection (OK
DialogResult), explorer will be notified to query the file properties and
perform some housekeeping like updating the recent used documents list, for
this the explorer process needs to open the file (unfortunately non shared),
if you look at your code, this will mostly occur at the same time you try to
open the file for writing, which obvioulsly fails.
One thing you can try is to insert a Thread.Sleep(100); after the using
block, this should give sufficient time to explorer to execute it's task and
to close the file.

Willy.
 
W

Willy Denoyette [MVP]

| | > The SaveFileDialog does not open the selected file unless you open the
| > file
| > by calling SaveFileDialog.OpenFile.
|
| It obviously does so. Whether it's the .NET portion of SaveFileDialog
| that's doing that or not, I can't say. For all I know, this is a general
| problem that exists in the regular Win32 common dialog implementations,
| inhereted by .NET. But invoking the dialog results in the file being
| locked, that much I am sure.
|
No it's not the SaveFileDialog which locks the file (it doesn't even open
the file), it's the explorer.exe (the windwos shell) who opens the file.

| > Note that I don't quite understand why
| > you are using SaveFileDialog instead of the OpenFileDialog, you aren't
| > using
| > any functionality of this class other than select a file.
|
| Um, because I am prompting the user to *save* the file. It is simply not
| true that I am not "using any functionality of this class other than
select
| a file". There are genuine and subtle differences between the way the
Open
| and Save dialogs work, and it's important for an application to use the
| appropriate one in the appropriate situation to preserve these subtle
| differences.
|

This class is a simple solution for users to save file data using standard
Windows dialogs, you have to implement your own save logic, to do so you
should use the OpenFile method (which keeps a copy of the original file) and
attach it to a stream, which you can change or not and finaly save. Now ,you
don't use any of this, you don't use the class as it was intended to be
used.


| Is it your assertion that it's my fault the problem I am having because
I'm
| using the SaveFileDialog instead of the OpenFileDialog? If not, I'm at a
| loss as to why you would even mention it anyway (even assuming it was a
| legitimate complaint of my code, when it's obviously not).
|

No, it's not my assertion, both OpenFileDialog and SaveFileDialog may
introduce some (maybe different) side effects because both use a Common
Control which integrates with the shell (explorer.exe) by mean of an
explorer hook procedure, this is done to display the explorer-style dialog.
Also, a shell extention component can register a handler component (COM
shell extention) to perform specific actions based on file types, the
explorer will post an event to all registerd handlers whenever a registerd
filetype is opened, the handler is free to open the file. To find the link
between the file and the handler, explorer may have to read (that is, open,
read extended file information, read volume info, close) the file and that's
exactly what happens here when the user clicks 'Save'.
Note that in general there is no problem with this, explorer closes the file
before the Dialog result is returned to the caller, but a handler may well
customize the dialog by providing a hook procedure a template or both, and
here the handler could open the file in exclusive mode and you start a race.
I would suggest you to grab a copy of Sysinternals
http://www.sysinternals.com/ "filemon" and look who's having the file open
while your program tries to open the same file.

Willy.
 
P

Peter Duniho

Willy Denoyette said:
No it's not the SaveFileDialog which locks the file (it doesn't even open
the file), it's the explorer.exe (the windwos shell) who opens the file.

You are making a semantic argument that is irrelevant. For the .NET
programmer, using only the .NET framework, any behavior or functionality
that framework offers is considered to be the framework itself. If the
framework inherets bad behavior from some component it uses, it is still the
framework that has the bad behavior.

Do I refrain from saying that my program locks the file when it wants to
write to it, just because it's not actually my code that does the actual
locking? I don't think so.

Beyond that, this is not a problem that I've seen with non-.NET programs. I
can't say for sure that it doesn't exist, but I've never had to work around
the common file dialogs locking files that I'm trying to write to after I
call the save file dialog from a regular Win32 application (and I've been
writing Windows software since *before* there was a Win32). So, it remains
to be shown that this problem is not unique to .NET. As far as I can tell,
it is.
This class is a simple solution for users to save file data using standard
Windows dialogs, you have to implement your own save logic, to do so you
should use the OpenFile method (which keeps a copy of the original file)

What do you mean by "keeps a copy of the original file"? There may not even
*be* an original file, and if there is, are you saying that a copy of the
original file is created somewhere and that's what's used in the stream?
and
attach it to a stream, which you can change or not and finaly save. Now
,you
don't use any of this, you don't use the class as it was intended to be
used.

If Microsoft intends me to use the OpenFile method any time I use the
SaveFileDialog class, they need to provide an OpenFile overload that allows
for the same flags and options I get with the FileStream constructors.

Until such time (and IMHO even after such time), it is just plain silly to
claim that I am not using the dialog as intended, when I'm using the
documented functionality in the only way that addresses my specific need.
Given that the OpenFile method doesn't do what I want, what do you suggest I
do rather than using the SaveFileDialog? Why does the SaveFileDialog
include a FileName property if Microsoft intends for programmers to ONLY use
the OpenFile method to open a file specified through that dialog?
| Is it your assertion that it's my fault the problem I am having because
I'm
| using the SaveFileDialog instead of the OpenFileDialog? If not, I'm at
a
| loss as to why you would even mention it anyway (even assuming it was a
| legitimate complaint of my code, when it's obviously not).
|

No, it's not my assertion, both OpenFileDialog and SaveFileDialog may
introduce some (maybe different) side effects

Then why are you questioning my use of the SaveFileDialog versus the
OpenFileDialog? How is my use of the SaveFileDialog rather than the
OpenFileDialog relevant to this question at all?

Pete
 
W

Willy Denoyette [MVP]

| | > No it's not the SaveFileDialog which locks the file (it doesn't even
open
| > the file), it's the explorer.exe (the windwos shell) who opens the file.
|
| You are making a semantic argument that is irrelevant. For the .NET
| programmer, using only the .NET framework, any behavior or functionality
| that framework offers is considered to be the framework itself. If the
| framework inherets bad behavior from some component it uses, it is still
the
| framework that has the bad behavior.
|

You are talking like a naive developer, the one who doesn't look any further
than the small API set he's using. if something goes wrong it's the API.

| Do I refrain from saying that my program locks the file when it wants to
| write to it, just because it's not actually my code that does the actual
| locking? I don't think so.
|

I can't tell, you don't post that significant part of your code, I don't
know what you are doing in,

if (szFile != null)
{
// open file here...this is where the exception occurs
}
I have no idea how you do open the file, all I know is that there is a
sharing conflict. So please post your implementation, investigate who's
holding the file and in what mode it's been opened, else I will stop
replying.


| Beyond that, this is not a problem that I've seen with non-.NET programs.
I
| can't say for sure that it doesn't exist, but I've never had to work
around
| the common file dialogs locking files that I'm trying to write to after I
| call the save file dialog from a regular Win32 application (and I've been
| writing Windows software since *before* there was a Win32). So, it
remains
| to be shown that this problem is not unique to .NET. As far as I can
tell,
| it is.
|

It has nothing to do with .NET. But of course to you, everything is .NET
once you use the framework, right?

| > This class is a simple solution for users to save file data using
standard
| > Windows dialogs, you have to implement your own save logic, to do so you
| > should use the OpenFile method (which keeps a copy of the original file)
|
| What do you mean by "keeps a copy of the original file"? There may not
even
| *be* an original file, and if there is, are you saying that a copy of the
| original file is created somewhere and that's what's used in the stream?
|
No, sorry, this is realy badly explained. Here's another try:
If the file doesn't exists, FileOpen creates the file with RW access and
returns the stream reference, as the file doesn't exists, it cannot be
opened by someone else, no problem here.
However, if the file exists, FileOpen returns a null stream reference, and
it's up to you to implement the file creation and Save logics. And here is
where the problem starts, if the file is opened by someone else, it's up to
you to handle this, say by creating a temporary file and copy this one to
the original file when done or wait until the file gets released. But before
you can do this you have to find out who's accessing the file and why, I say
it's the explorer and I also explained why, but I also said that you can
register a hook procedure (and/or a template hook) such that you can modify
the standard behavior of the common dialog control. So when you install a
third party product, it's possible that they register a shell hook, and
that's where it gets messy. So please try to investigate who's holding the
file and stop complaining about this tiny .NET wrapper class.



If the file doen exist, you have to implement your own save logig based on
File
| > and
| > attach it to a stream, which you can change or not and finaly save. Now
| > ,you
| > don't use any of this, you don't use the class as it was intended to be
| > used.
|
| If Microsoft intends me to use the OpenFile method any time I use the
| SaveFileDialog class, they need to provide an OpenFile overload that
allows
| for the same flags and options I get with the FileStream constructors.
|

No, they don't, it's explained in the docs what the class is designed for,
if you don't like the behavior, you will have to design your own save
dialog.

| Until such time (and IMHO even after such time), it is just plain silly to
| claim that I am not using the dialog as intended, when I'm using the
| documented functionality in the only way that addresses my specific need.
| Given that the OpenFile method doesn't do what I want, what do you suggest
I
| do rather than using the SaveFileDialog? Why does the SaveFileDialog
| include a FileName property if Microsoft intends for programmers to ONLY
use
| the OpenFile method to open a file specified through that dialog?
|
Again the behavior is clearly explained in the docs, if it doesn't fit your
needs implement your own dialog, if you don't like to use the implementation
of the OpenFile method, that is create a new file using the selected
filename, implement your own behavior no problem.

| > | Is it your assertion that it's my fault the problem I am having
because
| > I'm
| > | using the SaveFileDialog instead of the OpenFileDialog? If not, I'm
at
| > a
| > | loss as to why you would even mention it anyway (even assuming it was
a
| > | legitimate complaint of my code, when it's obviously not).
| > |
| >
| > No, it's not my assertion, both OpenFileDialog and SaveFileDialog may
| > introduce some (maybe different) side effects
|
| Then why are you questioning my use of the SaveFileDialog versus the
| OpenFileDialog? How is my use of the SaveFileDialog rather than the
| OpenFileDialog relevant to this question at all?

The side effects may be different as the explorer hook procedures may be
different.

Willy.
 
W

Willy Denoyette [MVP]

|
| I can't tell, you don't post that significant part of your code, I don't
| know what you are doing in,
|
| if (szFile != null)
| {
| // open file here...this is where the exception occurs
| }

To illustrate this point, add the following to a Form...

private void SomeButton_Click(object sender, EventArgs e)
{
string szFile = null;
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.OverwritePrompt = false; // no overwrite prompt
if (sfd.ShowDialog() == DialogResult.OK)
{
szFile = sfd.FileName;
}
}
if (szFile != null)
{
string filePath = szFile;
try
{
// uncomment to get the exceptional behavior
FileStream fs = new FileStream(szFile, FileMode.Create,
FileAccess.ReadWrite /*, FileShare.ReadWrite*/);
}
finally
{
this.Text = szFile;
}
}
}

Run the program and select the SAME existing file twice in succession, an AV
will be thrown on you the second time. Uncomment the FileStream's FileShare
argument and run again, no exception will be thrown no matter the number of
times you select the same file.
The reason it succeeds the first time is because the explorer has to create
the recent used file links in the users profile (File system and registry),
the second time you select the same file explorer will use the cached link
(through the SaveFileDialog's hook proc, other hooks my do different
things). Note also that File system indexers may have hooks installed which
may open the file when being notified of a change , same goes for virus
scanners or quota managers (and other filesystem filter drivers).
So, whether you consider this as bad behavior (in which case you should file
a bug) or not, you have to program defensively, be prepared that you may get
AV whenever you use the shell integrated stuff and program accordingly.
For instance, if you don't want to open the file for shared read/write, try
to open in Write mode first and handle the AV exception by opening the file
as shown above.

Willy.
 
W

Willy Denoyette [MVP]

|
| |
||
|| I can't tell, you don't post that significant part of your code, I don't
|| know what you are doing in,
||
|| if (szFile != null)
|| {
|| // open file here...this is where the exception occurs
|| }
|
| To illustrate this point, add the following to a Form...
|
| private void SomeButton_Click(object sender, EventArgs e)
| {
| string szFile = null;
| using (SaveFileDialog sfd = new SaveFileDialog())
| {
| sfd.OverwritePrompt = false; // no overwrite prompt
| if (sfd.ShowDialog() == DialogResult.OK)
| {
| szFile = sfd.FileName;
| }
| }
| if (szFile != null)
| {
| string filePath = szFile;
| try
| {
| // uncomment to get the exceptional behavior
| FileStream fs = new FileStream(szFile, FileMode.Create,
| FileAccess.ReadWrite /*, FileShare.ReadWrite*/);
| }
| finally
| {
| this.Text = szFile;
| }
| }
| }
|
| Run the program and select the SAME existing file twice in succession, an
AV
| will be thrown on you the second time. Uncomment the FileStream's
FileShare
| argument and run again, no exception will be thrown no matter the number
of
| times you select the same file.
| The reason it succeeds the first time is because the explorer has to
create
| the recent used file links in the users profile (File system and
registry),
| the second time you select the same file explorer will use the cached link
| (through the SaveFileDialog's hook proc, other hooks my do different
| things). Note also that File system indexers may have hooks installed
which
| may open the file when being notified of a change , same goes for virus
| scanners or quota managers (and other filesystem filter drivers).
| So, whether you consider this as bad behavior (in which case you should
file
| a bug) or not, you have to program defensively, be prepared that you may
get
| AV whenever you use the shell integrated stuff and program accordingly.
| For instance, if you don't want to open the file for shared read/write,
try
| to open in Write mode first and handle the AV exception by opening the
file
| as shown above.
|
| Willy.

As a final note (sorry for 'spamming' this thread), above behavior doesn't
show up when using the OpenFile method to open the selected file like this:

...
string szFile = null;
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.OverwritePrompt = false; // no overwrite prompt
if (sfd.ShowDialog() == DialogResult.OK)
{
if (szFile != null)
{
szFile = sfd.FileName;
... = sfd.OpenFile();
... // do you thing and write the stream
}
}
}

Willy.
 
W

Willy Denoyette [MVP]

|
| |
||
|| I can't tell, you don't post that significant part of your code, I don't
|| know what you are doing in,
||
|| if (szFile != null)
|| {
|| // open file here...this is where the exception occurs
|| }
|
| To illustrate this point, add the following to a Form...
|
| private void SomeButton_Click(object sender, EventArgs e)
| {
| string szFile = null;
| using (SaveFileDialog sfd = new SaveFileDialog())
| {
| sfd.OverwritePrompt = false; // no overwrite prompt
| if (sfd.ShowDialog() == DialogResult.OK)
| {
| szFile = sfd.FileName;
| }
| }
| if (szFile != null)
| {
| string filePath = szFile;
| try
| {
| // uncomment to get the exceptional behavior
| FileStream fs = new FileStream(szFile, FileMode.Create,
| FileAccess.ReadWrite /*, FileShare.ReadWrite*/);
| }
| finally
| {
| this.Text = szFile;
| }
| }
| }
|
| Run the program and select the SAME existing file twice in succession, an
AV
| will be thrown on you the second time. Uncomment the FileStream's
FileShare
| argument and run again, no exception will be thrown no matter the number
of
| times you select the same file.
| The reason it succeeds the first time is because the explorer has to
create
| the recent used file links in the users profile (File system and
registry),
| the second time you select the same file explorer will use the cached link
| (through the SaveFileDialog's hook proc, other hooks my do different
| things). Note also that File system indexers may have hooks installed
which
| may open the file when being notified of a change , same goes for virus
| scanners or quota managers (and other filesystem filter drivers).
| So, whether you consider this as bad behavior (in which case you should
file
| a bug) or not, you have to program defensively, be prepared that you may
get
| AV whenever you use the shell integrated stuff and program accordingly.
| For instance, if you don't want to open the file for shared read/write,
try
| to open in Write mode first and handle the AV exception by opening the
file
| as shown above.
|
| Willy.
|
|

Sorry, forget about this, the code snip is completely wrong, the reason it
fails at the next time is because the stream was not closed :-((
The code should look something like this...
...
if(!String.IsNullOrEmpty(sfd.FileName))
{
string filePath = szFile;
try
{
FileStream fs = new FileStream(szFile, FileMode.Create,
FileAccess.ReadWrite);
//use file stream...
}
catch(IOException)
{
// another process may hold the file.
// if sharing violation, open the file using shared
read/write....
try
{
FileStream fs = new FileStream(szFile, FileMode.Create,
FileAccess.ReadWrite, FileShare.ReadWrite);
// process stream
}
catch(...)
{
// this is real bad, can't open the stream
}
finally
{
if(fs != null)
fs.Close();
//....
}
}

The same goes when using the OpenFile method you will have implement
something like this...

if (sfd.ShowDialog() == DialogResult.OK)
{
if(!String.IsNullOrEmpty(sfd.FileName))
{
try
{
FileStream fs = (FileStream)sfd.OpenFile())
{
//...
}
}
catch(...)
{ // handle sharing violation... like above
}
finally
// Close stream
}

Willy.
 
P

Peter Duniho

Willy Denoyette said:
You are talking like a naive developer, the one who doesn't look any
further
than the small API set he's using. if something goes wrong it's the API.

And you are talking like a Microsoft apologist, one who can't imagine that
an API ought to have a consistent, deterministic, reliable behavior.

It's one thing for there to be a possibility that the file might become
locked before I try to access it. Any number of processes may do that, and
that's fine with me. However, it's just plain *dumb* for the one API that
is supposed to return a file to open for writing to actually *lock* that
file and not unlock it before returning that file information to the program
that called it.

Do you actually believe that it is reasonable behavior to require the caller
to loop, repeatedly attempting to open the desired file until it can be
opened?
| Do I refrain from saying that my program locks the file when it wants to
| write to it, just because it's not actually my code that does the actual
| locking? I don't think so.
|

I can't tell, you don't post that significant part of your code, I don't
know what you are doing in,

if (szFile != null)
{
// open file here...this is where the exception occurs
}
I have no idea how you do open the file, all I know is that there is a
sharing conflict. So please post your implementation, investigate who's
holding the file and in what mode it's been opened, else I will stop
replying.

As I said before, all I'm doing is creating a new FileStream, using the "new
FileStream()" syntax. It's not rocket science.
[...]
No, sorry, this is realy badly explained. Here's another try:
If the file doesn't exists, FileOpen creates the file with RW access and
returns the stream reference, as the file doesn't exists, it cannot be
opened by someone else, no problem here.
However, if the file exists, FileOpen returns a null stream reference, and
it's up to you to implement the file creation and Save logics. [...]

Do you mean "OpenFile"? There's no "FileOpen" method.

Where in the documentation does it say that "it's up to me to implement the
file creation and Save logics"? In the documentation for
SaveFileDialog.OpenFile, it reads:

"Opens the file with read/write permission selected by the user"

Furthermore, it *specifically* says to use the FileName property to avoid
having the SaveFileDialog from deleting existing data in an existing file:

"For security purposes, this method creates a new file with the selected
name and opens it with read/write permissions. This can cause unintentional
loss of data if you select an existing file to save to. To save data to an
existing file while retaining existing data, use the File class to open the
file using the file name returned in the FileName property"

I see absolutely no documentation that says "if the file exists, OpenFile
returns a null stream reference". In fact, it specifically says that if the
file exists, it will still return a valid stream, but will overwrite the
existing file with a new one. Nor do I see anything that says that it's up
to me "to implement the file creation and Save logics" (unless by that you
simply mean that I need to use the FileName property as a parameter to a
method such as creating a new FileStream object, in which case *that's
exactly what I'm doing*).

Nowhere in the documentation does it suggest that the SaveFileDialog may
itself lock the file and that I need to repeatedly attempt to open the file
until it has unlocked it.

You have several times referred to documentation that does not appear to
actually exist. Perhaps you could actually post some references in the form
of URLs where I can read the supposed documentation that says I'm doing this
all wrong.
It has nothing to do with .NET. But of course to you, everything is .NET
once you use the framework, right?

I have NEVER seen this happen, and I was one of the very first developers to
actually use the common file dialogs. It's true, a lot of years have gone
by since they first appeared, and who knows what people at Microsoft have
done since then. But I've never seen the file save dialog keep a file
locked after returning to the caller, except in the .NET context.

That strongly suggests a possible .NET relevance.

If you can post a non-.NET example that reproduces the exact same problem, I
will be happy to accept your claim that this is not a .NET problem. Until
then the evidence, however circumstantial it may be, points to a .NET
problem.
[...]
No, they don't, it's explained in the docs what the class is designed for,
if you don't like the behavior, you will have to design your own save
dialog.

From the documentation:

"Prompts the user to select a location for saving a file. This class cannot
be inherited"

and

"This class can either open and overwrite an existing file or create a new
file. Most of the functionality for this class is found in the FileDialog
class"

That's it as far as "explained in the docs what the class is designed for".
There's no mention in the FileDialog class description about the dialog
locking the file, or suggesting that I am improperly using the class by
getting the file name from the FileName property.
Where in the documentation do you see it saying that I should not get the
file name from the FileName property, or that I must call the OpenFile
method, or that the OpenFile method returns a null stream if the file
exists, or that if a null stream is returned, I must implement my own file
open and save logic?
| Then why are you questioning my use of the SaveFileDialog versus the
| OpenFileDialog? How is my use of the SaveFileDialog rather than the
| OpenFileDialog relevant to this question at all?

The side effects may be different as the explorer hook procedures may be
different.


It doesn't matter if the side effects may be different if the SaveFileDialog
class is the correct class to use. You specifically asked me why I'm not
using the OpenFileDialog, as if that was the class I was supposed to use.
Since it's NOT the class I'm supposed to use, how it behaves isn't relevant
here, not with respect to solving the problem being discussed.

Pete
 
W

Willy Denoyette [MVP]

| | > You are talking like a naive developer, the one who doesn't look any
| > further
| > than the small API set he's using. if something goes wrong it's the API.
|
| And you are talking like a Microsoft apologist, one who can't imagine that
| an API ought to have a consistent, deterministic, reliable behavior.

No, I'm talking like someone who's trying to help you out to find a solution
for the issue you (and possibly others) have. But it looks like you are here
to insult those who are trying to help.


|
| It's one thing for there to be a possibility that the file might become
| locked before I try to access it. Any number of processes may do that,
and
| that's fine with me. However, it's just plain *dumb* for the one API that
| is supposed to return a file to open for writing to actually *lock* that
| file and not unlock it before returning that file information to the
program
| that called it.
|
| Do you actually believe that it is reasonable behavior to require the
caller
| to loop, repeatedly attempting to open the desired file until it can be
| opened?

Where did I suggest you to loop or whatever?

|
| > | Do I refrain from saying that my program locks the file when it wants
to
| > | write to it, just because it's not actually my code that does the
actual
| > | locking? I don't think so.
| > |
| >
| > I can't tell, you don't post that significant part of your code, I don't
| > know what you are doing in,
| >
| > if (szFile != null)
| > {
| > // open file here...this is where the exception occurs
| > }
| > I have no idea how you do open the file, all I know is that there is a
| > sharing conflict. So please post your implementation, investigate who's
| > holding the file and in what mode it's been opened, else I will stop
| > replying.
|
| As I said before, all I'm doing is creating a new FileStream, using the
"new
| FileStream()" syntax. It's not rocket science.
|
No, no rocket science, but still too hard for you to post your code.

| > [...]
| > No, sorry, this is realy badly explained. Here's another try:
| > If the file doesn't exists, FileOpen creates the file with RW access and
| > returns the stream reference, as the file doesn't exists, it cannot be
| > opened by someone else, no problem here.
| > However, if the file exists, FileOpen returns a null stream reference,
and
| > it's up to you to implement the file creation and Save logics. [...]
|
| Do you mean "OpenFile"? There's no "FileOpen" method.
|

Sorry meant, OpenFile.

| Where in the documentation does it say that "it's up to me to implement
the
| file creation and Save logics"? In the documentation for
| SaveFileDialog.OpenFile, it reads:
|
[1]From: Working with the SaveFileDialog Component (Windows Forms
Programming) in MSDN.
<snip
Use it as a simple solution for enabling users to save files instead of
configuring your own dialog box. By relying on standard Windows dialog
boxes, the basic functionality of applications you create is immediately
familiar to users. Be aware, however, that when using the SaveFileDialog
component, you must write your own file-saving logic.
/snip>

| "Opens the file with read/write permission selected by the user"
|
| Furthermore, it *specifically* says to use the FileName property to avoid
| having the SaveFileDialog from deleting existing data in an existing file:
|
| "For security purposes, this method creates a new file with the selected
| name and opens it with read/write permissions. This can cause
unintentional
| loss of data if you select an existing file to save to. To save data to an
| existing file while retaining existing data, use the File class to open
the
| file using the file name returned in the FileName property"
|
| I see absolutely no documentation that says "if the file exists, OpenFile
| returns a null stream reference".

That's right, bad wording of my part, should read....
However, if the file exists, FileOpen fails (which means there is sharing
violation) the method throws and returns a null Stream reference.


In fact, it specifically says that if the
| file exists, it will still return a valid stream, but will overwrite the
| existing file with a new one. Nor do I see anything that says that it's
up
| to me "to implement the file creation and Save logics" (unless by that you
| simply mean that I need to use the FileName property as a parameter to a
| method such as creating a new FileStream object, in which case *that's
| exactly what I'm doing*).
|
That's right, however I didn't see your code yet.

| Nowhere in the documentation does it suggest that the SaveFileDialog may
| itself lock the file and that I need to repeatedly attempt to open the
file
| until it has unlocked it.
|

No, because the SaveFileDialog implementation doesn't lock the file (unless
you call OpenFile or whatever other IO API).

| You have several times referred to documentation that does not appear to
| actually exist. Perhaps you could actually post some references in the
form
| of URLs where I can read the supposed documentation that says I'm doing
this
| all wrong.

Several times? mind to tell me when I refered to the docs except when I
said:
<No, they don't, it's explained in the docs what the class is designed for,
if you don't like the behavior, you will have to design your own save
dialog.>

See [1]above, for a reference.

|
| > It has nothing to do with .NET. But of course to you, everything is .NET
| > once you use the framework, right?
|
| I have NEVER seen this happen, and I was one of the very first developers
to
| actually use the common file dialogs. It's true, a lot of years have gone
| by since they first appeared, and who knows what people at Microsoft have
| done since then. But I've never seen the file save dialog keep a file
| locked after returning to the caller, except in the .NET context.
|
Again it's not the common dialog (wrapped by the .NET class) who's locking
the file, IF it's not your own code who has the file open, it must be
another process who opens the file (non-shared), that other 'component' can
be anything from a badly written shell extention that has registerd a hook
or another badly implemented process that opens the file non-shared (less
likely, but still possible).


| That strongly suggests a possible .NET relevance.
|
| If you can post a non-.NET example that reproduces the exact same problem,
I
| will be happy to accept your claim that this is not a .NET problem. Until
| then the evidence, however circumstantial it may be, points to a .NET
| problem.
|

I have a C# sample that doesn't have the issue you are seeing (not using any
loops and sleeps etc..), no matter the file type and the times I call it.


| > [...]
| > No, they don't, it's explained in the docs what the class is designed
for,
| > if you don't like the behavior, you will have to design your own save
| > dialog.
|
| From the documentation:
|
| "Prompts the user to select a location for saving a file. This class
cannot
| be inherited"
|
| and
|
| "This class can either open and overwrite an existing file or create a new
| file. Most of the functionality for this class is found in the FileDialog
| class"
|
| That's it as far as "explained in the docs what the class is designed
for".
| There's no mention in the FileDialog class description about the dialog
| locking the file, or suggesting that I am improperly using the class by
| getting the file name from the FileName property.
| Where in the documentation do you see it saying that I should not get the
| file name from the FileName property, or that I must call the OpenFile
| method, or that the OpenFile method returns a null stream if the file
| exists, or that if a null stream is returned, I must implement my own file
| open and save logic?
|

See [1] above.

| > [...]
|
| > | Then why are you questioning my use of the SaveFileDialog versus the
| > | OpenFileDialog? How is my use of the SaveFileDialog rather than the
| > | OpenFileDialog relevant to this question at all?
| >
| > The side effects may be different as the explorer hook procedures may be
| > different.
|
|
| It doesn't matter if the side effects may be different if the
SaveFileDialog
| class is the correct class to use. You specifically asked me why I'm not
| using the OpenFileDialog, as if that was the class I was supposed to use.
| Since it's NOT the class I'm supposed to use, how it behaves isn't
relevant
| here, not with respect to solving the problem being discussed.

All I wanted to know is if both behaved the same, that's all.
As long as I don't see any code that illustrates the issue, I wont reply any
further. All I'm trying here is to help you out, if you think you don't need
any help it's ok for me.


Willy.
 
P

Peter Duniho

Willy Denoyette said:
No, I'm talking like someone who's trying to help you out to find a
solution
for the issue you (and possibly others) have. But it looks like you are
here
to insult those who are trying to help.

You're the one calling me a "naive developer", and I'm being insulting?

Uh, right. You've promised to stop "helping" a couple of times now...maybe
now would be a good time to keep that promise.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top