Set a Key on ImageList control at specific Index - winforms

I have an ImageList Control in a powershell form where I'm adding a few icons from a folder with this function:
function addToImagelist
{
$ICOs = Get-ChildItem "$IconDir" | Where-Object { $_.Extension -eq ".ico" }
foreach ($ICO in $ICOs)
{
Write-Debug "$IconDir\$ICO"
$Icon = [System.Drawing.Image]::FromFile("$IconDir\$ICO")
$imagelistIcons.Images.Add([String]$ICO, $Icon)
}
}
It's setting the ImageKey of every icon to the filename + extension.
What I'm now trying to do is changing the ImageKey afterwards.
I tried:
$imagelistIcons.Images.SetKeyName(0, 'NewKey')
But this outputs the following error:
ERROR: Exception calling "SetKeyName" with "2" argument(s): "Index was outside the bounds of the array."
Any idea what am I doing wrong here? $imagelistIcons.Images.Count tells me that there are definitely items in the ImageList.

Related

Visual Studio PowerShell Drag & Drop Attachments From Outlook to List Box

i have got the drag and drop working on my Windows Form. i can drop items from my desktop or any folder but if i try to Drag an attachment straight from Outlook it won't do any thing. do i need to add extra PowerShell commands int my current code
######################################## This is For Drag And Drop
$listBox1_DragOver = [System.Windows.Forms.DragEventHandler]{
if ($_.Data.GetDataPresent([Windows.Forms.DataFormats]::FileDrop))
{
$_.Effect = 'Copy'
}
Else
{
$_.Effect = 'None'
}
}
$listBox1_DragDrop = [System.Windows.Forms.DragEventHandler]{
foreach ($filename in $_.Data.GetData([Windows.Forms.DataFormats]::FileDrop))
{
$listBox1.Items.Add($filename)
}
}
### Add events to form ###
$listBox1.Add_DragOver($listBox1_DragOver)
$listBox1.Add_DragDrop($listBox1_DragDrop)
#$form.Add_FormClosed($form_FormClosed)
#### Show form and return result ###
$dialogResult = $Form12.ShowDialog()
if ($dialogResult -eq [System.Windows.Forms.DialogResult]::OK)
{
$Form12.SuspendLayout()
[array]$items = $listbox1.Items| sort -CaseSensitive
if ($items.Count -gt 1){
$items
}
ELSE
{
[string]$items[0]
}
$Form12.Close() | out-null
}
After lot's of research i came across the code below, to summarise what it does.
it will let you drag and drop files out of Outlook, it will then copy and paste that into a folder which then gives you the path and file name. it is pretty cool so if anyone else is stuck here is my working script and how i implemented it to my form
######################################## This is For Drag And Drop
$Listbox1.AllowDrop = $true
$Listbox1.Add_DragDrop({
if ($_.Data.GetDataPresent([Windows.Forms.DataFormats]::FileDrop)) {
foreach ($FileName in $_.Data.GetData([Windows.Forms.DataFormats]::FileDrop)) {
Copy-Item -Path $FileName -Destination $textbox6.text -Force
$Listbox1.Items.Add($FileName)
}
}
else
{
$Outlook = New-Object -ComObject Outlook.Application;
$Selection = $Outlook.ActiveExplorer().Selection
foreach ($Item in $Selection) {
foreach ($Attachment in $Item.Attachments) {
Write-Verbose $Attachment.FileName
$Name = Join-Path -Path $textbox6.text-ChildPath $Attachment.FileName
$Attachment.SaveAsFile($Name)
}
}
}
})
$Listbox1.Add_DragEnter({$_.Effect = [Windows.Forms.DragDropEffects]::Copy})
$Form12.Controls.Add($Listbox1)
# Activate the form
[void] $Form12.ShowDialog()
i had to change my form a little by adding an Input Folder Button & Textbox6 to show the text of the folder that has been selected this is important as the Script above needs a directory to save the files to, please see code below.
###################################### Get Folder Using Folder Browser and output text into textbox
$button4_Click = {
$folderBrowserDialog3=New-Object System.Windows.Forms.FolderBrowserDialog
[void]$folderBrowserDialog3.ShowDialog()
$folderBrowserDialog3.SelectedPath
$textBox6.Text = $folderBrowserDialog3.SelectedPath
}
$button6_Click = {
$folderBrowserDialog1=New-Object System.Windows.Forms.FolderBrowserDialog
[void]$folderBrowserDialog1.ShowDialog()
$folderBrowserDialog1.SelectedPath
$textBox2.Text = $folderBrowserDialog1.SelectedPath
}
$button7_Click = {
$folderBrowserDialog2=New-Object System.Windows.Forms.FolderBrowserDialog
[void]$folderBrowserDialog2.ShowDialog()
$folderBrowserDialog2.SelectedPath
$textBox3.Text = $folderBrowserDialog2.SelectedPath
}
after i got that to work the thing that bugged me the most was i couldn't see the files in the listbox so i added a button to do exactly that please see code below
###################################### Shows Files In ListBox 1
$button5_Click = {
#$textbox8.Text = ""
$listBox1.Items.Clear()
$items = Get-ChildItem $textbox6.Text
ForEach($item in $items){
$listBox1.Items.Add($item.FullName)
}
}
as you can see i have added $listbox1.Items.Clear() this will enable you to keep clicking the show files button without it duplicating the path and file in the listbox
the final result of my Form has come out great please see my image layout below, if anyone needs help to get it working on your own Form please comment and i will do my best to help.

