Skip to content

Troubleshooting

อาการ:

  • 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
  • ตรวจว่า 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> หลักด้านบนสุด ดังนี้ครับ:

โปรเจ็กต์.csproj
<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)”

ให้สร้างคลาสตัวช่วยส่งสัญญาณบันทึกข้อความลงบนโฟลเดอร์ระบบ:

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 ของคุณ:

Command.cs
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 ค้างคาให้อย่างเรียบร้อย ป้องกันปัญหาไฟล์โปรเจ็กต์พังทลายกึ่งกลางครับ