一键导入
acad-sheet-sets
Sheet Set (.dst) COM API — sheets, subsets, custom properties, lock management
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Sheet Set (.dst) COM API — sheets, subsets, custom properties, lock management
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Corridors, baselines, regions, assemblies, subassemblies, corridor surfaces
Custom subassembly .NET design — CorridorState, SubassemblyGenerator, targets, SATemplate
Stock-subassembly pattern catalog — need→file index, worked DrawImplement recipes, point/link/shape code reference, mined from Autodesk's 120 VB stock subassemblies
Superelevation design, attainment, cross slope, pivot points, lane config
Block definitions, references, attributes, dynamic block properties, exploding
Dimensions — aligned, rotated, arc, radial, angular, ordinate, text overrides
| name | acad-sheet-sets |
| description | Sheet Set (.dst) COM API — sheets, subsets, custom properties, lock management |
Sheet Set Manager has no managed .NET API — it is COM-only.
Access it via dynamic COM using CLSIDs. Do NOT reference AcSmComponents.Interop.dll in .NET 8 plugins — it causes ReflectionTypeLoadException during NETLOAD. Use the dynamic pattern shown below.
internal static class AcSmGuids
{
// IAcSmSheetSetMgr — the root manager object
public const string SheetSetMgr = "bcfa0ab8-cefc-43e9-841b-8141f44fe240";
// IAcSmCustomPropertyValue — create new custom property values
public const string CustomPropertyValue = "909a5a17-c368-40cb-8b92-d174395128df";
}
using System;
using System.Runtime.InteropServices;
// Create the manager
var mgrType = Type.GetTypeFromCLSID(new Guid(AcSmGuids.SheetSetMgr), true);
dynamic mgr = Activator.CreateInstance(mgrType);
object db = null;
bool weOpened = false;
try
{
// Reuse already-open database if available
try { db = mgr.FindOpenDatabase(dstPath); } catch { }
if (db == null)
{
db = mgr.OpenDatabase(dstPath, false); // false = read/write
weOpened = true;
}
// ... use db ...
}
finally
{
if (weOpened && db != null)
mgr.Close(db); // close only if we opened it
if (db != null) Marshal.ReleaseComObject(db);
Marshal.ReleaseComObject(mgr);
}
CRITICAL: Always call Marshal.ReleaseComObject() on every COM object when done. Failure to release causes the .dst file to stay locked after Civil 3D exits.
The database must be locked before writing. Lock status codes:
internal static class AcSmLockStatus
{
public const int UnLocked = 0;
public const int Locked_Local = 1; // locked by current process
public const int Locked_Remote = 2; // locked by another user
public const int Locked_ReadOnly = 4;
public const int Locked_AccessDenied = 8;
public const int Locked_NotConnected = 16;
}
dynamic db = database;
int status = (int)db.GetLockStatus();
if (status == AcSmLockStatus.UnLocked)
db.LockDb(db); // pass the db object as argument
// Unlock with commit=true to save, commit=false to discard
db.UnlockDb(db, commit: true);
// Query lock owner (returns empty strings if not locked)
string userName = "", machineName = "";
db.GetLockOwnerInfo(out userName, out machineName);
Stale lock recovery — if the same user/machine holds the lock (e.g., after a crash), force-release then re-acquire:
if (userName.Equals(Environment.UserName, StringComparison.OrdinalIgnoreCase)
&& machineName.Equals(Environment.MachineName, StringComparison.OrdinalIgnoreCase))
{
db.UnlockDb(db, false);
db.LockDb(db);
}
The sheet set contains a tree of components — each is either a sheet or a subset (folder). Distinguish them by whether GetSheetEnumerator() succeeds.
dynamic sheetSet = db.GetSheetSet();
try
{
string name = (string)sheetSet.GetName();
dynamic rootEnum = sheetSet.GetSheetEnumerator();
try
{
rootEnum.Reset();
dynamic comp;
while ((comp = rootEnum.Next()) != null)
{
try
{
// Try as subset (has GetSheetEnumerator)
try
{
dynamic subsetEnum = comp.GetSheetEnumerator();
string subsetName = (string)comp.GetName();
// recurse into subsetEnum...
Marshal.ReleaseComObject(subsetEnum);
continue;
}
catch { /* not a subset */ }
// It's a sheet
string number = (string)(comp.GetNumber() ?? "");
string title = (string)(comp.GetTitle() ?? "");
}
finally { Marshal.ReleaseComObject(comp); }
}
}
finally { Marshal.ReleaseComObject(rootEnum); }
}
finally { Marshal.ReleaseComObject(sheetSet); }
// From a sheet component (already have dynamic comp)
dynamic bag = comp.GetCustomPropertyBag(); // may return null
if (bag == null) return;
try
{
dynamic enumerator = bag.GetPropertyEnumerator();
try
{
enumerator.Reset();
while (true)
{
string propName = null;
dynamic propValue = null;
try { enumerator.Next(out propName, out propValue); }
catch { break; }
if (propName == null) break;
try
{
int flags = (int)propValue.GetFlags();
// Filter to custom sheet properties
if ((flags & AcSmPropertyFlags.CUSTOM_SHEET_PROP) != 0 ||
(flags & AcSmPropertyFlags.IS_CHILD) != 0)
{
string value = propValue.GetValue()?.ToString() ?? "";
}
}
finally { if (propValue != null) Marshal.ReleaseComObject(propValue); }
}
}
finally { Marshal.ReleaseComObject(enumerator); }
}
finally { Marshal.ReleaseComObject(bag); }
internal static class AcSmPropertyFlags
{
public const int CUSTOM_SHEET_PROP = 1; // user-defined per-sheet property
public const int IS_CHILD = 2; // inherited from sheet set level
}
Requires an active lock on the database.
dynamic bag = sheet.GetCustomPropertyBag();
try
{
dynamic propValue = null;
try
{
// Try to get existing property
try { propValue = bag.GetProperty(propertyName); }
catch { /* doesn't exist yet */ }
if (propValue == null)
{
// Create new property value object
var pvType = Type.GetTypeFromCLSID(new Guid(AcSmGuids.CustomPropertyValue), true);
propValue = Activator.CreateInstance(pvType);
propValue.InitNew(bag);
propValue.SetFlags(AcSmPropertyFlags.CUSTOM_SHEET_PROP);
}
propValue.SetValue(newValue);
bag.SetProperty(propertyName, propValue);
}
finally { if (propValue != null) Marshal.ReleaseComObject(propValue); }
}
finally { Marshal.ReleaseComObject(bag); }
// Requires lock on db
sheet.SetNumber("C1.01");
sheet.SetTitle("Site Plan");
// Commit (save to .dst file)
db.UnlockDb(db, true);
// Discard (no changes written)
db.UnlockDb(db, false);
Always call UnlockDb in a finally block to avoid leaving the file locked.
No extra DLL references needed beyond the standard three:
<Reference Include="acdbmgd">
<HintPath>$(ArxMgdPath)\acdbmgd.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="acmgd">
<HintPath>$(ArxMgdPath)\acmgd.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="accoremgd">
<HintPath>$(ArxMgdPath)\accoremgd.dll</HintPath>
<Private>False</Private>
</Reference>
Do not add AcSmComponents.dll or AcSmComponents.Interop.dll as references — they cause ReflectionTypeLoadException at NETLOAD time in .NET 8 plugins.
try/finally with Marshal.ReleaseComObject(). Missing a release keeps the .dst locked until AutoCAD exits.GetSheetEnumerator() and catching the exception if it fails.GetCustomPropertyBag() can return null — check before using. Sheets with no custom properties may return null.FindOpenDatabase throws if the database is not open — always wrap in try/catch and fall back to OpenDatabase.FindOpenDatabase returned an existing handle, do not call mgr.Close(db) — that would close a database another part of the application is using.dynamic COM via CLSID instead (the pattern shown above)./c3d-api:c3d-project-setup - DLL references, acad_path.props, plugin setup/c3d-api:autocad-class-reference - AutoCAD .NET class overview