Get datetaken attribute on file - powershell

I am trying to write a script that will get the DATETAKEN attribute from a photo and create a folder structure based on that and move the file to this new location. I have found scripts on google that I'm trying to use but when I running it, it returns:
PS C:\Temp> C:\Temp\MovePhoto.ps1
GAC Version Location
--- ------- -------- True v4.0.30319
C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0....
Move-Item : The process cannot access the file because it is being
used by another process. At C:\Temp\MovePhoto.ps1:43 char:5
+ Move-Item $FileFullName "$FileDirFull\$FileBaseNameNU"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (C:\NoBackup\TES...RA\IMG_1372.JPG:FileInfo) [Move- Item],
IOException
+ FullyQualifiedErrorId : MoveFileInfoItemIOError,Microsoft.PowerShell.Commands.MoveItemCommand
If I do the script without the SystemDrawing line it works. But then I can't get the DATETAKEN attribute. I just can't figure out what I am missing.
Here is the script
[reflection.assembly]::LoadWithPartialName("System.Drawing")
$FileAll = (Get-ChildItem $SourcePath -Recurse | where {!$_.psiscontainer} | Select-Object Name,Fullname,BaseName,Extension,CreationTime,LastWriteTime,Length,#{Name="MD5";Expression={Get-Md5Hash $_.fullname}} | group MD5 | Where {$_.Count -gt 1 } | %{$_.Group} | sort MD5)
foreach ($File in $FileAll) {
$FileBaseName = $File.BaseName
$FileExtension = $File.Extension
$FileFullName = $File.FullName
$FileBaseNameNu = $FileBaseName + $FileExtension
$FileName = $File.Name
}
$foo = New-Object -TypeName System.Drawing.Bitmap -ArgumentList $FileFullName
$date = $foo.GetPropertyItem(36867).Value[0..9]
$arYear = [Char]$date[0],[Char]$date[1],[Char]$date[2],[Char]$date[3]
$arMonth = [Char]$date[5],[Char]$date[6]
$arDay = [Char]$date[8],[Char]$date[9]
$strYear = [String]::Join("",$arYear)
$strMonth = [String]::Join("",$arMonth)
$strDay = [String]::Join("",$arDay)
$DateTaken = $strYear + "-" + $strMonth + "-" + $strDay
$FileLastWriteTime = $File.LastWriteTime
$FileDirYear = $FileLastWriteTime.Year
$FileDirDate = $FileLastWriteTime.ToShortDateString()
$FileDirFull = "$DestinationPath\DUBLETTER\$FileDirYear\$DateTaken"
# Create destination path
if ((Test-Path $FileDirFull) -eq $false) {
New-Item -Path $FileDirFull -ItemType Directory
}
if (Test-Path (Join-Path $FileDirFull $File.Name)) {
$n = 0
while ((Test-Path (Join-Path $FileDirFull $FileBaseNameNU)) -eq $true){
$FileBaseNameNU = $FileBaseName + "-" + ++$n + $FileExtension
}
}
Move-Item $FileFullName "$FileDirFull\$FileBaseNameNU"
}

Can you try to replace
[reflection.assembly]::LoadWithPartialName("System.Drawing")
by
Add-Type -AssemblyName "system.drawing"
Forget it, your trouble is with your file C:\NoBackup\TES...RA\IMG_1372.JPG wich can't be moved because it's open (seems to be open for usage in $foo var). Try first to copy it. You perhaps can use $foo.Dispose() before Move-Item $FileFullName "$FileDirFull\$FileBaseNameNU"

I've done this in VBScript, using GetDetailsOf()... see MSDN
This can be done using PowerShell, see... Scripting Guys
There may be a .NET method way of doing this, i.e.: more "native" to PS.

Related

How to rename multiple pdfs in Powershell using itextsharp

