Solved

Checking File Status

Posted on 1997-11-21
24
539 Views
Last Modified: 2010-04-04
 How can I check whether a file is already open? Is it possible to identify who has opened the file?
  I've tried to check this using an exception handler like this:

  *Check the file exists*
  try
    *Open the file with TFileStream*
    *File isn't already open*
  except
    *File is already open*
  end;

  however this seems to cause an access violation depite my attempts to handle the exceptions. Can I check whether the file is open without using exceptions? Cheers for your help.

  Steven.

  P.S. If you know ANYTHING about Metafiles, then why not check out my question which is loitering, unanswered, at the bottom of this list:
http://www.experts-exchange.com/topics/bin/ShowQ?qid=8630021056 (Translucency in Metafiles)
Lots of points on offer.
0
Comment
Question by:StevenB
  • 9
  • 8
  • 4
  • +2
24 Comments
 
LVL 4

Author Comment

by:StevenB
ID: 1351360
Edited text of question
0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 1351361
Hi

The method you cite is a standard method for checking if a file is open.  This code works perfectly on my system in Delphi 3 assuming that the file exists:

procedure TForm1.Button1Click(Sender: TObject);
var
  fs: TFileStream;
begin
  try
    fs := TFileStream.Create('C:\Test.dat', fmOpenReadWrite or fmShareExclusive);
    ShowMessage('Nobody else is using the file.');
    fs.Free;
  except
    ShowMessage('The file cannot be open exclusively or does not exist.');
  end;
end;


I don't think there is a way to see WHO has the file open...

JB

P.S.  Sorry - can't help with other question...
0
 
LVL 2

Expert Comment

by:alona041797
ID: 1351362
I think the problem is Delphi catches all exceptions in the IDE regardless of you having or not having a handler for them.

Try running your program outside of the IDE and check if it works.
0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 1351363
Forgive me alona, but I think you are getting confused with VB.  Delphi has an option to break (i.e. stop) on all exceptions when running a program from the IDE, but the program's assembled code is the same as without the IDE.  The IDE runs the same exe file that you would run from Windows Explorer, so if the program works outside of the Delphi IDE it will work within the IDE too.  The only difference is that if the IDE catches the exception (when "Break on exception" is on), it takes you to the line that caused the exception.  By stepping the code you will still enter the "except" part of the try-except block.

JB
0
 
LVL 4

Author Comment

by:StevenB
ID: 1351364
 Hi JimBob and Alona,
 
  I'll try and elaborate on the problems a bit: I'm using an exception handler to identify if the file is open or not. (Basically the same as the one JimBob describes, except that the fs.Free is within a resource protection block (fs is never freed in your example if the exception occurs JimBob)) This works fine and successfully identifies whether the file can be opened or not, however some time after the execution point has left the procedure containing the code, an access violation occurs.
  The first thing I assumed was that the access violation was my fault, but after endless checking of my code, I can't find the guilty lines. I then discovered that the Executable ran perfectly and the access violation only occurs when I'm running the application through Delphi in debug mode (despite your assertion JimBob that : if the program works outside of the Delphi IDE it will work within the IDE too. Nothing is ever that simple eh.) I'm now forced to conclude that the access violations are an artifact of the Delphi environment, and are somehow being caused by my attempts to generate exceptions to check the status of the file. This is why I figured that they could be cured by finding a check for the file status, that did not relly on exceptions. I've never been totally comfortable with using exceptions for validity checking.
  Since the executable of my application runs perfectly the problem is not too much of a nightmare as concerns releasing the application, however it is making debugging a real chore, as I cannot test the new code I write, I simply have to compile it and then run the executable and guess what's going on behind the scenes.
  So my question still stands: Can I check to see whether the file is opened, without generating exceptions?

  Thanks for all your comments,
  Steven.
0
 
LVL 1

Accepted Solution

by:
anilms earned 200 total points
ID: 1351365
There is another way for checking file status - a very old but a very efficient way - carried over from Tubro Pascal. Set the compiler directive  {$I-} and check for IOResult. If the IO operation you are performing is successful, IOResult will contain 0, otherwise it will contain some other value. For example, if you are trying to open a file which does not exist, it will return 2. Check sample code given below :

{$I-}  {Turns of IO Checking, control is now in your hands}
     Reset(f); {or Rewrite(f)}
     if IOResult=0 then ShowMessage('Successful') else
     case IOResult of
     1: ShowMessage('Not able to create file');
     2: ShowMessage('File Does not exist');
     ...
     end

You can check the various values of IOResult very quickly and simply. I have always found that for IO checking, this is faster than try-exception.
0
 
LVL 1

Expert Comment

by:anilms
ID: 1351366
Regarding wanting to know which user is using the file - you can add some lines of code to get the user's name when he first opens up the program and then write it to the Windows Registry. You can check for this easily.
0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 1351367
Hi Steven

Interesting...  One more thing, though.  You said that fs is never freed in my example if the exception occurs.  This is correct, and needs to be this way.  If the exception occurs when creating the TFileStream, then it is never created and you will get an access violation if you try to free it, like this:
try
    fs := TFileStream.Create('...', fmShareExclusive);
