├── .editorconfig
├── .gitignore
├── .gitmodules
├── CodeContracts.ruleset
├── CustomDictionary.xml
├── Directory.Build.props
├── Key.snk
├── LICENSE
├── README.md
├── ReleaseNotes.md
├── Screenshot.png
├── Wax.Model
├── FodyWeavers.xml
├── FodyWeavers.xsd
├── Mapping
│ ├── DirectoryMapping.cs
│ ├── FeatureMapping.cs
│ ├── FileMapping.cs
│ ├── MappingState.cs
│ └── UnmappedFile.cs
├── Properties
│ └── AssemblyInfo.cs
├── Tools
│ ├── AssemblyHelper.cs
│ ├── SerializerExtensions.cs
│ └── XmlExtensions.cs
├── VisualStudio
│ ├── BuildFileGroups.cs
│ ├── DteExtensions.cs
│ ├── Project.cs
│ ├── ProjectOutput.cs
│ ├── ProjectOutputGroup.cs
│ ├── ProjectReference.cs
│ └── Solution.cs
├── Wax.Model.csproj
└── Wix
│ ├── ProjectConfiguration.cs
│ ├── WixComponentGroupNode.cs
│ ├── WixComponentNode.cs
│ ├── WixDefine.cs
│ ├── WixDirectoryNode.cs
│ ├── WixFeatureNode.cs
│ ├── WixFileNode.cs
│ ├── WixNames.cs
│ ├── WixNode.cs
│ ├── WixProject.cs
│ └── WixSourceFile.cs
├── Wax.sln
├── Wax.sln.DotSettings
├── Wax
├── 200x200.png
├── 32x32.png
├── FodyWeavers.xml
├── FodyWeavers.xsd
├── GroupBox.cs
├── Guids.cs
├── MainView.xaml
├── MainView.xaml.cs
├── MainViewModel.cs
├── PkgCmdID.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.de.resx
│ ├── Resources.fr.resx
│ ├── Resources.resx
│ ├── Resources.zh-CN.resx
│ └── launchSettings.json
├── Resources
│ ├── Icon32.pdn
│ ├── Package.ico
│ ├── VSColorScheme.xaml
│ ├── btn_donate_SM.gif
│ ├── icon.png
│ ├── images.png
│ ├── like.png
│ ├── ok.png
│ ├── refresh.png
│ └── warning.png
├── ShellView.xaml
├── ShellView.xaml.cs
├── Themes
│ └── Generic.xaml
├── ToolWindow.cs
├── VSPackage.de.resx
├── VSPackage.fr.resx
├── VSPackage.resx
├── VSPackage.zh-CN.resx
├── VsPackage.cs
├── Wax.csproj
├── Wax.vsct
└── source.extension.vsixmanifest
└── azure-pipelines.yml
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # VSTHRD010: Invoke single-threaded types on Main thread
4 | dotnet_diagnostic.VSTHRD010.severity = suggestion
5 |
6 | # IDE0008: Use explicit type
7 | csharp_style_var_elsewhere = true
8 |
9 | # IDE0008: Use explicit type
10 | csharp_style_var_for_built_in_types = true
11 |
12 | # IDE0008: Use explicit type
13 | csharp_style_var_when_type_is_apparent = true
14 |
--------------------------------------------------------------------------------
/.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 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 |
24 | # Visual Studio 2015 cache/options directory
25 | .vs/
26 | # Uncomment if you have tasks that create the project's static files in wwwroot
27 | #wwwroot/
28 |
29 | # MSTest test Results
30 | [Tt]est[Rr]esult*/
31 | [Bb]uild[Ll]og.*
32 |
33 | # NUNIT
34 | *.VisualState.xml
35 | TestResult.xml
36 |
37 | # Build Results of an ATL Project
38 | [Dd]ebugPS/
39 | [Rr]eleasePS/
40 | dlldata.c
41 |
42 | # DNX
43 | project.lock.json
44 | artifacts/
45 |
46 | *_i.c
47 | *_p.c
48 | *_i.h
49 | *.ilk
50 | *.meta
51 | *.obj
52 | *.pch
53 | *.pdb
54 | *.pgc
55 | *.pgd
56 | *.rsp
57 | *.sbr
58 | *.tlb
59 | *.tli
60 | *.tlh
61 | *.tmp
62 | *.tmp_proj
63 | *.log
64 | *.vspscc
65 | *.vssscc
66 | .builds
67 | *.pidb
68 | *.svclog
69 | *.scc
70 |
71 | # Chutzpah Test files
72 | _Chutzpah*
73 |
74 | # Visual C++ cache files
75 | ipch/
76 | *.aps
77 | *.ncb
78 | *.opendb
79 | *.opensdf
80 | *.sdf
81 | *.cachefile
82 |
83 | # Visual Studio profiler
84 | *.psess
85 | *.vsp
86 | *.vspx
87 | *.sap
88 |
89 | # TFS 2012 Local Workspace
90 | $tf/
91 |
92 | # Guidance Automation Toolkit
93 | *.gpState
94 |
95 | # ReSharper is a .NET coding add-in
96 | _ReSharper*/
97 | *.[Rr]e[Ss]harper
98 | *.DotSettings.user
99 |
100 | # JustCode is a .NET coding add-in
101 | .JustCode
102 |
103 | # TeamCity is a build add-in
104 | _TeamCity*
105 |
106 | # DotCover is a Code Coverage Tool
107 | *.dotCover
108 |
109 | # NCrunch
110 | _NCrunch_*
111 | .*crunch*.local.xml
112 | nCrunchTemp_*
113 |
114 | # MightyMoose
115 | *.mm.*
116 | AutoTest.Net/
117 |
118 | # Web workbench (sass)
119 | .sass-cache/
120 |
121 | # Installshield output folder
122 | [Ee]xpress/
123 |
124 | # DocProject is a documentation generator add-in
125 | DocProject/buildhelp/
126 | DocProject/Help/*.HxT
127 | DocProject/Help/*.HxC
128 | DocProject/Help/*.hhc
129 | DocProject/Help/*.hhk
130 | DocProject/Help/*.hhp
131 | DocProject/Help/Html2
132 | DocProject/Help/html
133 |
134 | # Click-Once directory
135 | publish/
136 |
137 | # Publish Web Output
138 | *.[Pp]ublish.xml
139 | *.azurePubxml
140 | # TODO: Comment the next line if you want to checkin your web deploy settings
141 | # but database connection strings (with potential passwords) will be unencrypted
142 | *.pubxml
143 | *.publishproj
144 |
145 | # NuGet Packages
146 | *.nupkg
147 | # The packages folder can be ignored because of Package Restore
148 | **/packages/*
149 | # except build/, which is used as an MSBuild target.
150 | !**/packages/build/
151 | # Uncomment if necessary however generally it will be regenerated when needed
152 | #!**/packages/repositories.config
153 | # NuGet v3's project.json files produces more ignoreable files
154 | *.nuget.props
155 | *.nuget.targets
156 |
157 | # Microsoft Azure Build Output
158 | csx/
159 | *.build.csdef
160 |
161 | # Microsoft Azure Emulator
162 | ecf/
163 | rcf/
164 |
165 | # Microsoft Azure ApplicationInsights config file
166 | ApplicationInsights.config
167 |
168 | # Windows Store app package directory
169 | AppPackages/
170 | BundleArtifacts/
171 |
172 | # Visual Studio cache files
173 | # files ending in .cache can be ignored
174 | *.[Cc]ache
175 | # but keep track of directories ending in .cache
176 | !*.[Cc]ache/
177 |
178 | # Others
179 | ClientBin/
180 | ~$*
181 | *~
182 | *.dbmdl
183 | *.dbproj.schemaview
184 | *.pfx
185 | *.publishsettings
186 | node_modules/
187 | orleans.codegen.cs
188 |
189 | # RIA/Silverlight projects
190 | Generated_Code/
191 |
192 | # Backup & report files from converting an old project file
193 | # to a newer Visual Studio version. Backup files are not needed,
194 | # because we have git ;-)
195 | _UpgradeReport_Files/
196 | Backup*/
197 | UpgradeLog*.XML
198 | UpgradeLog*.htm
199 |
200 | # SQL Server files
201 | *.mdf
202 | *.ldf
203 |
204 | # Business Intelligence projects
205 | *.rdl.data
206 | *.bim.layout
207 | *.bim_*.settings
208 |
209 | # Microsoft Fakes
210 | FakesAssemblies/
211 |
212 | # GhostDoc plugin setting file
213 | *.GhostDoc.xml
214 |
215 | # Node.js Tools for Visual Studio
216 | .ntvs_analysis.dat
217 |
218 | # Visual Studio 6 build log
219 | *.plg
220 |
221 | # Visual Studio 6 workspace options file
222 | *.opt
223 |
224 | # Visual Studio LightSwitch build output
225 | **/*.HTMLClient/GeneratedArtifacts
226 | **/*.DesktopClient/GeneratedArtifacts
227 | **/*.DesktopClient/ModelManifest.xml
228 | **/*.Server/GeneratedArtifacts
229 | **/*.Server/ModelManifest.xml
230 | _Pvt_Extensions
231 |
232 | # Paket dependency manager
233 | .paket/paket.exe
234 |
235 | # FAKE - F# Make
236 | .fake/
237 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "BAML"]
2 | path = BAML
3 | url = https://github.com/tom-englert/BAML.git
4 | branch = master
5 |
--------------------------------------------------------------------------------
/CodeContracts.ruleset:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/CustomDictionary.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | tomenglertde
9 | VSIX
10 | visualstudiogallery
11 | workitem
12 | wix
13 | dte
14 | dlls
15 | Unmap
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | tomenglertde
30 | vsix
31 | HKLM
32 | HKCU
33 | HKCR
34 | HKMU
35 | DWORD
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1.7.0.0
5 | tom-englert.de
6 | Tom's Toolbox
7 | Copyright © tom-englert.de 2017-2021
8 | true
9 | 9.0
10 | true
11 | embedded
12 | true
13 | enable
14 | true
15 | ..\Key.snk
16 | true
17 | win
18 |
19 |
--------------------------------------------------------------------------------
/Key.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tom-englert/Wax/57dd8d47972869363733e66712cf813d3efec7d8/Key.snk
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017-2020 Tom Englert
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Wax [](https://dev.azure.com/tom-englert/Open%20Source/_build/latest?definitionId=47&branchName=master)
2 | An interactive editor for [WiX Toolset](http://wixtoolset.org/) setup projects.
3 |
4 | _Wax keeps your candle burning_
5 |
6 | While it's an easy task to create an empty setup project with the [WiX Toolset](http://wixtoolset.org/), populating the list of deployable
7 | files and even more keeping the list up to date can be a very fumbling task.
8 | This tool is a Visual Studio Extension that helps you to create, verify and maintain the list of the deployed files in an interactive GUI.
9 |
10 | Just select the projects that you want to install in the list box on the left side.
11 | Two data grids on the right will show you the target directories needed and files to be installed, and how they are mapped to the file nodes in your WiX setup project.
12 |
13 | Files that have no corresponding item in the WiX setup project are show in red. You can add them to the setup project by clicking the '+' button in the rightmost column.
14 | Files that already exist in the setup project are shown in yellow. You can link them together by clicking the '?' button in the rightmost column.
15 | Files that already exist in the setup project but have multiple matches are shown in orange. You can link items together by selecting the proper file from the combo box.
16 |
17 | The file grid supports multiple selection, so you can apply all commands to many files in one step.
18 |
19 | 
20 |
21 | ## Installation
22 |
23 | ---
24 | ### Make sure you have installed the [WiX Toolset](http://wixtoolset.org/) before installing Wax!
25 | ---
26 |
27 | This tool is a Visual Studio Extension. Use the Visual Studio Extension Manager, install from the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=TomEnglert.Wax), or download the binaries and double click the Wax.vsix in the windows explorer.
28 |
29 | The Wax editor is a tool to maintain WiX projects, so you should have installed the [WiX Toolset](http://wixtoolset.org/).
30 |
31 | ## Usage
32 |
33 | ### Preparing the solution
34 |
35 | We'll assume here that you already have a solution with some projects that you want to deploy.
36 | The Wax editor is collecting the output of the selected project(s) to install and all dependencies to populate the files list.
37 | In order to get the full benefit from this tool, all files that need to be installed should be part of the projects.
38 | Files that are not generated by the build, e.g. read-me files or documentation, should be added to the project with the `Build Action` set to `Content` and `Copy to Output Directory` to `Copy always` or `Copy if newer`.
39 | If you follow this pattern, you won't have to manually fumble around with additional files in the WiX project.
40 |
41 | ### Create a new WiX Setup project
42 |
43 | If you don't already have a WiX Setup project in your solution, create a new one now:
44 | - In Visual Studio Click `File`, then click `New`, then click `Project`.
45 | - Choose the Windows Installer XML node in the Project types tree, then select Setup Project
46 | - Name your project and press OK.
47 |
48 | ### Open the editor
49 |
50 | The WiX Setup Editor menu entry is located in the Visual Studios "Tools" menu.
51 |
52 | 
53 |
54 | When parsing the projects, all files of the `Built`, `ContentFiles` and `LocalizedResourceDlls` build groups are collected. If you want to deploy the symbol files with your project, check the `Deploy symbols` toggle button in the tool bar.
55 |
56 | There are five sections you have to edit in sequential order. If a green check mark appears in the top right corner, this section is complete. If there is a red exclamation mark, the section needs editing.
57 |
58 | If you have made conceptual changes to your solution while the editor is open, click the refresh button.
59 |
60 | ### (1) Select the WiX project to edit.
61 |
62 | As the first step select the WiX project you want to edit. If there is only one setup project in the solution, it will be already selected.
63 |
64 | ### (2) Map the root directory.
65 |
66 | The editor needs to know the root directory definition in the wix file. Simply select it from the items in the combo box.
67 | If you have created a new WiX Setup project, there is only one and it is named ```INSTALLFOLDER```.
68 |
69 | ### (3) Select the project(s) to install.
70 |
71 | Select the project(s) that you want to install. This is maybe just the one .exe project in your solution.
72 | Since dependencies are detected automatically and don't need to be selected explicitly, they are not shown in the list; also test projects are hidden by default.
73 | However if you think some project is missing here, check the "Show all projects" check box to see every project of the solution.
74 |
75 | Every project you select will be automatically added to the WiX projects references, so the build order will be correct and you can use project reference variables in the WiX project.
76 |
77 | ### (4) Create the directory mappings.
78 |
79 | If you projects need to deploy files into subdirectories of the ```INSTALLFOLDER```, you need to define or map them here.
80 | If you are starting with an empty project, just click on the "+" in the rightmost column of each directory to create the WiX definitions.
81 | If there are already directories defined in the WiX project, a combo box will appear where you can select the directory that maps to the projects output folder.
82 |
83 | ### (5) Create the file mappings.
84 |
85 | The file mappings list shows all output from the selected projects.
86 | If you are starting with an empty WiX project, all files will be shown in red and the state is "Unmapped".
87 | You can select all files and click on the "+" button in the rightmost column to create all files entries.
88 |
89 | If you are editing an existing WiX project that you have created manually or e.g. with the "Harvest" tool, files might also appear as "Unique" or "Ambiguous".
90 | "Unique" means that there is only one file with that name in the whole solution, so it probably matches the file to be installed. Click on the "?" button in the rightmost column to confirm the match.
91 | "Ambiguous" means that there are several files with the same name. Use the combo box to select the matching file.
92 |
93 | ### Post processing
94 |
95 | #### Save all files
96 | Wax adds a new file to your setup project, named ```.wax```. This file stores all configurations you have made in the above steps. Make sure to save this file with your project, since it contains all information you need to maintain the project later. Also make sure the WiX files (.wxs, .wxi) are saved.
97 |
98 | #### Check your feature tree
99 | Wax will add a ComponentGroupRef node for all component groups it creates to the first feature it finds in your project. If you have just one feature defined, this will be fine; if you have more than one feature in your setup project, copy or move the entries as desired.
100 |
101 | ```
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | ```
110 |
111 | Now you should be able to build the setup project.
112 |
113 | Powered by 
114 | Support this Project: 
115 |
--------------------------------------------------------------------------------
/ReleaseNotes.md:
--------------------------------------------------------------------------------
1 | 1.7:
2 | - Fix #76: Prerequisites outdated
3 | - Fix color scheme
4 |
5 | 1.6:
6 | - Detect new SDK style test projects: all projects referencing MSTest.TestFramework.
7 | - Drop support of VS2017.
8 | - Support of VS2022.
9 | - Improve resolving of binary source dir by favoring top level project output.
10 | - Improve display of implicit selected items.
11 | - UX improvements
12 |
13 | 1.5:
14 | - Support dark mode
15 | - Allow filtering of project items
16 | - Fix #69: Implicit selected projects are not detected in netstandard format projects
17 |
18 | 1.4:
19 | - Performance improvements.
20 |
21 | 1.3:
22 | - Support VS2019
23 |
24 | 1.2:
25 | - Ignore VS references for assemblies, only rely on the real references of the target assemblies.
26 | - Detect late bound XAML references
27 |
28 | 1.1:
29 | - use new version numbering scheme compliant with build agent auto-numbering
30 |
31 | 1.0.27.0:
32 | - Fix second tier reference lookup.
33 | - Show feature mappings.
34 | - Make localization satellites optional.
35 | - Fix #48: EnvDTE.Project.Dte may throw on imcomplete Project implementations.
36 | - Fix #44: outdated document link
37 |
38 | 1.0.26.0:
39 | - Fix #6: null reference exception on project with invalid project references
40 |
41 | 1.0.25.0:
42 | - Fix #6: Another flavor of this issue occured.
43 | - Fix #41: support WiX v4 templates.
44 |
45 | 1.0.24.0:
46 | - Fix #42: EnvDTE.ProjectItem throws null ref exception when item has been removed
47 |
48 | 1.0.23.0:
49 | - Update packages to align with other extensions (to avoid https://connect.microsoft.com/VisualStudio/feedback/details/2993889/)
50 |
51 | 1.0.22.0:
52 | - Fix undefined behavior when not all projects have been built yet and project output is not available.
53 |
54 | 1.0.21.0:
55 | - Added french translation
56 | - Make second tier sattelite dlls optional.
57 |
58 | 1.0.20.0:
59 | - #13, #21: Corectly handle output folder for non-default output locations, e.g. WebApi projects.
60 | - #15, #26, #29: Include second tier references and their sattelite dlls.
61 | - #17, #20: Improve readablity in dark theme.
62 | - #19: Prefix names of directories if they match well known property names to avoid conflicts.
63 | - #23: Generated ComponentGroupRef nodes sometimes do not appear.
64 | - Added Chinese and German translation.
65 |
66 | 1.0.19.0:
67 | - Fix #11: Handle deep directory structures
68 | - Fix #12: Allow DirectoryNodes without name to support merge modules with the "MergeRedirectFolder"
69 | - Format XML when adding new nodes.
70 |
71 | - 1.0.18.0:
72 | - Fix #10: VSIX installer problem with .net framework version
73 |
74 | 1.0.17.0:
75 | - Fix #9: VS does not always reliably detect the installation of Wix
76 |
77 | 1.0.16.0:
78 | - Support VS15 RC
79 |
80 | 1.0.15.0:
81 | - Fix #6: Possible null ref exception when a project has no valid references
82 |
83 | 1.0.14.0
84 | - Fix #3: No vertical scrollbar in directory pane.
85 |
86 | 1.0.13.0
87 | - Fix http://waxsetupeditor.codeplex.com/workitem/4692
88 |
89 | 1.0.12.0
90 | - Support VS15
91 |
92 | 1.0.11.0
93 | - WI4692: Somtimes VS throws exceptions when enumerating project references.
94 | - Exclude project references that are not marked as copy local
95 |
96 | 1.0.10.0
97 | - WI4656: Visual Studio crashes on GetSubProjects
98 | - WI4662: Path comparison should not be case sensitive
99 |
100 | 1.0.9.0
101 | - WI4655: Format XML
102 |
103 | 1.0.8.0
104 | - Fix missing XML-declaration element in .wxs file.
105 | - New icon, internal fixes.
106 |
107 | 1.0.7.0
108 | - WI4586: Suggestion for better file source determination. => Projects references are added and project reference variables used to locate the files.
109 | - WI4599: Sub folders not copied => Wax now creates the ComponentGroupRef entries.
110 |
111 | 1.0.6.0
112 | - Generate default defines for project output folders.
113 | - Improve UX in project lists.
114 |
115 | 1.0.5.0
116 | - WI1408: Fix missing scrollbar issue.
117 | - Add documentation.
118 |
119 | 1.0.4.0
120 | - Improve UI
121 | - Unmapped items can be removed
122 |
123 | 1.0.3.0
124 | - Id's may not contain characters except letters, digits, '.', '_'
125 |
126 | 1.0.2.0
127 | - Deployment of symbols optional per project.
128 | - Show unmapped files.
129 | - Sort projects by folder/name.
130 |
131 | 1.0.1.0
132 | - Make it compile and run with VS2010 too.
--------------------------------------------------------------------------------
/Screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tom-englert/Wax/57dd8d47972869363733e66712cf813d3efec7d8/Screenshot.png
--------------------------------------------------------------------------------
/Wax.Model/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Wax.Model/FodyWeavers.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Used to control if the On_PropertyName_Changed feature is enabled.
12 |
13 |
14 |
15 |
16 | Used to control if the Dependent properties feature is enabled.
17 |
18 |
19 |
20 |
21 | Used to control if the IsChanged property feature is enabled.
22 |
23 |
24 |
25 |
26 | Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.
27 |
28 |
29 |
30 |
31 | Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.
32 |
33 |
34 |
35 |
36 | Used to control if equality checks should use the Equals method resolved from the base class.
37 |
38 |
39 |
40 |
41 | Used to control if equality checks should use the static Equals method resolved from the base class.
42 |
43 |
44 |
45 |
46 | Used to turn off build warnings from this weaver.
47 |
48 |
49 |
50 |
51 | Used to turn off build warnings about mismatched On_PropertyName_Changed methods.
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
62 |
63 |
64 |
65 |
66 | A comma-separated list of error codes that can be safely ignored in assembly verification.
67 |
68 |
69 |
70 |
71 | 'false' to turn off automatic generation of the XML Schema file.
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/Wax.Model/Mapping/DirectoryMapping.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Mapping
2 | {
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Windows.Input;
6 |
7 | using PropertyChanged;
8 |
9 | using tomenglertde.Wax.Model.Wix;
10 |
11 | using TomsToolbox.Wpf;
12 |
13 | [AddINotifyPropertyChangedInterface]
14 | public class DirectoryMapping
15 | {
16 | private readonly WixProject _wixProject;
17 |
18 | public DirectoryMapping(string directory, WixProject wixProject, IList unmappedNodes)
19 | {
20 | Directory = directory;
21 | _wixProject = wixProject;
22 | Id = wixProject.GetDirectoryId(directory);
23 | UnmappedNodes = unmappedNodes;
24 |
25 | MappedNode = wixProject.DirectoryNodes.FirstOrDefault(node => node.Id == Id);
26 | }
27 |
28 | public string Directory { get; }
29 |
30 | public string Id { get; }
31 |
32 | public IList UnmappedNodes { get; }
33 |
34 | public ICommand AddDirectoryCommand => new DelegateCommand(CanAddDirectory, AddDirectory);
35 |
36 | public ICommand ClearMappingCommand => new DelegateCommand(CanClearMapping, ClearMapping);
37 |
38 | public WixDirectoryNode? MappedNodeSetter
39 | {
40 | get => null;
41 | set
42 | {
43 | if (value != null)
44 | {
45 | MappedNode = value;
46 | }
47 | }
48 | }
49 |
50 | [OnChangedMethod(nameof(OnMappedNodeChanged))]
51 | public WixDirectoryNode? MappedNode { get; set; }
52 |
53 | private void OnMappedNodeChanged(WixDirectoryNode? oldValue, WixDirectoryNode? newValue)
54 | {
55 | if (oldValue != null)
56 | {
57 | UnmappedNodes.Add(oldValue);
58 | _wixProject.UnmapDirectory(Directory);
59 | }
60 |
61 | if (newValue != null)
62 | {
63 | UnmappedNodes.Remove(newValue);
64 | _wixProject.MapDirectory(Directory, newValue);
65 | }
66 | }
67 |
68 | private void AddDirectory()
69 | {
70 | MappedNode = _wixProject.AddDirectoryNode(Directory);
71 | }
72 |
73 | private bool CanAddDirectory()
74 | {
75 | return (MappedNode == null);
76 | }
77 |
78 | private void ClearMapping()
79 | {
80 | MappedNode = null;
81 | }
82 |
83 | private bool CanClearMapping()
84 | {
85 | return (MappedNode != null) && (!_wixProject.HasDefaultDirectoryId(this));
86 | }
87 | }
88 | }
--------------------------------------------------------------------------------
/Wax.Model/Mapping/FeatureMapping.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Mapping
2 | {
3 | using System.Collections.Generic;
4 |
5 | using tomenglertde.Wax.Model.VisualStudio;
6 | using tomenglertde.Wax.Model.Wix;
7 |
8 | public class FeatureMapping
9 | {
10 | public FeatureMapping(WixFeatureNode featureNode, ICollection installedFiles,
11 | ICollection projects, ICollection requiredProjectOutputs, ICollection missingProjectOutputs)
12 | {
13 | FeatureNode = featureNode;
14 | InstalledFiles = installedFiles;
15 | Projects = projects;
16 | RequiredProjectOutputs = requiredProjectOutputs;
17 | MissingProjectOutputs = missingProjectOutputs;
18 | }
19 |
20 | public WixFeatureNode FeatureNode { get; }
21 |
22 | public ICollection InstalledFiles { get; }
23 |
24 | public ICollection Projects { get; }
25 |
26 | public ICollection RequiredProjectOutputs { get; }
27 |
28 | public ICollection MissingProjectOutputs { get; }
29 |
30 | public FeatureMapping? Parent { get; set; }
31 |
32 | public ICollection Children { get; } = new List();
33 | }
34 | }
--------------------------------------------------------------------------------
/Wax.Model/Mapping/FileMapping.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Mapping
2 | {
3 | using System;
4 | using System.Collections;
5 | using System.Collections.Generic;
6 | using System.Collections.ObjectModel;
7 | using System.Collections.Specialized;
8 | using System.IO;
9 | using System.Linq;
10 | using System.Windows.Input;
11 |
12 | using PropertyChanged;
13 |
14 | using tomenglertde.Wax.Model.VisualStudio;
15 | using tomenglertde.Wax.Model.Wix;
16 |
17 | using TomsToolbox.ObservableCollections;
18 | using TomsToolbox.Wpf;
19 |
20 | [AddINotifyPropertyChangedInterface]
21 | public class FileMapping
22 | {
23 | private readonly ProjectOutputGroup _projectOutputGroup;
24 | private readonly ObservableCollection _allUnmappedProjectOutputs;
25 | private readonly ObservableFilteredCollection _unmappedProjectOutputs;
26 | private readonly WixProject _wixProject;
27 | private readonly IList _allUnmappedFiles;
28 | private readonly ObservableFilteredCollection _unmappedFiles;
29 |
30 | public FileMapping(ProjectOutputGroup projectOutputGroup, ObservableCollection allUnmappedProjectOutputs, WixProject wixProject, IList allUnmappedFiles)
31 | {
32 | _projectOutputGroup = projectOutputGroup;
33 | _allUnmappedProjectOutputs = allUnmappedProjectOutputs;
34 | _wixProject = wixProject;
35 | _allUnmappedFiles = allUnmappedFiles;
36 |
37 | Id = wixProject.GetFileId(TargetName);
38 |
39 | MappedNode = wixProject.FileNodes.FirstOrDefault(node => node.Id == Id);
40 |
41 | _unmappedProjectOutputs = new ObservableFilteredCollection(_allUnmappedProjectOutputs, item => string.Equals(item?.FileName, DisplayName, StringComparison.OrdinalIgnoreCase));
42 | _unmappedProjectOutputs.CollectionChanged += UnmappedProjectOutputs_CollectionChanged;
43 |
44 | _unmappedFiles = new ObservableFilteredCollection(allUnmappedFiles, item => string.Equals(item?.Node.Name, DisplayName, StringComparison.OrdinalIgnoreCase));
45 | _unmappedFiles.CollectionChanged += UnmappedNodes_CollectionChanged;
46 |
47 | UpdateMappingState();
48 | }
49 |
50 | public string DisplayName => _projectOutputGroup.FileName;
51 |
52 | public string Id { get; }
53 |
54 | public string UniqueName => _projectOutputGroup.TargetName;
55 |
56 | public string Extension => Path.GetExtension(_projectOutputGroup.TargetName);
57 |
58 | public string TargetName => _projectOutputGroup.TargetName;
59 |
60 | public string SourceName => _projectOutputGroup.SourceName;
61 |
62 | public IList UnmappedNodes => _unmappedFiles;
63 |
64 | [DoNotNotify]
65 | public ICommand AddFileCommand => new DelegateCommand(_ => CanAddFile(), AddFile);
66 |
67 | [DoNotNotify]
68 | public ICommand ClearMappingCommand => new DelegateCommand(_ => CanClearMapping(), ClearMapping);
69 |
70 | [DoNotNotify]
71 | public ICommand ResolveFileCommand => new DelegateCommand(_ => CanResolveFile(), ResolveFile);
72 |
73 | public Project Project =>
74 | _projectOutputGroup.ProjectOutputs
75 | .Select(output => output.Project)
76 | .SortByRelevance()
77 | .First();
78 |
79 | public Project TopLevelProject
80 | {
81 | get
82 | {
83 | var project = Project;
84 | while (true)
85 | {
86 | var referencedBy = project.ImplicitSelectedByProjects.SortByRelevance().FirstOrDefault();
87 | if (referencedBy == null)
88 | return project;
89 | project = referencedBy;
90 | }
91 | }
92 | }
93 |
94 | public WixFileNode? MappedNodeSetter
95 | {
96 | get => null;
97 | set
98 | {
99 | if (value != null)
100 | {
101 | MappedNode = value;
102 | }
103 | }
104 | }
105 |
106 | [OnChangedMethod(nameof(OnMappedNodeChanged))]
107 | public WixFileNode? MappedNode { get; set; }
108 |
109 | private void OnMappedNodeChanged(WixFileNode? oldValue, WixFileNode? newValue)
110 | {
111 | if (oldValue != null)
112 | {
113 | _allUnmappedFiles.Add(new UnmappedFile(oldValue, _allUnmappedFiles));
114 | _wixProject.UnmapFile(TargetName);
115 | _allUnmappedProjectOutputs.Add(_projectOutputGroup);
116 | }
117 |
118 | if (newValue != null)
119 | {
120 | var unmappedFile = _allUnmappedFiles.FirstOrDefault(file => Equals(file.Node, newValue));
121 | _allUnmappedFiles.Remove(unmappedFile);
122 | _wixProject.MapFile(TargetName, newValue);
123 | _allUnmappedProjectOutputs.Remove(_projectOutputGroup);
124 | }
125 |
126 | UpdateMappingState();
127 | }
128 |
129 | public MappingState MappingState { get; set; }
130 |
131 | private void UnmappedNodes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
132 | {
133 | UpdateMappingState();
134 | }
135 |
136 | private void UnmappedProjectOutputs_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
137 | {
138 | UpdateMappingState();
139 | }
140 |
141 | private bool CanAddFile()
142 | {
143 | return (MappedNode == null) && !_unmappedFiles.Any();
144 | }
145 |
146 | private static void AddFile(IEnumerable selectedItems)
147 | {
148 | selectedItems.Cast().ToList().ForEach(fileMapping => fileMapping.AddFile());
149 | }
150 |
151 | private void AddFile()
152 | {
153 | if (CanAddFile())
154 | {
155 | MappedNode = _wixProject.AddFileNode(this);
156 | }
157 | }
158 |
159 | private bool CanClearMapping()
160 | {
161 | return (MappedNode != null) && (!_wixProject.HasDefaultFileId(this));
162 | }
163 |
164 | private static void ClearMapping(IEnumerable selectedItems)
165 | {
166 | selectedItems.Cast().ToList().ForEach(fileMapping => fileMapping.ClearMapping());
167 | }
168 |
169 | private void ClearMapping()
170 | {
171 | if (CanClearMapping())
172 | {
173 | MappedNode = null;
174 | }
175 | }
176 |
177 | private bool CanResolveFile()
178 | {
179 | return (MappedNode == null) && (_unmappedFiles.Count == 1);
180 | }
181 |
182 | private static void ResolveFile(IEnumerable selectedItems)
183 | {
184 | selectedItems.Cast().ToList().ForEach(fileMapping => fileMapping.ResolveFile());
185 | }
186 |
187 | private void ResolveFile()
188 | {
189 | if (CanResolveFile())
190 | {
191 | MappedNode = _unmappedFiles[0];
192 | }
193 | }
194 |
195 | private void UpdateMappingState()
196 | {
197 | if (MappedNode != null)
198 | {
199 | MappingState = MappingState.Resolved;
200 | return;
201 | }
202 |
203 | switch (_unmappedFiles.Count)
204 | {
205 | case 0:
206 | MappingState = MappingState.Unmapped;
207 | return;
208 |
209 | case 1:
210 | if (_unmappedProjectOutputs.Count == 1)
211 | {
212 | MappingState = MappingState.Unique;
213 | return;
214 | }
215 | break;
216 | }
217 |
218 | MappingState = MappingState.Ambiguous;
219 | }
220 | }
221 | }
--------------------------------------------------------------------------------
/Wax.Model/Mapping/MappingState.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Mapping
2 | {
3 | public enum MappingState
4 | {
5 | Unmapped,
6 | Ambiguous,
7 | Unique,
8 | Resolved
9 | }
10 | }
--------------------------------------------------------------------------------
/Wax.Model/Mapping/UnmappedFile.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Mapping
2 | {
3 | using System.Collections.Generic;
4 | using System.Windows.Input;
5 |
6 | using tomenglertde.Wax.Model.Wix;
7 |
8 | using TomsToolbox.Wpf;
9 |
10 | public class UnmappedFile
11 | {
12 | private readonly IList _allUnmappedFiles;
13 |
14 | public UnmappedFile(WixFileNode node, IList allUnmappedFiles)
15 | {
16 | Node = node;
17 | _allUnmappedFiles = allUnmappedFiles;
18 | }
19 |
20 | public ICommand DeleteCommand => new DelegateCommand(Delete);
21 |
22 | public WixFileNode Node { get; }
23 |
24 | public WixFileNode ToWixFileNode()
25 | {
26 | return Node;
27 | }
28 |
29 | public static implicit operator WixFileNode?(UnmappedFile? file) => file?.Node;
30 |
31 | private void Delete()
32 | {
33 | _allUnmappedFiles.Remove(this);
34 | Node.Remove();
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Wax.Model/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Markup;
2 |
3 | [assembly: XmlnsDefinition("urn:wax", "tomenglertde.Wax.Model")]
4 | [assembly: XmlnsDefinition("urn:wax", "tomenglertde.Wax.Model.Mapping")]
5 | [assembly: XmlnsDefinition("urn:wax", "tomenglertde.Wax.Model.Tools")]
6 | [assembly: XmlnsDefinition("urn:wax", "tomenglertde.Wax.Model.VisualStudio")]
7 | [assembly: XmlnsDefinition("urn:wax", "tomenglertde.Wax.Model.Wix")]
8 |
--------------------------------------------------------------------------------
/Wax.Model/Tools/AssemblyHelper.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Tools
2 | {
3 | using System;
4 | using System.Collections;
5 | using System.Collections.Generic;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Reflection;
9 | using System.Resources;
10 |
11 | using Baml;
12 |
13 | using Mono.Cecil;
14 |
15 | using TomsToolbox.Essentials;
16 |
17 | public static class AssemblyHelper
18 | {
19 | private static readonly Dictionary _referenceCache = new(StringComparer.OrdinalIgnoreCase);
20 | private static readonly Dictionary _directoryCache = new(StringComparer.OrdinalIgnoreCase);
21 |
22 | public static IReadOnlyCollection FindReferences(string target, string outputDirectory)
23 | {
24 | var timeStamp = File.GetLastWriteTime(target);
25 |
26 | if (_referenceCache.TryGetValue(target, out var cacheEntry) && (cacheEntry.TimeStamp == timeStamp))
27 | {
28 | return cacheEntry.References;
29 | }
30 |
31 | var existingAssemblies = FindExistingAssemblies(outputDirectory);
32 |
33 | var references = FindReferences(target, existingAssemblies);
34 |
35 | _referenceCache[target] = new ReferenceCacheEntry(references, timeStamp);
36 |
37 | return references;
38 | }
39 |
40 | private static Dictionary FindExistingAssemblies(string outputDirectory)
41 | {
42 | var folder = new DirectoryInfo(outputDirectory);
43 | var files = folder.GetFiles("*.dll");
44 | var hash = files.Select(file => file.LastWriteTime.GetHashCode()).Aggregate(0, HashCode.Aggregate);
45 |
46 | Dictionary existingAssemblies;
47 |
48 | if (_directoryCache.TryGetValue(folder.FullName, out var directoryCacheEntry) && (directoryCacheEntry.Hash == hash))
49 | {
50 | existingAssemblies = directoryCacheEntry.AssemblyNames;
51 | }
52 | else
53 | {
54 | existingAssemblies = files
55 | .Select(file => file.FullName)
56 | .Select(TryGetAssemblyName)
57 | .ExceptNullItems()
58 | .ToDictionary(assemblyName => assemblyName.Name);
59 |
60 | _directoryCache[folder.FullName] = new DirectoryCacheEntry(existingAssemblies, hash);
61 | }
62 |
63 | return existingAssemblies;
64 | }
65 |
66 | private static AssemblyName? TryGetAssemblyName(string assemblyFile)
67 | {
68 | try
69 | {
70 | return AssemblyName.GetAssemblyName(assemblyFile);
71 | }
72 | catch
73 | {
74 | return null;
75 | }
76 | }
77 |
78 | private static IReadOnlyCollection FindReferences(string target, IDictionary existingAssemblies)
79 | {
80 | try
81 | {
82 | var assembly = ModuleDefinition.ReadModule(target);
83 |
84 | var usedAssemblies = FindXamlReferences(existingAssemblies, assembly);
85 |
86 | var referencedAssemblyNames = assembly.AssemblyReferences
87 | .Select(item => item?.Name)
88 | .ExceptNullItems()
89 | .Select(existingAssemblies.GetValueOrDefault)
90 | .ExceptNullItems();
91 |
92 | usedAssemblies.AddRange(referencedAssemblyNames);
93 |
94 | return usedAssemblies.ToList().AsReadOnly();
95 | }
96 | catch
97 | {
98 | return Array.Empty();
99 | }
100 | }
101 |
102 | private static HashSet FindXamlReferences(IDictionary existingAssemblies, ModuleDefinition assembly)
103 | {
104 | var assemblyResources = assembly.Resources?.OfType().FirstOrDefault(res => res.Name?.EndsWith("g.resources", StringComparison.Ordinal) == true);
105 |
106 | var usedAssemblies = new HashSet();
107 |
108 | if (assemblyResources == null)
109 | return usedAssemblies;
110 |
111 | var resourceStream = assemblyResources.GetResourceStream();
112 |
113 | if (resourceStream == null)
114 | return usedAssemblies;
115 |
116 | using (var resourceReader = new ResourceReader(resourceStream))
117 | {
118 | foreach (DictionaryEntry entry in resourceReader)
119 | {
120 | if ((entry.Key as string)?.EndsWith(".baml", StringComparison.Ordinal) != true)
121 | continue;
122 |
123 | if (entry.Value is not Stream bamlStream)
124 | continue;
125 |
126 | var records = Baml.ReadDocument(bamlStream);
127 |
128 | foreach (var name in records.OfType().Select(ai => new AssemblyName(ai.AssemblyFullName)))
129 | {
130 | if (existingAssemblies.TryGetValue(name.Name, out var assemblyName) && ((name.Version == null) || (name.Version <= assemblyName.Version)))
131 | {
132 | usedAssemblies.Add(assemblyName);
133 | }
134 | }
135 | }
136 | }
137 |
138 | return usedAssemblies;
139 | }
140 |
141 | private class ReferenceCacheEntry
142 | {
143 | public ReferenceCacheEntry(IReadOnlyCollection references, DateTime timeStamp)
144 | {
145 | References = references;
146 | TimeStamp = timeStamp;
147 | }
148 |
149 | public IReadOnlyCollection References { get; }
150 |
151 | public DateTime TimeStamp { get; }
152 | }
153 |
154 | private class DirectoryCacheEntry
155 | {
156 | public DirectoryCacheEntry(Dictionary assemblyNames, int hash)
157 | {
158 | AssemblyNames = assemblyNames;
159 | Hash = hash;
160 | }
161 |
162 | public Dictionary AssemblyNames { get; }
163 |
164 | public int Hash { get; }
165 | }
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/Wax.Model/Tools/SerializerExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Tools
2 | {
3 | using System.Globalization;
4 | using System.IO;
5 | using System.Text;
6 | using System.Xml;
7 | using System.Xml.Serialization;
8 |
9 | public static class SerializerExtensions
10 | {
11 | public static T Deserialize(this string? data) where T : class, new()
12 | {
13 | if (string.IsNullOrEmpty(data))
14 | return new T();
15 |
16 | var serializer = new XmlSerializer(typeof(T));
17 |
18 | try
19 | {
20 | var xmlReader = new XmlTextReader(new StringReader(data));
21 |
22 | if (serializer.CanDeserialize(xmlReader))
23 | return (serializer.Deserialize(xmlReader) as T) ?? new T();
24 | }
25 | catch
26 | {
27 | // file is corrupt
28 | }
29 |
30 | return new T();
31 | }
32 |
33 | public static string Serialize(this T value)
34 | {
35 | var result = new StringBuilder();
36 |
37 | var serializer = new XmlSerializer(typeof(T));
38 |
39 | using (var stringWriter = new StringWriter(result, CultureInfo.InvariantCulture))
40 | {
41 | serializer.Serialize(stringWriter, value);
42 | }
43 |
44 | return result.ToString();
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Wax.Model/Tools/XmlExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Tools
2 | {
3 | using System.Linq;
4 | using System.Xml;
5 | using System.Xml.Linq;
6 |
7 | public static class XmlExtensions
8 | {
9 | public static void RemoveSelfAndWhiteSpace(this XElement element)
10 | {
11 | if ((element.PreviousNode is XText previous) && string.IsNullOrWhiteSpace(previous.Value))
12 | {
13 | previous.Remove();
14 | }
15 |
16 | element.Remove();
17 | }
18 |
19 | public static void AddWithFormatting(this XElement parent, XElement item)
20 | {
21 | var firstNode = parent.FirstNode;
22 | var lastNode = parent.LastNode;
23 |
24 | if ((firstNode?.NodeType == XmlNodeType.Text) && (lastNode != null))
25 | {
26 | var whiteSpace = "\n" + ((firstNode as XText)?.Value?.Split('\n').LastOrDefault() ?? new string(' ', lastNode.GetDefaultIndent()));
27 |
28 | lastNode.AddBeforeSelf(new XText(whiteSpace), item);
29 | }
30 | else
31 | {
32 | var previousNode = parent.PreviousNode;
33 |
34 | var whiteSpace = "\n" + ((previousNode as XText)?.Value?.Split('\n').LastOrDefault() ?? new string(' ', parent.GetDefaultIndent()));
35 |
36 | parent.Add(new XText(whiteSpace + " "), item, new XText(whiteSpace));
37 | }
38 | }
39 |
40 | private static int GetDefaultIndent(this XObject item)
41 | {
42 | return item.Parent?.GetDefaultIndent() ?? 0 + 2;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Wax.Model/VisualStudio/BuildFileGroups.cs:
--------------------------------------------------------------------------------
1 | // ReSharper disable UnusedMember.Global
2 | namespace tomenglertde.Wax.Model.VisualStudio
3 | {
4 | using System;
5 |
6 | [Flags]
7 | public enum BuildFileGroups
8 | {
9 | None = 0,
10 | LocalizedResourceDlls = 1,
11 | XmlSerializer = 2,
12 | ContentFiles = 4,
13 | Built = 8,
14 | SourceFiles = 16,
15 | Symbols = 32,
16 | Documentation = 64
17 | }
18 | }
--------------------------------------------------------------------------------
/Wax.Model/VisualStudio/DteExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.VisualStudio
2 | {
3 | using System;
4 | using System.Collections;
5 | using System.Collections.Generic;
6 | using System.Diagnostics;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Runtime.InteropServices;
10 | using System.Xml.Linq;
11 |
12 | using Microsoft.VisualStudio.Shell.Flavor;
13 | using Microsoft.VisualStudio.Shell.Interop;
14 |
15 | using IServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
16 |
17 | internal static class DteExtensions
18 | {
19 | ///
20 | /// Gets all projects in the solution.
21 | ///
22 | /// The solution.
23 | /// The projects.
24 | public static IReadOnlyCollection GetProjects(this EnvDTE.Solution solution)
25 | {
26 | var items = new List();
27 |
28 | var projects = solution.Projects;
29 |
30 | if (projects == null)
31 | return items;
32 |
33 | for (var i = 1; i <= projects.Count; i++)
34 | {
35 | try
36 | {
37 | var project = projects.Item(i);
38 | if (project == null)
39 | continue;
40 |
41 | items.Add(project);
42 |
43 | project.ProjectItems.GetSubProjects(items);
44 | }
45 | catch
46 | {
47 | Trace.TraceError("Error loading project #" + i);
48 | }
49 | }
50 |
51 | return items;
52 | }
53 |
54 | private static void GetSubProjects(this IEnumerable? projectItems, ICollection items)
55 | {
56 | if (projectItems == null)
57 | return;
58 |
59 | foreach (var projectItem in projectItems.OfType())
60 | {
61 | projectItem.GetSubProjects(items);
62 | }
63 | }
64 |
65 | private static void GetSubProjects(this EnvDTE.ProjectItem? projectItem, ICollection items)
66 | {
67 | var subProject = projectItem?.SubProject;
68 |
69 | if (subProject == null)
70 | return;
71 |
72 | items.Add(subProject);
73 |
74 | subProject.ProjectItems.GetSubProjects(items);
75 | }
76 |
77 | public static IEnumerable EnumerateAllProjectItems(this EnvDTE.Project project)
78 | {
79 | if (project.ProjectItems == null)
80 | yield break;
81 |
82 | foreach (var item in project.ProjectItems.OfType())
83 | {
84 | yield return item;
85 |
86 | foreach (var subItem in EnumerateProjectItems(item))
87 | {
88 | yield return subItem;
89 | }
90 | }
91 | }
92 |
93 | private static IEnumerable EnumerateProjectItems(EnvDTE.ProjectItem projectItem)
94 | {
95 | if (projectItem.ProjectItems == null)
96 | yield break;
97 |
98 | foreach (var item in projectItem.ProjectItems.OfType())
99 | {
100 | yield return item;
101 |
102 | foreach (var subItem in EnumerateProjectItems(item))
103 | {
104 | yield return subItem;
105 | }
106 | }
107 | }
108 |
109 | public static string GetProjectTypeGuids(this EnvDTE.Project proj)
110 | {
111 | try
112 | {
113 | var dte = proj.TryGetDte();
114 | if (dte == null)
115 | return string.Empty;
116 |
117 | var solution = GetService(dte);
118 |
119 | if (solution == null)
120 | return string.Empty;
121 |
122 | var result = solution.GetProjectOfUniqueName(proj.UniqueName, out IVsHierarchy hierarchy);
123 |
124 | if (result == 0)
125 | {
126 | if (hierarchy is IVsAggregatableProjectCorrected aggregatableProject)
127 | {
128 | result = aggregatableProject.GetAggregateProjectTypeGuids(out string projectTypeGuids);
129 |
130 | if ((result == 0) && (projectTypeGuids != null))
131 | return projectTypeGuids;
132 |
133 | }
134 | }
135 | }
136 | catch
137 | {
138 | // internal error
139 | }
140 |
141 | return string.Empty;
142 | }
143 |
144 | private static T? GetService(object serviceProvider) where T : class
145 | {
146 | return (T?)GetService((IServiceProvider)serviceProvider, typeof(T).GUID);
147 | }
148 |
149 | private static object? GetService(IServiceProvider serviceProvider, Guid guid)
150 | {
151 | var hr = serviceProvider.QueryService(guid, guid, out var serviceHandle);
152 |
153 | if (hr != 0)
154 | {
155 | Marshal.ThrowExceptionForHR(hr);
156 | }
157 | else if (!serviceHandle.Equals(IntPtr.Zero))
158 | {
159 | var service = Marshal.GetObjectForIUnknown(serviceHandle);
160 | Marshal.Release(serviceHandle);
161 | return service;
162 | }
163 |
164 | return null;
165 | }
166 |
167 | public static string? TryGetFileName(this EnvDTE.ProjectItem projectItem)
168 | {
169 | try
170 | {
171 | // some items report a file count > 0 but don't return a file name!
172 | if (projectItem.FileCount > 0)
173 | {
174 | return projectItem.FileNames[0];
175 | }
176 | }
177 | catch (Exception)
178 | {
179 | // s.a.
180 | }
181 |
182 | return null;
183 | }
184 |
185 | public static XDocument GetXmlContent(this EnvDTE.ProjectItem projectItem, LoadOptions loadOptions)
186 | {
187 | return XDocument.Parse(projectItem.GetContent(), loadOptions);
188 | }
189 |
190 | public static string GetContent(this EnvDTE.ProjectItem projectItem)
191 | {
192 | try
193 | {
194 | if (!projectItem.IsOpen)
195 | projectItem.Open();
196 |
197 | var document = projectItem.Document;
198 |
199 | if (document != null)
200 | {
201 | return GetContent((EnvDTE.TextDocument)document.Object("TextDocument"));
202 | }
203 |
204 | var fileName = projectItem.TryGetFileName();
205 |
206 | if (string.IsNullOrEmpty(fileName))
207 | return string.Empty;
208 |
209 | return File.ReadAllText(fileName);
210 | }
211 | catch (Exception)
212 | {
213 | return string.Empty;
214 | }
215 | }
216 |
217 | private static string GetContent(EnvDTE.TextDocument document)
218 | {
219 | return document.StartPoint.CreateEditPoint().GetText(document.EndPoint);
220 | }
221 |
222 | public static void SetContent(this EnvDTE.ProjectItem projectItem, string text)
223 | {
224 | if (!projectItem.IsOpen)
225 | projectItem.Open(EnvDTE.Constants.vsViewKindCode);
226 |
227 | var document = projectItem.Document;
228 |
229 | if (document != null)
230 | {
231 | SetContent(document, text);
232 | }
233 | else
234 | {
235 | var fileName = projectItem.TryGetFileName();
236 |
237 | if (string.IsNullOrEmpty(fileName))
238 | return;
239 |
240 | File.WriteAllText(fileName, text);
241 | }
242 | }
243 |
244 | private static void SetContent(EnvDTE.Document document, string? text)
245 | {
246 | var textDocument = (EnvDTE.TextDocument)document.Object("TextDocument");
247 |
248 | textDocument.StartPoint.CreateEditPoint().ReplaceText(textDocument.EndPoint, text, 0);
249 | }
250 |
251 | public static object? TryGetObject(this EnvDTE.Project? project)
252 | {
253 | try
254 | {
255 | return project?.Object;
256 | }
257 | catch
258 | {
259 | return null;
260 | }
261 | }
262 |
263 | public static EnvDTE80.DTE2? TryGetDte(this EnvDTE.Project? project)
264 | {
265 | try
266 | {
267 | return project?.DTE as EnvDTE80.DTE2;
268 | }
269 | catch
270 | {
271 | return null;
272 | }
273 | }
274 |
275 | public static bool GetCopyLocal(this VSLangProj.Reference? reference)
276 | {
277 | if (reference == null)
278 | return false;
279 |
280 | try
281 | {
282 | return reference.CopyLocal || (reference.ContainingProject != null);
283 | }
284 | catch
285 | {
286 | return false;
287 | }
288 | }
289 | }
290 | }
291 |
--------------------------------------------------------------------------------
/Wax.Model/VisualStudio/ProjectOutput.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.VisualStudio
2 | {
3 | using System;
4 | using System.IO;
5 |
6 | public class ProjectOutput
7 | {
8 | public ProjectOutput(Project project, string relativeFileName, BuildFileGroups buildFileGroup, string binaryTargetDirectory)
9 | {
10 | Project = project;
11 |
12 | var prefix = binaryTargetDirectory + @"\";
13 |
14 | if ((buildFileGroup != BuildFileGroups.ContentFiles) && relativeFileName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
15 | {
16 | SourceName = relativeFileName.Substring(prefix.Length);
17 | }
18 | else
19 | {
20 | SourceName = relativeFileName;
21 | }
22 |
23 | BuildFileGroup = buildFileGroup;
24 |
25 | TargetName = (BuildFileGroup == BuildFileGroups.ContentFiles) ? SourceName : Path.Combine(binaryTargetDirectory, SourceName);
26 | }
27 |
28 | public ProjectOutput(Project project, string relativeFileName, string binaryTargetDirectory)
29 | {
30 | Project = project;
31 | SourceName = relativeFileName;
32 | TargetName = Path.Combine(binaryTargetDirectory, relativeFileName);
33 | }
34 |
35 | public ProjectOutput(Project project, VSLangProj.Reference reference, string binaryTargetDirectory)
36 | : this(project, Path.GetFileName(reference.Path), binaryTargetDirectory)
37 | {
38 | }
39 |
40 | public string SourceName { get; }
41 |
42 | public string TargetName { get; }
43 |
44 | public Project Project { get; }
45 |
46 | public bool IsReference => BuildFileGroup == BuildFileGroups.None;
47 |
48 | public BuildFileGroups BuildFileGroup { get; }
49 |
50 | public override string ToString()
51 | {
52 | return TargetName;
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Wax.Model/VisualStudio/ProjectOutputGroup.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.VisualStudio
2 | {
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 |
7 | public class ProjectOutputGroup
8 | {
9 | public ProjectOutputGroup(string targetName, IReadOnlyCollection projectOutputs)
10 | {
11 | TargetName = targetName;
12 | ProjectOutputs = projectOutputs;
13 | Projects = new HashSet(projectOutputs.Select(p => p.Project));
14 | }
15 |
16 | public IReadOnlyCollection ProjectOutputs { get; }
17 |
18 | public ICollection Projects { get; }
19 |
20 | public string TargetName { get; }
21 |
22 | public string FileName => Path.GetFileName(TargetName);
23 |
24 | public string SourceName => ProjectOutputs.First().SourceName;
25 |
26 | public override string ToString()
27 | {
28 | return TargetName;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Wax.Model/VisualStudio/ProjectReference.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.VisualStudio
2 | {
3 | using System;
4 | using System.Linq;
5 |
6 | using Equatable;
7 |
8 | [ImplementsEquatable]
9 | public class ProjectReference
10 | {
11 | private readonly Solution _solution;
12 | private readonly VSLangProj.Reference _reference;
13 |
14 | public ProjectReference(Solution solution, VSLangProj.Reference reference)
15 | {
16 | _solution = solution;
17 | _reference = reference;
18 | }
19 |
20 | public Project? SourceProject => _solution.Projects.SingleOrDefault(p => string.Equals(p.UniqueName, _reference.SourceProject?.UniqueName, StringComparison.OrdinalIgnoreCase));
21 |
22 | [Equals]
23 | public string? Identity => _reference.Identity;
24 | }
25 | }
--------------------------------------------------------------------------------
/Wax.Model/VisualStudio/Solution.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.VisualStudio
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 |
7 | using tomenglertde.Wax.Model.Wix;
8 |
9 | public class Solution
10 | {
11 | private readonly EnvDTE.Solution _solution;
12 |
13 | public Solution(EnvDTE.Solution solution)
14 | {
15 | _solution = solution;
16 |
17 | Projects = _solution.GetProjects()
18 | .Select(project => new Project(this, project))
19 | .Where(project => project.IsVsProject)
20 | .OrderBy(project => project.Name)
21 | .ToList()
22 | .AsReadOnly();
23 |
24 | foreach (var project in Projects)
25 | {
26 | if (project == null)
27 | continue;
28 |
29 | foreach (var dependency in project.GetProjectReferences())
30 | {
31 | dependency.SourceProject?.ReferencedBy.Add(project);
32 | }
33 | }
34 |
35 | // Microsoft.Tools.WindowsInstallerXml.VisualStudio.OAWixProject
36 | WixProjects = _solution.GetProjects()
37 | .Where(project => "{930c7802-8a8c-48f9-8165-68863bccd9dd}".Equals(project.Kind, StringComparison.OrdinalIgnoreCase))
38 | .Select(project => new WixProject(this, project))
39 | .Where(project => !project.IsBootstrapper)
40 | .OrderBy(project => project.Name)
41 | .ToList()
42 | .AsReadOnly();
43 | }
44 |
45 | public string? FullName => _solution.FullName;
46 |
47 | public IEnumerable Projects { get; }
48 |
49 | public IEnumerable WixProjects { get; }
50 |
51 | public IEnumerable EnumerateTopLevelProjects => Projects
52 | .Where(project => project.IsTopLevelProject)
53 | .OrderBy(project => project.Name);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Wax.Model/Wax.Model.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | net472
5 | tomenglertde.Wax.Model
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Tools\Baml.cs
14 |
15 |
16 |
17 |
18 | 1.9.0
19 | all
20 |
21 |
22 | 6.6.0
23 | all
24 |
25 |
26 | 2021.3.0
27 | all
28 |
29 |
30 | 1.10.0
31 | all
32 |
33 |
34 |
35 | 15.0.1
36 |
37 |
38 | 0.11.4
39 |
40 |
41 | all
42 | runtime; build; native; contentfiles; analyzers; buildtransitive
43 |
44 |
45 | 3.4.0
46 | all
47 |
48 |
49 |
50 |
51 |
52 | 2.7.4
53 |
54 |
55 |
--------------------------------------------------------------------------------
/Wax.Model/Wix/ProjectConfiguration.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Wix
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Xml.Serialization;
7 |
8 | using TomsToolbox.Essentials;
9 |
10 | [Serializable]
11 | [XmlType("Configuration")]
12 | public class ProjectConfiguration
13 | {
14 | private string[]? _deployProjectNames;
15 |
16 | [XmlArray("DeployedProjects")]
17 | public string[] DeployedProjectNames
18 | {
19 | get => _deployProjectNames ?? Array.Empty();
20 | set => _deployProjectNames = value;
21 | }
22 |
23 | [XmlArray("DirectoryMappings")]
24 | public MappingItem[] DirectoryMappingNames
25 | {
26 | get => DirectoryMappings.Select(item => new MappingItem { Key = item.Key, Value = item.Value }).ToArray();
27 | set => DirectoryMappings = value.ToDictionary(item => item.Key, item => item.Value, StringComparer.OrdinalIgnoreCase);
28 | }
29 |
30 | [XmlArray("FileMappings")]
31 | public MappingItem[] FileMappingNames
32 | {
33 | get => FileMappings.Select(item => new MappingItem { Key = item.Key, Value = item.Value }).ToArray();
34 | set => FileMappings = value.ToDictionary(item => item.Key, item => item.Value, StringComparer.OrdinalIgnoreCase);
35 | }
36 |
37 | [XmlIgnore]
38 | public Dictionary DirectoryMappings { get; private set; } = new Dictionary(StringComparer.OrdinalIgnoreCase);
39 |
40 | [XmlIgnore]
41 | public Dictionary FileMappings { get; private set; } = new Dictionary(StringComparer.OrdinalIgnoreCase);
42 |
43 | [XmlElement("DeploySymbols")]
44 | public bool DeploySymbols { get; set; }
45 |
46 | [XmlElement("DeployLocalizations")]
47 | public bool DeployLocalizations { get; set; } = true;
48 |
49 | [XmlElement("DeployExternalLocalizations")]
50 | public bool DeployExternalLocalizations { get; set; }
51 |
52 | [XmlElement("ExcludedProjectItems")]
53 | public string ExcludedProjectItemsValue
54 | {
55 | get => ExcludedProjectItems.IsNullOrEmpty() ? "-" : ExcludedProjectItems;
56 | set => ExcludedProjectItems = value == "-" ? null : value;
57 | }
58 |
59 | [XmlIgnore]
60 | public string? ExcludedProjectItems { get; set; }
61 | }
62 |
63 | [Serializable]
64 | [XmlType("Item")]
65 | public class MappingItem
66 | {
67 | [XmlAttribute("Key")]
68 | public string Key
69 | {
70 | get;
71 | set;
72 | } = string.Empty;
73 |
74 | [XmlAttribute("Value")]
75 | public string Value
76 | {
77 | get;
78 | set;
79 | } = string.Empty;
80 | }
81 | }
--------------------------------------------------------------------------------
/Wax.Model/Wix/WixComponentGroupNode.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Wix
2 | {
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Xml.Linq;
6 |
7 | using tomenglertde.Wax.Model.Mapping;
8 |
9 | using TomsToolbox.Essentials;
10 |
11 | public class WixComponentGroupNode : WixNode
12 | {
13 | public WixComponentGroupNode(WixSourceFile sourceFile, XElement node)
14 | : base(sourceFile, node)
15 | {
16 | }
17 |
18 | public string? Directory => GetAttribute("Directory");
19 |
20 | public IEnumerable Components => Node
21 | .Descendants(WixNames.ComponentNode)
22 | .Where(node => node.Parent == Node)
23 | .Select(node => node.GetAttribute("Id"))
24 | .Where(id => !string.IsNullOrEmpty(id))
25 | .ExceptNullItems();
26 |
27 | public IEnumerable ComponentRefs => Node
28 | .Descendants(WixNames.ComponentRefNode)
29 | .Where(node => node.Parent == Node)
30 | .Select(node => node.GetAttribute("Id"))
31 | .Where(id => !string.IsNullOrEmpty(id))
32 | .ExceptNullItems();
33 |
34 | public IEnumerable ComponentGroupRefs => Node
35 | .Descendants(WixNames.ComponentGroupRefNode)
36 | .Where(node => node.Parent == Node)
37 | .Select(node => node.GetAttribute("Id"))
38 | .Where(id => !string.IsNullOrEmpty(id))
39 | .ExceptNullItems();
40 |
41 | public WixFileNode AddFileComponent(string id, string name, FileMapping fileMapping)
42 | {
43 | return SourceFile.AddFileComponent(this, id, name, fileMapping);
44 | }
45 |
46 | public IEnumerable EnumerateComponents(IDictionary componentGroupNodes, IDictionary componentNodes)
47 | {
48 | var byComponentGroupRef = ComponentGroupRefs.Select(componentGroupNodes.GetValueOrDefault)
49 | .ExceptNullItems()
50 | .SelectMany(cg => cg.EnumerateComponents(componentGroupNodes, componentNodes));
51 |
52 | var byComponentRef = ComponentRefs.Select(componentNodes.GetValueOrDefault)
53 | .ExceptNullItems();
54 |
55 | var children = Components.Select(componentNodes.GetValueOrDefault)
56 | .ExceptNullItems();
57 |
58 |
59 | return byComponentGroupRef.Concat(byComponentRef).Concat(children);
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/Wax.Model/Wix/WixComponentNode.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Wix
2 | {
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Xml.Linq;
6 |
7 | using TomsToolbox.Essentials;
8 |
9 | public class WixComponentNode : WixNode
10 | {
11 | public WixComponentNode(WixSourceFile sourceFile, XElement node)
12 | : base(sourceFile, node)
13 | {
14 | }
15 |
16 | public string? Directory => GetAttribute("Directory");
17 |
18 | public IEnumerable Files => Node
19 | .Descendants(WixNames.FileNode)
20 | .Where(node => node.Parent == Node)
21 | .Select(node => node.GetAttribute("Id"))
22 | .Where(id => !string.IsNullOrEmpty(id))
23 | .ExceptNullItems();
24 |
25 | public IEnumerable EnumerateFiles(IDictionary fileNodes)
26 | {
27 | return Files.Select(fileNodes.GetValueOrDefault!).ExceptNullItems();
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Wax.Model/Wix/WixDefine.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Wix
2 | {
3 | using System.Linq;
4 | using System.Xml.Linq;
5 |
6 | using Equatable;
7 |
8 | [ImplementsEquatable]
9 | public class WixDefine
10 | {
11 | public WixDefine(WixSourceFile sourceFile, XProcessingInstruction node)
12 | {
13 | SourceFile = sourceFile;
14 | Node = node;
15 | }
16 |
17 | public string Name => Node.Data.Split('=').Select(item => item.Trim()).FirstOrDefault() ?? "";
18 |
19 | [Equals]
20 | public XProcessingInstruction Node { get; }
21 |
22 | public WixSourceFile SourceFile { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Wax.Model/Wix/WixDirectoryNode.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Wix
2 | {
3 | using System.Globalization;
4 | using System.Linq;
5 | using System.Xml.Linq;
6 |
7 | using tomenglertde.Wax.Model.Tools;
8 |
9 | using TomsToolbox.Essentials;
10 |
11 | public class WixDirectoryNode : WixNode
12 | {
13 | private WixDirectoryNode? _parent;
14 |
15 | public WixDirectoryNode(WixSourceFile sourceFile, XElement node)
16 | : base(sourceFile, node)
17 | {
18 | }
19 |
20 | public WixDirectoryNode? Parent => _parent ??= ResolveParent();
21 |
22 | public string Path
23 | {
24 | get
25 | {
26 | var name = Name ?? ".";
27 | return Parent != null ? (Parent.Path + @"\" + name) : name;
28 | }
29 | }
30 |
31 | public WixDirectoryNode AddSubDirectory(string id, string name)
32 | {
33 | var directoryElement = new XElement(WixNames.DirectoryNode, new XAttribute("Id", id), new XAttribute("Name", name));
34 |
35 | Node.AddWithFormatting(directoryElement);
36 |
37 | SourceFile.Save();
38 |
39 | return SourceFile.AddDirectoryNode(directoryElement);
40 | }
41 |
42 | private WixDirectoryNode? ResolveParent()
43 | {
44 | var parentElement = Node.Parent;
45 |
46 | if (parentElement == null)
47 | return null;
48 |
49 | return parentElement.Name.LocalName switch
50 | {
51 | "Directory" or "DirectoryRef" => SourceFile.Project.DirectoryNodes.FirstOrDefault(node => node.Id == parentElement.GetAttribute("Id")),
52 | _ => null,
53 | };
54 | }
55 |
56 | public override string ToString()
57 | {
58 | return string.Format(CultureInfo.CurrentCulture, "{0} ({1})", Id, Path);
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/Wax.Model/Wix/WixFeatureNode.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Wix
2 | {
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Xml.Linq;
6 |
7 | using tomenglertde.Wax.Model.Tools;
8 |
9 | using TomsToolbox.Essentials;
10 |
11 | public class WixFeatureNode : WixNode
12 | {
13 | public WixFeatureNode(WixSourceFile sourceFile, XElement node)
14 | : base(sourceFile, node)
15 | {
16 | }
17 |
18 | public WixFeatureNode? Parent { get; private set; }
19 |
20 | public IEnumerable ComponentGroupRefs => Node
21 | .Descendants(WixNames.ComponentGroupRefNode)
22 | .Where(node => node.Parent == Node)
23 | .Select(node => node.GetAttribute("Id"))
24 | .Where(id => !string.IsNullOrEmpty(id))
25 | .ExceptNullItems();
26 |
27 | public IEnumerable ComponentRefs => Node
28 | .Descendants(WixNames.ComponentRefNode)
29 | .Where(node => node.Parent == Node)
30 | .Select(node => node.GetAttribute("Id"))
31 | .Where(id => !string.IsNullOrEmpty(id))
32 | .ExceptNullItems();
33 |
34 | public IEnumerable Components => Node
35 | .Descendants(WixNames.ComponentNode)
36 | .Where(node => node.Parent == Node)
37 | .Select(node => node.GetAttribute("Id"))
38 | .Where(id => !string.IsNullOrEmpty(id))
39 | .ExceptNullItems();
40 |
41 | public IEnumerable Features => Node
42 | .Descendants(WixNames.FeatureNode)
43 | .Where(node => node.Parent == Node)
44 | .Select(node => node.GetAttribute("Id"))
45 | .Where(id => !string.IsNullOrEmpty(id))
46 | .ExceptNullItems();
47 |
48 | public IEnumerable FeatureRefs => Node
49 | .Descendants(WixNames.FeatureRefNode)
50 | .Where(node => node.Parent == Node)
51 | .Select(node => node.GetAttribute("Id"))
52 | .Where(id => !string.IsNullOrEmpty(id))
53 | .ExceptNullItems();
54 |
55 | public void AddComponentGroupRef(string id)
56 | {
57 | Node.AddWithFormatting(new XElement(WixNames.ComponentGroupRefNode, new XAttribute("Id", id)));
58 | }
59 |
60 | public void BuildTree(IDictionary allFeatures)
61 | {
62 | var children = Features.Concat(FeatureRefs)
63 | .Select(allFeatures.GetValueOrDefault!)
64 | .ExceptNullItems();
65 |
66 | foreach (var child in children)
67 | {
68 | child.Parent = this;
69 | }
70 | }
71 |
72 | public IEnumerable EnumerateInstalledFiles(IDictionary componentGroupNodes, IDictionary componentNodes, IDictionary fileNodes)
73 | {
74 | var byComponentGroupRef = ComponentGroupRefs.Select(componentGroupNodes.GetValueOrDefault!)
75 | .ExceptNullItems()
76 | .SelectMany(cg => cg.EnumerateComponents(componentGroupNodes, componentNodes));
77 |
78 | var byComponentRef = ComponentRefs.Select(componentNodes.GetValueOrDefault!)
79 | .ExceptNullItems();
80 |
81 | var children = Components.Select(componentNodes.GetValueOrDefault!)
82 | .ExceptNullItems();
83 |
84 | var files = byComponentGroupRef.Concat(byComponentRef).Concat(children).SelectMany(c => c?.EnumerateFiles(fileNodes));
85 |
86 | if (Parent != null)
87 | {
88 | files = Parent.EnumerateInstalledFiles(componentGroupNodes, componentNodes, fileNodes).Concat(files);
89 | }
90 |
91 | return files;
92 | }
93 |
94 | public override string ToString()
95 | {
96 | return Id;
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/Wax.Model/Wix/WixFileNode.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Wix
2 | {
3 | using System.Collections.Generic;
4 | using System.Globalization;
5 | using System.Linq;
6 | using System.Xml.Linq;
7 |
8 | using tomenglertde.Wax.Model.Tools;
9 |
10 | using TomsToolbox.Essentials;
11 |
12 | public class WixFileNode : WixNode
13 | {
14 | private readonly IList _collection;
15 | private WixComponentGroupNode? _componentGroup;
16 |
17 | public WixFileNode(WixSourceFile sourceFile, XElement node, IList collection)
18 | : base(sourceFile, node)
19 | {
20 | _collection = collection;
21 | }
22 |
23 | public string? Source => GetAttribute("Source");
24 |
25 | public WixComponentGroupNode? ComponentGroup => _componentGroup ??= ResolveComponentGroup();
26 |
27 | public void Remove()
28 | {
29 | var parentNode = Node.Parent;
30 |
31 | _collection.Remove(this);
32 |
33 | Node.RemoveSelfAndWhiteSpace();
34 |
35 | if (parentNode != null && (parentNode.Name == WixNames.ComponentNode) && !parentNode.Descendants().Any())
36 | {
37 | parentNode.Remove();
38 | }
39 |
40 | SourceFile.Save();
41 | }
42 |
43 | public override string ToString()
44 | {
45 | return string.Format(CultureInfo.CurrentCulture, "{0} ({1}, {2})", Id, Name, Source);
46 | }
47 |
48 | private WixComponentGroupNode? ResolveComponentGroup()
49 | {
50 | var componentNode = Node.Parent;
51 |
52 | var componentGroupNode = componentNode?.Parent;
53 |
54 | if (componentGroupNode == null)
55 | return null;
56 |
57 | var componentGroups = SourceFile.Project.ComponentGroupNodes;
58 |
59 | if (componentGroupNode.Name == WixNames.ComponentGroupNode)
60 | {
61 | _componentGroup = componentGroups.FirstOrDefault(group => group.Node == componentGroupNode);
62 | }
63 | else if (componentGroupNode.Name == WixNames.ComponentGroupRefNode)
64 | {
65 | _componentGroup = componentGroups.FirstOrDefault(group => group.Id == componentGroupNode.GetAttribute("Id"));
66 | }
67 |
68 | return null;
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/Wax.Model/Wix/WixNames.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Wix
2 | {
3 | using System.Xml.Linq;
4 |
5 | public class WixNames
6 | {
7 | private readonly string _namespace;
8 |
9 | public WixNames(string @namespace)
10 | {
11 | _namespace = @namespace;
12 | }
13 |
14 | public XName FileNode => XName.Get("File", _namespace);
15 |
16 | public XName DirectoryNode => XName.Get("Directory", _namespace);
17 |
18 | public XName FragmentNode => XName.Get("Fragment", _namespace);
19 |
20 | public XName DirectoryRefNode => XName.Get("DirectoryRef", _namespace);
21 |
22 | public XName ComponentNode => XName.Get("Component", _namespace);
23 |
24 | public XName ComponentGroupNode => XName.Get("ComponentGroup", _namespace);
25 |
26 | public XName ComponentRefNode => XName.Get("ComponentRef", _namespace);
27 |
28 | public XName ComponentGroupRefNode => XName.Get("ComponentGroupRef", _namespace);
29 |
30 | public XName FeatureNode => XName.Get("Feature", _namespace);
31 |
32 | public XName FeatureRefNode => XName.Get("FeatureRef", _namespace);
33 |
34 | public XName PropertyNode => XName.Get("Property", _namespace);
35 |
36 | public XName RegistrySearch => XName.Get("RegistrySearch", _namespace);
37 |
38 | public XName CustomActionRefNode => XName.Get("CustomActionRef", _namespace);
39 |
40 | public const string Define = "define";
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Wax.Model/Wix/WixNode.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Wix
2 | {
3 | using System.Xml.Linq;
4 |
5 | using Equatable;
6 |
7 | using JetBrains.Annotations;
8 |
9 | using TomsToolbox.Essentials;
10 |
11 | [ImplementsEquatable]
12 | public class WixNode
13 | {
14 | public WixNode(WixSourceFile sourceFile, XElement node)
15 | {
16 | SourceFile = sourceFile;
17 | Node = node;
18 | }
19 |
20 | [Equals]
21 | [UsedImplicitly]
22 | public string Kind => Node.Name.LocalName;
23 |
24 | [Equals]
25 | public string Id => GetAttribute("Id") ?? string.Empty;
26 |
27 | public string? Name => GetAttribute("Name");
28 |
29 | internal XElement Node { get; }
30 |
31 | public WixSourceFile SourceFile { get; }
32 |
33 | public WixNames WixNames => SourceFile.WixNames;
34 |
35 | protected string? GetAttribute(string name)
36 | {
37 | return Node.GetAttribute(name);
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/Wax.Model/Wix/WixSourceFile.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax.Model.Wix
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Collections.ObjectModel;
6 | using System.Diagnostics;
7 | using System.Globalization;
8 | using System.IO;
9 | using System.Linq;
10 | using System.Xml;
11 | using System.Xml.Linq;
12 |
13 | using tomenglertde.Wax.Model.Mapping;
14 | using tomenglertde.Wax.Model.Tools;
15 | using tomenglertde.Wax.Model.VisualStudio;
16 |
17 | public class WixSourceFile
18 | {
19 | private readonly EnvDTE.ProjectItem _projectItem;
20 | private readonly XDocument _xmlFile;
21 | private XDocument _rawXmlFile;
22 | private readonly XElement _root;
23 | private readonly List _fileNodes;
24 | private readonly List _directoryNodes;
25 | private readonly List _componentGroupNodes;
26 | private readonly List _componentNodes;
27 | private readonly List _featureNodes;
28 | private readonly List _defines;
29 |
30 | public WixSourceFile(WixProject project, EnvDTE.ProjectItem projectItem)
31 | {
32 | Project = project;
33 | _projectItem = projectItem;
34 |
35 | try
36 | {
37 | _xmlFile = _projectItem.GetXmlContent(LoadOptions.PreserveWhitespace);
38 | _rawXmlFile = _projectItem.GetXmlContent(LoadOptions.None);
39 | }
40 | catch
41 | {
42 | var placeholder = @"";
43 | _xmlFile = XDocument.Parse(placeholder);
44 | _rawXmlFile = XDocument.Parse(placeholder);
45 | }
46 |
47 | var root = _xmlFile.Root;
48 |
49 | _root = root ?? throw new InvalidDataException("Invalid source file: " + projectItem.TryGetFileName());
50 |
51 | WixNames = new WixNames(root.GetDefaultNamespace().NamespaceName);
52 |
53 | _defines = root.Nodes().OfType()
54 | .Where(p => p.Target.Equals(WixNames.Define, StringComparison.Ordinal))
55 | .Select(p => new WixDefine(this, p))
56 | .ToList();
57 |
58 | _componentGroupNodes = root.Descendants(WixNames.ComponentGroupNode)
59 | .Select(node => new WixComponentGroupNode(this, node))
60 | .ToList();
61 |
62 | _componentNodes = root.Descendants(WixNames.ComponentNode)
63 | .Select(node => new WixComponentNode(this, node))
64 | .ToList();
65 |
66 | _fileNodes = new List();
67 |
68 | _fileNodes.AddRange(root
69 | .Descendants(WixNames.FileNode)
70 | .Select(node => new WixFileNode(this, node, _fileNodes)));
71 |
72 | _directoryNodes = root
73 | .Descendants(WixNames.DirectoryNode)
74 | .Select(node => new WixDirectoryNode(this, node))
75 | .Where(node => node.Id != "TARGETDIR")
76 | .ToList();
77 |
78 | _featureNodes = root
79 | .Descendants(WixNames.FeatureNode)
80 | .Select(node => new WixFeatureNode(this, node))
81 | .ToList();
82 |
83 | var featureNodesLookup = _featureNodes.ToDictionary(item => item.Id);
84 |
85 | foreach (var featureNode in _featureNodes)
86 | {
87 | featureNode.BuildTree(featureNodesLookup);
88 | }
89 | }
90 |
91 | public WixNames WixNames { get; }
92 |
93 | public IEnumerable FileNodes => new ReadOnlyCollection(_fileNodes);
94 |
95 | public IEnumerable DirectoryNodes => new ReadOnlyCollection(_directoryNodes);
96 |
97 | public IEnumerable ComponentGroupNodes => new ReadOnlyCollection(_componentGroupNodes);
98 |
99 | public IEnumerable FeatureNodes => new ReadOnlyCollection(_featureNodes);
100 |
101 | public IEnumerable ComponentNodes => new ReadOnlyCollection(_componentNodes);
102 |
103 | public WixProject Project { get; }
104 |
105 | public bool HasChanges
106 | {
107 | get
108 | {
109 | try
110 | {
111 | var xmlFile = _projectItem.GetXmlContent(LoadOptions.None);
112 |
113 | return xmlFile.ToString(SaveOptions.DisableFormatting) != _rawXmlFile.ToString(SaveOptions.DisableFormatting);
114 | }
115 | catch (XmlException)
116 | {
117 | return true;
118 | }
119 | }
120 | }
121 |
122 | internal WixDirectoryNode AddDirectory(string id, string name, string parentId)
123 | {
124 | var root = _root;
125 |
126 | var fragmentElement = new XElement(WixNames.FragmentNode);
127 | root.AddWithFormatting(fragmentElement);
128 |
129 | var directoryRefElement = new XElement(WixNames.DirectoryRefNode, new XAttribute("Id", parentId));
130 | fragmentElement.AddWithFormatting(directoryRefElement);
131 |
132 | var directoryElement = new XElement(WixNames.DirectoryNode, new XAttribute("Id", id), new XAttribute("Name", name));
133 | directoryRefElement.AddWithFormatting(directoryElement);
134 |
135 | Save();
136 |
137 | return AddDirectoryNode(directoryElement);
138 | }
139 |
140 | public WixDirectoryNode AddDirectoryNode(XElement directoryElement)
141 | {
142 | var directoryNode = new WixDirectoryNode(this, directoryElement);
143 | _directoryNodes.Add(directoryNode);
144 | return directoryNode;
145 | }
146 |
147 | internal WixComponentGroupNode AddComponentGroup(string directoryId)
148 | {
149 | var root = _root;
150 |
151 | var fragmentElement = new XElement(WixNames.FragmentNode);
152 | root.AddWithFormatting(fragmentElement);
153 |
154 | var componentGroupElement = new XElement(WixNames.ComponentGroupNode, new XAttribute("Id", directoryId + "_files"), new XAttribute("Directory", directoryId));
155 | fragmentElement.AddWithFormatting(componentGroupElement);
156 |
157 | Save();
158 |
159 | var componentGroup = new WixComponentGroupNode(this, componentGroupElement);
160 | _componentGroupNodes.Add(componentGroup);
161 | return componentGroup;
162 | }
163 |
164 | internal WixFileNode AddFileComponent(WixComponentGroupNode componentGroup, string id, string name, FileMapping fileMapping)
165 | {
166 | Debug.Assert(componentGroup.SourceFile.Equals(this));
167 |
168 | var project = fileMapping.TopLevelProject;
169 |
170 | var variableName = string.Format(CultureInfo.InvariantCulture, "{0}_TargetDir", project.Name);
171 |
172 | ForceDirectoryVariable(variableName, project);
173 |
174 | var componentElement = new XElement(WixNames.ComponentNode, new XAttribute("Id", id), new XAttribute("Guid", Guid.NewGuid().ToString()));
175 |
176 | componentGroup.Node.AddWithFormatting(componentElement);
177 |
178 | var source = string.Format(CultureInfo.InvariantCulture, "$(var.{0}){1}", variableName, fileMapping.SourceName);
179 |
180 | var fileElement = new XElement(WixNames.FileNode, new XAttribute("Id", id), new XAttribute("Name", name), new XAttribute("Source", source));
181 |
182 | componentElement.AddWithFormatting(fileElement);
183 |
184 | Save();
185 |
186 | var fileNode = new WixFileNode(componentGroup.SourceFile, fileElement, _fileNodes);
187 | _fileNodes.Add(fileNode);
188 | return fileNode;
189 | }
190 |
191 | internal void Save()
192 | {
193 | _projectItem.SetContent(_xmlFile.Declaration + _xmlFile.ToString(SaveOptions.DisableFormatting));
194 | _rawXmlFile = _projectItem.GetXmlContent(LoadOptions.None);
195 | }
196 |
197 | private void ForceDirectoryVariable(string variableName, Project project)
198 | {
199 | if (_defines.Any(d => d.Name.Equals(variableName, StringComparison.Ordinal)))
200 | return;
201 |
202 | var data = string.Format(CultureInfo.InvariantCulture, "{0}=$(var.{1}.TargetDir)", variableName, project.Name);
203 | var processingInstruction = new XProcessingInstruction(WixNames.Define, data);
204 |
205 | var lastNode = _defines.LastOrDefault();
206 |
207 | if (lastNode != null)
208 | {
209 | lastNode.Node.AddAfterSelf(processingInstruction);
210 | }
211 | else
212 | {
213 | var firstNode = _root.FirstNode;
214 | if (firstNode != null)
215 | {
216 | firstNode.AddBeforeSelf(processingInstruction);
217 | }
218 | else
219 | {
220 | _root.Add(processingInstruction);
221 | }
222 | }
223 |
224 | _defines.Add(new WixDefine(this, processingInstruction));
225 | }
226 | }
227 | }
--------------------------------------------------------------------------------
/Wax.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31402.337
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wax", "Wax\Wax.csproj", "{A9900171-C65D-40ED-8764-74C80E05DB9B}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wax.Model", "Wax.Model\Wax.Model.csproj", "{A3F2A655-5194-4A7A-AA66-E9A536F3E352}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5A77E932-3BEE-4FCB-A6C6-CBEDCE1B927D}"
11 | ProjectSection(SolutionItems) = preProject
12 | .editorconfig = .editorconfig
13 | Directory.Build.props = Directory.Build.props
14 | LICENSE = LICENSE
15 | README.md = README.md
16 | ReleaseNotes.md = ReleaseNotes.md
17 | Screenshot.png = Screenshot.png
18 | EndProjectSection
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Release|Any CPU = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {A9900171-C65D-40ED-8764-74C80E05DB9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {A9900171-C65D-40ED-8764-74C80E05DB9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {A9900171-C65D-40ED-8764-74C80E05DB9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {A9900171-C65D-40ED-8764-74C80E05DB9B}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {A3F2A655-5194-4A7A-AA66-E9A536F3E352}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {A3F2A655-5194-4A7A-AA66-E9A536F3E352}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {A3F2A655-5194-4A7A-AA66-E9A536F3E352}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {A3F2A655-5194-4A7A-AA66-E9A536F3E352}.Release|Any CPU.Build.0 = Release|Any CPU
34 | EndGlobalSection
35 | GlobalSection(SolutionProperties) = preSolution
36 | HideSolutionNode = FALSE
37 | EndGlobalSection
38 | GlobalSection(ExtensibilityGlobals) = postSolution
39 | SolutionGuid = {CC847233-AC59-4104-B415-47B4C3F9FCB3}
40 | EndGlobalSection
41 | EndGlobal
42 |
--------------------------------------------------------------------------------
/Wax.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | OPTIMISTIC
3 | True
4 | 1
5 | NEVER
6 | True
7 | True
8 | True
9 | True
10 | True
11 | True
12 | True
13 | True
14 | True
15 | True
16 | True
--------------------------------------------------------------------------------
/Wax/200x200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tom-englert/Wax/57dd8d47972869363733e66712cf813d3efec7d8/Wax/200x200.png
--------------------------------------------------------------------------------
/Wax/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tom-englert/Wax/57dd8d47972869363733e66712cf813d3efec7d8/Wax/32x32.png
--------------------------------------------------------------------------------
/Wax/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Wax/FodyWeavers.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Used to control if the On_PropertyName_Changed feature is enabled.
12 |
13 |
14 |
15 |
16 | Used to control if the Dependent properties feature is enabled.
17 |
18 |
19 |
20 |
21 | Used to control if the IsChanged property feature is enabled.
22 |
23 |
24 |
25 |
26 | Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form.
27 |
28 |
29 |
30 |
31 | Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project.
32 |
33 |
34 |
35 |
36 | Used to control if equality checks should use the Equals method resolved from the base class.
37 |
38 |
39 |
40 |
41 | Used to control if equality checks should use the static Equals method resolved from the base class.
42 |
43 |
44 |
45 |
46 | Used to turn off build warnings from this weaver.
47 |
48 |
49 |
50 |
51 | Used to turn off build warnings about mismatched On_PropertyName_Changed methods.
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
61 |
62 |
63 |
64 |
65 | A comma-separated list of error codes that can be safely ignored in assembly verification.
66 |
67 |
68 |
69 |
70 | 'false' to turn off automatic generation of the XML Schema file.
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/Wax/GroupBox.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax
2 | {
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.Windows;
5 | using System.Windows.Controls;
6 |
7 | using TomsToolbox.Wpf;
8 |
9 | public class GroupBox : HeaderedContentControl
10 | {
11 | static GroupBox()
12 | {
13 | DefaultStyleKeyProperty.OverrideMetadata(typeof(GroupBox), new FrameworkPropertyMetadata(typeof(GroupBox)));
14 | }
15 |
16 |
17 | public bool IsOk
18 | {
19 | get => this.GetValue(IsOkProperty);
20 | set => SetValue(IsOkProperty, value);
21 | }
22 | ///
23 | /// Identifies the IsOk dependency property
24 | ///
25 | public static readonly DependencyProperty IsOkProperty =
26 | DependencyProperty.Register("IsOk", typeof(bool), typeof(GroupBox));
27 |
28 |
29 | public int Ordinal
30 | {
31 | get => this.GetValue(OrdinalProperty);
32 | set => SetValue(OrdinalProperty, value);
33 | }
34 | ///
35 | /// Identifies the Ordinal dependency property
36 | ///
37 | public static readonly DependencyProperty OrdinalProperty =
38 | DependencyProperty.Register("Ordinal", typeof(int), typeof(GroupBox));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Wax/Guids.cs:
--------------------------------------------------------------------------------
1 | // Guids.cs
2 | // MUST match guids.h
3 |
4 | // ReSharper disable InconsistentNaming
5 | // ReSharper disable IdentifierTypo
6 | // ReSharper disable UnusedMember.Global
7 | namespace tomenglertde.Wax
8 | {
9 | using System;
10 |
11 | internal static class GuidList
12 | {
13 | public const string guidWaxPkgString = "6b9b0621-c739-4ee5-9834-a0ba2a8d3596";
14 | public const string guidWaxCmdSetString = "b2efcdeb-6b97-430e-82eb-ef8c27280004";
15 | public const string guidToolWindowPersistanceString = "ba4ab97f-d341-4b14-b8c9-3cba5e401a5f";
16 |
17 | public static readonly Guid guidWaxCmdSet = new(guidWaxCmdSetString);
18 | }
19 | }
--------------------------------------------------------------------------------
/Wax/MainView.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax
2 | {
3 | using System;
4 | using System.Linq;
5 | using System.Windows;
6 | using System.Windows.Controls;
7 | using System.Windows.Media;
8 |
9 | ///
10 | /// Interaction logic for MainWindow.xaml
11 | ///
12 | public partial class MainView
13 | {
14 | private readonly EnvDTE80.DTE2 _dte;
15 |
16 | public MainView(EnvDTE80.DTE2 dte)
17 | {
18 | _dte = dte;
19 |
20 | InitializeComponent();
21 |
22 | Loaded += Self_Loaded;
23 | }
24 |
25 | public bool IsDarkTheme
26 | {
27 | get => (bool)GetValue(IsDarkThemeProperty);
28 | set => SetValue(IsDarkThemeProperty, value);
29 | }
30 | public static readonly DependencyProperty IsDarkThemeProperty = DependencyProperty.Register(
31 | "IsDarkTheme", typeof(bool), typeof(MainView), new PropertyMetadata(default(bool)));
32 |
33 | internal MainViewModel? ViewModel
34 | {
35 | get => DataContext as MainViewModel;
36 | set => DataContext = value;
37 | }
38 |
39 | private void Self_Loaded(object sender, RoutedEventArgs e)
40 | {
41 | var viewModel = ViewModel;
42 |
43 | var solution = _dte.Solution;
44 |
45 | if ((solution == null) || (viewModel == null) || (viewModel.Solution.FullName != solution.FullName) || (viewModel.HasExternalChanges) || !viewModel.Solution.Projects.Any())
46 | {
47 | Refresh(solution);
48 | }
49 | }
50 |
51 | private void Refresh_Click(object sender, RoutedEventArgs e)
52 | {
53 | Refresh(_dte.Solution);
54 | }
55 |
56 | private void Refresh(EnvDTE.Solution? solution)
57 | {
58 | if (solution == null)
59 | {
60 | ViewModel = null;
61 | return;
62 | }
63 |
64 | try
65 | {
66 | ViewModel = new MainViewModel(solution);
67 | }
68 | catch (Exception ex)
69 | {
70 | MessageBox.Show("Error loading: " + ex);
71 | }
72 | }
73 |
74 | protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
75 | {
76 | base.OnPropertyChanged(e);
77 |
78 | if (e.Property == DataContextProperty)
79 | {
80 |
81 | if ((e.OldValue is not MainViewModel oldViewModel) || (e.NewValue is not MainViewModel newViewModel))
82 | return;
83 |
84 | var oldSelectedProject = newViewModel.Solution.WixProjects.FirstOrDefault(p => p.Equals(oldViewModel.SelectedWixProject));
85 |
86 | if (oldSelectedProject == null)
87 | return;
88 |
89 | newViewModel.SelectedWixProject = oldSelectedProject;
90 | return;
91 | }
92 |
93 | if ((e.Property != ForegroundProperty) && (e.Property != BackgroundProperty))
94 | return;
95 |
96 | var foreground = ToGray((Foreground as SolidColorBrush)?.Color);
97 | var background = ToGray((Background as SolidColorBrush)?.Color);
98 |
99 | IsDarkTheme = background < foreground;
100 | }
101 |
102 | private static double ToGray(Color? color)
103 | {
104 | return color?.R * 0.21 + color?.G * 0.72 + color?.B * 0.07 ?? 0.0;
105 | }
106 |
107 | private void SetupProjectListBox_Loaded(object sender, RoutedEventArgs e)
108 | {
109 | var listBox = (ListBox)sender;
110 |
111 | if ((listBox.Items.Count == 1) && (listBox.SelectedIndex == -1))
112 | {
113 | listBox.SelectedIndex = 0;
114 | }
115 | }
116 | }
117 | }
--------------------------------------------------------------------------------
/Wax/PkgCmdID.cs:
--------------------------------------------------------------------------------
1 | // PkgCmdID.cs
2 | // MUST match PkgCmdID.h
3 |
4 | // ReSharper disable InconsistentNaming
5 | // ReSharper disable IdentifierTypo
6 | // ReSharper disable UnusedMember.Global
7 | namespace tomenglertde.Wax
8 | {
9 | internal static class PkgCmdIDList
10 | {
11 | public const uint cmdidWax = 0x100;
12 | public const uint cmdidWaxToolWindow = 0x101;
13 |
14 | }
15 | }
--------------------------------------------------------------------------------
/Wax/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.Shell;
2 |
3 | [assembly: System.Windows.ThemeInfo(System.Windows.ResourceDictionaryLocation.None, System.Windows.ResourceDictionaryLocation.SourceAssembly)]
4 |
5 | [assembly: ProvideCodeBase(CodeBase = "$PackageFolder$\\Microsoft.Xaml.Behaviors.dll")]
6 | [assembly: ProvideCodeBase(CodeBase = "$PackageFolder$\\DataGridExtensions.dll")]
7 | [assembly: ProvideCodeBase(CodeBase = "$PackageFolder$\\TomsToolbox.Essentials.dll")]
8 | [assembly: ProvideCodeBase(CodeBase = "$PackageFolder$\\TomsToolbox.ObservableCollections.dll")]
9 | [assembly: ProvideCodeBase(CodeBase = "$PackageFolder$\\TomsToolbox.Wpf.dll")]
10 | [assembly: ProvideCodeBase(CodeBase = "$PackageFolder$\\TomsToolbox.Wpf.Styles.dll")]
--------------------------------------------------------------------------------
/Wax/Properties/Resources.de.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | Das Fenster kann nicht erzeugt werden.
122 |
123 |
124 | WiX Setup Editor
125 |
126 |
127 | Debug-Symbole
128 |
129 |
130 | Setup-Projekt:
131 |
132 |
133 | Stammverzeichnis
134 |
135 |
136 | Zu installierende Projekte:
137 |
138 |
139 | Alle Projekte anzeigen
140 |
141 |
142 | Verzeichnis-Zuordnungen
143 |
144 |
145 | Verzeichnis
146 |
147 |
148 | WiX-Definition
149 |
150 |
151 | Eigene Zuordnung entfernen.
152 |
153 |
154 | Neuen Verzeichnisknoten erstellen.
155 |
156 |
157 | Datei
158 |
159 |
160 | Erw.
161 |
162 |
163 | Status
164 |
165 |
166 | Nicht zugeordnete Dateien
167 |
168 |
169 | Id
170 |
171 |
172 | Name
173 |
174 |
175 | Quelle
176 |
177 |
178 | Nicht zugeordnete Datei entfernen.
179 |
180 |
181 | Dateizuordnungen
182 |
183 |
184 | Eigene Datei-Zuordnung entfernen.
185 |
186 |
187 | Neuen Dateiknoten erstellen.
188 |
189 |
190 | Eindeutige Zuordnung bestätigen.
191 |
192 |
193 | Neu laden
194 |
195 |
196 | Unterstütze das Projekt, in dem du die Anwendung bewertest.
197 |
198 |
199 | Dokumentation
200 |
201 |
202 | Das Projekt unterstützen.
203 |
204 |
205 | Ext. Lokalisierungen
206 |
207 |
208 | Debug-Symbole mitinstallieren.
209 |
210 |
211 | Lokalisierungen externer Komponenten installieren.
212 |
213 |
214 | Lokalisierungen
215 |
216 |
217 | Lokalisierungen installieren.
218 |
219 |
220 | Referenziert von:
221 |
222 |
223 | Projekttyp:
224 |
225 |
--------------------------------------------------------------------------------
/Wax/Properties/Resources.fr.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | Impossible de créer la fenêtre de cet outil.
122 |
123 |
124 | Editeur d'installeur Wix
125 |
126 |
127 | Déployer les symboles
128 |
129 |
130 | Projet d'installation à éditer :
131 |
132 |
133 | Répertoire racine
134 |
135 |
136 | Projets à installer :
137 |
138 |
139 | Afficher tous les projets
140 |
141 |
142 | Mappages de répertoires
143 |
144 |
145 | Répertoire
146 |
147 |
148 | Définition Wix
149 |
150 |
151 | Supprimer le mappage personnalisé.
152 |
153 |
154 | Créer un nouveau noeud de type répertoire.
155 |
156 |
157 | Fichier
158 |
159 |
160 | Ext.
161 |
162 |
163 | Etat
164 |
165 |
166 | Fichiers non mappés
167 |
168 |
169 | Id
170 |
171 |
172 | Nom
173 |
174 |
175 | Source
176 |
177 |
178 | Supprimer les fichiers non mappés.
179 |
180 |
181 | Mappages de fichiers
182 |
183 |
184 | Supprimer le mappage de fichier personnalisé.
185 |
186 |
187 | Créer un nouveau noeud de type fichier.
188 |
189 |
190 | Confirmer le mappage unique de fichier.
191 |
192 |
193 | Rafraîchir
194 |
195 |
196 | Supporter le projet en écrivant un bref avis.
197 |
198 |
199 | Documentation
200 |
201 |
202 | Supporter le projet.
203 |
204 |
205 | Localisations externes
206 |
207 |
208 | Ajouter les symboles dans le projet d'installation.
209 |
210 |
211 | Ajouter aussi les librairies satellites des références externes dans le projet d'installation.
212 |
213 |
214 | Localisations
215 |
216 |
217 | Ajouter aussi les librairies satellites dans le projet d'installation.
218 |
219 |
220 | Référencé par:
221 |
222 |
223 | Type de projet:
224 |
225 |
--------------------------------------------------------------------------------
/Wax/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | text/microsoft-resx
119 |
120 |
121 | 2.0
122 |
123 |
124 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
125 |
126 |
127 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
128 |
129 |
130 | Can not create tool window.
131 |
132 |
133 | WiX Setup Editor
134 |
135 |
136 | Symbols
137 |
138 |
139 | Setup project to edit:
140 |
141 |
142 | Root directory
143 |
144 |
145 | Projects to install:
146 |
147 |
148 | Show all projects
149 |
150 |
151 | Directory mappings
152 |
153 |
154 | Directory
155 |
156 |
157 | WiX definition
158 |
159 |
160 | Remove the custom mapping.
161 |
162 |
163 | Create a new directory node.
164 |
165 |
166 | File
167 |
168 |
169 | Ext.
170 | @MutedRule(PunctuationTail)
171 |
172 |
173 | State
174 |
175 |
176 | Unmapped Files
177 |
178 |
179 | Id
180 |
181 |
182 | Name
183 |
184 |
185 | Source
186 |
187 |
188 | Remove unmapped file.
189 |
190 |
191 | File mappings
192 |
193 |
194 | Remove custom file mapping.
195 |
196 |
197 | Create a new file node.
198 |
199 |
200 | Confirm unique file mapping.
201 |
202 |
203 | Refresh
204 |
205 |
206 | Support the project by writing a short review.
207 |
208 |
209 | Documentation
210 |
211 |
212 | Support the project.
213 |
214 |
215 | Ext. Localizations
216 |
217 |
218 | Include the debug symbols in the setup project.
219 |
220 |
221 | Include also external/3rd party localizations in the setup project.
222 |
223 |
224 | Localizations
225 |
226 |
227 | Include localizations in the setup project.
228 |
229 |
230 | Referenced by:
231 |
232 |
233 | Project type:
234 |
235 |
--------------------------------------------------------------------------------
/Wax/Properties/Resources.zh-CN.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 无法创建工具窗口。
122 |
123 |
124 | Wix安装编辑器
125 |
126 |
127 | 部署调试符号
128 |
129 |
130 | 安装项目编辑:
131 |
132 |
133 | 根目录
134 |
135 |
136 | 项目安装至:
137 |
138 |
139 | 显示所有项目
140 |
141 |
142 | 目录映射
143 |
144 |
145 | 目录
146 |
147 |
148 | Wix定义
149 |
150 |
151 | 移除自定义映射。
152 |
153 |
154 | 创建新目录节点。
155 |
156 |
157 | 文件
158 |
159 |
160 | 扩展名
161 |
162 |
163 | 状态
164 |
165 |
166 | 未映射文件
167 |
168 |
169 | 标识
170 |
171 |
172 | 名称
173 |
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 |
--------------------------------------------------------------------------------
/Wax/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "VS2022": {
4 | "commandName": "Executable",
5 | "executablePath": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Common7\\IDE\\devenv.exe",
6 | "commandLineArgs": "/RootSuffix Exp"
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/Wax/Resources/Icon32.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tom-englert/Wax/57dd8d47972869363733e66712cf813d3efec7d8/Wax/Resources/Icon32.pdn
--------------------------------------------------------------------------------
/Wax/Resources/Package.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tom-englert/Wax/57dd8d47972869363733e66712cf813d3efec7d8/Wax/Resources/Package.ico
--------------------------------------------------------------------------------
/Wax/Resources/VSColorScheme.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
8 |
10 |
12 |
14 |
16 |
18 |
19 |
21 |
22 |
24 |
26 |
27 |
29 |
31 |
32 |
34 |
36 |
38 |
39 |
41 |
43 |
44 |
46 |
48 |
50 |
51 |
53 |
55 |
57 |
58 |
60 |
62 |
63 |
--------------------------------------------------------------------------------
/Wax/Resources/btn_donate_SM.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tom-englert/Wax/57dd8d47972869363733e66712cf813d3efec7d8/Wax/Resources/btn_donate_SM.gif
--------------------------------------------------------------------------------
/Wax/Resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tom-englert/Wax/57dd8d47972869363733e66712cf813d3efec7d8/Wax/Resources/icon.png
--------------------------------------------------------------------------------
/Wax/Resources/images.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tom-englert/Wax/57dd8d47972869363733e66712cf813d3efec7d8/Wax/Resources/images.png
--------------------------------------------------------------------------------
/Wax/Resources/like.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tom-englert/Wax/57dd8d47972869363733e66712cf813d3efec7d8/Wax/Resources/like.png
--------------------------------------------------------------------------------
/Wax/Resources/ok.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tom-englert/Wax/57dd8d47972869363733e66712cf813d3efec7d8/Wax/Resources/ok.png
--------------------------------------------------------------------------------
/Wax/Resources/refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tom-englert/Wax/57dd8d47972869363733e66712cf813d3efec7d8/Wax/Resources/refresh.png
--------------------------------------------------------------------------------
/Wax/Resources/warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tom-englert/Wax/57dd8d47972869363733e66712cf813d3efec7d8/Wax/Resources/warning.png
--------------------------------------------------------------------------------
/Wax/ShellView.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Wax/ShellView.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax
2 | {
3 | ///
4 | /// Interaction logic for ShellView.xaml
5 | ///
6 | public partial class ShellView
7 | {
8 | public ShellView()
9 | {
10 | InitializeComponent();
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Wax/Themes/Generic.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
34 |
--------------------------------------------------------------------------------
/Wax/ToolWindow.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax
2 | {
3 | using System;
4 | using System.ComponentModel;
5 | using System.Diagnostics;
6 | using System.Runtime.InteropServices;
7 | using System.Windows;
8 | using System.Windows.Controls.Primitives;
9 |
10 | using Microsoft.VisualStudio;
11 | using Microsoft.VisualStudio.Shell;
12 | using Microsoft.VisualStudio.Shell.Interop;
13 |
14 | using tomenglertde.Wax.Model.VisualStudio;
15 |
16 | using TomsToolbox.Wpf;
17 |
18 | ///
19 | /// This class implements the tool window exposed by this package and hosts a user control.
20 | ///
21 | /// In Visual Studio tool windows are composed of a frame (implemented by the shell) and a pane,
22 | /// usually implemented by the package implementer.
23 | ///
24 | /// This class derives from the ToolWindowPane class provided from the MPF in order to use its
25 | /// implementation of the IVsUIElementPane interface.
26 | ///
27 | [Guid("ba4ab97f-d341-4b14-b8c9-3cba5e401a5f")]
28 | public class ToolWindow : ToolWindowPane
29 | {
30 | private EnvDTE80.DTE2? _dte;
31 |
32 | ///
33 | /// Standard constructor for the tool window.
34 | ///
35 | public ToolWindow()
36 | : base(null)
37 | {
38 | // Just to reference something to force load of referenced libraries.
39 | BindingErrorTracer.Start(msg => { Debug.WriteLine(msg); });
40 |
41 | Caption = Properties.Resources.ToolWindowTitle;
42 |
43 | BitmapResourceID = 301;
44 | BitmapIndex = 1;
45 | }
46 |
47 | protected override void OnCreate()
48 | {
49 | base.OnCreate();
50 |
51 | EventManager.RegisterClassHandler(typeof(MainView), ButtonBase.ClickEvent, new RoutedEventHandler(Navigate_Click));
52 |
53 | var t = typeof(Solution);
54 | if (t.GetMembers().Length == 0)
55 | {
56 | // Just to make sure the assembly is loaded, loading it dynamically from XAML may not work!
57 | }
58 |
59 | _dte = (EnvDTE80.DTE2)GetService(typeof(EnvDTE.DTE));
60 |
61 | Content = new ShellView { Content = new MainView(_dte) };
62 | }
63 |
64 | private void Navigate_Click(object sender, RoutedEventArgs e)
65 | {
66 | var source = e.OriginalSource as FrameworkElement;
67 |
68 | var button = source?.TryFindAncestorOrSelf();
69 | if (button == null)
70 | return;
71 |
72 | var url = source.Tag as string;
73 | if (string.IsNullOrEmpty(url) || !url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
74 | return;
75 |
76 | CreateWebBrowser(url);
77 | }
78 |
79 |
80 | [Localizable(false)]
81 | private void CreateWebBrowser(string url)
82 | {
83 | var webBrowsingService = (IVsWebBrowsingService?)GetService(typeof(SVsWebBrowsingService));
84 | if (webBrowsingService != null)
85 | {
86 | var hr = webBrowsingService.Navigate(url, (uint)__VSWBNAVIGATEFLAGS.VSNWB_WebURLOnly, out var pFrame);
87 | if (ErrorHandler.Succeeded(hr) && (pFrame != null))
88 | {
89 | hr = pFrame.Show();
90 | if (ErrorHandler.Succeeded(hr))
91 | return;
92 | }
93 | }
94 |
95 | Process.Start(url);
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Wax/VSPackage.de.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | Wax
122 |
123 |
124 | Wix Setup Editor
125 |
126 |
--------------------------------------------------------------------------------
/Wax/VSPackage.fr.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | Wax
122 |
123 |
124 | Wix Setup Editor
125 |
126 |
--------------------------------------------------------------------------------
/Wax/VSPackage.resx:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | text/microsoft-resx
120 |
121 |
122 | 2.0
123 |
124 |
125 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
126 |
127 |
128 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
129 |
130 |
131 |
132 | Wax
133 |
134 |
135 | WiX Setup Editor
136 |
137 |
138 |
139 | Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
140 |
141 |
142 |
143 | Resources\Images.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
144 |
145 |
--------------------------------------------------------------------------------
/Wax/VSPackage.zh-CN.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | Wax
122 |
123 |
124 | Wix安装编辑器
125 |
126 |
--------------------------------------------------------------------------------
/Wax/VsPackage.cs:
--------------------------------------------------------------------------------
1 | namespace tomenglertde.Wax
2 | {
3 | using System;
4 | using System.ComponentModel.Design;
5 | using System.Diagnostics;
6 | using System.Globalization;
7 | using System.Runtime.InteropServices;
8 | using System.Threading;
9 |
10 | using Microsoft.VisualStudio;
11 | using Microsoft.VisualStudio.Shell;
12 | using Microsoft.VisualStudio.Shell.Interop;
13 |
14 | ///
15 | /// This is the class that implements the package exposed by this assembly.
16 | ///
17 | /// The minimum requirement for a class to be considered a valid package for Visual Studio
18 | /// is to implement the IVsPackage interface and register itself with the shell.
19 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
20 | /// to do it: it derives from the Package class that provides the implementation of the
21 | /// IVsPackage interface and uses the registration attributes defined in the framework to
22 | /// register itself and its components with the shell.
23 | ///
24 | // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is
25 | // a package.
26 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
27 | // This attribute is used to register the informations needed to show the this package
28 | // in the Help/About dialog of Visual Studio.
29 | [InstalledProductRegistration("#110", "#112", "Wax", IconResourceID = 400)]
30 | // This attribute is needed to let the shell know that this package exposes some menus.
31 | [ProvideMenuResource("Menus.ctmenu", 1)]
32 | // This attribute registers a tool window exposed by this package.
33 | [ProvideToolWindow(typeof(ToolWindow))]
34 | [Guid(GuidList.guidWaxPkgString)]
35 | public sealed class VsPackage : AsyncPackage
36 | {
37 | ///
38 | /// Default constructor of the package.
39 | /// Inside this method you can place any initialization code that does not require
40 | /// any Visual Studio service because at this point the package object is created but
41 | /// not sited yet inside Visual Studio environment. The place to do all the other
42 | /// initialization is the Initialize method.
43 | ///
44 | public VsPackage()
45 | {
46 | Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", ToString()));
47 | }
48 |
49 | ///
50 | /// This function is called when the user clicks the menu item that shows the
51 | /// tool window. See the Initialize method to see how the menu item is associated to
52 | /// this function using the OleMenuCommandService service and the MenuCommand class.
53 | ///
54 | private void ShowToolWindow(object? sender, EventArgs e)
55 | {
56 | // Get the instance number 0 of this tool window. This window is single instance so this instance
57 | // is actually the only one.
58 | // The last flag is set to true so that if the tool window does not exists it will be created.
59 | var window = FindToolWindow(typeof(ToolWindow), 0, true);
60 | if (window?.Frame == null)
61 | {
62 | throw new NotSupportedException(Properties.Resources.CanNotCreateWindow);
63 | }
64 | var windowFrame = (IVsWindowFrame)window.Frame;
65 | ErrorHandler.ThrowOnFailure(windowFrame.Show());
66 | }
67 |
68 | ///
69 | /// Initialization of the package; this method is called right after the package is sited, so this is the place
70 | /// where you can put all the initialization code that rely on services provided by VisualStudio.
71 | ///
72 | protected override async System.Threading.Tasks.Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
73 | {
74 | Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", ToString()));
75 |
76 | await base.InitializeAsync(cancellationToken, progress).ConfigureAwait(false);
77 |
78 | var menuCommandService = await GetServiceAsync(typeof(IMenuCommandService)).ConfigureAwait(false);
79 |
80 | await JoinableTaskFactory.SwitchToMainThreadAsync();
81 |
82 | // Add our command handlers for menu (commands must exist in the .vsct file)
83 | if (menuCommandService is OleMenuCommandService mcs)
84 | {
85 | // Create the command for the tool window
86 | var toolWindowCommandID = new CommandID(GuidList.guidWaxCmdSet, (int)PkgCmdIDList.cmdidWaxToolWindow);
87 | var menuToolWin = new MenuCommand(ShowToolWindow, toolWindowCommandID);
88 | mcs.AddCommand(menuToolWin);
89 | }
90 |
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Wax/Wax.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | net472
5 | tomenglertde.Wax
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | ReleaseNotes.md
14 | PreserveNewest
15 | true
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Always
24 | true
25 |
26 |
27 | Always
28 | true
29 |
30 |
31 | LICENSE
32 | Always
33 | true
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | True
42 | True
43 | Resources.resx
44 |
45 |
46 | PublicResXFileCodeGenerator
47 | Resources.Designer.cs
48 | Designer
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 2.5.13
61 |
62 |
63 | 6.6.0
64 | all
65 |
66 |
67 | 2021.3.0
68 | all
69 |
70 |
71 |
72 | all
73 | runtime; build; native; contentfiles; analyzers; buildtransitive
74 |
75 |
76 | 3.4.0
77 | all
78 |
79 |
80 |
81 |
82 |
83 | 1.7.0
84 |
85 |
86 | 2.7.4
87 |
88 |
89 | 15.0.1
90 |
91 |
92 | all
93 | runtime; build; native; contentfiles; analyzers; buildtransitive
94 |
95 |
96 | all
97 |
98 |
99 |
--------------------------------------------------------------------------------
/Wax/Wax.vsct:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
28 |
29 |
36 |
37 |
38 |
42 |
43 |
46 |
47 |
48 |
49 |
51 |
52 |
59 |
60 |
68 |
69 |
77 |
78 |
79 |
80 |
81 |
82 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/Wax/source.extension.vsixmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Wax
6 | An interactive WiX-Setup editor; easily maintain the files to be installed.
7 | https://github.com/tom-englert/Wax
8 | LICENSE
9 | https://github.com/tom-englert/Wax
10 | ReleaseNotes.md
11 | 32x32.png
12 | 200x200.png
13 | WiX, Install, MSI, Windows Installer, Setup
14 |
15 |
16 |
19 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | name: $(Build.DefinitionName) $(date:yyyyMMdd)$(rev:.r)
2 |
3 | pool:
4 | vmImage: 'windows-latest'
5 | demands:
6 | - msbuild
7 | - vstest
8 |
9 | variables:
10 | BuildPlatform: 'Any CPU'
11 | BuildConfiguration: 'Release'
12 |
13 | steps:
14 | - task: PowerShell@1
15 | displayName: 'Set build version'
16 | inputs:
17 | scriptType: inlineScript
18 | inlineScript: |
19 | (new-object Net.WebClient).DownloadString("https://raw.github.com/tom-englert/BuildScripts/master/BuildScripts.ps1") | iex
20 | $version = Project-SetVersion "Directory.Build.props"
21 | $version | Build-AppendVersionToBuildNumber
22 | condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
23 |
24 | - task: VSBuild@1
25 | displayName: 'Build solution'
26 | inputs:
27 | solution: 'wax.sln'
28 | vsVersion: '16.0'
29 | msbuildArgs: '-restore'
30 | platform: '$(BuildPlatform)'
31 | configuration: '$(BuildConfiguration)'
32 | createLogFile: true
33 |
34 | - task: CopyFiles@1
35 | displayName: 'Copy Files to: $(build.artifactstagingdirectory)'
36 | inputs:
37 | SourceFolder: '$(build.sourcesdirectory)'
38 | Contents: '**\bin\$(BuildConfiguration)\**\*.vsix'
39 | TargetFolder: '$(build.artifactstagingdirectory)'
40 | flattenFolders: true
41 | condition: succeededOrFailed()
42 |
43 | - task: PublishBuildArtifacts@1
44 | displayName: 'Publish Artifact: VSIX'
45 | inputs:
46 | PathtoPublish: '$(build.artifactstagingdirectory)\wax.vsix'
47 | ArtifactName: VSIX
48 |
49 | - task: PowerShell@1
50 | displayName: 'Publish to vsix-gallery'
51 | inputs:
52 | scriptType: inlineScript
53 | inlineScript: |
54 | (new-object Net.WebClient).DownloadString("https://raw.github.com/tom-englert/BuildScripts/master/BuildScripts.ps1") | iex
55 |
56 | Vsix-PublishToGallery "$(build.artifactstagingdirectory)\wax.vsix"
57 | condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
58 |
--------------------------------------------------------------------------------