Im trying to rename every pdf file in a folder according to variables obtained from those files but i cant get it to work, i can rename one file at a time but i cant manage to make it work for every file.
Here is what i have to rename one file:
Add-Type -Path "C:\Program Files\...\itextsharp.dll"
$file = "C:\Program Files\...\pdf file.pdf"
$pdf = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList $file
$text=[iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($pdf,1)
$startss = $text.LastIndexOf("Completo")
$endss = $text.LastIndexOf("Doc")
$name = $text.Substring($startss +9,$endss - $startss-10)
$startss2 = $text.LastIndexOf("Modalidad")
$endss2 = $text.LastIndexOf("(Entre")
$mode = $text.Substring($startss2 +10,$endss2 - $startss2-10)
Rename-Item -NewName ($name + "-" + $mode + ".pdf") -Path "$file"
$pdf.Close()
And here is what i have to rename every file:
$folder = "C:\...\pdfs folder"
Add-Type -Path "C:\Program Files\...\itextsharp.dll"
$pdffiles = Get-ChildItem -File "$folder\*.pdf"
Foreach($file in $pdffiles)
{
$pdf = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList $file
$text=[iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($pdf,1)
$startss = $text.LastIndexOf("Completo")
$endss = $text.LastIndexOf("Doc")
$name = $text.Substring($startss +9,$endss - $startss-10)
$startss2 = $text.LastIndexOf("Modalidad")
$endss2 = $text.LastIndexOf("(Entre")
$mode = $text.Substring($startss2 +10,$endss2 - $startss2-10)
Rename-Item -NewName ($name + "-" + $mode + "-" + ".pdf") -Path "$file"
$pdf.Close()
}
Hope someone can help me to make it work, thanks in advance

How to make powershell with itextsharp edit multiple pdfs

I managed to make a script that opens a pdf file, reads it, makes 2 variables with information from it and saves the file using those variables, but i cant make it do that to all the pdf files in a folder. i dont know if anyone can help me. This is what i have so far:
$file = "C:\Users\..." #path to my pdf file
Add-Type -Path "C:\Program Files\...\itextsharp.dll"
$pdf = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList $file
$text=[iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($pdf,1)
$startss = $text.LastIndexOf("Completo")
$endss = $text.LastIndexOf("Doc")
$name = $text.Substring($startss +9,$endss - $startss-10)
$startss2 = $text.LastIndexOf("Modalidad")
$endss2 = $text.LastIndexOf("(Entre")
$mode = $text.Substring($startss2 +10,$endss2 - $startss2-10)
$pdf.Close()
Rename-Item -NewName ($name + "-" + $mode + "-" + ".pdf") -Path "$file"
You just need a foreach loop. I would recommend reading about Get-ChildItem if you're not sure what it is and some basics on foreach loops.
$folder = "C:\Users\path\to\pdf folder"
Add-Type -Path "C:\Program Files\...\itextsharp.dll"
$pdfFiles = Get-ChildItem "$folder\*.pdf"
foreach($file in $pdfFiles)
{
$pdf = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList $file
$text= [iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($pdf,1)
$startss = $text.LastIndexOf("Completo")
$endss = $text.LastIndexOf("Doc")
$name = $text.Substring($startss +9,$endss - $startss-10)
$startss2 = $text.LastIndexOf("Modalidad")
$endss2 = $text.LastIndexOf("(Entre")
$mode = $text.Substring($startss2 +10,$endss2 - $startss2-10)
$pdf.Close()
Rename-Item -NewName ($name + "-" + $mode + "-" + ".pdf") -Path $file
}

How to loop through column values from a table and create folders via powershell

I'm trying to achieve the following via powershell:
I have a table(TBL_DDL) with 5 columns (CATALOG,SCHEMA,OBJECT_TYPE,OBJECT_NAME,DDL)
Now, i'm extract data from this table and then trying to create a folder structure by concatenating first 4 columns (CATALOG,SCHEMA,OBJECT_TYPE,OBJECT_NAME) in C: drive and then exporting the data in DDL column in txt file.
For eg: C:\"CATALOG"\"SCHEMA"\"OBJECT_TYPE"\"OBJECT_NAME"\DDL.txt
I'm trying to achieve this via powershell. Can anyone help me please?
$SqlCmd = 'snowsql -c example -d tu_test -s public -q "select catalog,schema,OBJECT_TYPE,OBJECT_NAME,DDL from SF_TBL_DDL limit 2"'
$MultiArray = #(Invoke-Expression $SqlCmd)
$dt = New-Object System.Data.Datatable
[void]$dt.Columns.Add("CATALOG")
[void]$dt.Columns.Add("SCHEMA")
$Output = foreach ($Object in $MultiArray)
{
foreach ($SCHEMA in $Object.SCHEMA)
{
$someother = New-Object -TypeName psobject -Property #{CATALOG = $Object.CATALOG; SCHEMA = $SCHEMA}
$nRow = $dt.NewRow()
$nRow.CATALOG = $someother.CATALOG
$nRow.SCHEMA = $someother.SCHEMA
$dt.Rows.Add($nRow)
}
}
$dt.row.count
At the moment, i'm getting 0 rows in $dt.
Cheers
You can use System.Data.DataTable object the pull your result set and then loop through it to perform the required operation.
Here GetTableValues function will retrieve the table values and then use following cmdlet to create directory and file
New-Item -ItemType "directory" -Path $dirPath
New-Item -ItemType "file" -Path $filePath
Complete code looks like this
function GetTableValues(){
$DBConnectionString = "<Your DB connection string>";
$sqlConn = new-object System.Data.SqlClient.sqlConnection $DBConnectionString;
$sqlConn.Open();
$sqlCommand = $sqlConn.CreateCommand();
$sqlCommand.CommandText = "select catalog,[schema],OBJECT_TYPE,OBJECT_NAME,DDL from TBL_DDL"; ##Put your correct query here
$result = $sqlCommand.ExecuteReader();
$table = New-Object System.Data.DataTable;
$table.Load($result);
$sqlConn.Close();
return $table;
}
$tableValue = GetTableValues;
foreach ($Row in $tableValue)
{
$filePath = "C:\" + $Row.catalog.TrimEnd() + "\" + $Row.schema.TrimEnd() + "\" + $Row.OBJECT_TYPE.TrimEnd() + "\" + $Row.OBJECT_NAME.TrimEnd() + "\" + $Row.DDL.TrimEnd() + ".txt"
$dirPath = "C:\" + $Row.catalog.TrimEnd() + "\" + $Row.schema.TrimEnd() + "\" + $Row.OBJECT_TYPE.TrimEnd() + "\" + $Row.OBJECT_NAME.TrimEnd()
New-Item -ItemType "directory" -Path $dirPath ##Creates directory
New-Item -ItemType "file" -Path $filePath ##Creates file in $dirPath directory
}
This works perfectly fine for me.

Powershell Loop to Write Password Protected Files

I'm trying to read excel files into Powershell, open, password protect them and write them back. I can do it individually but within a loop the script fails:
#working individually
$f = ("C:my\path\Out Files\1234dv.xlsx")
$outfile = $f.FullName + "out"
$xlNormal = -4143
$xl = new-object -comobject excel.application
$xl.Visible = $True
$xl.DisplayAlerts = $False
$wb = $xl.Workbooks.Open($f)
$a = $wb.SaveAs("C:my\path\Out Files\test.xls",$xlNormal,"test")
$a = $xl.Quit()
$a = Release-Ref($ws)
$a = Release-Ref($wb)
$a = Release-Ref($xl)
#not working in loop, error after
function Release-Ref ($ref) {
([System.Runtime.InteropServices.Marshal]::ReleaseComObject(
[System.__ComObject]$ref) -gt 0)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
foreach ($f in Get-ChildItem "C:\my\path\Out Files"){
$ff = $f
$outfile = $f.FullName + "out"
$xlNormal = -4143
$xl = new-object -comobject excel.application
$xl.Visible = $True
$xl.DisplayAlerts = $False
$wb = $xl.Workbooks.Open($ff)
$a = $wb.SaveAs("C:\my\path\Out Files\test.xls",$xlNormal,"test")
$a = $xl.Quit()
$a = Release-Ref($ws)
$a = Release-Ref($wb)
$a = Release-Ref($xl)
}
Sorry, we couldn't find 1234dv.xlsx. Is it possible it was moved,
renamed or deleted? At line:16 char:5
+ $wb = $xl.Workbooks.Open($ff)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException COM object that has been
separated from its underlying RCW cannot be used. At line:17 char:5
+ $a = $wb.SaveAs("C:\my\path ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], InvalidComObjectException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.InvalidComObjectException
That error repeats for all four test files I'm working with.
I'm not really familiar with Powershell so I relied on MS docs, and I couldn't password protect the files in python so thought this would be easier. I know this doesn't address the password yet either but trying to get the loop to work first. Any help would be greatly appreciated. Thank you.
You should use
$wb = $xl.Workbooks.Open($ff.FullName)
To give Excel the full file path. Otherwise, $ff is a FileInfo object where a string (path) is required
Slightly off topic for your question , but not for your intent :
From a security perspective using .xls passwords is not security, it is merely an annoyance.
If you need security, then i suggest you use something like Azure Information protection that allows you to encrypt , and share the file securely only with those that need access.
You still need to create your xls or .xlsx files (or any other file for that matter) then you can the powershell simply loop over them :
PS C:\>foreach ($file in (Get-ChildItem -Path \\server1\Docs -Recurse -Force |
where {!$_.PSIsContainer} |
Where-Object {$_.Extension -eq ".xls"})) {
Protect-RMSFile -File $file.PSPath -InPlace -DoNotPersistEncryptionKey All -TemplateID "e6ee2481-26b9-45e5-b34a-f744eacd53b0" -OwnerEmail "IT#Contoso.com"
}
https://learn.microsoft.com/en-us/powershell/module/azureinformationprotection/protect-rmsfile?view=azureipps

Issues running a Powershell script without running a VB script first

I was looking for a solution to pin a shortcut or program to the task in win 10 with PS. I found Pin program to taskbar using PS in Windows 10. The VB Script works,
If WScript.Arguments.Count < 1 Then WScript.Quit
'----------------------------------------------------------------------
Set objFSO = CreateObject("Scripting.FileSystemObject")
objFile = WScript.Arguments.Item(0)
sKey1 = "HKCU\Software\Classes\*\shell\{:}\\"
sKey2 = Replace(sKey1, "\\", "\ExplorerCommandHandler")
'----------------------------------------------------------------------
With WScript.CreateObject("WScript.Shell")
KeyValue = .RegRead("HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer" & _
"\CommandStore\shell\Windows.taskbarpin\ExplorerCommandHandler")
.RegWrite sKey2, KeyValue, "REG_SZ"
With WScript.CreateObject("Shell.Application")
With .Namespace(objFSO.GetParentFolderName(objFile))
With .ParseName(objFSO.GetFileName(objFile))
.InvokeVerb("{:}")
End With
End With
End With
.Run("Reg.exe delete """ & Replace(sKey1, "\\", "") & """ /F"), 0, True
End With
'----------------------------------------------------------------------
I can invoke VB script from PS but a helpful person converted the script to PS
Param($Target)
$KeyPath1 = "HKCU:\SOFTWARE\Classes"
$KeyPath2 = "*"
$KeyPath3 = "shell"
$KeyPath4 = "{:}"
$ValueName = "ExplorerCommandHandler"
$ValueData =
(Get-ItemProperty `
("HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\" + `
"CommandStore\shell\Windows.taskbarpin")
).ExplorerCommandHandler
$Key2 = (Get-Item $KeyPath1).OpenSubKey($KeyPath2, $true)
$Key3 = $Key2.CreateSubKey($KeyPath3, $true)
$Key4 = $Key3.CreateSubKey($KeyPath4, $true)
$Key4.SetValue($ValueName, $ValueData)
$Shell = New-Object -ComObject "Shell.Application"
$Folder = $Shell.Namespace((Get-Item $Target).DirectoryName)
$Item = $Folder.ParseName((Get-Item $Target).Name)
$Item.InvokeVerb("{:}")
$Key3.DeleteSubKey($KeyPath4)
if ($Key3.SubKeyCount -eq 0 -and $Key3.ValueCount -eq 0) {
$Key2.DeleteSubKey($KeyPath3)
}
However this PS script will not run unless the VB script has been ran at least one time. Is there a way to make the PS script work without having to run the VB script?
The error I get when trying to run the PS script without running the VB script at least once before it:
You cannot call a method on a null-valued expression.
At \\server\Utilities\TaskbarPin.ps1:41 char:5
+ $Key3 = $Key2.CreateSubKey($KeyPath3, $true)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At \\server\Utilities\TaskbarPin.ps1:42 char:5
+ $Key4 = $Key3.CreateSubKey($KeyPath4, $true)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At \\server\Utilities\TaskbarPin.ps1:43 char:5
+ $Key4.SetValue($KeyValue, $ValueData)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At \\server\Utilities\TaskbarPin.ps1:50 char:5
+ $Key3.DeleteSubKey($KeyPath4)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
I do not get an error after using the VB script once to do the task.
You should not have been impacted by this this way.
The code works as designed, but you have to call the path the exe fully.
I just converted it to a function and it is successful with no other dependencies.
Function Add-AppToTaskbar
{
[cmdletbinding()]
Param
(
[string]$Target
)
$KeyPath1 = "HKCU:\SOFTWARE\Classes"
$KeyPath2 = "*"
$KeyPath3 = "shell"
$KeyPath4 = "{:}"
$ValueName = "ExplorerCommandHandler"
$ValueData =
(Get-ItemProperty `
("HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\" + `
"CommandStore\shell\Windows.taskbarpin")
).ExplorerCommandHandler
$Key2 = (Get-Item $KeyPath1).OpenSubKey($KeyPath2, $true)
$Key3 = $Key2.CreateSubKey($KeyPath3, $true)
$Key4 = $Key3.CreateSubKey($KeyPath4, $true)
$Key4.SetValue($ValueName, $ValueData)
$Shell = New-Object -ComObject "Shell.Application"
$Folder = $Shell.Namespace((Get-Item $Target).DirectoryName)
$Item = $Folder.ParseName((Get-Item $Target).Name)
$Item.InvokeVerb("{:}")
$Key3.DeleteSubKey($KeyPath4)
if ($Key3.SubKeyCount -eq 0 -and $Key3.ValueCount -eq 0)
{$Key2.DeleteSubKey($KeyPath3)}
}
Add-AppToTaskbar -Target 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
BTW, these pinned things live in two places on your system:
Here:
$env:USERPROFILE\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar
Registry:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Taskband
Both are required.
Update based on OP's comment
I just ran this locally and remotely, both are successful. See results below.
The local host I am using - WS2012R2 set as a workstation role
I don't have any W10 systems in my lab. The earlier test was on a local W10 host.
Executed in the console host, ISE and VSCode.
PS C:\Windows\system32> $env:COMPUTERNAME
LabWS01
# PS Version
PS C:\Windows\system32> $PSVersionTable
Name Value
---- -----
PSVersion 4.0
WSManStackVersion 3.0
SerializationVersion 1.1.0.1
CLRVersion 4.0.30319.42000
BuildVersion 6.3.9600.18968
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0}
PSRemotingProtocolVersion 2.2
# the current user profile pinned location filtered for notepad*
PS C:\Windows\system32> Get-ChildItem -Path "$env:USERPROFILE\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Notepad*"
# Tested path to remote share
PS C:\Windows\system32> Test-path -Path '\\Server\ShareName\Add-AppToTaskbar.ps1'
True
# Ran the script from that remote share
PS C:\Windows\system32> \\Server\ShareName\Add-AppToTaskbar.ps1 'c:\windows\notepad.exe'
or this way...
Start-process -FilePath Powershell -ArgumentList '\\Server\ShareName\Add-AppToTaskbar.ps1 -Target C:\Windows\notepad.exe'
# Review pinned item location, filtered for notepad*
PS C:\Windows\system32> Get-ChildItem -Path "$env:USERPROFILE\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Notepad*"
Directory: C:\Users\Labuser001\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 8/9/2018 8:48 PM 791 Notepad.lnk
Shortcut shows pinned to taskbar.
So, this sounds environmental on your side. Now you can pin apps using GPO if this issue continues.
I've modified your function so that selectively pins or unpins items to the taskbar. Previously, the problem is that the pin command was not exclusive, it would unpin the application if it was already pinned. With further detection of what was pinned in a binary registry value, it has been possible to determine that the item has already been pinned and it will not attempt to pin the item twice.
Set-AppPinTaskbarCsv is a function that was customized for our environment, I only include it as an example only, if someone wanted to roll this out in a login script to ensure the users have all of the apps they need pinned, it would need a good deal of modification and simplification. It has some functions which are not included that check group membership and reformat strings to expand variables, which are not required. After pinning the applications, it displays more reliably if explorer is restarted, and the Csv function will restart explorer if any items are pinned.
Function Set-PinTaskbar {
Param (
[parameter(Mandatory=$True, HelpMessage="Target item to pin")]
[ValidateNotNullOrEmpty()]
[string] $Target
,
[Parameter(Mandatory=$False, HelpMessage="Target item to unpin")]
[switch]$Unpin
)
If (!(Test-Path $Target)) {
Write-Warning "$Target does not exist"
Break
}
$Reg = #{}
$Reg.Key1 = "*"
$Reg.Key2 = "shell"
$Reg.Key3 = "{:}"
$Reg.Value = "ExplorerCommandHandler"
$Reg.Data = (Get-ItemProperty ("HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell\Windows.taskbarpin")).ExplorerCommandHandler
$Reg.Path1 = "HKCU:\SOFTWARE\Classes"
$Reg.Path2 = Join-Path $Reg.Path1 $Reg.Key1
$Reg.Path3 = Join-Path $Reg.Path2 $Reg.Key2
$Reg.Path4 = Join-Path $Reg.Path3 $Reg.Key3
If (!(Test-Path -LiteralPath $Reg.Path2)) {
New-Item -ItemType Directory -Path $Reg.Path1 -Name [System.Management.Automation.WildcardPattern]::Escape($Reg.Key1)
}
If (!(Test-Path -LiteralPath $Reg.Path3)) {
New-Item -ItemType Directory -Path ([System.Management.Automation.WildcardPattern]::Escape($Reg.Path2)) -Name $Reg.Key2
}
If (!(Test-Path -LiteralPath $Reg.Path4)) {
New-Item -ItemType Directory -Path ([System.Management.Automation.WildcardPattern]::Escape($Reg.Path3)) -Name $Reg.Key3
}
Set-ItemProperty -Path ([System.Management.Automation.WildcardPattern]::Escape($Reg.Path4)) -Name $Reg.Value -Value $Reg.Data
$Shell = New-Object -ComObject "Shell.Application"
$Folder = $Shell.Namespace((Get-Item $Target).DirectoryName)
$Item = $Folder.ParseName((Get-Item $Target).Name)
# Registry key where the pinned items are located
$RegistryKey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Taskband"
# Binary registry value where the pinned items are located
$RegistryValue = "FavoritesResolve"
# Gets the contents into an ASCII format
$CurrentPinsProperty = ([system.text.encoding]::ASCII.GetString((Get-ItemProperty -Path $RegistryKey -Name $RegistryValue | Select-Object -ExpandProperty $RegistryValue)))
# Filters the results for only the characters that we are looking for, so that the search will function
[string]$CurrentPinsResults = $CurrentPinsProperty -Replace '[^\x20-\x2f^\x30-\x3a\x41-\x5c\x61-\x7F]+', ''
# Globally Unique Identifiers for common system folders, to replace in the pin results
$Guid = #{}
$Guid.FOLDERID_ProgramFilesX86 = #{
"ID" = "{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}"
"Path" = ${env:ProgramFiles(x86)}
}
$Guid.FOLDERID_ProgramFilesX64 = #{
"ID" = "{6D809377-6AF0-444b-8957-A3773F02200E}"
"Path" = $env:ProgramFiles
}
$Guid.FOLDERID_ProgramFiles = #{
"ID" = "{905e63b6-c1bf-494e-b29c-65b732d3d21a}"
"Path" = $env:ProgramFiles
}
$Guid.FOLDERID_System = #{
"ID" = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}"
"Path" = Join-Path $env:WINDIR "System32"
}
$Guid.FOLDERID_Windows = #{
"ID" = "{F38BF404-1D43-42F2-9305-67DE0B28FC23}"
"Path" = $env:WINDIR
}
ForEach ($GuidEntry in $Guid.Keys) {
$CurrentPinsResults = $CurrentPinsResults -replace $Guid.$GuidEntry.ID,$Guid.$GuidEntry.Path
}
$Split = $CurrentPinsResults -split ('C:')
$SplitOutput = #()
# Process each path entry, remove invalid characters, test to determine if the path is valid
ForEach ($Entry in $Split) {
If ($Entry.Substring(0,1) -eq '\') {
# Get a list of invalid path characters
$InvalidPathCharsRegEx = [IO.Path]::GetInvalidPathChars() -join ''
$InvalidPathChars = "[{0}]" -f [RegEx]::Escape($InvalidPathCharsRegEx)
$EntryProcessedPhase1 = "C:" + ($Entry -replace $InvalidPathChars)
$EntryProcessedPhase2 = $null
# Remove characters from the path until it is resolvable
ForEach ($Position in $EntryProcessedPhase1.Length .. 1) {
If (Test-Path $EntryProcessedPhase1.Substring(0,$Position)) {
$EntryProcessedPhase2 = $EntryProcessedPhase1.Substring(0,$Position)
Break
}
}
# If the path resolves, add it to the array of paths
If ($EntryProcessedPhase2) {
$SplitOutput += $EntryProcessedPhase2
}
}
}
$PinnedItems = #()
$Shell = New-Object -ComObject WScript.Shell
ForEach ($Path in $SplitOutput) {
# Determines if the entry in the registry is a link in the standard folder, if it is, resolve the path of the shortcut and add it to the array of pinnned items
If ((Split-Path $Path) -eq (Join-Path $env:USERPROFILE "AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar")) {
$Shell.CreateShortcut($Path).TargetPath
$PinnedItems += $Shell.CreateShortcut($Path).TargetPath
}
Else {
# If the link or executable is not in the taskbar folder, add it directly
$PinnedItems += $Path
}
}
# Unpin if the application is pinned
If ($Unpin.IsPresent) {
If ($PinnedItems -contains $Target) {
$Item.InvokeVerb("{:}")
Write-Host "Unpinning application $Target"
}
}
Else {
# Only pin the application if it hasn't been pinned
If ($PinnedItems -notcontains $Target) {
$Item.InvokeVerb("{:}")
Write-Host "Pinning application $Target"
}
}
# Remove the registry key and subkeys required to pin the application
If (Test-Path $Reg.Path3) {
Remove-Item -LiteralPath $Reg.Path3 -Recurse
}
}
Function Set-PinTaskbarCsv {
Param (
[Parameter(Mandatory=$true)]
$PinHashTable
,
[Parameter(Mandatory=$true)]
$UnpinHashTable
)
$Organization = "LIHC"
$RootRegistry = "HKCU:\Software\" + $Organization
$RootRegistryPinned = Join-Path $RootRegistry "Pinned"
# Unpin applications from taskbar
ForEach ($Entry in $UnpinHashTable.Keys) {
$Location = Format-VariablesString -String $UnpinHashTable.$Entry.Location
Add-Log "Taskbar app unpinned" $Location
Set-PinTaskbar -Target $Location -Unpin
}
# Pin applications to taskbar
$Groups = #("Group1","Group2","Group3","Group4","Group5")
ForEach ($Entry in $PinHashTable.Keys) {
$Entry
$Location = Format-VariablesString -String $PinHashTable.$Entry.Location
$ToTaskbar = [string]::IsNullOrWhiteSpace($PinHashTable.$Entry.Group1)
ForEach ($Group in $Groups) {
If (!([string]::IsNullOrWhiteSpace($PinHashTable.$Entry.$Group))) {
$ToTaskbar = (Get-UserGroups -Username $env:USERNAME -Group $PinHashTable.$Entry.$Group) -or $ToTaskbar
}
}
If (!([string]::IsNullOrWhiteSpace($PinHashTable.$Entry.TestPath))) {
$ToTaskbar = ((Test-Path $PinHashTable.$Entry.TestPath) -or (Test-Path $PinHashTable.$Entry.TestPath2)) -and $true
}
If ($ToTaskbar -and (Test-Path $Location) -and (!(Get-ItemProperty $RootRegistryPinned $Location -ErrorAction SilentlyContinue))) {
#Set-AppPinTaskbar -Application $Location
Set-PinTaskbar -Target $Location
Add-Log "Taskbar app Pinned" $Location
New-ItemProperty -Path $RootRegistryPinned -Name $Location 2>&1 > $null
$Status = $true
}
}
If ($Status) {
Get-Process -Name explorer | Stop-Process
Start-Process -FilePath explorer.exe
}
}