diff --git a/OneMoreSetupActions/Actions/CheckBitnessAction.cs b/OneMoreSetupActions/Actions/CheckBitnessAction.cs
new file mode 100644
index 0000000000..569957ab6d
--- /dev/null
+++ b/OneMoreSetupActions/Actions/CheckBitnessAction.cs
@@ -0,0 +1,59 @@
+//************************************************************************************************
+// Copyright © 2021 Steven M Cohn. All rights reserved.
+//************************************************************************************************
+
+namespace OneMoreSetupActions
+{
+ using System;
+ using System.Windows.Forms;
+
+
+ ///
+ /// Confirms that the installer bitness matches the OS bitness
+ ///
+ internal class CheckBitnessAction : CustomAction
+ {
+ private readonly bool x64;
+
+
+ public CheckBitnessAction(Logger logger, Stepper stepper, bool x64)
+ : base(logger, stepper)
+ {
+ this.x64 = x64;
+ }
+
+
+ public override int Install()
+ {
+ logger.WriteLine();
+ logger.WriteLine("CheckBitnessAction.Install ---");
+
+ var oarc = Environment.Is64BitOperatingSystem ? "x64" : "x86";
+ var iarc = Environment.Is64BitProcess ? "x64" : "x86";
+ var rarc = x64 ? "x64" : "x86";
+ logger.WriteLine($"Installer architecture ({iarc}), OS architecture ({oarc}), requesting ({rarc})");
+
+ if (Environment.Is64BitOperatingSystem != Environment.Is64BitProcess ||
+ Environment.Is64BitOperatingSystem != x64)
+ {
+ var msg = $"Installer architecture ({iarc}) does not match OS ({oarc}) or request ({rarc})";
+ logger.WriteLine(msg);
+
+ MessageBox.Show(
+ $"This is a {iarc} bit installer.\nYou must use the OneMore {oarc} bit installer.",
+ "Incompatible Installer",
+ MessageBoxButtons.OK, MessageBoxIcon.Error);
+
+ return FAILURE;
+ }
+
+ return SUCCESS;
+ }
+
+
+ public override int Uninstall()
+ {
+ return SUCCESS;
+ }
+ }
+}
diff --git a/OneMoreSetupActions/Actions/CheckOneNoteAction.cs b/OneMoreSetupActions/Actions/CheckOneNoteAction.cs
new file mode 100644
index 0000000000..6b628276f5
--- /dev/null
+++ b/OneMoreSetupActions/Actions/CheckOneNoteAction.cs
@@ -0,0 +1,441 @@
+//************************************************************************************************
+// Copyright © 2023 Steven M Cohn. All rights reserved.
+//************************************************************************************************
+
+namespace OneMoreSetupActions
+{
+ using Microsoft.Win32;
+ using System;
+ using System.IO;
+ using System.Text.RegularExpressions;
+
+
+ ///
+ /// verifies the OneNote configuration to derisk OneMore not activating.
+ ///
+ internal class CheckOneNoteAction : CustomAction
+ {
+ private string onenoteCLSID;
+ private string onenoteCurVer;
+ private string onenoteTypeLib;
+
+
+ public CheckOneNoteAction(Logger logger, Stepper stepper)
+ : base(logger, stepper)
+ {
+ }
+
+
+ public override int Install()
+ {
+ logger.WriteLine();
+ logger.WriteLine("CheckOneNoteAction.Install ---");
+
+ if (VerifyOneNoteApplication() == SUCCESS &&
+ VerifyRootClass("CLSID") == SUCCESS &&
+ VerifyRootClass(@"WOW6432Node\CLSID") == SUCCESS &&
+ VerifyRootClass(@"Software\Classes\CLSID") == SUCCESS &&
+ VerifyCurVer() == SUCCESS &&
+ VerifyTypeLib() == SUCCESS)
+ {
+ return SUCCESS;
+ }
+
+ logger.Indented = false;
+ return FAILURE;
+ }
+
+
+ private int VerifyOneNoteApplication()
+ {
+ logger.WriteLine(nameof(VerifyOneNoteApplication) + "()");
+ logger.Indented = true;
+
+ // [HKEY_CLASSES_ROOT\OneNote.Application]
+ // @= "Application Class"
+ var path = "OneNote.Application";
+ var key = Registry.ClassesRoot.OpenSubKey(path, false);
+
+ if (key == null)
+ {
+ logger.WriteLine($"error finding HKCR:\\{path}");
+ return FAILURE;
+ }
+
+ logger.WriteLine($"checking {key}");
+
+ try
+ {
+ // onenoteCLSID
+ // [HKEY_CLASSES_ROOT\OneNote.Application\CLSID]
+ // @="{DC67E480-C3CB-49F8-8232-60B0C2056C8E}"
+ onenoteCLSID = key.GetSubKeyPropertyValue("CLSID", string.Empty);
+ if (onenoteCLSID == null)
+ {
+ return FAILURE;
+ }
+
+ logger.WriteLine($"found CLSID {onenoteCLSID}");
+
+ // onenoteCurVer
+ // [HKEY_CLASSES_ROOT\OneNote.Application\CurVer]
+ // @="OneNote.Application.15"
+ onenoteCurVer = key.GetSubKeyPropertyValue("CurVer", string.Empty);
+ if (onenoteCurVer == null)
+ {
+ return FAILURE;
+ }
+
+ logger.WriteLine($"found CurVer {onenoteCurVer}");
+ }
+ catch (Exception exc)
+ {
+ logger.WriteLine($"error reading OneNote.Application info");
+ logger.WriteLine(exc);
+ return FAILURE;
+ }
+ finally
+ {
+ key.Dispose();
+ }
+
+ logger.WriteLine("OK");
+ logger.Indented = false;
+ return SUCCESS;
+ }
+
+
+ private int VerifyRootClass(string root)
+ {
+ logger.WriteLine(nameof(VerifyRootClass) + $"({root})");
+ logger.Indented = true;
+
+ // [HKEY_CLASSES_ROOT\CLSID\{DC67E480-C3CB-49F8-8232-60B0C2056C8E}]
+ // @="Application2 Class"
+ var path = $"{root}\\{onenoteCLSID}";
+ var key = root.StartsWith("Software")
+ ? Registry.LocalMachine.OpenSubKey(path, false)
+ : Registry.ClassesRoot.OpenSubKey(path, false);
+
+ if (key == null)
+ {
+ logger.WriteLine($"error finding HKCR:\\{path}");
+ return FAILURE;
+ }
+
+ logger.WriteLine($"checking {key}");
+
+ try
+ {
+ // [HKEY_CLASSES_ROOT\CLSID\{DC67E480-C3CB-49F8-8232-60B0C2056C8E}\InprocServer32]
+ // Assembly="Microsoft.Office.Interop.OneNote, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C"
+ var p = "InprocServer32";
+ var aname = key.GetSubKeyPropertyValue(p, "Assembly");
+ if (aname == null)
+ {
+ return FAILURE;
+ }
+
+ if (VerifyGacAssembly(aname) != SUCCESS)
+ {
+ logger.WriteLine($"error confirming {p} '{aname}'");
+ return FAILURE;
+ }
+
+ logger.WriteLine($"verified {p} {aname}");
+
+ // remaining properties are WARNINGs/Informational...
+
+ //[HKEY_CLASSES_ROOT\CLSID\{DC67E480-C3CB-49F8-8232-60B0C2056C8E}\ProgID]
+ //@="OneNote.Application.15"
+ p = "ProgID";
+ var progID = key.GetSubKeyPropertyValue(p, string.Empty, false);
+ if (progID == null)
+ {
+ logger.WriteLine($"warning finding subkey {key.Name}\\{p}");
+ }
+ else if (progID != onenoteCurVer)
+ {
+ logger.WriteLine($"warning confirming ProgID '{progID}' as {onenoteCurVer}");
+ }
+ else
+ {
+ logger.WriteLine($"verified ProgID as {progID}");
+ }
+
+ //[HKEY_CLASSES_ROOT\CLSID\{DC67E480-C3CB-49F8-8232-60B0C2056C8E}\TypeLib]
+ //@="{0EA692EE-BB50-4E3C-AEF0-356D91732725}"
+ p = "TypeLib";
+ var typelib = key.GetSubKeyPropertyValue(p, string.Empty, false);
+ if (progID == null)
+ {
+ logger.WriteLine($"warning finding subkey {key.Name}\\{p}");
+ }
+ else if (!root.StartsWith("Software"))
+ {
+ onenoteTypeLib = typelib;
+ logger.WriteLine($"found TypeLib as {onenoteTypeLib}");
+ }
+ else if (typelib != onenoteTypeLib)
+ {
+ logger.WriteLine($"warning confirming TypeLib '{typelib}' as {onenoteTypeLib}");
+ }
+ else
+ {
+ logger.WriteLine($"verified TypeLib as {onenoteTypeLib}");
+ }
+
+ //[HKEY_CLASSES_ROOT\CLSID\{DC67E480-C3CB-49F8-8232-60B0C2056C8E}\VersionIndependentProgID]
+ //@="OneNote.Application"
+ p = "VersionIndependentProgID";
+ var vip = key.GetSubKeyPropertyValue(p, string.Empty, false);
+ if (string.IsNullOrWhiteSpace(vip))
+ {
+ logger.WriteLine($"warning finding subkey {key.Name}\\{p}");
+ }
+ else
+ {
+ var path2 = $"{vip}\\CurVer";
+ using (var key2 = Registry.ClassesRoot.OpenSubKey(path2, false))
+ {
+ if (key2 == null)
+ {
+ logger.WriteLine($"warning finding key {path2}");
+ }
+ else
+ {
+ var ver = (string)key2.GetValue(string.Empty);
+ if (string.IsNullOrWhiteSpace(ver) ||
+ ver != onenoteCurVer)
+ {
+ logger.WriteLine($"warning confirming VersionIndependentProgID ({ver ?? "null"} != {onenoteCurVer})");
+ }
+ else
+ {
+ logger.WriteLine($"verified VersionIndependentProgID as {ver}");
+ }
+ }
+ }
+ }
+ }
+ catch (Exception exc)
+ {
+ logger.WriteLine($"error reading CLSID info");
+ logger.WriteLine(exc);
+ return FAILURE;
+ }
+ finally
+ {
+ key.Dispose();
+ }
+
+ logger.WriteLine("OK");
+ logger.Indented = false;
+ return SUCCESS;
+ }
+
+
+ private int VerifyGacAssembly(string fullName)
+ {
+ // AssemblyName class doesn't expose token so just parse the string
+ var matches = Regex.Matches(fullName, @"([^,].*), Version=([^,].*), .+?PublicKeyToken=(.*)");
+ if (matches.Count != 1 || !matches[0].Success)
+ {
+ logger.WriteLine($"error parsing GAC assembly name {fullName}");
+ return FAILURE;
+ }
+
+ var name = matches[0].Groups[1].Value;
+ var version = matches[0].Groups[2].Value;
+ var token = matches[0].Groups[3].Value;
+
+ var path = Path.Combine(
+ Environment.GetEnvironmentVariable("windir"),
+ @"assembly\\GAC_MSIL", name, $"{version}__{token}",
+ $"{name}.dll");
+
+ if (!File.Exists(path))
+ {
+ return FAILURE;
+ }
+
+ return SUCCESS;
+ }
+
+
+ private int VerifyCurVer()
+ {
+ logger.WriteLine(nameof(VerifyCurVer) + "()");
+ logger.Indented = true;
+
+ //[HKEY_CLASSES_ROOT\OneNote.Application.15]
+ //@= "Application2 Class"
+ var path = onenoteCurVer;
+ var key = Registry.ClassesRoot.OpenSubKey(path, false);
+
+ if (key == null)
+ {
+ logger.WriteLine($"error finding HKCR:\\{path}");
+ return FAILURE;
+ }
+
+ logger.WriteLine($"checking {key}");
+
+ try
+ {
+ //[HKEY_CLASSES_ROOT\OneNote.Application.15\CLSID]
+ //@= "{DC67E480-C3CB-49F8-8232-60B0C2056C8E}"
+ var p = "CLSID";
+ var clsid = key.GetSubKeyPropertyValue(p, string.Empty);
+ if (clsid == null)
+ {
+ return FAILURE;
+ }
+
+ if (clsid != onenoteCLSID)
+ {
+ logger.WriteLine($"error confirming CLSID '{clsid}' as {onenoteCLSID}");
+ return FAILURE;
+ }
+
+ logger.WriteLine($"verified CLSID {clsid}");
+ }
+ catch (Exception exc)
+ {
+ logger.WriteLine($"error reading CurVer info");
+ logger.WriteLine(exc);
+ return FAILURE;
+ }
+ finally
+ {
+ key.Dispose();
+ }
+
+ logger.WriteLine("OK");
+ logger.Indented = false;
+ return SUCCESS;
+ }
+
+
+ private int VerifyTypeLib()
+ {
+ logger.WriteLine(nameof(VerifyTypeLib) + "()");
+ logger.Indented = true;
+
+ //[HKEY_CLASSES_ROOT\TypeLib\{0EA692EE-BB50-4E3C-AEF0-356D91732725}]
+ var path = $"TypeLib\\{onenoteTypeLib}";
+ var key = Registry.ClassesRoot.OpenSubKey(path, false);
+
+ if (key == null)
+ {
+ logger.WriteLine($"error finding HKCR:\\{path}");
+ return FAILURE;
+ }
+
+ logger.WriteLine($"checking {key}");
+
+ try
+ {
+ string p;
+ var foundLib = false;
+
+ // 1.0, 1.1, ...
+ foreach (var subname in key.GetSubKeyNames())
+ {
+ using (var sub = key.OpenSubKey(subname, false))
+ {
+ //[HKEY_CLASSES_ROOT\TypeLib\{0EA692EE-BB50-4E3C-AEF0-356D91732725}\1.0]
+ //"PrimaryInteropAssemblyName"="Microsoft.Office.Interop.OneNote, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C"
+ // => C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.OneNote\15.0.0.0__71e9bce111e9429c
+ var pia = (string)sub.GetValue("PrimaryInteropAssemblyName");
+ if (!string.IsNullOrWhiteSpace(pia))
+ {
+ var status = VerifyGacAssembly(pia);
+ if (status == FAILURE)
+ {
+ logger.WriteLine($"error finding GAC assembly {pia}");
+ }
+ else
+ {
+ logger.WriteLine($"verified {pia}");
+ }
+ }
+ else
+ {
+ //[HKEY_CLASSES_ROOT\TypeLib\{0EA692EE-BB50-4E3C-AEF0-356D91732725}\1.1]
+ //@="Microsoft OneNote 15.0 Object Library"
+ var value = (string)sub.GetValue(string.Empty);
+ if (Regex.Match(value, @"Microsoft OneNote .+ (?:Object|Type) Library").Success)
+ {
+ //[HKEY_CLASSES_ROOT\TypeLib\{0EA692EE-BB50-4E3C-AEF0-356D91732725}\1.1\0\Win32]
+ //@="C:\\Program Files\\Microsoft Office\\Root\\Office16\\ONENOTE.EXE\\3"
+ p = "0\\Win32";
+ var win32 = sub.GetSubKeyPropertyValue(p, string.Empty, false);
+ if (win32 == null)
+ {
+ logger.WriteLine($@"warning did not find key HKCR:\\{sub.Name}\{p}");
+ }
+ // strip off trailing "\3"
+ else if (!File.Exists(Path.GetDirectoryName(win32)))
+ {
+ logger.WriteLine($@"warning did not find Win32 path {win32}");
+ }
+ else
+ {
+ logger.WriteLine($"verified Win32 Object Library {win32}");
+ foundLib = true;
+ }
+
+ //[HKEY_CLASSES_ROOT\TypeLib\{0EA692EE-BB50-4E3C-AEF0-356D91732725}\1.1\0\win64]
+ //@="C:\\Program Files\\Microsoft Office\\root\\Office16\\ONENOTE.EXE\\3"
+ p = "0\\Win32";
+ var win64 = sub.GetSubKeyPropertyValue(p, string.Empty, false);
+ if (win64 == null)
+ {
+ logger.WriteLine($@"warning did not find key HKCR:\\{sub.Name}\{p}");
+ }
+ // strip off trailing "\3"
+ else if (!File.Exists(Path.GetDirectoryName(win64)))
+ {
+ logger.WriteLine($@"warning did not find win64 path {win64}");
+ }
+ else
+ {
+ logger.WriteLine($"verified win64 Object Library {win64}");
+ foundLib = true;
+ }
+ }
+ }
+ }
+ }
+
+ // if neither Win32 nor win64 subkeys were found
+ if (!foundLib)
+ {
+ logger.WriteLine($"error finding object library info under {path}");
+ return FAILURE;
+ }
+ }
+ catch (Exception exc)
+ {
+ logger.WriteLine($"error reading CurVer info");
+ logger.WriteLine(exc);
+ return FAILURE;
+ }
+ finally
+ {
+ key.Dispose();
+ }
+
+ logger.WriteLine("OK");
+ logger.Indented = false;
+ return SUCCESS;
+ }
+
+
+ public override int Uninstall()
+ {
+ return SUCCESS;
+ }
+ }
+}
diff --git a/OneMoreSetupActions/Logger.cs b/OneMoreSetupActions/Logger.cs
index e68fe4fbf2..19ea84a27a 100644
--- a/OneMoreSetupActions/Logger.cs
+++ b/OneMoreSetupActions/Logger.cs
@@ -17,9 +17,8 @@ internal class Logger : IDisposable
public Logger(string name)
{
- writer = new StreamWriter(
- Path.Combine(Path.GetTempPath(), $"{name}.log"),
- true);
+ LogPath = Path.Combine(Path.GetTempPath(), $"{name}.log");
+ writer = new StreamWriter(LogPath, true);
}
@@ -51,6 +50,12 @@ protected virtual void Dispose(bool disposing)
#endregion Lifecycle
+ public string LogPath { get; private set; }
+
+
+ public bool Indented { get; set; }
+
+
public void WriteLine()
{
WriteLine(string.Empty);
@@ -59,8 +64,9 @@ public void WriteLine()
public void WriteLine(string message)
{
- Console.WriteLine(message);
- writer.WriteLine(message);
+ var m = Indented ? $"... {message}" : message;
+ Console.WriteLine(m);
+ writer.WriteLine(m);
writer.Flush();
}
diff --git a/OneMoreSetupActions/OneMoreSetupActions.csproj b/OneMoreSetupActions/OneMoreSetupActions.csproj
index 31764154cd..820e57b084 100644
--- a/OneMoreSetupActions/OneMoreSetupActions.csproj
+++ b/OneMoreSetupActions/OneMoreSetupActions.csproj
@@ -43,6 +43,7 @@
+
@@ -52,6 +53,8 @@
+
+
diff --git a/OneMoreSetupActions/Program.cs b/OneMoreSetupActions/Program.cs
index 8edddd1736..aadac0c3d3 100644
--- a/OneMoreSetupActions/Program.cs
+++ b/OneMoreSetupActions/Program.cs
@@ -3,6 +3,7 @@
//************************************************************************************************
#pragma warning disable S1118 // Utility classes should not have public constructors
+#pragma warning disable S6605 // "Exists" method should be used instead of the "Any" extension
namespace OneMoreSetupActions
{
@@ -11,6 +12,7 @@ namespace OneMoreSetupActions
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Principal;
+ using System.Windows.Forms;
class Program
@@ -52,14 +54,31 @@ static void Main(string[] args)
logger.WriteLine($"direct action: {args[0]} .. {DateTime.Now}");
}
+ ReportContext();
+
+ int status;
+
if (args.Any(a => a == "--x64" || a == "--x86"))
{
- CheckBitness(args.Any(a => a == "--x64"));
+ var x64 = args.Any(a => a == "--x64");
+ status = new CheckBitnessAction(logger, stepper, x64).Install();
+ if (status != CustomAction.SUCCESS)
+ {
+ Environment.Exit(status);
+ }
}
- ReportContext();
+ status = new CheckOneNoteAction(logger, stepper).Install();
+ if (status != CustomAction.SUCCESS)
+ {
+ MessageBox.Show($"The OneNote installation looks to be invalid. OneMore may not appear " +
+ "in the OneNote ribbon until OneNote is repaired. For more information, " +
+ $"check the logs at\n{logger.LogPath}",
+ "OneNote Configuration Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
- int status;
+ // treat as warning for now...
+ //Environment.Exit(status);
+ }
switch (args[0])
{
@@ -77,6 +96,14 @@ static void Main(string[] args)
status = new ActiveSetupAction(logger, stepper).Install();
break;
+ case "--install-checkbitness":
+ status = new CheckBitnessAction(logger, stepper, true).Install();
+ break;
+
+ case "--install-checkonenote":
+ status = new CheckOneNoteAction(logger, stepper).Install();
+ break;
+
case "--install-edge":
status = new EdgeWebViewAction(logger, stepper).Install();
break;
@@ -119,22 +146,6 @@ static void Main(string[] args)
}
- static void CheckBitness(bool x64)
- {
- var oarc = Environment.Is64BitOperatingSystem ? "x64" : "x86";
- var iarc = Environment.Is64BitProcess ? "x64" : "x86";
- var rarc = x64 ? "x64" : "x86";
- logger.WriteLine($"Installer architecture ({iarc}), OS architecture ({oarc}), requesting ({rarc})");
-
- if (Environment.Is64BitOperatingSystem != Environment.Is64BitProcess ||
- Environment.Is64BitOperatingSystem != x64)
- {
- logger.WriteLine($"Installer architecture ({iarc}) does not match OS ({oarc}) or request ({rarc})");
- Environment.Exit(CustomAction.USEREXIT);
- }
- }
-
-
static void ReportContext()
{
// current user...
@@ -188,6 +199,10 @@ static int Install()
{
logger.WriteLine("install failed; error registering");
logger.WriteLine(exc);
+
+ MessageBox.Show($"Error installing. Check the logs {logger.LogPath}",
+ "Action Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+
return CustomAction.FAILURE;
}
}
@@ -223,6 +238,10 @@ static int Uninstall()
{
logger.WriteLine("uninstall failed; error unregistering");
logger.WriteLine(exc);
+
+ MessageBox.Show($"Error uninstalling. Check the logs {logger.LogPath}",
+ "Action Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+
return CustomAction.FAILURE;
}
}
diff --git a/OneMoreSetupActions/RegistryHelper.cs b/OneMoreSetupActions/RegistryHelper.cs
index cdc09d2de1..dcde253e60 100644
--- a/OneMoreSetupActions/RegistryHelper.cs
+++ b/OneMoreSetupActions/RegistryHelper.cs
@@ -2,11 +2,14 @@
// Copyright © 2021 Steven M Cohn. All rights reserved.
//************************************************************************************************
+#pragma warning disable S6605 // "Exists" method should be used instead of the "Any" extension
+
namespace OneMoreSetupActions
{
using Microsoft.Win32;
using System;
using System.IO;
+ using System.Linq;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Security.Principal;
@@ -100,6 +103,47 @@ private static void CopyTree(RegistryKey source, RegistryKey target)
}
+ ///
+ /// Safely return the value of a property under the named subkey.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static string GetSubKeyPropertyValue(
+ this RegistryKey key, string subkeyName, string propName, bool verbose = true)
+ {
+ try
+ {
+ using (var sub = key.OpenSubKey(subkeyName, false))
+ {
+ if (sub == null)
+ {
+ if (verbose)
+ {
+ logger?.WriteLine($"could not find subkey {key.Name}\\{subkeyName}");
+ }
+ return null;
+ }
+
+ var value = (string)sub.GetValue(propName);
+ if (value == null && verbose)
+ {
+ logger?.WriteLine($"could not find property {key.Name}\\{subkeyName}\\{propName}");
+ }
+
+ return value;
+ }
+ }
+ catch (Exception exc)
+ {
+ logger?.WriteLine($"error reading subkey property {key.Name}\\{subkeyName}\\{propName}");
+ logger?.WriteLine(exc);
+ return null;
+ }
+ }
+
+
///
/// Returns the SID of the currently logged in user that is running the installer,
/// which may differ from the current user context because the installer may be
diff --git a/build.ps1 b/build.ps1
index c9902ed284..7a42bb527f 100644
--- a/build.ps1
+++ b/build.ps1
@@ -20,189 +20,193 @@ or reinstalling Visual Studio. It is required to build installer kits from the c
# CmdletBinding adds -Verbose functionality, SupportsShouldProcess adds -WhatIf
[CmdletBinding(SupportsShouldProcess = $true)]
param (
- [int] $configbits = 64,
- [switch] $fast,
- [switch] $both,
- [switch] $prep
- )
+ [int] $configbits = 64,
+ [switch] $fast,
+ [switch] $both,
+ [switch] $prep
+ )
Begin
{
- $script:devenv = ''
- $script:vdproj = ''
-
- function FindVisualStudio
- {
- $cmd = Get-Command devenv -ErrorAction:SilentlyContinue
- if ($cmd -ne $null)
- {
- $script:devenv = $cmd.Source
- return $true
- }
-
- $0 = 'C:\Program Files\Microsoft Visual Studio\2022'
- if (FindVS $0) { return $true }
-
- $0 = 'C:\Program Files (x86)\Microsoft Visual Studio\2019'
- return FindVS $0
- }
-
- function FindVS
- {
- param($vsroot)
- $script:devenv = Join-Path $vsroot 'Enterprise\Common7\IDE\devenv.com'
-
- if (!(Test-Path $devenv))
- {
- $script:devenv = Join-Path $vsroot 'Professional\Common7\IDE\devenv.com'
- }
- if (!(Test-Path $devenv))
- {
- $script:devenv = Join-Path $vsroot 'Community\Common7\IDE\devenv.com'
- }
-
- if (!(Test-Path $devenv))
- {
- Write-Host "devenv not found in $vsroot" -ForegroundColor Yellow
- return $false
- }
-
- $script:ideroot = Split-Path -Parent $devenv
- return $true
- }
-
- function DisableOutOfProcBuild
- {
- $0 = Join-Path $ideroot 'CommonExtensions\Microsoft\VSI\DisableOutOfProcBuild'
- if (Test-Path $0)
- {
- Push-Location $0
- if (Test-Path .\DisableOutOfProcBuild.exe) {
- .\DisableOutOfProcBuild.exe
- }
- Pop-Location
- Write-Host '... disabled out-of-proc builds; reboot is recommended'
- return
- }
- Write-Host "*** could not find $0\DisableOutOfProcBuild.exe" -ForegroundColor Yellow
- }
-
- function PreserveVdproj
- {
- Write-Host '... preserving vdproj' -ForegroundColor DarkGray
- Copy-Item $vdproj .\vdproj.tmp -Force -Confirm:$false
- }
-
- function RestoreVdproj
- {
- Write-Host '... restoring vdproj' -ForegroundColor DarkGray
- Copy-Item .\vdproj.tmp $vdproj -Force -Confirm:$false
- Remove-Item .\vdproj.tmp -Force
- }
-
- function Configure
- {
- param([int]$bitness)
- $lines = @(Get-Content $vdproj)
-
- $productVersion = $lines | `
- Where-Object { $_ -match '"ProductVersion" = "8:(.+?)"' } | `
- ForEach-Object { $matches[1] }
-
- Write-Host
- Write-Host "... configuring vdproj for x$bitness build of $productVersion" -ForegroundColor Yellow
-
- '' | Out-File $vdproj -nonewline
-
- $lines | ForEach-Object `
- {
- if ($_ -match '"OutputFileName" = "')
- {
- # "OutputFilename" = "8:Debug\\OneMore_v_Setupx86.msi"
- $line = $_.Replace('OneMore_v_', "OneMore_$($productVersion)_")
- if ($bitness -eq 64) {
- $line.Replace('x86', 'x64') | Out-File $vdproj -Append
- } else {
- $line.Replace('x64', 'x86') | Out-File $vdproj -Append
- }
- }
- elseif ($_ -match '"DefaultLocation" = "')
- {
- # "DefaultLocation" = "8:[ProgramFilesFolder][Manufacturer]\\[ProductName]"
- if ($bitness -eq 64) {
- $_.Replace('ProgramFilesFolder', 'ProgramFiles64Folder') | Out-File $vdproj -Append
- } else {
- $_.Replace('ProgramFiles64Folder', 'ProgramFilesFolder') | Out-File $vdproj -Append
- }
- }
- elseif ($_ -match '"TargetPlatform" = "')
- {
- # x86 -> "3:0"
- # x64 -> "3:1"
- if ($bitness -eq 64) {
- '"TargetPlatform" = "3:1"' | Out-File $vdproj -Append
- } else {
- '"TargetPlatform" = "3:0"' | Out-File $vdproj -Append
- }
- }
- #elseif ($_ -match '"SourcePath" = .*WebView2Loader\.dll"$')
- #{
- # if ($bitness -eq 64) {
- # $_.Replace('\\x86', '\\x64') | Out-File $vdproj -Append
- # $_.Replace('win-x86', 'win-x64') | Out-File $vdproj -Append
- # } else {
- # $_.Replace('\\x64', '\\x86') | Out-File $vdproj -Append
- # $_.Replace('win-x64', 'win-x86') | Out-File $vdproj -Append
- # }
- #}
- elseif (($_ -match '"Name" = "8:OneMoreSetupActions --install ') -or `
- ($_ -match '"Arguments" = "8:--install '))
- {
- if ($bitness -eq 64) {
- $_.Replace('x86', 'x64') | Out-File $vdproj -Append
- } else {
- $_.Replace('x64', 'x86') | Out-File $vdproj -Append
- }
- }
- elseif ($_ -notmatch '^"Scc')
- {
- $_ | Out-File $vdproj -Append
- }
- }
- }
-
- function BuildFast
- {
- param([int]$bitness)
- Write-Host "... building x$bitness DLLs" -ForegroundColor Yellow
-
- # build...
-
- Push-Location OneMore
- BuildComponent 'OneMore' $true
- Pop-Location
-
- Push-Location OneMoreCalendar
- BuildComponent 'OneMoreCalendar' $true
- Pop-Location
-
- Push-Location OneMoreProtocolHandler
- BuildComponent 'OneMoreProtocolHandler'
- Pop-Location
+ $script:devenv = ''
+ $script:vsregedit = ''
+ $script:vdproj = ''
+
+ function FindVisualStudio
+ {
+ $cmd = Get-Command devenv -ErrorAction:SilentlyContinue
+ if ($cmd -ne $null)
+ {
+ $script:devenv = $cmd.Source
+ $script:ideroot = Split-Path -Parent $devenv
+ $script:vsregedit = Join-Path $ideroot 'VSRegEdit.exe'
+ return $true
+ }
+
+ $0 = 'C:\Program Files\Microsoft Visual Studio\2022'
+ if (FindVS $0) { return $true }
+
+ $0 = 'C:\Program Files (x86)\Microsoft Visual Studio\2019'
+ return FindVS $0
+ }
+
+ function FindVS
+ {
+ param($vsroot)
+ $script:devenv = Join-Path $vsroot 'Enterprise\Common7\IDE\devenv.com'
+
+ if (!(Test-Path $devenv))
+ {
+ $script:devenv = Join-Path $vsroot 'Professional\Common7\IDE\devenv.com'
+ }
+ if (!(Test-Path $devenv))
+ {
+ $script:devenv = Join-Path $vsroot 'Community\Common7\IDE\devenv.com'
+ }
+
+ if (!(Test-Path $devenv))
+ {
+ Write-Host "devenv not found in $vsroot" -ForegroundColor Yellow
+ return $false
+ }
+
+ $script:ideroot = Split-Path -Parent $devenv
+ $script:vsregedit = Join-Path $ideroot 'VSRegEdit.exe'
+ return $true
+ }
+
+ function DisableOutOfProcBuild
+ {
+ $0 = Join-Path $ideroot 'CommonExtensions\Microsoft\VSI\DisableOutOfProcBuild'
+ if (Test-Path $0)
+ {
+ Push-Location $0
+ if (Test-Path .\DisableOutOfProcBuild.exe) {
+ .\DisableOutOfProcBuild.exe
+ }
+ Pop-Location
+ Write-Host '... disabled out-of-proc builds; reboot is recommended'
+ return
+ }
+ Write-Host "*** could not find $0\DisableOutOfProcBuild.exe" -ForegroundColor Yellow
+ }
+
+ function PreserveVdproj
+ {
+ Write-Host '... preserving vdproj' -ForegroundColor DarkGray
+ Copy-Item $vdproj .\vdproj.tmp -Force -Confirm:$false
+ }
+
+ function RestoreVdproj
+ {
+ Write-Host '... restoring vdproj' -ForegroundColor DarkGray
+ Copy-Item .\vdproj.tmp $vdproj -Force -Confirm:$false
+ Remove-Item .\vdproj.tmp -Force
+ }
+
+ function Configure
+ {
+ param([int]$bitness)
+ $lines = @(Get-Content $vdproj)
+
+ $productVersion = $lines | `
+ Where-Object { $_ -match '"ProductVersion" = "8:(.+?)"' } | `
+ ForEach-Object { $matches[1] }
+
+ Write-Host
+ Write-Host "... configuring vdproj for x$bitness build of $productVersion" -ForegroundColor Yellow
+
+ '' | Out-File $vdproj -nonewline
+
+ $lines | ForEach-Object `
+ {
+ if ($_ -match '"OutputFileName" = "')
+ {
+ # "OutputFilename" = "8:Debug\\OneMore_v_Setupx86.msi"
+ $line = $_.Replace('OneMore_v_', "OneMore_$($productVersion)_")
+ if ($bitness -eq 64) {
+ $line.Replace('x86', 'x64') | Out-File $vdproj -Append
+ } else {
+ $line.Replace('x64', 'x86') | Out-File $vdproj -Append
+ }
+ }
+ elseif ($_ -match '"DefaultLocation" = "')
+ {
+ # "DefaultLocation" = "8:[ProgramFilesFolder][Manufacturer]\\[ProductName]"
+ if ($bitness -eq 64) {
+ $_.Replace('ProgramFilesFolder', 'ProgramFiles64Folder') | Out-File $vdproj -Append
+ } else {
+ $_.Replace('ProgramFiles64Folder', 'ProgramFilesFolder') | Out-File $vdproj -Append
+ }
+ }
+ elseif ($_ -match '"TargetPlatform" = "')
+ {
+ # x86 -> "3:0"
+ # x64 -> "3:1"
+ if ($bitness -eq 64) {
+ '"TargetPlatform" = "3:1"' | Out-File $vdproj -Append
+ } else {
+ '"TargetPlatform" = "3:0"' | Out-File $vdproj -Append
+ }
+ }
+ #elseif ($_ -match '"SourcePath" = .*WebView2Loader\.dll"$')
+ #{
+ # if ($bitness -eq 64) {
+ # $_.Replace('\\x86', '\\x64') | Out-File $vdproj -Append
+ # $_.Replace('win-x86', 'win-x64') | Out-File $vdproj -Append
+ # } else {
+ # $_.Replace('\\x64', '\\x86') | Out-File $vdproj -Append
+ # $_.Replace('win-x64', 'win-x86') | Out-File $vdproj -Append
+ # }
+ #}
+ elseif (($_ -match '"Name" = "8:OneMoreSetupActions --install ') -or `
+ ($_ -match '"Arguments" = "8:--install '))
+ {
+ if ($bitness -eq 64) {
+ $_.Replace('x86', 'x64') | Out-File $vdproj -Append
+ } else {
+ $_.Replace('x64', 'x86') | Out-File $vdproj -Append
+ }
+ }
+ elseif ($_ -notmatch '^"Scc')
+ {
+ $_ | Out-File $vdproj -Append
+ }
+ }
+ }
+
+ function BuildFast
+ {
+ param([int]$bitness)
+ Write-Host "... building x$bitness DLLs" -ForegroundColor Yellow
+
+ # build...
+
+ Push-Location OneMore
+ BuildComponent 'OneMore' $true
+ Pop-Location
+
+ Push-Location OneMoreCalendar
+ BuildComponent 'OneMoreCalendar' $true
+ Pop-Location
+
+ Push-Location OneMoreProtocolHandler
+ BuildComponent 'OneMoreProtocolHandler'
+ Pop-Location
- Push-Location OneMoreSetupActions
- BuildComponent 'OneMoreSetupActions'
- Pop-Location
- }
-
- function BuildComponent
- {
- param($name, $restore = $false)
- # output file cannot exist before build
- if (Test-Path .\Debug\*)
- {
- Remove-Item .\Debug\*.* -Force -Confirm:$false
- }
+ Push-Location OneMoreSetupActions
+ BuildComponent 'OneMoreSetupActions'
+ Pop-Location
+ }
+
+ function BuildComponent
+ {
+ param($name, $restore = $false)
+ # output file cannot exist before build
+ if (Test-Path .\Debug\*)
+ {
+ Remove-Item .\Debug\*.* -Force -Confirm:$false
+ }
# nuget restore
if ($restore)
{
@@ -210,70 +214,86 @@ Begin
write-Host $cmd -ForegroundColor DarkGray
nuget restore .\$name.csproj
}
- $cmd = "$devenv .\$name.csproj /build ""Debug|AnyCPU"" /project $name /projectconfig Debug"
- write-Host $cmd -ForegroundColor DarkGray
- . $devenv .\$name.csproj /build "Debug|AnyCPU" /project $name /projectconfig Debug
- }
-
- function Build
- {
- param([int]$bitness)
- Write-Host "... building x$bitness MSI" -ForegroundColor Yellow
-
- # output file cannot exist before build
- if (Test-Path .\Debug\*)
- {
- Remove-Item .\Debug\*.* -Force -Confirm:$false
- }
-
- $cmd = "$devenv .\OneMoreSetup.vdproj /build ""Debug|x$bitness"" /project Setup /projectconfig Debug /out `$env:TEMP\OneMoreBuild.log"
- write-Host $cmd -ForegroundColor DarkGray
-
- # build
- . $devenv .\OneMoreSetup.vdproj /build "Debug|x$bitness" /project Setup /projectconfig Debug /out $env:TEMP\OneMorebuild.log
-
- # move msi to Downloads for safe-keeping and to allow next Platform build
- Move-Item .\Debug\*.msi $home\Downloads -Force
- Write-Host "... x$bitness MSI copied to $home\Downloads\" -ForegroundColor DarkYellow
- }
+ $cmd = "$devenv .\$name.csproj /build ""Debug|AnyCPU"" /project $name /projectconfig Debug"
+ write-Host $cmd -ForegroundColor DarkGray
+ . $devenv .\$name.csproj /build "Debug|AnyCPU" /project $name /projectconfig Debug
+ }
+
+ function Build
+ {
+ param([int]$bitness)
+ Write-Host "... building x$bitness MSI" -ForegroundColor Yellow
+
+ # output file cannot exist before build
+ if (Test-Path .\Debug\*)
+ {
+ Remove-Item .\Debug\*.* -Force -Confirm:$false
+ }
+
+ if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent)
+ {
+ Write-Host '... enabling MSBuild verbose logging' -ForegroundColor DarkYellow
+ $cmd = ". '$vsregedit' set local HKCU General MSBuildLoggerVerbosity dword 4`n"
+ write-Host $cmd -ForegroundColor DarkGray
+ . $vsregedit set local HKCU General MSBuildLoggerVerbosity dword 4 | Out-Null
+ }
+
+ $cmd = "$devenv .\OneMoreSetup.vdproj /build ""Debug|x$bitness"" /project Setup /projectconfig Debug /out `$env:TEMP\OneMoreBuild.log"
+ write-Host $cmd -ForegroundColor DarkGray
+
+ # build
+ . $devenv .\OneMoreSetup.vdproj /build "Debug|x$bitness" /project Setup /projectconfig Debug /out $env:TEMP\OneMorebuild.log
+
+ # move msi to Downloads for safe-keeping and to allow next Platform build
+ Move-Item .\Debug\*.msi $home\Downloads -Force
+ Write-Host "... x$bitness MSI copied to $home\Downloads\" -ForegroundColor DarkYellow
+
+ if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent)
+ {
+ Write-Host '... disabling MSBuild verbose logging' -ForegroundColor DarkYellow
+ $cmd = ". '$vsregedit' set local HKCU General MSBuildLoggerVerbosity dword 1`n"
+ write-Host $cmd -ForegroundColor DarkGray
+ . $vsregedit set local HKCU General MSBuildLoggerVerbosity dword 1 | Out-Null
+ }
+ }
}
Process
{
- if (-not (FindVisualStudio))
- {
- return
- }
-
- if ($prep)
- {
- DisableOutOfProcBuild
- return
- }
-
- if ($fast)
- {
- BuildFast 64
- }
- else
- {
- Push-Location OneMoreSetup
- $script:vdproj = Resolve-Path .\OneMoreSetup.vdproj
- PreserveVdproj
-
- if ($configbits -eq 86 -or $both)
- {
- Configure 86
- Build 86
- }
-
- if ($configBits -eq 64 -or $both)
- {
- Configure 64
- Build 64
- }
-
- RestoreVdproj
- }
-
- Pop-Location
+ if (-not (FindVisualStudio))
+ {
+ return
+ }
+
+ if ($prep)
+ {
+ DisableOutOfProcBuild
+ return
+ }
+
+ if ($fast)
+ {
+ BuildFast 64
+ }
+ else
+ {
+ Push-Location OneMoreSetup
+ $script:vdproj = Resolve-Path .\OneMoreSetup.vdproj
+ PreserveVdproj
+
+ if ($configbits -eq 86 -or $both)
+ {
+ Configure 86
+ Build 86
+ }
+
+ if ($configBits -eq 64 -or $both)
+ {
+ Configure 64
+ Build 64
+ }
+
+ RestoreVdproj
+ }
+
+ Pop-Location
}