├── .gitattributes ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CertWAC.sln ├── LICENSE ├── Program ├── Action.cpp ├── Action.h ├── ActionDialog.cpp ├── ActionDialog.h ├── CertWAC.cpp ├── CertWAC.rc ├── ComputerCertificate.cpp ├── ComputerCertificate.h ├── ErrorDialog.cpp ├── ErrorDialog.h ├── ErrorRecord.cpp ├── ErrorRecord.h ├── InstallInfo.cpp ├── InstallInfo.h ├── LoadCertificates.cpp ├── MainDialog.h ├── MainDialogController.cpp ├── MainDialogUI.cpp ├── Program.vcxproj ├── Program.vcxproj.filters ├── Resources │ ├── CertWAC.ico │ ├── Error.ico │ ├── TinyGreenBox.bmp │ └── TinyRedBox.bmp ├── StringUtility.cpp ├── StringUtility.h ├── WindowsUtility.cpp ├── WindowsUtility.h └── resource.h ├── README.md └── Setup ├── MITLicense.rtf ├── Microsoft_VC141_CRT_x86.msm ├── Product.wxs └── Setup.wixproj /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.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 | Release - MSI/ 19 | Release - Standalone 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # DNX 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | 50 | *_i.c 51 | *_p.c 52 | *_i.h 53 | *.ilk 54 | *.meta 55 | *.obj 56 | *.pch 57 | *.pdb 58 | *.pgc 59 | *.pgd 60 | *.rsp 61 | *.sbr 62 | *.tlb 63 | *.tli 64 | *.tlh 65 | *.tmp 66 | *.tmp_proj 67 | *.log 68 | *.vspscc 69 | *.vssscc 70 | .builds 71 | *.pidb 72 | *.svclog 73 | *.scc 74 | 75 | # Chutzpah Test files 76 | _Chutzpah* 77 | 78 | # Visual C++ cache files 79 | ipch/ 80 | *.aps 81 | *.ncb 82 | *.opendb 83 | *.opensdf 84 | *.sdf 85 | *.cachefile 86 | *.VC.db 87 | *.VC.VC.opendb 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | *.sap 94 | 95 | # TFS 2012 Local Workspace 96 | $tf/ 97 | 98 | # Guidance Automation Toolkit 99 | *.gpState 100 | 101 | # ReSharper is a .NET coding add-in 102 | _ReSharper*/ 103 | *.[Rr]e[Ss]harper 104 | *.DotSettings.user 105 | 106 | # JustCode is a .NET coding add-in 107 | .JustCode 108 | 109 | # TeamCity is a build add-in 110 | _TeamCity* 111 | 112 | # DotCover is a Code Coverage Tool 113 | *.dotCover 114 | 115 | # NCrunch 116 | _NCrunch_* 117 | .*crunch*.local.xml 118 | nCrunchTemp_* 119 | 120 | # MightyMoose 121 | *.mm.* 122 | AutoTest.Net/ 123 | 124 | # Web workbench (sass) 125 | .sass-cache/ 126 | 127 | # Installshield output folder 128 | [Ee]xpress/ 129 | 130 | # DocProject is a documentation generator add-in 131 | DocProject/buildhelp/ 132 | DocProject/Help/*.HxT 133 | DocProject/Help/*.HxC 134 | DocProject/Help/*.hhc 135 | DocProject/Help/*.hhk 136 | DocProject/Help/*.hhp 137 | DocProject/Help/Html2 138 | DocProject/Help/html 139 | 140 | # Click-Once directory 141 | publish/ 142 | 143 | # Publish Web Output 144 | *.[Pp]ublish.xml 145 | *.azurePubxml 146 | # TODO: Comment the next line if you want to checkin your web deploy settings 147 | # but database connection strings (with potential passwords) will be unencrypted 148 | #*.pubxml 149 | *.publishproj 150 | 151 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 152 | # checkin your Azure Web App publish settings, but sensitive information contained 153 | # in these scripts will be unencrypted 154 | PublishScripts/ 155 | 156 | # NuGet Packages 157 | *.nupkg 158 | # The packages folder can be ignored because of Package Restore 159 | **/packages/* 160 | # except build/, which is used as an MSBuild target. 161 | !**/packages/build/ 162 | # Uncomment if necessary however generally it will be regenerated when needed 163 | #!**/packages/repositories.config 164 | # NuGet v3's project.json files produces more ignoreable files 165 | *.nuget.props 166 | *.nuget.targets 167 | 168 | # Microsoft Azure Build Output 169 | csx/ 170 | *.build.csdef 171 | 172 | # Microsoft Azure Emulator 173 | ecf/ 174 | rcf/ 175 | 176 | # Windows Store app package directories and files 177 | AppPackages/ 178 | BundleArtifacts/ 179 | Package.StoreAssociation.xml 180 | _pkginfo.txt 181 | 182 | # Visual Studio cache files 183 | # files ending in .cache can be ignored 184 | *.[Cc]ache 185 | # but keep track of directories ending in .cache 186 | !*.[Cc]ache/ 187 | 188 | # Others 189 | ClientBin/ 190 | ~$* 191 | *~ 192 | *.dbmdl 193 | *.dbproj.schemaview 194 | *.jfm 195 | *.pfx 196 | *.publishsettings 197 | node_modules/ 198 | orleans.codegen.cs 199 | 200 | # Since there are multiple workflows, uncomment next line to ignore bower_components 201 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 202 | #bower_components/ 203 | 204 | # RIA/Silverlight projects 205 | Generated_Code/ 206 | 207 | # Backup & report files from converting an old project file 208 | # to a newer Visual Studio version. Backup files are not needed, 209 | # because we have git ;-) 210 | _UpgradeReport_Files/ 211 | Backup*/ 212 | UpgradeLog*.XML 213 | UpgradeLog*.htm 214 | 215 | # SQL Server files 216 | *.mdf 217 | *.ldf 218 | 219 | # Business Intelligence projects 220 | *.rdl.data 221 | *.bim.layout 222 | *.bim_*.settings 223 | 224 | # Microsoft Fakes 225 | FakesAssemblies/ 226 | 227 | # GhostDoc plugin setting file 228 | *.GhostDoc.xml 229 | 230 | # Node.js Tools for Visual Studio 231 | .ntvs_analysis.dat 232 | 233 | # Visual Studio 6 build log 234 | *.plg 235 | 236 | # Visual Studio 6 workspace options file 237 | *.opt 238 | 239 | # Visual Studio LightSwitch build output 240 | **/*.HTMLClient/GeneratedArtifacts 241 | **/*.DesktopClient/GeneratedArtifacts 242 | **/*.DesktopClient/ModelManifest.xml 243 | **/*.Server/GeneratedArtifacts 244 | **/*.Server/ModelManifest.xml 245 | _Pvt_Extensions 246 | 247 | # Paket dependency manager 248 | .paket/paket.exe 249 | paket-files/ 250 | 251 | # FAKE - F# Make 252 | .fake/ 253 | 254 | # JetBrains Rider 255 | .idea/ 256 | *.sln.iml 257 | 258 | # CodeRush 259 | .cr/ 260 | 261 | # Python Tools for Visual Studio (PTVS) 262 | __pycache__/ 263 | *.pyc -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CertWAC.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.421 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Program", "Program\Program.vcxproj", "{99E5DCA9-6082-41E6-BD22-0633BF652E04}" 7 | EndProject 8 | Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "Setup", "Setup\Setup.wixproj", "{7E3A17A9-BE47-4F20-8B07-149420318D86}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x86 = Debug|x86 13 | MSI Release|x86 = MSI Release|x86 14 | Standalone Release|x86 = Standalone Release|x86 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {99E5DCA9-6082-41E6-BD22-0633BF652E04}.Debug|x86.ActiveCfg = Debug|Win32 18 | {99E5DCA9-6082-41E6-BD22-0633BF652E04}.Debug|x86.Build.0 = Debug|Win32 19 | {99E5DCA9-6082-41E6-BD22-0633BF652E04}.MSI Release|x86.ActiveCfg = Release - MSI|Win32 20 | {99E5DCA9-6082-41E6-BD22-0633BF652E04}.MSI Release|x86.Build.0 = Release - MSI|Win32 21 | {99E5DCA9-6082-41E6-BD22-0633BF652E04}.Standalone Release|x86.ActiveCfg = Release - Standalone|Win32 22 | {99E5DCA9-6082-41E6-BD22-0633BF652E04}.Standalone Release|x86.Build.0 = Release - Standalone|Win32 23 | {7E3A17A9-BE47-4F20-8B07-149420318D86}.Debug|x86.ActiveCfg = Debug|x86 24 | {7E3A17A9-BE47-4F20-8B07-149420318D86}.MSI Release|x86.ActiveCfg = Release|x86 25 | {7E3A17A9-BE47-4F20-8B07-149420318D86}.MSI Release|x86.Build.0 = Release|x86 26 | {7E3A17A9-BE47-4F20-8B07-149420318D86}.Standalone Release|x86.ActiveCfg = Release|x86 27 | EndGlobalSection 28 | GlobalSection(SolutionProperties) = preSolution 29 | HideSolutionNode = FALSE 30 | EndGlobalSection 31 | GlobalSection(ExtensibilityGlobals) = postSolution 32 | SolutionGuid = {5B73A920-2FB9-4D16-A13A-8B0744805DC7} 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Eric Siron 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 | -------------------------------------------------------------------------------- /Program/Action.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Action.h" 3 | 4 | static void RunAction(const HWND Owner, const std::wstring& Action, const std::wstring& Params) 5 | { 6 | SHELLEXECUTEINFO ActionInfo; 7 | ActionInfo.cbSize = sizeof(SHELLEXECUTEINFO); 8 | ActionInfo.fMask = SEE_MASK_NOCLOSEPROCESS; 9 | ActionInfo.hwnd = Owner; 10 | ActionInfo.lpVerb = L"open"; 11 | ActionInfo.lpFile = Action.c_str(); 12 | ActionInfo.lpParameters = Params.c_str(); 13 | ActionInfo.lpDirectory = NULL; 14 | ActionInfo.nShow = SW_HIDE; 15 | ActionInfo.hInstApp = NULL; 16 | ShellExecuteEx(&ActionInfo); 17 | if ((int)ActionInfo.hInstApp >= 32) // https://docs.microsoft.com/windows/desktop/api/shellapi/ns-shellapi-_shellexecuteinfoa 18 | { 19 | WaitForSingleObject(ActionInfo.hProcess, INFINITE); 20 | } 21 | PostMessage(Owner, WMU_SUBTHREADCOMPLETE, 0, 0); 22 | } 23 | 24 | void StartAction(const HWND Owner, const std::wstring& Action, const std::wstring& Params) 25 | { 26 | std::thread ActionThread(RunAction, Owner, Action, Params); 27 | ActionThread.detach(); 28 | } 29 | -------------------------------------------------------------------------------- /Program/Action.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #define WMU_SUBTHREADCOMPLETE (WM_APP + 1) 7 | 8 | // Spawns a separate thread to execute Action with Params. 9 | // Sends WMU_SUBTHREADCOMPLETE message to Owner when Action completes. 10 | // - will NOT work with COM objects 11 | void StartAction(const HWND Owner, const std::wstring& Action, const std::wstring& Params); -------------------------------------------------------------------------------- /Program/ActionDialog.cpp: -------------------------------------------------------------------------------- 1 | #include "ActionDialog.h" 2 | #include "Action.h" 3 | #include "WindowsUtility.h" 4 | 5 | INT_PTR CALLBACK ActionDialog::SharedDialogProc(HWND hDialog, UINT uMessage, WPARAM wParam, LPARAM lParam) 6 | { 7 | ActionDialog* ThisActionDialog{ nullptr }; 8 | if (uMessage == WM_INITDIALOG) 9 | { 10 | ThisActionDialog = (ActionDialog*)lParam; 11 | ThisActionDialog->HandleDialogAction = hDialog; 12 | } 13 | else 14 | ThisActionDialog = (ActionDialog*)GetWindowLongPtr(hDialog, GWL_USERDATA); 15 | 16 | if (ThisActionDialog) 17 | return ThisActionDialog->ThisDialogProc(uMessage, wParam, lParam); 18 | return FALSE; 19 | } 20 | 21 | void ActionDialog::UpdateStatus(const std::wstring& Message) 22 | { 23 | SetDlgItemText(HandleDialogAction, IDC_STATUSDISPLAY, Message.c_str()); 24 | } 25 | 26 | void ActionDialog::InitiateAction() 27 | { 28 | if (CompletedActions < DialogActions.size()) 29 | { 30 | auto [Description, Command, Params] {DialogActions[CompletedActions]}; 31 | UpdateStatus(Description); 32 | StartAction(HandleDialogAction, Command, Params); 33 | } 34 | else 35 | { 36 | EndDialog(HandleDialogAction, 0); 37 | } 38 | } 39 | 40 | INT_PTR CALLBACK ActionDialog::ThisDialogProc(UINT uMessage, WPARAM wParam, LPARAM lParam) 41 | { 42 | switch (uMessage) 43 | { 44 | case WM_INITDIALOG: 45 | SetWindowLongPtr(HandleDialogAction, GWL_USERDATA, (LONG_PTR)this); 46 | CenterInParent(HandleDialogMain, HandleDialogAction); 47 | InitiateAction(); 48 | break; 49 | case WM_CLOSE: 50 | EndDialog(HandleDialogAction, 0); 51 | return TRUE; 52 | case WMU_SUBTHREADCOMPLETE: 53 | ++CompletedActions; 54 | InitiateAction(); 55 | return TRUE; 56 | } 57 | return FALSE; 58 | } 59 | 60 | ActionDialog::ActionDialog(const HINSTANCE Instance, const HWND Parent, const std::vector>& Actions) 61 | :AppInstance(Instance), HandleDialogMain(Parent), DialogActions(Actions) 62 | { 63 | DialogBoxParam(Instance, MAKEINTRESOURCE(IDD_ACTIONDIALOG), HandleDialogMain, &SharedDialogProc, (LPARAM)this); 64 | PostQuitMessage(0); 65 | } -------------------------------------------------------------------------------- /Program/ActionDialog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "resource.h" 7 | 8 | // Small non-interactive dialog that places itself directly above and in the middle of its owner. 9 | // Displays current task in a static control. 10 | // Carries out designated actions in a separate thread to prevent "not responding" status. 11 | // Closes itself and its parent once all tasks complete. 12 | class ActionDialog 13 | { 14 | private: 15 | /* Windows components */ 16 | const HINSTANCE AppInstance{ 0 }; 17 | const HWND HandleDialogMain{ 0 }; 18 | HWND HandleDialogAction{ 0 }; 19 | static INT_PTR CALLBACK SharedDialogProc(HWND hDialog, UINT uMessage, WPARAM wParam, LPARAM lParam); 20 | INT_PTR CALLBACK ThisDialogProc(UINT uMessage, WPARAM wParam, LPARAM lParam); 21 | 22 | /* Controller */ 23 | const std::vector>& DialogActions; 24 | void UpdateStatus(const std::wstring& Message); 25 | size_t CompletedActions{ 0 }; 26 | void InitiateAction(); 27 | public: 28 | ActionDialog(const HINSTANCE Instance, const HWND Parent, const std::vector>& Actions); 29 | ActionDialog(const ActionDialog&) = delete; 30 | ActionDialog& operator=(const ActionDialog&) = delete; 31 | ActionDialog(ActionDialog&&) = delete; 32 | ActionDialog& operator=(ActionDialog&&) = delete; 33 | }; -------------------------------------------------------------------------------- /Program/CertWAC.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "MainDialog.h" 4 | 5 | int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) 6 | { 7 | MainDialog Main(hInstance); 8 | Main.Show(nCmdShow); 9 | 10 | MSG msg; 11 | BOOL GetMessageResult; 12 | while ((GetMessageResult = GetMessage(&msg, 0, 0, 0)) > 0) 13 | { 14 | if (!IsDialogMessage(Main.GetWindowHandle(), &msg)) 15 | { 16 | TranslateMessage(&msg); 17 | DispatchMessage(&msg); 18 | } 19 | } 20 | return GetMessageResult; 21 | } 22 | -------------------------------------------------------------------------------- /Program/CertWAC.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejsiron/CertWAC/2aac25a546aed6f633b6585be40e23b1b96a60e9/Program/CertWAC.rc -------------------------------------------------------------------------------- /Program/ComputerCertificate.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ComputerCertificate.h" 3 | #include "StringUtility.h" 4 | 5 | const wchar_t* RegExPatternDomainContextOrCanonicalName{ L"(?:DC|CN)=([^,]*)" }; 6 | 7 | static std::wstring GetStringFromFileTime(const FILETIME& Time) 8 | { 9 | std::wstring ReturnTime{}; 10 | SYSTEMTIME SystemTime{ 0 }; 11 | FileTimeToSystemTime(&Time, &SystemTime); 12 | int TimeCharCount; 13 | if (TimeCharCount = GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, DATE_SHORTDATE, &SystemTime, 14 | NULL, NULL, 0, NULL)) 15 | { 16 | ReturnTime.resize(TimeCharCount); 17 | GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, DATE_SHORTDATE, &SystemTime, 18 | NULL, ReturnTime.data(), TimeCharCount, NULL); 19 | } 20 | CleanWindowsString(ReturnTime); 21 | return ReturnTime; 22 | } 23 | 24 | const std::wstring ComputerCertificate::ValidFrom() const 25 | { 26 | return GetStringFromFileTime(validfrom); 27 | } 28 | 29 | const std::wstring ComputerCertificate::ValidTo() const 30 | { 31 | return GetStringFromFileTime(validto); 32 | } 33 | 34 | const bool ComputerCertificate::IsWithinValidityPeriod() const noexcept 35 | { 36 | SYSTEMTIME NowSystemTime{ 0 }; 37 | GetSystemTime(&NowSystemTime); 38 | FILETIME NowFileTime{ 0 }; 39 | SystemTimeToFileTime(&NowSystemTime, &NowFileTime); 40 | ULONGLONG qwNow{ (((ULONGLONG)NowFileTime.dwHighDateTime) << 32) + NowFileTime.dwLowDateTime }; 41 | ULONGLONG qwValidFrom{ (((ULONGLONG)validfrom.dwHighDateTime) << 32) + validfrom.dwLowDateTime }; 42 | ULONGLONG qwValidTo{ (((ULONGLONG)validto.dwHighDateTime) << 32) + validto.dwLowDateTime }; 43 | return (qwValidFrom < qwNow && qwNow < qwValidTo); 44 | } 45 | 46 | std::wstring ComputerCertificate::FQDNFromSimpleRDN(const std::wstring& RDN) 47 | { 48 | std::vectorFQDNComponents{}; 49 | const std::wregex FQDNComponentMatch{ RegExPatternDomainContextOrCanonicalName }; 50 | using rxit = std::wsregex_iterator; 51 | rxit EmptyRegexIterator{}; 52 | for (rxit DomainPartWalker(RDN.cbegin(), RDN.cend(), FQDNComponentMatch); 53 | DomainPartWalker != EmptyRegexIterator; ++DomainPartWalker) 54 | { 55 | FQDNComponents.emplace_back((*DomainPartWalker)[1]); 56 | } 57 | return JoinString(L".", FQDNComponents); 58 | } -------------------------------------------------------------------------------- /Program/ComputerCertificate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "ErrorRecord.h" 6 | 7 | // represents a PKI certificate from the local Windows certificate store 8 | // only exposes portions germane to this application 9 | class ComputerCertificate 10 | { 11 | private: 12 | std::wstring subjectname{}; 13 | std::wstring issuer{}; 14 | std::wstring thumbprint{}; 15 | FILETIME validfrom{ 0 }; 16 | FILETIME validto{ 0 }; 17 | std::wstring subjectalternatenames{}; 18 | bool serverauthentication{ false }; 19 | bool privatekey{ false }; 20 | friend std::pair> GetComputerCertificates(); 21 | 22 | public: 23 | ComputerCertificate() {} 24 | ComputerCertificate(const ComputerCertificate&) = default; 25 | ComputerCertificate& operator=(const ComputerCertificate&) = default; 26 | ComputerCertificate(ComputerCertificate&&)=default; 27 | ComputerCertificate& operator=(ComputerCertificate&&) = default; 28 | const std::wstring& SubjectName() const { return subjectname; } 29 | const std::wstring Issuer() const { return issuer; } 30 | const std::wstring Thumbprint() const { return thumbprint; } 31 | const std::wstring ValidFrom() const; 32 | const std::wstring ValidTo() const; 33 | std::wstring SubjectAlternateNames() const noexcept { return subjectalternatenames; } 34 | const bool HasServerAuthentication() const noexcept { return serverauthentication; } 35 | const bool HasPrivateKey() const noexcept { return privatekey; } 36 | const bool IsWithinValidityPeriod() const noexcept; 37 | static std::wstring FQDNFromSimpleRDN(const std::wstring& RDN); 38 | }; 39 | 40 | std::pair> GetComputerCertificates(); -------------------------------------------------------------------------------- /Program/ErrorDialog.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ErrorDialog.h" 4 | #include "ErrorRecord.h" 5 | #include "WindowsUtility.h" 6 | 7 | INT_PTR CALLBACK ErrorDialog::SharedDialogProc(HWND hDialog, UINT uMessage, WPARAM wParam, LPARAM lParam) 8 | { 9 | ErrorDialog* ThisErrorDialog{ nullptr }; 10 | if (uMessage == WM_INITDIALOG) 11 | { 12 | ThisErrorDialog = (ErrorDialog*)lParam; 13 | ThisErrorDialog->HandleDialogError = hDialog; 14 | BringWindowToTop(hDialog); 15 | } 16 | else 17 | ThisErrorDialog = (ErrorDialog*)GetWindowLongPtr(hDialog, GWL_USERDATA); 18 | 19 | if (ThisErrorDialog) 20 | return ThisErrorDialog->ThisDialogProc(uMessage, wParam, lParam); 21 | return FALSE; 22 | } 23 | 24 | INT_PTR CALLBACK ErrorDialog::ThisDialogProc(UINT uMessage, WPARAM wParam, LPARAM lParam) 25 | { 26 | switch (uMessage) 27 | { 28 | case WM_COMMAND: 29 | switch (LOWORD(wParam)) 30 | { 31 | case IDOK: 32 | SendMessage(HandleDialogError, WM_CLOSE, 0, 0); 33 | return TRUE; 34 | } 35 | break; 36 | case WM_INITDIALOG: 37 | SetWindowLongPtr(HandleDialogError, GWL_USERDATA, (LONG_PTR)this); 38 | CenterInParent(HandleDialogParent, HandleDialogError); 39 | SetDlgItemText(HandleDialogError, IDC_ERRORCODE, std::to_wstring(Error.ErrorCode()).c_str()); 40 | SetDlgItemText(HandleDialogError, IDC_ERRORMESSAGE, Error.ErrorMessage().c_str()); 41 | SetDlgItemText(HandleDialogError, IDC_ACTIVITYMESSAGE, Error.Activity().c_str()); 42 | SetFocus(GetDlgItem(HandleDialogError, IDOK)); 43 | break; 44 | case WM_CLOSE: 45 | EndDialog(HandleDialogError, 0); 46 | return TRUE; 47 | } 48 | return FALSE; 49 | } 50 | 51 | static std::wstring FormatErrorForPopup(const DWORD ErrorCode, const std::wstring& ErrorMessage, const std::wstring& Activity) noexcept 52 | { 53 | return std::wstring{ L"Activity: " + Activity + L"\r\nErrorCode: " + std::to_wstring(ErrorCode) + L": " + ErrorMessage }; 54 | } 55 | 56 | ErrorDialog::ErrorDialog(const HINSTANCE Instance, const HWND Parent, const ErrorRecord& ErrorDetails) 57 | :HandleDialogParent(Parent), Error(ErrorDetails) 58 | { 59 | DialogBoxParam(Instance, MAKEINTRESOURCE(IDD_ERRORDIALOG), Parent, &SharedDialogProc, (LPARAM)this); 60 | } -------------------------------------------------------------------------------- /Program/ErrorDialog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "ErrorRecord.h" 6 | #include "resource.h" 7 | 8 | class ErrorDialog 9 | { 10 | private: 11 | /* Windows components */ 12 | const HINSTANCE AppInstance{ 0 }; 13 | HWND HandleDialogParent{ 0 }; 14 | HWND HandleDialogError{ 0 }; 15 | static INT_PTR CALLBACK SharedDialogProc(HWND hDialog, UINT uMessage, WPARAM wParam, LPARAM lParam); 16 | INT_PTR CALLBACK ThisDialogProc(UINT uMessage, WPARAM wParam, LPARAM lParam); 17 | 18 | /* Controller */ 19 | const ErrorRecord Error; 20 | 21 | public: 22 | ErrorDialog(const HINSTANCE Instance, const HWND Parent, const ErrorRecord& ErrorDetails); 23 | ErrorDialog(const ErrorDialog&) = delete; 24 | ErrorDialog& operator=(const ErrorDialog&) = delete; 25 | ErrorDialog(ErrorDialog&&) = delete; 26 | ErrorDialog& operator=(ErrorDialog&&) = delete; 27 | }; -------------------------------------------------------------------------------- /Program/ErrorRecord.cpp: -------------------------------------------------------------------------------- 1 | #include "ErrorRecord.h" 2 | 3 | const std::wstring ErrorRecord::TranslateErrorCode(DWORD ErrorCode) 4 | { 5 | std::wstring ErrorMessage{}; 6 | LPTSTR szLocalErrorBuffer{ nullptr }; 7 | 8 | FormatMessage( 9 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 10 | NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 11 | (LPTSTR)&szLocalErrorBuffer, 0, NULL 12 | ); 13 | if (szLocalErrorBuffer) 14 | { 15 | ErrorMessage.assign(szLocalErrorBuffer); 16 | LocalFree(szLocalErrorBuffer); 17 | } 18 | else 19 | { 20 | ErrorMessage.assign(L"Unable to process error message for code " + std::to_wstring(ErrorCode)); 21 | } 22 | return ErrorMessage; 23 | } 24 | -------------------------------------------------------------------------------- /Program/ErrorRecord.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class ErrorRecord 6 | { 7 | private: 8 | DWORD errorcode; 9 | std::wstring activity; 10 | public: 11 | ErrorRecord(DWORD Code = 0, std::wstring ActivityText = L"Unknown") : errorcode(Code), activity(std::move(ActivityText)) {} 12 | ErrorRecord(const ErrorRecord&) = default; 13 | ErrorRecord& operator=(const ErrorRecord&) = default; 14 | ErrorRecord(ErrorRecord&&) = default; 15 | ErrorRecord& operator=(ErrorRecord&&) = default; 16 | const DWORD ErrorCode() const noexcept { return errorcode; } 17 | static const std::wstring TranslateErrorCode(DWORD ErrorCode); 18 | const std::wstring ErrorMessage() const noexcept { return TranslateErrorCode(errorcode); } 19 | const std::wstring Activity() const noexcept { return activity; } 20 | }; 21 | -------------------------------------------------------------------------------- /Program/InstallInfo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "InstallInfo.h" 5 | 6 | const wchar_t* RegistryExpectedAppRoot{ L"SOFTWARE\\Microsoft\\ServerManagementGateway" }; 7 | const wchar_t* PortFieldName{ L"SmePort" }; 8 | const wchar_t* RegistryUninstallRoot{ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall" }; 9 | const wchar_t* ExpectedApplicationName{ L"Windows Admin Center" }; 10 | const wchar_t* AppDisplayNameFieldName{ L"DisplayName" }; 11 | const wchar_t* AppModifyPathFieldName{ L"ModifyPath" }; 12 | 13 | const wchar_t* ActivityAccessingUninstallRegistry{ L"Accessing uninstall registry branch" }; 14 | const wchar_t* ActivitySearchingForInstallation{ L"Searching for WAC installation key" }; 15 | const wchar_t* ActivityReadingAppModifyKVP{ L"Reading app modify command line" }; 16 | const wchar_t* ActivityAccessingAppRegistry{ L"Accessing application registry branch" }; 17 | const wchar_t* ActivityReadingPortKVP{ L"Reading port value from registry" }; 18 | const wchar_t* ActivitySettingPortKVP{ L"Writing port value to registry" }; 19 | 20 | constexpr DWORD REG_READ_ACCESS{ KEY_READ | KEY_WOW64_64KEY }; 21 | constexpr DWORD KVPValueLength{ 1024 }; 22 | 23 | auto DeleteHKEY = [](HKEY Key) noexcept { RegCloseKey(Key); }; 24 | using RegistryKey = std::unique_ptr; 25 | 26 | static std::pair OpenRegistryKey(const HKEY StartKey, const std::wstring& SubkeyName, const bool WriteAccess = false) noexcept 27 | { 28 | HKEY DesiredKey{ 0 }; 29 | LSTATUS Result{ RegOpenKeyEx(StartKey, SubkeyName.c_str(), 0, REG_READ_ACCESS | (WriteAccess ? KEY_WRITE : 0), &DesiredKey) }; 30 | return std::make_pair(Result, RegistryKey(DesiredKey, DeleteHKEY)); 31 | } 32 | 33 | static std::pair GetMaxSubkeyNameLength(HKEY ParentKey) noexcept 34 | { 35 | DWORD MaxSubkeySize{ 0 }; 36 | LSTATUS SizeQueryResult{ RegQueryInfoKey(ParentKey, NULL, NULL, NULL, NULL, &MaxSubkeySize, NULL, NULL, NULL, NULL, NULL, NULL) }; 37 | return std::make_pair(SizeQueryResult, MaxSubkeySize); 38 | } 39 | 40 | static std::pair GetKVPStringValue(HKEY ParentKey, const wchar_t* KVPKeyName) noexcept 41 | { 42 | DWORD ValueType; 43 | DWORD RetrievedValueLength{ KVPValueLength }; 44 | wchar_t ReceiveBuffer[KVPValueLength]; 45 | SecureZeroMemory(ReceiveBuffer, KVPValueLength); 46 | LSTATUS ValueQueryResult{ RegQueryValueEx(ParentKey, KVPKeyName, NULL, &ValueType, (LPBYTE)ReceiveBuffer, &RetrievedValueLength) }; 47 | if (ValueQueryResult == ERROR_SUCCESS && !((ValueType == REG_SZ) || (ValueType == REG_EXPAND_SZ))) 48 | { 49 | ValueQueryResult = ERROR_FILE_NOT_FOUND; 50 | ReceiveBuffer[0] = 0; 51 | } 52 | return std::make_pair(ValueQueryResult, std::wstring{ ReceiveBuffer }); 53 | } 54 | 55 | static std::pair FindSubkeyWithExpectedKVP(HKEY ParentKey, const wchar_t* KVPKeyName, const wchar_t* KVPKeyValue) noexcept 56 | { 57 | LSTATUS LastResult{ 0 }; 58 | std::wstring SubkeyName{}; 59 | auto[GetMaxSubkeyLengthResult, SubkeyNameLength] { GetMaxSubkeyNameLength(ParentKey)}; 60 | LastResult = GetMaxSubkeyLengthResult; 61 | if (GetMaxSubkeyLengthResult == ERROR_SUCCESS) 62 | { 63 | std::wstring RetrievedKeyName{}; 64 | RetrievedKeyName.reserve(++SubkeyNameLength); 65 | LSTATUS SubEnumResult{ 0 }; 66 | DWORD SubkeyIndex{ 0 }; 67 | DWORD SubkeyNameRetrievedSize{ SubkeyNameLength }; 68 | while ((SubEnumResult = RegEnumKeyEx(ParentKey, SubkeyIndex, RetrievedKeyName.data(), &SubkeyNameRetrievedSize, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS) 69 | { 70 | ++SubkeyIndex; 71 | SubkeyNameRetrievedSize = SubkeyNameLength; // reset during each iteration or the next will fail 72 | auto[SubkeyOpenResult, Subkey] { OpenRegistryKey(ParentKey, RetrievedKeyName)}; 73 | if (SubkeyOpenResult != ERROR_SUCCESS) 74 | { 75 | continue; 76 | } 77 | 78 | auto[GetValueResult, Value] { GetKVPStringValue(Subkey.get(), KVPKeyName)}; 79 | if (GetValueResult == ERROR_SUCCESS && Value == KVPKeyValue) 80 | { 81 | SubkeyName.assign(RetrievedKeyName.c_str()); // direct assignment does not work 82 | } 83 | } 84 | switch (SubEnumResult) 85 | { 86 | case ERROR_NO_MORE_ITEMS: 87 | GetMaxSubkeyLengthResult = SubkeyName.length() > 0 ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND; 88 | break; 89 | default: 90 | GetMaxSubkeyLengthResult = SubEnumResult; 91 | break; 92 | } 93 | } 94 | return std::make_pair(GetMaxSubkeyLengthResult, SubkeyName); 95 | } 96 | 97 | static std::pair GetModifyPath() noexcept 98 | { 99 | std::wstring MSICmd{}; 100 | auto[RootOpenResult, RootKey] { OpenRegistryKey(HKEY_LOCAL_MACHINE, RegistryUninstallRoot)}; 101 | LSTATUS ActivityResult{ RootOpenResult }; 102 | if (ActivityResult != ERROR_SUCCESS) 103 | { 104 | return std::pair(ErrorRecord(RootOpenResult, ActivityAccessingUninstallRegistry), MSICmd); 105 | } 106 | 107 | auto[InstallOpenResult, OptInstallKeyName] { FindSubkeyWithExpectedKVP(RootKey.get(), 108 | AppDisplayNameFieldName, ExpectedApplicationName)}; 109 | ActivityResult = InstallOpenResult; 110 | if (InstallOpenResult == ERROR_SUCCESS) 111 | { 112 | auto[OptOpenResult, OptInstallKey] {OpenRegistryKey(RootKey.get(), OptInstallKeyName)}; 113 | ActivityResult = OptOpenResult; 114 | if (ActivityResult == ERROR_SUCCESS) 115 | { 116 | auto[GetPathResult, DiscoveredPath] { GetKVPStringValue(OptInstallKey.get(), AppModifyPathFieldName)}; 117 | ActivityResult = GetPathResult; 118 | MSICmd = DiscoveredPath; 119 | } 120 | } 121 | return std::pair(ErrorRecord(ActivityResult, ActivitySearchingForInstallation), MSICmd); 122 | } 123 | 124 | static std::pair GetListeningPort() noexcept 125 | { 126 | int Port{ 0 }; 127 | auto[RootOpenResult, RootKey] { OpenRegistryKey(HKEY_LOCAL_MACHINE, RegistryExpectedAppRoot)}; 128 | if (RootOpenResult != ERROR_SUCCESS) 129 | { 130 | return std::pair(ErrorRecord(RootOpenResult, ActivityAccessingAppRegistry), Port); 131 | } 132 | auto[GetPortResult, DiscoveredPort] { GetKVPStringValue(RootKey.get(), PortFieldName)}; 133 | if (GetPortResult == ERROR_SUCCESS) 134 | Port = std::stoi(std::wstring{ DiscoveredPort }); 135 | return std::pair(ErrorRecord(GetPortResult, ActivityReadingPortKVP), Port); 136 | } 137 | 138 | std::tuple GetWACInstallInfo() noexcept 139 | { 140 | auto [GetModifyRegistryResult, ModifyPath] {GetModifyPath()}; 141 | if (GetModifyRegistryResult.ErrorCode() == ERROR_SUCCESS) 142 | { 143 | auto [GetPortResult, Port] {GetListeningPort()}; 144 | return std::make_tuple(GetPortResult, ModifyPath, Port); 145 | } 146 | return std::make_tuple(GetModifyRegistryResult, std::wstring{}, 0); 147 | } 148 | 149 | ErrorRecord SetListeningPort(const unsigned int Port) noexcept 150 | { 151 | auto[RootOpenResult, RootKey] { OpenRegistryKey(HKEY_LOCAL_MACHINE, RegistryExpectedAppRoot, true)}; 152 | if (RootOpenResult != ERROR_SUCCESS) 153 | { 154 | return ErrorRecord(RootOpenResult, ActivityAccessingAppRegistry); 155 | } 156 | auto PortBytes{ std::to_wstring(Port) }; 157 | return ErrorRecord(RegSetValueEx( 158 | RootKey.get(), PortFieldName, 0, REG_SZ, (BYTE*)PortBytes.c_str(), 159 | (PortBytes.size() + 1) * 2), // wants bytes, these are wchar_ts 160 | ActivitySettingPortKVP); 161 | } 162 | -------------------------------------------------------------------------------- /Program/InstallInfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "ErrorRecord.h" 5 | 6 | // Retrieves installation information for Windows Admin Center 7 | // - ErrorRecord: the last error message the function encountered 8 | // - wstring: WAC's MSI ModifyPath setting 9 | // - int: current WAC port number 10 | std::tuple GetWACInstallInfo() noexcept; 11 | 12 | // Updates WAC's relevant registry key to reflect the new port 13 | ErrorRecord SetListeningPort(const unsigned int Port) noexcept; -------------------------------------------------------------------------------- /Program/LoadCertificates.cpp: -------------------------------------------------------------------------------- 1 | #pragma comment(lib, "crypt32.lib") 2 | #pragma comment(lib, "NCrypt.lib") 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "ComputerCertificate.h" 8 | #include "StringUtility.h" 9 | 10 | const char* ServerAuthenticationOID{ "1.3.6.1.5.5.7.3.1" }; 11 | 12 | class CertificateStore 13 | { 14 | private: 15 | HCERTSTORE Store; 16 | public: 17 | CertificateStore(HCERTSTORE NewStore) :Store(NewStore) {} 18 | ~CertificateStore() { if (Store) CertCloseStore(Store, 0); } 19 | HCERTSTORE operator()() noexcept { return Store; } 20 | const bool IsValid() const noexcept { return Store != nullptr; } 21 | }; 22 | 23 | enum class CertNames 24 | { 25 | Subject, 26 | Issuer 27 | }; 28 | 29 | static std::wstring GetNameFromCertificate(PCCERT_CONTEXT pCertContext, CertNames DesiredCertName) 30 | { 31 | PCERT_NAME_BLOB DesiredName; 32 | switch (DesiredCertName) 33 | { 34 | case CertNames::Issuer: 35 | DesiredName = &pCertContext->pCertInfo->Issuer; 36 | break; 37 | default: 38 | DesiredName = &pCertContext->pCertInfo->Subject; 39 | break; 40 | } 41 | 42 | DWORD NameSize{ 0 }; 43 | std::wstring CertName{}; 44 | NameSize = CertNameToStr(pCertContext->dwCertEncodingType, DesiredName, 45 | CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, NULL, NameSize); 46 | if (NameSize) 47 | { 48 | CertName.resize(NameSize); 49 | CertNameToStr(pCertContext->dwCertEncodingType, DesiredName, 50 | CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG, CertName.data(), NameSize); 51 | } 52 | CleanWindowsString(CertName); 53 | return std::move(CertName); 54 | } 55 | 56 | std::pair> GetComputerCertificates() 57 | { 58 | const wchar_t* StoreName{ L"MY" }; 59 | std::vector Certificates{}; 60 | 61 | auto ComputerStore = CertificateStore( 62 | CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, StoreName)); 63 | 64 | if (!ComputerStore.IsValid()) 65 | { 66 | return std::make_pair(ErrorRecord(GetLastError(), L"Opening computer certificate store"), Certificates); 67 | } 68 | 69 | PCCERT_CONTEXT pCertContext{ nullptr }; 70 | while (pCertContext = CertEnumCertificatesInStore(ComputerStore(), pCertContext)) 71 | { 72 | ComputerCertificate ThisCert{}; 73 | ThisCert.subjectname = std::move(GetNameFromCertificate(pCertContext, CertNames::Subject)); 74 | ThisCert.issuer = std::move(GetNameFromCertificate(pCertContext, CertNames::Issuer)); 75 | ThisCert.validfrom = pCertContext->pCertInfo->NotBefore; 76 | ThisCert.validto = pCertContext->pCertInfo->NotAfter; 77 | 78 | // Enhanced Key Usage 79 | DWORD EnhancedKeyUsageSize{ 0 }; 80 | if (CertGetEnhancedKeyUsage(pCertContext, 0, NULL, &EnhancedKeyUsageSize)) 81 | { 82 | std::vector EKU{}; 83 | EKU.resize(EnhancedKeyUsageSize); 84 | if (CertGetEnhancedKeyUsage(pCertContext, 0, (CERT_ENHKEY_USAGE*)EKU.data(), &EnhancedKeyUsageSize)) 85 | { 86 | for (auto i{ 0 }; i != ((CERT_ENHKEY_USAGE*)(EKU.data()))->cUsageIdentifier; ++i) 87 | { 88 | if (std::string{ ServerAuthenticationOID } == std::string{ ((CERT_ENHKEY_USAGE*)(EKU.data()))->rgpszUsageIdentifier[i] }) 89 | { 90 | ThisCert.serverauthentication = true; 91 | } 92 | } 93 | 94 | } 95 | } 96 | 97 | // SAN 98 | PCERT_EXTENSION Extension = nullptr; 99 | PCERT_ALT_NAME_INFO pAlternateNameInfo = nullptr; 100 | PCERT_ALT_NAME_ENTRY pAlternateNameEntry = nullptr; 101 | 102 | std::string ExtensionOID{}; 103 | for (auto i{ 0 }; i != pCertContext->pCertInfo->cExtension; ++i) 104 | { 105 | Extension = &pCertContext->pCertInfo->rgExtension[i]; 106 | ExtensionOID.assign(Extension->pszObjId); 107 | if (ExtensionOID == szOID_SUBJECT_ALT_NAME || ExtensionOID == szOID_SUBJECT_ALT_NAME2) 108 | { 109 | DWORD DataSize{ 0 }; 110 | if (CryptFormatObject(pCertContext->dwCertEncodingType, 0, 0, NULL, szOID_SUBJECT_ALT_NAME2, 111 | Extension->Value.pbData, Extension->Value.cbData, NULL, &DataSize)) 112 | { 113 | ThisCert.subjectalternatenames.resize(DataSize); 114 | if (CryptFormatObject(pCertContext->dwCertEncodingType, 0, 0, NULL, szOID_SUBJECT_ALT_NAME2, 115 | Extension->Value.pbData, Extension->Value.cbData, 116 | (void*)ThisCert.subjectalternatenames.data(), &DataSize)) 117 | { 118 | CleanWindowsString(ThisCert.subjectalternatenames); 119 | } 120 | } 121 | } 122 | } 123 | 124 | // thumbprint 125 | DWORD CertSHA1HashSize{ 0 }; 126 | if (CryptHashCertificate2(BCRYPT_SHA1_ALGORITHM, 0, NULL, pCertContext->pbCertEncoded, 127 | pCertContext->cbCertEncoded, NULL, &CertSHA1HashSize)) 128 | { 129 | std::vector ThumbprintBytes; 130 | ThumbprintBytes.resize(CertSHA1HashSize); 131 | if (CryptHashCertificate2(BCRYPT_SHA1_ALGORITHM, 0, NULL, pCertContext->pbCertEncoded, 132 | pCertContext->cbCertEncoded, ThumbprintBytes.data(), &CertSHA1HashSize)) 133 | { 134 | std::wstringstream HashStream; 135 | HashStream << std::hex << std::uppercase; 136 | for (auto const& HashByte : ThumbprintBytes) 137 | { 138 | HashStream << std::setw(2) << std::setfill(L'0') << HashByte; 139 | } 140 | ThisCert.thumbprint = HashStream.str(); 141 | } 142 | } 143 | 144 | // detect private key 145 | HCRYPTPROV_OR_NCRYPT_KEY_HANDLE PrivateKeyHandle; 146 | DWORD PrivateKeyInfo{ 0 }; 147 | DWORD KeySpec{ 0 }; 148 | BOOL MustFree{ FALSE }; 149 | if (CryptAcquireCertificatePrivateKey(pCertContext, CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG, NULL, &PrivateKeyHandle, &KeySpec, &MustFree)) 150 | { 151 | ThisCert.privatekey = true; 152 | } 153 | if (MustFree) 154 | { 155 | if (KeySpec == CERT_NCRYPT_KEY_SPEC) { NCryptFreeObject(PrivateKeyHandle); } 156 | else { CryptReleaseContext(PrivateKeyHandle, 0); } 157 | } 158 | Certificates.emplace_back(std::move(ThisCert)); 159 | } 160 | if (pCertContext != nullptr) 161 | CertFreeCertificateContext(pCertContext); 162 | 163 | return std::make_pair(ErrorRecord(ERROR_SUCCESS, L"Reading certificates"), Certificates); 164 | } 165 | -------------------------------------------------------------------------------- /Program/MainDialog.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "ComputerCertificate.h" 4 | #include "resource.h" 5 | 6 | class MainDialog 7 | { 8 | private: 9 | /* Windows components */ 10 | HINSTANCE AppInstance{ 0 }; 11 | HWND HandleDialogMain{ 0 }; 12 | HICON AppIcon{ 0 }; 13 | static INT_PTR CALLBACK SharedDialogProc(HWND hDialog, UINT uMessage, WPARAM wParam, LPARAM lParam); 14 | INT_PTR CALLBACK ThisDialogProc(UINT uMessage, WPARAM wParam, LPARAM lParam); 15 | HBITMAP TinyGreenBox{ 0 }; 16 | HBITMAP TinyRedBox{ 0 }; 17 | 18 | /* Controller components */ 19 | std::vector Certificates{}; 20 | std::wstring CmdlineModifyPath{}; 21 | std::wstring Thumbprint{}; 22 | const void InitDialog() noexcept; 23 | void Refresh(); 24 | void EnableDialogItem(const int DialogItem, const bool Enable = true) const noexcept { EnableWindow(GetDlgItem(HandleDialogMain, DialogItem), Enable); }; 25 | void DisplayCertificateList() noexcept; 26 | void DisplayCertificate(); 27 | void SetPictureBoxImage(const INT PictureBoxID, const bool Good = true); 28 | void StartActions(); 29 | 30 | public: 31 | MainDialog(HINSTANCE Instance); 32 | MainDialog(const MainDialog&) = delete; 33 | MainDialog& operator=(const MainDialog&) = delete; 34 | MainDialog(MainDialog&&) = default; 35 | MainDialog& operator=(MainDialog&&) = default; 36 | void Show(int ShowCommand) const noexcept { ShowWindow(HandleDialogMain, ShowCommand); } 37 | const HWND GetWindowHandle() const noexcept { return HandleDialogMain; } 38 | void AssignModifyPath(const std::wstring& NewPath) { CmdlineModifyPath = NewPath; } 39 | }; -------------------------------------------------------------------------------- /Program/MainDialogController.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ActionDialog.h" 3 | #include "ErrorDialog.h" 4 | #include "InstallInfo.h" 5 | #include "MainDialog.h" 6 | #include "StringUtility.h" 7 | 8 | const wchar_t* MSIMiddle{ L" /qb SSL_CERTIFICATE_OPTION=installed SME_THUMBPRINT=" }; 9 | const wchar_t* ActivityPortValidation{ L"Verifying that port is in range of 1 to 65535" }; 10 | const wchar_t* ActionDescriptionRunningInstaller{ L"Running installer" }; 11 | const wchar_t* ActionDescriptionStartingService{ L"Starting WAC service" }; 12 | const wchar_t* ActionServiceController{ L"sc.exe" }; // do not localize 13 | const wchar_t* ParamsServiceController{ L"start ServerManagementGateway" }; // do not localize 14 | 15 | static std::pair BuildMsiCommandSet(std::wstring RegistryPath, std::wstring Thumbprint) 16 | { 17 | std::vector MsiSet{ SplitString(L" ", RegistryPath) }; 18 | if (MsiSet.size() > 1) 19 | { 20 | return std::make_pair(MsiSet[0], (MsiSet[1] + MSIMiddle + Thumbprint)); 21 | } 22 | else 23 | { // these values should always be "msiexec.exe[SPACE]...", but this might work if something changed 24 | return std::make_pair(RegistryPath, MSIMiddle + Thumbprint); 25 | } 26 | } 27 | 28 | void MainDialog::StartActions() 29 | { 30 | BOOL PortReadResult; 31 | unsigned int Port{ GetDlgItemInt(HandleDialogMain, IDC_PORT, &PortReadResult, FALSE) }; 32 | if (PortReadResult != TRUE || Port < 1 || Port > 65535) // max valid range for IP ports 33 | { 34 | ErrorDialog(AppInstance, HandleDialogMain, ErrorRecord(ERROR_INVALID_PARAMETER, ActivityPortValidation)); 35 | SendMessage(GetDlgItem(HandleDialogMain, IDC_PORT), EM_SETSEL, 0, -1); 36 | SetFocus(GetDlgItem(HandleDialogMain, IDC_PORT)); 37 | return; 38 | } 39 | ErrorRecord SetPortError{ SetListeningPort(Port) }; 40 | if (SetPortError.ErrorCode() != ERROR_SUCCESS) 41 | { 42 | ErrorDialog(AppInstance, HandleDialogMain, SetPortError); 43 | } 44 | std::vector> Actions; 45 | auto [MsiCmd, MsiParams] {BuildMsiCommandSet(CmdlineModifyPath, Thumbprint)}; 46 | Actions.emplace_back(std::tuple(ActionDescriptionRunningInstaller, MsiCmd, MsiParams)); 47 | Actions.emplace_back(std::tuple(ActionDescriptionStartingService, ActionServiceController, ParamsServiceController)); 48 | ActionDialog ActionWindow(AppInstance, HandleDialogMain, Actions); 49 | } 50 | 51 | void MainDialog::Refresh() 52 | { 53 | // load and process WAC installation information 54 | auto[InfoLoadError, ModifyPath, Port] {GetWACInstallInfo()}; 55 | SetPictureBoxImage(IDC_ICONWACINSTALLED, InfoLoadError.ErrorCode() == ERROR_SUCCESS); 56 | CmdlineModifyPath = ModifyPath; 57 | SetDlgItemText(HandleDialogMain, IDC_PORT, std::to_wstring(Port).c_str()); 58 | if (InfoLoadError.ErrorCode() != ERROR_SUCCESS) 59 | ErrorDialog(AppInstance, HandleDialogMain, InfoLoadError); 60 | 61 | // load and process installed computer certificates 62 | auto[CertLoadError, CertificateList] {GetComputerCertificates()}; 63 | Certificates = CertificateList; 64 | DisplayCertificateList(); 65 | if (CertLoadError.ErrorCode() != ERROR_SUCCESS) 66 | ErrorDialog(AppInstance, HandleDialogMain, CertLoadError); 67 | } -------------------------------------------------------------------------------- /Program/MainDialogUI.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "ComputerCertificate.h" 4 | #include "ErrorDialog.h" 5 | #include "ErrorRecord.h" 6 | #include "InstallInfo.h" 7 | #include "MainDialog.h" 8 | #include "StringUtility.h" 9 | 10 | const wchar_t* NewLine{ L"\r\n" }; 11 | const wchar_t* ActivitySetupEnvironment{ L"Setting up application environment" }; 12 | const wchar_t* NoCertsFound{ L"No computer certificates found" }; 13 | const wchar_t* RegexPatternCommaWithOptionalSpace{ L", ?" }; 14 | const wchar_t* FieldSubject{ L"Subject:" }; // program will determine whether or not to prepend a L" " 15 | const wchar_t* FieldIssuer{ L"Issuer: " }; 16 | const wchar_t* FieldSAN{ L"Subject Alternate Names:" }; // program will determine whether or not to prepend a L" " 17 | const wchar_t* FieldValidFrom{ L"Valid from: " }; 18 | const wchar_t* FieldValidTo{ L"Valid to: " }; 19 | const wchar_t* FieldThumbprint{ L"Thumbprint: " }; 20 | 21 | static std::wstring ConvertSplitForDisplay(const std::vector& SplitInput) 22 | { 23 | std::wstring Output{}; 24 | if (SplitInput.size() > 1) 25 | { 26 | for (auto const& InputLine : SplitInput) 27 | { 28 | Output.append(L"\r\n- " + InputLine); 29 | } 30 | } 31 | else if (SplitInput.size() == 1) 32 | Output = L" " + SplitInput[0]; 33 | Output.append(NewLine); 34 | return Output; 35 | } 36 | 37 | static std::wstring ConvertSplitForDisplay(const std::wstring& GluePattern, const std::wstring& Composite) 38 | { 39 | return ConvertSplitForDisplay(SplitString(GluePattern, Composite)); 40 | } 41 | 42 | INT_PTR CALLBACK MainDialog::SharedDialogProc(HWND hDialog, UINT uMessage, WPARAM wParam, LPARAM lParam) 43 | { 44 | MainDialog* AppDialog{ nullptr }; 45 | if (uMessage == WM_INITDIALOG) 46 | { 47 | AppDialog = (MainDialog*)lParam; 48 | AppDialog->HandleDialogMain = hDialog; 49 | } 50 | else 51 | AppDialog = (MainDialog*)GetWindowLongPtr(hDialog, GWL_USERDATA); 52 | 53 | if (AppDialog) 54 | return AppDialog->ThisDialogProc(uMessage, wParam, lParam); 55 | return FALSE; 56 | } 57 | 58 | INT_PTR CALLBACK MainDialog::ThisDialogProc(UINT uMessage, WPARAM wParam, LPARAM lParam) 59 | { 60 | switch (uMessage) 61 | { 62 | case WM_COMMAND: 63 | switch (LOWORD(wParam)) 64 | { 65 | case IDOK: 66 | StartActions(); 67 | return TRUE; 68 | case IDCANCEL: 69 | SendMessage(HandleDialogMain, WM_CLOSE, 0, 0); 70 | return TRUE; 71 | case IDC_CERTLIST: 72 | if (HIWORD(wParam) == CBN_SELCHANGE) 73 | DisplayCertificate(); 74 | return TRUE; 75 | case IDC_REFRESH: 76 | Refresh(); 77 | return TRUE; 78 | } 79 | break; 80 | case WM_INITDIALOG: 81 | { 82 | SetLastError(ERROR_SUCCESS); 83 | SetWindowLongPtr(HandleDialogMain, GWL_USERDATA, (LONG_PTR)this); 84 | if (auto LastError = GetLastError()) 85 | { 86 | ErrorDialog(AppInstance, HandleDialogMain, ErrorRecord(LastError, ActivitySetupEnvironment)); 87 | PostQuitMessage(LastError); 88 | } 89 | else 90 | { 91 | InitDialog(); 92 | } 93 | } 94 | break; 95 | case WM_CLOSE: 96 | DestroyWindow(HandleDialogMain); 97 | DeleteObject(TinyGreenBox); 98 | DeleteObject(TinyRedBox); 99 | return TRUE; 100 | case WM_DESTROY: 101 | PostQuitMessage(0); 102 | return TRUE; 103 | } 104 | return FALSE; 105 | } 106 | 107 | void MainDialog::DisplayCertificateList() noexcept 108 | { 109 | EnableDialogItem(IDC_CERTLIST, false); 110 | size_t ItemCount{ Certificates.size() }; 111 | HWND CertList{ GetDlgItem(HandleDialogMain, IDC_CERTLIST) }; 112 | SendMessage(CertList, CB_RESETCONTENT, 0, 0); 113 | 114 | if (ItemCount) 115 | { 116 | for (auto const& Certificate : Certificates) 117 | { 118 | SendMessage(CertList, CB_ADDSTRING, 0, (LPARAM)Certificate.FQDNFromSimpleRDN(Certificate.SubjectName()).c_str()); 119 | } 120 | RECT CertListComboSize{ 0 }; 121 | GetWindowRect(CertList, &CertListComboSize); 122 | SetWindowPos(CertList, 0, 0, 0, CertListComboSize.right - CertListComboSize.left, 123 | (SendMessage(CertList, CB_GETITEMHEIGHT, 0, 0) * ItemCount * 2), SWP_NOMOVE | SWP_NOZORDER); 124 | EnableDialogItem(IDC_CERTLIST, true); 125 | } 126 | else 127 | SendMessage(CertList, CB_ADDSTRING, 0, (LPARAM)NoCertsFound); 128 | SendMessage(CertList, CB_SETCURSEL, 0, 0); 129 | DisplayCertificate(); 130 | } 131 | 132 | void MainDialog::DisplayCertificate() 133 | { 134 | std::wstring CertificateText{}; 135 | bool StatusGreenWACDetection{ CmdlineModifyPath.size() > 0 }; 136 | bool StatusGreenCertificateValid{ false }; 137 | bool StatusGreenServerAuth{ false }; 138 | bool StatusGreenPrivateKey{ false }; 139 | 140 | HWND CertList{ GetDlgItem(HandleDialogMain, IDC_CERTLIST) }; 141 | if (IsWindowEnabled(CertList) && Certificates.size()) 142 | { 143 | ComputerCertificate& Certificate{ Certificates.at(SendMessage(CertList, CB_GETCURSEL, 0, 0)) }; 144 | Thumbprint = Certificate.Thumbprint(); 145 | std::wstringstream CertificateDisplay{}; 146 | CertificateDisplay << FieldSubject << ConvertSplitForDisplay(RegexPatternCommaWithOptionalSpace, Certificate.SubjectName()); 147 | CertificateDisplay << FieldIssuer << ConvertSplitForDisplay(RegexPatternCommaWithOptionalSpace, Certificate.Issuer()); 148 | CertificateDisplay << FieldSAN << ConvertSplitForDisplay(RegexPatternCommaWithOptionalSpace, Certificate.SubjectAlternateNames()); 149 | CertificateDisplay << FieldValidFrom << Certificate.ValidFrom() << NewLine; 150 | CertificateDisplay << FieldValidTo << Certificate.ValidTo() << NewLine; 151 | CertificateDisplay << FieldThumbprint << Thumbprint; 152 | CertificateText = CertificateDisplay.str(); 153 | StatusGreenCertificateValid = Certificate.IsWithinValidityPeriod(); 154 | StatusGreenServerAuth = Certificate.HasServerAuthentication(); 155 | StatusGreenPrivateKey = Certificate.HasPrivateKey(); 156 | } 157 | SetPictureBoxImage(IDC_ICONCERTVALID, StatusGreenCertificateValid); 158 | SetPictureBoxImage(IDC_ICONSERVAUTHALLOWED, StatusGreenServerAuth); 159 | SetPictureBoxImage(IDC_ICONHASPRIVATEKEY, StatusGreenPrivateKey); 160 | SetDlgItemText(HandleDialogMain, IDC_CERTDETAILS, CertificateText.c_str()); 161 | EnableDialogItem(IDOK, StatusGreenWACDetection && StatusGreenCertificateValid && 162 | StatusGreenServerAuth && StatusGreenPrivateKey); 163 | } 164 | 165 | void MainDialog::SetPictureBoxImage(const INT PictureBoxID, const bool Good) 166 | { 167 | HBITMAP SelectedImage = Good ? TinyGreenBox : TinyRedBox; 168 | SendDlgItemMessage(HandleDialogMain, PictureBoxID, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)SelectedImage); 169 | } 170 | 171 | const void MainDialog::InitDialog() noexcept 172 | { 173 | AppIcon = LoadIcon(AppInstance, MAKEINTRESOURCE(IDI_CERTWAC)); 174 | TinyGreenBox = LoadBitmap(AppInstance, MAKEINTRESOURCE(IDB_TINYGREENBOX)); 175 | TinyRedBox = LoadBitmap(AppInstance, MAKEINTRESOURCE(IDB_TINYREDBOX)); 176 | SendMessage(HandleDialogMain, WM_SETICON, ICON_SMALL, (LPARAM)AppIcon); 177 | Refresh(); 178 | } 179 | 180 | MainDialog::MainDialog(HINSTANCE Instance) : AppInstance(Instance) 181 | { 182 | HandleDialogMain = CreateDialogParam(AppInstance, MAKEINTRESOURCE(IDD_MAIN), 0, &SharedDialogProc, (LPARAM)this); 183 | } -------------------------------------------------------------------------------- /Program/Program.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release - Standalone 10 | Win32 11 | 12 | 13 | Release - MSI 14 | Win32 15 | 16 | 17 | 18 | 15.0 19 | {99E5DCA9-6082-41E6-BD22-0633BF652E04} 20 | Win32Proj 21 | CertWAC 22 | 10.0.17763.0 23 | 24 | 25 | 26 | Application 27 | true 28 | v141 29 | Unicode 30 | 31 | 32 | Application 33 | false 34 | v141 35 | true 36 | Unicode 37 | Spectre 38 | 39 | 40 | Application 41 | false 42 | v141 43 | true 44 | Unicode 45 | Spectre 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | true 64 | $(SolutionName) 65 | 66 | 67 | false 68 | $(SolutionName) 69 | 70 | 71 | false 72 | $(SolutionName) 73 | 74 | 75 | 76 | Level3 77 | Disabled 78 | true 79 | WINVER=0x0A00;_WIN32_WINNT=0x0A00;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 80 | true 81 | stdcpp17 82 | 83 | 84 | true 85 | Windows 86 | RequireAdministrator 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | Level3 95 | MinSpace 96 | true 97 | true 98 | true 99 | WINVER=0x0A00;_WIN32_WINNT=0x0A00;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 100 | true 101 | stdcpp17 102 | Size 103 | true 104 | MultiThreaded 105 | 106 | 107 | true 108 | true 109 | true 110 | Windows 111 | RequireAdministrator 112 | 113 | 114 | 115 | 116 | 117 | 118 | Level3 119 | MinSpace 120 | true 121 | true 122 | true 123 | WINVER=0x0A00;_WIN32_WINNT=0x0A00;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 124 | true 125 | stdcpp17 126 | Size 127 | true 128 | MultiThreadedDLL 129 | 130 | 131 | true 132 | true 133 | true 134 | Windows 135 | RequireAdministrator 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 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 | -------------------------------------------------------------------------------- /Program/Program.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files 47 | 48 | 49 | 50 | 51 | Resource Files 52 | 53 | 54 | 55 | 56 | Source Files 57 | 58 | 59 | Source Files 60 | 61 | 62 | Source Files 63 | 64 | 65 | Source Files 66 | 67 | 68 | Source Files 69 | 70 | 71 | Source Files 72 | 73 | 74 | Source Files 75 | 76 | 77 | Source Files 78 | 79 | 80 | Source Files 81 | 82 | 83 | Source Files 84 | 85 | 86 | Source Files 87 | 88 | 89 | Source Files 90 | 91 | 92 | 93 | 94 | Resource Files 95 | 96 | 97 | Resource Files 98 | 99 | 100 | Resource Files 101 | 102 | 103 | Resource Files 104 | 105 | 106 | -------------------------------------------------------------------------------- /Program/Resources/CertWAC.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejsiron/CertWAC/2aac25a546aed6f633b6585be40e23b1b96a60e9/Program/Resources/CertWAC.ico -------------------------------------------------------------------------------- /Program/Resources/Error.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejsiron/CertWAC/2aac25a546aed6f633b6585be40e23b1b96a60e9/Program/Resources/Error.ico -------------------------------------------------------------------------------- /Program/Resources/TinyGreenBox.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejsiron/CertWAC/2aac25a546aed6f633b6585be40e23b1b96a60e9/Program/Resources/TinyGreenBox.bmp -------------------------------------------------------------------------------- /Program/Resources/TinyRedBox.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejsiron/CertWAC/2aac25a546aed6f633b6585be40e23b1b96a60e9/Program/Resources/TinyRedBox.bmp -------------------------------------------------------------------------------- /Program/StringUtility.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "StringUtility.h" 4 | 5 | void CleanWindowsString(std::wstring& WindowsString) 6 | { 7 | while (WindowsString.size() && *WindowsString.crbegin() == L'\0') 8 | { 9 | WindowsString.pop_back(); 10 | } 11 | } 12 | 13 | std::wstring JoinString(const std::wstring& Glue, const std::vector& Parts) 14 | { 15 | // strings in this app are few and small; needs modification for JoinedString.resize() pre-calculation and allocation error handling before using in general-purpose code 16 | std::wstring JoinedString{}; 17 | if (Parts.size()) 18 | { 19 | JoinedString = *Parts.cbegin(); 20 | if (JoinedString.size() > 1) 21 | { 22 | for (auto Part{ ++Parts.cbegin() }; Part != Parts.cend(); ++Part) 23 | { 24 | JoinedString += Glue + *Part; 25 | } 26 | } 27 | } 28 | return JoinedString; 29 | } 30 | 31 | std::vector SplitString(const std::wstring& GluePattern, const std::wstring& Composite) 32 | { 33 | std::vectorSplitComponents{}; 34 | if (GluePattern.size()) 35 | { 36 | std::vector> GlueLocations{}; 37 | const std::wregex ComponentMatch{ GluePattern }; 38 | using rxit = std::wsregex_iterator; 39 | rxit EmptyRegexIterator{}; 40 | for (rxit PartWalker(Composite.cbegin(), Composite.cend(), ComponentMatch); 41 | PartWalker != EmptyRegexIterator; ++PartWalker) 42 | { 43 | GlueLocations.emplace_back(std::make_pair(PartWalker->position(), (*PartWalker).str().length())); 44 | } 45 | 46 | size_t CompositeStart{ 0 }; 47 | size_t CompositeSectionLength{ 0 }; 48 | for (auto const& GlueLocation : GlueLocations) 49 | { 50 | if (CompositeStart < Composite.length()) 51 | { 52 | CompositeSectionLength = GlueLocation.first - CompositeStart; 53 | SplitComponents.emplace_back(Composite.substr(CompositeStart, CompositeSectionLength)); 54 | CompositeStart += CompositeSectionLength + GlueLocation.second; 55 | } 56 | } 57 | size_t LastLength = Composite.length() - CompositeStart; 58 | SplitComponents.emplace_back(Composite.substr(CompositeStart, LastLength)); 59 | } 60 | else 61 | SplitComponents.emplace_back(Composite); 62 | return SplitComponents; 63 | } -------------------------------------------------------------------------------- /Program/StringUtility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | // use with std::wstring created from Windows APIs that write a terminating null zero 6 | // -- does not remove nulls from middle of a string (ex: registry REG_MULTI_SZ) 7 | void CleanWindowsString(std::wstring& WindowsString); 8 | 9 | // Combines a vector of strings into a single string, separated by Glue 10 | std::wstring JoinString(const std::wstring& Glue, const std::vector& Parts); 11 | template 12 | std::wstring JoinString(const T Glue, const std::vector& Parts) { return JoinString(std::wstring{ Glue }, Parts); } 13 | 14 | // Splits a string, using a regex match against GluePattern 15 | // * only YOU can prevent ill-formed regex patterns 16 | std::vector SplitString(const std::wstring& GluePattern, const std::wstring& Composite); 17 | // Splits a string, using a regex match against GluePattern 18 | // * only YOU can prevent ill-formed regex patterns 19 | template 20 | std::vector SplitString(const T GluePattern, const std::wstring& Composite) { return SplitString(std::wstring(GluePattern), Composite); } -------------------------------------------------------------------------------- /Program/WindowsUtility.cpp: -------------------------------------------------------------------------------- 1 | #include "WindowsUtility.h" 2 | 3 | void CenterInParent(HWND Parent, HWND Child) noexcept 4 | { // https://docs.microsoft.com/en-us/windows/desktop/dlgbox/using-dialog-boxes 5 | RECT ParentRect, ChildRect, TargetRect; 6 | GetWindowRect(Parent, &ParentRect); 7 | GetWindowRect(Child, &ChildRect); 8 | CopyRect(&TargetRect, &ParentRect); 9 | OffsetRect(&ChildRect, -ChildRect.left, -ChildRect.top); 10 | OffsetRect(&TargetRect, -TargetRect.left, -TargetRect.top); 11 | OffsetRect(&TargetRect, -ChildRect.right, -ChildRect.bottom); 12 | SetWindowPos(Child, HWND_TOP, 13 | (ParentRect.left + (TargetRect.right / 2)), 14 | (ParentRect.top + (TargetRect.bottom / 2)), 15 | 0, 0, SWP_NOSIZE); 16 | } -------------------------------------------------------------------------------- /Program/WindowsUtility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void CenterInParent(HWND Parent, HWND Child) noexcept; -------------------------------------------------------------------------------- /Program/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by CertWAC.rc 4 | // 5 | #define IDD_MAIN 101 6 | #define IDI_CERTWAC 102 7 | #define IDB_TINYGREENBOX 103 8 | #define IDB_TINYREDBOX 104 9 | #define IDD_ACTIONDIALOG 105 10 | #define IDI_ICONERROR 106 11 | #define IDD_ERRORDIALOG 107 12 | #define IDC_CERTPROMPT 1001 13 | #define IDC_CERTLIST 1002 14 | #define IDC_REFRESH 1003 15 | #define IDC_CERTDETAILS 1004 16 | #define IDC_CERTCONDITIONS 1005 17 | #define IDC_ICONWACINSTALLED 1006 18 | #define IDC_ICONCERTVALID 1007 19 | #define IDC_ICONSERVAUTHALLOWED 1008 20 | #define IDC_ICONHASPRIVATEKEY 1009 21 | #define IDC_PORT 1010 22 | #define IDC_STATUSDISPLAY 1011 23 | #define IDC_ERRORPICTURE 1012 24 | #define IDC_ERRORCODE 1013 25 | #define IDC_ERRORMESSAGE 1014 26 | #define IDC_ACTIVITYMESSAGE 1015 27 | 28 | // Next default values for new objects 29 | // 30 | #ifdef APSTUDIO_INVOKED 31 | #ifndef APSTUDIO_READONLY_SYMBOLS 32 | #define _APS_NEXT_RESOURCE_VALUE 109 33 | #define _APS_NEXT_COMMAND_VALUE 40001 34 | #define _APS_NEXT_CONTROL_VALUE 1016 35 | #define _APS_NEXT_SYMED_VALUE 101 36 | #endif 37 | #endif 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Windows Admin Center Certificate Selector 2 | 3 | Graphical tool for easy selection of certificates to use in Windows Admin Center 4 | 5 | ## Archive Notice 6 | 7 | Starting with Windows Admin Center version 2410 (v2), Microsoft's installer includes a drop-down box for selecting the certificate (it works much like this app). That means that this app does not currently provide value. If you want to look at some code for accessing PKI certificates on Windows using C++ and Win32, you might find some use for the code. 8 | 9 | Thanks everyone for supporting this project! 10 | 11 | ## Brief 12 | 13 | [Windows Admin Center](https://www.microsoft.com/en-us/cloud-platform/windows-admin-center) (WAC) is a powerful tool that allows you to monitor and maintain your Windows systems via a convenient HTML 5 interface. It uses a PKI certificate to encrypt your connection to its web interface. Unfortunately, it lacks an intuitive, simple interface for selecting which certificate to present. You must drill through the installed certificates to find the one that you want, copy the _Thumbprint_ value to a plain-text tool to clear out invalid characters, start the WAC installer, and paste in the thumbprint. The procedure is especially unpleasant if you installed WAC on Windows Server Core. [Microsoft has closed the UserVoice request to make this simpler](https://windowsserver.uservoice.com/forums/295071-management-tools/suggestions/33950335-how-do-windows-admin-center-change-certificate), indicating that they currently have no intent to make this easier. 14 | 15 | The WAC Certificate Selector neatly solves the usability problem. If you have WAC installed and a valid certificate in the local computer certificate store, you select it and let WAC handle the rest. 16 | 17 | ## BETA NOTICE 18 | 19 | This tool is currently in public beta. Please use caution if trying in production environments. 20 | 21 | ## Compatibility and Requirements 22 | 23 | The Windows Admin Certificate Selector requires: 24 | 25 | - Windows Server 2016 or later (including Core and Semi-Annual Channel), any edition 26 | - An installation of [Windows Admin Center](https://www.microsoft.com/en-us/cloud-platform/windows-admin-center) in gateway mode. Only GA versions are supported, although it might work with preview builds. 27 | - A certificate in the local computer store with an Enhanced Key Usage of Server Authentication. It will work with self-signed certificates. 28 | - Local administrative privileges 29 | - _Recommended_: For the MSI package distribution only, an installation of the [Microsoft Visual C++ Redistributable for Visual Studio 2017](https://visualstudio.microsoft.com/downloads/) (look under the **Other Tools and Frameworks Section**). See the section on the MSI package for more information. 30 | 31 | ## Packages 32 | 33 | You can download release builds from the [Releases page](https://github.com/ejsiron/CertWAC/releases). Choose one of the two packages: 34 | 35 | ### Standalone EXE 36 | 37 | This distribution has all necessary supporting libraries statically linked so that it will run without supporting files. This is the recommended package for short-term one-off usage. Note that because of the static linking, Windows Update will not patch any flaws in the supporting libraries. 38 | 39 | ### MSI package 40 | 41 | This distribution packages supporting DLLs. If your system has the full Visual Studio C++ 2017 x86 redistributable package, Windows Update will keep them current. This is the recommended package for long-term installations. 42 | 43 | The installer also makes these changes (configurable): 44 | 45 | - Modification of the PATH environment so that you can run **CertWAC.exe** from any prompt. 46 | - A shortcut icon on the Start menu 47 | 48 | ## Usage 49 | 50 | If you have not yet installed Windows Admin Center, run its installer. When prompted for certificate information, allow WAC to generate a self-signed certificate. Next, request a certificate from your provider, whether a PKI operated internally by your organization or a public PKI certificate retailer. The certificate must have the _Server Authentication_ Enhanced Key Usage. Most providers include that in _Web Server_ templates. Install the certificate into the local computer certificate store. 51 | 52 | Once you have WAC installed and a certificate prepared, run WAC Certificate Selector. If you obtained the MSI installation package, it will optionally place a shortcut on your Start menu and add its program folder to the PATH. If you have the standalone EXE, run **CertWAC.exe** from the location where you placed it. 53 | 54 | WAC Certificate Selector presents a drop-down list of the available computer certificates. It will show details of the selected certificate in the large text box. In order to be eligible, a certificate must meet all of the following criteria: 55 | 56 | - Within validity period 57 | - Have the _Server Authentication_ Enhanced Key Usage 58 | - The system must have the matching private key 59 | 60 | If all of the above are true, and a gateway installation of Windows Admin Center is detected, the **OK** button will be active. 61 | 62 | _Optional_: WAC Certificate Selector includes an option to change the port that WAC listens on. 63 | 64 | **Warning**: The reconfiguration stops the Windows Admin Center service. Windows Admin Center will be down and unavailable while MSI installs the certificate. 65 | 66 | ## How It Works 67 | 68 | WAC Certificate Selector calls on Windows Admin Center's own MSI install package using the [/qb](https://docs.microsoft.com/en-us/windows/desktop/Msi/command-line-options) option to install the certificate. Upon clicking OK, you will see the Windows Installer progress bar. WAC invokes MSI following [Microsoft's official documentation](https://docs.microsoft.com/en-us/windows-server/manage/windows-admin-center/deploy/install) with one exception: WAC's installer ignores any entered value for SME_PORT (you can verify by reviewing the logs). Therefore, WAC Certificate Selector will modify the related registry key prior to invoking MSI. The installer will read the updated value and apply it while changing the certificate. 69 | 70 | Additionally, it appears that the Windows Admin Center MSI package is malformed with the /qb option, as it leaves the service in the stopped state. At the end of the installation, WAC Certificate Selector will instruct the system to start the service. 71 | 72 | ## Building from source 73 | 74 | To build the source code included in these projects without modification, you will need a copy of [Visual Studio 2017](https://visualstudio.microsoft.com/downloads/) and a copy of the [WiX Toolset 3.11.1](http://wixtoolset.org/). Visual Studio must be configured for C++ development and include the Windows 10 SDK. 75 | -------------------------------------------------------------------------------- /Setup/MITLicense.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff31507\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi31507\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;} 2 | {\f37\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} 3 | {\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;} 4 | {\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} 5 | {\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;} 6 | {\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f43\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f44\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} 7 | {\f46\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f47\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f48\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f49\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} 8 | {\f50\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f51\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f413\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\f414\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} 9 | {\f416\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\f417\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\f418\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\f419\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);} 10 | {\f420\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\f421\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} 11 | {\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} 12 | {\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} 13 | {\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} 14 | {\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} 15 | {\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} 16 | {\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;} 17 | {\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);}{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);} 18 | {\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} 19 | {\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} 20 | {\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} 21 | {\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} 22 | {\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} 23 | {\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} 24 | {\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} 25 | {\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} 26 | {\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;} 27 | {\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} 28 | {\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} 29 | {\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} 30 | {\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} 31 | {\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}} 32 | {\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0; 33 | \red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1 34 | \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 35 | \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* 36 | \ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 37 | \widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused 38 | Normal Table;}}{\*\rsidtbl \rsid6110421\rsid6702547}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\operator Siron, Eric J}{\creatim\yr2019\mo2\dy27\hr15\min13} 39 | {\revtim\yr2019\mo2\dy27\hr15\min13}{\version2}{\edmins0}{\nofpages1}{\nofwords158}{\nofchars905}{\nofcharsws1061}{\vern55}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}} 40 | \paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect 41 | \widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701 42 | \dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale100\rsidroot6702547 \nouicompat \fet0{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1 43 | \pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5 44 | \pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang 45 | {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 46 | \fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \ab\af37 \ltrch\fcs0 \b\f37\lang9\langfe1033\langnp9\insrsid6110421 \hich\af37\dbch\af31505\loch\f37 MIT License}{\rtlch\fcs1 \af37 \ltrch\fcs0 47 | \f37\lang9\langfe1033\langnp9\insrsid6110421 48 | \par }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6702547 \hich\af37\dbch\af31505\loch\f37 Copyright (c) 2019 Eric Siron}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6110421 49 | \par \hich\af37\dbch\af31505\loch\f37 Permission is hereby granted, free of charge, to any person obtaining a copy}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6702547 \hich\af37\dbch\af31505\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 50 | \f37\lang9\langfe1033\langnp9\insrsid6110421 \hich\af37\dbch\af31505\loch\f37 of this software and associated documentation files (the "Software"), to deal}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6702547 51 | \hich\af37\dbch\af31505\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6110421 \hich\af37\dbch\af31505\loch\f37 in the Software without restriction, including without limitation the rights}{\rtlch\fcs1 \af37 \ltrch\fcs0 52 | \f37\lang9\langfe1033\langnp9\insrsid6702547 \hich\af37\dbch\af31505\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6110421 \hich\af37\dbch\af31505\loch\f37 53 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6702547 \hich\af37\dbch\af31505\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 54 | \f37\lang9\langfe1033\langnp9\insrsid6110421 \hich\af37\dbch\af31505\loch\f37 copies of the Software, and to permit persons to whom the Software is}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6702547 55 | \hich\af37\dbch\af31505\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6110421 \hich\af37\dbch\af31505\loch\f37 furnished to do so, subject to the }{\rtlch\fcs1 \af37 \ltrch\fcs0 56 | \f37\lang9\langfe1033\langnp9\insrsid6702547 \hich\af37\dbch\af31505\loch\f37 following conditions:}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6110421 57 | \par \hich\af37\dbch\af31505\loch\f37 The above copyright notice and this permission notice shall be included in all}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6702547 \hich\af37\dbch\af31505\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 58 | \f37\lang9\langfe1033\langnp9\insrsid6110421 \hich\af37\dbch\af31505\loch\f37 copies or substantial portions of the Software. 59 | \par \hich\af37\dbch\af31505\loch\f37 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6702547 \hich\af37\dbch\af31505\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 60 | \f37\lang9\langfe1033\langnp9\insrsid6110421 \hich\af37\dbch\af31505\loch\f37 IMPLIED, INCLUDING BUT NOT LIM\hich\af37\dbch\af31505\loch\f37 ITED TO THE WARRANTIES OF MERCHANTABILITY,}{\rtlch\fcs1 \af37 \ltrch\fcs0 61 | \f37\lang9\langfe1033\langnp9\insrsid6702547 \hich\af37\dbch\af31505\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6110421 \hich\af37\dbch\af31505\loch\f37 62 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6702547 \hich\af37\dbch\af31505\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 63 | \f37\lang9\langfe1033\langnp9\insrsid6110421 \hich\af37\dbch\af31505\loch\f37 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6702547 64 | \hich\af37\dbch\af31505\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6110421 \hich\af37\dbch\af31505\loch\f37 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, AR\hich\af37\dbch\af31505\loch\f37 ISING FROM,} 65 | {\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6702547 \hich\af37\dbch\af31505\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6110421 \hich\af37\dbch\af31505\loch\f37 66 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6702547 \hich\af37\dbch\af31505\loch\f37 }{\rtlch\fcs1 \af37 \ltrch\fcs0 67 | \f37\lang9\langfe1033\langnp9\insrsid6110421 \hich\af37\dbch\af31505\loch\f37 SOFTWARE.}{\rtlch\fcs1 \af37 \ltrch\fcs0 \f37\lang9\langfe1033\langnp9\insrsid6110421 68 | \par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a 69 | 9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad 70 | 5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 71 | b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 72 | 0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 73 | a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f 74 | c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 75 | 0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 76 | a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 77 | 6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b 78 | 4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b 79 | 4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100b6f4679893070000c9200000160000007468656d652f7468656d652f 80 | 7468656d65312e786d6cec59cd8b1bc915bf07f23f347d97f5d5ad8fc1f2a24fcfda33b6b164873dd648a5eef2547789aad28cc56208de532e81c026e49085bd 81 | ed21842cecc22eb9e48f31d8249b3f22afaa5bdd5552c99e191c3061463074977eefd5afde7bf5de53d5ddcf5e26d4bbc05c1096f6fcfa9d9aefe174ce16248d 82 | 7afeb3d9a4d2f13d2151ba4094a5b8e76fb0f03fbbf7eb5fdd454732c609f6403e1547a8e7c752ae8eaa5531876124eeb0154ee1bb25e30992f0caa3ea82a34b 83 | d09bd06aa3566b55134452df4b51026a1f2f97648ebd9952e9dfdb2a1f53784da5500373caa74a35b6243476715e5708b11143cabd0b447b3eccb3609733fc52 84 | fa1e4542c2173dbfa6fffceabdbb5574940b517940d6909be8bf5c2e17589c37f49c3c3a2b260d823068f50bfd1a40e53e6edc1eb7c6ad429f06a0f91c569a71 85 | b175b61bc320c71aa0ecd1a17bd41e35eb16ded0dfdce3dc0fd5c7c26b50a63fd8c34f2643b0a285d7a00c1feee1c3417730b2f56b50866fede1dbb5fe28685b 86 | fa3528a6243ddf43d7c25673b85d6d0159327aec8477c360d26ee4ca4b144443115d6a8a254be5a1584bd00bc6270050408a24493db959e1259a43140f112567 87 | 9c7827248a21f056286502866b8ddaa4d684ffea13e827ed5174849121ad780113b137a4f87862cec94af6fc07a0d537206f7ffef9cdeb1fdfbcfee9cd575fbd 88 | 79fdf77c6eadca923b466964cafdf2dd1ffef3cd6fbd7ffff0ed2f5fff319b7a172f4cfcbbbffdeedd3ffef93ef5b0e2d2146ffff4fdbb1fbf7ffbe7dfffebaf 89 | 5f3bb4f7393a33e1339260e13dc297de5396c0021dfcf119bf9ec42c46c494e8a791402952b338f48f656ca11f6d10450edc00db767cce21d5b880f7d72f2cc2 90 | d398af2571687c182716f094313a60dc6985876a2ec3ccb3751ab927e76b13f714a10bd7dc43945a5e1eaf579063894be530c616cd2714a5124538c5d253dfb1 91 | 738c1dabfb8210cbaea764ce99604be97d41bc01224e93ccc899154da5d03149c02f1b1741f0b7659bd3e7de8051d7aa47f8c246c2de40d4417e86a965c6fb68 92 | 2d51e252394309350d7e8264ec2239ddf0b9891b0b099e8e3065de78818570c93ce6b05ec3e90f21cdb8dd7e4a37898de4929cbb749e20c64ce4889d0f6394ac 93 | 5cd829496313fbb938871045de13265df05366ef10f50e7e40e941773f27d872f787b3c133c8b026a53240d4376beef0e57dccacf89d6ee8126157aae9f3c44a 94 | b17d4e9cd131584756689f604cd1255a60ec3dfbdcc160c05696cd4bd20f62c82ac7d815580f901dabea3dc5027a25d5dcece7c91322ac909de2881de073bad9 95 | 493c1b9426881fd2fc08bc6eda7c0ca52e7105c0633a3f37818f08f480102f4ea33c16a0c308ee835a9fc4c82a60ea5db8e375c32dff5d658fc1be7c61d1b8c2 96 | be04197c6d1948eca6cc7b6d3343d49aa00c9819822ec3956e41c4727f29a28aab165b3be596f6a62ddd00dd91d5f42424fd6007b4d3fb84ffbbde073a8cb77f 97 | f9c6b10f3e4ebfe3566c25ab6b763a8792c9f14e7f7308b7dbd50c195f904fbfa919a175fa04431dd9cf58b73dcd6d4fe3ffdff73487f6f36d2773a8dfb8ed64 98 | 7ce8306e3b99fc70e5e3743265f3027d8d3af0c80e7af4b14f72f0d46749289dca0dc527421ffc08f83db398c0a092d3279eb838055cc5f0a8ca1c4c60e1228e 99 | b48cc799fc0d91f134462b381daafb4a492472d591f0564cc0a1911e76ea5678ba4e4ed9223becacd7d5c16656590592e5782d2cc6e1a04a66e856bb3cc02bd4 100 | 6bb6913e68dd1250b2d721614c6693683a48b4b783ca48fa58178ce620a157f65158741d2c3a4afdd6557b2c805ae115f8c1edc1cff49e1f06200242701e07cd 101 | f942f92973f5d6bbda991fd3d3878c69450034d8db08283ddd555c0f2e4fad2e0bb52b78da2261849b4d425b46377822869fc17974aad1abd0b8aeafbba54b2d 102 | 7aca147a3e08ad9246bbf33e1637f535c8ede6069a9a9982a6de65cf6f35430899395af5fc251c1ac363b282d811ea3717a211dcbccc25cf36fc4d32cb8a0b39 103 | 4222ce0cae934e960d122231f728497abe5a7ee1069aea1ca2b9d51b90103e59725d482b9f1a3970baed64bc5ce2b934dd6e8c284b67af90e1b35ce1fc568bdf 104 | 1cac24d91adc3d8d1797de195df3a708422c6cd795011744c0dd413db3e682c0655891c8caf8db294c79da356fa3740c65e388ae62945714339967709dca0b3a 105 | faadb081f196af190c6a98242f8467912ab0a651ad6a5a548d8cc3c1aafb6121653923699635d3ca2aaa6abab39835c3b60cecd8f26645de60b53531e434b3c2 106 | 67a97b37e576b7b96ea74f28aa0418bcb09fa3ea5ea12018d4cac92c6a8af17e1a56393b1fb56bc776811fa07695226164fdd656ed8edd8a1ae19c0e066f54f9 107 | 416e376a6168b9ed2bb5a5f5adb979b1cdce5e40f2184197bba6526857c2c92e47d0104d754f92a50dd8222f65be35e0c95b73d2f3bfac85fd60d80887955a27 108 | 1c57826650ab74c27eb3d20fc3667d1cd66ba341e31514161927f530bbb19fc00506dde4f7f67a7cefee3ed9ded1dc99b3a4caf4dd7c5513d777f7f5c6e1bb7b 109 | 8f40d2f9b2d598749bdd41abd26df627956034e854bac3d6a0326a0ddba3c9681876ba9357be77a1c141bf390c5ae34ea5551f0e2b41aba6e877ba9576d068f4 110 | 8376bf330efaaff23606569ea58fdc16605ecdebde7f010000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d65 111 | 2f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d36 112 | 3f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e 113 | 3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d985 114 | 0528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c020000130000000000000000000000 115 | 0000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000000000 116 | 000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c0000000000000000000000000019020000 117 | 7468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d0014000600080000002100b6f4679893070000c92000001600000000000000 118 | 000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b01000027000000 119 | 000000000000000000009d0a00007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000980b00000000} 120 | {\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d 121 | 617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 122 | 6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 123 | 656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} 124 | {\*\latentstyles\lsdstimax375\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; 125 | \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; 126 | \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; 127 | \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; 128 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5; 129 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9; 130 | \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3; 131 | \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6; 132 | \lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent; 133 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer; 134 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures; 135 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference; 136 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text; 137 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List; 138 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3; 139 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3; 140 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3; 141 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing; 142 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent; 143 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4; 144 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation; 145 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading; 146 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3; 147 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong; 148 | \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature; 149 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; 150 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; 151 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; 152 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Table;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List; 153 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 1; 154 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 2; 155 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 2; 156 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 3; 157 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 2; 158 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 6; 159 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 2; 160 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 6; 161 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 2; 162 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Contemporary;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Elegant;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Professional; 163 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 2; 164 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Theme;\lsdsemihidden1 \lsdlocked0 Placeholder Text; 165 | \lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2; 166 | \lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List; 167 | \lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;\lsdpriority61 \lsdlocked0 Light List Accent 1; 168 | \lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdsemihidden1 \lsdlocked0 Revision; 169 | \lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1; 170 | \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;\lsdpriority72 \lsdlocked0 Colorful List Accent 1; 171 | \lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; 172 | \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2; 173 | \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2; 174 | \lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; 175 | \lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3; 176 | \lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4; 177 | \lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; 178 | \lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4; 179 | \lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5; 180 | \lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; 181 | \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; 182 | \lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; 183 | \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; 184 | \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; 185 | \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; 186 | \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; 187 | \lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; 188 | \lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; 189 | \lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; 190 | \lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; 191 | \lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; 192 | \lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; 193 | \lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; 194 | \lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; 195 | \lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; 196 | \lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; 197 | \lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; 198 | \lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; 199 | \lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; 200 | \lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; 201 | \lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; 202 | \lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; 203 | \lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; 204 | \lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; 205 | \lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; 206 | \lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; 207 | \lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; 208 | \lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; 209 | \lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; 210 | \lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; 211 | \lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention; 212 | \lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;}}{\*\datastore 010500000200000018000000 213 | 4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 214 | d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 215 | ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 216 | ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 217 | ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 218 | fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 219 | ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 220 | ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 221 | ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 222 | ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000f094 223 | f648e1ced401feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 224 | 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 225 | 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 226 | 0000000000000000000000000000000000000000000000000105000000000000}} -------------------------------------------------------------------------------- /Setup/Microsoft_VC141_CRT_x86.msm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ejsiron/CertWAC/2aac25a546aed6f633b6585be40e23b1b96a60e9/Setup/Microsoft_VC141_CRT_x86.msm -------------------------------------------------------------------------------- /Setup/Product.wxs: -------------------------------------------------------------------------------- 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 | WIXUI_EXITDIALOGOPTIONALCHECKBOX=1 and NOT Installed 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /Setup/Setup.wixproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 3.10 7 | 7e3a17a9-be47-4f20-8b07-149420318d86 8 | 2.0 9 | SetupCertWAC 10 | Package 11 | 12 | 13 | bin\$(Configuration)\ 14 | obj\$(Configuration)\ 15 | Debug 16 | 17 | 18 | bin\$(Configuration)\ 19 | obj\$(Configuration)\ 20 | 21 | 22 | 23 | 24 | 25 | 26 | $(WixExtDir)\WixUtilExtension.dll 27 | WixUtilExtension 28 | 29 | 30 | $(WixExtDir)\WixUIExtension.dll 31 | WixUIExtension 32 | 33 | 34 | 35 | 36 | Program 37 | {99e5dca9-6082-41e6-bd22-0633bf652e04} 38 | True 39 | True 40 | Binaries;Content;Satellites 41 | INSTALLFOLDER 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 57 | --------------------------------------------------------------------------------