#requires -version 2.0 ## PoshCode module v 3.6 ## UPDATED FOR CTP3 ## See comments for each function for changes ... ############################################################################################################## ## Provides cmdlets for working with scripts from the PoshCode Repository: ## Get-PoshCodeUpgrade - get the latest version of this script from the PoshCode server ## Get-PoshCode - Search for and download code snippets ## New-PoshCode - Upload new code snippets ## Remove-DownloadFlag - Unblock files downloaded from the internet ## Set-DownloadFlag - Mark files as downloaded (used by Get-PoshCode) ## Get-WebFile - Download ############################################################################################################## Set-StrictMode -Version Latest $PoshCode = "http://PoshCode.org/" | Add-Member -type NoteProperty -Name "UserName" -Value "Anonymous" -Passthru | Add-Member -type ScriptProperty -Name "ScriptLocation" -Value { $module = $null Get-Module PoshCode | Select -expand Path -EA "SilentlyContinue" | Tee -var module if(!$module) { # Try finding it by path, since it's not loaded as "PoshCode" Get-Module | ? {$_.Name -match "^$([RegEx]::Escape($PsScriptRoot))"} | Select -expand Path } } -Passthru | Add-Member -type ScriptProperty -Name "ModuleName" -Value { if( Get-Module PoshCode ) { "PoshCode" } else { Get-Module | ? {$_.Name -match "^$([RegEx]::Escape($PsScriptRoot))"} | Select -expand Name } } -Passthru | Add-Member -type NoteProperty -Name "ScriptVersion" -Value 3.5 -Passthru | Add-Member -type NoteProperty -Name "ApiVersion" -Value 1 -Passthru ## Set-DownloadFlag ############################################################################################################## ## marks code as being "remote" (i.e.: downloaded from the internet) ## That is, we set the alternate data stream, like IE7 or Firefox3 would ############################################################################################################## function Set-DownloadFlag { [CmdletBinding()] PARAM ( [Parameter(Position=0, Mandatory=$false, ValueFromPipelineByPropertyName=$true)] [Alias("FullName")] [string] $Path , [Switch]$Passthru ) $FS = new-object NTFS.FileStreams($Path) $null = $fs.add('Zone.Identifier') $stream = $fs.Item('Zone.Identifier').open() $sw = [System.IO.streamwriter]$stream $Sw.writeline('[ZoneTransfer]') $sw.writeline('ZoneID=4') $sw.close() $stream.close() if($Passthru){ Get-ChildItem $Path } } ## Remove-DownloadFlag ############################################################################################################## ## removes the alternate data stream that marks code as being "remote" (i.e.: downloaded from the internet) ############################################################################################################## function Remove-DownloadFlag { [CmdletBinding()] PARAM ( [Parameter(Position=0, Mandatory=$false, ValueFromPipelineByPropertyName=$true)] [Alias("FullName")] [string] $Path , [Switch]$Passthru ) $FS = new-object NTFS.FileStreams($Path) $null = $fs['Zone.Identifier'].delete() if($Passthru){ Get-ChildItem $Path } } ## New-PoshCode (formerly Send-Paste) ############################################################################################################## ## Uploads code to the PowerShell Script Repository and returns the url for you. ############################################################################################################## ## Usage: ## get-content myscript.ps1 | New-PoshCode "An example for you" "This is just to show how to do it" ## would send the script with the specified title and description ## ls *.ps1 | New-PoshCode -Keep Forever ## would flood the pastebin site with all your scripts, using filename as the title ## and a generic description, and mark them for storing indefinitely ## get-history -count 5 | % { $_.CommandLine } | New-PoshCode ## would paste the last 5 commands in your history! ############################################################################################################## ## History: ## v 3.1 - Fixed the $URL parameter so that it's settable again. *This* function should work on any pastebin site ## v 3.0 - Renamed to New-PoshCode. ## Removed the -Permanent switch, since this is now exclusive to the permanent repository ## v 2.1 - Changed some defaults ## - Added "PermanentPosh" switch ( -P ) to switch the upload to the PowerShellCentral repository ## v 2.0 - works with "pastebin" (including posh.jaykul.com/p/ and PowerShellCentral.com/scripts/) ## v 1.0 - Worked with a special pastebin ############################################################################################################## function New-PoshCode { [CmdletBinding()] PARAM( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] [Alias("FullName")] [string] $Path , [Parameter(Position=5, Mandatory=$true)][string]$Description , [Parameter(Position=10)][string]$Author = $($PoshCode.UserName) , [Parameter(Position=15)][PoshCodeLanguage]$Language="posh" , [Parameter(Position=20, Mandatory=$false)] [ValidateScript({ if($_ -match "^[dmf]") { return $true } else { throw "Please specify 'day', 'month', or 'forever'" } })] [string]$Keep="f" , [Alias("BaseName,Name")] [Parameter(Mandatory=$false, ValueFromPipelineByPropertyName=$true)] [string]$Title , [Parameter(Mandatory=$false)][string]$url= $($PoshCode) ) BEGIN { $null = [Reflection.Assembly]::LoadWithPartialName("System.Web") [string]$data = "" [string]$meta = "" if($language) { $meta = "format=" + [System.Web.HttpUtility]::UrlEncode($language) # $url = $url + "?" +$lang } else { $meta = "format=text" } # Note how simplified this is by switch -regex ($Keep) { "^d" { $meta += "&expiry=d" } "^m" { $meta += "&expiry=m" } "^f" { $meta += "&expiry=f" } } if($Description) { $meta += "&descrip=" + [System.Web.HttpUtility]::UrlEncode($Description) } else { $meta += "&descrip=" } $meta += "&poster=" + [System.Web.HttpUtility]::UrlEncode($Author) function Send-PoshCode ($meta, $title, $data, $url= $($PoshCode)) { $meta += "&paste=Send&posttitle=" + [System.Web.HttpUtility]::UrlEncode($Title) $data = $meta + "&code2=" + [System.Web.HttpUtility]::UrlEncode($data) $request = [System.Net.WebRequest]::Create($url) $request.ContentType = "application/x-www-form-urlencoded" $request.ContentLength = $data.Length $request.Method = "POST" $post = new-object IO.StreamWriter $request.GetRequestStream() $post.Write($data,0,$data.Length) $post.Flush() $post.Close() # $reader = new-object IO.StreamReader $request.GetResponse().GetResponseStream() ##,[Text.Encoding]::UTF8 # write-output $reader.ReadToEnd() # $reader.Close() write-output $request.GetResponse().ResponseUri.AbsoluteUri $request.Abort() } } PROCESS { $EAP = $ErrorActionPreference $ErrorActionPreference = "SilentlyContinue" if(Test-Path $Path -PathType Leaf) { $ErrorActionPreference = $EAP Write-Verbose $Path Write-Output $(Send-PoshCode $meta $Title $([string]::join("`n",(Get-Content $Path))) $url) } elseif(Test-Path $Path -PathType Container) { $ErrorActionPreference = $EAP Write-Error "Can't upload folders yet: $Path" } else { ## Todo, handle folders? $ErrorActionPreference = $EAP if(!$data -and !$Title){ $Title = Read-Host "Give us a title for your post" } $data += "`n" + $Path } } END { if($data) { Write-Output $(Send-PoshCode $meta $Title $data $url) } } } ## Get-PoshCode (download Repository script) ############################################################################################################## ## Downloads a specified script from a Pastbin.com based site, by Id ## ### OR ### ## Searches the powershellcentral.com/script site and returns lists of results ## All search terms are automatically surrounded with wildcards ## NOTE: powershellcentral.com currently uses MySql fulltext search syntax... ############################################################################################################## ## Usage: ## Get-PoshCode Authenticode ## will search the repository for scripts dealing with Authenticode, and list the results ## Normally, you will take one of those IDs and do this: ## Get-PoshCode 456 ## will download the script 456 and save it to file (based on it's name/contents) ## This example would yield a file: Get_Set Signature (CTP2).psm1 ## Get-PoshCode 456 -passthru ## would output the contents of that script into the pipeline, so eg: ## (Get-PoshCode 456 -passthru) -replace "AuthenticodeSignature","SillySig" ## Get-PoshCode 456 $ProfileDir\Authenticode.psm1 ## would download the script to Authenticode.ps1 in the specified directory ## Get-PoshCode SCOM | Get-PoshCode ## would search the repository for all scripts about SCOM, and then download them! ## Get-PoshCode SCOM | Get-PoshCode | Remove-DownloadFlag ## would search the repository for all scripts about SCOM, and then download them and unblock them ## see the comments on Set-DownloadFlag and Remove-DownloadFlag ############################################################################################################## ## History: ## v 3.4 - Add "-Language" parameter to force PowerShell only, fix upgrade to leave INVALID as .psm1 ## v 3.2 - Add "-Upgrade" switch to cause the script to upgrade itself. ## v 3.1 - Add "Huddled.PoshCode.ScriptInfo" to TypeInfo, so it can be formatted by a ps1xml ## - Add ConvertTo-Module function to try to rename .ps1 scripts to .psm1 ## - Fixed exceptions thrown by searches which return no results ## - Removed the auto-wildcards!!!! ## NOTE: to get the same results as before you must now put * on the front and end of searches ## This is so that searches on the website work the same as searches here... ## My intention is to improve the website's search instead of leaving this here. ## NOTE: the website currently will not search for words less than 4 characters long ## v 3.0 - Working against the new RSS-based API ## - And using ParameterSets, which work in CTP2 ## v 2.0 - Combined with Find-Poshcode into a single script ## v 1.0 - Working against our special pastebin ############################################################################################################## function Get-PoshCode { [CmdletBinding(DefaultParameterSetName="Download")] PARAM( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Search")] [string]$Query , [Parameter(Position=0, Mandatory=$true, ValueFromPipelineByPropertyName=$true, ParameterSetName="Download" )] [int]$Id , [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Upgrade")] [switch]$Upgrade , [Parameter(Position=1, Mandatory=$false, ValueFromPipelineByPropertyName=$true)] [Alias("FullName")] [string]$SaveAs , [Parameter(Position=2, Mandatory=$false, ValueFromPipelineByPropertyName=$true)] [ValidateSet('text','asp','bash','cpp','csharp','posh','vbnet','xml','all')] [string]$Language="posh" , [switch]$InBrowser , [switch]$Passthru , [Parameter(Mandatory=$false)][string]$url= $($PoshCode) ) PROCESS { Write-Debug "ParameterSet Name: $($PSCmdlet.ParameterSetName)" if($Language -eq 'all') { $Language = "" } switch($PSCmdlet.ParameterSetName) { "Search" { $results = @(([xml](Get-WebFile "$($url)api$($PoshCode.ApiVersion)/$($query)&lang=$($Language)" -passthru)).rss.channel.GetElementsByTagName("item")) if($results.Count -eq 0 ) { Write-Host "Zero Results for '$query'" -Fore Red -Back Black } else { $results | Select @{ n="Id";e={$($_.link -replace $url,'') -as [int]}}, @{n="Title";e={$_.title}}, @{n="Author";e={$_.creator}}, @{n="Date";e={$_.pubDate }}, @{n="Link";e={$_.guid.get_InnerText() }}, @{n="Description";e={"$($_.description.get_InnerText())`n" }} | ForEach { $_.PSObject.TypeNames.Insert( 0, "Huddled.PoshCode.ScriptInfo" ); $_ } } } "Download" { if(!$InBrowser) { if($Passthru) { Get-WebFile "$($url)?dl=$id" -Passthru } elseif($SaveAs) { Get-WebFile "$($url)?dl=$id" -fileName $SaveAs | ConvertTo-Module | Set-DownloadFlag -Passthru } else { Get-WebFile "$($url)?dl=$id" | ConvertTo-Module | Set-DownloadFlag -Passthru } } else { [Diagnostics.Process]::Start( "$($url)$id" ) } } "Upgrade" { Get-PoshCodeUpgrade } } } } ## Get-PoshCodeUpgrade ############################################################################################################## ## Downloads a new PoshCode script version, and archives old versions.. ############################################################################################################## ## History: ## v3.3 - Removes old versions, and checks the signature. ## v3.2 - First script version with Upgrade function ############################################################################################################## function Get-PoshCodeUpgrade { $VersionFile = [IO.Path]::ChangeExtension( $PoshCode.ScriptLocation, ("{0}{1}" -f $PoshCode.ScriptVersion, [IO.Path]::GetExtension($PoshCode.ScriptLocation))) # Copy it to make sure we don't loose it Copy-Item $PoshCode.ScriptLocation $VersionFile # Remove old ones ... Remove-Item ( [IO.Path]::ChangeExtension( $PoshCode.ScriptLocation, ".*$([IO.Path]::GetExtension( $($PoshCode.ScriptLocation) ))") ) -exclude ([IO.Path]::GetFileName($VersionFile)) -Confirm # Finally, get the new one $NewFile = Get-WebFile "$($PoshCode)PoshCode.psm1" -fileName ( [IO.Path]::ChangeExtension( $PoshCode.ScriptLocation, ".INVALID.ps1")) if( Test-Signature -File $NewFile ) { Move-Item $NewFile $PoshCode.ScriptLocation -Force -passthru | Remove-DownloadFlag -Passthru Add-Module $($PoshCode.ModuleName) -Force } else { Write-Error "Signature is Not Valid on new version." Move-Item $NewFile ([IO.Path]::ChangeExtension( $PoshCode.ScriptLocation, ".INVALID.psm1")) Get-Item ([IO.Path]::ChangeExtension( $PoshCode.ScriptLocation, ".INVALID.psm1")) } } ## Test-Signature - Returns true if the signature is valid OR is signed by "F05F583BB5EA4C90E3B9BF1BDD0B657701245BD5" function Test-Signature { [CmdletBinding(DefaultParameterSetName="File")] PARAM ( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Signature")] # We can't actually require the type, or we won't be able to check the fake ones from Joel's Authenticode module # [System.Management.Automation.Signature] $Signature , [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="File")] [System.IO.FileInfo] $File ) PROCESS { if($File -and (Test-Path $File -PathType Leaf)) { $Signature = Get-AuthenticodeSignature $File } if(!$Signature) { return $false } else { $result = $false; try { $result = ((($Signature.Status -eq "UnknownError") -and $Signature.SignerCertificate -and ($Signature.SignerCertificate.Thumbprint -eq "F05F583BB5EA4C90E3B9BF1BDD0B657701245BD5") ) -or $Signature.Status -eq "Valid" ) } catch { } finally { return $result } } } } filter ConvertTo-Module { $oldFile = $_ if( ([IO.Path]::GetExtension($oldFile) -eq ".ps1") -and [Regex]::Match( [IO.File]::ReadAllText($oldFile), "^[^#]*Export-ModuleMember.*", "MultiLine").Success ) { $fileName = [IO.Path]::ChangeExtension($oldFile, ".psm1") Move-Item $oldFile $fileName -Force Get-Item $fileName } else { $oldFile } } ## Get-WebFile (aka wget for PowerShell) ############################################################################################################## ## Downloads a file or page from the web ## History: ## v3.8 - Add UserAgent calculation and parameter ## v3.7 - Add file-name guessing and cleanup ## v3.6 - Add -Passthru switch to output TEXT files ## v3.5 - Add -Quiet switch to turn off the progress reports ... ## v3.4 - Add progress report for files which don't report size ## v3.3 - Add progress report for files which report their size ## v3.2 - Use the pure Stream object because StreamWriter is based on TextWriter: ## it was messing up binary files, and making mistakes with extended characters in text ## v3.1 - Unwrap the filename when it has quotes around it ## v3 - rewritten completely using HttpWebRequest + HttpWebResponse to figure out the file name, if possible ## v2 - adds a ton of parsing to make the output pretty ## added measuring the scripts involved in the command, (uses Tokenizer) ############################################################################################################## function Get-WebFile { param( $url = (Read-Host "The URL to download"), $fileName = $null, [switch]$Passthru, [switch]$quiet, [string]$UserAgent = "PoshCode/$($PoshCode.ScriptVersion)" ) Write-Verbose "Downloading '$url'" $req = [System.Net.HttpWebRequest]::Create($url); $req.UserAgent = $( "{0} (PowerShell {1}; .NET CLR {2}; {3}; http://PoshCode.org)" -f $UserAgent, $(if($Host.Version){$Host.Version}else{"1.0"}), [Environment]::Version, [Environment]::OSVersion.ToString().Replace("Microsoft Windows ", "Win") ) $res = $req.GetResponse(); if($fileName -and !(Split-Path $fileName)) { $fileName = Join-Path (Convert-Path (Get-Location -PSProvider "FileSystem")) $fileName } elseif((!$Passthru -and ($fileName -eq $null)) -or (($fileName -ne $null) -and (Test-Path -PathType "Container" $fileName))) { [string]$fileName = ([regex]'(?i)filename=(.*)$').Match( $res.Headers["Content-Disposition"] ).Groups[1].Value $fileName = $fileName.trim("\/""'") $ofs = "" $fileName = [Regex]::Replace($fileName, "[$([Regex]::Escape(""$([System.IO.Path]::GetInvalidPathChars())$([IO.Path]::AltDirectorySeparatorChar)$([IO.Path]::DirectorySeparatorChar)""))]", "_") $ofs = " " if(!$fileName) { $fileName = $res.ResponseUri.Segments[-1] $fileName = $fileName.trim("\/") if(!$fileName) { $fileName = Read-Host "Please provide a file name" } $fileName = $fileName.trim("\/") if(!([IO.FileInfo]$fileName).Extension) { $fileName = $fileName + "." + $res.ContentType.Split(";")[0].Split("/")[1] } } $fileName = Join-Path (Convert-Path (Get-Location -PSProvider "FileSystem")) $fileName } if($Passthru) { $encoding = [System.Text.Encoding]::GetEncoding( $res.CharacterSet ) [string]$output = "" } if($res.StatusCode -eq 200) { [int]$goal = $res.ContentLength $reader = $res.GetResponseStream() if($fileName) { $writer = new-object System.IO.FileStream $fileName, "Create" } [byte[]]$buffer = new-object byte[] 4096 [int]$total = [int]$count = 0 do { $count = $reader.Read($buffer, 0, $buffer.Length); if($fileName) { $writer.Write($buffer, 0, $count); } if($Passthru){ $output += $encoding.GetString($buffer,0,$count) } elseif(!$quiet) { $total += $count if($goal -gt 0) { Write-Progress "Downloading $url" "Saving $total of $goal" -id 0 -percentComplete (($total/$goal)*100) } else { Write-Progress "Downloading $url" "Saving $total bytes..." -id 0 } } } while ($count -gt 0) $reader.Close() if($fileName) { $writer.Flush() $writer.Close() } if($Passthru){ $output } } $res.Close(); if($fileName) { Set-DownloadFlag $fileName -PassThru } } Export-ModuleMember Get-PoshCode, New-PoshCode, Remove-DownloadFlag, Set-DownloadFlag, Get-WebFile, Get-PoshCodeUpgrade #Add-Type -Path "$PsScriptRoot\NTFS.cs" Add-Type -TypeDefinition @' using System; using System.IO; using System.Collections; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; /// ///Encapsulates access to alternative data streams of an NTFS file. ///Adapted from a C++ sample by Dino Esposito, ///http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnfiles/html/ntfs5.asp /// namespace NTFS { /// /// Wraps the API functions, structures and constants. /// internal class Kernel32 { public const char STREAM_SEP = ':'; public const int INVALID_HANDLE_VALUE = -1; public const int MAX_PATH = 256; [Flags()] public enum FileFlags : uint { WriteThrough = 0x80000000, Overlapped = 0x40000000, NoBuffering = 0x20000000, RandomAccess = 0x10000000, SequentialScan = 0x8000000, DeleteOnClose = 0x4000000, BackupSemantics = 0x2000000, PosixSemantics = 0x1000000, OpenReparsePoint = 0x200000, OpenNoRecall = 0x100000 } [Flags()] public enum FileAccessAPI : uint { GENERIC_READ = 0x80000000, GENERIC_WRITE = 0x40000000 } /// /// Provides a mapping between a System.IO.FileAccess value and a FileAccessAPI value. /// /// The value to map. /// The value. public static FileAccessAPI Access2API(FileAccess Access) { FileAccessAPI lRet = 0; if ((Access & FileAccess.Read)==FileAccess.Read) lRet |= FileAccessAPI.GENERIC_READ; if ((Access & FileAccess.Write)==FileAccess.Write) lRet |= FileAccessAPI.GENERIC_WRITE; return lRet; } [StructLayout(LayoutKind.Sequential)] public struct LARGE_INTEGER { public int Low; public int High; public long ToInt64() { return (long)High * 4294967296 + (long)Low; } } [StructLayout(LayoutKind.Sequential)] public struct WIN32_STREAM_ID { public int dwStreamID; public int dwStreamAttributes; public LARGE_INTEGER Size; public int dwStreamNameSize; } [DllImport("kernel32")] public static extern SafeFileHandle CreateFile(string Name, FileAccessAPI Access, FileShare Share, int Security, FileMode Creation, FileFlags Flags, int Template); [DllImport("kernel32")] public static extern bool DeleteFile(string Name); [DllImport("kernel32")] public static extern bool CloseHandle(SafeFileHandle hObject); [DllImport("kernel32")] public static extern bool BackupRead(SafeFileHandle hFile, IntPtr pBuffer, int lBytes, ref int lRead, bool bAbort, bool bSecurity, ref int Context); [DllImport("kernel32")] public static extern bool BackupRead(SafeFileHandle hFile, ref WIN32_STREAM_ID pBuffer, int lBytes, ref int lRead, bool bAbort, bool bSecurity, ref int Context); [DllImport("kernel32")] public static extern bool BackupSeek(SafeFileHandle hFile, int dwLowBytesToSeek, int dwHighBytesToSeek, ref int dwLow, ref int dwHigh, ref int Context); } /// /// Encapsulates a single alternative data stream for a file. /// public class StreamInfo { private FileStreams _parent; private string _name; private long _size; internal StreamInfo(FileStreams Parent, string Name, long Size) { _parent = Parent; _name = Name; _size = Size; } /// /// The name of the stream. /// public string Name { get { return _name; } } /// /// The size (in bytes) of the stream. /// public long Size { get { return _size; } } public override string ToString() { return String.Format("{1}{0}{2}{0}$DATA", Kernel32.STREAM_SEP, _parent.FileName, _name); } public override bool Equals(Object o) { if (o is StreamInfo) { StreamInfo f = (StreamInfo)o; return (f._name.Equals(_name) && f._parent.Equals(_parent)); } else if (o is string) { return ((string)o).Equals(ToString()); } else return base.Equals(o); } public override int GetHashCode() { return ToString().GetHashCode(); } #region Open /// /// Opens or creates the stream in read-write mode, with no sharing. /// /// A wrapper for the stream. public FileStream Open() { return Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); } /// /// Opens or creates the stream in read-write mode with no sharing. /// /// The action for the stream. /// A wrapper for the stream. public FileStream Open(FileMode Mode) { return Open(Mode, FileAccess.ReadWrite, FileShare.None); } /// /// Opens or creates the stream with no sharing. /// /// The action for the stream. /// The level for the stream. /// A wrapper for the stream. public FileStream Open(FileMode Mode, FileAccess Access) { return Open(Mode, Access, FileShare.None); } /// /// Opens or creates the stream. /// /// The action for the stream. /// The level for the stream. /// The level for the stream. /// A wrapper for the stream. public FileStream Open(FileMode Mode, FileAccess Access, FileShare Share) { try { SafeFileHandle hFile = Kernel32.CreateFile(ToString(), Kernel32.Access2API(Access), Share, 0, Mode, 0, 0); return new FileStream(hFile, Access); } catch { return null; } } #endregion #region Delete /// /// Deletes the stream from the file. /// /// A value: true if the stream was deleted, false if there was an error. public bool Delete() { return Kernel32.DeleteFile(ToString()); } #endregion } /// /// Encapsulates the collection of alternative data streams for a file. /// A collection of objects. /// public class FileStreams : CollectionBase { private FileInfo _file; #region Constructors public FileStreams(string File) { _file = new FileInfo(File); initStreams(); } public FileStreams(FileInfo file) { _file = file; initStreams(); } /// /// Reads the streams from the file. /// private void initStreams() { //Open the file with backup semantics SafeFileHandle hFile = Kernel32.CreateFile(_file.FullName, Kernel32.FileAccessAPI.GENERIC_READ, FileShare.Read, 0, FileMode.Open, Kernel32.FileFlags.BackupSemantics, 0); if (hFile.IsInvalid) return; try { Kernel32.WIN32_STREAM_ID sid = new Kernel32.WIN32_STREAM_ID(); int dwStreamHeaderSize = Marshal.SizeOf(sid); int Context = 0; bool Continue = true; while (Continue) { //Read the next stream header int lRead = 0; Continue = Kernel32.BackupRead(hFile, ref sid, dwStreamHeaderSize, ref lRead, false, false, ref Context); if (Continue && lRead == dwStreamHeaderSize) { if (sid.dwStreamNameSize>0) { //Read the stream name lRead = 0; IntPtr pName = Marshal.AllocHGlobal(sid.dwStreamNameSize); try { Continue = Kernel32.BackupRead(hFile, pName, sid.dwStreamNameSize, ref lRead, false, false, ref Context); char[] bName = new char[sid.dwStreamNameSize]; Marshal.Copy(pName,bName,0,sid.dwStreamNameSize); //Name is of the format ":NAME:$DATA\0" string sName = new string(bName); int i = sName.IndexOf(Kernel32.STREAM_SEP, 1); if (i>-1) sName = sName.Substring(1, i-1); else { //This should never happen. //Truncate the name at the first null char. i = sName.IndexOf('\0'); if (i>-1) sName = sName.Substring(1, i-1); } //Add the stream to the collection base.List.Add(new StreamInfo(this,sName,sid.Size.ToInt64())); } finally { Marshal.FreeHGlobal(pName); } } //Skip the stream contents int l = 0; int h = 0; Continue = Kernel32.BackupSeek(hFile, sid.Size.Low, sid.Size.High, ref l, ref h, ref Context); } else break; } } finally { Kernel32.CloseHandle(hFile); } } #endregion #region File /// /// Returns the object for the wrapped file. /// public FileInfo FileInfo { get { return _file; } } /// /// Returns the full path to the wrapped file. /// public string FileName { get { return _file.FullName; } } /// /// Returns the size of the main data stream, in bytes. /// public long FileSize { get {return _file.Length;} } /// /// Returns the size of all streams for the file, in bytes. /// public long Size { get { long size = this.FileSize; foreach (StreamInfo s in this) size += s.Size; return size; } } #endregion #region Open /// /// Opens or creates the default file stream. /// /// public FileStream Open() { return new FileStream(_file.FullName, FileMode.OpenOrCreate); } /// /// Opens or creates the default file stream. /// /// The for the stream. /// public FileStream Open(FileMode Mode) { return new FileStream(_file.FullName, Mode); } /// /// Opens or creates the default file stream. /// /// The for the stream. /// The for the stream. /// public FileStream Open(FileMode Mode, FileAccess Access) { return new FileStream(_file.FullName, Mode, Access); } /// /// Opens or creates the default file stream. /// /// The for the stream. /// The for the stream. /// The for the stream. /// public FileStream Open(FileMode Mode, FileAccess Access, FileShare Share) { return new FileStream(_file.FullName, Mode, Access, Share); } #endregion #region Delete /// /// Deletes the file, and all alternative streams. /// public void Delete() { for (int i=base.List.Count;i>0;i--) { base.List.RemoveAt(i); } _file.Delete(); } #endregion #region Collection operations /// /// Add an alternative data stream to this file. /// /// The name for the stream. /// The index of the new item. public int Add(string Name) { StreamInfo FSI = new StreamInfo(this, Name, 0); int i = base.List.IndexOf(FSI); if (i==-1) i = base.List.Add(FSI); return i; } /// /// Removes the alternative data stream with the specified name. /// /// The name of the string to remove. public void Remove(string Name) { StreamInfo FSI = new StreamInfo(this, Name, 0); int i = base.List.IndexOf(FSI); if (i>-1) base.List.RemoveAt(i); } /// /// Returns the index of the specified object in the collection. /// /// The object to find. /// The index of the object, or -1. public int IndexOf(StreamInfo FSI) { return base.List.IndexOf(FSI); } /// /// Returns the index of the object with the specified name in the collection. /// /// The name of the stream to find. /// The index of the stream, or -1. public int IndexOf(string Name) { return base.List.IndexOf(new StreamInfo(this, Name, 0)); } public StreamInfo this[int Index] { get { return (StreamInfo)base.List[Index]; } } public StreamInfo this[string Name] { get { int i = IndexOf(Name); if (i==-1) return null; else return (StreamInfo)base.List[i]; } } #endregion #region Overrides /// /// Throws an exception if you try to add anything other than a StreamInfo object to the collection. /// protected override void OnInsert(int index, object value) { if (!(value is StreamInfo)) throw new InvalidCastException(); } /// /// Throws an exception if you try to add anything other than a StreamInfo object to the collection. /// protected override void OnSet(int index, object oldValue, object newValue) { if (!(newValue is StreamInfo)) throw new InvalidCastException(); } /// /// Deletes the stream from the file when you remove it from the list. /// protected override void OnRemoveComplete(int index, object value) { try { StreamInfo FSI = (StreamInfo)value; if (FSI != null) FSI.Delete(); } catch {} } public new StreamEnumerator GetEnumerator() { return new StreamEnumerator(this); } #endregion #region StreamEnumerator public class StreamEnumerator : object, IEnumerator { private IEnumerator baseEnumerator; public StreamEnumerator(FileStreams mappings) { this.baseEnumerator = ((IEnumerable)(mappings)).GetEnumerator(); } public StreamInfo Current { get { return ((StreamInfo)(baseEnumerator.Current)); } } object IEnumerator.Current { get { return baseEnumerator.Current; } } public bool MoveNext() { return baseEnumerator.MoveNext(); } bool IEnumerator.MoveNext() { return baseEnumerator.MoveNext(); } public void Reset() { baseEnumerator.Reset(); } void IEnumerator.Reset() { baseEnumerator.Reset(); } } #endregion } } '@ Add-Type -TypeDefinition @' public enum PoshCodeLanguage { asp, bash, csharp, posh, vbnet, xml, text } '@ # SIG # Begin signature block # MIIIPgYJKoZIhvcNAQcCoIIILzCCCCsCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU3i+MeuLT7EgZ9YB0FHRT5Ecq # VKigggVbMIIFVzCCBD+gAwIBAgIRAO2rPg5HUjL4ofGGpnMP2jwwDQYJKoZIhvcN # AQEFBQAwgZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2Fs # dCBMYWtlIENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8G # A1UECxMYaHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNF # UkZpcnN0LU9iamVjdDAeFw0wODEwMDYwMDAwMDBaFw0wOTEwMDYyMzU5NTlaMIHE # MQswCQYDVQQGEwJVUzEOMAwGA1UEEQwFMTQ2MjMxETAPBgNVBAgMCE5ldyBZb3Jr # MRIwEAYDVQQHDAlSb2NoZXN0ZXIxFDASBgNVBAkMC01TIDA4MDEtMTdBMRowGAYD # VQQJDBExMzUwIEplZmZlcnNvbiBSZDEaMBgGA1UECgwRWGVyb3ggQ29ycG9yYXRp # b24xFDASBgNVBAsMC1NFRURVIFRvb2xzMRowGAYDVQQDDBFYZXJveCBDb3Jwb3Jh # dGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK50RXT2KUvECfWZ # weqeXzTCykPPRh9nC3Hzur/mmvkQHA8iinnSKX4j19C1/MV0rAEeCU1bF7Sgxvov # ORreM1Ye/wEqJLAUP/IGZI/qsmmwasGFGbnuevpA3WieNCr5cFGl8Y5Mx6ejaDFi # O0GT9EM6gOGZaEEMRbHZc4qXT7CrWScs4Yur5bBZsowaMk5JkvZgihhnN93QolEW # ObmtQZlbBDqLuoL9fUnIexlqqIrC/4h0K8VM26HvqhgGlQF2wf4t9xCHFJiX2F7D # B10lef5aXzyPVrvxxrRWyBtCQuL7xdXneRanJaYG3B3kclc+4/6dq9a+s/huXjmE # omumgGcCAwEAAaOCAW8wggFrMB8GA1UdIwQYMBaAFNrtZHQUnBQ8q92Zqb1bKE2L # PMnYMB0GA1UdDgQWBBT5ITlG5CdiD+nI0uTqnXNGnd44QjAOBgNVHQ8BAf8EBAMC # B4AwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAzARBglghkgBhvhC # AQEEBAMCBBAwRgYDVR0gBD8wPTA7BgwrBgEEAbIxAQIBAwIwKzApBggrBgEFBQcC # ARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLm5ldC9DUFMwQgYDVR0fBDswOTA3oDWg # M4YxaHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0 # LmNybDA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNv # bW9kb2NhLmNvbTAhBgNVHREEGjAYgRZKb2VsLkJlbm5ldHRAWGVyb3guY29tMA0G # CSqGSIb3DQEBBQUAA4IBAQAZxnV+BbJBohpy+wKs6U8hRiPUhDYaijzTyrZontf5 # PEyBbhAkJFIWauIaq9eSQEJeErXO/zuO6+wY/azBzOTleMM9qdGWHFtfAw5WiIuC # 90TzDBSuP7LImZV5Pb6nxRbesDF2U7EM5sBzYSWAMfpBmYRz97EHPW5QNzpBLFJn # Dhb/M27rDYh7FVjy1+C5E3glIa0A0q+lcxEtFuUij4JId+oMcfpSgYJZvR1Kvkjd # GDAtWCzvALaNFd65kChbrOqcClOCacQRnP9N4DJl/RVNKZtcUcVAyTpvOlJBA5vG # OVcsJT4TnSMjPX6d5pXMwcE1oWCUWvK99W+N81DvBBuZMYICTTCCAkkCAQEwgasw # gZUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtl # IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMY # aHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0 # LU9iamVjdAIRAO2rPg5HUjL4ofGGpnMP2jwwCQYFKw4DAhoFAKB4MBgGCisGAQQB # gjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK # KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFJCfFRIe # gnXhrIKjkzVOt1jE84TEMA0GCSqGSIb3DQEBAQUABIIBAG3iODNS+O0q4aYM07kf # IWWW2A0c/l97T9dB2LMCD487shUAw0mltu/ZxdUSO3rPILGepaB3Iboi6TyZN76n # 2PfxrMZ3nBKs2OC4/8ULoyUk/n8An1u0e2dfHoS8VprOgwZel0ecn8RFnp4P8fvJ # 3UFs83JiGlZKVD9Dx3isQpZ2b8QpWd8H1//GE0+4p/Gb2t8yfTaEAy8MxI6uQjnF # DK/b/me6UUwtffxZoUTvDjFWyV3aOHRKmmGQk7WCOipPnDq178BlU1JFq9FSPS6W # PCgEZK/QKlN1iCkPGrDlVCiImx/2UZwq/RBjMF0CpLyKAvUi1llwICym8Mz5uKZc # h38= # SIG # End signature block