Building ETABSharp: A C# Wrapper for the ETABS API
The raw ETABS API is verbose and weakly typed. Here's how I designed a fluent C# wrapper that makes structural automation actually enjoyable to write and get help from LLM.
The ETABS API is powerful. It exposes almost everything — model geometry, load cases, analysis results, design checks. But working with it directly is painful.
Methods are stringly typed. There’s no IntelliSense guidance on what parameters mean.
Error handling is COM-era ref parameters. You end up writing the same boilerplate
fifty times per script.
ETABSharp is a C# wrapper that fixes this.
The problem with the raw API
The pain is not only one API call. In real automation, you have to orchestrate stateful ETABS sessions safely:
Mode Aattach to a running ETABS and never kill the user’s appMode Bstart a hidden ETABS instance, run a batch task, and always close it- Handle model lock/unlock before edits
- Suppress save prompts in unattended flows
- Check dozens of integer return codes (
ret != 0) after every operation
This is the kind of control flow you end up writing:
ETABSApplication? app = null;
try
{
// Mode B: create isolated hidden ETABS
app = ETABSWrapper.CreateNew(startApplication: true);
if (app == null) throw new Exception("Failed to create ETABS instance.");
app.Application.Hide();
int openRet = app.Model.Files.OpenFile(TEST_MODEL_PATH);
if (openRet != 0) throw new Exception($"OpenFile failed: {openRet}");
if (app.Model.ModelInfo.IsLocked())
app.Model.ModelInfo.SetLocked(false);
int analysisRet = app.Model.Analyze.RunCompleteAnalysis();
if (analysisRet != 0) throw new Exception($"RunCompleteAnalysis failed: {analysisRet}");
int saveRet = app.Model.Files.SaveFile(TEST_MODEL_PATH);
if (saveRet != 0) throw new Exception($"SaveFile failed: {saveRet}");
}
finally
{
// Critical: hidden instance must always be closed
if (app != null) app.Application.ApplicationExit(false);
}
And if you need joint displacement from raw COM, you still have the long
Results.JointDispl(...) signature with many ref arrays.
The ETABSharp approach
ETABSharp wraps the common automation paths so the intent is clearer. For example, in your sidecar tests:
var app = ETABSWrapper.Connect(); // Mode A: attach to running ETABS
if (app == null) return;
var filePath = app.Model.ModelInfo.GetModelFilename(includePath: true);
var isLocked = app.Model.ModelInfo.IsLocked();
var caseStatuses = app.Model.Analyze.GetCaseStatus();
var isAnalyzed = caseStatuses.Any(cs => cs.IsFinished);
Console.WriteLine($"File: {filePath}");
Console.WriteLine($"Locked: {isLocked}");
Console.WriteLine($"Analyzed: {isAnalyzed}");
// Typed wrapper result instead of manual COM ref arrays
var jointDispl = app.Model.AnalysisResults.GetJointDispl("Joint28", eItemTypeElm.GroupElm);
Console.WriteLine($"Joint displacement rows: {jointDispl.NumberResults}");
Same ETABS power, but cleaner orchestration and safer session lifecycle rules.
Architecture decisions
1. Thin wrapper, not an abstraction
ETABSSharp wraps the ETABS COM API — it doesn’t try to replace it. Every method maps 1:1 to an ETABS API call. This means:
- The full API surface is available
- Result pattern
- Behaviour is predictable — if ETABS does it, ETABSharp can do it
- No magic or hidden logic
2. Result types instead of ref parameters
Every output is a strongly typed record:
public record JointDisplacement(
string Obj,
string LoadCase,
string StepType,
double StepNum,
double U1,
double U2,
double U3
);
3. Fluent access pattern
Can get results dirrect from API or you can get table results for those table dont have direct api like material table
var etabs = await ETABSWrapper.Connect();
// Navigate to what you need
var frameForces = app.Model
.AnalysisResults
.FrameForces("level6",eItemTypeElm.GroupElm );
// Get table from etabs model
var tableResults = app.Model
.DatabaseTables
.GetTableForDisplayArray(tableKey, fieldKeys, group);
The AI layer
The real reason I built ETABSharp isn’t just the cleaner API — it’s what the clean API enables: an MCP server that lets AI assistants talk to ETABS directly and building .
More on that in the next post.
Get it
- GitHub: github.com/tadoEng/EtabSharp
- Doc: deepwiki.com/tadodev/EtabSharp
- NuGet:
dotnet add package EtabSharp --version 0.3.1-beta
If you’re automating ETABS with C# and you have questions, open an issue or reach out. I use this in production work every week.
Discover more
Talking to ETABS in Natural Language: ETABSSharp + MCP
I built an MCP server on top of ETABSSharp that lets AI assistants query, modify, and run analysis on ETABS models through plain English. Here's how it works.
Saola: A Grasshopper Plugin Built on ETABSharp
ETABSharp made ETABS automation testable and composable. Saola takes that one step further — bringing it into Grasshopper so structural engineers can build parametric ETABS workflows without leaving the canvas.