Home
POV-Ray
PowerShell pipeline
PowerShell scripts
Out-Excel
code
Start-Monitor
Get-LogEvents
GUI_ServerInfo
RegistryFunctions
Get-ChildItemToDepth
Get-SharesAndPerm...
PoSh punctuation
Contact
Sitemap
Links

________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________

 function Out-Excel{
<#
.SYNOPSIS
Writes data to Excel-sheet.

.DESCRIPTION
Writes data to Excel-sheet, using appropriate column headers.

.PARAMETER ExcelFile
Full path to resulting Excel-file
File-extension determines Excel-version.
Defaults to C:\Temp\Results.xls (which is Excel97-2003)

.PARAMETER SheetName
Specifies the sheet name.
Defaults to Results

.PARAMETER property
Specifies the properties to report.
Defaults to all properties.

.PARAMETER Culture
Specifies the culture to use.
Defaults to [System.Threading.Thread]::CurrentThread.CurrentUICulture.Name

Normally, if the client computer runs the English version of Excel and the locale for the current user is configured for a language other than English, Excel will try to locate the language pack for the configured language.
If the language pack is not found, an error is reported.
This function circumvents this issue () by calling [System.__ComObject].InvokeMember-method with a culture-object.  

.PARAMETER raw
Switch to return raw objects

.PARAMETER NoForce
Switch to prevent overwrite of existing sheet.
Default is to overwrite existing sheet.

.NOTES
Written by FurBall - FurBall Productions

.EXAMPLE
$LogFolder = "C:\Temp"
$OutputFile = Join-Path $LogFolder "ProcessesWith500PlusHandles.xls"
Get-Process | where {$_.handles -gt 500} | Out-Excel -property name, handles -ExcelFile $OutputFile

.EXAMPLE
Get-Service | select -last 3 | Out-Excel -ExcelFile "C:\Temp\Services.xls" -SheetName "Last3" -NoForce

.EXAMPLE
Get-Service | where {$_.NAme -match "sp"}| Out-Excel -Culture "nl-NL"

.LINK
http://support.microsoft.com/kb/320369/en-us

#>
param (
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$false)][string]$ExcelFile = "C:\Temp\Results.xls",
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$false)][string]$SheetName = "Results",
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$false)][string[]]$Property,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$false)][string]$Culture = [System.Threading.Thread]::CurrentThread.CurrentUICulture.Name,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$false)][switch]$Raw,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$false)][switch]$NoForce,
    [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true, Mandatory=$false)]$InputObject
)

    begin {
        $bContinue = $true
        # start Excel
        $Excel = New-object -com Excel.Application
        if ($Excel -eq $null){
                Write-Warning "no Excel available"
                $bContinue = $false
                return
        }
        # Turn off screen updating so script runs faster
        Set-ExcelProperty $Excel Visible $false -Culture $Culture
        Set-ExcelProperty $Excel ScreenUpdating $false -Culture $Culture
        Set-ExcelProperty $Excel DisplayAlerts  $false -Culture $Culture

        if (!(Test-Path $ExcelFile)) {
            # open a new workbook
            $Workbook = Invoke-ExcelMethod $Excel.Workbooks Add -Culture $Culture
            # get the 1st sheet
            $Sheet = $Workbook.Worksheets.Item(1)
            # rename sheet
            Set-ExcelProperty $Sheet name $SheetName -Culture $Culture
            # activate the sheet
            Invoke-ExcelMethod $Sheet Activate -Culture $Culture
        }
        else{
            if (ExcelIsInUse $ExcelFile){
                Write-Warning "$ExcelFile is in use"
                $bContinue = $false
                return
            }
            else{
                if (!(SheetExists $ExcelFile $SheetName)){
                    # open existing Excelfile
                    $workbook = Invoke-ExcelMethod $excel.workbooks open $excelfile -Culture $Culture
                    # add a sheet
                    $Sheet = Invoke-ExcelMethod $Workbook "worksheets.add" -Culture $Culture
                    # rename sheet
                    Set-ExcelProperty $Sheet name $SheetName -Culture $Culture
                    # activate the sheet
                    Invoke-ExcelMethod $Sheet Activate -Culture $Culture
                }
                else{
                    if ($NoForce){
                        Write-Warning "$ExcelFile already exists and contains a sheet named $sheetname"
                        $bContinue = $false
                        return
                    }
                    else{
                        # open existing Excelfile
                        $workbook = Invoke-ExcelMethod $Excel "Workbooks.Open" $ExcelFile -Culture $Culture
                        # get sheets
                        $worksheets = Get-ExcelProperty $workbook "worksheets" -Culture $Culture
                        # delete specific worksheet
                        for ($i = 0 ; $i -lt $worksheets.Count ; $i ++){
                            if ((Get-ExcelProperty $worksheets[$i] Name -Culture $Culture) -like $SheetName){
                                Invoke-ExcelMethod $worksheets[$i] Delete -Culture $Culture
                            }
                        }
                        # add a sheet
                        $Sheet = Invoke-ExcelMethod $Workbook "worksheets.add" -Culture $Culture
                        # rename sheet
                        Set-ExcelProperty $Sheet name $SheetName -Culture $Culture
                        # activate the sheet
                        Invoke-ExcelMethod $Sheet Activate -Culture $Culture
                    }
                }
            }
        }
        # initialize row counter and empty hashtable to hold column headers
        $row = 1
        $headers = @{}
    }
    
    process {
        if (!$bContinue){
            return
        }
        else{
            if ($_ -eq $null) {return}
            # when we receive the object, we build the header table
            if (-not $property) {
                # if no list of properties was provided, we build one from the object’s properties
                $property=@()
                if ($raw) {
                    $_.properties.PropertyNames | ForEach-Object {$property += @($_)}
                }
                else {
                    $_.PsObject.get_properties() | ForEach-Object {$property += @($_.Name.ToString())}
                }
            }
            if ($row -eq 1) {
                $Column = 1
                foreach ($header in $property) {
                    $headers[$header] = $Column
                    $Sheet.Cells.Item($row,$Column).FormulaLocal = $header
                    $Column ++
                }
                # set some formatting values for the headers
                Set-ExcelProperty $Sheet "UsedRange.Interior.ColorIndex" "19" -Culture $Culture
                Set-ExcelProperty $Sheet "UsedRange.Font.ColorIndex" "11" -Culture $Culture
                Set-ExcelProperty $Sheet "UsedRange.Font.Bold" $true -Culture $Culture
                Set-ExcelProperty $Sheet "UsedRange.HorizontalAlignment" "7" -Culture $Culture
            }
            $row ++
            foreach ($header in $property) {
                # NOTE: next line of code is not the evaluation of a comparison, but the evaluation of an assignment!!
                if ($thisColumn = $headers[$header]) {
                    if ($raw) {
                        $Sheet.Cells.Item($row,$thisColumn).FormulaLocal = [string]$_.properties.$header
                    }
                    else {
                        $Sheet.Cells.Item($row,$thisColumn).FormulaLocal = [string]$_.$header
                    }
                }
            }
        }
    }
    
    end {
        if ($bContinue){
            if ($row -gt 1) { Invoke-ExcelMethod $Sheet "UsedRange.EntireColumn.AutoFit" -Culture $Culture }
            
            # save as Excel 97-2003 (works with Excel 2010, but not with 2003)
            if (($ExcelFile.endswith(".xls")) -and ($Excel.version -ge "14.0")){
                $arguments = $ExcelFile, "56"
                Invoke-ExcelMethod $Workbook SaveAs $arguments -Culture $Culture
            }
            else {
                Invoke-ExcelMethod $Workbook SaveAs $ExcelFile -Culture $Culture
            }
            Invoke-ExcelMethod $Workbook Close -Culture $Culture
        }
        $Excel.Quit()
        $PreviousErrorActionPreference = $ErrorActionPreference
        $ErrorActionPreference = "SilentlyContinue"
        Remove-Variable worksheets
        Remove-Variable sheet
        Remove-Variable workbook
        $ErrorActionPreference = $PreviousErrorActionPreference
        #get rid of the remaining Com-object, left behind despite .Quit()
        Remove-ComObject $Excel
        Remove-Variable Excel
    }
}

