Conversation
- 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>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
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.formatmdxweb service integration with a localMdxFormatter(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.
| if (Connect.FormatMdx) | ||
| { | ||
| InitiateFormatMDX(sMdxQuery.ToString()); | ||
| richTextBoxMDX.Text = MdxFormatter.Format(richTextBoxMDX.Text); | ||
| MdxFormatter.ApplySyntaxHighlighting(richTextBoxMDX); | ||
| richTextBoxMDX.SelectionStart = 0; | ||
| richTextBoxMDX.SelectionLength = 0; | ||
| richTextBoxMDX.ScrollToCaret(); |
There was a problem hiding this comment.
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.
| using System.Collections.Generic; | ||
| using System.Drawing; | ||
| using System.Text; | ||
| using System.Text.RegularExpressions; |
There was a problem hiding this comment.
Unused using System.Text.RegularExpressions; (no Regex usage in this file). Removing it avoids warnings and keeps imports minimal.
| using System.Text.RegularExpressions; |
| int start = i; | ||
| i += 2; | ||
| while (i < len - 1 && !(text[i] == '*' && text[i + 1] == '/')) i++; | ||
| i += 2; |
There was a problem hiding this comment.
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).
| i += 2; | |
| if (i < len - 1 && text[i] == '*' && text[i + 1] == '/') | |
| i += 2; | |
| else | |
| i = len; |
|
|
||
| // 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(); |
There was a problem hiding this comment.
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.
| // 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(); | |
| } |
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>
| 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"); |
There was a problem hiding this comment.
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>
|
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. |
Summary
com.msftlabs.formatmdx(Microsoft Labs) is offline. Replaced with a self-containedMdxFormatter.cs— MDX formatting works again without any external dependency.%LOCALAPPDATA%\OlapPivotTableExtensions\logs\for easier troubleshooting.PivotField.Hiddento avoid0x800A01A8COM errors inLevelChooserFormusingstatements)Marshal.ReleaseComObjectcall on VSTO-managed RCW (could cause crashes on shutdown)catch {}blocks with logged warningsWhy
The
com.msftlabs.formatmdxweb 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.