# Decompiling an Excel template into source - #1 C#

> Source: <https://kookerella.com/posts/decompiling-excel-template-1-csharp/>
> Published: 2026-08-26 00:00:00+00:00

# Decompiling an Excel template into source - #1 C#

You don’t have to hand-write Excel styling code. If someone already built the spreadsheet
you need to reproduce — a finance person’s invoice template, a report layout a designer
signed off on — you can reverse-engineer it into real source code, then wire in your own
data. This is the first of four posts walking through the same demo in C#, F#, XSLT, and
plain JSON. The full project is on GitHub:
[Kookerella.Demo.DecompileToSource](https://github.com/Kookerella-Ltd/Kookerella.Demo.DecompileToSource).

## What we’re starting from

A plain Excel file, built by hand in Excel. It has:

- A bold, 16pt “INVOICE” title
- A header row styled white-on-navy with a bottom border
- Two sample line-item rows, with an
`Amount`

column computed as`Qty * Unit Price`

Nobody wrote any code to produce this file — it’s just a spreadsheet.

## Step 1: Install the tool

The reverse-engineering happens via a CLI tool, `fsopenxmldsl-mcp`

, published as a
`dotnet`

global tool:

```
dotnet tool install -g Kookerella.FsOpenXmlDsl.Mcp
```

## Step 2: Reverse-engineer the template

```
fsopenxmldsl-mcp convert templates/InvoiceTemplate.xlsx --lang csharp -o decompiled/InvoiceTemplate.g.cs
```

This reads the real `.xlsx`

file and prints out C# code that reproduces it exactly. The
top line of the result is:

```
#:package Kookerella.CsOpenXmlDsl@0.3.2
```

That `#:package`

line is a [.NET 10 file-based app](https://learn.microsoft.com/dotnet/core/whatsnew/dotnet-10#file-based-apps)
directive — this single `.cs`

file is a complete, runnable program with no `.csproj`

needed. You can run it immediately, on any machine with the .NET 10 SDK:

```
cd decompiled
dotnet run InvoiceTemplate.g.cs
```

That produces `output.xlsx`

— pixel-for-pixel the same as the original template. Nothing
was lost in translation: title, header styling, borders, currency formatting, and the
`Qty * Unit Price`

formulas are all there, expressed as plain C# method calls
(`Cell.Text(...)`

, `.WithStyle(...)`

, `Cell.Formula(...)`

, and so on).

**This is the point where “decompiling a spreadsheet” stops being a metaphor.** You now
have the template as source code you can read, diff, and edit.

## Step 3: Adding the package to a real project

The file-based app above pinned the package inline for convenience. In a normal project you add it the usual way:

```
dotnet add package Kookerella.CsOpenXmlDsl
```

`Kookerella.CsOpenXmlDsl`

is a C# wrapper around a separate F# core library,
`Kookerella.FsOpenXmlDsl`

, which does the actual OOXML reading/writing.

## Step 4: Turning the decompiled snippet into real code

The decompiled file is disposable — it’s the starting point, not the deliverable. Nobody wants to hardcode “Widgets” and “Gadgets” forever. The diff from the decompiled file to a maintained version is deliberately small:

Wrap the top-level statements in a class and a

`Build`

method that takes real data (`IEnumerable<OrderLine>`

) instead of running standalone.Replace the two hardcoded item rows with a LINQ projection over that data:

``` js
var dataRows = orders.Select((order, i) =>
{
    var row = 4 + i;
    return Row.Of(
        Cell.Text(order.Item),
        Cell.Number(order.Quantity),
        Cell.Number(order.UnitPrice).WithStyle(CellStyle.Default.WithNumberFormat(NumberFormatKind.Currency)),
        Cell.Formula($"B{row}*C{row}", order.Quantity * order.UnitPrice).WithStyle(CellStyle.Default.WithNumberFormat(NumberFormatKind.Currency)));
});
```

Splice the generated rows into the sheet with a C# collection expression, instead of listing two hardcoded rows:

``` js
var sheet0 = Sheet.Create(
    "Invoice",
    [
        /* title row, exactly as decompiled */,
        /* header row, exactly as decompiled */,
        .. dataRows,
    ]);
```

Everything else — every style call, every color, every border — is copied verbatim from the decompiled file. The styling was never hand-written by a developer in the first place, so there’s nothing to “port” — only the two data rows needed to become data-driven.

## Step 5: Prove it stays correct

Because this is now real code, it can be tested like real code — something you can’t do
to a `.xlsx`

file sitting on a file share. The test suite checks three things a
spreadsheet can’t check about itself:

**Schema validity**— runs a real`OpenXmlValidator`

over the output. If a future change ever produces invalid OOXML, this catches it immediately instead of someone discovering a “repair this file?” dialog in Excel weeks later.**Correctness**— feeds in 0, 1, and 3 orders and checks the row count and the`Amount`

formula text/value are correct for each one.**Styling preservation**— pins the exact colors, bold, font size, and border style that came from the original template, so a future edit can’t silently drift the invoice’s look away from what the original designer signed off on.

## Recap

| Step | Command | Output |
|---|---|---|
| Install the tool | `dotnet tool install -g Kookerella.FsOpenXmlDsl.Mcp` | `fsopenxmldsl-mcp` on your PATH |
| Reverse-engineer | `fsopenxmldsl-mcp convert InvoiceTemplate.xlsx --lang csharp -o InvoiceTemplate.g.cs` | A runnable C# file that reproduces the original `.xlsx` exactly |
| Add the package | `dotnet add package Kookerella.CsOpenXmlDsl` | The C# wrapper over the F# core |
| Make it data-driven | Replace hardcoded rows with a LINQ projection over your own data model | A maintained generator |
| Verify | `dotnet test` | Schema validity, formula correctness, and styling all pinned by real tests |

The template never had to be redesigned in code, and the developer never had to
reverse-engineer OOXML XML by hand — the tool did that translation, once, and everything
after that is ordinary, testable C#. Next up: [the same demo in F#](/posts/decompiling-excel-template-2-fsharp/).
