I have encountered a new problem and I don't even know where exactly to start explaining. I will try my best, if something is unclear just ask me please.
I have an Excel workbook with informations (multiple rows) about DNS records - pretty similar to the powershell DNS syntax. e.g:
HostName RecordType TimeStamp TimeToLive RecordData
# A 0 00:05:00 127.0.0.1
I read them as arrays with the following little code - not very fast, but it works!:
#Read Excel
$row = [int]2
do {
if ($Sheet4.Cells.Item($Row,1).Text) {$ZoneName += $Sheet4.Cells.Item($Row,1).Text}
$HostName += $Sheet4.Cells.Item($Row,2).Text
$RecordType += $Sheet4.Cells.Item($Row,3).Text
$TimeStamp += $Sheet4.Cells.Item($Row,4).Text
$TimeToLive += $Sheet4.Cells.Item($Row,5).Text
$RecordData += $Sheet4.Cells.Item($Row,6).Text
$row = $row + [int] 1
} until (!$Sheet4.Cless.Item($row,2))
Now I have 6 arrays all stuck with information in different arrays, but all with the same amount of lines.
And now the tricky (atleast for me!) part:
I would like to stuff those 6 arrays into some special array I do not know, or in some sort of table I do not know how to create.
Why?
Because I want to compare those lines to this code ($Records to be specific):
$ZoneNames = (Get-DnsServerZone -ComputerName $DnsServer).zonename
$ZoneNames | foreach {$Records = (Get-DnsServerResourceRecord -ComputerName $DnsServer -ZoneName $_)}
$Records[0] would show me this (e.g.):
HostName RecordType Timestamp TimeToLive RecordData
-------- ---------- --------- ---------- ----------
# A 0 00:05:00 127.0.0.1
BUT: If I go deeper: $Records[0].RecordData:
IPv4Address PSComputerName
----------- --------------
127.0.0.1
So I would need to recreate this (above) sort of hierarchy to compare them (If I am right?).
I have tried it with a table like this (didn't work):
#Create Table object
$table = New-Object system.Data.DataTable “$ExcelRecords”
#Define Columns
$col2 = New-Object system.Data.DataColumn HostName,([string])
$col3 = New-Object system.Data.DataColumn RecordType,([string])
$col4 = New-Object system.Data.DataColumn TimeStamp,([string])
$col5 = New-Object system.Data.DataColumn TimeToLive,([string])
$col6 = New-Object system.Data.DataColumn RecordData,([string])
#Add the Columns
$table.columns.add($col2)
$table.columns.add($col3)
$table.columns.add($col4)
$table.columns.add($col5)
$table.columns.add($col6)
#Create a row
$r = $table.NewRow()
#Enter data in the row
$r.HostName = $HostName[$counter]
$r.RecordType = $RecordType[$counter]
$r.TimeStamp = $TimeStamp[$counter]
$r.TimeToLive = $TimeToLive[$counter]
$r.RecordData = $RecordData[$counter]
$RecordData
#Add the row to the table
$table.Rows.Add($r)
Tried comparing like this (didn't work):
if ($records[0] -like $table[0]) {write-host "works"}
This did work:
if ($records[0].hostname -like $table[0].hostname) {write-host "works"}
works
This did not (I guess this is the root of my problems):
if ($Records[0].RecordData -like $table[0].RecordData) {write-host "works"}
My main objective:
Check if there are Records on the DNS-Server, which aren't stated in the Excel sheet and delete them from the DNS-Server!
If you read through all the text, thanks for doing that! Appreciate every help.
Thanks in advance!
I'd start by creating an array of PS objects from your spreadsheet data.
#Read Excel
$row = [int]2
$DNS_Records =
do {
if ($Sheet4.Cells.Item($Row,1).Text) {
New-Object PSObject -Property #{
ZoneName = $Sheet4.Cells.Item($Row,1).Text
HostName = $Sheet4.Cells.Item($Row,2).Text
RecordType = $Sheet4.Cells.Item($Row,3).Text
TimeStamp = $Sheet4.Cells.Item($Row,4).Text
TimeToLive = $Sheet4.Cells.Item($Row,5).Text
RecordData = $Sheet4.Cells.Item($Row,6).Text
}
}
} until (!$Sheet4.Cless.Item($row,2))
Related
I try to bulk load data from Oracle to SqlServer through Powershell Sqlserver Module Sqlbulkcopy
On small Data, everything works fine, but on big Datasets, even if bachsize and streaming are set, sqlbulkcopy is taking all the memory available... until an out of memory
Also the notify function seems to give no answer, so I guess even with streaming=True, the process first load everything to memory...
What did I missed ?
$current = Get-Date
#copy table from Oracle table to SQL Server table
add-type -path "D:\oracle\product\12.1.0\client_1\odp.net\managed\common\Oracle.ManagedDataAccess.dll";
#define oracle connectin string
$conn_str = "cstr"
# query for oracle table
$qry = "
SELECT
ID,CREATEDT,MODIFIEDDT
FROM MYTABLE
WHERE source.ISSYNTHETIC=0 AND source.VALIDFROM >= TO_Date('2019-01-01','yyyy-mm-dd')
";
# key (on the left side) is the source column while value (on the right side) is the target column
[hashtable] $mapping = #{'ID'='ID';'CREATEDT'='CREATEDT';'MODIFIEDDT'};
$adapter = new-object Oracle.ManagedDataAccess.Client.OracleDataAdapter($qry, $conn_str);
#$info = new-object Oracle.ManagedDataAccess.Client;
#Write-Host ( $info | Format-Table | Out-String)
$dtbl = new-object System.Data.DataTable('MYTABLE');
#this Fill method will populate the $dtbl with the query $qry result
$adapter.Fill($dtbl);
#define sql server target instance
$sqlconn = "cstr";
$sqlbc = new-object system.data.sqlclient.Sqlbulkcopy($sqlconn)
$sqlbc.BatchSize = 1000;
$sqlbc.EnableStreaming = $true;
$sqlbc.NotifyAfter = 1000;
$sqlbc.DestinationTableName="DWHODS.MYTABLE";
#need to tell $sqlbc the column mapping info
foreach ($k in $mapping.keys)
{
$colMapping = new-object System.Data.SqlClient.SqlBulkCopyColumnMapping($k, $mapping[$k]);
$sqlbc.ColumnMappings.Add($colMapping) | out-null
}
$sqlbc.WriteToServer($dtbl);
$sqlbc.close;
$end= Get-Date
$diff= New-TimeSpan -Start $current -End $end
Write-Output "import needed : $diff"
Thanks to Jeroen, I changed the code like this, now its no more consuming memory :
$oraConn = New-Object Oracle.ManagedDataAccess.Client.OracleConnection($conn_str);
$oraConn.Open();
$command = $oraConn.CreateCommand();
$command.CommandText=$qry;
$reader = $command.ExecuteReader()
...
$sqlbc.WriteToServer($reader);
I'm trying to fill an Excel sheet using powershell :
0. Declaring Excel object
$Excel = New-Object -ComObject Excel.Application
$Excel.DisplayAlerts = $false
$Excel.ScreenUpdating = $false
$Excel.DisplayStatusBar = $false
$Excel.EnableEvents = $false
$Excel.Visible = $False
1. Reading data from a database table :
$dt1 = New-Object System.Data.Dataset
2. Getting the table :
$dt_table1 = $dt1.Tables[0]
3. Filling Excel file :
for ([Int]$m = 0; $m -lt $dt_table1.Rows.Count; $m++)
{
for ([Int]$r = 0; $r -lt $dt_table1.Columns.Count; $r++)
{
$incolumn = $r + 1;
$inrow = $inheaderlenght + 2 + $m;
if($incolumn -gt 2)
{
$Workbook.ActiveSheet.Cells.Item($inrow, $incolumn) = [System.Convert]::ToDecimal($dt_table1.Rows[$m].ItemArray[$r])
}
else
{
$Workbook.ActiveSheet.Cells.Item($inrow, $incolumn) = $dt_table1.Rows[$m].ItemArray[$r].ToString()
}
}
}
With a few hundreds of rows the sheet is filling in seconds, the problem is when i got thousands of rows, is very slow, for example to fill 21.500 rows it need 15 min at least.
I'm executing this code in my production server, with 32GB of RAM and an Intel Xeon processor.
I would like to improve the performance, i need to fill an Excel file with 32 sheets and only few sheets have thousands of rows.
UPDATE: I wanted to fill directly an array into the Excel sheet :
$excelArray = New-Object 'object[,]' $dt_table1.Rows.Count, $dt_table1.Columns.Count
$excelArray = ForEach($Row in $dt1.Tables[0].Rows){
$Record = New-Object PSObject
ForEach($Col in $dt1.Tables[0].Columns.ColumnName){
Add-Member -InputObject $Record -NotePropertyName $Col -NotePropertyValue $Row.$Col
}
$Record
}
But now, the next line fails:
$range = $WorkSheet.Range('A1', ([char](64 + $dt_table1.Columns.Count)).ToString() + ($dt_table1.Rows.Count).ToString() )
$range.Value2 = $excelArray
Shamelessly using some of f6a4's answer, I think this could work:
# 3. Filling Excel file :
# convert to array of objects
$tableData = $dt_table1 | Select-Object * -ExcludeProperty ItemArray, Table, RowError, RowState, HasErrors
# select the range in the worksheet
$endRange = '{0}{1}' -f ([char](64 + $dt_table1.Columns.Count)), $dt_table1.Rows.Count
$range = $Workbook.ActiveSheet.Range('A1', $endRange)
# copy the array to excel range
$range.Value2 = $tableData
Ok, now i found solution.
$excelArray | ConvertTo-CSV -NoType -Del "`t" | Select -Skip 1 | Clip
[void]$WorkSheet.columns.Item(1).cells.Item(2).PasteSpecial()
From 15 min to 2 min. This really helps me a lot, i have to produce like 500+ Excel files.
I'm trying to create a custom object based on server names from a text file.
The script I have goes and imports the txt file into a Variable. Then runs a foreach server in the servers variable to create the custom object. I would like to be able to output the object's properties as a table that doesn't include the header info each time.
See script and output below:
$SERVERS = gc c:\servers.txt
foreach ($srv in $SERVERS)
{
$Obj = New-Object PsObject -Property`
#{
Computername = $srv
SecurityGroup = (Get-QADComputer $srv).memberof
RebootDay = ((Get-QADComputer $srv).memberof).split(',').split(' ')[2]
Combined = ((Get-QADComputer $srv).memberof).split(',').split(' ').split('=')[1]
RebootTime = $obj.combined.substring(0,4)
}
echo $obj | ft Computername,RebootDay -autosize
}
This is the output currently:
Computername RebootDay
SERVER007 Sunday
Computername RebootDay
SERVER009 Sunday
Computername RebootDay
SERVER003 Sunday
I'd like it to look more like:
Computername RebootDay
SERVER007 Sunday
SERVER001 Sunday
SERVER009 Sunday
TessellatingHeckler was on the right track really. The issue with his code is that you can't pipe a ForEach($x in $y){} loop to anything (not to be confused with a ForEach-Object loop that you usually see shortened to just ForEach like $Servers | ForEach{<code here>}) You don't want to pipe objects to Format-Table one at a time, you want to pipe a collection of objects to it so that it looks nice. So here's the modified code:
$SERVERS = gc c:\servers.txt
$Results = foreach ($srv in $SERVERS)
{
New-Object PsObject -Property #{
Computername = $srv
SecurityGroup = (Get-QADComputer $srv).memberof
RebootDay = ((Get-QADComputer $srv).memberof).split(',').split(' ')[2]
Combined = ((Get-QADComputer $srv).memberof).split(',').split(' ').split('=')[1]
RebootTime = $obj.combined.substring(0,4)
}
}
$Results | FT ComputerName,RebootDay -auto
That collects the objects in an array, then you pass the whole array to Format-Table
Don't put the "ft" (Format-Table) command inside the loop, put it outside, once, at the end. e.g.
$SERVERS = gc c:\servers.txt
$results = foreach ($srv in $SERVERS)
{
$Obj = New-Object PsObject -Property`
#{
Computername = $srv
SecurityGroup = (Get-QADComputer $srv).memberof
RebootDay = ((Get-QADComputer $srv).memberof).split(',').split(' ')[2]
Combined = ((Get-QADComputer $srv).memberof).split(',').split(' ').split('=')[1]
RebootTime = $obj.combined.substring(0,4)
}
$Obj
}
$results | ft Computername,RebootDay -autosize
Edit: Fixed for foreach pipeline bug.
You could possibly neaten it a bit because you don't need to make a new PSObject for a hashtable, and then put the object into the pipeline; you don't need to repeat the Get-QADComputer commands three times. I'm suspicious that the $obj.combined line isn't doing anything - how can you refer to an object inside the properties of the new-object call, before it gets assigned that name? And the repeated splits could probably be combined because it operates on individual characters, not strings.
gc c:\servers.txt | foreach {
$memberof = (Get-QADComputer $_).memberof
#{
Computername = $_;
SecurityGroup = $memberof;
RebootDay = $memberof.split(', ')[2];
Combined = $memberof.split(', =')[1];
# ?? RebootTime = $obj.combined.substring(0,4)
}
} | ft Computername,RebootDay -autosize
I have some web script that I've adapted to run 7 T-SQL queries and output the results into 1 Excel workbook, one worksheet per query. I've just been asked if I can combine all 7 worksheets into one.
Here's my sample code which does copy a worksheet, however the entire column(s) are selected instead of just the UsedData. Also, the first worksheet's data on the destination worksheet is replaced by the second worksheets data.
Questions: Would it be simpler to get Powershell to output the 7 queries into One Excel Worksheet separated by two blank rows? Or modify the existing Powershell script to create the 7 worksheets then combine them into one?
Code is not pretty! I also have been really lost using $Excel = New-Object -ComObject excel.application followed by $Excel | Get-Member to explore how to get PowerShell to work with Excel. References on MSDN are usually for VB or C languages and I can't translate that into PowerShell.
--Edit, add code that stores 7 Query results in an array and outputs to the console. The data is correct but I'm just unsure how to approach piping that data into a single Excel Worksheet.
$docs = "C:\Temp\SQL\test.xlsx"
If (Test-Path $docs){Remove-Item $docs}
Function First-Query {
param([string[]]$queries)
$xlsObj = New-Object -ComObject Excel.Application
$xlsObj.DisplayAlerts = $false
## - Create new Workbook and Sheet (Visible = 1 / 0 not visible)
$xlsObj.Visible = 0
$xlsWb = $xlsobj.Workbooks.Add(1)
$xlsSh = $xlsWb.Worksheets.Add([System.Reflection.Missing]::Value, $xlsWb.Worksheets.Item($xlsWb.Worksheets.Coun))
$xlsSh.Name = 'Test'
for ($i = 0; $i -lt $queries.Count; $i++){
$query = $queries[$i]
$SQLServer = 'Server'
$Database = 'DataBase'
## - Connect to SQL Server using non-SMO class 'System.Data':
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $Database; Integrated Security = True"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $query
$SqlCmd.Connection = $SqlConnection
## - Extract and build the SQL data object '$DataSetTable':
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd;
$tables = New-Object System.Data.DataSet;
$SqlAdapter.Fill($tables)
$TableArray = #($tables)
$SqlConnection.Close()
$DataSetTable = $TableArray.Tables[0]
}#End For Loop
## - Build the Excel column heading:
[Array] $getColumnNames = $DataSetTable.Columns | Select ColumnName;
## - Build column header:
[Int] $RowHeader = 1;
foreach ($ColH in $getColumnNames){
$xlsSh.Cells.item(1, $RowHeader).font.bold = $true;
$xlsSh.Cells.item(1, $RowHeader) = $ColH.ColumnName;
$RowHeader++;
}
## - Adding the data start in row 2 column 1:
[Int] $rowData = 2;
[Int] $colData = 1;
foreach ($rec in $DataSetTable.Rows){
foreach ($Coln in $getColumnNames){
## - Next line convert cell to be text only:
$xlsSh.Cells.NumberFormat = "#";
## - Populating columns:
$xlsSh.Cells.Item($rowData, $colData) = `
$rec.$($Coln.ColumnName).ToString()
$ColData++
}
$rowData++; $ColData = 1
}
## - Adjusting columns in the Excel sheet:
$xlsRng = $xlsSH.usedRange
$xlsRng.EntireColumn.AutoFit() | Out-Null
#End for loop.
#Delete unwanted Sheet1.
$xlsWb.Sheets.Item('Sheet1').Delete()
#Set Monday to Active Sheet upon opening Workbook.
$xlsWb.Sheets.Item('Monday').Activate()
## ---------- Saving file and Terminating Excel Application ---------- ##
$xlsFile = "C:\Temp\SQL\test.xlsx"
$xlsObj.ActiveWorkbook.SaveAs($xlsFile) | Out-Null
$xlsObj.Quit()
## - End of Script - ##
start-sleep 2
While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsRng)) {'cleanup xlsRng'}
While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsSh)) {'cleanup xlsSh'}
While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsWb)) {'cleanup xlsWb'}
While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsObj)) {'cleanup xlsObj'}
[gc]::collect() | Out-Null
[gc]::WaitForPendingFinalizers() | Out-Null
}#End Function
$queries = #()
$queries += #'
SELECT DISTINCT
'#
First-Query -queries $queries
Not sure I really understand what your problem is but below is a "template" that might help you to do what you want. It shows you how you can create sheets and handle them. You'll have to fill the blanks (see the commented section, where you have to call your query function).
param (
[string] $ExcelFile = (Read-Host "Enter full path for Excel file")
)
try
{
$Error.Clear()
# http://support.microsoft.com/kb/320369
[System.Threading.Thread]::CurrentThread.CurrentCulture = [System.Globalization.CultureInfo] "en-US"
Push-Location
$scriptPath = Split-Path -parent $MyInvocation.MyCommand.Path
Set-Location $scriptPath
$Excel = New-Object -comobject Excel.Application
$Excel.Visible = $True
$WorksheetCount = 7
$Workbook = $Excel.Workbooks.Add()
$Workbook.Title = 'My Workbook'
$weekdays = #("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday")
($WorksheetCount - 1)..0 | %{
$sheet = $Excel.Worksheets.Add()
$sheet.Name = $weekdays[$_]
# $dataTable = Execute-Your-Query-Here-Returning-A-Data-Table
# $x = 0
# $dataTable | %{
# $sheet.cells.item($x, 1) = ...
# $sheet.cells.item($x, 2) = ...
# $x++
# }
}
$excel.ActiveWorkbook.SaveAs("$ExcelFile")
}
catch
{
"$($MyInvocation.InvocationName): $Error"
}
finally
{
Pop-Location
$Excel.Quit()
$Excel = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
}
So I'm trying to create a custom Datagridview to put into a GUI program I've made but I'm having some trouble.
So far I have this:
$form = New-Object System.Windows.Forms.Form
$form.Size = New-Object System.Drawing.Size(900,600)
$dataGridView = New-Object System.Windows.Forms.DataGridView
$dataGridView.Size=New-Object System.Drawing.Size(800,400)
$form.Controls.Add($dataGridView)
$dataGridView.ColumnCount = 4
$dataGridView.ColumnHeadersVisible = $true
$dataGridView.Columns[0].Name = "Process"
$dataGridView.Columns[1].Name = "ID"
$dataGridView.Columns[2].Name = "Description"
$dataGridView.Columns[3].Name = "Memory"
$row1 = get-process -property name | select Name
$rows = #($row1)
foreach ($row in $rows)
{
$dataGridView.Rows.Add($row.name)}
$form.ShowDialog()
My question is this:
How do I go about assigning different columns to differnt properties, so column 'process' would be for the procress name, column 'id' would be for the process id and so on.
so far, all I've managed to do is to assign one column a input range: Process Name.
Please help!
Thanks
Loop on all processes, and add each process properties in the order you have defined the columns:
get-process | foreach{
$dataGridView.Rows.Add($_.Name,$_.ID,$_.Description,$_.WorkingSet)
}
You could also generate the columns dynamically by selecting the properties you want to display, each property becomes a column name, and use the grid's DataSource property and an Array list to add the objects to the grid:
$form = New-Object System.Windows.Forms.Form
$form.Size = New-Object System.Drawing.Size(900,600)
$gps = get-process | select Name,ID,Description,#{n='Memory';e={$_.WorkingSet}}
$list = New-Object System.collections.ArrayList
$list.AddRange($gps)
$dataGridView = New-Object System.Windows.Forms.DataGridView -Property #{
Size=New-Object System.Drawing.Size(800,400)
ColumnHeadersVisible = $true
DataSource = $list
}
$form.Controls.Add($dataGridView)
$form.ShowDialog()
But why no to user Out-GridView from PoSH?:
get-process | select name, process, id, description, workingSet | Out-GridView
Akim - scripters try one-liners with output you can see but do little with, while programmers think about a user interface and putting control of the output in the user's hands, thus System.Windows.Forms.Form is a powershell programmer's best friend.
Shay - I have been doing some programming to use a DataGridView and my experience so far shows me I must take control of defining the DataGridView properties and NOT use .DataSource as shown in one example above. Convenient as it is, you do not then do much with your DataGridView other than show it on the form.
I started by setting .ColCount to the number of columns I wanted. Then named the columns. Thereafter I can tweak each column's properties by numbered location or name. I chose to let the user SORT on selected columns. As of 2013-03-23 I am still working on how to set the backgroundcolor of cells that I want to highlight. Should have that answer soon for those wanting to do the same. One example I found uses the value in the cell, not the location.
$datagridview = New-Object System.Windows.Forms.DataGridView
$datagridview.ColumnCount = 8
$datagridview.Columns[0].Name = "#ID"
$datagridview.Columns[1].Name = "Name"
...[snip]...
$datagridview.Columns[7].Name = "Company"
$datagridview.Columns["Name"].SortMode = "Automatic"
$datagridview.Columns[8].SortMode = "Automatic"
$datagridview.Columns[0].Width = 50
$datagridview.Columns["Description"].Width = 350
...[snip]...
foreach ($_ in $arraylist){[void]$datagridview.Rows.Add($($_.ID), $($_.Name),$($_.Path), $($_.Description), $($_.VM), $($_.WS), $($_.CPU), $($_.Company))}
I tried several ways to fiddle with cell backgroundcolors and only had success with Add_CellPainting. Heed the MSDN warning to NOT set the cellstyle in a specific location unless you truly want that location changed no matter what the user does. In the code below, row 2 column 4 is red no matter how you sort the datagridview. That could be an OOPS or did you really want that. Hmmm.... setting by value does all matching values, so if you have non-unique values then maybe you need additional logic to change only the one you want and repaint if the contents change.
$datagridview.Add_CellPainting(
{
param($Null, $EventArgs)
if ($([String]$EventArgs.Value).ToLower().Contains("ms") -eq $True) { $EventArgs.CellStyle.BackColor = "Blue" ; $EventArgs.CellStyle.ForeColor = "White" }
if ($([String]$EventArgs.Value).ToLower().Contains("windows") -eq $True) { $EventArgs.CellStyle.BackColor = "Yellow" }
if ($([String]$EventArgs.Value).ToLower().Contains("windows powershell") -eq $True) { $EventArgs.CellStyle.BackColor = "Green" }
if (($EventArgs.RowIndex -eq 2) -and ($EventArgs.ColumnIndex -eq 4)) {$EventArgs.CellStyle.BackColor = "Red" }
}
) # End of Add_CellPainting
Have since found another way to highlight the cell of my choosing:
$Script:PS_Log_Viewer_Form_row = $PS_Log_Viewer_Form_dataGridView1.Rows.Add("$($PS_Log_Viewer_total_records_ctr)", "$($PS_Log_Viewer_Form_line_date_time_sub)","$($PS_Log_Viewer_Form_line_acct)","$($PS_Log_Viewer_Form_line_msg)", "$($PS_Log_Viewer_Form_full_filename)-$($PS_Log_Viewer_file_records)")
$PS_Log_Viewer_Form_dataGridView1.Rows[$PS_Log_Viewer_Form_row].Cells[1].Style.BackColor = "BlanchedAlmond"
And searching all rows and setting (think find all):
for ($i = ($PS_Log_Viewer_Form_dataGridView1.FirstDisplayedScrollingRowIndex - 1) ; $i -gt 0 ; $i-- )
{
if ($PS_Log_Viewer_Form_dataGridView1.Rows[$i].Cells[3].Value.Contains("$($find_form_middle_flp_textbox_1.Text)") )
{
$PS_Log_Viewer_Form_dataGridView1.Rows[$i].Cells[3].Style.BackColor = $find_form_middle_flp_color_combobox_dropdownlist.SelectedItem
$PS_Log_Viewer_Form_dataGridView1.FirstDisplayedScrollingRowIndex = $i
$find_form_bottom_remarks = "Previous found at $($i)."
$i = 0
} # End of if ($PS_Log_Viewer_Form_dataGridView1.Rows[$i].Cells[3].Value.Contains("$($Script:PS_Log_Viewer_search_string)") )
} # End of for ($i = 0 ; $i -lt $PS_Log_Viewer_Form_dataGridView1.RowCount ; $i++ )