function Remove-ComObject{
<#
.SYNOPSIS
Releases COM-object

.DESCRIPTION
Releases specified COM-object
Using .Quit() on an Excel.Application-object does not get rid of the process.
Use this function to get rid of that process.

.PARAMETER Object
Excel-object  

.NOTES

.EXAMPLE
Remove-ComObject $Excel

.LINK

#>
param(
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$true)][System.__ComObject]$ComObject
)
    Write-Verbose  "Remove-ComObject $($ComObject.Name)"
    $hide = [System.Runtime.InteropServices.Marshal]::ReleaseComObject($ComObject)
    $hide = [System.GC]::Collect()
    $hide = [System.GC]::WaitForPendingFinalizers()
}

function ExcelIsInUse( [string] $ExcelFile ){
<#
.SYNOPSIS
Checks if Excel is in use.

.DESCRIPTION
Returns True if specified Excel-file is in use.

.PARAMETER Object
Excel-object  

.NOTES
Written by FurBall - FurBall Productions
Requires function Remove-ComObject

.EXAMPLE
$MyFile = "C:\Temp\Results.xls"
if (ExcelIsInUse $MyFile){
    Write-Warning "$MyFile is in use"
    return
}


.LINK

#>
    $objXLS = New-Object -comobject Excel.Application
    # Turn off screen updating so script runs faster
    $hide = Set-ExcelProperty $objXLS Visible $false
    $hide = Set-ExcelProperty $objXLS DisplayAlerts  $false
    $hide = Invoke-ExcelMethod $objXLS "Workbooks.Open" $ExcelFile
    $bReadOnly = Get-ExcelProperty $objXLS "ActiveWorkbook.ReadOnly"
    If ($bReadOnly ){
        $returnvalue = $true
    }
    else{
        $returnvalue = $false
    }
    $hide = Invoke-ExcelMethod $objXLS.ActiveWorkbook Close
      $hide = $objXLS.Quit()
    #get rid of the remaining Com-object, left behind despite .Quit()
    $hide = Remove-ComObject $objXLS
    $hide = Remove-Variable objXLS
    return $returnvalue
}

