A fájlok zárolása több szempontból is érdekes lehet számunkra. Egyrészt, ha pont nem szeretnénk, hogy egy fájl zárolva legyen, másrészt, ha éppen zárolni akarunk egy fájlt, hogy más ne férhessen hozzá.
A fájlok megnyitása a Windows mélyében egy összetett folyamat: fogantyú igénylése a fájlhoz, a jogosultságok ellenőrzése, fájl zárolása, fájl tartalmának olvasása vagy írása, fogantyú eleresztése. Ezt egyszerű felhasználóként szerencsére nem kell, hogy megtapasztaljuk, de néha jól jön, hogy ezeket a háttérfolyamatokat mi is vezérelhetjük. Ehhez a [System.IO.File ] osztály Open… jellegű statikus metódusait használhatjuk fel. Nézzük ezeket:
[1] PS C:\> [io.file] | gm -Static -Name open* | ft -Wrap
TypeName: System.IO.File
Name MemberType Definition
---- ---------- ----------
Open Method static System.IO.FileStream Open(string path, System.IO.F
ileMode mode), static System.IO.FileStream Open(string pa
th, System.IO.FileMode mode, System.IO.FileAccess access)
, static System.IO.FileStream Open(string path, System.IO
.FileMode mode, System.IO.FileAccess access, System.IO.Fi
leShare share)
OpenRead Method static System.IO.FileStream OpenRead(string path)
OpenText Method static System.IO.StreamReader OpenText(string path)
OpenWrite Method static System.IO.FileStream OpenWrite(string path)
Az OpenRead olvasásra, az OpenWrite írásra, az OpenText szövegként való olvasásra próbálja megnyitni nyitva is tartani a fájlt. A „sima” Open meg teljes finomhangolást ad a kezünkbe a megnyitás módját illetően. Mindegyik megnyitási metódusban az a közös, hogy visszatérésükkor egy fogantyú objektumot adnak vissza, amin keresztül kapcsolatban maradunk a megnyitott fájlunkkal egészen addig, amíg be nem zárjuk azt. Fontos, hogy az így megkapott fogantyút ne felejtsük el betenni egy változóba, hiszen ha ezt nem tesszük meg, akkor nem tudjuk ezt újra megkaparintani, hiszen már egyszer zárolta számunkra a fájlt az operációs rendszer, így másodjára már el fogja utasítani, és csak a PowerShell alkalmazás bezárásával szabadul fel újra a fájl.
Nézzünk akkor ezek felhasználására példákat. Elsőként tegyük rá a kezünket egy fájlra olvasási jelleggel:
[2] PS C:\> $h = [io.file]::OpenRead("C:\ee\a.txt")
Ezután ez lett a $h változóban:
[3] PS C:\> $h
CanRead : True
CanWrite : False
CanSeek : True
IsAsync : False
Length : 780
Name : C:\ee\a.txt
Position : 0
Handle : 1888
SafeFileHandle : Microsoft.Win32.SafeHandles.SafeFileHandle
CanTimeout : False
ReadTimeout :
WriteTimeout :
Látható, hogy a kérésünknek megfelelő fogantyút kaptunk vissza, azaz olvasni és keresni tudnánk a fájlban, de írni nem. Nézzük meg a $h tagjellemzőit is:
[4] PS C:\> $h | gm
TypeName: System.IO.FileStream
Name MemberType Definition
---- ---------- ----------
BeginRead Method System.IAsyncResult BeginRead(byte[] a...
BeginWrite Method System.IAsyncResult BeginWrite(byte[] ...
Close Method System.Void Close()
CreateObjRef Method System.Runtime.Remoting.ObjRef CreateO...
Dispose Method System.Void Dispose()
EndRead Method int EndRead(System.IAsyncResult asyncR...
EndWrite Method System.Void EndWrite(System.IAsyncResu...
Equals Method bool Equals(System.Object obj)
Flush Method System.Void Flush()
GetAccessControl Method System.Security.AccessControl.FileSecu...
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetType Method type GetType()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
Lock Method System.Void Lock(long position, long l...
Read Method int Read(byte[] array, int offset, int...
ReadByte Method int ReadByte()
Seek Method long Seek(long offset, System.IO.SeekO...
SetAccessControl Method System.Void SetAccessControl(System.Se...
SetLength Method System.Void SetLength(long value)
ToString Method string ToString()
Unlock Method System.Void Unlock(long position, long...
Write Method System.Void Write(byte[] array, int of...
WriteByte Method System.Void WriteByte(byte value)
CanRead Property System.Boolean CanRead {get;}
CanSeek Property System.Boolean CanSeek {get;}
CanTimeout Property System.Boolean CanTimeout {get;}
CanWrite Property System.Boolean CanWrite {get;}
Handle Property System.IntPtr Handle {get;}
IsAsync Property System.Boolean IsAsync {get;}
Length Property System.Int64 Length {get;}
Name Property System.String Name {get;}
Position Property System.Int64 Position {get;set;}
ReadTimeout Property System.Int32 ReadTimeout {get;set;}
SafeFileHandle Property Microsoft.Win32.SafeHandles.SafeFileHa...
WriteTimeout Property System.Int32 WriteTimeout {get;set;}
Látható, hogy a különböző elemi fájlkezelő műveletek metódusai mind ott sorakoznak. Van itt egy érdekes és egyben praktikus is, a SetLength. Ezzel be lehet állítani egy fájl méretét, azaz hogy mennyi területet foglaljon számára az operációs rendszer. Ez azért jó, mert így már jó előre le lehet foglalni a lemezterületet, és ha adatfolyamként írunk bele adatot (lásd következő fejezet), akkor sokkal gyorsabban fog ez menni, mivel nem kell folyamatosan újabb és újabb területeket megszerezni a fájlunk számára. Hogyan nézne ki akkor egy ilyen függvény, mellyel tetszőleges méretű fájlt lehet létrehozni:
function New-File {
param ($path, $size)
$h = [io.file]::Create($path)
$h.setlength($size)
$h.close()
}
Ha újra meg akarjuk ragadni ezt a fájlt olvasásra, akkor ez jelenleg sikerül, mivel az OpenRead az nem kizárólagosan zárolja a saját céljaira a fájlt:
[5] PS C:\> $h2 = [io.file]::OpenRead("C:\ee\a.txt")
[6] PS C:\> $h2
CanRead : True
CanWrite : False
CanSeek : True
IsAsync : False
Length : 780
Name : C:\ee\a.txt
Position : 0
Handle : 2300
SafeFileHandle : Microsoft.Win32.SafeHandles.SafeFileHandle
CanTimeout : False
ReadTimeout :
WriteTimeout :
Ha már nincs szükségünk a fájlra, akkor engedjük el a fogantyút a Close metódussal:
[7] PS C:\> $h.close()
[8] PS C:\> $h2.close()
Kizárólagosan is megnyithatjuk a fájlt, de ehhez az Open metódust kell használjuk, mert itt tudjuk a megosztási lehetőségeket meghatározni:
[21] PS C:\> $h = [io.file]::Open("C:\ee\a.txt","Open","Read","None")
Az Open metódusnál az első paraméter a fájl elérési útja. A második a megnyitás módja, azaz sima megnyitás legyen, létrehozás, felülírás, hozzáfűzés vagy ezek kombinációja. A pontos lehetőségek a System.IO.FileMode felsorolástípusból olvashatók ki. Miután én most egy meglevő fájlt akarok olvasni, ezért nekem elég az Open fájlmód. A harmadik paraméter az igényelt tevékenység, ami lehet Read, Write vagy ReadWrite. Nálam ez most Read. Végül azt állíthatjuk be, hogy másoknak mit engedek tenni ezzel a fájllal, amíg én is dolgozom rajta. Ez lehet semmi (None), olvasás, írás, olvasás-írás, törlés. Most én önző vagyok, másoknak semmilyen hozzáférést nem engedek.
Ha ezek után újból meg akarom nyitni ezt a fájl, akkor már hibát kapok:
[22] PS C:\> $h2 = [io.file]::OpenRead("C:\ee\a.txt")
Exception calling "OpenRead" with
"1" argument(s): "The process cannot access
the file 'C:\ee\a.txt' because it is being used by
another process."
At line:1 char:26
+ $h2 = [io.file]::OpenRead <<<<
("C:\ee\a.txt")
+
CategoryInfo : NotSpecified: (:)
[], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
Mire lehet mindezt felhasználni? Az elsődleges indok persze magának a fájlnak a megnyitása és zárolása, de ezzel a módszerrel lehet a pont azt is megvizsgálni, hogy egy fájl zárolva van-e már előlünk? Ha megpróbáljuk zárolni, de nem sikerül, akkor az azt jelenti, hogy valaki más már zárolta. Nézzünk erre egy függvényt, ami felderíti, hogy melyek a zárolt fájlok:
function Test-FileLock {
param(
[Parameter(
Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true
)]
[Alias("FullName")]
[string]
$path
)
process{
$file = Get-Item -Path $path -ErrorAction silentlycontinue -Force
if($file -and !$file.psiscontainer){
$readlock = $true
try{
$h = [io.file]::Open($path,"Open","Read","None")
$readlock = $false
}
catch{}
if(!$readlock){
$h.close()
}
$writelock = $true
try{
$h = [io.file]::Open($path,"Open","Write","None")
$writelock = $false
}
catch{}
if(!$writelock){
$h.close()
}
Add-Member -InputObject $file -MemberType noteproperty -Name LockedForRead -Value $readlock
Add-Member -InputObject $file -MemberType noteproperty -Name LockedForWrite -Value $writelock
$file
}
}
}
Például ezzel a c:\ könyvtárra így lehet felderíteni a zárolt fájlokat:
[29] PS C:\> dir c:\ -Force | Test-FileLock | ft name, locked*
Name LockedForRead LockedForWrite
---- ------------- --------------
hiberfil.sys True True
NemZárolt.txt False False
pagefile.sys True True
Megjegyzés
Természetesen ezzel a módszerrel nem csak akkor fogunk zárolási jelzést kapni egy fájlra, ha tényleg zárolva van, hanem akkor is, ha nincs megfelelő jogosultságunk a fájl olvasásához vagy írásához.