Skip to content

Commit a5e55d8

Browse files
committed
Merge branch 'master' of https://github.com/tadoEng/EtabSharp
2 parents c4cbb01 + 1588334 commit a5e55d8

5 files changed

Lines changed: 702 additions & 555 deletions

File tree

src/EtabSharp/Core/ETABSApplication.cs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using ETABSv1;
55
using Microsoft.Extensions.Logging;
66
using Microsoft.Extensions.Logging.Abstractions;
7+
using System.Runtime.InteropServices;
8+
79

810
namespace EtabSharp.Core;
911

@@ -157,18 +159,19 @@ public void ExecuteSafely(Action apiCall, string? functionName = null)
157159
/// If true, ETABS prompts to save unsaved changes.
158160
/// If false (default), exits immediately — correct for Mode B hidden instances.
159161
/// </param>
162+
// Close() remains explicit — user calls this when they want ETABS to exit
160163
public void Close(bool savePrompt = false)
161164
{
162165
if (_disposed) return;
163166

164167
try
165168
{
166169
_application.Value.ApplicationExit(savePrompt);
167-
_logger.LogInformation("ETABS application closed");
170+
_logger.LogInformation("ETABS application exited");
168171
}
169172
catch (Exception ex)
170173
{
171-
_logger.LogWarning(ex, "Error during Close: {Message}", ex.Message);
174+
_logger.LogWarning(ex, "Error during ApplicationExit: {Message}", ex.Message);
172175
}
173176
}
174177

@@ -179,10 +182,36 @@ public void Close(bool savePrompt = false)
179182
/// </summary>
180183
public void Dispose()
181184
{
182-
if (!_disposed)
185+
if (_disposed) return;
186+
_disposed = true;
187+
188+
// Only attempt COM cleanup on Windows platforms where Marshal.ReleaseComObject is supported.
189+
// This prevents CA1416 diagnostics and avoids calling Windows-only runtime APIs on other platforms.
190+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
191+
{
192+
try
193+
{
194+
// Release child managers first (reverse init order)
195+
if (_model.IsValueCreated)
196+
Marshal.ReleaseComObject(_model.Value);
197+
198+
if (_application.IsValueCreated)
199+
Marshal.ReleaseComObject(_application.Value);
200+
201+
// Release raw COM refs last (parents after children)
202+
Marshal.ReleaseComObject(_sapModel);
203+
Marshal.ReleaseComObject(_api);
204+
205+
_logger.LogInformation("COM references released");
206+
}
207+
catch (Exception ex)
208+
{
209+
_logger.LogWarning(ex, "Error releasing COM objects: {Message}", ex.Message);
210+
}
211+
}
212+
else
183213
{
184-
Close(false);
185-
_disposed = true;
214+
_logger.LogInformation("Skipping COM cleanup: not running on Windows platform");
186215
}
187216

188217
GC.SuppressFinalize(this);

src/EtabSharp/EtabSharp.csproj

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<!-- Package Identity -->
99
<Title>EtabSharp</Title>
1010
<PackageId>EtabSharp</PackageId>
11-
<Version>0.3.0-beta</Version>
11+
<Version>0.3.1-beta</Version>
1212
<Copyright>Copyright © Thanh Tu 2026</Copyright>
1313
<Authors>Thanh Tu</Authors>
1414

@@ -29,11 +29,8 @@
2929

3030
<!-- Package Release Notes -->
3131
<PackageReleaseNotes>
32-
v0.3.0-beta
33-
* New IApplication interface wrapping the ETABS cOAPI object, with a concrete ETABSApplicationManager implementation to manage starting, hiding, and cleanly exiting ETABS.
34-
* Safer application access via ETABSApplication.Application : IApplication; raw COM (cOAPI) and GetRawAPI()/GetRawModel() are now internal, and Close() routes through Application.ApplicationExit() for more robust shutdown.
35-
* PID-aware instance attachment in ETABSWrapper: connections now target a specific ETABS process when possible, and a new ConnectToProcess(int pid) API lets callers attach to an exact running instance.
36-
32+
v0.3.1-beta
33+
* Update dispose behavior correctly dispose of COM objects and release ETABS reference
3734
</PackageReleaseNotes>
3835

3936
<!-- CI/CD Settings -->

test/EtabSharp.Test/SkipHelper.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Plan (pseudocode):
2+
// 1. Provide a small helper `Skip` class in the same test namespace so existing calls like
3+
// `Skip.If(condition, "reason")` compile.
4+
// 2. Implement `If(bool condition, string reason)` to throw xUnit's skip exception when
5+
// condition is true. This causes xUnit to mark the test as skipped instead of failed.
6+
// 3. Keep the helper internal and minimal to avoid changing other code.
7+
// 4. Put detailed plan as comments here and then the implementation below.
8+
//
9+
// Detailed pseudocode:
10+
// - namespace: EtabSharp.Test (matches tests' namespace)
11+
// - using: Xunit (for SkipException)
12+
// - define internal static class Skip
13+
// - public static void If(bool condition, string reason)
14+
// - if condition is true:
15+
// - throw new SkipException(reason)
16+
// - else: return (no-op)
17+
// - This file will allow existing tests that call `Skip.If(...)` to compile and run,
18+
// and tests will be reported as skipped when the condition is true.
19+
20+
using Xunit;
21+
22+
namespace EtabSharp.Test
23+
{
24+
/// <summary>
25+
/// Minimal helper to support calls like `Skip.If(condition, "reason")` in tests.
26+
/// Calls Assert.Skip() so the runner reports the test as Skipped.
27+
/// </summary>
28+
internal static class Skip
29+
{
30+
public static void If(bool condition, string reason)
31+
{
32+
if (condition)
33+
{
34+
Assert.Skip(reason);
35+
}
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)