diff --git a/.gitignore b/.gitignore
index acf7cba..f3ffb81 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@ x86/
[Oo]bj/
Binaries/
Standalone/
+publish/
# Visual Studio
.vs/
@@ -43,3 +44,6 @@ Thumbs.db
ehthumbs.db
Desktop.ini
+# VS project upgrade/migration reports (auto-generated)
+UpgradeLog*.htm
+
diff --git a/DrawingListUC/AcadComUtils.cs b/DrawingListUC/AcadComUtils.cs
index 848ec5c..aa3042d 100644
--- a/DrawingListUC/AcadComUtils.cs
+++ b/DrawingListUC/AcadComUtils.cs
@@ -16,7 +16,7 @@
namespace DrawingListUC
{
- internal static class AcadComUtils
+ public static class AcadComUtils
{
// -------------------- PUBLIC API --------------------
diff --git a/DwgExportManager/AcadSession.cs b/DwgExportManager/AcadSession.cs
new file mode 100644
index 0000000..ac69db2
--- /dev/null
+++ b/DwgExportManager/AcadSession.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Threading;
+using DrawingListUC;
+
+namespace DwgExportManager
+{
+ // Dieu khien AutoCAD qua COM (attach vao instance dang chay hoac tao moi),
+ // mo/kich hoat ban ve va chuyen tab Model/Layout.
+ // Tai su dung AcadComUtils - nghiep vu ket noi AutoCAD da co san trong ScriptPro (DrawingListUC).
+ public class AcadSession
+ {
+ // HRESULT AutoCAD/COM tra ve khi ung dung dang ban (dang hien dialog, dang ve lai man hinh...)
+ // ScriptUI/DrawingListUC khong gap loi nay vi no chay tren luong UI (STA) co dang ky
+ // IOleMessageFilter (xem MessageFilter.cs) de COM tu dong retry. DwgExportManager goi COM
+ // ca tu luong nen (BackgroundWorker, MTA) - IOleMessageFilter KHONG co tac dung voi MTA,
+ // nen phai tu bat loi va retry thu cong o day.
+ private const int RPC_E_CALL_REJECTED = unchecked((int)0x80010001);
+ private const int RPC_E_SERVERCALL_RETRYLATER = unchecked((int)0x8001010A);
+ private const int MaxRetries = 60; // toi da ~15s (60 x 250ms)
+ private const int RetryDelayMs = 250;
+
+ private object _acadApp;
+
+ // Goi InvokeMember co tu dong retry khi AutoCAD bao "busy"
+ private static object Invoke(object target, string name, BindingFlags flags, object[] args)
+ {
+ int attempt = 0;
+ while (true)
+ {
+ try
+ {
+ return target.GetType().InvokeMember(name, flags, null, target, args);
+ }
+ catch (COMException ex) when (
+ (ex.HResult == RPC_E_CALL_REJECTED || ex.HResult == RPC_E_SERVERCALL_RETRYLATER) &&
+ attempt < MaxRetries)
+ {
+ attempt++;
+ Thread.Sleep(RetryDelayMs);
+ }
+ }
+ }
+
+ public object EnsureAcadRunning()
+ {
+ if (_acadApp != null)
+ {
+ try
+ {
+ // Kiem tra COM object con song khong (AutoCAD co the da bi dong tay)
+ Invoke(_acadApp, "Visible", BindingFlags.GetProperty, null);
+ return _acadApp;
+ }
+ catch
+ {
+ _acadApp = null;
+ }
+ }
+
+ _acadApp = AcadComUtils.TryGetAnyRunningAcad();
+ if (_acadApp == null)
+ _acadApp = AcadComUtils.CreateLatestAutoCADInstance();
+
+ AcadComUtils.SetVisible(_acadApp, true);
+ return _acadApp;
+ }
+
+ // Mo file (hoac kich hoat lai neu da mo san) va tra ve AcadDocument (COM object)
+ public object OpenDocument(string dwgPath)
+ {
+ object acadApp = EnsureAcadRunning();
+
+ object documents = Invoke(acadApp, "Documents", BindingFlags.GetProperty, null);
+
+ int count = (int)Invoke(documents, "Count", BindingFlags.GetProperty, null);
+
+ for (int i = 0; i < count; i++)
+ {
+ object existing = Invoke(documents, "Item", BindingFlags.InvokeMethod, new object[] { i });
+
+ string fullName = (string)Invoke(existing, "FullName", BindingFlags.GetProperty, null);
+
+ if (string.Equals(fullName, dwgPath, StringComparison.OrdinalIgnoreCase))
+ {
+ ActivateDocument(existing);
+ return existing;
+ }
+ }
+
+ object opened = Invoke(documents, "Open", BindingFlags.InvokeMethod,
+ new object[] { dwgPath, false, " " });
+
+ // Cho AutoCAD ve xong ban ve vua mo truoc khi goi tiep lenh COM khac,
+ // giam kha nang gap "application is busy" ngay sau khi Open.
+ Thread.Sleep(800);
+
+ ActivateDocument(opened);
+ return opened;
+ }
+
+ public void ActivateDocument(object document)
+ {
+ try
+ {
+ Invoke(document, "Activate", BindingFlags.InvokeMethod, null);
+ }
+ catch { }
+
+ try
+ {
+ AcadComUtils.SetVisible(_acadApp, true);
+ }
+ catch { }
+ }
+
+ // Chuyen sang tab Model hoac Layout co ten tuong ung
+ public void SetActiveTab(object document, string tabName)
+ {
+ if (string.IsNullOrEmpty(tabName) ||
+ string.Equals(tabName, "Model", StringComparison.OrdinalIgnoreCase))
+ return;
+
+ try
+ {
+ object layouts = Invoke(document, "Layouts", BindingFlags.GetProperty, null);
+
+ object layout = Invoke(layouts, "Item", BindingFlags.InvokeMethod, new object[] { tabName });
+
+ Invoke(document, "ActiveLayout", BindingFlags.SetProperty, new object[] { layout });
+
+ // Cho AutoCAD chuyen tab xong truoc khi goi tiep lenh COM khac
+ Thread.Sleep(300);
+ }
+ catch
+ {
+ // Khong tim thay layout (co the da bi doi ten) - giu nguyen tab hien tai
+ }
+ }
+
+ public void SetVariable(object document, string varName, object value)
+ {
+ try
+ {
+ Invoke(document, "SetVariable", BindingFlags.InvokeMethod, new object[] { varName, value });
+ }
+ catch { }
+ }
+
+ public int GetIntVariable(object document, string varName, int defaultValue)
+ {
+ try
+ {
+ object result = Invoke(document, "GetVariable", BindingFlags.InvokeMethod, new object[] { varName });
+ return Convert.ToInt32(result);
+ }
+ catch
+ {
+ return defaultValue;
+ }
+ }
+
+ public string GetStringVariable(object document, string varName, string defaultValue)
+ {
+ try
+ {
+ object result = Invoke(document, "GetVariable", BindingFlags.InvokeMethod, new object[] { varName });
+ return result?.ToString() ?? defaultValue;
+ }
+ catch
+ {
+ return defaultValue;
+ }
+ }
+
+ public void SendCommand(object document, string commandText)
+ {
+ Invoke(document, "SendCommand", BindingFlags.InvokeMethod, new object[] { commandText });
+ }
+
+ public void CloseDocument(object document)
+ {
+ try
+ {
+ Invoke(document, "Close", BindingFlags.InvokeMethod, new object[] { false, "" });
+ }
+ catch { }
+ }
+ }
+}
diff --git a/DwgExportManager/App.xaml b/DwgExportManager/App.xaml
new file mode 100644
index 0000000..9cfaf80
--- /dev/null
+++ b/DwgExportManager/App.xaml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/DwgExportManager/App.xaml.cs b/DwgExportManager/App.xaml.cs
new file mode 100644
index 0000000..b0e7618
--- /dev/null
+++ b/DwgExportManager/App.xaml.cs
@@ -0,0 +1,8 @@
+using System.Windows;
+
+namespace DwgExportManager
+{
+ public partial class App : Application
+ {
+ }
+}
diff --git a/DwgExportManager/DwgExportManager.csproj b/DwgExportManager/DwgExportManager.csproj
new file mode 100644
index 0000000..b7f6609
--- /dev/null
+++ b/DwgExportManager/DwgExportManager.csproj
@@ -0,0 +1,68 @@
+
+
+
+ net8.0-windows
+ true
+ true
+ WinExe
+ DwgExportManager
+ DwgExportManager
+ latest
+ disable
+ disable
+ AnyCPU;x64
+ PerMonitorV2
+ true
+
+
+
+
+ DwgExportManager
+
+ DwgExportManager
+ Copyright © 2026
+ 1.0.0.0
+ 1.0.0.0
+
+
+
+ DEBUG;TRACE
+ bin\Debug\
+ full
+ true
+
+
+
+ TRACE
+ $(SolutionDir)Binaries\
+ pdbonly
+ true
+
+
+
+ DEBUG;TRACE
+ $(SolutionDir)Binaries\x64\Debug\
+ full
+ true
+ x64
+
+
+
+ TRACE
+ $(SolutionDir)Binaries\x64\Release\
+ pdbonly
+ true
+ x64
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DwgExportManager/ExportEngine.cs b/DwgExportManager/ExportEngine.cs
new file mode 100644
index 0000000..815ddd5
--- /dev/null
+++ b/DwgExportManager/ExportEngine.cs
@@ -0,0 +1,222 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+
+namespace DwgExportManager
+{
+ public enum ExportFormat
+ {
+ PdfOnly,
+ PngOnly,
+ PdfAndPng
+ }
+
+ // Nghiep vu xuat hang loat: voi moi file, mo trong AutoCAD, chuyen den tab
+ // (Model/Layout) da chon tren luoi, plot ra PDF va/hoac PNG, roi dong lai
+ // khong luu. Tham so plot (kho A1 / Extents+Fit+Center) giu nguyen theo 2 script
+ // PlotAllLayoutsToPDF_A1.scr va PlotAllLayoutsToPNG.scr da tao truoc do, chi khac
+ // la chi xuat DUY NHAT tab nguoi dung chon cho tung file. Rieng kho anh PNG co the
+ // chon khac nhau cho tung dong (pngPaperSize), mac dinh DefaultPngPaperSize neu bo trong.
+ // File xuat ra dat CUNG TEN voi file DWG goc, luu trong thu muc con "danxuat"
+ // nam ngay trong thu muc goc (thu muc chua cac file DWG).
+ public class ExportEngine
+ {
+ // Ten thu muc con (nam ngay trong thu muc goc chua cac file DWG) de chua ket qua xuat
+ private const string OutputFolderName = "danxuat";
+
+ private const string PdfDevice = "DWG To PDF.pc3";
+ private const string PdfPaperSize = "ISO A1 (594.00 x 841.00 MM)";
+
+ private const string PngDevice = "PublishToWeb PNG.pc3";
+ private const string DefaultPngPaperSize = "1600 x 1280 Pixels";
+
+ private readonly AcadSession _session;
+
+ public ExportEngine(AcadSession session)
+ {
+ _session = session;
+ }
+
+ // Tra ve true neu xuat thanh cong, false neu co loi (se ghi vao out error)
+ public bool ExportFile(
+ string dwgPath, string tabName, ExportFormat format,
+ Func isStopRequested, ManualResetEventSlim pauseEvent,
+ out string error, string pngPaperSize = null)
+ {
+ error = null;
+ object document = null;
+ try
+ {
+ pauseEvent.Wait();
+ if (isStopRequested()) return false;
+
+ document = _session.OpenDocument(dwgPath);
+ _session.SetActiveTab(document, tabName);
+
+ _session.SetVariable(document, "FILEDIA", 0);
+ _session.SetVariable(document, "BACKGROUNDPLOT", 0);
+
+ // Bat log lenh cua AutoCAD de doc lai noi dung khi -PLOT that bai
+ // (vd sai ten kho giay/thiet bi) - SendCommand khong nem loi .NET
+ // khi AutoCAD tu choi lenh, nen phai doc log moi biet ly do that.
+ _session.SetVariable(document, "LOGFILEMODE", 1);
+ string logFile = _session.GetStringVariable(document, "LOGFILENAME", null);
+
+ string rootFolder = Path.GetDirectoryName(dwgPath);
+ string outputFolder = Path.Combine(rootFolder, OutputFolderName);
+ Directory.CreateDirectory(outputFolder);
+
+ string baseName = Path.GetFileNameWithoutExtension(dwgPath);
+ bool isModel = string.Equals(tabName, "Model", StringComparison.OrdinalIgnoreCase);
+ string layoutArg = isModel ? "Model" : tabName;
+
+ var failures = new List();
+
+ if (format == ExportFormat.PdfOnly || format == ExportFormat.PdfAndPng)
+ {
+ pauseEvent.Wait();
+ if (isStopRequested()) return false;
+
+ string pdfPath = Path.Combine(outputFolder, baseName + ".pdf");
+ long logPos = GetLogLength(logFile);
+ Plot(document, layoutArg, PdfDevice, PdfPaperSize, pdfPath);
+ WaitForIdle(document, 120_000);
+
+ if (!File.Exists(pdfPath))
+ failures.Add(BuildPlotFailureMessage("PDF", PdfDevice, PdfPaperSize, logFile, logPos));
+ }
+
+ if (format == ExportFormat.PngOnly || format == ExportFormat.PdfAndPng)
+ {
+ pauseEvent.Wait();
+ if (isStopRequested()) return false;
+
+ string effectivePngPaperSize = string.IsNullOrWhiteSpace(pngPaperSize)
+ ? DefaultPngPaperSize : pngPaperSize;
+
+ string pngPath = Path.Combine(outputFolder, baseName + ".png");
+ long logPos = GetLogLength(logFile);
+ Plot(document, layoutArg, PngDevice, effectivePngPaperSize, pngPath);
+ WaitForIdle(document, 120_000);
+
+ if (!File.Exists(pngPath))
+ failures.Add(BuildPlotFailureMessage("PNG", PngDevice, effectivePngPaperSize, logFile, logPos));
+ }
+
+ _session.SetVariable(document, "FILEDIA", 1);
+
+ if (failures.Count > 0)
+ {
+ error = string.Join(" | ", failures);
+ return false;
+ }
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ error = ex.Message;
+ return false;
+ }
+ finally
+ {
+ if (document != null)
+ _session.CloseDocument(document);
+ }
+ }
+
+ private static long GetLogLength(string logFile)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(logFile) || !File.Exists(logFile))
+ return 0;
+ return new FileInfo(logFile).Length;
+ }
+ catch
+ {
+ return 0;
+ }
+ }
+
+ // Doc phan noi dung MOI duoc ghi vao log lenh cua AutoCAD (tinh tu logPos)
+ // de biet AutoCAD tra loi gi cho lenh -PLOT vua gui (vd bao khong tim
+ // thay kho giay/thiet bi).
+ private static string ReadLogTail(string logFile, long logPos, int maxChars = 600)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(logFile) || !File.Exists(logFile))
+ return null;
+
+ using (var fs = new FileStream(logFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+ {
+ if (logPos > fs.Length) logPos = 0;
+ fs.Seek(logPos, SeekOrigin.Begin);
+ using (var sr = new StreamReader(fs))
+ {
+ string text = sr.ReadToEnd().Trim();
+ if (text.Length > maxChars)
+ text = text.Substring(text.Length - maxChars);
+ return text;
+ }
+ }
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private static string BuildPlotFailureMessage(
+ string formatName, string device, string paperSize, string logFile, long logPos)
+ {
+ string msg = $"Không tạo được {formatName} (kiểm tra thiết bị '{device}' / khổ giấy '{paperSize}' có tồn tại trên máy này không)";
+ string tail = ReadLogTail(logFile, logPos);
+ if (!string.IsNullOrEmpty(tail))
+ msg += $" — AutoCAD trả lời: {tail}";
+ return msg;
+ }
+
+ private void Plot(object document, string layoutArg, string device, string paperSize, string outFile)
+ {
+ string cmd =
+ "_.-PLOT\n" +
+ "Yes\n" + // Detailed plot configuration? Yes
+ layoutArg + "\n" + // Layout can plot
+ device + "\n" + // Output device
+ paperSize + "\n" + // Paper size
+ "Millimeters\n" + // Paper units
+ "Landscape\n" + // Orientation
+ "No\n" + // Plot upside down?
+ "Extents\n" + // Plot area
+ "Fit\n" + // Plot scale
+ "Center\n" + // Plot offset
+ "Yes\n" + // Plot with plot styles
+ ".\n" + // Giu nguyen bang plot style hien tai
+ "Yes\n" + // Plot with lineweights
+ "\n" + // Shade plot setting mac dinh
+ outFile + "\n" + // Ten file xuat ra
+ "No\n" + // Save changes to page setup? No
+ "Yes\n"; // Proceed with plot
+
+ _session.SendCommand(document, cmd);
+ Thread.Sleep(1000);
+ }
+
+ // Cho AutoCAD xu ly xong lenh plot (CMDACTIVE == 0) truoc khi lam tiep,
+ // toi da timeoutMs, tranh dong file khi PDF/PNG chua ghi xong.
+ private void WaitForIdle(object document, int timeoutMs)
+ {
+ DateTime end = DateTime.UtcNow.AddMilliseconds(timeoutMs);
+ while (DateTime.UtcNow < end)
+ {
+ int cmdActive = _session.GetIntVariable(document, "CMDACTIVE", 0);
+ if (cmdActive == 0)
+ return;
+ Thread.Sleep(300);
+ }
+ }
+ }
+}
diff --git a/DwgExportManager/LayoutReader.cs b/DwgExportManager/LayoutReader.cs
new file mode 100644
index 0000000..f72ea49
--- /dev/null
+++ b/DwgExportManager/LayoutReader.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using ACadSharp;
+using ACadSharp.IO;
+
+namespace DwgExportManager
+{
+ // Doc nhanh danh sach Layout (khong tinh Model) tu file DWG/DXF bang ACadSharp,
+ // KHONG can mo AutoCAD - dung de do vao ComboBox "Model/Layout" tren luoi.
+ public static class LayoutReader
+ {
+ public static List ReadLayoutNames(string filePath)
+ {
+ var names = new List();
+ try
+ {
+ string ext = Path.GetExtension(filePath).ToLowerInvariant();
+
+ CadDocument doc = ext == ".dxf"
+ ? DxfReader.Read(filePath)
+ : DwgReader.Read(filePath);
+
+ if (doc?.Layouts != null)
+ {
+ foreach (var layout in doc.Layouts)
+ {
+ string name = layout?.Name;
+ if (!string.IsNullOrEmpty(name) &&
+ !string.Equals(name, "Model", StringComparison.OrdinalIgnoreCase))
+ {
+ names.Add(name);
+ }
+ }
+ }
+ }
+ catch
+ {
+ // File loi, ma hoa, hoac phien ban chua ho tro: bo qua,
+ // luoi se chi con lua chon "Model".
+ }
+ return names;
+ }
+ }
+}
diff --git a/DwgExportManager/MainWindow.xaml b/DwgExportManager/MainWindow.xaml
new file mode 100644
index 0000000..b11004a
--- /dev/null
+++ b/DwgExportManager/MainWindow.xaml
@@ -0,0 +1,133 @@
+
+
+
+
+ 800 x 600 Pixels
+ 1024 x 768 Pixels
+ 1600 x 1280 Pixels
+ 2048 x 1536 Pixels
+ 3200 x 2400 Pixels
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DwgExportManager/MainWindow.xaml.cs b/DwgExportManager/MainWindow.xaml.cs
new file mode 100644
index 0000000..58ac675
--- /dev/null
+++ b/DwgExportManager/MainWindow.xaml.cs
@@ -0,0 +1,319 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using DwgExportManager.Models;
+using WinForms = System.Windows.Forms;
+
+namespace DwgExportManager
+{
+ public partial class MainWindow : Window
+ {
+ private readonly ObservableCollection _files = new ObservableCollection();
+ private readonly AcadSession _acadSession = new AcadSession();
+
+ private BackgroundWorker _exportWorker;
+ private readonly ManualResetEventSlim _pauseEvent = new ManualResetEventSlim(true);
+ private volatile bool _stopRequested;
+ private volatile bool _isRunning;
+
+ public MainWindow()
+ {
+ InitializeComponent();
+ DwgGrid.ItemsSource = _files;
+
+ // Cho phep COM tu dong retry khi AutoCAD bao "busy" thay vi nem loi
+ // ("The message filter indicated that the application is busy").
+ MessageFilter.Register();
+ Closed += (s, e) => MessageFilter.Revoke();
+ }
+
+ // ===== Dong 1: chon thu muc chua ban ve =====
+
+ private void BrowseButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (_isRunning) return;
+
+ using (var dlg = new WinForms.FolderBrowserDialog())
+ {
+ if (!string.IsNullOrEmpty(FolderPathTextBox.Text) && Directory.Exists(FolderPathTextBox.Text))
+ dlg.SelectedPath = FolderPathTextBox.Text;
+
+ if (dlg.ShowDialog() == WinForms.DialogResult.OK)
+ {
+ FolderPathTextBox.Text = dlg.SelectedPath;
+ LoadFolder(dlg.SelectedPath);
+ }
+ }
+ }
+
+ private void LoadFolder(string folder)
+ {
+ _files.Clear();
+
+ string[] dwgFiles;
+ try
+ {
+ dwgFiles = Directory.GetFiles(folder, "*.dwg")
+ .Concat(Directory.GetFiles(folder, "*.dxf"))
+ .OrderBy(f => Path.GetFileName(f), StringComparer.OrdinalIgnoreCase)
+ .ToArray();
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Không thể đọc thư mục:\n" + ex.Message, "DWG Export Manager",
+ MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+
+ if (dwgFiles.Length == 0)
+ {
+ StatusText.Text = "Không tìm thấy file .dwg/.dxf trong thư mục này.";
+ return;
+ }
+
+ StatusText.Text = "Đang đọc danh sách layout...";
+
+ // Doc Layout tung file (ACadSharp) tren luong nen de khong dong UI
+ Task.Run(() =>
+ {
+ foreach (string file in dwgFiles)
+ {
+ var item = new DwgFileItem
+ {
+ FileName = Path.GetFileName(file),
+ FullPath = file
+ };
+ item.AvailableTabs.Add("Model");
+ foreach (string layoutName in LayoutReader.ReadLayoutNames(file))
+ item.AvailableTabs.Add(layoutName);
+ item.SelectedTab = "Model";
+
+ var captured = item;
+ Dispatcher.Invoke(() => _files.Add(captured));
+ }
+
+ Dispatcher.Invoke(() => StatusText.Text = $"Đã tải {dwgFiles.Length} file.");
+ });
+ }
+
+ // ===== Dong 2: nut "Xem" tren tung dong luoi =====
+
+ private void ViewButton_Click(object sender, RoutedEventArgs e)
+ {
+ var button = sender as Button;
+ var item = button?.Tag as DwgFileItem;
+ if (item == null) return;
+
+ if (_isRunning)
+ {
+ MessageBox.Show("Đang trong quá trình xuất file, vui lòng chờ hoặc bấm Tạm dừng.",
+ "DWG Export Manager");
+ return;
+ }
+
+ button.IsEnabled = false;
+ StatusText.Text = "Đang mở " + item.FileName + " ...";
+ try
+ {
+ object document = _acadSession.OpenDocument(item.FullPath);
+ _acadSession.SetActiveTab(document, item.SelectedTab);
+ StatusText.Text = $"Đã mở {item.FileName} ({item.SelectedTab}) trong AutoCAD.";
+ }
+ catch (Exception ex)
+ {
+ StatusText.Text = "";
+ MessageBox.Show("Không thể mở AutoCAD để xem file:\n" + ex.Message,
+ "DWG Export Manager", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ finally
+ {
+ button.IsEnabled = true;
+ }
+ }
+
+ // ===== Dong 2: nut "Xóa" tren tung dong luoi =====
+
+ private void DeleteRowButton_Click(object sender, RoutedEventArgs e)
+ {
+ var button = sender as Button;
+ var item = button?.Tag as DwgFileItem;
+ if (item == null) return;
+
+ if (_isRunning)
+ {
+ MessageBox.Show("Đang trong quá trình xuất file, vui lòng chờ hoặc bấm Tạm dừng trước khi xóa.",
+ "DWG Export Manager");
+ return;
+ }
+
+ _files.Remove(item);
+ }
+
+ // ===== Thanh cong cu tren luoi: xoa nhieu ban ghi cung luc =====
+
+ private void DwgGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ int count = DwgGrid.SelectedItems.Count;
+ DeleteSelectedButton.IsEnabled = !_isRunning && count > 0;
+ DeleteSelectedButton.Content = count > 0 ? $"Xóa mục đã chọn ({count})" : "Xóa mục đã chọn";
+ }
+
+ private void DeleteSelectedButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (_isRunning)
+ {
+ MessageBox.Show("Đang trong quá trình xuất file, vui lòng chờ hoặc bấm Tạm dừng trước khi xóa.",
+ "DWG Export Manager");
+ return;
+ }
+
+ List selected = DwgGrid.SelectedItems.Cast().ToList();
+ if (selected.Count == 0) return;
+
+ foreach (DwgFileItem item in selected)
+ _files.Remove(item);
+
+ DeleteSelectedButton.Content = "Xóa mục đã chọn";
+ DeleteSelectedButton.IsEnabled = false;
+ }
+
+ // ===== Dong 2: nut "Xuất" tren tung dong luoi (xuat rieng 1 file) =====
+
+ private void ExportRowButton_Click(object sender, RoutedEventArgs e)
+ {
+ var button = sender as Button;
+ var item = button?.Tag as DwgFileItem;
+ if (item == null) return;
+
+ if (_isRunning)
+ {
+ MessageBox.Show("Đang trong quá trình xuất file khác, vui lòng chờ hoặc bấm Tạm dừng.",
+ "DWG Export Manager");
+ return;
+ }
+
+ StartExport(new List { item });
+ }
+
+ // ===== Dong 3: Xuat / Tam dung =====
+
+ private void ExportButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (_isRunning)
+ {
+ // Dang chay -> nut nay dong vai tro "Dung"
+ _stopRequested = true;
+ _pauseEvent.Set(); // nha tam dung (neu co) de vong lap thoat ngay
+ ExportButton.IsEnabled = false;
+ StatusText.Text = "Đang dừng...";
+ return;
+ }
+
+ if (_files.Count == 0)
+ {
+ MessageBox.Show("Chưa có file nào trong danh sách. Hãy chọn thư mục chứa bản vẽ trước.",
+ "DWG Export Manager");
+ return;
+ }
+
+ StartExport(_files.ToList());
+ }
+
+ // Dung chung cho ca xuat hang loat (nut Xuat o dong 3) va xuat rieng 1 file (nut Xuat tren luoi)
+ private void StartExport(List items)
+ {
+ if (items.Count == 0) return;
+
+ ExportFormat format =
+ FormatPdfRadio.IsChecked == true ? ExportFormat.PdfOnly :
+ FormatPngRadio.IsChecked == true ? ExportFormat.PngOnly :
+ ExportFormat.PdfAndPng;
+
+ _stopRequested = false;
+ _pauseEvent.Set();
+
+ _isRunning = true;
+ ExportButton.Content = "Dừng";
+ PauseButton.IsEnabled = true;
+ PauseButton.Content = "Tạm dừng";
+ BrowseButton.IsEnabled = false;
+ DeleteSelectedButton.IsEnabled = false;
+ ExportProgressBar.Value = 0;
+
+ _exportWorker = new BackgroundWorker { WorkerReportsProgress = true };
+ _exportWorker.DoWork += (s, args) => RunExport(items, format);
+ _exportWorker.ProgressChanged += (s, args) =>
+ {
+ ExportProgressBar.Value = args.ProgressPercentage;
+ StatusText.Text = args.UserState as string;
+ };
+ _exportWorker.RunWorkerCompleted += (s, args) =>
+ {
+ _isRunning = false;
+ ExportButton.Content = "Xuất";
+ ExportButton.IsEnabled = true;
+ PauseButton.IsEnabled = false;
+ PauseButton.Content = "Tạm dừng";
+ BrowseButton.IsEnabled = true;
+ DeleteSelectedButton.IsEnabled = DwgGrid.SelectedItems.Count > 0;
+ StatusText.Text = _stopRequested ? "Đã dừng." : "Hoàn tất xuất file.";
+ };
+ _exportWorker.RunWorkerAsync();
+ }
+
+ private void RunExport(List items, ExportFormat format)
+ {
+ var engine = new ExportEngine(_acadSession);
+ int total = items.Count;
+
+ for (int i = 0; i < total; i++)
+ {
+ if (_stopRequested) break;
+
+ DwgFileItem item = items[i];
+ Dispatcher.Invoke(() => item.Status = "Đang xuất...");
+ _exportWorker.ReportProgress(
+ (int)(i * 100.0 / total),
+ $"Đang xuất: {item.FileName} ({item.SelectedTab})...");
+
+ bool ok = engine.ExportFile(
+ item.FullPath, item.SelectedTab, format,
+ () => _stopRequested, _pauseEvent, out string error,
+ item.PngPaperSize);
+
+ var capturedItem = item;
+ var capturedOk = ok;
+ var capturedError = error;
+ Dispatcher.Invoke(() => capturedItem.Status = capturedOk ? "OK" : "Lỗi: " + capturedError);
+
+ _exportWorker.ReportProgress(
+ (int)((i + 1) * 100.0 / total),
+ ok ? $"Xong: {item.FileName}" : $"Lỗi: {item.FileName} - {error}");
+ }
+ }
+
+ private void PauseButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (!_isRunning) return;
+
+ if (_pauseEvent.IsSet)
+ {
+ _pauseEvent.Reset();
+ PauseButton.Content = "Tiếp tục";
+ StatusText.Text = "Đã tạm dừng.";
+ }
+ else
+ {
+ _pauseEvent.Set();
+ PauseButton.Content = "Tạm dừng";
+ }
+ }
+ }
+}
diff --git a/DwgExportManager/MessageFilter.cs b/DwgExportManager/MessageFilter.cs
new file mode 100644
index 0000000..7dbd861
--- /dev/null
+++ b/DwgExportManager/MessageFilter.cs
@@ -0,0 +1,82 @@
+namespace DwgExportManager
+{
+ using System;
+ using System.Runtime.InteropServices;
+
+ // Dang ky IOleMessageFilter tren luong UI (STA) de COM tu dong retry khi AutoCAD
+ // bao "busy" (dang hien dialog, dang xu ly lenh khac) thay vi nem loi ngay.
+ // Sao chep dung nghiep vu da hoat dong on dinh trong ScriptUI/MessageFilter.cs.
+ public class MessageFilter : IOleMessageFilter
+ {
+ public static void Register()
+ {
+ IOleMessageFilter newFilter = new MessageFilter();
+ IOleMessageFilter oldFilter = null;
+ int test = CoRegisterMessageFilter(newFilter, out oldFilter);
+
+ if (test != 0)
+ {
+ Console.WriteLine(string.Format("CoRegisterMessageFilter failed with error : {0}", test));
+ }
+ }
+
+ public static void Revoke()
+ {
+ IOleMessageFilter oldFilter = null;
+ CoRegisterMessageFilter(null, out oldFilter);
+ }
+
+ int IOleMessageFilter.HandleInComingCall(int dwCallType,
+ System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr
+ lpInterfaceInfo)
+ {
+ // SERVERCALL_ISHANDLED
+ return 0;
+ }
+
+ int IOleMessageFilter.RetryRejectedCall(System.IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
+ {
+ if (dwRejectType == 2)
+ // SERVERCALL_RETRYLATER
+ {
+ // Retry ngay lap tuc (0-99 = retry)
+ return 99;
+ }
+ // Qua ban, huy cuoc goi
+ return -1;
+ }
+
+ int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
+ {
+ // PENDINGMSG_WAITDEFPROCESS
+ return 2;
+ }
+
+ [DllImport("Ole32.dll")]
+ private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
+ }
+
+ [ComImport(), Guid("00000016-0000-0000-C000-000000000046"),
+ InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
+ interface IOleMessageFilter
+ {
+ [PreserveSig]
+ int HandleInComingCall(
+ int dwCallType,
+ IntPtr hTaskCaller,
+ int dwTickCount,
+ IntPtr lpInterfaceInfo);
+
+ [PreserveSig]
+ int RetryRejectedCall(
+ IntPtr hTaskCallee,
+ int dwTickCount,
+ int dwRejectType);
+
+ [PreserveSig]
+ int MessagePending(
+ IntPtr hTaskCallee,
+ int dwTickCount,
+ int dwPendingType);
+ }
+}
diff --git a/DwgExportManager/Models/DwgFileItem.cs b/DwgExportManager/Models/DwgFileItem.cs
new file mode 100644
index 0000000..6e1f86a
--- /dev/null
+++ b/DwgExportManager/Models/DwgFileItem.cs
@@ -0,0 +1,67 @@
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+
+namespace DwgExportManager.Models
+{
+ // Mot dong trong luoi: 1 file DWG/DXF + tab (Model/Layout) duoc chon de xuat
+ public class DwgFileItem : INotifyPropertyChanged
+ {
+ public string FileName { get; set; }
+
+ public string FullPath { get; set; }
+
+ // Danh sach "Model" + cac Layout doc duoc tu file, de do vao ComboBox
+ public ObservableCollection AvailableTabs { get; } = new ObservableCollection();
+
+ private string _selectedTab = "Model";
+ public string SelectedTab
+ {
+ get => _selectedTab;
+ set
+ {
+ if (_selectedTab != value)
+ {
+ _selectedTab = value;
+ OnPropertyChanged(nameof(SelectedTab));
+ }
+ }
+ }
+
+ // Kho anh (paper size) rieng cho xuat PNG cua dong nay, vd "1600 x 1280 Pixels"
+ // - phai trung ten mot media size da dang ky trong "PublishToWeb PNG.pc3" tren may.
+ private string _pngPaperSize = "1600 x 1280 Pixels";
+ public string PngPaperSize
+ {
+ get => _pngPaperSize;
+ set
+ {
+ if (_pngPaperSize != value)
+ {
+ _pngPaperSize = value;
+ OnPropertyChanged(nameof(PngPaperSize));
+ }
+ }
+ }
+
+ private string _status = "";
+ public string Status
+ {
+ get => _status;
+ set
+ {
+ if (_status != value)
+ {
+ _status = value;
+ OnPropertyChanged(nameof(Status));
+ }
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private void OnPropertyChanged(string propertyName)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
diff --git a/DwgExportManagerSetup/DwgExportManagerSetup.wixproj b/DwgExportManagerSetup/DwgExportManagerSetup.wixproj
new file mode 100644
index 0000000..793a3d2
--- /dev/null
+++ b/DwgExportManagerSetup/DwgExportManagerSetup.wixproj
@@ -0,0 +1,39 @@
+
+
+ bin\$(Configuration)\
+ obj\$(Configuration)\
+ $(MSBuildProjectDirectory)\..\
+
+
+ ExportPdf
+
+
+ $(SolutionDir)DwgExportManager\publish\win-x64\
+
+ x64
+ x64
+
+
+
+ Debug;PublishDir=$(PublishDir)
+
+
+
+ PublishDir=$(PublishDir)
+
+
+
+
diff --git a/DwgExportManagerSetup/Product.wxs b/DwgExportManagerSetup/Product.wxs
new file mode 100644
index 0000000..0e7b799
--- /dev/null
+++ b/DwgExportManagerSetup/Product.wxs
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MoTaNghiepVu.md b/MoTaNghiepVu.md
new file mode 100644
index 0000000..91a4aeb
--- /dev/null
+++ b/MoTaNghiepVu.md
@@ -0,0 +1,70 @@
+# Mô tả nghiệp vụ – ScriptProPlus
+
+## Tổng quan
+
+**ScriptProPlus** là công cụ chạy hàng loạt (batch) file script AutoCAD (`.scr`) trên nhiều bản vẽ `.dwg/.dxf` — dùng để tự động hoá việc **xuất PDF hàng loạt**, in ấn, hoặc chạy bất kỳ lệnh AutoCAD nào trên cả loạt file mà không cần mở tay từng bản vẽ.
+
+Kiến trúc gồm 3 project chính:
+
+- **ScriptUI** (WPF, .NET 8) — cửa sổ chính, ribbon menu (New/Load/Save list, Add DWG, Run…).
+- **DrawingListUC** (WinForms UserControl) — **chứa toàn bộ nghiệp vụ lõi**, được nhúng vào ScriptUI.
+- **ScriptProSetup** — trình cài đặt (WiX/MSI).
+
+## Luồng nghiệp vụ chính
+
+### 1. Tạo/nạp danh sách bản vẽ (Drawing List)
+
+- Người dùng thêm file `.dwg/.dxf` thủ công (`AddDWGFiles`) hoặc quét cả thư mục (`AddDWGFilesFromFolder`, có tuỳ chọn quét đệ quy `searchAllDirectories`).
+- Danh sách được lưu/nạp dưới dạng file dự án `.bpl` (định dạng text riêng, có versioning — hiện tại version 3) hoặc nạp từ file `.scp` cũ của ScriptPro gốc.
+- Mỗi dòng trong `.bpl` lưu: đường dẫn file DWG + trạng thái checked/skip.
+
+### 2. Cấu hình xử lý (`setOptions` → `OptionsDlg`)
+
+- Đường dẫn **script** `.scr` sẽ chạy trên mỗi bản vẽ.
+- Script khởi động (`startUpScript`) chạy 1 lần khi AutoCAD mở lên.
+- **Timeout** cho mỗi bản vẽ, **số bản vẽ xử lý trước khi restart AutoCAD** (`_restartDWGCount`) — để tránh rò rỉ bộ nhớ khi chạy hàng trăm file.
+- Có thể chỉ định **exe AutoCAD cụ thể** (nhiều version cài song song) hoặc dùng `accoreconsole.exe` (chế độ headless/command-line, không cần giao diện).
+- Chế độ chạy trên **bản vẽ trống** (`runWithoutOpen`) — không mở file, chỉ chạy script.
+- Diagnostic mode (dừng lại xác nhận sau mỗi bước) để debug script.
+
+### 3. Chạy batch (`runCheckedFiles` / `runSelectedFiles` / `runFailedFiles`)
+
+- Xây danh sách file cần chạy (`ThreadInput._FileInfolist`), khởi động 1 **BackgroundWorker** riêng để không đơ UI.
+- **Kết nối AutoCAD qua COM (`startAutoCAD`)**, có 2 kịch bản:
+ - Nếu chọn exe cụ thể: kiểm tra đã có tiến trình AutoCAD đó chạy chưa → gắn vào (attach) nếu có, không thì khởi chạy mới và bind qua ProgID theo version (vd `AutoCAD.Application.25.1`).
+ - Nếu không chọn: cố gắng gắn vào bất kỳ AutoCAD đang mở, nếu không có thì tạo instance mới (bản mới nhất đã đăng ký COM).
+- Cờ `_weOwnTheAcadInstance` đánh dấu ScriptPro có "sở hữu" AutoCAD hay không → chỉ AutoCAD do chính nó khởi chạy mới bị **đóng/kill tự động**; nếu người dùng đã mở sẵn AutoCAD thì ScriptPro chỉ dùng nhờ, không tắt.
+
+### 4. Vòng lặp xử lý từng bản vẽ (`batchProcessThread_DoWork`)
+
+- Với mỗi file: mở bản vẽ (`Documents.Open`) → chờ AutoCAD "quiescent" (rảnh) → gửi lệnh `_.SCRIPT <đường dẫn script>` qua `SendCommand` → đóng bản vẽ không lưu (script tự lo việc save/plot).
+- **Từ khoá thay thế trong script** (``, ``, ``, ``, ``) — cho phép script tham chiếu tên/đường dẫn file hiện tại (rất hữu ích để đặt tên PDF xuất ra theo tên bản vẽ).
+- Hỗ trợ **script lồng nhau** (nested script dùng `call`).
+- Có cơ chế **timeout riêng** (`_timeout`, BackgroundWorker phụ) — nếu bản vẽ xử lý quá lâu (AutoCAD treo/crash), tự động coi là fail và tiếp tục file kế.
+- Sau mỗi N bản vẽ (`_restartDWGCount`), **AutoCAD tự khởi động lại** để giải phóng tài nguyên.
+- Kết quả từng file (thành công/thất bại) cập nhật lên UI (đổi màu dòng, ghi trạng thái) và ghi vào **log file** (`ReportLog`).
+
+### 5. Kết thúc / dừng
+
+- Có thể `stopProcess()` để dừng giữa chừng (cờ `_stopBatchProcess`).
+- Khi xong, có thể **chỉ chạy lại các file thất bại** (`runFailedFiles`).
+- `writeDWGList` lưu lại kết quả (bao gồm cả option "chỉ lưu danh sách file failed").
+
+## Ví dụ script nghiệp vụ (xuất PDF) — `TestFiles/PlotToPDF.scr`
+
+```lisp
+(setq fileName (substr (getvar 'dwgname) 1 (- (strlen (getvar 'dwgname)) 4)))
+(setq fileName (strcat (getvar "dwgprefix") filename ".pdf"))
+filedia
+0
+(command "-PLOT" "YES" "MODEL" "Dwg To PDF.pc3" "ANSI expand B (11.00 x 17.00 Inches)"
+ "Inches" "Landscape" "NO" "Extents" "Fit" "Center" "Yes" "." "Yes" "" filename "NO" "YES")
+filedia
+1
+```
+
+Lấy tên bản vẽ hiện tại, đổi đuôi thành `.pdf`, tắt hộp thoại file (`filedia 0`), gọi lệnh `-PLOT` với driver ảo **"Dwg To PDF.pc3"** để xuất PDF cùng thư mục, cùng tên với DWG, rồi bật lại `filedia`. Đây chính là script mà ScriptPro chạy lặp lại trên toàn bộ danh sách DWG để xuất PDF hàng loạt.
+
+## Chế độ chạy dòng lệnh (silent/headless)
+
+File `.bpl` có thể truyền làm tham số dòng lệnh (`ScriptUI.exe project.bpl run exit`) để tự động nạp danh sách, chạy ngay và thoát không cần thao tác tay — phù hợp để **lên lịch chạy tự động** (Task Scheduler) xuất PDF định kỳ.
diff --git a/README.md b/README.md
index b73e151..9c31165 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,9 @@ The application now supports **AutoCAD 2025 and later** with no changes required
* **DrawingListUC** – Windows Forms control library (.NET 8)
* **ScriptUI** – WPF application (.NET 8)
-* **ScriptProSetup** – WiX-based installer (updated)
+* **DwgExportManager** – WPF application (.NET 8): browse a folder, pick Model/Layout per drawing, preview in AutoCAD, batch export to PDF/PNG
+* **ScriptProSetup** – WiX-based installer (updated), framework-dependent: requires .NET 8 Desktop Runtime on the target machine
+* **DwgExportManagerSetup** – WiX-based installer for DwgExportManager, **self-contained**: bundles the .NET 8 runtime, so it runs on machines without .NET 8 installed
## Build
diff --git a/ScriptProPlus.sln b/ScriptProPlus.sln
index 99e8414..117a53a 100644
--- a/ScriptProPlus.sln
+++ b/ScriptProPlus.sln
@@ -12,6 +12,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DrawingListUC", "DrawingLis
EndProject
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ScriptProSetup", "ScriptProSetup\ScriptProSetup.wixproj", "{A7E8B39E-9CA4-475A-8105-40F274791C94}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DwgExportManager", "DwgExportManager\DwgExportManager.csproj", "{59EAFD35-ECD9-4980-A4FA-CAA5E5A9BB02}"
+EndProject
+Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "DwgExportManagerSetup", "DwgExportManagerSetup\DwgExportManagerSetup.wixproj", "{1A19BA26-355F-49CF-A5D6-E7F76F8BD3D7}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -54,6 +58,24 @@ Global
{A7E8B39E-9CA4-475A-8105-40F274791C94}.Release|x64.ActiveCfg = Release|x86
{A7E8B39E-9CA4-475A-8105-40F274791C94}.Release|x86.ActiveCfg = Release|x86
{A7E8B39E-9CA4-475A-8105-40F274791C94}.Release|x86.Build.0 = Release|x86
+ {59EAFD35-ECD9-4980-A4FA-CAA5E5A9BB02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {59EAFD35-ECD9-4980-A4FA-CAA5E5A9BB02}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {59EAFD35-ECD9-4980-A4FA-CAA5E5A9BB02}.Debug|x64.ActiveCfg = Debug|x64
+ {59EAFD35-ECD9-4980-A4FA-CAA5E5A9BB02}.Debug|x64.Build.0 = Debug|x64
+ {59EAFD35-ECD9-4980-A4FA-CAA5E5A9BB02}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {59EAFD35-ECD9-4980-A4FA-CAA5E5A9BB02}.Debug|x86.Build.0 = Debug|Any CPU
+ {59EAFD35-ECD9-4980-A4FA-CAA5E5A9BB02}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {59EAFD35-ECD9-4980-A4FA-CAA5E5A9BB02}.Release|Any CPU.Build.0 = Release|Any CPU
+ {59EAFD35-ECD9-4980-A4FA-CAA5E5A9BB02}.Release|x64.ActiveCfg = Release|x64
+ {59EAFD35-ECD9-4980-A4FA-CAA5E5A9BB02}.Release|x64.Build.0 = Release|x64
+ {59EAFD35-ECD9-4980-A4FA-CAA5E5A9BB02}.Release|x86.ActiveCfg = Release|Any CPU
+ {59EAFD35-ECD9-4980-A4FA-CAA5E5A9BB02}.Release|x86.Build.0 = Release|Any CPU
+ {1A19BA26-355F-49CF-A5D6-E7F76F8BD3D7}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {1A19BA26-355F-49CF-A5D6-E7F76F8BD3D7}.Debug|x64.ActiveCfg = Debug|x64
+ {1A19BA26-355F-49CF-A5D6-E7F76F8BD3D7}.Debug|x86.ActiveCfg = Debug|x64
+ {1A19BA26-355F-49CF-A5D6-E7F76F8BD3D7}.Release|Any CPU.ActiveCfg = Release|x64
+ {1A19BA26-355F-49CF-A5D6-E7F76F8BD3D7}.Release|x64.ActiveCfg = Release|x64
+ {1A19BA26-355F-49CF-A5D6-E7F76F8BD3D7}.Release|x86.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/TestFiles/PlotAllLayoutsToPDF_A1.scr b/TestFiles/PlotAllLayoutsToPDF_A1.scr
new file mode 100644
index 0000000..aa8b208
--- /dev/null
+++ b/TestFiles/PlotAllLayoutsToPDF_A1.scr
@@ -0,0 +1,68 @@
+; PlotAllLayoutsToPDF_A1.scr
+; Xuat PDF cho TAT CA cac Layout (khung ten) trong ban ve hien tai
+; sang kho giay A1, tu dong Fit theo Extents, dat cung thu muc
+; va cung ten voi file DWG (them hau to _.pdf).
+;
+; Cach dung voi ScriptPro:
+; - Trong ScriptPro, tro Script sang file nay.
+; - ScriptPro se mo tung DWG va chay script nay tren tung file.
+;
+; Yeu cau:
+; - May tinh da cai san may in ao "DWG To PDF.pc3" (mac dinh co san trong AutoCAD).
+; - Trong pc3 do phai co san kho giay "ISO A1 (594.00 x 841.00 MM)".
+; Neu ten kho giay khac (vi du "ISO full bleed A1 (841.00 x 594.00 MM)"),
+; sua lai bien *paperSize* ben duoi cho dung.
+
+(vl-load-com)
+
+; Buoc dong bo, tranh loi khi plot nhieu layout lien tiep
+(setvar "BACKGROUNDPLOT" 0)
+(setvar "FILEDIA" 0)
+(setvar "CMDECHO" 0)
+
+(setq *paperSize* "ISO A1 (594.00 x 841.00 MM)")
+(setq *plotDevice* "DWG To PDF.pc3")
+
+(setq acadApp (vlax-get-acad-object))
+(setq acadDoc (vla-get-ActiveDocument acadApp))
+(setq dwgFullName (vla-get-FullName acadDoc))
+(setq dwgFolder (vl-filename-directory dwgFullName))
+(setq dwgBase (vl-filename-base dwgFullName))
+(setq layouts (vla-get-Layouts acadDoc))
+
+(vlax-for lay layouts
+ (setq layName (vla-get-Name lay))
+ (if (/= (strcase layName) "MODEL")
+ (progn
+ (vla-put-ActiveLayout acadDoc lay)
+ (setq pdfName (strcat dwgFolder "\\" dwgBase "_" layName ".pdf"))
+ (vl-catch-all-apply
+ 'command
+ (list
+ "-PLOT"
+ "YES" ; Detailed plot configuration? Yes
+ layName ; Layout can plot
+ *plotDevice* ; Output device
+ *paperSize* ; Paper size
+ "Millimeters" ; Paper units
+ "Landscape" ; Orientation
+ "NO" ; Plot upside down?
+ "Extents" ; Plot area
+ "Fit" ; Plot scale
+ "Center" ; Plot offset
+ "Yes" ; Plot with plot styles
+ "." ; Giu nguyen bang plot style hien tai
+ "Yes" ; Plot with lineweights
+ "" ; Shade plot setting mac dinh
+ pdfName ; Ten file PDF xuat ra
+ "NO" ; Save changes to page setup? No
+ "YES" ; Proceed with plot
+ )
+ )
+ )
+ )
+)
+
+(setvar "FILEDIA" 1)
+(setvar "CMDECHO" 1)
+(princ)
diff --git a/TestFiles/PlotAllLayoutsToPNG.scr b/TestFiles/PlotAllLayoutsToPNG.scr
new file mode 100644
index 0000000..d93fd59
--- /dev/null
+++ b/TestFiles/PlotAllLayoutsToPNG.scr
@@ -0,0 +1,72 @@
+; PlotAllLayoutsToPNG.scr
+; Xuat anh PNG cho TAT CA cac Layout (khung ten) trong ban ve hien tai,
+; dat cung thu muc va cung ten voi file DWG (them hau to _.png).
+;
+; Nghiep vu giong het PlotAllLayoutsToPDF_A1.scr, chi doi thiet bi "plot"
+; tu may in ao PDF sang may in ao raster "PublishToWeb PNG.pc3" (co san
+; trong moi ban cai AutoCAD, khong can cai them driver).
+;
+; Cach dung voi ScriptPro:
+; - Trong ScriptPro, tro Script sang file nay.
+; - ScriptPro se mo tung DWG va chay script nay tren tung file.
+;
+; Tuy chinh do phan giai:
+; - Kich thuoc anh dat trong bien *paperSize*, chon theo danh sach co san
+; cua "PublishToWeb PNG.pc3": "640 x 480 Pixels", "800 x 600 Pixels",
+; "1024 x 768 Pixels", "1600 x 1280 Pixels" (mac dinh dang dung, lon nhat).
+; - Neu can do phan giai cao hon (vi du xuat anh ban do quy hoach kho lon,
+; net chu ro), tao them "Custom size" cho PublishToWeb PNG.pc3 trong
+; AutoCAD (Plotter Manager) roi doi ten trong bien *paperSize* tuong ung.
+
+(vl-load-com)
+
+(setvar "BACKGROUNDPLOT" 0)
+(setvar "FILEDIA" 0)
+(setvar "CMDECHO" 0)
+
+(setq *paperSize* "1600 x 1280 Pixels")
+(setq *plotDevice* "PublishToWeb PNG.pc3")
+
+(setq acadApp (vlax-get-acad-object))
+(setq acadDoc (vla-get-ActiveDocument acadApp))
+(setq dwgFullName (vla-get-FullName acadDoc))
+(setq dwgFolder (vl-filename-directory dwgFullName))
+(setq dwgBase (vl-filename-base dwgFullName))
+(setq layouts (vla-get-Layouts acadDoc))
+
+(vlax-for lay layouts
+ (setq layName (vla-get-Name lay))
+ (if (/= (strcase layName) "MODEL")
+ (progn
+ (vla-put-ActiveLayout acadDoc lay)
+ (setq pngName (strcat dwgFolder "\\" dwgBase "_" layName ".png"))
+ (vl-catch-all-apply
+ 'command
+ (list
+ "-PLOT"
+ "YES" ; Detailed plot configuration? Yes
+ layName ; Layout can plot
+ *plotDevice* ; Output device (raster PNG)
+ *paperSize* ; Kich thuoc anh (pixels)
+ "Millimeters" ; Paper units (bo qua voi thiet bi raster)
+ "Landscape" ; Orientation
+ "NO" ; Plot upside down?
+ "Extents" ; Plot area
+ "Fit" ; Plot scale
+ "Center" ; Plot offset
+ "Yes" ; Plot with plot styles
+ "." ; Giu nguyen bang plot style hien tai
+ "Yes" ; Plot with lineweights
+ "" ; Shade plot setting mac dinh
+ pngName ; Ten file PNG xuat ra
+ "NO" ; Save changes to page setup? No
+ "YES" ; Proceed with plot
+ )
+ )
+ )
+ )
+)
+
+(setvar "FILEDIA" 1)
+(setvar "CMDECHO" 1)
+(princ)