Add to array without echoing index

I'm adding items to an array called $MissingIps using the following command
$MissingIps = #("")
[System.Collections.ArrayList]$ExistingIps = $MissingIps
if ($lbips -notcontains $awsip){
$MissingIps.Add("$awsip")
}
On execution, PowerShell automatically echos the index position, and I'm struggling to hide that.
Any ideas?
As an alternative to the suggested methods of suppressing the unwanted output: don't use an ArrayList collection in the first place. It's the Add() method of that class that generates the output. If you use a regular array you can append without output being generated:
$MissingIps = #()
if ($lbips -notcontains $awsip){
$MissingIps += $awsip
}
This will do the work:
$MissingIps = #("")
[System.Collections.ArrayList]$ExistingIps = $MissingIps
if ($lbips -notcontains $awsip){
$MissingIps.Add("$awsip") | out-null
}

Search and Remove Members from Objects

I'm attempting to search an existing object to find (members? is this what they're called?) where the name property is equal to the servername found in a text file (FalsePositiveDevices.txt) and then delete that entire member with all of its properties.
When I run the following there are no errors, but the $global:LimitedSNReportObject still has the members from the false positive list.
FalsePositiveList Example:
server1
server2
global:LimitedSNReportObject Example:
name,IPAddress,Platform
server1,10.10.10.10,Windows
server3,11.11.11.11,Linux
Code Example:
$FalsePositiveList = Get-Content
"C:\Users\USERNAME\Desktop\FalsePositiveDevices.txt"
foreach ($server in $FalsePositiveList) {
$global:LimitedSNReportObject | foreach-object {
if ($_.name -eq $server) {
$global:LimitedSNReportObject.psobject.members.remove($_)
}
}
}

Count # of errors in error log & Compare | Powershell (V2) | Array Data Structures