except
    fs.Free;    // Exception here, because fs was never created!
end;

Regarding your problem in the Delphi IDE, I can only imagine that Delphi is raising the exception and NOT your app.  Possibly you are opening a file that Delphi is busy with???

I have never had a problem with the exception method before, but if you really don't want to use it then perhaps anilms' answer is best.  Personally I would try to get to the bottom of the reason why the exception is raised within Delphi...

JB
0
 
LVL 4

Author Comment

by:StevenB
ID: 1351368
 Hi JimBob,

  Concerning our side issue ;) ->
 
  This is what the Delphi help has to say about 'free' : "Use Free to destroy an object. Free automatically calls the Destroy if the object instance is is not nil. Any object instantiated by a call to Create should be destroyed by a call to Free, so that the object an be properly destroyed and the memory released. Free is successful even if the object is nil, so if the object was never initialized, for example, calling Free won’t result in an error." I find it's always safest to protect resources within a 'try-finally' block, and I'm pretty sure that you should always attempt to free up something even if it failed to create correctly. Partial creation can leave assigned pointers/memory hanging about your system.

  Concerning the question ->

  I agree with you that the exception method should work perfectly and that there is obviously a problem somewhere. Ideally I'd like to track this down, but it seems a bit of a heavy problem that could take weeks to locate and I've got to release the application in four weeks. Basically : Not enough time. Sloppy, I know, but I have no options. Also I'm not sure how to go about finding the problem in the first place.
  UPDATE : In another manifestation of the problem, in a seperate piece of code, when the TFileStream.Create line fails, the whole application simply closes. As you can see this problem is a little confusing.



  anilms,

  I'll check your suggestion. It looks promising, I'll get back to you on it soon.

 

  Cheers everybody, Steven.
 
0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 1351369
Hehe...  Regarding the side issue again.

I agree with you regarding using try...finally blocks, but we were talking about try...except blocks.  You can ONLY free a nil object, or an object that has been properly created.  Try the following:  (Make sure you open a file that does NOT exist.)

var
  fs: TFileStream;
begin
  fs := nil;
  try
    fs := TFileStream.Create('C:\Invalid File', fmOpenReadWrite);
  except
    ShowMessage(IntToStr(Integer(fs)));
  end;
  fs.Free;
end;

