Skip to content

Commit aad4ece

Browse files
KenM76claude
authored andcommitted
v0.4.2: auto-provided Office interop aliases for .csx scripts
Implements FR_office_script_interop_alias.md in full. Fixes the CS0104 ambiguous-reference papercut that hit every Windows Office script: `Range`, `Exception`, `Application`, `Style`, `Font`, `Action`, `Page` collide between the auto-imported interop namespace and the BCL. `Range` was the worst because modern C# added `System.Range` for slicing, so the common idiom `Range used = xlSheet.UsedRange;` failed until each author re-typed `using Xl = global::Microsoft.Office.Interop.Excel;` at the top of every .csx. Mechanism (picked option b from the FR, not option a): - New `IComBridgePlugin.ScriptUsingAliases` contract member (default empty, so non-Office plugins are zero-impact). Each Windows Office plugin overrides to return its conventional two-letter alias body: excel→Xl, word→Wd, powerpoint→Pp, outlook→Ol — all qualified to global::Microsoft.Office.Interop.*. - ScriptHost.RunAsync concatenates contributed aliases onto a single preamble line at the top of the script source, preserves BOM + encoding so PDB emit still works (CS8055-free), then passes the combined stream to CSharpScript.Create. - Diagnostic line/column spans in Roslyn compile errors are rewritten back to the author's real source via a regex over the diagnostic ToString(). Errors located inside the preamble itself are left untouched so plugin-author bugs surface loudly instead of presenting as "your script line 0." Verified end-to-end: - Positive: each of word/excel/powerpoint/outlook resolves Wd.Application, Xl.Application, Pp.Application, Ol.MailItem with NO author-declared alias. - Negative: bare `Range` is still ambiguous CS0104 — we deliberately did NOT win the bare-name race against System.Range. - Line-number remap: the CS0104 from the negative test reports the author's real line, not the +1 compiled line. Mirrored to D:\Dev\ScripTree\lib\combridge\ and R:\ScripTree\lib\combridge\ per the CLAUDE.md deployment mandate; byte-sizes match the source build (host 162304, Outlook.Mac DLL 30720 from v0.4.1). Docs updated: - LLM/scripting.md § Office namespace shadowing rewritten to lead with the auto-alias and show the fully-qualify fallback. - LLM/troubleshooting.md gains a dedicated CS0104 entry with the alias table inline. - FR_office_script_interop_alias.md stamped with implementation log noting the option taken and verification results. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 49414d1 commit aad4ece

9 files changed

Lines changed: 348 additions & 16 deletions

File tree

