filesystem.psm1
set-strictMode -version latest
$winApi = add-type -name filesystem -namespace tq84 -passThru -memberDefinition '
[DllImport("shlwapi.dll", CharSet=CharSet.Auto)]
public static extern bool PathRelativePathTo(
[Out] System.Text.StringBuilder pszPath,
[In ] string pszFrom, [In ] System.IO.FileAttributes dwAttrFrom,
[In ] string pszTo , [In ] System.IO.FileAttributes dwAttrTo
);
'
function initialize-emptyDirectory {
#
# Create directory $directoryName
# If it already exists, clean it
#
param (
[string] $directoryName
)
if (test-path $directoryName) {
try {
#
# Try to remove directory.
# Use -errorAction stop so that catch block
# is executed if unsuccessful
#
remove-item -recurse -force -errorAction stop $directoryName
}
catch {
return $null
}
}
new-item $directoryName -type directory
}
function resolve-relativePath {
#
# Inspired by https://get-carbon.org/Resolve-RelativePath.html
#
# resolve-relativepath .\dir\subdir .\dir\another\sub\dir\file.txt
#
param (
[parameter (
mandatory = $true
)][string ] $dir ,
[parameter (
mandatory = $true
)][string[] ] $dest
)
#
# The WinAPI function PathRelativePathTo requires directory separators to be backslashes:
#
$dir = $dir -replace '/', '\'
$dest = $dest -replace '/', '\'
$relPath = new-object System.Text.StringBuilder 260
[string[]] $ret = @()
foreach ($dest_ in $dest) {
$ok = [tq84.filesystem]::PathRelativePathTo($relPath, $dir, [System.IO.FileAttributes]::Directory, $dest_, [System.IO.FileAttributes]::Normal)
$ret += $relPath.ToString()
}
return $ret
}
function write-file {
#
# write-file C:\users\rny\test\more\test\test\test.txt "one`ntwo`nthree"
# write-file ./foo/bar/baz/utf8.txt "B�rlauch"
# write-file ./foo/bar/baz/win-1252.txt "B�rlauch`nLibert�, Fraternit�, Kamillentee" ( [System.Text.Encoding]::GetEncoding(1252) )
#
param (
[parameter (mandatory=$true)]
[string] $file,
[parameter (mandatory=$true)]
[string] $content,
[parameter (mandatory=$false)]
[System.Text.Encoding] $enc = [System.Text.UTF8Encoding]::new($false) # UTF8 without BOM
)
$abs_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($file)
$abs_dir = $ExecutionContext.SessionState.Path.ParseParent($abs_path, $null)
if (! (test-path $abs_dir)) {
$null = mkdir $abs_dir
}
if (test-path $abs_path) {
remove-item $abs_path
}
[System.IO.File]::WriteAllText($abs_path, $content, $enc)
}
function test-fileLock {
#
# Inspired by
#
# http://mspowershell.blogspot.com/2008/07/locked-file-detection.html
#
# Attempts to open a file and trap the resulting error if the file is already open/locked
param (
[parameter (mandatory=$true)]
[string]$filePath
)
if (! (test-path $filePath) ) {
return $null
}
$filelocked = $false
$fileInfo = new-object System.IO.FileInfo $filePath
trap {
set-variable -name filelocked -value $true -scope 1
# $fileLocked = $true
continue
}
$fileStream = $fileInfo.Open( [System.IO.FileMode]::OpenOrCreate,[System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None )
if ($fileStream) {
$fileStream.Close()
}
$fileLocked
}
function get-openFileProcess {
#
# Copied and adapted from
# https://github.com/pldmgg/misc-powershell/blob/master/MyFunctions/PowerShellCore_Compatible/Get-FileLockProcess.ps1
#
[cmdletBinding()]
param(
[parameter(mandatory=$true)]
[string] $filePath
)
if (! $(test-path $filePath)) {
write-error "The path $filePath was not found! Halting!"
return
}
$csSrc = @"
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System;
using System.Diagnostics;
namespace tq84 {
static public class rstrtmgr {
[StructLayout(LayoutKind.Sequential)]
struct RM_UNIQUE_PROCESS {
public int dwProcessId;
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
}
const int RmRebootReasonNone = 0;
const int CCH_RM_MAX_APP_NAME = 255;
const int CCH_RM_MAX_SVC_NAME = 63;
enum RM_APP_TYPE {
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct RM_PROCESS_INFO {
public RM_UNIQUE_PROCESS Process;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
public string strAppName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
public string strServiceShortName;
public RM_APP_TYPE ApplicationType;
public uint AppStatus;
public uint TSSessionId;
[MarshalAs(UnmanagedType.Bool)]
public bool bRestartable;
}
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
static extern int RmRegisterResources(
uint pSessionHandle,
UInt32 nFiles,
string[] rgsFilenames,
UInt32 nApplications,
[In] RM_UNIQUE_PROCESS[] rgApplications,
UInt32 nServices,
string[] rgsServiceNames);
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
[DllImport("rstrtmgr.dll")]
static extern int RmEndSession(uint pSessionHandle);
[DllImport("rstrtmgr.dll")]
static extern int RmGetList(
uint dwSessionHandle,
out uint pnProcInfoNeeded,
ref uint pnProcInfo,
[In, Out] RM_PROCESS_INFO[] rgAffectedApps,
ref uint lpdwRebootReasons);
//
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
// https://github.com/wyday/wyupdate/blob/main/frmFilesInUse.cs
//
static public List<Process> GetOpenFileProcess(string path) {
uint handle;
string key = Guid.NewGuid().ToString();
List<Process> processes = new List<Process>();
int res = RmStartSession(out handle, 0, key);
if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker.");
try {
const int ERROR_MORE_DATA = 234;
uint pnProcInfoNeeded = 0,
pnProcInfo = 0,
lpdwRebootReasons = RmRebootReasonNone;
string[] resources = new string[] { path }; // Just checking on one resource.
res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
if (res != 0) throw new Exception("Could not register resource.");
//Note: there's a race condition here -- the first call to RmGetList() returns
// the total number of process. However, when we call RmGetList() again to get
// the actual processes this number may have increased.
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
if (res == ERROR_MORE_DATA) {
// Create an array to store the process results
RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
pnProcInfo = pnProcInfoNeeded;
// Get the list
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
if (res == 0) {
processes = new List<Process>((int)pnProcInfo);
// Enumerate all of the results and add them to the
// list to be returned
for (int i = 0; i < pnProcInfo; i++) {
try
{
processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
}
// catch the error -- in case the process is no longer running
catch (ArgumentException) { }
}
}
else throw new Exception("Could not list processes locking resource.");
}
else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
}
finally {
RmEndSession(handle);
}
return processes;
}
}
}
"@
add-type -typeDef $csSrc
return [tq84.rstrtmgr]::GetOpenFileProcess($filePath)
<# TODO: LINUX
if ($PSVersionTable.Platform -ne $null -and $PSVersionTable.Platform -ne "Win32NT") {
$lsofOutput = lsof $filePath
function Parse-lsofStrings ($lsofOutput, $Index) {
$($lsofOutput[$Index] -split " " | foreach {
if (![String]::IsNullOrWhiteSpace($_)) {
$_
}
}).Trim()
}
$lsofOutputHeaders = Parse-lsofStrings -lsofOutput $lsofOutput -Index 0
$lsofOutputValues = Parse-lsofStrings -lsofOutput $lsofOutput -Index 1
$Result = [pscustomobject]@{}
for ($i=0; $i -lt $lsofOutputHeaders.Count; $i++) {
$Result | Add-Member -MemberType NoteProperty -Name $lsofOutputHeaders[$i] -Value $lsofOutputValues[$i]
}
}
#>
$Result
}
function set-locationDocuments() {
set-location ([System.Environment]::GetFolderPath('MyDocuments'))
}