using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; // COM 객체 해제를 위해 필요 using System.Threading; using System.Threading.Tasks; using Teigha.DatabaseServices; using Teigha.Geometry; using Teigha.Runtime; using Excel = Microsoft.Office.Interop.Excel; namespace DwgExtractorManual.Models { /// /// DWG 파일에서 Excel로 데이터 내보내기 클래스 /// AttributeReference, AttributeDefinition, DBText, MText 추출 지원 /// internal class ExportExcel : IDisposable { // ODA 서비스 객체 private Services appServices; // Excel COM 객체들 private Excel.Application excelApplication; private Excel.Workbook workbook1; private Excel.Worksheet titleBlockSheet; // Title Block용 시트 private Excel.Worksheet textEntitiesSheet; // Text Entities용 시트 // 각 시트의 현재 행 번호 private int titleBlockCurrentRow = 2; // 헤더가 1행이므로 데이터는 2행부터 시작 private int textEntitiesCurrentRow = 2; // 헤더가 1행이므로 데이터는 2행부터 시작 // 생성자: ODA 및 Excel 초기화 public ExportExcel() { ActivateAndInitializeODA(); InitializeExcel(); } // ODA 제품 활성화 및 초기화 private void ActivateAndInitializeODA() { var userInfo = "c2FtYW4gZW5naW5lZXJpbmc="; var userSignature = "F0kuQTmtVpHtvl/TgaFVGE92/YqGmYR9SLoXckEjnOk8NoAQh7Sg6GQruVC04JqD4C/IipxJYqpqvMfMc2auRMG+cAJCKqKUE2djIMdkUdb+C5IVx2c97fcK5ba3n8DDvB54Upokajl+6j12yD8h8MKGOR3Z3zysObeXD62bFpQgp00GCYTqlxEZtTIRjHIPAfJfix8Y0jtXWWYyVJ3LYOu86as5xtx+hY1aakpYIJiQk/6pGd84qSn/9K1w8nxR7UrFzieDeQ/xM58BHSD4u/ZxVJwvv6Uy10tsdBFBTvfJMAFp05Y7yeyeCNr100tA3iOfmWoXAVRHfxnkPfiYR54aK04QI+R6OGkI+yd1oR5BtmN6BdDt3z8KYK5EpFGJGiJIGoUy5PvkYdJ2VV6xe9JWBiIJuI/tDn1Y+uyTQFA9qaDHnOURriXsRGfy8reDPf1eejybSJxWKkpilG6RXhq3xHlCkjZzh1Q45S+xYXNGatcWMm9nkn20M8Ke5JEVaI9w/p2GE36CHRtRQbt8kfPmsbWNXJCFr4svHW2MPbCKWoyn5XEyMWBnuAKi74zvczB13DKjf29SqSIgF5k/hwy2QrgvnaKzY1k8bw8w2/k0vJXcS3GKOB/ZYDle1tf/lkAD1HtnF9zE18TiXhVnqwAVjwg4ui1RPLn/LMs6b5Y="; Services.odActivate(userInfo, userSignature); appServices = new Services(); } // Excel 애플리케이션 및 워크시트 초기화 private void InitializeExcel() { try { var excelApp = new Excel.Application(); excelApplication = excelApp; excelApplication.Visible = false; // WPF에서는 숨김 처리 Excel.Workbook workbook = excelApp.Workbooks.Add(); workbook1 = workbook; // Title Block Sheet 설정 (기본 Sheet1) titleBlockSheet = (Excel.Worksheet)workbook.Sheets[1]; titleBlockSheet.Name = "Title Block"; SetupTitleBlockHeaders(); // Text Entities Sheet 추가 textEntitiesSheet = (Excel.Worksheet)workbook.Sheets.Add(); textEntitiesSheet.Name = "Text Entities"; SetupTextEntitiesHeaders(); } catch (System.Exception ex) { Console.WriteLine($"Excel 초기화 중 오류 발생: {ex.Message}"); ReleaseExcelObjects(); throw; } } // Title Block 시트 헤더 설정 private void SetupTitleBlockHeaders() { titleBlockSheet.Cells[1, 1] = "Type"; // 예: AttributeReference, AttributeDefinition titleBlockSheet.Cells[1, 2] = "Name"; // BlockReference 이름 또는 BlockDefinition 이름 titleBlockSheet.Cells[1, 3] = "Tag"; // Attribute Tag titleBlockSheet.Cells[1, 4] = "Prompt"; // Attribute Prompt titleBlockSheet.Cells[1, 5] = "Value"; // Attribute 값 (TextString) titleBlockSheet.Cells[1, 6] = "Path"; // 원본 DWG 파일 전체 경로 titleBlockSheet.Cells[1, 7] = "FileName"; // 원본 DWG 파일 이름만 // 헤더 행 스타일 Excel.Range headerRange = titleBlockSheet.Range["A1:G1"]; headerRange.Font.Bold = true; headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightBlue); } // Text Entities 시트 헤더 설정 private void SetupTextEntitiesHeaders() { textEntitiesSheet.Cells[1, 1] = "Type"; // DBText, MText textEntitiesSheet.Cells[1, 2] = "Layer"; // Layer 이름 textEntitiesSheet.Cells[1, 3] = "Text"; // 실제 텍스트 내용 textEntitiesSheet.Cells[1, 4] = "Path"; // 원본 DWG 파일 전체 경로 textEntitiesSheet.Cells[1, 5] = "FileName"; // 원본 DWG 파일 이름만 // 헤더 행 스타일 Excel.Range headerRange = textEntitiesSheet.Range["A1:E1"]; headerRange.Font.Bold = true; headerRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.LightGreen); } /// /// 단일 DWG 파일에서 AttributeReference/AttributeDefinition 데이터를 추출하여 /// 초기화된 Excel 워크시트에 추가합니다. /// /// 처리할 DWG 파일 경로 /// 진행 상태 보고를 위한 IProgress 객체 /// 작업 취소를 위한 CancellationToken /// 성공 시 true, 실패 시 false 반환 public bool ExportDwgToExcel(string filePath, IProgress progress = null, CancellationToken cancellationToken = default) { if (excelApplication == null) { Console.WriteLine("Excel이 초기화되지 않았습니다."); return false; } try { progress?.Report(0); cancellationToken.ThrowIfCancellationRequested(); // ODA Database 객체 생성 및 DWG 파일 읽기 using (var database = new Database(false, true)) { database.ReadDwgFile(filePath, FileOpenMode.OpenForReadAndWriteNoShare, false, null); cancellationToken.ThrowIfCancellationRequested(); progress?.Report(10); using (var tran = database.TransactionManager.StartTransaction()) { var bt = tran.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable; using (var btr = tran.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord) { int totalEntities = btr.Cast().Count(); int processedCount = 0; foreach (ObjectId entId in btr) { cancellationToken.ThrowIfCancellationRequested(); using (var ent = tran.GetObject(entId, OpenMode.ForRead) as Entity) { // Layer 이름 가져오기 (공통) string layerName = GetLayerName(ent.LayerId, tran, database); // AttributeDefinition 추출 if (ent is AttributeDefinition attDef) { titleBlockSheet.Cells[titleBlockCurrentRow, 1] = attDef.GetType().Name; titleBlockSheet.Cells[titleBlockCurrentRow, 2] = attDef.BlockName; titleBlockSheet.Cells[titleBlockCurrentRow, 3] = attDef.Tag; titleBlockSheet.Cells[titleBlockCurrentRow, 4] = attDef.Prompt; titleBlockSheet.Cells[titleBlockCurrentRow, 5] = attDef.TextString; titleBlockSheet.Cells[titleBlockCurrentRow, 6] = database.Filename; titleBlockSheet.Cells[titleBlockCurrentRow, 7] = Path.GetFileName(database.Filename); titleBlockCurrentRow++; } // BlockReference 및 그 안의 AttributeReference 추출 else if (ent is BlockReference blr) { foreach (ObjectId attId in blr.AttributeCollection) { cancellationToken.ThrowIfCancellationRequested(); using (var attRef = tran.GetObject(attId, OpenMode.ForRead) as AttributeReference) { if (attRef != null && attRef.TextString.Trim() !="") { titleBlockSheet.Cells[titleBlockCurrentRow, 1] = attRef.GetType().Name; titleBlockSheet.Cells[titleBlockCurrentRow, 2] = blr.Name; titleBlockSheet.Cells[titleBlockCurrentRow, 3] = attRef.Tag; titleBlockSheet.Cells[titleBlockCurrentRow, 4] = GetPromptFromAttributeReference(tran, blr, attRef.Tag); titleBlockSheet.Cells[titleBlockCurrentRow, 5] = attRef.TextString; titleBlockSheet.Cells[titleBlockCurrentRow, 6] = database.Filename; titleBlockSheet.Cells[titleBlockCurrentRow, 7] = Path.GetFileName(database.Filename); titleBlockCurrentRow++; } } } } // DBText 엔티티 추출 (별도 시트) else if (ent is DBText dbText) { textEntitiesSheet.Cells[textEntitiesCurrentRow, 1] = "DBText"; // Type textEntitiesSheet.Cells[textEntitiesCurrentRow, 2] = layerName; // Layer textEntitiesSheet.Cells[textEntitiesCurrentRow, 3] = dbText.TextString; // Text textEntitiesSheet.Cells[textEntitiesCurrentRow, 4] = database.Filename; // Path textEntitiesSheet.Cells[textEntitiesCurrentRow, 5] = Path.GetFileName(database.Filename); // FileName textEntitiesCurrentRow++; } // MText 엔티티 추출 (별도 시트) else if (ent is MText mText) { textEntitiesSheet.Cells[textEntitiesCurrentRow, 1] = "MText"; // Type textEntitiesSheet.Cells[textEntitiesCurrentRow, 2] = layerName; // Layer textEntitiesSheet.Cells[textEntitiesCurrentRow, 3] = mText.Contents; // Text textEntitiesSheet.Cells[textEntitiesCurrentRow, 4] = database.Filename; // Path textEntitiesSheet.Cells[textEntitiesCurrentRow, 5] = Path.GetFileName(database.Filename); // FileName textEntitiesCurrentRow++; } } processedCount++; double currentProgress = 10.0 + (double)processedCount / totalEntities * 80.0; progress?.Report(Math.Min(currentProgress, 90.0)); } } tran.Commit(); } } progress?.Report(100); return true; } catch (OperationCanceledException) { progress?.Report(0); return false; } catch (Teigha.Runtime.Exception ex) { progress?.Report(0); Console.WriteLine($"DWG 파일 처리 중 오류 발생: {ex.Message}"); return false; } catch (System.Exception ex) { progress?.Report(0); Console.WriteLine($"일반 오류 발생: {ex.Message}"); return false; } } // Paste the helper function from above here public string GetPromptFromAttributeReference(Transaction tr, BlockReference blockref, string tag) { string prompt = null; BlockTableRecord blockDef = tr.GetObject(blockref.BlockTableRecord, OpenMode.ForRead) as BlockTableRecord; if (blockDef == null) return null; foreach (ObjectId objId in blockDef) { AttributeDefinition attDef = tr.GetObject(objId, OpenMode.ForRead) as AttributeDefinition; if (attDef != null) { if (attDef.Tag.Equals(tag, System.StringComparison.OrdinalIgnoreCase)) { prompt = attDef.Prompt; break; } } } return prompt; } /// /// 현재 Excel 워크북을 지정된 경로에 저장하고 Excel 애플리케이션을 종료합니다. /// /// Excel 파일을 저장할 전체 경로 public void SaveAndCloseExcel(string savePath) { if (workbook1 == null) return; try { string directory = Path.GetDirectoryName(savePath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } workbook1.SaveAs(savePath, AccessMode: Excel.XlSaveAsAccessMode.xlNoChange); } catch (System.Exception ex) { Console.WriteLine($"Excel 파일 저장 중 오류 발생: {ex.Message}"); } finally { CloseExcelObjects(); } } private void CloseExcelObjects() { if (workbook1 != null) { try { workbook1.Close(false); } catch { } } if (excelApplication != null) { try { excelApplication.Quit(); } catch { } } ReleaseExcelObjects(); } private void ReleaseExcelObjects() { ReleaseComObject(titleBlockSheet); ReleaseComObject(textEntitiesSheet); ReleaseComObject(workbook1); ReleaseComObject(excelApplication); titleBlockSheet = null; textEntitiesSheet = null; workbook1 = null; excelApplication = null; } private void ReleaseComObject(object obj) { try { if (obj != null && Marshal.IsComObject(obj)) { Marshal.ReleaseComObject(obj); } } catch (System.Exception) { // 해제 중 오류 발생 시 무시 } finally { obj = null; } } /// /// Layer ID로부터 Layer 이름을 가져옵니다. /// /// Layer ObjectId /// 현재 트랜잭션 /// 데이터베이스 객체 /// Layer 이름 또는 빈 문자열 private string GetLayerName(ObjectId layerId, Transaction transaction, Database database) { try { using (var layerTableRecord = transaction.GetObject(layerId, OpenMode.ForRead) as LayerTableRecord) { return layerTableRecord?.Name ?? ""; } } catch (System.Exception ex) { Console.WriteLine($"Layer 이름 가져오기 오류: {ex.Message}"); return ""; } } public void Dispose() { if (excelApplication != null) { CloseExcelObjects(); } if (appServices != null) { appServices.Dispose(); appServices = null; } } } }