CHANGELOG.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,84 @@ All notable changes to this project will be documented in this file.
44
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/);
55
versions follow [SemVer](https://semver.org/spec/v2.0.0.html).
66

7+
## [0.4.2] — auto-provided interop aliases for Office scripts
8+
9+
Addresses `D:\Dev\FeatureRequests\ComBridge_FeatureRequests\FR_office_script_interop_alias.md`
10+
in full. Implements the FR's primary proposal (option **b**
11+
plugin-contributed aliases rendered into a host preamble) rather than the
12+
doc-only fallback.
13+
14+
### Why this exists
15+
16+
Every Windows Office `.csx` hit the same CS0104 pain point: the interop
17+
namespace defines its own `Range`, `Exception`, `Application`, `Style`,
18+
`Font`, `Action`, `Page`, etc., colliding with the same-named BCL types.
19+
`Range` is the worst offender because modern C# added `System.Range` for
20+
slicing syntax — so the single most common Office idiom
21+
(`Range used = xlSheet.UsedRange;`) failed to compile until every
22+
script author re-typed `using Xl = global::Microsoft.Office.Interop.Excel;`
23+
at the top. The FR identified this pattern sitting latent in 15 scripts
24+
across the ScripTreeApps catalog, surfacing only on first run.
25+
26+
### Added
27+
- **`IComBridgePlugin.ScriptUsingAliases`** — new optional contract
28+
member. Returns alias bodies (e.g. `"Xl = global::Microsoft.Office.Interop.Excel"`);
29+
the host renders them as `using <alias>;` directives. Default = empty,
30+
so existing plugins (SolidWorks, all Mac plugins) compile and run
31+
unchanged with zero behavior change.
32+
- **Alias preamble** in `ScriptHost.RunAsync` — concatenates each
33+
plugin's contributed aliases onto a single first line of the script
34+
source, preserving BOM + encoding so PDB emit still works
35+
(CS8055-free) and non-ASCII characters in script bodies round-trip
36+
intact. Roslyn `ScriptOptions.Imports` accepts namespaces but not
37+
alias directives, so the preamble route is the only working option.
38+
- **Diagnostic line-number remapping**`RemapDiagnosticLine` rewrites
39+
the `(LINE,COL)` span in Roslyn diagnostic strings so compile errors
40+
point at the author's real source. A CS0104 reported at compiled
41+
line 168 surfaces as line 167 (matching what the author sees in their
42+
editor). Errors inside the preamble itself (a plugin-author bug,
43+
not a script-author bug) are left untouched so they're loud.
44+
- **Four Windows Office plugins now contribute their alias**
45+
`excel``Xl`, `word``Wd`, `powerpoint``Pp`, `outlook``Ol`
46+
(all qualified to `global::Microsoft.Office.Interop.*`). Mac plugins
47+
contribute nothing — they don't have an interop namespace to shadow.
48+
49+
### What stays the same (deliberately)
50+
51+
- **Bare `Range` is still ambiguous.** The alias only guarantees a
52+
reliable qualifier (`Xl.Range`, `Wd.Range`) is always in scope; it
53+
does NOT silently pick the Office type over `System.Range`. This is
54+
the FR's explicit acceptance criterion — we don't want to win the
55+
bare-name race for the author.
56+
- **`System.Exception` etc. still need to be qualified** (or you can
57+
catch a non-colliding subtype like `COMException`). The new aliases
58+
fix the Office-side qualifier, not the BCL side.
59+
- **Existing scripts that already declare `using Xl = …` keep working.**
60+
Roslyn quietly accepts the duplicate (same alias to the same
61+
namespace); we don't conflict with manual declarations.
62+
63+
### Verified
64+
65+
- Positive: a Word `.csx` containing `typeof(Wd.Application).FullName`
66+
compiles and runs with NO author-declared alias.
67+
- Negative: a Word `.csx` containing bare `Range r;` still fails with
68+
CS0104 between `Microsoft.Office.Interop.Word.Range` and
69+
`System.Range` — confirming we didn't accidentally resolve the race.
70+
- Line-number remap: the CS0104 from the negative test surfaces at
71+
`(2,1)` (the author's actual line in the .csx), not `(3,1)`
72+
proving the preamble offset is being subtracted.
73+
74+
### Docs
75+
- `LLM/scripting.md` § "Office interop namespaces shadow common BCL
76+
names" rewritten to lead with the auto-provided alias (recommended)
77+
and show the fully-qualify fallback. New alias-mechanics note
78+
explains the preamble + line remap for plugin authors.
79+
- `LLM/troubleshooting.md` gained a dedicated CS0104 entry covering
80+
Office-interop / BCL collisions with the alias table inline and the
81+
rationale for keeping bare `Range` ambiguous.
82+
- `FR_office_script_interop_alias.md` stamped with implementation log
83+
noting which option was taken (b, not a) and why.
84+
785
## [0.4.1] — outlook search on macOS
886

987
Lifts the v0.4.0 deferral. The Mac Outlook plugin now ships a `search`

LLM/scripting.md

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,56 @@ the DLL alongside the script and use `#r "path/to/MyLib.dll"`.
7070
### ⚠ Office interop namespaces shadow common BCL names
7171

7272
Every Office interop namespace defines its OWN `Exception`, `Application`,
73-
`Style`, `Action`, `Font`, `Page` etc. — these clash with `System.*`. Inside
74-
any Office script (Excel/Word/PowerPoint/Outlook on Windows), the plugin's
75-
`ScriptUsings` imports the interop namespace, so unqualified references
76-
become ambiguous.
73+
`Style`, `Action`, `Font`, `Page`, **and `Range`** — these clash with
74+
`System.*`. Inside any Office script (Excel/Word/PowerPoint/Outlook on
75+
Windows), the plugin's `ScriptUsings` imports the interop namespace, so
76+
unqualified references become ambiguous.
77+
78+
`Range` is the worst offender because modern C# added `System.Range` (for
79+
`x[1..3]` slicing), so even the single most common Office idiom —
80+
`Range used = xlSheet.UsedRange;` — fails to compile with CS0104.
81+
82+
### ✓ Two-letter alias (auto-provided, v0.4.2+) — the recommended pattern
83+
84+
The four Windows Office plugins auto-contribute a conventional two-letter
85+
alias for their interop namespace, prepended to your script source before
86+
compile. You can write the qualified form **without declaring the alias
87+
yourself**:
88+
89+
| Plugin | Auto-provided alias | Use as |
90+
|---|---|---|
91+
| `excel` | `Xl = global::Microsoft.Office.Interop.Excel` | `Xl.Range used = ws.UsedRange;` |
92+
| `word` | `Wd = global::Microsoft.Office.Interop.Word` | `Wd.Range rng = wdDoc.Content;` |
93+
| `powerpoint` | `Pp = global::Microsoft.Office.Interop.PowerPoint` | `Pp.Shape sh = slide.Shapes[1];` |
94+
| `outlook` | `Ol = global::Microsoft.Office.Interop.Outlook` | `Ol.MailItem msg = (Ol.MailItem)item;` |
95+
96+
```csharp
97+
// ✓ works in v0.4.2+ — no `using Xl = ...;` needed
98+
Xl.Range used = xlSheet.UsedRange;
99+
foreach (Xl.Range row in used.Rows) { /* ... */ }
100+
catch (System.Exception ex) { Console.WriteLine(ex.Message); }
101+
```
102+
103+
Note that **bare `Range` is still ambiguous** — this is deliberate. The
104+
alias only guarantees a reliable qualifier (`Xl.Range`) is always in scope;
105+
it does NOT try to silently pick the Office type over `System.Range`.
106+
107+
#### Mechanics (only matters if you're debugging a weird error)
108+
109+
The host prepends `using Xl = global::Microsoft.Office.Interop.Excel; ` to
110+
the script source as a one-line preamble before passing it to Roslyn.
111+
Compile-error line/column numbers are remapped back to the author's real
112+
source — a CS0104 reported at "line 168" of the compiled source surfaces as
113+
line 167 of your .csx, matching what you'd see in an editor.
114+
115+
If you need to inspect the preamble or contribute one from your own
116+
plugin, see `IComBridgePlugin.ScriptUsingAliases` in
117+
`src/ComBridge.Core/IComBridgePlugin.cs`.
118+
119+
### Fallback: fully-qualify the BCL type
120+
121+
If you'd rather not use the alias (or you're on a combridge older than
122+
v0.4.2), fully-qualify the BCL side instead:
77123

