Skip to content

Instantly share code, notes, and snippets.

@MadhukarMoogala
Last active November 16, 2024 14:09
Show Gist options
  • Save MadhukarMoogala/2055d7204dfa1b9359cf216b7a771651 to your computer and use it in GitHub Desktop.
Save MadhukarMoogala/2055d7204dfa1b9359cf216b7a771651 to your computer and use it in GitHub Desktop.
Building a Sample AutoCAD Plugin using C++/CLI for AutoCAD 2025 and later version using .NET 8.0
//-----------------------------------------------------------------------------
//----- acrxEntryPoint.cpp
//-----------------------------------------------------------------------------
#include "StdAfx.h"
#include "resource.h"
//-----------------------------------------------------------------------------
#define szRDS _RXST("MAD")
using namespace System;
using namespace System::Windows::Forms;
using namespace System::Threading::Tasks;
using namespace System::Drawing;
using namespace Autodesk::AutoCAD::ApplicationServices::Core;
using namespace Autodesk::AutoCAD::Internal;
// Part 1: AutoCAD Database Helper Class
//----------------------------------------
class AcDbHelper
{
public:
// Smart pointer for AutoCAD database objects
template <class T> struct unique_db_ptr : public std::unique_ptr<T, void(*)(AcDbObject*)>
{
unique_db_ptr<T>(T* t) : std::unique_ptr<T, void(*)(AcDbObject*)>(t, closeOrDeleteDbObj) { }
static unique_db_ptr<T> create()
{
T* newObj = new T();
return unique_db_ptr<T>(newObj);
}
};
// Add entity to model space
static bool addToDb(AcDbEntity* pEnt, AcDbDatabase* pDb = nullptr)
{
if (!pDb)
pDb = acdbHostApplicationServices()->workingDatabase();
unique_db_ptr<AcDbEntity> ent(pEnt);
AcDbBlockTable* pBt;
if (Acad::eOk != pDb->getBlockTable(pBt, AcDb::kForRead))
return false;
unique_db_ptr<AcDbBlockTable> bt(pBt);
AcDbBlockTableRecord* pMs;
if (Acad::eOk != pBt->getAt(ACDB_MODEL_SPACE, pMs, AcDb::kForWrite))
return false;
return Acad::eOk == unique_db_ptr<AcDbBlockTableRecord>(pMs)->appendAcDbEntity(ent.get());
}
// Create and add circle to database
static bool createCircle(const AcGePoint3d& center, double radius, int colorIndex = 1)
{
auto circlePtr = unique_db_ptr<AcDbCircle>::create();
if (!circlePtr)
return false;
AcDbCircle* circle = circlePtr.get();
circle->setDatabaseDefaults();
circle->setRadius(radius);
circle->setColorIndex(colorIndex);
circle->setCenter(center);
return addToDb(circle);
}
// Generate a random color index
static int getRandomColorIndex()
{
int colorArray[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int index = rand() % 10;
return (colorArray[index]);
}
private:
// Helper function for smart pointer cleanup
static void closeOrDeleteDbObj(AcDbObject* pObj)
{
if (pObj->objectId().isNull())
delete pObj;
else
pObj->close();
}
};
// Part 2: UI Form Implementation with Clean Design
//----------------------------------------
namespace UIForms {
namespace AcadApp = Autodesk::AutoCAD::ApplicationServices::Core;
namespace AcadUtils = Autodesk::AutoCAD::Internal;
public ref class MainForm : public Form
{
private:
// UI Controls
Button^ drawButton;
NumericUpDown^ radiusInput;
Label^ radiusLabel;
// Initialize the form controls
void InitializeComponent()
{
this->Text = "Advanced AutoCAD Drawing Tool";
this->Width = 400;
this->Height = 200;
this->FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog;
this->StartPosition = FormStartPosition::CenterScreen;
// Radius input setup
radiusLabel = gcnew Label();
radiusLabel->Text = "Circle Radius:";
radiusLabel->Location = Point(50, 40);
radiusLabel->AutoSize = true;
radiusInput = gcnew NumericUpDown();
radiusInput->Location = Point(150, 38);
radiusInput->Minimum = 1;
radiusInput->Maximum = 10000;
radiusInput->Value = 10;
radiusInput->DecimalPlaces = 2;
// Draw button setup
drawButton = gcnew Button();
drawButton->Text = "Draw Circle";
drawButton->Location = Point(150, 80);
drawButton->Width = 100;
drawButton->Click += gcnew EventHandler(this, &MainForm::DrawButton_Click);
// Add controls to form
this->Controls->Add(radiusLabel);
this->Controls->Add(radiusInput);
this->Controls->Add(drawButton);
}
// Helper method that performs the drawing operation on the main thread
Task^ DrawCircleAsync(System::Object^ data)
{
// Create a TaskCompletionSource to manage the task lifecycle
auto tcs = gcnew TaskCompletionSource();
try
{
double radius = Convert::ToDouble(radiusInput->Value);
int colorIndex = AcDbHelper::getRandomColorIndex();
if (AcDbHelper::createCircle(AcGePoint3d::kOrigin, radius, colorIndex))
{
//Marks the task as successfully completed
tcs->SetResult();
}
else
{
throw gcnew Exception("Failed to create circle");
}
}
catch (System::Exception^ ex)
{
// Marks the task as completed with an exception
tcs->SetException(ex);
}
return tcs->Task;
}
/// <summary
/// Handles the "Draw Circle" button click, initiates the async drawing operation, and handles task completion or errors.
///</summary>
void DrawButton_Click(System::Object^ sender, System::EventArgs^ e)
{
// Disable the button to prevent multiple clicks
drawButton->Enabled = false;
try
{
// Retrieve the DocumentManager to interact with AutoCAD's document system.
auto dm = Autodesk::AutoCAD::ApplicationServices::Core::Application::DocumentManager;
// Create a delegate for the DrawCircleAsync method, which returns a Task.
auto callback = gcnew Func<Object^, Task^>(this, &MainForm::DrawCircleAsync);
// Execute the delegate asynchronously within AutoCAD's command context.
// This ensures that the drawing operation happens in the correct environment.
auto task = dm->ExecuteInCommandContextAsync(callback, nullptr);
//Attach a completion handler(OnCompleted) to the task to handle post - operation actions.
task->OnCompleted(gcnew Action(this, &MainForm::OnDrawCompleted));
}
catch (System::Exception^ ex)
{
MessageBox::Show("Error: " + ex->Message, "Error",
MessageBoxButtons::OK, MessageBoxIcon::Error);
}
}
void OnDrawCompleted()
{
try
{
// Enable the button
drawButton->Enabled = true;
// Set focus to the drawing area and zoom extents
AcadUtils::Utils::SetFocusToDwgView();
AcadUtils::Utils::CancelAndRunCmds("_.zoom\n_extents\n");
}
catch (System::Exception^ ex)
{
MessageBox::Show("Error: " + ex->Message, "Error",
MessageBoxButtons::OK, MessageBoxIcon::Error);
}
}
public:
MainForm() {
InitializeComponent();
}
};
}
// Part 3: ObjectARX Entry Point
//----------------------------------------
class CArxNetCoreApp : public AcRxArxApp {
public:
CArxNetCoreApp() : AcRxArxApp() {}
virtual AcRx::AppRetCode On_kInitAppMsg(void* pkt) {
return AcRxArxApp::On_kInitAppMsg(pkt);
}
virtual AcRx::AppRetCode On_kUnloadAppMsg(void* pkt) {
return AcRxArxApp::On_kUnloadAppMsg(pkt);
}
virtual void RegisterServerComponents() {
}
static void MADGUIToolLaunch() {
UIForms::MainForm^ form = gcnew UIForms::MainForm();
try
{
Autodesk::AutoCAD::ApplicationServices::Application::ShowModelessDialog(form);
}
catch (System::Exception^ ex)
{
acutPrintf(L"\nException occurred: %s", ex->Message);
}
}
};
IMPLEMENT_ARX_ENTRYPOINT(CArxNetCoreApp)
ACED_ARXCOMMAND_ENTRY_AUTO(CArxNetCoreApp, MADGUI, ToolLaunch, ToolLaunch, ACRX_CMD_MODAL, NULL)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment