소스 정보
- 저장소
- hebackus/c3d-api-plugin
- 최근 소스 활동
- 2026년 4월 9일 01:44
- 감지된 SKILL.md 언어
- 영어
- 스타
- 3
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/hebackus/c3d-api-plugin --skill acad-sheet-sets명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
SOC 직업 분류 기준
| 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