I have one question. I don't understand what I am doing wrong, I wrote the following script to tell me if the folder exists to show a message, but it is now showing it.
If (Test-Path -path ($LabServer + $CaseName) -PathType Container){
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.MessageBox]::Show('Folder name exists, please enter a new name')
The script is supposed to create multiple folders in a specific location. I want to check if the folder name (Case Name) already exists, and if it does, do not create the folder with the same name, but the script continues and creates the folder and sub-folders.
Here is the full script.
Thank you for all your help
# Load required assemblies
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
# Drawing form and controls
$CreateFolder = New-Object System.Windows.Forms.Form
$CreateFolder.Text = "Create Multiple Custodian Folders"
$CreateFolder.Size = New-Object System.Drawing.Size(350,415)
$CreateFolder.FormBorderStyle = "FixedDialog"
$CreateFolder.TopMost = $true
$CreateFolder.MaximizeBox = $false
$CreateFolder.MinimizeBox = $false
$CreateFolder.ControlBox = $true
$CreateFolder.StartPosition = "CenterScreen"
$CreateFolder.Font = "Segoe UI"
#======================== CASE NAME ========================#
# adding a label to my form
$label_message = New-Object System.Windows.Forms.Label
$label_message.Location = New-Object System.Drawing.Size(20,8)
$label_message.Size = New-Object System.Drawing.Size(100,15)
$label_message.Text = "Case Name"
$CreateFolder.Controls.Add($label_message)
# CaseName
$CaseName = New-Object System.Windows.Forms.TextBox
$CaseName.Location = New-Object System.Drawing.Size(20,30)
$CaseName.Size = New-Object System.Drawing.Size(300,25)
$CaseName.ScrollBars = "Vertical"
$CreateFolder.Controls.Add($CaseName)
#======================== DROPBOX ========================#
$label_messageCombobox = New-Object System.Windows.Forms.Label
$label_messageCombobox.Location = New-Object System.Drawing.Size(20,60)
$label_messageCombobox.Size = New-Object System.Drawing.Size(100,15)
$label_messageCombobox.Text = "Pick a Server"
$CreateFolder.Controls.Add($label_messageCombobox)
$DropdownBox = New-Object System.Windows.Forms.ComboBox
$DropdownBox.Location = New-Object System.Drawing.Size(20,80)
$DropdownBox.Size = New-Object System.Drawing.Size(300,15)
$DropdownBox.Height = 200
$Dropdownbox.DropDownStyle = "DropDownList"
$CreateFolder.Controls.Add($DropdownBox)
$Servers = #("Lab Machine 40","Lab Machine 45","Lab Machine 50","Lab Machine 55")
foreach($Server in $Servers){
$DropdownBox.Items.Add($Server) | Out-Null
}
#======================== FUNCTION TO GET SERVER ========================#
Function Get-Server(){
$SelectedServer = $DropdownBox.SelectedItem.ToString()
if($SelectedServer -eq "Lab Machine 50") {
$Script:LabServer = Set-Location "\\Server50\K$" -PassThru
}
elseif($SelectedServer -eq "Lab Machine 55") {
$Script:LabServer = Set-Location "\\Server55\K$" -PassThru
}
elseif($SelectedServer -eq "Lab Machine 40") {
$Script:LabServer = Set-Location "\\Server40\K$" -PassThru
}
elseif($SelectedServer -eq "Lab Machine 45") {
$Script:LabServer = Set-Location "\\Server45\K$" -PassThru
}
}
#======================== INPUTBOX ========================#
$label_message2 = New-Object System.Windows.Forms.Label
$label_message2.Location = New-Object System.Drawing.Size(20,110)
$label_message2.Size = New-Object System.Drawing.Size(100,15)
$label_message2.Text = "Custodian Names"
$CreateFolder.Controls.Add($label_message2)
# Inputbox
$Inputbox = New-Object System.Windows.Forms.TextBox
$Inputbox.Multiline = $True;
$Inputbox.Location = New-Object System.Drawing.Size(20,130)
$Inputbox.Size = New-Object System.Drawing.Size(300,200)
$Inputbox.ScrollBars = "Vertical"
$CreateFolder.Controls.Add($Inputbox)
#======================== BUTTON ========================#
# add a button ti create folder
$button_ClickMe = New-Object System.Windows.Forms.Button
$button_ClickMe.Location = New-Object System.Drawing.Size(45,340)
$button_ClickMe.Size = New-Object System.Drawing.Size(240,32)
$button_ClickMe.TextAlign = "MiddleCenter"
$button_ClickMe.Text = "Create Folders"
$button_ClickMe.Add_Click({
#$FolderExist = $LabServer.Text + $CaseName.Text
If ($CaseName.TextLength -eq 0){
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.MessageBox]::Show('Please Enter a Case Name')
}
else {
If (Test-Path -path ($LabServer + $CaseName) -PathType Container){
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.MessageBox]::Show('Folder name exists, please enter a new name')
}
else {
if ($Inputbox.TextLength -eq 0){
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.MessageBox]::Show('Please enter 1 custodian name')
}
else {
Get-Server
Set-Location $LabServer
New-Item $CaseName.Text -type directory
Start-Sleep -Seconds 5
Set-Location ($LabServer.Text + $CaseName.Text)
ForEach ($Folder in $Inputbox.lines) {
New-Item $Folder -type directory
}
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.MessageBox]::Show('Folders were created')
[System.Windows.Forms.Application]::Exit()
}
}
}
})
$CreateFolder.Controls.Add($button_ClickMe)
# show form
$CreateFolder.Add_Shown({$CreateFolder.Activate()})
[void] $CreateFolder.ShowDialog()
You almost answered your own question here:
#$FolderExist = $LabServer.Text + $CaseName.Text
Check out the comments in the code:
If ($CaseName.TextLength -eq 0)
{
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.MessageBox]::Show('Please Enter a Case Name')
}
else
{
# If we are here $LabServer is not defined because Get-Server did not run yet
# You can take 2 different approaches, either use your function or
# Test-Path using the ComboBox
# Approach 1: Define $LabServer
Get-Server
# Now you can Test-Path
$path = Join-Path $LabServer -ChildPath $CaseName.Text
If (Test-Path -Path $path -PathType Container)
{
.....
}
# Approach 2:
$path = Join-Path ("\\{0}\K$" -f $DropdownBox.SelectedItem) -ChildPath $CaseName.Text
If (Test-Path -Path $path -PathType Container)
{
.....
}
.....
Edit
I think it's worth adding a few recommendations to reduce the number of nested conditions you have on your AddClick listener.
First, by default at the start of your form, disable the button:
$button_ClickMe.Enabled = $false
Now you can add 2 listeners on TextChanged for both TextBox:
$textChangedEvent = {
if($caseName.TextLength -and $DropdownBox.SelectedItem -and $Inputbox.TextLength)
{
$button_ClickMe.Enabled = $true
}
else
{
$button_ClickMe.Enabled = $false
}
}
$CaseName.Add_TextChanged($textChangedEvent)
$Inputbox.Add_TextChanged($textChangedEvent)
With this you can remove the 2 conditions you have on $CaseName.TextLength -eq 0 and $Inputbox.TextLength -eq 0.
Lastly, your AddClick event would look like this (Note that you wouldn't need the Get-Server function):
$buttonClickEvent = {
$path = "\\{0}\K$" -f $DropdownBox.SelectedItem
$path = Join-Path $path -ChildPath $CaseName.Text
if(Test-Path $path -PathType Container)
{
[System.Windows.Forms.MessageBox]::Show('Folder name exists, please enter a new name')
return
}
New-Item $path -Type Directory
Start-Sleep -Seconds 5
foreach($folder in $Inputbox.Lines)
{
$folder = $Folder.Trim()
$newFolder = Join-Path $path -ChildPath $folder
New-Item $newFolder -Type Directory
[System.Windows.Forms.MessageBox]::Show('Folders were created')
[System.Windows.Forms.Application]::Exit()
}
}
$button_ClickMe.Add_Click($buttonClickEvent)
Related
Is there a way to generate a url to download files either when uploading or after a file has been uploaded?
I'm working a powershell script that will either upload a single file or zip a directory and upload it to Sharefile.
I have most of it complete, but I cannot figured out how generate a download url or have one created at the time of the upload.
Code for the form below
###### Share Link Automation
### Environment Setting
If (-not(test-path -path "C:\windows\ARP\ShareLink")) {
New-Item -Force -Path C:\Windows\ARP\ShareLink -ItemType Directory
}
$SFLinkDestination = "C:\windows\ARP\ShareLink"
# Function to upload files to share file
Function Upload-File {
param (
$LocalData
)
Add-PSSnapin ShareFile
#Run the following interactively to create a login token that can be used by Get-SfClient in unattended scripts
$sfClient = Get-SfClient -Name ((Join-Path $env:USERPROFILE "Documents\Sharefile") + "\MySubdomain.sfps")
#upload directory is relative to the root of the account
#get the current user's home folder to use as the starting point
$ShareFileHomeFolder = (Send-SfRequest $sfClient -Entity Items).Url
# Create a PowerShell provider for ShareFile pointing to Personal Folsers\Send
New-PSDrive -Name sfDrive -PSProvider ShareFile -Client $sfClient -Root "\Personal Folders\Send" -RootUri $ShareFilePath
#upload all the files (recursively) in the local folder to the specified folder in ShareFile
Copy-SfItem -Path $LocalData -Destination "sfDrive:"
#Remove the PSProvider when we are done
Remove-PSDrive sfdrive
Exit
}
#### Init PowerShell Gui
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
#### The Form
[System.Windows.Forms.Application]::EnableVisualStyles()
$SFLinkForm = New-Object system.Windows.Forms.Form
$SFLinkForm.ClientSize = '480,300'
$SFLinkForm.text = "Share File Link"
$SFLinkForm.BackColor = "#ffffff"
$SFLinkForm.TopMost = $false
# $Image = [system.drawing.image]::FromFile("G:\APPS\temp\abp_main.bmp")
$SFLinkForm.BackgroundImage = $Image
$SFLinkForm.BackgroundImageLayout = "None"
$SFLinkForm.AutoSizeMode = "GrowAndShrink"
$SFLinkFormTitle = New-Object system.Windows.Forms.Label
$SFLinkFormTitle.text = "Share File Link"
$SFLinkFormTitle.ForeColor = "DarkRed"
$SFLinkFormTitle.AutoSize = $true
$SFLinkFormTitle.width = 45
$SFLinkFormTitle.height = 20
$SFLinkFormTitle.location = New-Object System.Drawing.Point(20,100)
$SFLinkFormTitle.Font = 'Microsoft Sans Serif,13,style=Bold'
$SFLinkFormTitleZiP = New-Object system.Windows.Forms.Label
$SFLinkFormTitleZip.text = "Share File Zip Name:"
$SFLinkFormTitleZip.ForeColor = "DarkRed"
$SFLinkFormTitleZip.AutoSize = $true
$SFLinkFormTitleZip.width = 45
$SFLinkFormTitleZip.height = 20
$SFLinkFormTitleZip.location = New-Object System.Drawing.Point(20,175)
$SFLinkFormTitleZip.Font = 'Microsoft Sans Serif,10,style=Bold'
$SFLinkFormDescription = New-Object system.Windows.Forms.Label
$SFLinkFormDescription.text = "When creating a link ..... TBD"
$SFLinkFormDescription.ForeColor = "DarkRed"
$SFLinkFormDescription.AutoSize = $false
$SFLinkFormDescription.width = 450
$SFLinkFormDescription.height = 20
$SFLinkFormDescription.location = New-Object System.Drawing.Point(20,125)
$SFLinkFormDescription.Font = 'Microsoft Sans Serif,10'
### Zip File Namer
$SFLinkFormZip = New-Object System.Windows.Forms.TextBox
$SFLinkFormZip.Location = New-Object System.Drawing.Size(20,200)
$SFLinkFormZip.Size = New-Object System.Drawing.Size(220,20)
$SFLinkForm.Controls.Add($SFLinkFormZip)
#### File Selector
$SFLinkFormFileBtn = New-Object system.Windows.Forms.Button
$SFLinkFormFileBtn.BackColor = "#ffffff"
$SFLinkFormFileBtn.text = "File"
$SFLinkFormFileBtn.width = 90
$SFLinkFormFileBtn.height = 20
$SFLinkFormFileBtn.location = New-Object System.Drawing.Point(200,230)
$SFLinkFormFileBtn.Font = 'Microsoft Sans Serif,10,style=Bold'
$SFLinkFormFileBtn.ForeColor = "#0"
$SFLinkForm.Controls.Add($SFLinkFormFileBtn)
$SFLinkFormFileBtnLabel = New-Object system.Windows.Forms.Label
$SFLinkFormFileBtnLabel.text = "Click to Select a File:"
$SFLinkFormFileBtnLabel.ForeColor = "DarkRed"
$SFLinkFormFileBtnLabel.AutoSize = $true
$SFLinkFormFileBtnLabel.width = 25
$SFLinkFormFileBtnLabel.height = 20
$SFLinkFormFileBtnLabel.location = New-Object System.Drawing.Point(20,232)
$SFLinkFormFileBtnLabel.Font = 'Microsoft Sans Serif,10,style=Bold'
$SFLinkFormFileBtn.Add_Click(
{
Add-Type -AssemblyName System.Windows.Forms
$SFLinkFormFClick = New-Object System.Windows.Forms.OpenFileDialog
$SFLinkFormFClick.Title = "Please Select File"
$SFLinkFormFClick.InitialDirectory = $InitialDirectory
$SFLinkFormFClick.filter = “All files (*.*)| *.*”
# If ($SFLinkFormFClick.ShowDialog() -eq "Cancel") {
If ($SFLinkFormFClick.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
$LocalData = ($SFLinkFormFClick).FileName
# [System.Windows.Forms.MessageBox]::Show("No File Selected. Please select a file !", "Error", 0,
# [System.Windows.Forms.MessageBoxIcon]::Exclamation)
}
Write-Host "The File Selected is ($SFLinkFormFClick).FileName"
Write-Host "$LocalData"
Upload-File -LocalData "$LocalData"
$SFLinkForm.Close()
Return
Exit
# $LocalData = ($SFLinkFormFClick).FileName
}
)
### Directory Selector
$SFLinkFormDirBtn = New-Object system.Windows.Forms.Button
$SFLinkFormDirBtn.BackColor = "#ffffff"
$SFLinkFormDirBtn.text = "Directory"
$SFLinkFormDirBtn.width = 90
$SFLinkFormDirBtn.height = 20
$SFLinkFormDirBtn.location = New-Object System.Drawing.Point(200,255)
$SFLinkFormDirBtn.Font = 'Microsoft Sans Serif,10,style=Bold'
$SFLinkFormDirBtn.ForeColor = "#0"
$SFLinkForm.Controls.Add($SFLinkFormDirBtn)
$SFLinkFormDirBtnLabel = New-Object system.Windows.Forms.Label
$SFLinkFormDirBtnLabel.text = "Click to Select a Directory:"
$SFLinkFormDirBtnLabel.ForeColor = "DarkRed"
$SFLinkFormDirBtnLabel.AutoSize = $true
$SFLinkFormDirBtnLabel.width = 25
$SFLinkFormDirBtnLabel.height = 20
$SFLinkFormDirBtnLabel.location = New-Object System.Drawing.Point(20,257)
$SFLinkFormDirBtnLabel.Font = 'Microsoft Sans Serif,10,style=Bold'
$SFLinkFormDirBtn.Add_Click(
{
$SFLinkFormDClick = New-Object System.Windows.Forms.FolderBrowserDialog
if ($SFLinkFormDClick.ShowDialog() -eq [System.Windows.Forms.DialogResult]::"OK") {
$SFLinkFormDirName = $SFLinkFormDClick.SelectedPath
$ZipWrapper = $SFLinkFormZip.Text
$LocalData = (Join-Path C:\windows\ARP\ShareLink\ "$ZipWrapper.zip")
### Directgory Compression
# cd C:\windows\ARP\ShareLink
Compress-Archive "$SFLinkFormDirName\*" "$LocalData"
Upload-File -LocalData "$LocalData"
$SFLinkForm.Close()
Return
Exit
}
}
)
$SFLinkForm.controls.AddRange(#($SFLinkFormTitle,$SFLinkFormDescription,$SFLinkFormFileBtn,$SFLinkFormDirBtn,$SFLinkFormFileBtnLabel,$SFLinkFormDirBtnLabel,$SFLinkFormZip,$SFLinkFormTitleZiP))
$SFLinkForm.ShowDialog()
$SFLinkForm.Close()
Exit
Search all the API codes for Sharefile and could not find the powershell equivalence of "Get A Link" in the API Entities.
What I am trying to accomplish is to create buttons that launch exe files in a certain directory when clicked, but when I try using a foreach loop to create a few buttons, all of the buttons just launch the file the last button is supposed to launch.
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Main Window'
$form.Size = New-Object System.Drawing.Size(600,400)
$flp = New-Object System.Windows.Forms.FlowLayoutPanel
$flp.Location = New-Object System.Drawing.Point(0,0)
$flp.Height = $form.Height
$flp.Width = $form.Width
$form.Controls.Add($flp)
$files = Get-ChildItem "$home\Downloads" -Include *.exe -Name
foreach ($file in $files){
$button = New-Object System.Windows.Forms.Button
$flp.Controls.Add($button)
$button.Width = 100
$button.Height = 50
$button.Text = $file
$button.Add_Click{
Start-Process -FilePath "$home\Downloads\$file"
}
}
$form.Topmost = $true
$form.ShowDialog()
Whatever I'm doing is probably pretty stupid, so I was just looking for any alternatives or solutions to this other than to just hard code everything.
It is likely that you need to use .GetNewClosure() ScriptBlock method so that each script block (button click event) holds the current value of the $file variable at the moment of enumeration.
Example of what this means:
$blocks = foreach($i in 0..5) {
{ "hello $i" }
}
& $blocks[0] # => hello 5
& $blocks[1] # => hello 5
$blocks = foreach($i in 0..5) {
{ "hello $i" }.GetNewClosure()
}
& $blocks[0] # => hello 0
& $blocks[1] # => hello 1
In that sense, and assuming this is the issue, the following should work:
foreach ($file in $files) {
$button = New-Object System.Windows.Forms.Button
$button.Width = 100
$button.Height = 50
$button.Text = $file
$thisEvent = {
Start-Process -FilePath "$home\Downloads\$file"
}.GetNewClosure()
$button.Add_Click($thisEvent)
$flp.Controls.Add($button)
}
A nice alternative to having a need to use .GetNewClosure() can be seen on this answer. The .Tag property of the Button can be used to store the information of the file's path which then can be used on the button's .Click event:
foreach ($file in $files) {
$button = New-Object System.Windows.Forms.Button
$button.Width = 100
$button.Height = 50
$button.Text = $file
# Store the file's path in the Tag's property of this Button
$button.Tag = "$home\Downloads\$file"
$button.Add_Click({
Start-Process -FilePath $this.Tag
})
$flp.Controls.Add($button)
}
I want to decide which folder that I need to choose based on my data, then if I can't find it, it will show the GUI for waiting and do looping to check it. I try this code, I can find the folder, but when I can't find it once I want to show the GUI it returns some error.
This is how I checking the folder
function FIND {
Write-Host "call the function that can call the GUI.ps1 script"
$Path = "D:\Process"
Write-Host "Starting Mapping SSID and Finding Job"
$SSID_Unit = "111dddddfafafesa"
Try{
$Path_Job = (Get-Item (Get-ChildItem "$Path\*\SSID_LST" | Select-String -Pattern "$SSID_Unit").Path).Directory.FullName
$global:Result = [PSCustomObject]#{
Exists = $true
FileName = $Path_Job.FullName
Attempts = 1
}
Write-Host "Job'$($global:Result.FileName)' Exists. Found after $($global:Result.Attempts) attempts." -ForegroundColor Green
Write-Host "Continue to Assigned Job"
Pause
} Catch {
Write-Host "Waiting for the jobss"
& D:\X\Wait_GUI.ps1 -Path $Path_Job -MaxAttempts 20
Write-Host "Job not found after $($global:Result.Attempts) attempts." -ForegroundColor Red
}
}
FIND
This is the GUI
Param (
[string]$Path = '*.*',
[string]$MaxAttempts = 5
)
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
# set things up for the timer
$script:nAttempts = 0
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000 # 1 second
$timer.Add_Tick({
$global:Result = $null
$script:nAttempts++
$Path_Job = Get-Item -Path $Path
if ($Path_Job) {
$global:Result = [PSCustomObject]#{
Exists = $true
FileName = $Path_Job.FullName
Attempts = $script:nAttempts
}
$timer.Dispose()
$Form.Close()
}
elseif ($script:nAttempts -ge $MaxAttempts) {
$global:Result = [PSCustomObject]#{
Exists = $false
FileName = ''
Attempts = $script:nAttempts
}
$timer.Dispose()
$Form.Close()
}
})
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = '617,418'
$Form.text = "AutoGM"
$Form.BackColor = "#8b572a"
$Form.TopMost = $false
$Form.WindowState = 'Maximized'
$Label1 = New-Object system.Windows.Forms.Label
$Label1.text = "UNDER AUTOMATION PROCESS"
$Label1.AutoSize = $true
$Label1.width = 25
$Label1.height = 10
$Label1.Anchor = 'top,right,bottom,left'
$Label1.ForeColor = "#ffffff"
$Label1.Anchor = "None"
$Label1.TextAlign = "MiddleCenter"
$Label2 = New-Object system.Windows.Forms.Label
$Label2.text = "Waiting for the job..."
$Label2.AutoSize = $true
$Label2.width = 25
$Label2.height = 10
$Label2.ForeColor = "#ffffff"
$Label2.Anchor = "None"
$Label2.TextAlign = "MiddleCenter"
$Form.controls.AddRange(#($Label1,$Label2))
[void]$Form.Show()
# Write-Host $Form.Height
# Write-Host $Form.Width
$Label1.location = New-Object System.Drawing.Point(($Form.Width*0.35), ($Form.Height*0.4))
$Label2.location = New-Object System.Drawing.Point(($form.Width*0.43), ($Form.Height*0.5))
$L_S = (($Form.Width/2) - ($Form.Height / 2)) / 15
$Label1.Font = "Microsoft Sans Serif, $L_S, style=Bold"
$Label2.Font = "Microsoft Sans Serif, $L_S, style=Bold"
$Form.controls.AddRange(#($Label1,$Label2))
# start the timer as soon as the dialog is visible
$Form.Add_Shown({ $timer.Start() })
$Form.Visible = $false
[void]$Form.ShowDialog()
# clean up when done
$Form.Dispose()
if not found, it return this
Waiting for the jobss
Job not found after 1 attempts.
and the GUI is not shown
Ok, first of all, you are using different tests for the file and/or directory in the code and in the GUI. Furthermore, you call the GUI.ps1 file with a path set to a $null value.
I would change your code to something like this:
$Path = "D:\Process\*\SSID_LST\*" # the path to look for files
$SSID_Unit = "111dddddfafafesa" # the Search pattern to look for inside the files
function Test-FileWithGui {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, Position = 0)]
[string]$Path,
[Parameter(Mandatory = $true, Position = 2)]
[string]$Pattern,
[int]$MaxAttempts = 5
)
Write-Host "Starting Mapping SSID and Finding Job"
# set up an 'empty' $global:Result object to return on failure
$global:Result = '' | Select-Object #{Name = 'Exists'; Expression = {$false}}, FileName, Directory, #{Name = 'Attempts'; Expression = {1}}
# test if the given path is valid. If not, exit the function
if (!(Test-Path -Path $Path -PathType Container)) {
Write-Warning "Path '$Path' does not exist."
return
}
# try and find the first file that contains your search pattern
$file = Select-String -Path $Path -Pattern $Pattern -SimpleMatch -ErrorAction SilentlyContinue | Select-Object -First 1
if ($file) {
$file = Get-Item -Path $file.Path
$global:Result = [PSCustomObject]#{
Exists = $true
FileName = $file.FullName
Directory = $file.DirectoryName
Attempts = 1
}
}
else {
& "D:\GUI.ps1" -Path $Path -Pattern $Pattern -MaxAttempts $MaxAttempts
}
}
# call the function that can call the GUI.ps1 script
Test-FileWithGui -Path $Path -Pattern $SSID_Unit -MaxAttempts 20
# show the $global:Result object with all properties
$global:Result | Format-List
# check the Global result object
if ($global:Result.Exists) {
Write-Host "File '$($global:Result.FileName)' Exists. Found after $($global:Result.Attempts) attempts." -ForegroundColor Green
}
else {
Write-Host "File not found after $($global:Result.Attempts) attempts." -ForegroundColor Red
}
Next the GUI file.
As you are now searching for a file that contains some text, you need a third parameter to call this named Pattern.
Inside the GUI file we perform the exact same test as we did in the code above, using the parameter $Pattern as search string:
Param (
[string]$Path,
[string]$Pattern,
[int]$MaxAttempts = 5
)
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Application]::EnableVisualStyles()
# set things up for the timer
$script:nAttempts = 0
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 1000 # 1 second
$timer.Add_Tick({
$global:Result = $null
$script:nAttempts++
# use the same test as you did outside of the GUI
# try and find the first file that contains your search pattern
$file = Select-String -Path $Path -Pattern $Pattern -SimpleMatch -ErrorAction SilentlyContinue | Select-Object -First 1
if ($file) {
$file = Get-Item -Path $file.Path
$global:Result = [PSCustomObject]#{
Exists = $true
FileName = $file.FullName
Directory = $file.DirectoryName
Attempts = $script:nAttempts
}
$timer.Dispose()
$Form.Close()
}
elseif ($script:nAttempts -ge $MaxAttempts) {
$global:Result = [PSCustomObject]#{
Exists = $false
FileName = $null
Directory = $null
Attempts = $script:nAttempts
}
$script:nAttempts = 0
$timer.Dispose()
$Form.Close()
}
})
$Form = New-Object system.Windows.Forms.Form
$Form.ClientSize = '617,418'
$Form.Text = "AutoGM"
$Form.BackColor = "#8b572a"
$Form.TopMost = $true
$Form.WindowState = 'Maximized'
# I have removed $Label2 because it is easier to use
# just one label here and Dock it to Fill.
$Label1 = New-Object system.Windows.Forms.Label
$Label1.Text = "UNDER AUTOMATION PROCESS`r`n`r`nWaiting for the job..."
$Label1.AutoSize = $false
$Label1.Dock = 'Fill'
$Label1.TextAlign = "MiddleCenter"
$Label1.ForeColor = "#ffffff"
$L_S = (($Form.Width/2) - ($Form.Height / 2)) / 10
$Label1.Font = "Microsoft Sans Serif, $L_S, style=Bold"
$Form.controls.Add($Label1)
# start the timer as soon as the dialog is visible
$Form.Add_Shown({ $timer.Start() })
[void]$Form.ShowDialog()
# clean up when done
$Form.Dispose()
The results during testing came out like below
If the file was found within the set MaxAttempts tries:
Starting Mapping SSID and Finding Job
Exists : True
FileName : D:\Process\test\SSID_LST\blah.txt
Directory : D:\Process\test\SSID_LST
Attempts : 7
File 'D:\Process\test\SSID_LST\blah.txt' Exists. Found after 7 attempts.
When the file was NOT found:
Starting Mapping SSID and Finding Job
Exists : False
FileName :
Directory :
Attempts : 20
File not found after 20 attempts.
If even the folder $Path was not found, the output is
Starting Mapping SSID and Finding Job
WARNING: Path 'D:\Process\*\SSID_LST\*' does not exist.
Exists : False
FileName :
Directory :
Attempts : 1
File not found after 1 attempts.
Hope that helps
Following on from a previous question
The actual code is a lot more complex, but the following is working example of my loop:
Add-Type -AssemblyName System.Windows.Forms
$source = '\\servera\files'
$destination = '\\server b\files'
$form = New-Object System.Windows.Forms.Form
$CopyOutput = New-Object System.Windows.Forms.Label
$CopyOutput.Location = '10,15'
$CopyOutput.Size = '350,20'
$form.Text = "$DomainName Folder/Archive Copy"
$form.Size = '380,130'
$form.CancelButton = $ExitButton
$form.Add_FormClosing({
$script:CancelLoop = $true
})
$StartButton = New-Object System.Windows.Forms.Button
$StartButton.Name = 'StartButton'
$StartButton.Location = '10,50'
$StartButton.Size = '75,23'
$StartButton.Text = 'Start Copy'
$StartButton.Enabled = $true
$PauseButton = New-Object System.Windows.Forms.Button
$PauseButton.Location = '100,50'
$PauseButton.Size = '75,23'
$PauseButton.Text = 'Pause Copy'
$PauseButton.Enabled = $true
$StopButton = New-Object System.Windows.Forms.Button
$StopButton.Location = '190,50'
$StopButton.Size = '75,23'
$StopButton.Text = 'Stop Copy'
$StopButton.Enabled = $true
$ExitButton = New-Object System.Windows.Forms.Button
$ExitButton.Name = 'ExitButton'
$ExitButton.Location = '280,50'
$ExitButton.Size = '75,23'
$ExitButton.Text = 'Exit'
$ExitButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.Controls.AddRange(#($StartButton,$PauseButton,$StopButton,$ExitButton,$CopyOutput))
$StopButton.Add_Click({
$script:PauseToggle = $false
$script:CancelLoop = $true
})
$PauseButton.Add_Click({
# Boolean change value to true/false
$script:PauseToggle = !$script:PauseToggle
})
$StartButton.Add_Click({
$script:CancelLoop = $false
$script:PauseToggle = $false
$StopButton.Enabled = $true
$StartButton.Enabled = $false
Get-ChildItem -LiteralPath $Source -Recurse -File | ForEach {
Copy-Item -LiteralPath $_.FullName -Destination $Destination
$CopyOutput.Text = ('Copying' + $_.FullName)
[System.Windows.Forms.Application]::DoEvents()
If($script:CancelLoop -eq $true) {
$CopyOutput.Text = 'Cancel copy'
#Exit the loop
Break;
}
If ($script:PauseToggle) {
$CopyOutput.Text = 'Paused'
Do {
[System.Windows.Forms.Application]::DoEvents()
} Until (!$script:PauseToggle)
}
}
$CancelButton.Enabled = $false
$StartCopyButton.Enabled = $true
})
$form.ShowDialog()
$form.Dispose()
All the articles I can find say that this code should work ok - can anyone advise how to avoid the unhandled exception which results when the "Stop" button is pressed?
All the articles I can find say that this code should work ok - can anyone advise how to avoid the unhandled exception which results when the "Stop" button is pressed?
From what I can see in my research, this is an issue with using BREAK in forms. I did however find a workaround from here
Using this commandlet will stop the current pipeline instead of using BREAK
Filter Stop-Pipeline {
$sp = { Select-Object -First 1 }.GetSteppablePipeline($MyInvocation.CommandOrigin)
$sp.Begin($true)
$sp.Process(0)
}
I like to change a GUI element (windows-form in powershell ISE), when a new file is created. Therefore I set up a form and start a filesystemwatcher in another runspace (MWE):
# this function should be called when a new file is created
function foobar(){
$form.BackColor = "black"
}
# set up runspace for async FileSystemWatcher
$Runspace = [runspacefactory]::CreateRunspace()
$PowerShell = [System.Management.Automation.PowerShell]::Create()
$PowerShell.runspace = $Runspace
$Runspace.Open()
[void]$PowerShell.AddScript({
$logFile = 'C:\powershell\test.log'
$dirName = 'C:\powershell\'
$hotFolder = New-Object IO.FileSystemWatcher $dirName -Property #{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'}
Register-ObjectEvent $hotFolder Created -SourceIdentifier FileCreated -Action {
$name = $Event.SourceEventArgs.Name
$path = $Event.SourceEventArgs.FullPath
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
Out-File -FilePath $logFile -Append -InputObject "The file '$name' was $changeType at $timeStamp"
# this call does not work
foobar
}
})
$AsyncObject = $PowerShell.BeginInvoke()
# set up form
$form = New-Object System.Windows.Forms.Form
$form.ShowDialog()
The FileSystemWatcher works (log-file is written), but the call of "foobar" is ignored / does not work.
My first try was to Register the FileSystemWatcher within the form, which does not work (similar to this: FileSystemWatcher and GUI). I found this thread FileSystemWatcher kommt nicht mit Form zurecht (german only), which suggests the use of runspaces.
The runspace solves the stuck GUI-problem, but I need a way to trigger events in the form, when the fileSystemWatcher registers a new file. How can I achive that?
in short:
1) how can the FileSystemWatcher trigger a change of a GUI-element
2) are runspaces the right approach in this case
I'm not an expert in powershell (still learning). Any help and suggestions are appreciated.
Thanks in advance.
I did some research and found a solution:
added a synchronized hashtable to share variables between runspaces
added a button of the form to the hashtable (this button will be hidden in the final version drawing.Size(0,0) )
fileSystemWatcher makes use of performclick() to click the button
button calls desired function on click
There is still a point which feel awkward:
to unregister the fileSystemWatcher I set a shared variable to 1 and trigger the fileSystemWatcher by generating a file
Is there a more elegant way to do this?
Do I miss some points where the code is unnecessarily complicated?
Any comments are appreciated.
Here is a MWE. To use it set the $dir variable to your need. (MWE does not work in not updated powershell which ships with Win7)
# set working dir
$dir = 'C:\Downloads'
Write-Host 'working dir' $dir
# create function which is called when new file is created
function showPath($path){
$label.Text = $path
}
# set up runspace for async FileSystemWatcher
$Runspace = [runspacefactory]::CreateRunspace()
$Runspace.Open()
# synchronized hashtable and hashtable elements
$sync = [Hashtable]::Synchronized(#{})
$sync.path = 1 # random start value
$sync.exit = 0 # switch: if set to 1 fileSystemWatcher will be unregistert when next event occurs
$sync.dir = $dir
$btnNewFile= New-Object System.Windows.Forms.Button
$btnNewFile.Location = New-Object System.Drawing.Size(220,10)
$btnNewFile.Size = New-Object System.Drawing.Size(150,23)
$btnNewFile.Text = "do not click - fake button"
$btnNewFile.Add_Click({
$newPath = $sync.path
$form.text = $newPath
showPath($newPath)
})
$sync.btnNewFile = $btnNewFile
$Runspace.SessionStateProxy.SetVariable("sync", $sync)
$PowerShell = [System.Management.Automation.PowerShell]::Create()
$PowerShell.runspace = $Runspace
[void]$PowerShell.AddScript({
$logFile = Join-Path $sync.dir test.log
$dirName = $sync.dir
$hotFolder = New-Object IO.FileSystemWatcher $dirName -Property #{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'}
Register-ObjectEvent $hotFolder Created -SourceIdentifier FileCreated -Action {
$path = $Event.SourceEventArgs.FullPath
$name = $Event.SourceEventArgs.Name
$changeType = $Event.SourceEventArgs.ChangeType
$timeStamp = $Event.TimeGenerated
# check if exit condition is met
if($sync.exit -eq 1){
Out-File -FilePath $logFile -Append -InputObject "Exit file: '$name'"
Unregister-Event FileCreated
}
else{
Out-File -FilePath $logFile -Append -InputObject "The file '$name' was $changeType at $timeStamp"
# set path to synchroniszed variable
$sync.path = $path
# click Button to trigger function call
$sync.btnNewFile.PerformClick()
}
}
})
$AsyncObject = $PowerShell.BeginInvoke()
# GUI setup
$labelHeader = New-Object System.Windows.Forms.Label
$labelHeader.Location = New-Object System.Drawing.Size(10,50)
$labelHeader.Size = New-Object System.Drawing.Size(100,23)
$labelHeader.Text = 'path to new file:'
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Size(110,50)
$label.Size = New-Object System.Drawing.Size(200,23)
$label.Text = 'no file created'
$global:fileCounter = 0
$btnCreateFile = New-Object System.Windows.Forms.Button
$btnCreateFile.Location = New-Object System.Drawing.Size(10,10)
$btnCreateFile.Size = New-Object System.Drawing.Size(100,23)
$btnCreateFile.Text = "New File"
$btnCreateFile.Add_Click({
$global:fileCounter+=1
$fileName = "$global:fileCounter.txt"
$newFile = Join-Path $dir $fileName
New-Item $newFile -ItemType file
})
$btnExit = New-Object System.Windows.Forms.Button
$btnExit.Location = New-Object System.Drawing.Size(110,10)
$btnExit.Size = New-Object System.Drawing.Size(100,23)
$btnExit.Text = "&Exit"
$btnExit.Add_Click({
$sync.Exit = 1
$btnCreateFile.PerformClick()
$Powershell.Dispose()
$form.Close()
})
# set up form
$form = New-Object System.Windows.Forms.Form
$form.Width = 400
$form.Height = 120
$form.Controls.Add($btnCreateFile)
$form.Controls.Add($btnExit)
$form.Controls.Add($labelHeader)
$form.Controls.Add($label)
$form.Controls.Add($sync.btnNewFile)
$form.ShowDialog()
I really like your solution, but the problem, like you say, is not working for PS2.0, including the service pack 1 for Win7, where I need.
My solution for the GUI update works both in PS2(win7) and in PS3(win10), it is based in Windows Presentation Framework(WPF) instead Windows Forms, because with WPF we can use Data Binding and the INotifyPropertyChanged interface. I have based my work in Trevor Jones web How-To ,with some tricks to get work.
For your question about Unregister the system I have translated, from VB to PS, a post from Mike Ober in SpiceWorks, in which his concept to Register and Unregister the system based in global variables and possible errors inspired me.
Here is my code:
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase
Function Create-WPFWindow {
Param($Hash)
# Create a window object
$Window = New-Object System.Windows.Window
$Window.Width = '600'
$Window.Height = '300'
$Window.Title = 'WPF-CONTROL'
$window.WindowStartupLocation = [System.Windows.WindowStartupLocation]::CenterScreen
$Window.ResizeMode = [System.Windows.ResizeMode]::NoResize
# Create a Label object
$Label = New-Object System.Windows.Controls.Label
$Label.Height = 40
$Label.HorizontalContentAlignment = 'Left'
$Label.VerticalContentAlignment = 'Center'
$Label.FontSize = 15
$Label.Content = 'Actividad:'
$Hash.Label = $Label
# Create a TextBlock object
$TextBlock = New-Object System.Windows.Controls.TextBlock
$TextBlock.Height = 150
$TextBlock.FontSize = 20
$TextBlock.TextWrapping = 'Wrap'
$Hash.TextBlock = $TextBlock
# Create a Button1 object
$Button1 = New-Object System.Windows.Controls.Button
$Button1.Width = 300
$Button1.Height = 35
$Button1.HorizontalContentAlignment = 'Center'
$Button1.VerticalContentAlignment = 'Center'
$Button1.FontSize = 20
$Button1.Content = 'Iniciar'
$Hash.Button1 = $Button1
# Assemble the window
$StackPanel1 = New-Object System.Windows.Controls.StackPanel
$StackPanel1.Margin = '150,20,5,5'
$StackPanel1.Orientation = 'Horizontal'
$StackPanel1.Children.Add($Button1)
$StackPanel2 = New-Object System.Windows.Controls.StackPanel
$StackPanel2.Margin = '5,5,5,5'
$StackPanel2.Orientation = 'Vertical'
$StackPanel2.Children.Add($Label)
$StackPanel2.Children.Add($TextBlock)
$StackPanel = New-Object System.Windows.Controls.StackPanel
$StackPanel.Margin = '5,5,5,5'
$StackPanel.Children.Add($StackPanel1)
$StackPanel.Children.Add($StackPanel2)
$Window.Content = $StackPanel
# Stop the service and release the resources
$Window.Add_Closing({
$Hash.On = $false
$global:p.BeginInvoke()
$global:p.Dispose()})
$Hash.Window = $Window
}
$Hash = [hashtable]::Synchronized(#{})
# Create a WPF window and add it to a Hash table
Create-WPFWindow $Hash | Out-Null
# Create a datacontext for the TextBlock, we add it to the synchronized $Hash to update the GUI from the FileSystemWatcher Event.
$DataContext = New-Object System.Collections.ObjectModel.ObservableCollection[Object]
$Text = [string]'Pulse el botón para iniciar el sistema.'
$DataContext.Add($Text)
$Hash.TextBlock.DataContext = $DataContext
$Hash.DataContext = $DataContext
$Hash.path='C:\POWERSHELL_PROJECT\Result'
# These two vars are for my needs, you can obviate them or delete
$Hash.urlLOGIN=''
$Hash.urlLOTE=''
$Hash.fileWatcher = $null
$Hash.LOG='C:\POWERSHELL_PROJECT\Result\LOG.log'
$Hash.firstEvent = $false
$Hash.On=$false
$Hash.msg=''
# Create and set a binding on the TextBlock object
$Binding = New-Object System.Windows.Data.Binding -ArgumentList '[0]'
$Binding.Mode = [System.Windows.Data.BindingMode]::OneWay
[void][System.Windows.Data.BindingOperations]::SetBinding($Hash.TextBlock,[System.Windows.Controls.TextBlock]::TextProperty, $Binding)
# Add an event for the Button1 click to Register FileSystemWatcher and Unregister it
$Hash.Button1.Add_Click({
if ($Hash.On -eq $true){
$Hash.On = $false
$Hash.Button1.Background = 'Green'
$Hash.Button1.Content = 'Iniciar'
}else{
$Hash.On = $true
$Hash.Button1.Background = 'Red'
$Hash.Button1.Content = 'Detener'
}
$p.BeginInvoke() | Out-Null
})
# Multithreading runspaces for FileSystemWatcher
$rs_dForm = [Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
$rs_dForm.ApartmentState = 'STA'
$rs_dForm.ThreadOptions = 'ReuseThread'
$rs_dForm.Open()
$rs_dForm.SessionStateProxy.SetVariable('Hash', $Hash)
$p = [PowerShell]::Create().AddScript({
Function global:OnFileSystemWatcherError {
FileEventListener -Path $Hash.path
}
# With simple function we can refresh the Textbox and Log
Function global:Refresh-WPF-and-LOG {
$Hash.DataContext[0] = $Hash.msg
echo $Hash.msg >> $Hash.LOG
}
Function global:FileEventListener ($Path){
if ($Hash.On){
$Hash.fileWatcher = New-Object System.IO.FileSystemWatcher
$Hash.fileWatcher.Path = $Path
$Hash.fileWatcher.Filter = '*.xml'
$Hash.fileWatcher.IncludeSubdirectories = $false
$Hash.fileWatcher.InternalBufferSize = 32768
$Hash.fileWatcher.EnableRaisingEvents=$true
Register-ObjectEvent -InputObject $Hash.fileWatcher -EventName Changed -SourceIdentifier File.Changed -Action {
$Global:t = $event
if (!$Hash.firstEvent){
try{
# For example you can:
$Hash.msg = '[' + $(Get-Date -Format u | foreach {$_ -replace ' ','-'}).ToString() + ']--' + $event.SourceEventArgs.Name
}catch{
$Hash.msg = '[' + $(Get-Date -Format u | foreach {$_ -replace ' ','-'}).ToString() + ']--' + $_.Exception.Message + ' ' + $_.Exception.ItemName
}finally{
Refresh-WPF-and-LOG
}
$Hash.firstEvent=$true
}else{
$Hash.firstEvent=$false
}
}
# With this Register we control the errors from the FileSystemWatcher system, and reinit it this case
Register-ObjectEvent -InputObject $Hash.fileWatcher -EventName Error -SourceIdentifier File.Error -Action {
$Global:t = $event
$Hash.On = $false
OnFileSystemWatcherError
}
$Hash.msg = '[' + $(Get-Date -Format u | foreach {$_ -replace ' ','-'}).ToString() + ']--' + 'Servicio INICIADO.'
}else{
if ( $Hash.fileWatcher -ne $null ){
$Hash.fileWatcher.EnableRaisingEvents=$false
Unregister-Event File.Changed
Unregister-Event File.Error
$Hash.fileWatcher.Dispose()
$Hash.fileWatcher=$null
$Hash.msg='[' + $(Get-Date -Format u | foreach {$_ -replace ' ','-'}).ToString() + ']--' + 'Sistema DETENIDO.'
}
}
Refresh-WPF-and-LOG
}
FileEventListener -Path $Hash.path
})
$p.Runspace = $rs_dForm
# Show the window
$Hash.Window.ShowDialog() | Out-Null
I hope to help.