function Get-SheetNames{
Param(
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$true)][string]$ExcelFile
)
    $ConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=$ExcelFile;Extended Properties=Excel 8.0;"
    $Conn = new-object System.Data.OleDb.OleDbConnection($connString)
    $conn.open()
    $return = $Conn.GetOleDbSchemaTable([System.Data.OleDb.OleDbSchemaGuid]::tables,$null) |select TABLE_NAME | where{($_.TABLE_NAME).endswith('$')} | foreach{
                ($_.TABLE_NAME).tostring()
        }
    $conn.Close()
    return $return
}

function SheetExists{
#pass SheetName without trailing $
Param(
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$true)][string]$ExcelFile,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$true)][string]$SheetName
)
    [bool]$SheetExists = $false
    Get-SheetNames $ExcelFile | foreach {
            if ($_ -like "$SheetName`$"){$SheetExists = $true}
        }
    return $SheetExists
}

function Test-ExcelFile{
<#
.SYNOPSIS
Checks if Excel-file meets requirements

.DESCRIPTION
Checks if Excel-file meets requirements
    - checks if file exists
    - checks that file is not in use
    - option: checks if required sheet exists
    - option: checks if all required columns exist
    

.PARAMETER ExcelFile
Full name of the Excel-file  

.PARAMETER SheetName
Name of the sheet
pass SheetName without trailing $

.PARAMETER RequiredColumns
Name(s) of required column(s)

.NOTES
Written by FurBall - FurBall Productions

.EXAMPLE
$ExcelFile = "D:\Work\TSM\Scripts\InstallTSM\InstallManager\Data\Test.xls"
$sheet = "Servers"
$RequiredColumns = ("Behind Firewall", "Environment", "Domain", "CI Name", "ChangeNr", "Date", "Time", "InstallState", "StateDetails", "ErrorDetails", "Action", "Commentaar", "Backup Control", "Backup Controled By", "Backup Medium", "CheckControls", "CI Accepted", "CI Monitor", "Cluster", "Instance State", "Management Domain", "OS CINAME BASELINE", "Remote Control Type", "ROOM LOCATION", "RACK SUB LOCATION")
if (Test-ExcelFile $ExcelFile $sheet $RequiredColumns){
    # do some stuff
}
else {
    Write-Warning "failed the test"
}

.LINK

#>
Param(
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$true)][string]$ExcelFile,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$false)][string]$SheetName = "",
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$false)][string[]]$RequiredColumns = @()
)
    # ADO-Constants for Excel
    $adoLockOptimistic = 3
    $adoOpenStatic = 3          
    $adoCmdText = 1
    $returnvalue = $false
    # check if file exists
    if (!(Test-Path $ExcelFile)){Write-Warning "missing file: $ExcelFile"
    }
    else{
        # check if file is in use
        if (ExcelIsInUse $ExcelFile){Write-Warning "file in use: $ExcelFile"
        }
        else{
            if ($SheetName -eq ""){
                # no check for sheet is needed and previous checks were ok, so we return True
                $returnvalue = $true
            }
            else{
                # check if required sheet exists
                if (!(SheetExists $ExcelFile $SheetName)){Write-Warning  "missing sheet $SheetName in $ExcelFile"
                }
                else{
                    #calling SheetExists does not leave an open connection, so we open it here
                    $adoConnection = New-Object -comobject ADODB.Connection
                    $ConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=$ExcelFile;Extended Properties=Excel 8.0;"
                    $hide = $adoConnection.Open($ConnString)
                    if (!$?){
                        Write-Warning  "error connecting to $ExcelFile"
                        break
                    }
                    else{
                        #check if all required columns exist
                        $Sql = "Select * FROM [$SheetName`$] WHERE 1=0"
                        $adoRecordset = New-Object -comobject ADODB.Recordset
                        $hide = $adoRecordset.Open($Sql, $adoConnection, $adoOpenStatic, $adoLockOptimistic)
                        $MissingColumns = Get-MissingColumns $adoRecordset $RequiredColumns
                        if ($MissingColumns){
                            Write-Warning  "missing these columns in $ExcelFile"
                            Write-Warning  $MissingColumns
                        }
                        else{
                            $returnvalue = $true
                        }
                        $hide = $adoRecordset.Close()
                        $hide = $adoConnection.Close()
                    }
                }
            }
        }
    }
    return $returnvalue
}

