PSBasicInfo.psm1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
$script:memberDefinition = @'
 
    public struct FILE_BASIC_INFO
    {
        [MarshalAs(UnmanagedType.I8)]
        public Int64 CreationTime;
        [MarshalAs(UnmanagedType.I8)]
        public Int64 LastAccessTime;
        [MarshalAs(UnmanagedType.I8)]
        public Int64 LastWriteTime;
        [MarshalAs(UnmanagedType.I8)]
        public Int64 ChangeTime;
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 FileAttributes;
    }
 
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr CreateFile(
        [MarshalAs(UnmanagedType.LPTStr)] string filename,
        [MarshalAs(UnmanagedType.U4)] UInt32 access,
        [MarshalAs(UnmanagedType.U4)] UInt32 share,
        IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
        [MarshalAs(UnmanagedType.U4)] UInt32 creationDisposition,
        [MarshalAs(UnmanagedType.U4)] UInt32 flagsAndAttributes,
        IntPtr templateFile);
 
    [DllImport("kernel32.dll", SetLastError=true)]
    public static extern bool CloseHandle(IntPtr hObject);
 
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool GetFileInformationByHandleEx(
        IntPtr hFile,
        int infoClass,
        out FILE_BASIC_INFO fileInfo,
        uint dwBufferSize);
 
'@
 

Add-Type -MemberDefinition $script:memberDefinition -Name 'FileBasicInfo' -Namespace 'PSBasicInfo'

function Get-ItemBasicInfo 
{
    <#
    .SYNOPSIS
        This function retrieves FILE_BASIC_INFO structure from file system item.
    .DESCRIPTION
        This structure contains ChangeTime property. The property will be updated any time metadata or data is changed. LastWriteTime will be updated only if data is changed.
    .EXAMPLE
        PS C:\> Get-Item D:\Test|Get-ItemBasicInfo
        Pipe result of Get-item to Get-ItemBasicInfo
    .EXAMPLE
        PS C:\> Get-ChildItem -Recurse D:\Test|Get-ItemBasicInfo
        Pipe result of Get-ChildItem to Get-ItemBasicInfo
    .INPUTS
        System.IO.FileSystemInfo
    .OUTPUTS
        PSCustomObject
    #>

    [CmdletBinding()]
    param(
        # Path to file or directory
        [Parameter(Mandatory = $true,
                Position = 0, 
                ValueFromPipeline = $true,
                ValueFromPipelineByPropertyName = $true)]
        [ValidateScript({Test-Path -Path $_.FullName})]
        [System.IO.FileSystemInfo]
        $Path
    )
    
    begin 
    {
        
    }
    
    process
    {
        $currentPath = $Path.FullName
        
        try
        {
            Write-Verbose "CreateFile: Open file $currentPath"
            $fileHandle = [PSBasicInfo.FileBasicInfo]::CreateFile($currentPath,
                [System.IO.FileAccess]::Read,
                [System.IO.FileShare]::ReadWrite,
                [System.IntPtr]::Zero,
                [System.IO.FileMode]::Open,
                [System.UInt32]0x02000000,
                [System.IntPtr]::Zero)
            
            if($fileHandle -eq -1)
            {
                throw "CreateFile: Error opening file $Path"
            }
            
            # Output object
            $fileBasicInfo = New-Object -TypeName PSBasicInfo.FileBasicInfo+FILE_BASIC_INFO
            
            Write-Verbose "GetFileInformationByHandleEx: Get basic info"
            $bRetrieved = [PSBasicInfo.FileBasicInfo]::GetFileInformationByHandleEx($fileHandle,0,
                [ref]$fileBasicInfo,
                [System.Runtime.InteropServices.Marshal]::SizeOf($fileBasicInfo))
            
            if(!$bRetrieved)
            {
                throw "GetFileInformationByHandleEx: Error retrieving item information"
            }
            
            # Return result
            [PSCustomObject]@{
                Item = $Path
                CreationTime = [System.DateTime]::FromFileTime($fileBasicInfo.CreationTime)
                LastAccessTime = [System.DateTime]::FromFileTime($fileBasicInfo.LastAccessTime)
                LastWriteTime  = [System.DateTime]::FromFileTime($fileBasicInfo.LastWriteTime)
                ChangeTime     = [System.DateTime]::FromFileTime($fileBasicInfo.ChangeTime)
                FileAttributes = $fileBasicInfo.FileAttributes
            }
        }
        catch 
        {
            throw $_
        }
        finally
        {
            Write-Verbose "CloseHandle: Close file $currentPath"
            $bClosed = [PSBasicInfo.FileBasicInfo]::CloseHandle($fileHandle)
            
            if(!$bClosed)
            {
                Write-Warning "CloseHandle: Error closing handle $fileHandle of $Path"
            }
        }
    }
}