Fix first real VSTO build, add sender account selection, polish deploy

First live build against real Visual Studio/VSTO tooling surfaced two
issues in the hand-written AddIn scaffold: ThisAddIn derived from the
wrong base type (OutlookAddIn is an interface, not the concrete
OutlookAddInBase class), and VSTO Application projects cannot build
without a signed ClickOnce manifest, so SignManifests is now on with a
throwaway self-signed test certificate. Both Core/Core.Tests/AddIn now
build clean and Core.Tests passes.

Also: mail always sent from Outlook's default account with no way to
pick a different one (e.g. a shared mailbox) - added a "From account"
setting (Session.Accounts dropdown in SettingsForm, SendUsingAccount in
MailSender). And added deploy/ with both a local ClickOnce publish
script (real install/uninstall via Windows Apps, no manual registry
editing) and a manual registry sideload fallback for scripted rollouts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
redlineoandClaude Sonnet 5 committed 2026-07-24 09:51:52 +03:00
1 parent 7f0e0fdb23
commit 264ed74095
20 files changed
+756 -67

No files matched your search

@@ -45,6 +45,31 @@ namespace BulkArchiveMailer.Core
[DataMember(Name = "signatureHtmlPath")]
public string SignatureHtmlPath { get; set; } = "";
/// <summary>
/// SmtpAddress of the Outlook.Account (from the current session's
/// Session.Accounts) that outgoing mail should be sent from, e.g.
/// a shared/service mailbox distinct from the operator's own
/// primary account. Empty (or null - see note below) means "use
/// whatever account Outlook picks by default", the previous,
/// unconfigurable behavior (SendUsingAccount left unset).
///
/// Loading a config.json written before this field existed does
/// NOT throw - DataContractJsonSerializer happily ignores unknown
/// absence - but it also does NOT fall back to the "" default
/// above: DataContractJsonSerializer builds the target object via
/// FormatterServices.GetUninitializedObject, which skips
/// constructors *and* field/property initializers entirely, so a
/// member never touched during deserialization keeps the CLR
/// default instead (null for string). This is true of every
/// string field on this class, not just this one - every call
/// site that reads FromAccountSmtpAddress must treat null the
/// same as "", which MailSender/SettingsForm already do via
/// `?? ""` / string.IsNullOrEmpty rather than relying on this
/// initializer.
/// </summary>
[DataMember(Name = "fromAccountSmtpAddress")]
public string FromAccountSmtpAddress { get; set; } = "";
[DataMember(Name = "templates")]
public TemplateSetData Templates { get; set; } = new TemplateSetData();