I ran into an interesting issue today when removing rows in a DataGridView object in C#. I wrote a method that would loop through each cell to find a matching value that was passed, and when it found a match it would remove the cell’s associated row entirely. However, the method would only remove half of the cells with the associated data. As an example, if I had 6 rows with cells holding “Pancakes” and I wanted to remove the rows with cells containing “Pancakes,” it would only remove three.

So to provide a better example, here’s my crude MSPaint skills in action to impart a simple array of rows with cells holding certain values.

p1

Now, the first method removed the first cell with no issues, as it looped through the first element being 0, and found “pancake.” I noticed that it resorted the rows by decrementing their index values by 1.

p2

So the next iteration would find “toaster” and skip that row. With the row being skipped, the array of rows kept their same index values which led to element 2 that held “pancake” being removed. We were left with three values and it wanted to find Element 3, which naturally would be the 4th item in the list. It was out of bounds and we were left with “pancake”, “toaster”, “pancake” and if no error checking was implemented the program would vomit due to searching for a nonexistent element.

p3

The next solution would solve this issue. It starts as a do while loop with a bool parameter dictating whether a row was removed or not. The loop would continue to run while a row was removed. In the loop, a for loop was defined and looped through each row, and subsequently each cell. If a matching row was found and removed, the for loops would be broken and subsequently the row removed would be set to true. The do while loop would eventually iterate through each row and remove the rows associated because after the loop would break, the index count would start over at 0 and always iterate through the index’s new count.

p4

In return, I wrote this snippet to resolve the problem:

public void RemoveRow(DataGridView datagrid, string Identifier)
{
    bool rowRemoved = false;
    do
    {
        rowRemoved = false;
        for (int i = 0; i < datagrid.Rows.Count; i++)
        {
            for (int j = 0; j < datagrid.Rows[i].Cells.Count; j++)
            {
                try
                {
                    if (datagrid.Rows[i].Cells[j].Value != null)
                    {
                        if (datagrid.Rows[i].Cells[j].Value.ToString() == Identifier)
                        {
                            datagrid.Rows.RemoveAt(i);
                            rowRemoved = true;
                            break;
                        }
                        else { }
                    }
                }
                catch (Exception)
                {

                }
            }
            if (rowRemoved)
            {
                break;
            }
        }
    } while (rowRemoved);
}

I wanted to share a script I wrote a while back that will remotely install MSU patches. It requires PSTools to work and I ran this using Powershell 3.0.

This little method requires using two scripts, one that will install the MSU files locally, while the main script will execute the local installer remotely. It follows this process:

-The main script is ran and passed a path to where the MSU files are located at along with the secondary script, along with a textfile containing a list of computers.

The computer list should look like the following:
Computer1
CoolComputer252
ILikePancakes510

-The main script will robocopy all the files to the remote computer, this include the MSU patches and the secondary script.
-The main script subsequently runs PSExec on the secondary script which will install all the MSU patches under the system account.
-The secondary script then proceeds to taskkill itself to ensure the primary script starts installing patches on the next computer in the array.

It is also imperative that you have the proper credentials to remotely access the computer else this will fail.

Here is the first script.

Function Install_Patches([string]$path, [string]$computerList)
{
    Get-Content $computerList                                                     
    $arrayOfComputers = @()                                                  
    $arrayOfComputers = (Get-Content $computerList) -split "`n" 
    
    #Copy the files to each computer
    foreach($computer in $arrayOfComputers)
    {
        $OutputLocation = "\\" + $computer + "\C$\<LOCATION>"

        #path from the first paramater
        robocopy $path $OutputLocation /e /s
        Write-Host "Transfer completed for $computer"

        #patch the computer
        psexec $path -s cmd.exe /c "echo . | powershell.exe -executionpolicy bypass -file c:\<LOCATION>\CMDMSUInstall.ps1"
        Write-Host "Patching completed for $computer"
    }
}

Here is the second script:

cd C:\<LOCATION>
$path = "C:\<LOCATION>"
$files = Get-ChildItem $path -Recurse
$msus = $files | ? {$_.extension -eq ".msu"}

foreach($msu in $msus)
{
    $fullname = $msu.FullName
    $fullname = "`"" + $fullname + "`""
    $parameters = $fullname + " /quiet /norestart"
    $install = [System.Diagnostics.Process]::Start( "wusa",$parameters )
    $install.WaitForExit()
}
#kill itself to ensure it goes back to the next computer object
taskkill /f /im PSEXESVC.exe

Additonally, make sure you modify the location of where the patches will be copied too and where the local script is ran at.