Archive: NSIS FileOpen in read mode is shared?


NSIS FileOpen in read mode is shared?
  Hello,

When I open a file in mode "r" - FileOpen "filename" r, does it lock the file for exclusive use? I believe, in "r" mode, the process shouldn't lock the resource.

I have a common file which is written by a java program and read by NSIS. When NSIS opens the file in "r" mode and reads it, the java program throws an exception during its write with the message that the file is in use by another program.

Is there a way in NSIS to open shared resource for reading without locking it?

Thank you!


When a file is open for reading, it usually is shared for reading (i.e. no exclusive lock), but not for writing.

And it seems that is what NSIS does:

HANDLE NSISCALL myOpenFile(const char *fn, DWORD da, DWORD cd)

{
int attr = GetFileAttributes(fn);
returnCreateFile(
fn,
da,
FILE_SHARE_READ,
NULL,
cd,
attr == INVALID_FILE_ATTRIBUTES ? 0 : attr,
NULL
);
}
Sharing a file for writing, while you still are reading from it, is inherently dangerous...

(And, as FILE_SHARE_READ is hardcoded, you cannot change it without re-compiling the EXE headers)

Just made that, hope that can help.. ;)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Easily opens files with more options than FileOpen
>;; P1 :o: Handle returned
>;; P2 :i: File name
>;; P3 :i: Access Mode
>;; 'r' : Readonly
>;; 'w' : Writeonly
>;; 'rw' : Read+Write
>;; P4 :i: Share mode
>;; '' : None
>;; 'r' : Readonly
>;; 'rw' : Read+Write
>;; 'rwd' : Read+Write+Delete
>;; P5 :i: Create mode
>;; '' : Open existing only
>;; 'c' : Create if not exist
>;; 'o' : Create and Overwrite
>;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
!
define FileOpenEx "!insertmacro _FileOpenEx"
>!macro _FileOpenEx _Handle_ _File_ _Access_ _Share_ _Create_
Push "${_Create_}"
Push "${_Share_}"
Push "${_Access_}"
Push "${_File_}"
Call FileOpenEx
Pop${_Handle_}
!macroend

>Function FileOpenEx ;; $0:File, $1:Access, $2:Sharing, $3:Create
Exch$0
Exch
Exch$1
Exch 2
Exch$2
Exch 3
Exch$3

StrCmp"r" $1 0 +3
StrCpy$1 0x80000000 ;; GENERIC_READ
Goto+6
StrCmp"w" $1 0 +3
StrCpy$1 0x40000000 ;; GENERIC_WRITE
Goto+3
StrCmp"rw" $1 0 +3
StrCpy$1 0xC0000000 ;; GENERIC_READ | GENERIC_WRITE

StrCmp"" $2 0 +3
StrCpy$2 0 ;; FILE_SHARE_NONE
Goto+9
StrCmp"r" $2 0 +3
StrCpy$2 1 ;; FILE_SHARE_READ
Goto+6
StrCmp"rw" $2 0 +3
StrCpy$2 3 ;; FILE_SHARE_READ | FILE_SHARE_WRITE
Goto+3
StrCmp"rwd" $2 0 +3
StrCpy$2 7 ;; FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE

StrCmp"" $3 0 +3
StrCpy$3 3 ;; OPEN_EXISTING
Goto+6
StrCmp"c" $3 0 +3
StrCpy$3 4 ;; OPEN_ALWAYS
Goto+3
StrCmp"o" $3 0 +3
StrCpy$3 2 ;; CREATE_ALWAYS

System::Call 'Kernel32::CreateFile(t, i, i, i, i, i, i) i (r0, r1, r2, 0, r3, 0x80, 0) .r2' ;; Open/Create file

Pop$3
Pop$0
Pop$1
Exch$2
FunctionEnd
>
Example:
${FileOpenEx} $0 "SomeFile.ext" "r" "rwd" "" ;; opens SomeFile.ext in read mode with full share access, if it exists

*

It might be worth changing all those run time string comparisons to compile time (by using !if).

Stu


Thank you, all :)


If you really nead maximum speed, ex: if you open a large amount of files in a tight loop, you may want a more direct approach, something like:


"!insertmacro _ApiCreateFile"

>!macro _ApiCreateFile _Handle_ _File_ _Access_ _Share_ _Create_
System
::Call 'Kernel32::CreateFile(t, i, i, i, i, i, i) i ("${_File_}", ${_Access_}, ${_Share_}, 0, ${_Create_}, 0x80, 0) .s' ;; Open/Create file
Pop${_Handle_}
!macroend
>