function Get-ColumnNames{
Param(
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$true)]$recordset)
    if ($recordset.fields.count -gt 0){
        For($counter = 0; $counter -lt $recordset.fields.count; $counter++){
            $recordset.Fields.Item($counter).Name
        }
    }
}

function Get-MissingColumns{
Param(
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$true)]$recordset,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$false, Mandatory=$false)][string[]]$RequiredColumns = @()
)
    $MissingColumns = @()
    $ExistingColumns = Get-ColumnNames $recordset
    foreach($ColumNameToLookFor In $RequiredColumns){
        $bColumnFound = $false
        $ExistingColumns | foreach{
            if ($_ -like $ColumNameToLookFor){
                $bColumnFound = $true
            }
        }
        if (!$bColumnFound){$MissingColumns += $ColumNameToLookFor}
    }
    return $MissingColumns
}

function Invoke-ExcelMethod{
<#
.SYNOPSIS
Invokes Excel-method.

.DESCRIPTION
Invokes Excel-method, using a [System.__ComObject].InvokeMember-method with a culture-object.

.PARAMETER Object
Excel-object  

.PARAMETER Method
Method to invoke

.PARAMETER Parameters
Parameters for the method.
Multiple parameters must be passed as array.

.PARAMETER Culture
Specifies the culture to use.
Defaults to [System.Threading.Thread]::CurrentThread.CurrentUICulture.Name

Normally, if the client computer runs the English version of Excel and the locale for the current user is configured for a language other than English, Excel will try to locate the language pack for the configured language.
If the language pack is not found, an error is reported.
This function circumvents this issue () by calling [System.__ComObject].InvokeMember-method with a culture-object.  

.NOTES
Written by FurBall - FurBall Productions


.EXAMPLE
Invoke-ExcelMethod $sheet "UsedRange.EntireColumn.AutoFit" "nl-NL"
or
$params = @{
    Object = $sheet
    Method = "UsedRange.EntireColumn.AutoFit"
}
Invoke-ExcelMethod @params "nl-NL"

.EXAMPLE
# Open existing Excel-file, delete and activate sheets
# instantiate Excel-object
$Excel = New-object -com Excel.Application
# turn off screen updating so script runs faster
Set-ExcelProperty $Excel Visible $false
Set-ExcelProperty $Excel ScreenUpdating $false
# turn off alerts and force deletes/overwrites
Set-ExcelProperty $Excel DisplayAlerts  $false
# open existing Excelfile
$workbook = Invoke-ExcelMethod $excel "workbooks.open" $excelfile
# get sheets
$worksheets = Get-ExcelProperty $workbook "worksheets"
# delete specific worksheet
$SheetToDelete = "OldResults"
for ($i = 0 ; $i -lt $worksheets.Count ; $i ++){
    if ((Get-ExcelProperty $worksheets[$i] Name) -like $SheetToDelete){
        Invoke-ExcelMethod $worksheets[$i] Delete
    }
}
# activate specific worksheet
$WantedSheet = "Results"
for ($i = 0 ; $i -lt $worksheets.Count ; $i ++){
    if ((Get-ExcelProperty $worksheets[$i] Name) -like $WantedSheet){
        Invoke-ExcelMethod $worksheets[$i] Activate
        # and get a reference to it
        $Sheet = $Worksheets[$i]
    }
}
# change value of a cell
$row = 1
$Column = 1
$Sheet.Cells.Item($row,$Column).FormulaLocal = "New ColumnName"
# save and close Excelfile
Invoke-ExcelMethod $Workbook SaveAs $ExcelFile
Invoke-ExcelMethod $Workbook Close
$Excel.Quit()
#get rid of the remaining Com-object, left behind despite .Quit()
Remove-ComObject $Excel

.EXAMPLE
# Open new Excel-file, rename, add and format sheets
# instantiate Excel-object
$Excel = New-object -com Excel.Application
# turn off screen updating so script runs faster
Set-ExcelProperty $Excel Visible $false
Set-ExcelProperty $Excel ScreenUpdating $false
# turn off alerts and force deletes/overwrites
Set-ExcelProperty $Excel DisplayAlerts  $false
# add workbook
$Workbook = Invoke-ExcelMethod $Excel.Workbooks Add
# get sheets
$worksheets = Get-ExcelProperty $workbook "worksheets"
# rename sheet
Set-ExcelProperty $worksheets[0] name "Results"
# activate the worksheet
Invoke-ExcelMethod $worksheets[0] Activate
# and get a reference to it
$Sheet = $Worksheets[0]
# change value of a cell
$row = 1
$Column = 1
$Sheet.Cells.Item($row,$Column).FormulaLocal = "First column"
$Sheet.Cells.Item($row,2).FormulaLocal = "Second column"
# autofit columns
Invoke-ExcelMethod $sheet "UsedRange.EntireColumn.AutoFit"
# set some formatting values for the headers
Set-ExcelProperty $Sheet "UsedRange.Interior.ColorIndex" "19"
Set-ExcelProperty $Sheet "UsedRange.Font.ColorIndex" "11"
Set-ExcelProperty $Sheet "UsedRange.Font.Bold" $true
Set-ExcelProperty $Sheet "UsedRange.HorizontalAlignment" "7"
# add a sheet
$newsheet = Invoke-ExcelMethod $workbook.worksheets Add
Set-ExcelProperty $newsheet name "EmptySheet"
# save and close Excelfile
Invoke-ExcelMethod $Workbook SaveAs $ExcelFile
Invoke-ExcelMethod $Workbook Close
$Excel.Quit()
#get rid of the remaining Com-object, left behind despite .Quit()
Remove-ComObject $Excel

.LINK
http://support.microsoft.com/kb/320369/en-us
http://www.fourcats.nl/Out-Excel.html

#>
Param(
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, Mandatory=$true)][object]$Object,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, Mandatory=$true)][string]$Method,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, Mandatory=$false)]$Parameters,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, Mandatory=$false)][string]$Culture = [System.Threading.Thread]::CurrentThread.CurrentUICulture.Name
    )
    
    Write-Verbose  "Invoke-ExcelMethod $method` $parameters for object $($object.Name) with culture $culture"
    $MethodsToTreatDifferently = @{
        Open = ""
        Save = ""
        SaveAs = ""
        SaveAsXMLData = ""
        SaveCopyAs = ""
        SaveWorkSpace = ""
        MergeWorkbook = ""    
    }
    if ($parameters){
        if (!($parameters -is [array])){
            [array]$parameters = @($parameters)
        }
    }
    $CultureObject = [System.Globalization.CultureInfo]$culture
    $binding = "System.Reflection.BindingFlags" -as [type]
    if (!($MethodsToTreatDifferently.Contains($method)) ){
        $arrProperties = $method.Split(".")
        $method = $arrProperties[-1]
        for ($i = 0 ; $i -lt $arrProperties.Count -1 ; $i ++){
            $object = [System.__ComObject].InvokeMember($arrProperties[$i], $binding::GetProperty, $null, $object, $null, $CultureObject)
        }
    }
    [System.__ComObject].InvokeMember($method, $binding::InvokeMethod, $null, $object, $parameters, $CultureObject)
}