78124
```csharp
79125
// ❌ fails in any Outlook .csx with:
@@ -87,15 +133,17 @@ try { ... }
87133
catch (System.Exception ex) { Console.WriteLine(ex.Message); }
88134
```
89135

90-
Common collisions to fully-qualify when writing Office scripts:
91-
92-
| BCL type | Use as |
93-
|---|---|
94-
| `Exception` | `System.Exception` |
95-
| `Action` | `System.Action` (for delegates; Office's `Action` is different) |
96-
| `Style` | `System.Drawing.Style` if you actually want it; otherwise just don't import `System.Drawing` |
97-
| `Font`, `Color` | `System.Drawing.Font` / `System.Drawing.Color` |
98-
| `Page` (PPT) | `System.Web.Page` etc. |
136+
Common collisions to fully-qualify (or use the auto-provided alias on the
137+
Office side):
138+
139+
| BCL type | Use as | Or |
140+
|---|---|---|
141+
| `Range` (modern C# slicing) | `System.Range` | `Xl.Range` / `Wd.Range` |
142+
| `Exception` | `System.Exception` | `Ol.Exception` (rarely what you want) |
143+
| `Action` | `System.Action` (delegate) | `Wd.Action` / `Pp.Action` (different concept) |
144+
| `Style`, `Font`, `Color` | `System.Drawing.*` | `Wd.Style` / `Xl.Style` / `Pp.Style` |
145+
| `Page` | `System.Web.Page` | `Wd.Page` / `Pp.Page` |
146+
| `Application` | `System.Windows.Application` | `Xl.Application` etc. |
99147

100148
Or restructure to avoid the conflict: throw and let the host log it
101149
instead of `catch`ing, or catch a more specific type

LLM/troubleshooting.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,58 @@ the right section based on when the error fires.
66

77
## Build-time errors
88

9+
### `error CS0104: 'Range' is an ambiguous reference between 'Microsoft.Office.Interop.Word.Range' and 'System.Range'` (or `Exception`, `Application`, `Style`, …)
10+
11+
**Symptom.** A `.csx` running under any Windows Office plugin
12+
(`excel` / `word` / `powerpoint` / `outlook`) fails to compile with
13+
CS0104 on a bare identifier the BCL and the Office interop namespace
14+
both define. Most common offender is `Range` because modern C# added
15+
`System.Range` for slicing syntax.
16+
17+
**Why.** The Office plugin's `ScriptUsings` imports the interop
18+
namespace so authors can write `Application`, `Range`, etc. unqualified.
19+
But that namespace also defines its own `Exception`, `Application`,
20+
`Style`, `Action`, `Font`, `Range`, etc. that collide with `System.*`.
21+
Roslyn refuses to pick one.
22+
23+
**Fix (v0.4.2+ — recommended).** Use the **auto-provided two-letter
24+
alias** the plugin contributes to your script. No `using ... = ...`
25+
declaration needed — it's already in scope:
26+
27+
```csharp
28+
Xl.Range used = xlSheet.UsedRange; // not bare Range
29+
Wd.Range rng = wdDoc.Content;
30+
catch (System.Exception ex) { ... } // qualify the BCL side
31+
```
32+
33+
Alias table:
34+
35+
| Plugin | Auto-alias |
36+
|--------------|-----------------------------------------------------|
37+
| `excel` | `Xl = global::Microsoft.Office.Interop.Excel` |
38+
| `word` | `Wd = global::Microsoft.Office.Interop.Word` |
39+
| `powerpoint` | `Pp = global::Microsoft.Office.Interop.PowerPoint` |
40+
| `outlook` | `Ol = global::Microsoft.Office.Interop.Outlook` |
41+
42+
**Fix (older combridge).** Either declare the alias yourself at the
43+
top of the .csx:
44+
45+
```csharp
46+
using Xl = global::Microsoft.Office.Interop.Excel;
47+
Xl.Range used = xlSheet.UsedRange;
48+
```
49+
50+
…or fully-qualify the BCL side every time (`System.Exception`,
51+
`System.Range`, etc.). See `LLM/scripting.md` § "Office interop
52+
namespaces shadow common BCL names" for the full collision table.
53+
54+
**Note: bare `Range` stays ambiguous on purpose.** The auto-alias
55+
guarantees a reliable qualifier is in scope; it does NOT silently win
56+
the race against `System.Range`. If you want bare `Range` to mean the
57+
Office type, add `using Range = Xl.Range;` to your own script.
58+
59+
See FR `FR_office_script_interop_alias.md` for the design rationale.
60+
961
### `error COMBRIDGE001: ... could not locate its required interop assemblies`
1062

1163
**Cause.** A plugin declared `@(RequiredInteropFile)` items in its csproj

src/ComBridge.Core/IComBridgePlugin.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,38 @@ public interface IComBridgePlugin
4848
/// <summary>Namespaces auto-imported in user scripts.</summary>
4949
IEnumerable<string> ScriptUsings { get; }
5050

51+
/// <summary>
52+
/// C# alias directives the host prepends to the script source before
53+
/// compiling, one per element. Each element is the alias body WITHOUT
54+
/// the leading <c>using</c> keyword and WITHOUT the trailing semicolon,
55+
/// e.g. <c>"Wd = global::Microsoft.Office.Interop.Word"</c> — the host
56+
/// emits <c>using Wd = global::Microsoft.Office.Interop.Word;</c>.
57+
/// <para>
58+
/// Why this exists separately from <see cref="ScriptUsings"/>: Roslyn's
59+
/// <c>ScriptOptions.Imports</c> accepts namespaces and <c>static</c>
60+
/// usings but NOT alias directives, so an alias can't be contributed
61+
/// the same way namespaces are. The script host works around it by
62+
/// rendering aliases into a one-line preamble at the top of the script
63+
/// source and remapping diagnostic line numbers back to the author's
64+
/// real source. See <see cref="ScriptHost.RunAsync"/>.
65+
/// </para>
66+
/// <para>
67+
/// Purpose: solve the CS0104 Office-interop / BCL name collision
68+
/// (<c>Range</c>, <c>Exception</c>, <c>Application</c>, <c>Style</c>, …)
69+
/// without making every author re-type the alias in every <c>.csx</c>.
70+
/// Default = empty. Office plugins (Word/Excel/PowerPoint/Outlook on
71+
/// Windows) override to contribute their conventional two-letter alias
72+
/// (<c>Wd</c>/<c>Xl</c>/<c>Pp</c>/<c>Ol</c>).
73+
/// </para>
74+
/// <para>
75+
/// We deliberately do NOT try to win the bare-name race — bare
76+
/// <c>Range</c> stays ambiguous. The alias only guarantees a reliable
77+
/// qualifier (<c>Wd.Range</c>, <c>Xl.Range</c>) is always in scope.
78+
/// </para>
79+
/// <para>See FR <c>FR_office_script_interop_alias.md</c>.</para>
80+
/// </summary>
81+
IEnumerable<string> ScriptUsingAliases => Array.Empty<string>();
82+
5183
/// <summary>
5284
/// Plugin-specific commands (in addition to the built-in <c>run-script</c>).
5385
/// Examples: SW <c>checkpoint</c>, Excel <c>dump-sheet</c>.

src/ComBridge.Core/ScriptHost.cs

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Text;
2+
using System.Text.RegularExpressions;
13
using Microsoft.CodeAnalysis;
24
using Microsoft.CodeAnalysis.CSharp.Scripting;
35
using Microsoft.CodeAnalysis.Scripting;
@@ -136,13 +138,41 @@ public static async Task<int> RunAsync(
136138
loader.RegisterDependency(plugin.GetType().Assembly);
137139
loader.RegisterDependency(plugin.GlobalsType.Assembly);
138140

139-
using var scriptStream = File.OpenRead(scriptPath);
140-
var script = CSharpScript.Create(scriptStream, options, plugin.GlobalsType, loader);
141+
// Build the alias preamble (FR_office_script_interop_alias.md).
142+
// Each plugin-contributed alias is rendered as `using <alias>; `
143+
// and the whole set is concatenated onto a SINGLE first line so
144+
// line-number offsetting is exactly 1 (the script body's line 1
145+
// becomes compiled line 2). If a plugin contributes nothing, the
146+
// preamble is empty and there's zero behavior change.
147+
var aliases = plugin.ScriptUsingAliases?.ToList() ?? new List<string>();
148+
string preamble = aliases.Count == 0
149+
? ""
150+
: string.Concat(aliases.Select(a => $"using {a}; ")) + "\n";
151+
int preambleLineOffset = preamble.Length == 0 ? 0 : 1;
152+
153+
// To keep PDB emit happy (CS8055 needs explicit encoding), we
154+
// build a MemoryStream that preserves the source file's BOM (if
155+
// any) and uses the matching encoding for the preamble bytes.
156+
// Without this, mixing UTF-8-BOM script content with default-
157+
// encoded preamble would corrupt non-ASCII characters in the body.
158+
byte[] scriptBytes = File.ReadAllBytes(scriptPath);
159+
(Encoding enc, int bomLen) = DetectEncoding(scriptBytes);
160+
using var ms = new MemoryStream(bomLen + preamble.Length * 2 + scriptBytes.Length);
161+
if (bomLen > 0) ms.Write(scriptBytes, 0, bomLen);
162+
if (preamble.Length > 0)
163+
{
164+
byte[] preBytes = enc.GetBytes(preamble);
165+
ms.Write(preBytes, 0, preBytes.Length);
166+
}
167+
ms.Write(scriptBytes, bomLen, scriptBytes.Length - bomLen);
168+
ms.Position = 0;
169+
170+
var script = CSharpScript.Create(ms, options, plugin.GlobalsType, loader);
141171
var diags = script.Compile();
142172
var errors = diags.Where(d => d.Severity == DiagnosticSeverity.Error).ToList();
143173
if (errors.Count > 0)
144174
{
145-
foreach (var d in errors) output.WriteLine(d.ToString());
175+
foreach (var d in errors) output.WriteLine(RemapDiagnosticLine(d.ToString(), preambleLineOffset));
146176
return 3;
147177
}
148178
var state = await script.RunAsync(globals);
@@ -169,4 +199,50 @@ public static async Task<int> RunAsync(
169199
Console.SetError(originalErr);
170200
}
171201
}
202+
203+
/// <summary>
204+
/// Detect the encoding + BOM length of raw source bytes. Default is
205+
/// UTF-8 without BOM (Roslyn's expectation for the streaming overload).
206+
/// </summary>
207+
private static (Encoding enc, int bomLen) DetectEncoding(byte[] bytes)
208+
{
209+
if (bytes.Length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF)
210+
return (new UTF8Encoding(true), 3);
211+
if (bytes.Length >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE)
212+
return (Encoding.Unicode, 2); // UTF-16 LE
213+
if (bytes.Length >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF)
214+
return (Encoding.BigEndianUnicode, 2); // UTF-16 BE
215+
if (bytes.Length >= 4 && bytes[0] == 0 && bytes[1] == 0 && bytes[2] == 0xFE && bytes[3] == 0xFF)
216+
return (Encoding.UTF32, 4);
217+
return (new UTF8Encoding(false), 0);
218+
}
219+
220+
/// <summary>
221+
/// Rewrite Roslyn diagnostic strings so reported line numbers point at
222+
/// the author's real source. Diagnostic text format is:
223+
/// <c>&lt;path&gt;(LINE,COL): severity ID: message</c> — we capture
224+
/// <c>(LINE,COL)</c> and subtract the preamble offset from LINE.
225+
/// <para>
226+
/// If the offending span is actually inside the injected preamble
227+
/// (which would be a bug in the plugin's alias declaration, not the
228+
/// author's script), we leave the line number alone so the bug is
229+
/// visible rather than presenting as "your script line 0" gibberish.
230+
/// </para>
231+
/// </summary>
232+
private static readonly Regex DiagLocRx = new(
233+
@"\((?<line>\d+),(?<col>\d+)\)",
234+
RegexOptions.Compiled);
235+
236+
private static string RemapDiagnosticLine(string diagText, int preambleLineOffset)
237+
{
238+
if (preambleLineOffset == 0) return diagText;
239+
return DiagLocRx.Replace(diagText, m =>
240+
{
241+
int reportedLine = int.Parse(m.Groups["line"].Value);
242+
int col = int.Parse(m.Groups["col"].Value);
243+
int realLine = reportedLine - preambleLineOffset;
244+
if (realLine < 1) return m.Value; // preamble-side error — leave as-is
245+
return $"({realLine},{col})";
246+
});
247+
}
172248
}

src/plugins/ComBridge.Plugins.Excel/ExcelPlugin.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ public IEnumerable<MetadataReference> ScriptReferences
103103
"Microsoft.Office.Interop.Excel",
104104
};
105105

106+
/// <summary>
107+
/// Auto-provide the conventional two-letter alias for the Excel interop
108+
/// namespace so user .csx files can write <c>Xl.Range used = ws.UsedRange;</c>
109+
/// without re-declaring <c>using Xl = global::Microsoft.Office.Interop.Excel;</c>
110+
/// at the top of every script. See FR_office_script_interop_alias.md.
111+
/// </summary>
112+
public IEnumerable<string> ScriptUsingAliases => new[]
113+
{
114+
"Xl = global::Microsoft.Office.Interop.Excel",
115+
};
116+
106117
public IEnumerable<IBridgeCommand> Commands => new IBridgeCommand[]
107118
{
108119
new InfoCommand(),

0 commit comments

Comments
 (0)