├── .gitattributes ├── .github ├── dependabot.yml ├── release-drafter.yml └── workflows │ ├── codeql.yml │ ├── main.yml │ └── publish.yml ├── .gitignore ├── Create-Package.ps1 ├── Directory.Build.props ├── GitVersion.yml ├── LICENSE.txt ├── MSRL-LICENSE.txt ├── NuGet ├── content │ └── PowerShellWixExtension.DummyFile.txt └── tools │ ├── Install.ps1 │ ├── Remove.psm1 │ └── Uninstall.ps1 ├── PowerShellActions ├── CustomAction.config ├── CustomAction.cs ├── IExitCode.cs ├── PowerShellActions.csproj ├── PowerShellTask.cs ├── Properties │ └── AssemblyInfo.cs ├── WixHost.cs ├── WixHostRawUserInterface.cs └── WixHostUserInterface.cs ├── PowerShellLibrary ├── Library.wxs └── PowerShellLibrary.wixproj ├── PowerShellWixExtension.nuspec ├── PowerShellWixExtension.sln ├── PowerShellWixExtension.sln.DotSettings ├── PowerShellWixExtension ├── PowerShellCompilerExtension.cs ├── PowerShellWixExtension.cs ├── PowerShellWixExtension.csproj ├── PowerShellWixExtensionSchema.cs ├── PowerShellWixExtensionSchema.xsd ├── Properties │ └── AssemblyInfo.cs └── TableDefinitions.xml ├── Publish-Package.ps1 ├── README.md ├── Settings.StyleCop ├── Tests ├── Pester.Tests.ps1 ├── PowerShellWixInlineScriptTest │ ├── PowerShellWixInlineScriptTest.wixproj │ ├── Product.wxs │ └── TextFile1.txt └── PowerShellWixTest │ ├── PowerShellWixTest.wixproj │ ├── Product.wxs │ ├── ProgressDlg.wxs │ └── Test.ps1 ├── tables.xsd └── version.json /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Basic set up for three package managers 2 | 3 | version: 2 4 | updates: 5 | 6 | # Maintain dependencies for GitHub Actions 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$RESOLVED_VERSION' 2 | tag-template: 'v$RESOLVED_VERSION' 3 | 4 | template: | 5 | ## What’s Changed 6 | 7 | $CHANGES -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | branches: [ "main" ] 19 | schedule: 20 | - cron: '44 23 * * 6' 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze (${{ matrix.language }}) 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners (GitHub.com only) 29 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 30 | runs-on: windows-2019 31 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 32 | permissions: 33 | # required for all workflows 34 | security-events: write 35 | 36 | # required to fetch internal or private CodeQL packs 37 | packages: read 38 | 39 | # only required for workflows in private repositories 40 | actions: read 41 | contents: read 42 | 43 | strategy: 44 | fail-fast: false 45 | matrix: 46 | include: 47 | - language: csharp 48 | build-mode: manual 49 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 50 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 51 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 52 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 53 | steps: 54 | - name: Checkout repository 55 | uses: actions/checkout@v4 56 | 57 | - name: Setup MSBuild.exe 58 | uses: microsoft/setup-msbuild@v2.0.0 59 | 60 | # Initializes the CodeQL tools for scanning. 61 | - name: Initialize CodeQL 62 | uses: github/codeql-action/init@v3 63 | with: 64 | languages: ${{ matrix.language }} 65 | build-mode: ${{ matrix.build-mode }} 66 | # If you wish to specify custom queries, you can do so here or in a config file. 67 | # By default, queries listed here will override any specified in a config file. 68 | # Prefix the list here with "+" to use these queries and those in the config file. 69 | 70 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 71 | # queries: security-extended,security-and-quality 72 | 73 | # If the analyze step fails for one of the languages you are analyzing with 74 | # "We were unable to automatically build your code", modify the matrix above 75 | # to set the build mode to "manual" for that language. Then modify this step 76 | # to build your code. 77 | # ℹ️ Command-line programs to run using the OS shell. 78 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 79 | - if: matrix.build-mode == 'manual' 80 | run: | 81 | nuget restore /p:NoGitVersioning=true 82 | msbuild PowerShellWixExtension.sln /p:NoGitVersioning=true 83 | 84 | - name: Perform CodeQL Analysis 85 | uses: github/codeql-action/analyze@v3 86 | with: 87 | category: "/language:${{matrix.language}}" 88 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | update_release_draft: 11 | name: Update release draft 12 | runs-on: ubuntu-latest 13 | outputs: 14 | Release_Id: ${{ steps.create_release.outputs.id}} 15 | Release_name: ${{ steps.create_release.outputs.name}} 16 | Release_tag_name: ${{ steps.create_release.outputs.tag_name}} 17 | Release_body: ${{ steps.create_release.outputs.body}} 18 | Release_html_url: ${{ steps.create_release.outputs.html_url}} 19 | Release_upload_url: ${{ steps.create_release.outputs.upload_url}} 20 | 21 | permissions: 22 | contents: write 23 | 24 | steps: 25 | # - uses: GitHubSecurityLab/actions-permissions/monitor@v1 26 | # with: 27 | # config: ${{ vars.PERMISSIONS_CONFIG }} 28 | 29 | - name: Checkout 30 | uses: actions/checkout@v4 31 | with: 32 | fetch-depth: 0 33 | 34 | - name: Nerdbank.GitVersioning 35 | uses: dotnet/nbgv@v0.4.2 36 | with: 37 | # Defines ALL version variables as environment variables, with a "NBGV_" prefix. 38 | setAllVars: true 39 | # Defines a few common version variables as environment variables, with a "Git" prefix (e.g. GitBuildVersion, GitBuildVersionSimple, GitAssemblyInformationalVersion). 40 | setCommonVars: true 41 | 42 | - uses: release-drafter/release-drafter@v6 43 | if: github.ref == 'refs/heads/main' # Running this action only for main branch 44 | id: create_release 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | with: 48 | version: ${{ env.NBGV_SemVer2 }} 49 | 50 | build: 51 | needs: [update_release_draft] 52 | 53 | runs-on: windows-2019 54 | 55 | permissions: 56 | checks: write 57 | contents: write 58 | 59 | env: 60 | Configuration: Release 61 | Release_body: ${{ needs.update_release_draft.outputs.Release_body }} 62 | 63 | steps: 64 | - name: Dump needs context 65 | env: 66 | NEEDS_CONTEXT: ${{ toJson(needs) }} 67 | run: echo "$NEEDS_CONTEXT" 68 | 69 | - name: Checkout 70 | uses: actions/checkout@v4 71 | with: 72 | fetch-depth: 0 73 | 74 | - name: Setup MSBuild.exe 75 | uses: microsoft/setup-msbuild@v2.0.0 76 | 77 | - name: NuGet Restore 78 | run: nuget restore 79 | 80 | - name: Build 81 | run: msbuild PowerShellWixExtension.sln 82 | # NBGV is run as part of the build, so actions after here have access to NBGV_ env variables. 83 | 84 | # For some reason, running msiexec from Pester doesn't work quite right. 85 | - name: msiexec 86 | run: | 87 | Start-Process msiexec.exe -Wait -ArgumentList "/i Tests\PowerShellWixInlineScriptTest\bin\Release\PowerShellWixInlineScriptTest.msi /q /liwearucmopvx ${{ github.workspace }}\inlinescript-install.log" 88 | Start-Process msiexec.exe -Wait -ArgumentList "/x Tests\PowerShellWixInlineScriptTest\bin\Release\PowerShellWixInlineScriptTest.msi /q /liwearucmopvx ${{ github.workspace }}\inlinescript-uninstall.log" 89 | Start-Process msiexec.exe -Wait -ArgumentList "/i Tests\PowerShellWixTest\bin\Release\PowerShellWixTest.msi /q /liwearucmopvx ${{ github.workspace }}\script-install.log" 90 | Start-Process msiexec.exe -Wait -ArgumentList "/x Tests\PowerShellWixTest\bin\Release\PowerShellWixTest.msi /q /liwearucmopvx ${{ github.workspace }}\script-uninstall.log" 91 | 92 | - name: Pester 93 | id: test_module 94 | uses: zyborg/pester-tests-report@v1 95 | with: 96 | include_paths: tests 97 | github_token: ${{ secrets.GITHUB_TOKEN }} 98 | tests_fail_step: true 99 | 100 | - uses: actions/upload-artifact@v4 101 | if: ${{ always() }} 102 | with: 103 | name: test logs 104 | path: ${{ github.workspace }}\**\*.log 105 | 106 | - name: Pack 107 | run: nuget pack .\PowerShellWixExtension.nuspec -Version "$env:NBGV_NuGetPackageVersion" -Properties "Configuration=$env:Configuration;releasenotes=$env:Release_body" 108 | 109 | - uses: actions/upload-artifact@v4 110 | with: 111 | name: nupkg 112 | path: ${{ github.workspace }}\PowerShellWixExtension.${{ env.NBGV_NuGetPackageVersion }}.nupkg 113 | 114 | - name: Remove existing release asset 115 | uses: flcdrg/remove-release-asset-action@v4 116 | if: github.ref == 'refs/heads/main' # Running this action only for main branch 117 | with: 118 | # The release id to remove asset from 119 | release_id: ${{ needs.update_release_draft.outputs.Release_Id }} 120 | # The name of the asset you want to remove 121 | asset_name: PowerShellWixExtension.nupkg 122 | env: 123 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 124 | continue-on-error: true 125 | 126 | - name: Upload Release Asset 127 | id: upload-release-asset 128 | uses: actions/upload-release-asset@v1 129 | if: github.ref == 'refs/heads/main' # Running this action only for master branch 130 | env: 131 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 132 | with: 133 | upload_url: ${{ needs.update_release_draft.outputs.Release_upload_url }} 134 | asset_path: ${{ github.workspace }}\PowerShellWixExtension.${{ env.NBGV_NuGetPackageVersion }}.nupkg 135 | asset_name: PowerShellWixExtension.nupkg 136 | asset_content_type: application/octet-stream 137 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: # https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads#release 6 | - published 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | # - uses: GitHubSecurityLab/actions-permissions/monitor@v1 14 | # with: 15 | # config: ${{ vars.PERMISSIONS_CONFIG }} 16 | 17 | - name: Setup .NET 18 | uses: actions/setup-dotnet@v4 19 | with: 20 | dotnet-version: | 21 | 8.0.x 22 | 23 | - name: Download Assets 24 | uses: i3h/download-release-asset@v1.3.3 25 | with: 26 | owner: ${{ github.event.repository.owner.login }} 27 | repo: ${{ github.event.repository.name }} 28 | tag: ${{ github.event.release.tag_name }} 29 | file: '/.*\.nupkg/' 30 | token: ${{ secrets.GITHUB_TOKEN }} 31 | 32 | - run: ls -alR 33 | name: List files 34 | 35 | - name: Publish the package to nuget.org 36 | run: dotnet nuget push "*.nupkg" --source https://api.nuget.org/v3/index.json -k $NUGET_AUTH_TOKEN --skip-duplicate 37 | env: 38 | NUGET_AUTH_TOKEN: ${{ secrets.NUGET_API_KEY }} 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 19 | !packages/*/build/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | 25 | *_i.c 26 | *_p.c 27 | *.ilk 28 | *.meta 29 | *.obj 30 | *.pch 31 | *.pdb 32 | *.pgc 33 | *.pgd 34 | *.rsp 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.vspscc 43 | *.vssscc 44 | .builds 45 | *.pidb 46 | *.log 47 | *.scc 48 | 49 | # Visual C++ cache files 50 | ipch/ 51 | *.aps 52 | *.ncb 53 | *.opensdf 54 | *.sdf 55 | *.cachefile 56 | 57 | # Visual Studio profiler 58 | *.psess 59 | *.vsp 60 | *.vspx 61 | 62 | # Guidance Automation Toolkit 63 | *.gpState 64 | 65 | # ReSharper is a .NET coding add-in 66 | _ReSharper*/ 67 | *.[Rr]e[Ss]harper 68 | 69 | # TeamCity is a build add-in 70 | _TeamCity* 71 | 72 | # DotCover is a Code Coverage Tool 73 | *.dotCover 74 | 75 | # NCrunch 76 | *.ncrunch* 77 | .*crunch*.local.xml 78 | 79 | # Installshield output folder 80 | [Ee]xpress/ 81 | 82 | # DocProject is a documentation generator add-in 83 | DocProject/buildhelp/ 84 | DocProject/Help/*.HxT 85 | DocProject/Help/*.HxC 86 | DocProject/Help/*.hhc 87 | DocProject/Help/*.hhk 88 | DocProject/Help/*.hhp 89 | DocProject/Help/Html2 90 | DocProject/Help/html 91 | 92 | # Click-Once directory 93 | publish/ 94 | 95 | # Publish Web Output 96 | *.Publish.xml 97 | 98 | # NuGet Packages Directory 99 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 100 | #packages/ 101 | 102 | # Windows Azure Build Output 103 | csx 104 | *.build.csdef 105 | 106 | # Windows Store app package directory 107 | AppPackages/ 108 | 109 | # Others 110 | sql/ 111 | *.Cache 112 | ClientBin/ 113 | [Ss]tyle[Cc]op.* 114 | ~$* 115 | *~ 116 | *.dbmdl 117 | *.[Pp]ublish.xml 118 | *.pfx 119 | *.publishsettings 120 | 121 | # RIA/Silverlight projects 122 | Generated_Code/ 123 | 124 | # Backup & report files from converting an old project file to a newer 125 | # Visual Studio version. Backup files are not needed, because we have git ;-) 126 | _UpgradeReport_Files/ 127 | Backup*/ 128 | UpgradeLog*.XML 129 | UpgradeLog*.htm 130 | 131 | # SQL Server files 132 | App_Data/*.mdf 133 | App_Data/*.ldf 134 | 135 | 136 | #LightSwitch generated files 137 | GeneratedArtifacts/ 138 | _Pvt_Extensions/ 139 | ModelManifest.xml 140 | 141 | # ========================= 142 | # Windows detritus 143 | # ========================= 144 | 145 | # Windows image file caches 146 | Thumbs.db 147 | ehthumbs.db 148 | 149 | # Folder config file 150 | Desktop.ini 151 | 152 | # Recycle Bin used on file shares 153 | $RECYCLE.BIN/ 154 | 155 | # Mac desktop service store files 156 | .DS_Store 157 | /Libs 158 | /NuGet/tools/lib 159 | /NuGet/*.nupkg 160 | /.vs/ 161 | /*.nupkg 162 | -------------------------------------------------------------------------------- /Create-Package.ps1: -------------------------------------------------------------------------------- 1 | msbuild /p:Configuration=Release 2 | 3 | Write-Host "Expect 1 warning" 4 | .\nuget.exe pack .\PowerShellWixExtension.nuspec -OutputDirectory .\NuGet -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 3.3.37 6 | all 7 | 8 | 9 | -------------------------------------------------------------------------------- /GitVersion.yml: -------------------------------------------------------------------------------- 1 | assembly-versioning-scheme: MajorMinorPatchTag 2 | mode: ContinuousDelivery 3 | branches: {} 4 | ignore: 5 | sha: [] 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 David Gardiner 2 | 3 | Some files (where indicated) are licensed under the Microsoft Reciprocal License 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. -------------------------------------------------------------------------------- /MSRL-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2004, Outercurve Foundation. 2 | This software is released under the Microsoft Reciprocal License (MS-RL) (the "License"); you may not use the software except in compliance with the License. 3 | 4 | The text of the Microsoft Reciprocal License (MS-RL) can be found online at: 5 | http://opensource.org/licenses/ms-rl 6 | 7 | 8 | Microsoft Reciprocal License (MS-RL) 9 | 10 | This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. 11 | 12 | 1. Definitions 13 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. 14 | A "contribution" is the original software, or any additions or changes to the software. 15 | A "contributor" is any person that distributes its contribution under this license. 16 | "Licensed patents" are a contributor's patent claims that read directly on its contribution. 17 | 18 | 2. Grant of Rights 19 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. 20 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 21 | 22 | 3. Conditions and Limitations 23 | (A) Reciprocal Grants- For any file you distribute that contains code from the software (in source code or binary format), you must provide recipients the source code to that file along with a copy of this license, which license will govern that file. You may license other files that are entirely your own work and do not contain code from the software under any terms you choose. 24 | (B) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. 25 | (C) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. 26 | (D) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. 27 | (E) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. 28 | (F) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 29 | -------------------------------------------------------------------------------- /NuGet/content/PowerShellWixExtension.DummyFile.txt: -------------------------------------------------------------------------------- 1 | This file is used to invoke the PowerShell scripts during NuGet package installation. 2 | It should be removed automatically after the package is installed. If this file is still present in your project, you can safely delete it. -------------------------------------------------------------------------------- /NuGet/tools/Install.ps1: -------------------------------------------------------------------------------- 1 | # Created by Alexander Köplinger, 2013 2 | 3 | param($installPath, $toolsPath, $package, $project) 4 | 5 | # ensure that we are installing into a WiX project 6 | if ($project.Kind -ne '{930c7802-8a8c-48f9-8165-68863bccd9dd}') 7 | { 8 | throw "'$($project.Name)' is not a WiX project! This package will only work on WiX projects." 9 | } 10 | 11 | # remove dummy file from project 12 | $dummy = $project.ProjectItems.Item("PowerShellWixExtension.DummyFile.txt").Delete() 13 | 14 | $msBuildProj = @([Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName))[0] 15 | 16 | # remove previous changes (for cases where Uninstall.ps1 wasn't executed properly) 17 | Import-Module (Join-Path $toolsPath "Remove.psm1") 18 | Remove-Changes $msBuildProj 19 | 20 | # add the property group directly before the WixTargetsPath-Import, according to http://wixtoolset.org/documentation/manual/v3/msbuild/daily_builds.html 21 | $itemGroup = $msBuildProj.Xml.CreateItemGroupElement() 22 | 23 | $wixImport = $msBuildProj.Xml.Children | Where-Object { $_.Project -eq '$(WixTargetsPath)' } 24 | $msBuildProj.Xml.InsertBeforeChild($itemGroup, $wixImport) 25 | 26 | # Calculate relative path to package from project 27 | $projectDir = [System.IO.Path]::GetDirectoryName($project.FullName) 28 | 29 | Push-Location $projectDir 30 | 31 | $hintPath = Resolve-Path ($toolsPath + '\lib\PowerShellWixExtension.dll') -Relative 32 | 33 | Pop-Location 34 | 35 | # 36 | # ..\..\Libs\PowerShellWixExtension\PowerShellWixExtension.dll 37 | # PowerShellWixExtension 38 | # 39 | 40 | $metadata = New-Object 'System.Collections.Generic.Dictionary[String, String]' 41 | $metadata.Add("HintPath", $hintPath) 42 | $metadata.Add("Name", "PowerShellWixExtension") 43 | $itemGroup.AddItem('WixExtension', 'PowerShellWixExtension', $metadata) 44 | 45 | # save changes 46 | $project.Save($null) 47 | $msBuildProj.ReevaluateIfNecessary() -------------------------------------------------------------------------------- /NuGet/tools/Remove.psm1: -------------------------------------------------------------------------------- 1 | # Created by Alexander Köplinger, 2013 2 | 3 | function Remove-Changes 4 | { 5 | param( 6 | [parameter(Position = 0, Mandatory = $true)] 7 | [Microsoft.Build.Evaluation.Project]$msBuildProj 8 | ) 9 | 10 | #TODO: this can probably be improved 11 | $wixToolPathProperties = $msBuildProj.Xml.AllChildren | Where-Object { $_.Include -eq 'PowerShellWixExtension' } 12 | 13 | if ($wixToolPathProperties) 14 | { 15 | foreach($item in $wixToolPathProperties) 16 | { 17 | $itemGroup = $item.Parent 18 | $itemGroup.RemoveChild($item) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /NuGet/tools/Uninstall.ps1: -------------------------------------------------------------------------------- 1 | # Originally created by Alexander Köplinger, 2013 2 | 3 | param($installPath, $toolsPath, $package, $project) 4 | 5 | $msBuildProj = @([Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName))[0] 6 | 7 | # remove changes 8 | Import-Module (Join-Path $toolsPath "Remove.psm1") 9 | Remove-Changes $msBuildProj 10 | 11 | # need to add and remove a dummy item, otherwise saving the project doesn't work. 12 | $project.ProjectItems.AddFolder("PowerShellWixExtension.DummyFolder", $null).Delete() 13 | 14 | # save changes 15 | $project.Save($null) 16 | $msBuildProj.ReevaluateIfNecessary() -------------------------------------------------------------------------------- /PowerShellActions/CustomAction.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /PowerShellActions/CustomAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Xml.Linq; 4 | using System.Xml.Serialization; 5 | using Microsoft.Deployment.WindowsInstaller; 6 | 7 | using View = Microsoft.Deployment.WindowsInstaller.View; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | 11 | namespace PowerShellActions 12 | { 13 | public class CustomActions 14 | { 15 | public const uint TickIncrement = 10000; 16 | 17 | // Specify or calculate the total number of ticks the custom action adds to the length of the ProgressBar 18 | public const uint TotalTicks = TickIncrement * NumberItems; 19 | private const uint NumberItems = 100; 20 | 21 | private const string PowerShellFilesElevatedDeferredProperty = "PowerShellFilesElevatedDeferred"; 22 | private const string PowerShellFilesDeferredProperty = "PowerShellFilesDeferred"; 23 | private const string PowerShellScriptsElevatedDeferredProperty = "PowerShellScriptsElevatedDeferred"; 24 | private const string PowerShellScriptsDeferredProperty = "PowerShellScriptsDeferred"; 25 | 26 | private const string PowerShellFilesElevatedUninstallDeferredProperty = "PowerShellFilesElevatedUninstallDeferred"; 27 | private const string PowerShellFilesUninstallDeferredProperty = "PowerShellFilesUninstallDeferred"; 28 | private const string PowerShellScriptsElevatedUninstallDeferredProperty = "PowerShellScriptsElevatedUninstallDeferred"; 29 | private const string PowerShellScriptsUninstallDeferredProperty = "PowerShellScriptsUninstallDeferred"; 30 | 31 | [CustomAction] 32 | public static ActionResult PowerShellFilesImmediate(Session session) 33 | { 34 | session.Log("PowerShellFilesImmediate start"); 35 | 36 | return FilesImmediate(session, 0, PowerShellFilesDeferredProperty); 37 | } 38 | 39 | [CustomAction] 40 | public static ActionResult PowerShellFilesDeferred(Session session) 41 | { 42 | session.Log("PowerShellFilesDeferred start"); 43 | 44 | return FilesDeferred(session, PowerShellFilesDeferredProperty); 45 | } 46 | 47 | [CustomAction] 48 | public static ActionResult PowerShellFilesElevatedImmediate(Session session) 49 | { 50 | session.Log("PowerShellFilesElevatedImmediate start"); 51 | 52 | return FilesImmediate(session, 1, PowerShellFilesElevatedDeferredProperty); 53 | } 54 | 55 | [CustomAction] 56 | public static ActionResult PowerShellFilesElevatedDeferred(Session session) 57 | { 58 | session.Log("PowerShellFilesElevatedDeferred start"); 59 | 60 | return FilesDeferred(session, PowerShellFilesElevatedDeferredProperty); 61 | } 62 | 63 | [CustomAction] 64 | public static ActionResult PowerShellFilesUninstall(Session session) 65 | { 66 | session.Log("PowerShellFilesUninstall start"); 67 | 68 | return FilesImmediate(session, 0, PowerShellFilesUninstallDeferredProperty); 69 | } 70 | 71 | 72 | [CustomAction] 73 | public static ActionResult PowerShellFilesUninstallDeferred(Session session) 74 | { 75 | session.Log("PowerShellFilesUninstallDeferred start"); 76 | 77 | return FilesDeferred(session, PowerShellFilesUninstallDeferredProperty); 78 | } 79 | 80 | 81 | [CustomAction] 82 | public static ActionResult PowerShellFilesElevatedUninstall(Session session) 83 | { 84 | session.Log("PowerShellFilesElevatedUninstall start"); 85 | 86 | return FilesImmediate(session, 1, PowerShellFilesElevatedUninstallDeferredProperty); 87 | } 88 | 89 | [CustomAction] 90 | public static ActionResult PowerShellFilesElevatedUninstallDeferred(Session session) 91 | { 92 | session.Log("PowerShellFilesElevatedUninstallDeferred start"); 93 | 94 | return FilesDeferred(session, PowerShellFilesElevatedUninstallDeferredProperty); 95 | } 96 | 97 | [CustomAction] 98 | public static ActionResult PowerShellScriptsImmediate(Session session) 99 | { 100 | session.Log("PowerShellScriptsImmediate start"); 101 | 102 | return ScriptsImmediate(session, 0, PowerShellScriptsDeferredProperty); 103 | } 104 | 105 | [CustomAction] 106 | public static ActionResult PowerShellScriptsDeferred(Session session) 107 | { 108 | session.Log("PowerShellScriptsDeferred start"); 109 | 110 | return ScriptsDeferred(session, PowerShellScriptsDeferredProperty); 111 | } 112 | 113 | [CustomAction] 114 | public static ActionResult PowerShellScriptsElevatedImmediate(Session session) 115 | { 116 | session.Log("PowerShellScriptsElevatedImmediate start"); 117 | 118 | return ScriptsImmediate(session, 1, PowerShellScriptsElevatedDeferredProperty); 119 | } 120 | 121 | [CustomAction] 122 | public static ActionResult PowerShellScriptsElevatedDeferred(Session session) 123 | { 124 | session.Log("PowerShellScriptsElevatedDeferred start"); 125 | 126 | return ScriptsDeferred(session, PowerShellScriptsElevatedDeferredProperty); 127 | } 128 | 129 | [CustomAction] 130 | public static ActionResult PowerShellScriptsUninstall(Session session) 131 | { 132 | session.Log("PowerShellScriptsUninstall start"); 133 | 134 | return ScriptsImmediate(session, 0, PowerShellScriptsUninstallDeferredProperty); 135 | } 136 | 137 | [CustomAction] 138 | public static ActionResult PowerShellScriptsUninstallDeferred(Session session) 139 | { 140 | session.Log("PowerShellScriptsUninstallDeferred start"); 141 | 142 | return ScriptsDeferred(session, PowerShellScriptsUninstallDeferredProperty); 143 | } 144 | 145 | [CustomAction] 146 | public static ActionResult PowerShellScriptsElevatedUninstall(Session session) 147 | { 148 | session.Log("PowerShellScriptsElevatedUninstall start"); 149 | 150 | return ScriptsImmediate(session, 1, PowerShellScriptsElevatedUninstallDeferredProperty); 151 | } 152 | 153 | [CustomAction] 154 | public static ActionResult PowerShellScriptsElevatedUninstallDeferred(Session session) 155 | { 156 | session.Log("PowerShellScriptsElevatedUninstallDeferred start"); 157 | 158 | return ScriptsDeferred(session, PowerShellScriptsElevatedUninstallDeferredProperty); 159 | } 160 | 161 | [Serializable] 162 | public class ScriptActionData 163 | { 164 | public string Id { get; set; } 165 | public string Script { get; set; } 166 | public bool IgnoreErrors { get; set; } 167 | } 168 | 169 | private static ActionResult ScriptsImmediate(Session session, int elevated, string deferredProperty) 170 | { 171 | Database db = session.Database; 172 | 173 | if (!db.Tables.Contains("PowerShellScripts")) 174 | return ActionResult.Success; 175 | 176 | try 177 | { 178 | List scripts = new List(); 179 | using (View view = db.OpenView(string.Format("SELECT `Id`, `Script`, `IgnoreErrors`, `Condition` FROM `PowerShellScripts` WHERE `Elevated` = {0} ORDER BY `Order`", elevated))) 180 | { 181 | view.Execute(); 182 | 183 | Record row; 184 | while ((row = view.Fetch()) != null) 185 | { 186 | string condition = row["Condition"]?.ToString(); 187 | if (!string.IsNullOrEmpty(condition) && !session.EvaluateCondition(condition)) 188 | { 189 | session.Log($"Condition evaluated to false. Skip PS script {row["Id"]?.ToString()}"); 190 | continue; 191 | } 192 | 193 | string script = Encoding.Unicode.GetString(Convert.FromBase64String(row["Script"].ToString())); 194 | script = session.Format(script); 195 | script = Convert.ToBase64String(Encoding.Unicode.GetBytes(script)); 196 | 197 | ScriptActionData data = new ScriptActionData() 198 | { 199 | Id = row["Id"].ToString(), 200 | Script = script, 201 | IgnoreErrors = row["IgnoreErrors"].ToString() == "1" 202 | }; 203 | 204 | scripts.Add(data); 205 | session.Log("Adding {0} to CustomActionData", data.Id); 206 | } 207 | } 208 | 209 | XmlSerializer srlz = new XmlSerializer(scripts.GetType()); 210 | using (StringWriter sw = new StringWriter()) 211 | { 212 | srlz.Serialize(sw, scripts); 213 | session[deferredProperty] = sw.ToString(); 214 | } 215 | 216 | // Tell the installer to increase the value of the final total 217 | // length of the progress bar by the total number of ticks in 218 | // the custom action. 219 | MessageResult iResult; 220 | using (var hProgressRec = new Record(2)) 221 | { 222 | hProgressRec[1] = 3; 223 | hProgressRec[2] = TotalTicks; 224 | iResult = session.Message(InstallMessage.Progress, hProgressRec); 225 | } 226 | 227 | if (iResult == MessageResult.Cancel) 228 | { 229 | return ActionResult.UserExit; 230 | } 231 | 232 | return ActionResult.Success; 233 | } 234 | catch (Exception ex) 235 | { 236 | session.Log(ex.ToString()); 237 | return ActionResult.Failure; 238 | } 239 | finally 240 | { 241 | db.Close(); 242 | } 243 | } 244 | 245 | private static ActionResult ScriptsDeferred(Session session, string deferredProperty) 246 | { 247 | MessageResult iResult; 248 | using (var hActionRec = new Record(3)) 249 | { 250 | hActionRec[1] = deferredProperty; 251 | hActionRec[2] = "PowerShell Scripts"; 252 | hActionRec[3] = "[1] of [2], [3]"; 253 | iResult = session.Message(InstallMessage.ActionStart, hActionRec); 254 | } 255 | 256 | if (iResult == MessageResult.Cancel) 257 | { 258 | return ActionResult.UserExit; 259 | } 260 | 261 | // Tell the installer to use explicit progress messages. 262 | using (var hProgressRec = new Record(3)) 263 | { 264 | hProgressRec[1] = 1; 265 | hProgressRec[2] = 1; 266 | hProgressRec[3] = 0; 267 | iResult = session.Message(InstallMessage.Progress, hProgressRec); 268 | } 269 | 270 | if (iResult == MessageResult.Cancel) 271 | { 272 | return ActionResult.UserExit; 273 | } 274 | 275 | try 276 | { 277 | List scripts = new List(); 278 | string cad = session["CustomActionData"]; 279 | XmlSerializer srlz = new XmlSerializer(scripts.GetType()); 280 | if (string.IsNullOrWhiteSpace(cad)) 281 | { 282 | session.Log("Nothing to do"); 283 | return ActionResult.Success; 284 | } 285 | 286 | using (StringReader sr = new StringReader(cad)) 287 | { 288 | IEnumerable tempScripts = srlz.Deserialize(sr) as IEnumerable; 289 | if (tempScripts != null) 290 | { 291 | scripts.AddRange(tempScripts); 292 | } 293 | } 294 | 295 | foreach (ScriptActionData datum in scripts) 296 | { 297 | string script = Encoding.Unicode.GetString(Convert.FromBase64String(datum.Script)); 298 | session.Log($"Executing PowerShell script {datum.Id}:\n{script}"); 299 | 300 | using (var task = new PowerShellTask(script, session)) 301 | { 302 | bool result = false; 303 | try 304 | { 305 | result = task.Execute(); 306 | session.Log("PowerShell terminating errors: {0}", result); 307 | } 308 | catch (Exception ex) 309 | { 310 | result = false; 311 | session.Log(ex.ToString()); 312 | } 313 | if (!result) 314 | { 315 | if (datum.IgnoreErrors) 316 | { 317 | session.Log("Script execution failed. Ignoring error"); 318 | continue; 319 | } 320 | 321 | session.Log("Returning Failure"); 322 | return ActionResult.Failure; 323 | } 324 | } 325 | } 326 | 327 | return ActionResult.Success; 328 | } 329 | catch (Exception ex) 330 | { 331 | session.Log("PowerShell terminating error, returning Failure"); 332 | session.Log(ex.ToString()); 333 | return ActionResult.Failure; 334 | } 335 | } 336 | 337 | private static ActionResult FilesImmediate(Session session, int elevated, string deferredProperty) 338 | { 339 | Database db = session.Database; 340 | 341 | const string tableName = "PowerShellFiles"; 342 | if (!db.Tables.Contains(tableName)) 343 | return ActionResult.Success; 344 | 345 | try 346 | { 347 | XDocument doc; 348 | using (View view = db.OpenView(string.Format("SELECT `Id`, `File`, `Arguments`, `IgnoreErrors`, `Condition` FROM `{0}` WHERE `Elevated` = {1} ORDER BY `Order`", tableName, elevated))) 349 | { 350 | view.Execute(); 351 | 352 | doc = new XDocument(new XDeclaration("1.0", "utf-16", "yes"), new XElement("r")); 353 | 354 | foreach (Record row in view) 355 | { 356 | string condition = row["Condition"]?.ToString(); 357 | if (!string.IsNullOrEmpty(condition) && !session.EvaluateCondition(condition)) 358 | { 359 | session.Log($"Condition evaluated to false. Skip PS file {row["Id"]?.ToString()}"); 360 | continue; 361 | } 362 | 363 | var args = session.Format(row["Arguments"].ToString()); 364 | var IgnoreErrors = session.Format(row["IgnoreErrors"].ToString()); 365 | 366 | session.Log("args '{0}'", args); 367 | 368 | doc.Root.Add(new XElement("d", new XAttribute("Id", row["Id"]), 369 | new XAttribute("file", session.Format(row["File"].ToString())), new XAttribute("args", args), new XAttribute("IgnoreErrors", IgnoreErrors))); 370 | } 371 | } 372 | 373 | var cad = new CustomActionData { { "xml", doc.ToString() } }; 374 | 375 | session[deferredProperty] = cad.ToString(); 376 | 377 | // Tell the installer to increase the value of the final total 378 | // length of the progress bar by the total number of ticks in 379 | // the custom action. 380 | MessageResult iResult; 381 | using (var hProgressRec = new Record(2)) 382 | { 383 | hProgressRec[1] = 3; 384 | hProgressRec[2] = TotalTicks; 385 | iResult = session.Message(InstallMessage.Progress, hProgressRec); 386 | } 387 | 388 | if (iResult == MessageResult.Cancel) 389 | { 390 | return ActionResult.UserExit; 391 | } 392 | 393 | return ActionResult.Success; 394 | } 395 | catch (Exception ex) 396 | { 397 | session.Log(ex.Message); 398 | return ActionResult.Failure; 399 | } 400 | finally 401 | { 402 | db.Close(); 403 | } 404 | } 405 | 406 | private static ActionResult FilesDeferred(Session session, string deferredProperty) 407 | { 408 | 409 | // Installer is executing the installation script. Set up a 410 | // record specifying appropriate templates and text for 411 | // messages that will inform the user about what the custom 412 | // action is doing. Tell the installer to use this template and 413 | // text in progress messages. 414 | MessageResult iResult; 415 | using (var hActionRec = new Record(3)) 416 | { 417 | hActionRec[1] = deferredProperty; 418 | hActionRec[2] = "PowerShell Files"; 419 | hActionRec[3] = "[1] of [2], [3]"; 420 | iResult = session.Message(InstallMessage.ActionStart, hActionRec); 421 | } 422 | 423 | if (iResult == MessageResult.Cancel) 424 | { 425 | return ActionResult.UserExit; 426 | } 427 | 428 | // Tell the installer to use explicit progress messages. 429 | using (var hProgressRec = new Record(3)) 430 | { 431 | hProgressRec[1] = 1; 432 | hProgressRec[2] = 1; 433 | hProgressRec[3] = 0; 434 | iResult = session.Message(InstallMessage.Progress, hProgressRec); 435 | } 436 | 437 | if (iResult == MessageResult.Cancel) 438 | { 439 | return ActionResult.UserExit; 440 | } 441 | 442 | try 443 | { 444 | if (!session.CustomActionData.ContainsKey("xml")) 445 | { 446 | session.Log("Skipping as no CustomActionData key 'xml'"); 447 | return ActionResult.NotExecuted; 448 | } 449 | 450 | string content = session.CustomActionData["xml"]; 451 | 452 | XDocument doc = XDocument.Parse(content); 453 | 454 | foreach (XElement row in doc.Root.Elements("d")) 455 | { 456 | string file = row.Attribute("file").Value; 457 | string arguments = row.Attribute("args").Value; 458 | string IgnoreErrors = row.Attribute("IgnoreErrors").Value; 459 | session.Log($"Executing PowerShell file: '{file}' {arguments}"); 460 | 461 | using (var task = new PowerShellTask(file, arguments, session)) 462 | { 463 | try 464 | { 465 | bool result = task.Execute(); 466 | session.Log("PowerShell non-terminating errors: {0}", !result); 467 | if (!result) 468 | { 469 | if (!IgnoreErrors.Equals("0")) 470 | { 471 | session.Log("Ignoring failure due to 'IgnoreErrors' marking"); 472 | } 473 | else 474 | { 475 | session.Log("Returning Failure"); 476 | return ActionResult.Failure; 477 | } 478 | } 479 | } 480 | catch (Exception ex) 481 | { 482 | if (!IgnoreErrors.Equals("0")) 483 | { 484 | session.Log("Ignoring PowerShell error due to 'IgnoreErrors' marking"); 485 | session.Log(ex.ToString()); 486 | } 487 | else 488 | { 489 | session.Log("PowerShell terminating error, returning Failure"); 490 | session.Log(ex.ToString()); 491 | return ActionResult.Failure; 492 | } 493 | } 494 | } 495 | } 496 | 497 | return ActionResult.Success; 498 | } 499 | catch (Exception ex) 500 | { 501 | session.Log("PowerShell terminating error, returning Failure"); 502 | session.Log(ex.ToString()); 503 | return ActionResult.Failure; 504 | } 505 | } 506 | } 507 | } -------------------------------------------------------------------------------- /PowerShellActions/IExitCode.cs: -------------------------------------------------------------------------------- 1 | namespace PowerShellActions 2 | { 3 | internal interface IExitCode 4 | { 5 | int ExitCode { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /PowerShellActions/PowerShellActions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {0A83F788-68C1-4533-8732-6B8E3FBC5282} 9 | Library 10 | Properties 11 | PowerShellActions 12 | PowerShellActions 13 | v4.5 14 | 512 15 | $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.CA.targets 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | False 39 | ..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /PowerShellActions/PowerShellTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Management.Automation; 5 | using System.Management.Automation.Runspaces; 6 | using System.Text; 7 | using Microsoft.Deployment.WindowsInstaller; 8 | 9 | namespace PowerShellActions 10 | { 11 | internal class PowerShellTask : IDisposable, IExitCode 12 | { 13 | private readonly Session _session; 14 | 15 | /// 16 | /// The context that the Windows PowerShell script will run under. 17 | /// 18 | private Pipeline _pipeline; 19 | 20 | internal PowerShellTask(string script, Session session) 21 | { 22 | _session = session; 23 | Runspace runspace = RunspaceFactory.CreateRunspace(new WixHost(session, this)); 24 | 25 | _pipeline = runspace.CreatePipeline(); 26 | _pipeline.Commands.AddScript(script); 27 | _pipeline.Runspace.Open(); 28 | _pipeline.Runspace.SessionStateProxy.SetVariable("session", session); 29 | } 30 | 31 | internal PowerShellTask(string file, string arguments, Session session) 32 | { 33 | _session = session; 34 | RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create(); 35 | Runspace runspace = RunspaceFactory.CreateRunspace(new WixHost(session, this), runspaceConfiguration); 36 | 37 | runspace.Open(); 38 | 39 | var scriptInvoker = new RunspaceInvoke(runspace); 40 | scriptInvoker.Invoke("Set-ExecutionPolicy RemoteSigned -Scope Process"); 41 | 42 | _pipeline = runspace.CreatePipeline(); 43 | 44 | // http://stackoverflow.com/a/530418/25702 45 | _pipeline.Commands.AddScript(string.Format("& '{0}' {1}", file, arguments)); 46 | _pipeline.Runspace.SessionStateProxy.SetVariable("session", session); 47 | } 48 | 49 | public int ExitCode { get; set; } 50 | 51 | public void Dispose() 52 | { 53 | Dispose(true); 54 | GC.SuppressFinalize(this); 55 | } 56 | 57 | /// 58 | /// Execute Script, return false if any errors 59 | /// 60 | /// 61 | public bool Execute() 62 | { 63 | var results = _pipeline.Invoke(); 64 | 65 | using (var record = new Record(0)) 66 | { 67 | record[0] = string.Format("Exit code {0}", ExitCode); 68 | _session.Message(InstallMessage.Info, record); 69 | 70 | if (results?.Any() ?? false) 71 | { 72 | _session.Log("Output"); 73 | 74 | foreach (var r in results) 75 | { 76 | if (r?.BaseObject != null) 77 | { 78 | _session.Log("\t" + r?.BaseObject?.ToString() ?? ""); 79 | } 80 | } 81 | } 82 | 83 | var errors = Errors(); 84 | if (errors != null) 85 | { 86 | _session.Log("Non-terminating errors"); 87 | 88 | record[0] = errors; 89 | _session.Message(InstallMessage.Error, record); 90 | } 91 | 92 | // Using .Error instead of .HadErrors to support any PS version. 93 | return (((_pipeline?.Error?.Count ?? 0) == 0) && (errors == null) && (ExitCode == 0)); 94 | } 95 | } 96 | 97 | public string Errors() 98 | { 99 | // check for errors (non-terminating) 100 | if ((_pipeline?.Error?.Count ?? 0) > 0) 101 | { 102 | var builder = new StringBuilder(); 103 | 104 | // iterate over Error PipeLine until end 105 | while (!_pipeline.Error.EndOfPipeline) 106 | { 107 | // read one PSObject off the pipeline 108 | var value = _pipeline.Error.Read() as PSObject; 109 | if (value != null) 110 | { 111 | // get the ErrorRecord 112 | var r = value.BaseObject as ErrorRecord; 113 | if (r != null) 114 | { 115 | // build whatever kind of message you want 116 | builder.AppendLine(r.InvocationInfo?.MyCommand?.Name ?? "" + " : " + r.Exception.Message); 117 | builder.AppendLine(r.InvocationInfo?.PositionMessage ?? ""); 118 | builder.AppendLine(string.Format("+ CategoryInfo: {0}", r.CategoryInfo)); 119 | builder.AppendLine( 120 | string.Format("+ FullyQualifiedErrorId: {0}", r.FullyQualifiedErrorId ?? "")); 121 | } 122 | } 123 | } 124 | return builder.ToString(); 125 | } 126 | 127 | return null; 128 | } 129 | 130 | protected virtual void Dispose(bool disposing) 131 | { 132 | if (disposing) 133 | { 134 | if (_pipeline.Runspace != null) 135 | { 136 | _pipeline.Runspace.Dispose(); 137 | _pipeline = null; 138 | } 139 | } 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /PowerShellActions/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("PowerShellActions")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("PowerShellActions")] 11 | [assembly: AssemblyCopyright("Copyright © David Gardiner 2014-2021")] 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | 15 | // Setting ComVisible to false makes the types in this assembly not visible 16 | // to COM components. If you need to access a type in this assembly from 17 | // COM, set the ComVisible attribute to true on that type. 18 | [assembly: ComVisible(false)] 19 | 20 | // The following GUID is for the ID of the typelib if this project is exposed to COM 21 | [assembly: Guid("e9c2a5aa-6c22-41f0-bf56-6a62b83d0001")] 22 | -------------------------------------------------------------------------------- /PowerShellActions/WixHost.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Management.Automation.Host; 4 | using System.Threading; 5 | 6 | using Microsoft.Deployment.WindowsInstaller; 7 | 8 | namespace PowerShellActions 9 | { 10 | internal class WixHost : PSHost 11 | { 12 | private readonly IExitCode _exitCode; 13 | private readonly Guid _guid; 14 | private readonly PSHostUserInterface _wixHostUserInterface; 15 | 16 | public WixHost(Session session, IExitCode exitCode) 17 | { 18 | _exitCode = exitCode; 19 | _wixHostUserInterface = new WixHostUserInterface(session); 20 | _guid = Guid.NewGuid(); 21 | } 22 | 23 | public override string Name 24 | { 25 | get 26 | { 27 | return "WixHost"; 28 | } 29 | } 30 | 31 | public override PSHostUserInterface UI 32 | { 33 | get 34 | { 35 | return _wixHostUserInterface; 36 | } 37 | } 38 | 39 | public override CultureInfo CurrentCulture 40 | { 41 | get 42 | { 43 | return Thread.CurrentThread.CurrentCulture; 44 | } 45 | } 46 | 47 | public override CultureInfo CurrentUICulture 48 | { 49 | get 50 | { 51 | return Thread.CurrentThread.CurrentUICulture; 52 | } 53 | } 54 | 55 | public override Guid InstanceId 56 | { 57 | get 58 | { 59 | return _guid; 60 | } 61 | } 62 | 63 | public override Version Version 64 | { 65 | get 66 | { 67 | return new Version(1, 0, 0, 0); 68 | } 69 | } 70 | 71 | public override void EnterNestedPrompt() 72 | { 73 | throw new NotImplementedException("EnterNestedPrompt"); 74 | } 75 | 76 | public override void ExitNestedPrompt() 77 | { 78 | throw new NotImplementedException("ExitNestedPrompt"); 79 | } 80 | 81 | public override void NotifyBeginApplication() 82 | { 83 | } 84 | 85 | public override void NotifyEndApplication() 86 | { 87 | } 88 | 89 | public override void SetShouldExit(int exitCode) 90 | { 91 | _exitCode.ExitCode = exitCode; 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /PowerShellActions/WixHostRawUserInterface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Management.Automation.Host; 3 | 4 | namespace PowerShellActions 5 | { 6 | public class WixHostRawUserInterface : PSHostRawUserInterface 7 | { 8 | public WixHostRawUserInterface() 9 | { 10 | BufferSize = new Size(130, 25); 11 | } 12 | 13 | public override ConsoleColor BackgroundColor { get; set; } 14 | public override Size BufferSize { get; set; } 15 | public override Coordinates CursorPosition { get; set; } 16 | public override int CursorSize { get; set; } 17 | public override ConsoleColor ForegroundColor { get; set; } 18 | 19 | public override bool KeyAvailable 20 | { 21 | get 22 | { 23 | throw new NotImplementedException("KeyAvailable"); 24 | } 25 | } 26 | 27 | public override Size MaxPhysicalWindowSize 28 | { 29 | get 30 | { 31 | throw new NotImplementedException("MaxPhysicalWindowSize"); 32 | } 33 | } 34 | 35 | public override Size MaxWindowSize 36 | { 37 | get 38 | { 39 | throw new NotImplementedException("MaxWindowSize"); 40 | } 41 | } 42 | 43 | public override Coordinates WindowPosition { get; set; } 44 | public override Size WindowSize { get; set; } 45 | public override string WindowTitle { get; set; } 46 | 47 | public override KeyInfo ReadKey(ReadKeyOptions options) 48 | { 49 | throw new NotImplementedException("ReadKey"); 50 | } 51 | 52 | public override void FlushInputBuffer() 53 | { 54 | } 55 | 56 | public override void SetBufferContents(Coordinates origin, BufferCell[,] contents) 57 | { 58 | } 59 | 60 | public override void SetBufferContents(Rectangle rectangle, BufferCell fill) 61 | { 62 | } 63 | 64 | public override BufferCell[,] GetBufferContents(Rectangle rectangle) 65 | { 66 | throw new NotImplementedException("GetBufferContents"); 67 | } 68 | 69 | public override void ScrollBufferContents(Rectangle source, Coordinates destination, Rectangle clip, BufferCell fill) 70 | { 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /PowerShellActions/WixHostUserInterface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Management.Automation; 6 | using System.Management.Automation.Host; 7 | using System.Security; 8 | 9 | using Microsoft.Deployment.WindowsInstaller; 10 | 11 | namespace PowerShellActions 12 | { 13 | internal class WixHostUserInterface : PSHostUserInterface 14 | { 15 | private readonly Session _session; 16 | private readonly WixHostRawUserInterface _wixHostRawUserInterface; 17 | private string _progressActivity; 18 | 19 | public WixHostUserInterface(Session session) 20 | { 21 | _session = session; 22 | _wixHostRawUserInterface = new WixHostRawUserInterface(); 23 | _progressActivity = string.Empty; 24 | } 25 | 26 | [SuppressMessage("StyleCopPlus.StyleCopPlusRules", "SP0100:AdvancedNamingRules", Justification = "Reviewed. Suppression is OK here.")] 27 | public override PSHostRawUserInterface RawUI 28 | { 29 | get 30 | { 31 | return _wixHostRawUserInterface; 32 | } 33 | } 34 | 35 | public override int PromptForChoice(string caption, string message, Collection choices, int defaultChoice) 36 | { 37 | throw new NotImplementedException("PromptForChoice"); 38 | } 39 | 40 | public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName, PSCredentialTypes allowedCredentialTypes, PSCredentialUIOptions options) 41 | { 42 | throw new NotImplementedException("PromptForCredential"); 43 | } 44 | 45 | public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName) 46 | { 47 | throw new NotImplementedException("PromptForCredential"); 48 | } 49 | 50 | public override string ReadLine() 51 | { 52 | throw new NotImplementedException("ReadLine"); 53 | } 54 | 55 | public override SecureString ReadLineAsSecureString() 56 | { 57 | throw new NotImplementedException("ReadLineAsSecureString"); 58 | } 59 | 60 | public override void Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value) 61 | { 62 | _session.Log(value); 63 | } 64 | 65 | public override void Write(string value) 66 | { 67 | _session.Log(value); 68 | } 69 | 70 | public override void WriteDebugLine(string message) 71 | { 72 | _session.Log(message); 73 | } 74 | 75 | public override void WriteErrorLine(string value) 76 | { 77 | var record = new Record(0); 78 | record[0] = value; 79 | _session.Message(InstallMessage.Error, record); 80 | } 81 | 82 | public override void WriteLine(string value) 83 | { 84 | _session.Log(value); 85 | } 86 | 87 | public override void WriteProgress(long sourceId, ProgressRecord progressRecord) 88 | { 89 | Record hActionRec = new Record(3); 90 | Record hProgressRec = new Record(3); 91 | 92 | // Installer is executing the installation script. Set up a 93 | // record specifying appropriate templates and text for 94 | // messages that will inform the user about what the custom 95 | // action is doing. Tell the installer to use this template and 96 | // text in progress messages. 97 | hActionRec[1] = progressRecord.PercentComplete; 98 | hActionRec[2] = 100; // 100% 99 | hActionRec[3] = progressRecord.CurrentOperation; 100 | 101 | // Specify that an update of the progress bar’s position in 102 | // this case means to move it forward by one increment. 103 | hProgressRec[1] = 2; 104 | hProgressRec[2] = CustomActions.TickIncrement; 105 | hProgressRec[3] = 0; 106 | 107 | _session.Message(InstallMessage.ActionData, hActionRec); 108 | _session.Message(InstallMessage.Progress, hProgressRec); 109 | } 110 | 111 | public override void WriteVerboseLine(string message) 112 | { 113 | _session.Log(message); 114 | } 115 | 116 | public override void WriteWarningLine(string message) 117 | { 118 | var record = new Record(0); 119 | record[0] = message; 120 | _session.Message(InstallMessage.Warning, record); 121 | } 122 | 123 | public override Dictionary Prompt(string caption, string message, Collection descriptions) 124 | { 125 | throw new NotImplementedException("Prompt"); 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /PowerShellLibrary/Library.wxs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 18 | 19 | 24 | 25 | 31 | 32 | 37 | 38 | 44 | 45 | 50 | 51 | 57 | 58 | 63 | 64 | 70 | 71 | 76 | 77 | 83 | 84 | 89 | 90 | 96 | 97 | 102 | 103 | 109 | 110 | 111 | 112 | NOT Installed 113 | 114 | NOT Installed 115 | 116 | NOT Installed 117 | 118 | NOT Installed 119 | 120 | 121 | NOT Installed 122 | 123 | NOT Installed 124 | 125 | NOT Installed 126 | 127 | NOT Installed 128 | 129 | 130 | REMOVE="ALL" 131 | 132 | REMOVE="ALL" 133 | 134 | REMOVE="ALL" 135 | 136 | REMOVE="ALL" 137 | 138 | 139 | REMOVE="ALL" 140 | 141 | REMOVE="ALL" 142 | 143 | REMOVE="ALL" 144 | 145 | REMOVE="ALL" 146 | 147 | 148 | 149 | PowerShell Files 150 | PowerShell Inline 151 | PowerShell Inline (elevated) 152 | PowerShell Files (elevated) 153 | 154 | PowerShell Uninstall Files 155 | PowerShell Uninstall Inline 156 | PowerShell Uninstall Inline (elevated) 157 | PowerShell Uninstall Files (elevated) 158 | 159 | 160 | -------------------------------------------------------------------------------- /PowerShellLibrary/PowerShellLibrary.wixproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 3.8 7 | 598472f5-d044-41f2-a8f0-82976139c499 8 | 2.0 9 | PowerShellLibrary 10 | Library 11 | $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets 12 | $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets 13 | 14 | 15 | bin\$(Configuration)\ 16 | obj\$(Configuration)\ 17 | Debug 18 | True 19 | False 20 | True 21 | True 22 | 23 | 24 | bin\$(Configuration)\ 25 | obj\$(Configuration)\ 26 | True 27 | False 28 | True 29 | True 30 | 31 | 32 | 33 | 34 | 35 | 36 | PowerShellActions 37 | {0a83f788-68c1-4533-8732-6b8e3fbc5282} 38 | True 39 | True 40 | Binaries;Content;Satellites 41 | INSTALLFOLDER 42 | 43 | 44 | 45 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /PowerShellWixExtension.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PowerShellWixExtension 5 | 2.1.0 6 | Wix Extension for PowerShell 7 | Wix Extension that allows running PowerShell scripts 8 | An extension for Wix that allows running PowerShell scripts, either from .ps1 files included in the MSI, or by embedding PowerShell script directly 9 | David Gardiner 10 | David Gardiner 11 | https://github.com/flcdrg/PowerShellWixExtension/ 12 | LICENSE.txt 13 | 14 | false 15 | WiX PowerShell MSI Installer WixToolset XML 16 | $releasenotes$ 17 | docs\README.md 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /PowerShellWixExtension.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25123.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerShellWixExtension", "PowerShellWixExtension\PowerShellWixExtension.csproj", "{C1A335B5-3575-4AEC-9260-8C18CE59DB9B}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {598472F5-D044-41F2-A8F0-82976139C499} = {598472F5-D044-41F2-A8F0-82976139C499} 9 | EndProjectSection 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerShellActions", "PowerShellActions\PowerShellActions.csproj", "{0A83F788-68C1-4533-8732-6B8E3FBC5282}" 12 | EndProject 13 | Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "PowerShellLibrary", "PowerShellLibrary\PowerShellLibrary.wixproj", "{598472F5-D044-41F2-A8F0-82976139C499}" 14 | EndProject 15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5A176C3F-31D2-43DB-BAEA-A0DF71CC34FF}" 16 | ProjectSection(SolutionItems) = preProject 17 | PowerShellWixExtension.nuspec = PowerShellWixExtension.nuspec 18 | tables.xsd = tables.xsd 19 | EndProjectSection 20 | EndProject 21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{D7480EA6-0D6B-4010-A15F-460EB85DF682}" 22 | EndProject 23 | Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "PowerShellWixInlineScriptTest", "Tests\PowerShellWixInlineScriptTest\PowerShellWixInlineScriptTest.wixproj", "{BBA07F7F-E4D8-47F7-9A05-4CD6644236C2}" 24 | EndProject 25 | Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "PowerShellWixTest", "Tests\PowerShellWixTest\PowerShellWixTest.wixproj", "{0E852B37-0DD2-4FE9-8E20-123C0040F8BA}" 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|Any CPU = Debug|Any CPU 30 | Debug|Mixed Platforms = Debug|Mixed Platforms 31 | Debug|x86 = Debug|x86 32 | Release|Any CPU = Release|Any CPU 33 | Release|Mixed Platforms = Release|Mixed Platforms 34 | Release|x86 = Release|x86 35 | EndGlobalSection 36 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 37 | {C1A335B5-3575-4AEC-9260-8C18CE59DB9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {C1A335B5-3575-4AEC-9260-8C18CE59DB9B}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {C1A335B5-3575-4AEC-9260-8C18CE59DB9B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 40 | {C1A335B5-3575-4AEC-9260-8C18CE59DB9B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 41 | {C1A335B5-3575-4AEC-9260-8C18CE59DB9B}.Debug|x86.ActiveCfg = Debug|Any CPU 42 | {C1A335B5-3575-4AEC-9260-8C18CE59DB9B}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {C1A335B5-3575-4AEC-9260-8C18CE59DB9B}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {C1A335B5-3575-4AEC-9260-8C18CE59DB9B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 45 | {C1A335B5-3575-4AEC-9260-8C18CE59DB9B}.Release|Mixed Platforms.Build.0 = Release|Any CPU 46 | {C1A335B5-3575-4AEC-9260-8C18CE59DB9B}.Release|x86.ActiveCfg = Release|Any CPU 47 | {0A83F788-68C1-4533-8732-6B8E3FBC5282}.Debug|Any CPU.ActiveCfg = Debug|x86 48 | {0A83F788-68C1-4533-8732-6B8E3FBC5282}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 49 | {0A83F788-68C1-4533-8732-6B8E3FBC5282}.Debug|Mixed Platforms.Build.0 = Debug|x86 50 | {0A83F788-68C1-4533-8732-6B8E3FBC5282}.Debug|x86.ActiveCfg = Debug|x86 51 | {0A83F788-68C1-4533-8732-6B8E3FBC5282}.Debug|x86.Build.0 = Debug|x86 52 | {0A83F788-68C1-4533-8732-6B8E3FBC5282}.Release|Any CPU.ActiveCfg = Release|x86 53 | {0A83F788-68C1-4533-8732-6B8E3FBC5282}.Release|Mixed Platforms.ActiveCfg = Release|x86 54 | {0A83F788-68C1-4533-8732-6B8E3FBC5282}.Release|Mixed Platforms.Build.0 = Release|x86 55 | {0A83F788-68C1-4533-8732-6B8E3FBC5282}.Release|x86.ActiveCfg = Release|x86 56 | {0A83F788-68C1-4533-8732-6B8E3FBC5282}.Release|x86.Build.0 = Release|x86 57 | {598472F5-D044-41F2-A8F0-82976139C499}.Debug|Any CPU.ActiveCfg = Debug|x86 58 | {598472F5-D044-41F2-A8F0-82976139C499}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 59 | {598472F5-D044-41F2-A8F0-82976139C499}.Debug|Mixed Platforms.Build.0 = Debug|x86 60 | {598472F5-D044-41F2-A8F0-82976139C499}.Debug|x86.ActiveCfg = Debug|x86 61 | {598472F5-D044-41F2-A8F0-82976139C499}.Debug|x86.Build.0 = Debug|x86 62 | {598472F5-D044-41F2-A8F0-82976139C499}.Release|Any CPU.ActiveCfg = Release|x86 63 | {598472F5-D044-41F2-A8F0-82976139C499}.Release|Mixed Platforms.ActiveCfg = Release|x86 64 | {598472F5-D044-41F2-A8F0-82976139C499}.Release|Mixed Platforms.Build.0 = Release|x86 65 | {598472F5-D044-41F2-A8F0-82976139C499}.Release|x86.ActiveCfg = Release|x86 66 | {598472F5-D044-41F2-A8F0-82976139C499}.Release|x86.Build.0 = Release|x86 67 | {BBA07F7F-E4D8-47F7-9A05-4CD6644236C2}.Debug|Any CPU.ActiveCfg = Debug|x86 68 | {BBA07F7F-E4D8-47F7-9A05-4CD6644236C2}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 69 | {BBA07F7F-E4D8-47F7-9A05-4CD6644236C2}.Debug|Mixed Platforms.Build.0 = Debug|x86 70 | {BBA07F7F-E4D8-47F7-9A05-4CD6644236C2}.Debug|x86.ActiveCfg = Debug|x86 71 | {BBA07F7F-E4D8-47F7-9A05-4CD6644236C2}.Debug|x86.Build.0 = Debug|x86 72 | {BBA07F7F-E4D8-47F7-9A05-4CD6644236C2}.Release|Any CPU.ActiveCfg = Release|x86 73 | {BBA07F7F-E4D8-47F7-9A05-4CD6644236C2}.Release|Mixed Platforms.ActiveCfg = Release|x86 74 | {BBA07F7F-E4D8-47F7-9A05-4CD6644236C2}.Release|Mixed Platforms.Build.0 = Release|x86 75 | {BBA07F7F-E4D8-47F7-9A05-4CD6644236C2}.Release|x86.ActiveCfg = Release|x86 76 | {BBA07F7F-E4D8-47F7-9A05-4CD6644236C2}.Release|x86.Build.0 = Release|x86 77 | {0E852B37-0DD2-4FE9-8E20-123C0040F8BA}.Debug|Any CPU.ActiveCfg = Debug|x86 78 | {0E852B37-0DD2-4FE9-8E20-123C0040F8BA}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 79 | {0E852B37-0DD2-4FE9-8E20-123C0040F8BA}.Debug|Mixed Platforms.Build.0 = Debug|x86 80 | {0E852B37-0DD2-4FE9-8E20-123C0040F8BA}.Debug|x86.ActiveCfg = Debug|x86 81 | {0E852B37-0DD2-4FE9-8E20-123C0040F8BA}.Debug|x86.Build.0 = Debug|x86 82 | {0E852B37-0DD2-4FE9-8E20-123C0040F8BA}.Release|Any CPU.ActiveCfg = Release|x86 83 | {0E852B37-0DD2-4FE9-8E20-123C0040F8BA}.Release|Mixed Platforms.ActiveCfg = Release|x86 84 | {0E852B37-0DD2-4FE9-8E20-123C0040F8BA}.Release|Mixed Platforms.Build.0 = Release|x86 85 | {0E852B37-0DD2-4FE9-8E20-123C0040F8BA}.Release|x86.ActiveCfg = Release|x86 86 | {0E852B37-0DD2-4FE9-8E20-123C0040F8BA}.Release|x86.Build.0 = Release|x86 87 | EndGlobalSection 88 | GlobalSection(SolutionProperties) = preSolution 89 | HideSolutionNode = FALSE 90 | EndGlobalSection 91 | GlobalSection(NestedProjects) = preSolution 92 | {BBA07F7F-E4D8-47F7-9A05-4CD6644236C2} = {D7480EA6-0D6B-4010-A15F-460EB85DF682} 93 | {0E852B37-0DD2-4FE9-8E20-123C0040F8BA} = {D7480EA6-0D6B-4010-A15F-460EB85DF682} 94 | EndGlobalSection 95 | EndGlobal 96 | -------------------------------------------------------------------------------- /PowerShellWixExtension.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | 1 3 | False 4 | True 5 | True 6 | True 7 | True 8 | True 9 | True -------------------------------------------------------------------------------- /PowerShellWixExtension/PowerShellCompilerExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Text; 4 | using System.Xml; 5 | using System.Xml.Schema; 6 | using Microsoft.Tools.WindowsInstallerXml; 7 | 8 | namespace PowerShellWixExtension 9 | { 10 | public class PowerShellCompilerExtension : CompilerExtension 11 | { 12 | private readonly XmlSchema _schema; 13 | 14 | public PowerShellCompilerExtension() 15 | { 16 | _schema = LoadXmlSchemaHelper(Assembly.GetExecutingAssembly(), "PowerShellWixExtension.PowerShellWixExtensionSchema.xsd"); 17 | } 18 | 19 | public override XmlSchema Schema 20 | { 21 | get 22 | { 23 | return _schema; 24 | } 25 | } 26 | 27 | public override void ParseElement(SourceLineNumberCollection sourceLineNumbers, XmlElement parentElement, XmlElement element, params string[] contextValues) 28 | { 29 | switch (parentElement.LocalName) 30 | { 31 | case "Product": 32 | case "Fragment": 33 | switch (element.LocalName) 34 | { 35 | case "Script": 36 | ParseScriptElement(element); 37 | break; 38 | case "File": 39 | ParseFileElement(element); 40 | break; 41 | default: 42 | Core.UnexpectedElement(parentElement, element); 43 | break; 44 | } 45 | 46 | break; 47 | default: 48 | Core.UnexpectedElement( 49 | parentElement, 50 | element); 51 | break; 52 | } 53 | } 54 | 55 | private void ParseFileElement(XmlNode node) 56 | { 57 | SourceLineNumberCollection sourceLineNumber = Preprocessor.GetSourceLineNumbers(node); 58 | 59 | string superElementId = null; 60 | string file = null; 61 | string arguments = null; 62 | string condition = null; 63 | var elevated = YesNoType.No; 64 | YesNoType ignoreErrors = YesNoType.No; 65 | int order = 1000000000 + sourceLineNumber[0].LineNumber; 66 | 67 | foreach (XmlAttribute attribute in node.Attributes) 68 | { 69 | if (attribute.NamespaceURI.Length == 0 || 70 | attribute.NamespaceURI == _schema.TargetNamespace) 71 | { 72 | switch (attribute.LocalName) 73 | { 74 | case "Id": 75 | superElementId = Core.GetAttributeIdentifierValue(sourceLineNumber, attribute); 76 | break; 77 | case "File": 78 | file = Core.GetAttributeValue(sourceLineNumber, attribute, false); 79 | break; 80 | case "Arguments": 81 | arguments = Core.GetAttributeValue(sourceLineNumber, attribute); 82 | break; 83 | case "Elevated": 84 | elevated = Core.GetAttributeYesNoValue(sourceLineNumber, attribute); 85 | break; 86 | case "IgnoreErrors": 87 | ignoreErrors = Core.GetAttributeYesNoValue(sourceLineNumber, attribute); 88 | break; 89 | case "Order": 90 | order = Core.GetAttributeIntegerValue(sourceLineNumber, attribute, 0, 1000000000); 91 | break; 92 | case "Condition": 93 | condition = Core.GetAttributeValue(sourceLineNumber, attribute); 94 | break; 95 | default: 96 | Core.UnexpectedAttribute(sourceLineNumber, attribute); 97 | break; 98 | } 99 | } 100 | else 101 | { 102 | Core.UnsupportedExtensionAttribute(sourceLineNumber, attribute); 103 | } 104 | } 105 | 106 | if (string.IsNullOrEmpty(superElementId)) 107 | { 108 | Core.OnMessage( 109 | WixErrors.ExpectedAttribute(sourceLineNumber, node.Name, "Id")); 110 | } 111 | 112 | if (string.IsNullOrEmpty(file)) 113 | { 114 | Core.OnMessage( 115 | WixErrors.ExpectedElement(sourceLineNumber, node.Name, "File")); 116 | } 117 | 118 | if (!Core.EncounteredError) 119 | { 120 | Row superElementRow = Core.CreateRow(sourceLineNumber, "PowerShellFiles"); 121 | 122 | superElementRow[0] = superElementId; 123 | superElementRow[1] = file; 124 | superElementRow[2] = arguments; 125 | superElementRow[3] = elevated == YesNoType.Yes ? 1 : 0; 126 | superElementRow[4] = (ignoreErrors == YesNoType.Yes) ? 1 : 0; 127 | superElementRow[5] = order; 128 | superElementRow[6] = condition; 129 | } 130 | 131 | Core.CreateWixSimpleReferenceRow(sourceLineNumber, "CustomAction", "PowerShellFilesImmediate"); 132 | } 133 | 134 | private void ParseScriptElement(XmlNode node) 135 | { 136 | SourceLineNumberCollection sourceLineNumber = Preprocessor.GetSourceLineNumbers(node); 137 | 138 | string superElementId = null; 139 | string scriptData = null; 140 | string condition = null; 141 | var elevated = YesNoType.No; 142 | YesNoType ignoreErrors = YesNoType.No; 143 | int order = 1000000000 + sourceLineNumber[0].LineNumber; 144 | 145 | foreach (XmlAttribute attribute in node.Attributes) 146 | { 147 | if (attribute.NamespaceURI.Length == 0 || 148 | attribute.NamespaceURI == _schema.TargetNamespace) 149 | { 150 | switch (attribute.LocalName) 151 | { 152 | case "Id": 153 | superElementId = Core.GetAttributeIdentifierValue(sourceLineNumber, attribute); 154 | break; 155 | case "Elevated": 156 | elevated = Core.GetAttributeYesNoValue(sourceLineNumber, attribute); 157 | break; 158 | case "IgnoreErrors": 159 | ignoreErrors = Core.GetAttributeYesNoValue(sourceLineNumber, attribute); 160 | break; 161 | case "Order": 162 | order = Core.GetAttributeIntegerValue(sourceLineNumber, attribute, 0, 1000000000); 163 | break; 164 | case "Condition": 165 | condition = Core.GetAttributeValue(sourceLineNumber, attribute); 166 | break; 167 | 168 | default: 169 | Core.UnexpectedAttribute(sourceLineNumber, attribute); 170 | break; 171 | } 172 | } 173 | else 174 | { 175 | Core.UnsupportedExtensionAttribute(sourceLineNumber, attribute); 176 | } 177 | } 178 | 179 | if (node.HasChildNodes) 180 | { 181 | var cdata = node.ChildNodes[0] as XmlCDataSection; 182 | 183 | if (cdata != null) 184 | 185 | // Need to encode, as column doesn't like having line feeds 186 | scriptData = Convert.ToBase64String(Encoding.Unicode.GetBytes(cdata.Data)); 187 | } 188 | 189 | if (string.IsNullOrEmpty(superElementId)) 190 | { 191 | Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumber, node.Name, "Id")); 192 | } 193 | 194 | if (string.IsNullOrEmpty(scriptData)) 195 | { 196 | Core.OnMessage(WixErrors.ExpectedElement(sourceLineNumber, node.Name, "CDATA")); 197 | } 198 | 199 | if (!Core.EncounteredError) 200 | { 201 | Row superElementRow = Core.CreateRow(sourceLineNumber, "PowerShellScripts"); 202 | 203 | superElementRow[0] = superElementId; 204 | superElementRow[1] = scriptData; 205 | superElementRow[2] = elevated == YesNoType.Yes ? 1 : 0; 206 | superElementRow[3] = (ignoreErrors == YesNoType.Yes) ? 1 : 0; 207 | superElementRow[4] = order; 208 | superElementRow[5] = condition; 209 | } 210 | 211 | Core.CreateWixSimpleReferenceRow(sourceLineNumber, "CustomAction", "PowerShellScriptsImmediate"); 212 | } 213 | } 214 | } -------------------------------------------------------------------------------- /PowerShellWixExtension/PowerShellWixExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Microsoft.Tools.WindowsInstallerXml; 3 | 4 | namespace PowerShellWixExtension 5 | { 6 | public class PowerShellWixExtension : WixExtension 7 | { 8 | private CompilerExtension _compilerExtension; 9 | private Library _library; 10 | private TableDefinitionCollection _tableDefinitions; 11 | 12 | public override CompilerExtension CompilerExtension 13 | { 14 | get 15 | { 16 | return _compilerExtension ?? (_compilerExtension = new PowerShellCompilerExtension()); 17 | } 18 | } 19 | 20 | public override TableDefinitionCollection TableDefinitions 21 | { 22 | get 23 | { 24 | return _tableDefinitions ?? (_tableDefinitions = LoadTableDefinitionHelper(Assembly.GetExecutingAssembly(), "PowerShellWixExtension.TableDefinitions.xml")); 25 | } 26 | } 27 | 28 | public override Library GetLibrary(TableDefinitionCollection tableDefinitions) 29 | { 30 | return _library ?? (_library = LoadLibraryHelper(Assembly.GetExecutingAssembly(), "PowerShellWixExtension.PowerShellLibrary.wixlib", tableDefinitions)); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /PowerShellWixExtension/PowerShellWixExtension.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C1A335B5-3575-4AEC-9260-8C18CE59DB9B} 8 | Library 9 | Properties 10 | PowerShellWixExtension 11 | PowerShellWixExtension 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | PowerShellLibrary 47 | false 48 | 49 | 50 | $(WIX)\bin\wix.dll 51 | 52 | 53 | 54 | 55 | 56 | 57 | PowerShellWixExtensionSchema.xsd 58 | 59 | 60 | 61 | 62 | 63 | Designer 64 | 65 | 66 | 67 | 68 | Designer 69 | 70 | 71 | 72 | 73 | PowerShellLibrary.wixlib 74 | 75 | 76 | 77 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /PowerShellWixExtension/PowerShellWixExtensionSchema.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Microsoft.Tools.WindowsInstallerXml; 8 | 9 | namespace PowerShellWixExtension 10 | { 11 | } 12 | -------------------------------------------------------------------------------- /PowerShellWixExtension/PowerShellWixExtensionSchema.xsd: -------------------------------------------------------------------------------- 1 |  2 | 10 | 11 | 12 | The schema for the PowerShellWixExtension WiX Extension 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Embed PowerShell commands using CDATA 26 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | The ID for the element. 37 | 38 | 39 | 40 | 41 | 42 | Set to true if script should run as an elevated user 43 | 44 | 45 | 46 | 47 | 48 | Set to true to ignore PowerShell errors 49 | 50 | 51 | 52 | 53 | 54 | Order of script execution. Note that ordering is within the context and type. For example, elevated scripts will run on different context than non-elevated. Defaults to run in line-number order. Files with explicit ordering will be executed before files with implicit ordering 55 | 56 | 57 | 58 | 59 | 60 | Condition on executing the script. For example, 'NOT Installed' or 'REMOVE="ALL"' 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | Run a PowerShell script file 79 | 80 | 81 | 82 | 83 | 86 | 87 | The ID for the element. 88 | 89 | 90 | 91 | 92 | 93 | The reference to a PowerShell file 94 | 95 | 96 | 97 | 98 | 99 | The arguments for PowerShell file 100 | 101 | 102 | 103 | 104 | 105 | Set to true if script should run as an elevated user 106 | 107 | 108 | 109 | 110 | 111 | Set to true to ignore PowerShell errors 112 | 113 | 114 | 115 | 116 | 117 | Order of script execution. Note that ordering is within the context and type. For example, elevated scripts will run on different context than non-elevated. Defaults to run in line-number order. Files with explicit ordering will be executed before files with implicit ordering 118 | 119 | 120 | 121 | 122 | 123 | Condition on executing the script. For example, 'NOT Installed' or 'REMOVE="ALL"' 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /PowerShellWixExtension/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using Microsoft.Tools.WindowsInstallerXml; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("PowerShellWixExtension")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("PowerShellWixExtension")] 13 | [assembly: AssemblyCopyright("Copyright © David Gardiner 2014-2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7abfedd7-d0f1-489a-a1f4-fb5e3e0bd038")] 24 | 25 | [assembly: AssemblyDefaultWixExtension(typeof(PowerShellWixExtension.PowerShellWixExtension))] 26 | -------------------------------------------------------------------------------- /PowerShellWixExtension/TableDefinitions.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 12 | 13 | 20 | 21 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 45 | 46 | 49 | 50 | 57 | 58 | 67 | 68 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Publish-Package.ps1: -------------------------------------------------------------------------------- 1 | # Get latest nupkg 2 | 3 | $nupkg = Get-ChildItem .\Nuget\*.nupkg | Sort-Object -Descending -Property LastWriteTime | Select-Object -First 1 4 | 5 | .\NuGet Push $nupkg -source nuget.org -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerShellWixExtension 2 | 3 | A Wix Extension for running PowerShell scripts 4 | 5 | ## NuGet Package 6 | 7 | [![NuGet](https://img.shields.io/nuget/v/PowerShellWixExtension.svg?maxAge=2592)](https://www.nuget.org/packages/PowerShellWixExtension/) [![AppVeyor](https://ci.appveyor.com/api/projects/status/github/flcdrg/PowerShellWixExtension?style=plastic)](https://ci.appveyor.com/project/DavidGardiner/powershellwixextension) 8 | 9 | All ready to add to an existing Wix project. Grab the latest version from https://www.nuget.org/packages/PowerShellWixExtension/ 10 | 11 | ## Getting Started 12 | 13 | 1. Add a reference to the PowerShellWixExtension.dll in your Wix Setup Project (NuGet package recommended) 14 | 2. Add namespace to .wxs file 15 | 16 | ```xml 17 | 18 | 19 | ``` 20 | 21 | 4. To execute a .ps1 file that ships with the project 22 | 23 | ```xml 24 | 25 | ``` 26 | 27 | 5. To execute inline script use 28 | 29 | ```xml 30 | 31 | 41 | 42 | ``` 43 | 44 | ## Notes 45 | 46 | ### Custom sequences 47 | 48 | You can customise when a set of scripts are run by adding your own `` element inside your `` element. eg. 49 | 50 | ```xml 51 | 52 | NOT Installed 53 | 54 | ``` 55 | 56 | The four defined actions are: 57 | 58 | 1. `PowerShellScriptsDeferred` 59 | 2. `PowerShellScriptsElevatedDeferred` 60 | 3. `PowerShellFilesDeferred` 61 | 4. `PowerShellFilesElevatedDeferred` 62 | 63 | ### Inline Scripts 64 | 65 | * Be aware that if your inline script uses square brackets \[ \], you'll need to escape them like [\\[] [\\]] otherwise they will be interpreted as MSI properties (unless that is what you wanted!) 66 | -------------------------------------------------------------------------------- /Settings.StyleCop: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | False 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | False 15 | 16 | 17 | 18 | 19 | False 20 | 21 | 22 | 23 | 24 | False 25 | 26 | 27 | 28 | 29 | False 30 | 31 | 32 | 33 | 34 | False 35 | 36 | 37 | 38 | 39 | False 40 | 41 | 42 | 43 | 44 | False 45 | 46 | 47 | 48 | 49 | False 50 | 51 | 52 | 53 | 54 | False 55 | 56 | 57 | 58 | 59 | False 60 | 61 | 62 | 63 | 64 | False 65 | 66 | 67 | 68 | 69 | False 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | False 80 | 81 | 82 | 83 | 84 | False 85 | 86 | 87 | 88 | 89 | False 90 | 91 | 92 | 93 | 94 | False 95 | 96 | 97 | 98 | 99 | False 100 | 101 | 102 | 103 | 104 | False 105 | 106 | 107 | 108 | 109 | Spaces:False 110 | _$(aaBb) 111 | _$(aaBb) 112 | $(AaBb) 113 | $(AaBb) 114 | $(AaBb) 115 | $(AaBb):$(AA_BB) 116 | 3D A B C FxCop ID IDs OK StyleCop UI X 117 | 118 | 119 | 120 | 121 | 122 | 123 | False 124 | 125 | 126 | 127 | 128 | False 129 | 130 | 131 | 132 | 133 | False 134 | 135 | 136 | 137 | 138 | False 139 | 140 | 141 | 142 | 143 | False 144 | 145 | 146 | 147 | 148 | False 149 | 150 | 151 | 152 | 153 | False 154 | 155 | 156 | 157 | 158 | False 159 | 160 | 161 | 162 | 163 | False 164 | 165 | 166 | 167 | 168 | False 169 | 170 | 171 | 172 | 173 | False 174 | 175 | 176 | 177 | 178 | False 179 | 180 | 181 | 182 | 183 | False 184 | 185 | 186 | 187 | 188 | False 189 | 190 | 191 | 192 | 193 | False 194 | 195 | 196 | 197 | 198 | False 199 | 200 | 201 | 202 | 203 | False 204 | 205 | 206 | 207 | 208 | False 209 | 210 | 211 | 212 | 213 | False 214 | 215 | 216 | 217 | 218 | False 219 | 220 | 221 | 222 | 223 | False 224 | 225 | 226 | 227 | 228 | False 229 | 230 | 231 | 232 | 233 | False 234 | 235 | 236 | 237 | 238 | False 239 | 240 | 241 | 242 | 243 | False 244 | 245 | 246 | 247 | 248 | False 249 | 250 | 251 | 252 | 253 | False 254 | 255 | 256 | 257 | 258 | False 259 | 260 | 261 | 262 | 263 | False 264 | 265 | 266 | 267 | 268 | False 269 | 270 | 271 | 272 | 273 | False 274 | 275 | 276 | 277 | 278 | False 279 | 280 | 281 | 282 | 283 | False 284 | 285 | 286 | 287 | 288 | False 289 | 290 | 291 | 292 | 293 | False 294 | 295 | 296 | 297 | 298 | False 299 | 300 | 301 | 302 | 303 | False 304 | 305 | 306 | 307 | 308 | False 309 | 310 | 311 | 312 | 313 | False 314 | 315 | 316 | 317 | 318 | False 319 | 320 | 321 | 322 | 323 | False 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | False 334 | 335 | 336 | 337 | 338 | False 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | False 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | False 359 | 360 | 361 | 362 | 363 | False 364 | 365 | 366 | 367 | 368 | True 369 | 370 | 371 | 372 | 373 | 374 | 375 | -------------------------------------------------------------------------------- /Tests/Pester.Tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module Pester 2 | 3 | $base = $env:GITHUB_WORKSPACE 4 | 5 | if (-not $base) { 6 | $base = "." 7 | } 8 | 9 | Describe 'Inline Scripts' { 10 | 11 | It 'Install' { 12 | 'inlinescript-install.log' | Should -FileContentMatch 'This is an inline script, running non-elevated' 13 | } 14 | } 15 | 16 | Describe 'Scripts' { 17 | 18 | It 'Install' { 19 | 'script-install.log' | Should -FileContentMatch 'This is going to Output' 20 | } 21 | } -------------------------------------------------------------------------------- /Tests/PowerShellWixInlineScriptTest/PowerShellWixInlineScriptTest.wixproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 3.10 7 | bba07f7f-e4d8-47f7-9a05-4cd6644236c2 8 | 2.0 9 | PowerShellWixInlineScriptTest 10 | Package 11 | $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets 12 | $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets 13 | 14 | 15 | bin\$(Configuration)\ 16 | obj\$(Configuration)\ 17 | Debug 18 | False 19 | True 20 | True 21 | 22 | 23 | bin\$(Configuration)\ 24 | obj\$(Configuration)\ 25 | False 26 | True 27 | True 28 | 29 | 30 | 31 | 32 | 33 | 34 | ..\..\Libs\PowerShellWixExtension.dll 35 | PowerShellWixExtension 36 | 37 | 38 | $(WixExtDir)\WixUIExtension.dll 39 | WixUIExtension 40 | 41 | 42 | 43 | 44 | 45 | 46 | 54 | -------------------------------------------------------------------------------- /Tests/PowerShellWixInlineScriptTest/Product.wxs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | NOT Installed 16 | 17 | 18 | 19 | 38 | 39 | 40 | 41 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /Tests/PowerShellWixInlineScriptTest/TextFile1.txt: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /Tests/PowerShellWixTest/PowerShellWixTest.wixproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 3.8 7 | 0e852b37-0dd2-4fe9-8e20-123c0040f8ba 8 | 2.0 9 | PowerShellWixTest 10 | Package 11 | $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets 12 | $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets 13 | 14 | 15 | bin\$(Configuration)\ 16 | obj\$(Configuration)\ 17 | Debug 18 | False 19 | True 20 | 21 | 22 | bin\$(Configuration)\ 23 | obj\$(Configuration)\ 24 | True 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ..\..\Libs\PowerShellWixExtension.dll 33 | PowerShellWixExtension 34 | 35 | 36 | $(WixExtDir)\WixUIExtension.dll 37 | WixUIExtension 38 | 39 | 40 | 41 | 42 | 43 | 44 | 52 | -------------------------------------------------------------------------------- /Tests/PowerShellWixTest/Product.wxs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 47 | 48 | 49 | 50 | 59 | 60 | 61 | 62 | 69 | 70 | 71 | 72 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 1 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /Tests/PowerShellWixTest/ProgressDlg.wxs: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 1 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 29 | 32 | 35 | 38 | 41 | 44 | 47 | 50 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /Tests/PowerShellWixTest/Test.ps1: -------------------------------------------------------------------------------- 1 | param([string] $first) 2 | 3 | $DebugPreference = "Continue" 4 | $VerbosePreference = "Continue" 5 | 6 | Write-Host "Testing Test.ps1 - $first" 7 | 8 | Write-Output "This is going to Output" 9 | 10 | Write-Verbose "This is going to verbose" 11 | 12 | Write-Debug "This is going to Debug" 13 | 14 | Write-Host "Current identity" 15 | 16 | $wid=[System.Security.Principal.WindowsIdentity]::GetCurrent() 17 | $prp=new-object System.Security.Principal.WindowsPrincipal($wid) 18 | $adm=[System.Security.Principal.WindowsBuiltInRole]::Administrator 19 | 20 | Write-Host $wid.Name 21 | Write-Host $prp.IsInRole($adm) -------------------------------------------------------------------------------- /tables.xsd: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | 15 | Schema for describing table definitions in Windows Installer. 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Boolean whether rows in this table create symbols 35 | 36 | 37 | 38 | 39 | Name of table in Windows Installer database 40 | 41 | 42 | 43 | 44 | Specifies if table is virtual or not 45 | 46 | 47 | 48 | 49 | Specifies if the table is a part of the Bootstrapper Application Data manifest 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | Name of column in Windows Installer table 60 | 61 | 62 | 63 | 64 | 65 | Whether this column was added by a transform. 66 | 67 | 68 | 69 | 70 | 71 | Type of column in Windows Installer table 72 | 73 | 74 | 75 | 76 | 77 | Type of column in Windows Installer table 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | Boolean whether column is primary key of Windows Installer table 90 | 91 | 92 | 93 | 94 | 95 | Boolean whether column is nullable in Windows Installer table 96 | 97 | 98 | 99 | 100 | 101 | Boolean whether column is virtual in Windows Installer table 102 | 103 | 104 | 105 | 106 | 107 | Enumeration specifying how column should have the ModuleId appended 108 | 109 | 110 | 111 | 112 | 113 | Set to "yes" in order to allow substitution for localized variables. 114 | 115 | 116 | 117 | 118 | 119 | Minimum value for column in Windows Installer table 120 | 121 | 122 | 123 | 124 | 125 | Maximum value for column in Windows Installer table 126 | 127 | 128 | 129 | 130 | 131 | Foreign key table for column in Windows Installer table 132 | 133 | 134 | 135 | 136 | 137 | Maximum value for column in Windows Installer table 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | Specific column data types for column 150 | 151 | 152 | 153 | 154 | 155 | List of permissible values for the column 156 | 157 | 158 | 159 | 160 | 161 | Description of column 162 | 163 | 164 | 165 | 166 | 167 | Set to "yes" in order to make the idt exporter escape whitespace characters \r, \n, and \t. 168 | 169 | 170 | 171 | 172 | 173 | Set to "yes" in order to make the Intermediate and Output objects wrap their data in a CDATA element to preserve whitespace. 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "3.0", 4 | "publicReleaseRefSpec": [ 5 | "^refs/heads/main$", 6 | "^refs/heads/v\\d+(?:\\.\\d+)?$" 7 | ], 8 | "nugetPackageVersion": { 9 | "semVer": 2 10 | }, 11 | "cloudBuild": { 12 | "setAllVariables": true, 13 | "buildNumber": { 14 | "enabled": true, 15 | "includeCommitId": { 16 | "when": "always", 17 | "where": "buildMetadata" 18 | } 19 | } 20 | } 21 | } --------------------------------------------------------------------------------