Recent

Author Topic: Opening files for reading with CreateFileW if another program has opened them  (Read 5861 times)

Gizmo

  • Hero Member
  • *****
  • Posts: 831
Hi

I am creating handles to files that I need to only read (not create new versions of, not write to, and not create if they don't exist) using (courtesy of EngKin at http://forum.lazarus.freepascal.org/index.php/topic,24765.msg149502.html#msg149502):

Code: [Select]
{$ifdef windows}
  if IOResult <> 0 then
  begin
    ws := UTF8ToUTF16(FileName);
    security.nLength := Sizeof(TSecurityAttributes);
    security.bInheritHandle := true;
    security.lpSecurityDescriptor := nil;
    filerec(f).handle:=CreateFileW(@ws[1],GENERIC_READ, file_Share_Read, @security, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  end;
{$endif windows}

However, if the file is open by another application, my program reports the "Invalid file handle" error. I gather the key element is the dwCreationDisposition part...in my case, "OPEN_EXISTING". I read http://msdn.microsoft.com/en-gb/library/windows/desktop/aa363858%28v=vs.85%29.aspx and gather my options are :


CREATE_ALWAYS : Creates a new file, always.
CREATE_NEW : Creates a new file, only if it does not already exist.
OPEN_ALWAYS : Opens a file, always. // sounded promising, except it states if the file doesn't exist, it creates it!
OPEN_EXISTING : Opens a file or device, only if it exists. // my current option, but seems to fail if another program has it open already
TRUNCATE_EXISTING : Opens a file and truncates it so that its size is zero bytes, only if it exists.

I have looked at the API docs here but I'm still rather unclear as to what value to pass to ensure that my program can read the file, even if it is open by something else?

taazz

  • Hero Member
  • *****
  • Posts: 5368
I have looked at the API docs here but I'm still rather unclear as to what value to pass to ensure that my program can read the file, even if it is open by something else?

You can't the first one that opens the file defines the rules that others can access it. try to make sure that the share mode does not disallow anything. eg if the other process has the file open for write and you  do not include the share_write mode then your file will fail to open. But if the first has open it with share mode noe then what ever you define as share mode will not allow you to open the file. For more details http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858%28v=vs.85%29.aspx.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Gizmo

  • Hero Member
  • *****
  • Posts: 831
Thanks Taaz.

So the best all round option is this? :

Code: [Select]
   filerec(f).handle:=CreateFileW(@ws[1],GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, @security, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

taazz

  • Hero Member
  • *****
  • Posts: 5368
the best all around option would be to wait until the file is closed from the other app. and then open it with exclusive access, otherwise the calculated hash might be invalid even before the calculation finishes. eg data changing after you have read them or even data appended after you have finished the hash calculation.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Gizmo

  • Hero Member
  • *****
  • Posts: 831
OK Taaz.

Rather than my program displaying the error as seen in the screenshot attached, is there a way to ensure the program skips that one, for now, and then either re-tries later or at least tells the user "File X could not be accessed and needs to be closed" rather than just "Invalid file handle"?


taazz

  • Hero Member
  • *****
  • Posts: 5368
Code: [Select]
   filerec(f).handle:=CreateFileW(@ws[1],GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, @security, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

  if filerec(f).handle = INVALID_HANDLE_VALUE then RaiseLastOSError;
You know all this would have been clear if you have read the link I post it.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Gizmo

  • Hero Member
  • *****
  • Posts: 831
Apologies - I use RaiseLastOSError elsewhere in my program but in an area where there's no point to continue.

In this instance, I could do with it simply logging the filename that it couldn't deal with, and continue with the others and then come back to the one(s) it could not access. RaiseLastOSError 'just' throws the error and stops on that file.

But you've helped me enough Taaz, thanks. I will keep digging for the answer.

taazz

  • Hero Member
  • *****
  • Posts: 5368
Code: [Select]

  if filerec(f).handle = INVALID_HANDLE_VALUE then
     Memo1.Lines.add(
     //or
     // log(
     // writeLn(
    'Can''t Open file <filename>'+LineEnding+'OS error Message : ' + SysErrorMessage(GetLastOSError));
    //Continue; //used inside a loop to jump to the next iteration.
  end;
« Last Edit: July 29, 2014, 11:38:17 pm by taazz »
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11458
  • FPC developer.
Note that in FPC 2.7.1 this is equal to the below portable code:

Code: [Select]
var x : utf8string;

begin
  x:='something utf8';
  filemode:=fmsharedenyNone;  // On Windows also allows delete.
  assignfile(f,x);
  {$I-}
  Reset(f);
  {$I+}
  everythingok:=ioresult=0;
  if everythingok then 
    begin
       handle:=getfilehandle(f);
       .. your code here
    end;
 


 

TinyPortal © 2005-2018