Files
manual_wpf/Models/ExportExcel.cs
2025-07-16 17:40:50 +09:00

388 lines
18 KiB
C#

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
{
/// <summary>
/// DWG 파일에서 Excel로 데이터 내보내기 클래스
/// AttributeReference, AttributeDefinition, DBText, MText 추출 지원
/// </summary>
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);
}
/// <summary>
/// 단일 DWG 파일에서 AttributeReference/AttributeDefinition 데이터를 추출하여
/// 초기화된 Excel 워크시트에 추가합니다.
/// </summary>
/// <param name="filePath">처리할 DWG 파일 경로</param>
/// <param name="progress">진행 상태 보고를 위한 IProgress 객체</param>
/// <param name="cancellationToken">작업 취소를 위한 CancellationToken</param>
/// <returns>성공 시 true, 실패 시 false 반환</returns>
public bool ExportDwgToExcel(string filePath, IProgress<double> 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<ObjectId>().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;
}
/// <summary>
/// 현재 Excel 워크북을 지정된 경로에 저장하고 Excel 애플리케이션을 종료합니다.
/// </summary>
/// <param name="savePath">Excel 파일을 저장할 전체 경로</param>
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;
}
}
/// <summary>
/// Layer ID로부터 Layer 이름을 가져옵니다.
/// </summary>
/// <param name="layerId">Layer ObjectId</param>
/// <param name="transaction">현재 트랜잭션</param>
/// <param name="database">데이터베이스 객체</param>
/// <returns>Layer 이름 또는 빈 문자열</returns>
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;
}
}
}
}