This code will only work if you set fs := nil at the beginning.  If you take that line out, you will get an access violation.  The call to fs.Free will do nothing if fs = nil (which will be the case if the file doesn't exist).  If fs = nil, then the call to Free is redundant.

JB
0
 
LVL 4

Author Comment

by:StevenB
ID: 1351370
 Hmmm, JimBob, it seems you're right, now I'm confused.

  Firstly, when I refer to an exception handler I mean a Try..Except block. When I say resource protection block I mean Try..Finally. Now I was always under the impression that ALL resources should be very carefully looked after and protected with religous fervor, as a result I put all my free statements within a finally block, so that they are ALWAYS called and I'm not trickling precious pointers down the drain. I'm aware that if the Create method fails, then it returns nil, and as a result the  Free method will do nothing, but I thought nothing of it, since the Free method is supposedly 'Safe' and will never cause an exception (Ah, how naive).
  This logic lead me to write code that looked like this:

  try
    *Create the object*
    *Use the object*
  finally
    *Free the object*
  end

  Doing this means that all exceptions caused during the creation and utilisation of the object cause will not prevent it from being destroyed. It seems however that the TFileStream behaves a little differently. If the Create fails then calling Free causes an exception, so it appears the code must be written :

  *Create*
  try
    *Use*
  finally
    *Free*
  end;

  Now, if you do this with other objects and the create fails then you never free the object and you are wasting resources.

  Err, am I right?

  Steven.

  P.S. anilms, sorry please bear with me, I'm getting there.
0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 1351371
Hi Steven

You are right, but I think there is one further point.  The usual way of using try...finally is your 2nd method (for ALL objects, not just TFileStream):
*Create*
try
    *Use*
finally
    *Free*
end;

If the object is NOT successfully created at *Create* an exception is generated and:
1) The code never reaches the "try" statement, which is ok because you don't need to free the object.
2) No memory is wasted (except memory which the object's constructor creates, which has nothing to do with the object itself.  The programmer has no control over this kind of thing).  The object therefore hasn't been initialized and you must not free it.  (If the pointer to the object was nil it will remain nil, so you can free it but it does nothing...)

An exception occurs when the pointer to the object is NOT nil, and the object's constructor fails, and you then try to free the object.  If the constructor fails, the pointer to the object is unchanged, i.e. still points to whereever it was pointing before the call to the constructor.  That is why in my example (in previous comment) if you take "fs := nil;" out, then fs is pointing to somewhere in memory (because of "var fs: TFileStream;") and when the constructor fails fs is still pointing to somewhere in memory, so that when you free fs you get a GPF...

I hope this makes sense.  I said it a bit clumsily...
JB
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 4

Author Comment

by:StevenB
ID: 1351372
 Hi,

  I've been away/busy for a few days, but I've had a chance to assimilate all the stuff you've been saying. I took a deep breath and rewrote the code in question, but this time I used the resource protection format that you suggested JimBob. This approach along with other twists and tweaks seems to have eliminated the problem. It is quite likely that I've been attempting to access the TFileStream at some point which was creating the violations.
  Now, my original question was to find a different way to check the status of a file and anilms has provided a useful answer in that regard, however the root of the problem lay elsewhere it seems, and I'm very grateful to JimBob for your help. I reckon the best thing to do is to grade anilms answer (since it does answer the question asked, and JimBob has more than enough points anyway :) ) Cheers for your help JimBob, I'll buy you a pint if you're ever in Leeds (or give you an extra high grade if you answer any of my other questions ever).

  Steven.

  (I'm posting this as a comment first, so that everyone can see it without having to pay, I'll grade in a few days)
0
 
LVL 4

Author Comment

by:StevenB
ID: 1351373
 Cheers for your help.

  Steven.
0
 
LVL 1

Expert Comment

by:Pandora
ID: 8016559
Hi all, just wondered if I could tag on a quick quicky!  Does this mean that all local objects must be explicitly set to nil when you create them? I thought Delphi did that as part of its initialisation but if jimbob's right about the requirement of the fs := nil; line in his example above that kinda implies not!?

0
 
LVL 1

Expert Comment

by:Pandora
ID: 8016861
Hi all, just wondered if I could tag on a quick quicky!  Does this mean that all local objects must be explicitly set to nil when you create them? I thought Delphi did that as part of its initialisation but if jimbob's right about the requirement of the fs := nil; line in his example above that kinda implies not!? I vaguely remember doing this as at some point and the compiler telling me it was a redundant line. Hmm.

0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 8016864
Hi Pandora

Normally you would not need to set an object variable to nil before creating it. From what I remember of the discussion above, the problem was that the object's constructor was causing the problem, thus the object was never properly created and thus you could not call Free because the object variable was not valid.

BTW, when you declare an object variable (fs in the example above) Delphi does not initialize it to nil.

If the constructor fails the object variable is not changed, as shown in the e.g. below:

// fs is currently a "random" pointer.
try
  fs := TFileStream.Create('Bad file name.txt', fmOpenRead);
except
  // Exception occurs and fs is still a random pointer.
end;

The following code is safe because the exception in line 1 means that the execution never gets to line 5.

1:  fs := TFileStream.Create('Bad file name.txt', fmOpenRead);
2:  try
3:    // Do something with fs...
4:  finally
5:    fs.Free;
6:  end;

The key is putting the finally and except blocks in the right place. Note in the example below that you do not need to initialize fs to nil because if TFileStream.Create fails then you don't call fs.Free.

 1:  try
 2:    fs := TFileStream.Create('Bad file name.txt', fmOpenRead);
 3:    try
 4:      // Do something with fs...
 5:    finally
 6:      fs.Free;
 7:    end;
 8:  except
 9:    // Jumps to here from line 2 because TFileStream.Create fails.
10:  end;

Regards,
JimBob
0
 
LVL 1

Expert Comment

by:Pandora
ID: 8024293
Aha! more by fluke than anything else thats what I do, so phew! thanks jimbob that's really kind of you, much appreciated. Best wishes P :)
0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 8024404
You're welcome.
JB
0
 
LVL 4

Author Comment

by:StevenB
ID: 8053783
 It's interesting to look back at old questions and realise how much you've learned over the last six years :o)

  Incidentally JimBob, I feel a little guilty about not giving you any points for this question, especially since you've been monitoring it all this time, please find your deserved reward here:

http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20535731.html

  :o)
0
 
LVL 4

Author Comment

by:StevenB
ID: 8053819
 Pandora, there is a nice little "trick" where setting objects to nil before you create them can save you lines of code. If you are creating multiple objects then it would be traditionally safest to protect them all in their own nested resource protection block:

1)   A := TSomething.Create();
2)   try
3)     B := TSomething.Create();
4)     try
5)       A.DoSomething;
6)       B.DoSomething;
7)     finally
8)       B.Free;
9)     end;
10)  finally
11)    A.Free;
12)  end;


However you can take advantage of the fact that freeing an object is safe if the variable is nil, by rewriting the above code as:

1)   B := nil;
2)   A := TSomething.Create();
3)   try
4)     B := TSomething.Create();
5)     A.DoSomething;
6)     B.DoSomething;
7)   finally
8)     B.Free;
9)     A.Free;
10)  end;

by initialising object variables to nil you can arrange your code so that you only need one resource protection block, nomatter how many objects you are creating.

  Steven
0
 
LVL 1

Expert Comment

by:Pandora
ID: 8053927
Thanks Steven - & what a happy ending, Jimbob gets his points, you've got older & wiser & I've got, well, I've got 6 more years worth of PAQ's to wade through!! Thanks to both of you, best wishes P :)
0
 
LVL 4

Author Comment

by:StevenB
ID: 8053946
No worries P :o)
0
 
LVL 5

Expert Comment

by:JimBob091197
ID: 8055394
6 years - hard to believe it's been that long!!

Thanks for the offer of pts, Steven. No need to feel guilty! Glad to help.

Regards,
JB
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now