Troubleshooting
Duplicated AddInId
Section titled “Duplicated AddInId”อาการ:
- Revit แจ้งว่า duplicated add-in id
- plugin ไม่ถูก initialize
วิธีแก้:
- สร้าง GUID ใหม่
- ใช้
Registry Format - เอาวงเล็บ
{}ออกก่อนใส่ใน.addin
Plugin ไม่ขึ้นใน External Tools
Section titled “Plugin ไม่ขึ้นใน External Tools”เช็กตามลำดับนี้
- ไฟล์
.addinอยู่ในC:\ProgramData\Autodesk\Revit\Addins\2026\หรือไม่ - ค่า
Type="Command"ถูกหรือไม่ FullClassNameตรงกับ namespace/class จริงหรือไม่Assemblyชี้ไป DLL ที่มีอยู่จริงหรือไม่
ถ้าโปรเจ็กต์ของคุณใช้ Ribbon เอง ให้เช็ก Type="Application" และ FullClassName ต้องชี้ไป App แทน
Build ผ่านแต่ Revit ไม่โหลด
Section titled “Build ผ่านแต่ Revit ไม่โหลด”- ดูว่า
.addinยังชี้ไป path เก่าจากbin\Debugหรือไม่ - เช็กว่า deploy folder มี DLL ล่าสุดจริง
- ปิด Revit แล้วเปิดใหม่หลัง build
Debug ไม่เข้า breakpoint
Section titled “Debug ไม่เข้า breakpoint”- ตรวจว่า launch profile ชี้ไป
Revit.exeจริง - ตรวจว่ากด
F5จาก profile ที่ถูก - ตรวจว่าเปิด command จากเมนูที่ตรงกับชนิด add-in ของคุณ หลัง debugger attach แล้ว
คำเตือนรุ่นขัดแย้งขณะ Build (Conflict Warning MSB3277)
Section titled “คำเตือนรุ่นขัดแย้งขณะ Build (Conflict Warning MSB3277)”อาการ:
เมื่อสั่งคอมไพล์โปรเจ็กต์ด้วยคำสั่ง dotnet build หรือกด Build ใน Visual Studio แล้วปรากฏข้อความแจ้งเตือนคำเตือนสีเหลือง (Warning) รหัส MSB3277 ที่ระบุว่า:
warning MSB3277: Found conflicts between different versions of "Microsoft.VisualBasic" or "System.Drawing" that could not be resolved.
สาเหตุ:
เกิดจากการที่ปลั๊กอินของเราทำงานอยู่บนมาตรฐาน .NET 8.0 (ซึ่งมีไฟล์ระบบพื้นฐานอย่าง System.Drawing.dll รุ่น 4.0.0.0 หรือ Microsoft.VisualBasic.dll รุ่น 10.0.0.0) แต่ไฟล์อ้างอิงหลักของโปรแกรม Autodesk Revit (เช่น RevitAPI.dll หรือ RevitAPIUI.dll) มีการพึ่งพากับรุ่นย่อยเหล่านั้นในเวอร์ชันที่ต่างกันเล็กน้อย (เช่น รุ่น 8.0.0.0 หรือรุ่น 10.1.0.0) ทำให้ตัวระบบคอมไพเลอร์ MSBuild ทำการแจ้งข้อความเตือนความสับสนเรื่องเวอร์ชันให้คุณทราบเฉยๆ ครับ
การประเมินความปลอดภัย:
- ปลอดภัย 100% และปลั๊กอินทำงานได้สมบูรณ์: นี่คือ คำเตือน (Warning) ไม่ใช่ ข้อผิดพลาด (Error) โครงสร้างโค้ดปลั๊กอินของคุณคอมไพล์ผ่านสมบูรณ์ดี
- เมื่อนำปลั๊กอินเข้าไปรันทำงานจริงในโปรแกรม Revit ตัวระบบหลังบ้านของ Revit จะมีกลไกสลับการเรียกใช้งานรุ่นไฟล์ย่อยให้เข้ากับเครื่องยนต์หลักของตัวเองโดยอัตโนมัติอยู่แล้ว ปลั๊กอินจึงทำงานปกติสุขไม่ได้รับผลกระทบใดๆ ครับ
วิธีการซ่อนคำเตือนไม่ให้รบกวนสายตา (Optional):
หากคุณไม่ต้องการให้ข้อความเตือนสี่ห้าสิบบรรทัดเหล่านี้มาแสดงรบกวนสายตาขณะสั่ง Build ให้เปิดไฟล์คอนฟิกโปรเจ็กต์ โปรเจ็กต์.csproj ของคุณขึ้นมา แล้วทำการแทรกบรรทัดแท็กปิดคำเตือนเข้าไปในกลุ่ม <PropertyGroup> หลักด้านบนสุด ดังนี้ครับ:
<PropertyGroup> <!-- โค้ดตั้งค่าโปรเจ็กต์เดิมของคุณ... -->
<!-- 🟢 แทรกบรรทัดนี้ลงไปเพื่อสั่งซ่อนคำเตือนการชนรุ่น DLL ที่ไม่มีผลกระทบ --> <MSBuildWarningsAsMessages>MSB3277</MSBuildWarningsAsMessages> </PropertyGroup>🛡️ สถาปัตยกรรมรับมือกับความเสียหายและระบบบันทึกประวัติ (Robust Exception Handling & Logger)
Section titled “🛡️ สถาปัตยกรรมรับมือกับความเสียหายและระบบบันทึกประวัติ (Robust Exception Handling & Logger)”ในการใช้งานจริง หากปลั๊กอินของคุณเกิด Error ขึ้นมาระหว่างทำงาน (เช่น ดึงพารามิเตอร์ผิดประเภท หรือผู้ใช้ลบวัตถุที่กำลังตามหาอยู่ทิ้งไป) หากคุณไม่ได้เขียนป้องกันไว้ ปลั๊กอินจะกระชากระบบ Revit ให้ปิดตัวลงหรือดีด Error โค้ดพังทันที ซึ่งทำให้ผู้ใช้งานสูญเสียข้อมูลงานที่กำลังทำค้างไว้
เพื่อยกระดับปลั๊กอินขึ้นสู่ เกรดใช้จริงในองค์กร เรามาสร้าง Custom Logger เพื่อคอยเก็บกวาดข้อมูลข้อผิดพลาดบันทึกลงเป็นไฟล์ .log ในเครื่อง และแจ้งเตือนหน้าต่าง Popup สวยๆ ให้ผู้ใช้งานทราบอย่างสุภาพกันครับ!
1. โค้ดเขียนระบบบันทึกประวัติ (Logger.cs)
Section titled “1. โค้ดเขียนระบบบันทึกประวัติ (Logger.cs)”ให้สร้างคลาสตัวช่วยส่งสัญญาณบันทึกข้อความลงบนโฟลเดอร์ระบบ:
using System;using System.IO;
namespace RevitToolkit;
public static class Logger{ private static readonly string LogFolder = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "RevitToolkit", "Logs" );
public static void LogError(Exception ex, string customMessage = "") { try { // 1. สร้างโฟลเดอร์สำหรับเก็บประวัติ หากระบบยังไม่มี if (!Directory.Exists(LogFolder)) { Directory.CreateDirectory(LogFolder); }
// 2. ตั้งชื่อไฟล์บันทึกรายวัน เช่น log_2026_05_26.txt string logFilePath = Path.Combine(LogFolder, $"log_{DateTime.Now:yyyy_MM_dd}.txt");
// 3. จัดการประกอบข้อความที่จะบันทึกอย่างละเอียด string logContent = $"========================================\n" + $"Timestamp: {DateTime.Now:yyyy-MM-dd HH:mm:ss}\n" + $"Context: {customMessage}\n" + $"Error: {ex.Message}\n" + $"Source: {ex.Source}\n" + $"StackTrace:\n{ex.StackTrace}\n" + $"========================================\n\n";
// 4. เขียนบันทึกแนบต่อท้ายไฟล์เดิม (Append) File.AppendAllText(logFilePath, logContent); } catch { // ละเว้นเพื่อไม่ให้ระบบ logger ดึงโปรแกรมหลักล่มซ้อนกัน } }}2. วิธีนำไปครอบและเรียกใช้งานในคำสั่ง (Command.cs)
Section titled “2. วิธีนำไปครอบและเรียกใช้งานในคำสั่ง (Command.cs)”นำโค้ดดักจับ Exception (try-catch) ไปครอบคำสั่งหลักทั้งหมดในเมธอด Execute ของคุณ:
using System;using Autodesk.Revit.Attributes;using Autodesk.Revit.DB;using Autodesk.Revit.UI;
namespace RevitToolkit;
[Transaction(TransactionMode.Manual)]public class Command : IExternalCommand{ public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { Document doc = commandData.Application.ActiveUIDocument.Document;
try { // --- โค้ดทำงานหลักเขียนแก้ไขโมเดลของคุณอยู่ตรงนี้ --- var columns = new FilteredElementCollector(doc) .OfCategory(BuiltInCategory.OST_StructuralColumns) .WhereElementIsNotElementType() .ToElements();
using (Transaction trans = new Transaction(doc, "Auto Number Columns")) { trans.Start();
int counter = 1; foreach (Element col in columns) { Parameter markParam = col.get_Parameter(BuiltInParameter.ALL_MODEL_MARK); if (markParam != null && !markParam.IsReadOnly) { markParam.Set($"C-{counter:D2}"); counter++; } }
trans.Commit(); }
return Result.Succeeded; } catch (Exception ex) { // 1. สั่งให้เขียนข้อมูล StackTrace ลงในไฟล์ log เพื่อให้โปรแกรมเมอร์มาส่องแก้ไขได้ภายหลัง Logger.LogError(ex, "เกิดปัญหาระหว่างประมวลผลคำสั่งหลักบน Command");
// 2. จัดรูปแบบการแสดงผลหน้าต่างแจ้งเตือนให้ผู้ใช้งานรับทราบอย่างเป็นมิตร TaskDialog mainDialog = new TaskDialog("RevitToolkit - Error Notification") { MainIcon = TaskDialogIcon.TaskDialogIconWarning, MainInstruction = "เกิดข้อผิดพลาดในการรันปลั๊กอิน", MainContent = $"ระบบขอยกเลิกการทำงานเนื่องจาก: {ex.Message}", ExpandedContent = $"ประวัติข้อผิดพลาด:\n{ex.StackTrace}\n\n*ไฟล์ประวัติฉบับเต็มถูกบันทึกไว้ใน LocalAppData/RevitToolkit/Logs*", CommonButtons = TaskDialogCommonButtons.Close }; mainDialog.Show();
// 3. คืนสถานะ Failed ส่งกลับไปยัง Revit เพื่อความปลอดภัย (Revit จะทำการยกเลิก Transaction ให้อัตโนมัติ) return Result.Failed; } }}กฎทองของความปลอดภัย
ในการเขียนดักจับ Error ด้วย try-catch ห้ามละเว้นประวัติการคืนค่าสถานะเด็ดขาด! เมื่อเกิดข้อผิดพลาดขึ้นในกระบวนการทำงานหลัก คุณจะต้องคืนค่า Result.Failed กลับไปเสมอ เพื่อส่งสัญญาณเตือนภัยให้หลังบ้านของ Revit รับทราบและรีบยุติการบันทึกประวัติ หรือจัดการเคลียร์ Transaction ค้างคาให้อย่างเรียบร้อย ป้องกันปัญหาไฟล์โปรเจ็กต์พังทลายกึ่งกลางครับ