function Get-ExcelProperty{
<#
.SYNOPSIS
Returns property of Excel-object.

.DESCRIPTION
Returns property of Excel-object, using a [System.__ComObject].InvokeMember-method with a culture-object.

.PARAMETER Object
Excel-object  

.PARAMETER Property
Property to get.

.PARAMETER Culture
Specifies the culture to use.
Defaults to [System.Threading.Thread]::CurrentThread.CurrentUICulture.Name

Normally, if the client computer runs the English version of Excel and the locale for the current user is configured for a language other than English, Excel will try to locate the language pack for the configured language.
If the language pack is not found, an error is reported.
This function circumvents this issue () by calling [System.__ComObject].InvokeMember-method with a culture-object.  

.NOTES
Written by FurBall - FurBall Productions


.EXAMPLE
Get-ExcelProperty $sheet "UsedRange.Interior.ColorIndex"

.EXAMPLE
$params = @{
    Object = $sheet
    Property = "Name"
}
Get-ExcelProperty @params "nl-NL"

.EXAMPLE
# Open existing Excel-file, delete and activate sheets
# instantiate Excel-object
$Excel = New-object -com Excel.Application
# turn off screen updating so script runs faster
Set-ExcelProperty $Excel Visible $false
Set-ExcelProperty $Excel ScreenUpdating $false
# turn off alerts and force deletes/overwrites
Set-ExcelProperty $Excel DisplayAlerts  $false
# open existing Excelfile
$workbook = Invoke-ExcelMethod $excel "workbooks.open" $excelfile
# get sheets
$worksheets = Get-ExcelProperty $workbook "worksheets"
# delete specific worksheet
$SheetToDelete = "OldResults"
for ($i = 0 ; $i -lt $worksheets.Count ; $i ++){
    if ((Get-ExcelProperty $worksheets[$i] Name) -like $SheetToDelete){
        Invoke-ExcelMethod $worksheets[$i] Delete
    }
}
# activate specific worksheet
$WantedSheet = "Results"
for ($i = 0 ; $i -lt $worksheets.Count ; $i ++){
    if ((Get-ExcelProperty $worksheets[$i] Name) -like $WantedSheet){
        Invoke-ExcelMethod $worksheets[$i] Activate
        # and get a reference to it
        $Sheet = $Worksheets[$i]
    }
}
# change value of a cell
$row = 1
$Column = 1
$Sheet.Cells.Item($row,$Column).FormulaLocal = "New ColumnName"
# save and close Excelfile
Invoke-ExcelMethod $Workbook SaveAs $ExcelFile
Invoke-ExcelMethod $Workbook Close
$Excel.Quit()
#get rid of the remaining Com-object, left behind despite .Quit()
Remove-ComObject $Excel

.EXAMPLE
# Open new Excel-file, rename, add and format sheets
# instantiate Excel-object
$Excel = New-object -com Excel.Application
# turn off screen updating so script runs faster
Set-ExcelProperty $Excel Visible $false
Set-ExcelProperty $Excel ScreenUpdating $false
# turn off alerts and force deletes/overwrites
Set-ExcelProperty $Excel DisplayAlerts  $false
# add workbook
$Workbook = Invoke-ExcelMethod $Excel.Workbooks Add
# get sheets
$worksheets = Get-ExcelProperty $workbook "worksheets"
# rename sheet
Set-ExcelProperty $worksheets[0] name "Results"
# activate the worksheet
Invoke-ExcelMethod $worksheets[0] Activate
# and get a reference to it
$Sheet = $Worksheets[0]
# change value of a cell
$row = 1
$Column = 1
$Sheet.Cells.Item($row,$Column).FormulaLocal = "First column"
$Sheet.Cells.Item($row,2).FormulaLocal = "Second column"
# autofit columns
Invoke-ExcelMethod $sheet "UsedRange.EntireColumn.AutoFit"
# set some formatting values for the headers
Set-ExcelProperty $Sheet "UsedRange.Interior.ColorIndex" "19"
Set-ExcelProperty $Sheet "UsedRange.Font.ColorIndex" "11"
Set-ExcelProperty $Sheet "UsedRange.Font.Bold" $true
Set-ExcelProperty $Sheet "UsedRange.HorizontalAlignment" "7"
# add a sheet
$newsheet = Invoke-ExcelMethod $workbook.worksheets Add
Set-ExcelProperty $newsheet name "EmptySheet"
# save and close Excelfile
Invoke-ExcelMethod $Workbook SaveAs $ExcelFile
Invoke-ExcelMethod $Workbook Close
$Excel.Quit()
#get rid of the remaining Com-object, left behind despite .Quit()
Remove-ComObject $Excel

.LINK
http://support.microsoft.com/kb/320369/en-us

#>
Param(
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, Mandatory=$true)][object]$object,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, Mandatory=$true)][string]$property,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, Mandatory=$false)][string]$culture = [System.Threading.Thread]::CurrentThread.CurrentUICulture.Name
    )
    Write-Verbose "Get-ExcelProperty $property for object $($object.Name) with culture $culture"
    $CultureObject = [System.Globalization.CultureInfo]$culture
    $binding = "System.Reflection.BindingFlags" -as [type]
    $arrProperties = $property.Split(".")
    $property = $arrProperties[-1]
    for ($i = 0 ; $i -lt $arrProperties.Count -1 ; $i ++){
        $object = [System.__ComObject].InvokeMember($arrProperties[$i], $binding::GetProperty, $null, $object, $null, $CultureObject)
    }
    return [System.__ComObject].InvokeMember($property, $binding::GetProperty, $null, $object, $null, $CultureObject)
}