I have a hashtable of error codes and the max # of times it is acceptable for them to occur across a directory of error logs.
Like so:
Key Value
err1 2
err2 1
This table is created dynamically based on input from a control file (xml) so the # of elements can change.
I want to now be able to look for 'err1' and 'err2', count the # of times those occur, and then compare that back to the hashtable.
so I have something like this:
ForEach($file in $logs){
ForEach($key in $hashTable.keys){
If(Select-String $file -pattern $key){
#get key value, increment a counter for this key value (error code)
}
}
}
#Psuedo for next step...
<#
ForEach($key in $hashTable.keys){
If (CountFound > Key.Value) {
write-host $("Error: " + $key + " occurred too much."
}
}
#>
Is there a data structure in PowerShell that is good at storing variable/value pairs that is easily and quickly modifiable?
I don't want to create an array for each key value, then add an element to that array each time I find a matching error code in a file, then count the different array's lengths. But that's the best solution I can think of.
You can go OO way and have class to represent each error type you read from external source and then update those error instances based on your logic. Since you asked only for the better structure to hold your data, I focused only on that. Logic you already have.
$errorClass = #"
public class Error
{
public Error(string errorCode, int maxOccurances) {
ErrorCode = errorCode;
MaxOccurances = maxOccurances;
}
public string ErrorCode;
public int MaxOccurances;
public int ActualOccurances;
}
"#
Add-Type -TypeDefinition $errorClass
$error1 = New-Object Error("Err1", 2) # You get these values from xml
$error1.ActualOccurances = 5 # Update this property based on your logic
$error2 = New-Object Error("Err2", 1)
$error2.ActualOccurances = 3
$errArray = #($error1, $error2)
foreach ($err in $errArray) {
if ($err.ActualOccurances -gt $err.MaxOccurances) {
write-host $("Error: '" + $err.ErrorCode + "' occurred too much.")
}
}
Output:
Error: 'Err1' occurred too much.
Error: 'Err2' occurred too much.
I think this is probably the easiest way to obtain my goal.
ForEach($key in $hashTable.keys){
$count = 0
ForEach($file in $logs){
If(Select-String $file -pattern $key){
$count++
}
}
If($count -gt $hashTable.Get_Item($key){
#Do something
}
}
This way I avoid having another data structure entirely.

Add One Object from an Array to another Array

I can't figure out what I'm doing wrong and hope someone can point me in the right direction.
I'm trying to iterate through an array of objects and testing on each object and when something is true, I want to take that object and add it to it's own array, as a single object (just like it was in the original array of objects). I seem to be adding the information to the new array, but when I reference the new array by doing newarray[0] it gives me the first item of the object, not the entire object itself.
The issue appears to be with this line:
$global:MachinesInAD += $global:MachineObject
The data in the csv file is a machine hostname, the machines IP address, an error code, and an agent ID.
e.g. MACHINENAME, 10.10.10.10, ERROR101, 123456FF
Function ReadExcelReport (){
$global:Report = "C:\TEMP\Tools\Scripts\agents.csv"
$Unresponsive = import-csv $global:Report | Where-Object {($_.State -eq "QUEUED" -or $_.State -eq "FAILED")} #Add items to array from spreadsheet where the state is equal to queued or failed
$global:UnresponsiveMachineInfo = #()
foreach ($item in $Unresponsive){
$global:UnresponsiveMachineInfo += ,#($item.'Hostname', $item.'IP Address',$item.'Error',$item.'Agent Cert ID') #Build the object - Add the following columns hostname, ip address, error, agent cert id
}
ADCheck
}
Function ADCheck (){
$Machine = $null
$global:MachinesInAD = #()
$global:MachinesNotInAD = #()
$global:MachineObject = New-Object system.object
$global:MachineObject = $Null
$global:MachinesInAD = $null
$global:UnresponsiveMachineInfo | foreach-object { #Iterate through each object in the array
$global:MachineObject = $_
$Machine = $_[0] #Set Machine to the hostname AKA the first element in the array for the current ($_) object (objects defined above)
try{
write-host "Checking A.D. for: $Machine"
if (Get-ADComputer $Machine){ #Check to see if the machine is in A.D.
write-host "Found $Machine in A.D." -ForegroundColor Green
$global:MachinesInAD += $global:MachineObject
}
}
catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { #If the machine was NOT in A.D. catch the error it creates and...
write-warning -message "Machine $Machine not found in A.D."
$global:MachinesNotInAd += $MachineObject
}
}
}
This is happening because what you're calling an object, is just an array (which.. is an object, but your properties are elements, not properties).
Anyway, when you do this:
$global:MachinesInAD += $global:MachineObject
You end up concatenating the arrays.
#(1,2,3) + #(4,5,6)
That results in an array of 6 elements, not 3 numbers and an array.
You should use either a [hashtable] or a [PSObject] instead of an array; or as you did when you built the original one, you'll need to force it into a one elements array, something like:
$global:MachinesInAD += ,#($global:MachineObject)

Resources