Open-ExcelPackage.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
Function Open-ExcelPackage  {
<#
.Synopsis
    Returns an ExcelPackage object for the specified XLSX fil.e
.Description
    Import-Excel and Export-Excel open an Excel file, carry out their tasks and close it again.
    Sometimes it is necessary to open a file and do other work on it.
    Open-ExcelPackage allows the file to be opened for these tasks.
    It takes a -KillExcel switch to make sure Excel is not holding the file open;
    a -Password parameter for existing protected files,
    and a -Create switch to set-up a new file if no file already exists.
.Example
    >
    PS> $excel = Open-ExcelPackage -Path "$env:TEMP\test99.xlsx" -Create
    $ws = Add-WorkSheet -ExcelPackage $excel
 
   This will create a new file in the temp folder if it doesn't already exist.
   It then adds a worksheet - because no name is specified it will use the
   default name of "Sheet1"
.Example
     >
    PS> $excel = Open-ExcelPackage -path "$xlPath" -Password $password
    $sheet1 = $excel.Workbook.Worksheets["sheet1"]
    Set-ExcelRange -Range $sheet1.Cells["E1:S1048576"], $sheet1.Cells["V1:V1048576"] -NFormat ([cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern)
    Close-ExcelPackage $excel -Show
 
   This will open the password protected file at $xlPath using the password stored
   in $Password. Sheet1 is selected and formatting applied to two blocks of the sheet;
   then the file is and saved and loaded into Excel.
#>

    [CmdLetBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")]
    [OutputType([OfficeOpenXml.ExcelPackage])]
    Param (
        #The path to the file to open.
        [Parameter(Mandatory=$true)]$Path,
        #If specified, any running instances of Excel will be terminated before opening the file.
        [switch]$KillExcel,
        #The password for a protected worksheet, as a [normal] string (not a secure string).
        [String]$Password,
        #By default Open-ExcelPackage will only opens an existing file; -Create instructs it to create a new file if required.
        [switch]$Create
    )

    if($KillExcel)         {
        Get-Process -Name "excel" -ErrorAction Ignore | Stop-Process
        while (Get-Process -Name "excel" -ErrorAction Ignore) {}
    }

    $Path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path)
    #If -Create was not specified only open the file if it exists already (send a warning if it doesn't exist).
    if ($Create -and -not (Test-Path -Path $path)) {
        #Create the directory if required.
        $targetPath = Split-Path -Parent -Path $Path
        if (!(Test-Path -Path $targetPath)) {
                Write-Debug "Base path $($targetPath) does not exist, creating"
                $null = New-item -ItemType Directory -Path $targetPath -ErrorAction Ignore
        }
        New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path
    }
    elseif (Test-Path -Path $path) {
        if ($Password) {$pkgobj = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path , $Password }
        else           {$pkgobj = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path }
        if ($pkgobj) {
            foreach ($w in $pkgobj.Workbook.Worksheets) {
                $sb = [scriptblock]::Create(('$this.workbook.Worksheets["{0}"]' -f $w.name))
                Add-Member -InputObject $pkgobj -MemberType ScriptProperty -Name $w.name -Value $sb
            }
            return $pkgobj
        }
    }
    else   {Write-Warning "Could not find $path" }
 }

Function Close-ExcelPackage {
    <#
      .Synopsis
        Closes an Excel Package, saving, saving under a new name or abandoning changes and opening the file in Excel as required.
      .Description
        When working with an ExcelPackage object, the Workbook is held in memory and not saved until the .Save() method of the package is called.
        Close-ExcelPackage saves and disposes of the Package object. It can be called with -NoSave to abandon the file without saving, with a new "SaveAs" filename,
        and/or with a password to protect the file. And -Show will open the file in Excel;
        -Calculate will try to update the workbook, although not everything can be recalculated
      .Example
        Close-ExcelPackage -show $excel
        $excel holds a package object, this saves the workbook and loads it into Excel.
      .Example
        Close-ExcelPackage -NoSave $excel
        $excel holds a package object, this disposes of it without writing it to disk.
    #>

    [CmdLetBinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")]
    Param (
    #Package to close.
    [parameter(Mandatory=$true, ValueFromPipeline=$true)]
    [OfficeOpenXml.ExcelPackage]$ExcelPackage,
    #Open the file in Excel.
    [switch]$Show,
    #Abandon the file without saving.
    [Switch]$NoSave,
    #Save file with a new name (ignored if -NoSave Specified).
    $SaveAs,
    #Password to protect the file.
    [ValidateNotNullOrEmpty()]
    [String]$Password,
    #Attempt to recalculation the workbook before saving
    [switch]$Calculate
    )
    if ( $NoSave)      {$ExcelPackage.Dispose()}
    else {
          if ($Calculate) {
            try   { [OfficeOpenXml.CalculationExtension]::Calculate($ExcelPackage.Workbook) }
            Catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook."}
          }
          if ($SaveAs) {
              $SaveAs = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($SaveAs)
              if ($Password) {$ExcelPackage.SaveAs( $SaveAs, $Password ) }
              else           {$ExcelPackage.SaveAs( $SaveAs)}
          }
          Else         {
              if ($Password) {$ExcelPackage.Save($Password) }
              else           {$ExcelPackage.Save()          }
              $SaveAs = $ExcelPackage.File.FullName
          }
          $ExcelPackage.Dispose()
          if ($Show)   {Start-Process -FilePath $SaveAs }
    }
}