function Set-ExcelProperty{
<#
.SYNOPSIS
Changes property-value of Excel-object.

.DESCRIPTION
Changes property-value of Excel-object, using a [System.__ComObject].InvokeMember-method with a culture-object.

.PARAMETER Object
Excel-object  

.PARAMETER Property
Property to change.

.PARAMETER Parameters
Parameters for the property.
Multiple parameters must be passed as array.

.PARAMETER Culture
Specifies the culture to use.
Defaults to [System.Threading.Thread]::CurrentThread.CurrentUICulture.Name

Normally, if the client computer runs the English version of Excel and the locale for the current user is configured for a language other than English, Excel will try to locate the language pack for the configured language.
If the language pack is not found, an error is reported.
This function circumvents this issue () by calling [System.__ComObject].InvokeMember-method with a culture-object.  

.NOTES
Written by FurBall - FurBall Productions


.EXAMPLE
Set-ExcelProperty $sheet "UsedRange.Interior.ColorIndex" "19"

.EXAMPLE
$params = @{
    Object = $sheet
    Property = "Name"
    Parameters = "Hoera"
}
Set-ExcelProperty @params "nl-NL"

.EXAMPLE
# Open existing Excel-file, delete and activate sheets
# instantiate Excel-object
$Excel = New-object -com Excel.Application
# turn off screen updating so script runs faster
Set-ExcelProperty $Excel Visible $false
Set-ExcelProperty $Excel ScreenUpdating $false
# turn off alerts and force deletes/overwrites
Set-ExcelProperty $Excel DisplayAlerts  $false
# open existing Excelfile
$workbook = Invoke-ExcelMethod $excel "workbooks.open" $excelfile
# get sheets
$worksheets = Get-ExcelProperty $workbook "worksheets"
# delete specific worksheet
$SheetToDelete = "OldResults"
for ($i = 0 ; $i -lt $worksheets.Count ; $i ++){
    if ((Get-ExcelProperty $worksheets[$i] Name) -like $SheetToDelete){
        Invoke-ExcelMethod $worksheets[$i] Delete
    }
}
# activate specific worksheet
$WantedSheet = "Results"
for ($i = 0 ; $i -lt $worksheets.Count ; $i ++){
    if ((Get-ExcelProperty $worksheets[$i] Name) -like $WantedSheet){
        Invoke-ExcelMethod $worksheets[$i] Activate
        # and get a reference to it
        $Sheet = $Worksheets[$i]
    }
}
# change value of a cell
$row = 1
$Column = 1
$Sheet.Cells.Item($row,$Column).FormulaLocal = "New ColumnName"
# save and close Excelfile
Invoke-ExcelMethod $Workbook SaveAs $ExcelFile
Invoke-ExcelMethod $Workbook Close
$Excel.Quit()
#get rid of the remaining Com-object, left behind despite .Quit()
Remove-ComObject $Excel

.EXAMPLE
# Open new Excel-file, rename, add and format sheets
# instantiate Excel-object
$Excel = New-object -com Excel.Application
# turn off screen updating so script runs faster
Set-ExcelProperty $Excel Visible $false
Set-ExcelProperty $Excel ScreenUpdating $false
# turn off alerts and force deletes/overwrites
Set-ExcelProperty $Excel DisplayAlerts  $false
# add workbook
$Workbook = Invoke-ExcelMethod $Excel.Workbooks Add
# get sheets
$worksheets = Get-ExcelProperty $workbook "worksheets"
# rename sheet
Set-ExcelProperty $worksheets[0] name "Results"
# activate the worksheet
Invoke-ExcelMethod $worksheets[0] Activate
# and get a reference to it
$Sheet = $Worksheets[0]
# change value of a cell
$row = 1
$Column = 1
$Sheet.Cells.Item($row,$Column).FormulaLocal = "First column"
$Sheet.Cells.Item($row,2).FormulaLocal = "Second column"
# autofit columns
Invoke-ExcelMethod $sheet "UsedRange.EntireColumn.AutoFit"
# set some formatting values for the headers
Set-ExcelProperty $Sheet "UsedRange.Interior.ColorIndex" "19"
Set-ExcelProperty $Sheet "UsedRange.Font.ColorIndex" "11"
Set-ExcelProperty $Sheet "UsedRange.Font.Bold" $true
Set-ExcelProperty $Sheet "UsedRange.HorizontalAlignment" "7"
# add a sheet
$newsheet = Invoke-ExcelMethod $workbook.worksheets Add
Set-ExcelProperty $newsheet name "EmptySheet"
# save and close Excelfile
Invoke-ExcelMethod $Workbook SaveAs $ExcelFile
Invoke-ExcelMethod $Workbook Close
$Excel.Quit()
#get rid of the remaining Com-object, left behind despite .Quit()
Remove-ComObject $Excel

.LINK
http://support.microsoft.com/kb/320369/en-us

#>
Param(
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, Mandatory=$true)][object]$object,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, Mandatory=$true)][string]$property,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, Mandatory=$false)]$parameters,
    [Parameter(ValueFromPipeline=$false,ValueFromPipelineByPropertyName=$true, Mandatory=$false)][string]$culture = [System.Threading.Thread]::CurrentThread.CurrentUICulture.Name
    )
    Write-Verbose "Set-ExcelProperty $property` $parameters for object $($object.Name) with culture $culture"
    if ($parameters){
        if (!($parameters -is [array])){
            [array]$parameters = @($parameters)
        }
    }
    $CultureObject = [System.Globalization.CultureInfo]$culture
    $binding = "System.Reflection.BindingFlags" -as [type]
    $arrProperties = $property.Split(".")
    $property = $arrProperties[-1]
    for ($i = 0 ; $i -lt $arrProperties.Count -1 ; $i ++){
        $object = [System.__ComObject].InvokeMember($arrProperties[$i], $binding::GetProperty, $null, $object, $null, $CultureObject)
    }
    return [System.__ComObject].InvokeMember($property, $binding::SetProperty, $null, $object, $parameters, $CultureObject)
}



function Remove-Garbage {
<#
.SYNOPSIS
Removes orphaned processes

.DESCRIPTION
Remove-ComObject releases a specified COM-object, but even this will not always remove the process.
Use Remove-Garbage after the function that called Remove-ComObject to get rid of that process.

.EXAMPLE
Remove-Garbage

.LINK

#>
    [System.GC]::Collect()
}

Set-Alias cg Remove-Garbage

Export-ModuleMember -function Out-Excel, Remove-ComObject, ExcelIsInUse, Test-ExcelFile, Invoke-ExcelMethod, Get-ExcelProperty, Set-ExcelProperty, Remove-Garbage, SheetExists, Get-SheetNames

to Top of Page

FurBall Productions | furball@casema.nl