Skip to content

Fix broken MDX formatter, add NLog, upgrade to .NET 4.8 - #48

Closed
dasimon wants to merge 5 commits into
OlapPivotTableExtensions:masterfrom
dasimon:master
Closed

dasimon wants to merge 5 commits into
OlapPivotTableExtensions:masterfrom
dasimon:master

Conversation

@dasimon

@dasimon dasimon commented Apr 21, 2026

Copy link
Copy Markdown

Summary

  • Replace dead web service: com.msftlabs.formatmdx (Microsoft Labs) is offline. Replaced with a self-contained MdxFormatter.cs — MDX formatting works again without any external dependency.
  • Add NLog logging: structured file logging to %LOCALAPPDATA%\OlapPivotTableExtensions\logs\ for easier troubleshooting.
  • Upgrade to .NET 4.8: updated target framework and all NuGet packages (MSAL 4.46→4.66, IdentityModel 6.22→7.7).
  • Bug fixes:
    • Cache PivotField.Hidden to avoid 0x800A01A8 COM errors in LevelChooserForm
    • Fix registry key leaks (using statements)
    • Remove incorrect Marshal.ReleaseComObject call on VSTO-managed RCW (could cause crashes on shutdown)
    • Replace silent catch {} blocks with logged warnings

Why

The com.msftlabs.formatmdx web service has been offline for years, silently breaking the MDX formatting feature for all users. This is the most critical fix.

The .NET 4.5.2 target framework reached end of support in 2022; 4.8 is the current stable LTS release for .NET Framework.

- Replace dead com.msftlabs.formatmdx web service with local MdxFormatter.cs
- Add NLog structured logging (file sink, %LOCALAPPDATA%)
- Upgrade target framework from .NET 4.5.2 to .NET 4.8
- Update MSAL 4.46→4.66, IdentityModel 6.22→7.7, add NLog 5.3.4
- Cache PivotField.Hidden to avoid 0x800A01A8 COM errors
- Fix registry key leaks (using statements)
- Fix Marshal.ReleaseComObject misuse on VSTO-managed RCW
- Replace silent catch{} blocks with NLog warnings
- Add README.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 21, 2026 13:05
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes the Excel VSTO add-in by removing the defunct online MDX formatting dependency, restoring MDX formatting locally, adding file-based logging via NLog, and upgrading the project to .NET Framework 4.8 with updated identity packages.

Changes:

  • Replaced the dead com.msftlabs.formatmdx web service integration with a local MdxFormatter (formatting + RichTextBox syntax highlighting).
  • Added NLog logging and replaced multiple silent catch {} blocks with logged warnings; fixed a few shutdown/registry/COM-related issues.
  • Upgraded the add-in project to .NET Framework 4.8 and updated NuGet/package bindings; removed old web reference artifacts.

Reviewed changes

Copilot reviewed 21 out of 23 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
README.md Documents fork changes (local formatter, NLog, .NET 4.8).
OlapPivotTableExtensions/xlEvents.cs Logs previously swallowed exceptions during COM handling.
OlapPivotTableExtensions/packages.config Targets net48; updates identity packages; adds NLog.
OlapPivotTableExtensions/app.config Updates binding redirects for upgraded packages.
OlapPivotTableExtensions/Web References/com.msftlabs.formatmdx/formatter.wsdl Removes dead web-service WSDL artifact.
OlapPivotTableExtensions/Web References/com.msftlabs.formatmdx/formatter.disco Removes dead web-service discovery artifact.
OlapPivotTableExtensions/Web References/com.msftlabs.formatmdx/Reference.map Removes dead web-service reference map.
OlapPivotTableExtensions/Web References/com.msftlabs.formatmdx/Reference.cs Removes generated SOAP client implementation.
OlapPivotTableExtensions/SortableList.cs Logs exceptions during comparison instead of swallowing.
OlapPivotTableExtensions/PowerPivotLaunchedChecker.cs Logs swallowed exceptions.
OlapPivotTableExtensions/PivotTableKpiUtility.cs Logs swallowed exceptions.
OlapPivotTableExtensions/OlapPivotTableExtensions2016.csproj Upgrades to v4.8; adds NLog + local formatter; removes web references; adjusts VSTO refs.
OlapPivotTableExtensions/NLog.config Adds file logging configuration under LocalAppData.
OlapPivotTableExtensions/MdxFormatter.cs New local MDX formatter + tokenizer + syntax highlighting.
OlapPivotTableExtensions/MainForm.cs Switches MDX formatting from web-service async to local formatting/highlighting; improves cancellation checks; logs exceptions.
OlapPivotTableExtensions/LevelChooserForm.cs Caches PivotField.Hidden to avoid COM error after modifying other fields.
OlapPivotTableExtensions/Impersonater.cs Disposes WindowsIdentity via using.
OlapPivotTableExtensions/CubeSearcher.cs Fixes equality comparison bug and adds GetHashCode; logs previously silent exceptions.
OlapPivotTableExtensions/Connect.cs Introduces Connect.Log (NLog); avoids releasing VSTO-managed RCW; uses using for registry keys; adds safer command-bar button checks.
OlapPivotTableExtensions/CalculationsLibrary.cs Guards missing library file; improves error reporting.
OlapPivotTableExtensions/AdomdClientWrappers/AdomdConnection.cs Implements IDisposable and disposes underlying connections with logging.
.gitignore Ignores upgrade/build logs and local tooling config.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 331 to +337
if (Connect.FormatMdx)
{
InitiateFormatMDX(sMdxQuery.ToString());
richTextBoxMDX.Text = MdxFormatter.Format(richTextBoxMDX.Text);
MdxFormatter.ApplySyntaxHighlighting(richTextBoxMDX);
richTextBoxMDX.SelectionStart = 0;
richTextBoxMDX.SelectionLength = 0;
richTextBoxMDX.ScrollToCaret();

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting + syntax highlighting is now done synchronously on the UI thread (MdxFormatter.Format + ApplySyntaxHighlighting). Because highlighting iterates every token and performs many RichTextBox.Select operations, large MDX queries can noticeably freeze the UI. Consider running the formatting/highlighting work off the UI thread (then Invoke to apply results), or at least gating it by input length / debouncing so UI responsiveness is preserved.

Copilot uses AI. Check for mistakes.
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Text.RegularExpressions;

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused using System.Text.RegularExpressions; (no Regex usage in this file). Removing it avoids warnings and keeps imports minimal.

Suggested change
using System.Text.RegularExpressions;

Copilot uses AI. Check for mistakes.
int start = i;
i += 2;
while (i < len - 1 && !(text[i] == '*' && text[i + 1] == '/')) i++;
i += 2;

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tokenize can throw ArgumentOutOfRangeException when parsing an unterminated block comment (no closing */). The loop stops at end-of-string, but i += 2 still advances past len, and the subsequent Substring(start, i - start) will fail. Handle EOF by clamping i to len when no terminator is found (and/or only advancing by 2 when */ is actually present).

Suggested change
i += 2;
if (i < len - 1 && text[i] == '*' && text[i + 1] == '/')
i += 2;
else
i = len;

Copilot uses AI. Check for mistakes.
Comment on lines +193 to +214

// Reset all to black first
rtb.SelectAll();
rtb.SelectionColor = Color.Black;

// Apply coloring by token
var tokens = Tokenize(text);
int pos = 0;
foreach (Token tok in tokens)
{
Color color = GetTokenColor(tok);
if (color != Color.Black)
{
rtb.Select(pos, tok.Value.Length);
rtb.SelectionColor = color;
}
pos += tok.Value.Length;
}

// Restore
rtb.Select(savedStart, savedLen);
rtb.ResumeLayout();

Copilot AI Apr 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ApplySyntaxHighlighting calls rtb.SuspendLayout() but doesn't use a try/finally to guarantee ResumeLayout() runs. If tokenization or selection throws (e.g., malformed input), the RichTextBox may remain in a suspended layout state for the rest of the session. Wrap the body in try/finally and restore selection/layout in the finally block.

Suggested change
// Reset all to black first
rtb.SelectAll();
rtb.SelectionColor = Color.Black;
// Apply coloring by token
var tokens = Tokenize(text);
int pos = 0;
foreach (Token tok in tokens)
{
Color color = GetTokenColor(tok);
if (color != Color.Black)
{
rtb.Select(pos, tok.Value.Length);
rtb.SelectionColor = color;
}
pos += tok.Value.Length;
}
// Restore
rtb.Select(savedStart, savedLen);
rtb.ResumeLayout();
try
{
// Reset all to black first
rtb.SelectAll();
rtb.SelectionColor = Color.Black;
// Apply coloring by token
var tokens = Tokenize(text);
int pos = 0;
foreach (Token tok in tokens)
{
Color color = GetTokenColor(tok);
if (color != Color.Black)
{
rtb.Select(pos, tok.Value.Length);
rtb.SelectionColor = color;
}
pos += tok.Value.Length;
}
}
finally
{
// Restore
rtb.Select(savedStart, savedLen);
rtb.ResumeLayout();
}

Copilot uses AI. Check for mistakes.
dasimon and others added 2 commits April 21, 2026 17:01
Produces OlapPivotTableExtensions-vX.Y.Z-setup.exe — copies binaries,
registers the VSTO add-in via VSTOInstaller.exe, checks .NET 4.8 prereq,
and supports clean uninstall via Add/Remove Programs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread OlapPivotTableExtensions/MainForm.cs Outdated
private void linkCodeplex_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.Process.Start("https://olappivottableextensions.github.io");
System.Diagnostics.Process.Start("https://github.com/dasimon/OlapPivotTableExtensions");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m definitely not going to be able to merge this. Maybe this could be a config file that’s changed during build?

Replaces hardcoded Process.Start URL strings with constants from a
dedicated UrlConstants class, so fork-specific URLs are isolated to
one file rather than scattered through MainForm.cs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@dasimon

dasimon commented Apr 22, 2026

Copy link
Copy Markdown
Author

Good point — I've opened a separate PR (#49 ) that introduces a UrlConstants.cs file to hold all link URLs. That way forks only need to change one file rather than patching MainForm.cs directly. This PR can be closed.

@dasimon dasimon closed this Apr 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants