Hi,
If you want to use a standalone Windows Form executable, you have to keep mind you have to use the COM API which is less powerfull than the .NET one and which have compatibility issues with platform (32 or 64 bits) and with AutoCAD major versions.
Here's a trivial example using a very simple Windows Form:

The Form Class code (Autodesk.AutoCAD.Interop and Autodesk.AutoCAD.Interop.Common libraries corresponding to targetted version of AutoCAD have to be referenced).
using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Interop.Common;
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WinFormStandAlone
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
btnOK.Click += BtnOK_Click;
}
private void BtnOK_Click(object sender, EventArgs e)
{
double[] startPoint, endPoint;
if (!TryParseToPoint(txtStartPoint.Text, out startPoint))
MessageBox.Show($"Invalid Start point.");
if (!TryParseToPoint(txtEndPoint.Text, out endPoint))
MessageBox.Show($"Invalid End point.");
AcadApplication acadApp;
string progId = "AutoCAD.Application.19";
try
{
acadApp = (AcadApplication)Marshal.GetActiveObject(progId);
}
catch
{
try
{
acadApp = (AcadApplication)Activator.CreateInstance(Type.GetTypeFromProgID(progId), true);
}
catch
{
MessageBox.Show("Instance of 'AutoCAD.Application' could not be created.");
return;
}
}
while (true)
{
try { acadApp.Visible = true; break; }
catch { }
}
acadApp.WindowState = AcWindowState.acMax;
AcadDocument doc = acadApp.ActiveDocument;
AcadModelSpace modelSpace = doc.ModelSpace;
modelSpace.AddLine(startPoint, endPoint);
}
private static bool TryParseToPoint(string source, out double[] point)
{
point = null;
string[] array = source.Split(',');
if (array.Length < 2 || array.Length > 3)
return false;
double x, y, z = 0.0;
if (!double.TryParse(array[0], out x))
return false;
if (!double.TryParse(array[1], out y))
return false;
if (array.Length == 3 && !double.TryParse(array[2], out z))
return false;
point = new[] { x, y, z };
return true;
}
}
}
Using the AutoCAD .NET API, you can also have a user interface as modal dialog (the dialog form have the same controls as upper):

using System.Windows.Forms;
namespace InProcessDialog
{
public partial class Dialog : Form
{
public string StartPoint => txtStartPoint.Text;
public string EndPoint => txtEndPoint.Text;
public Dialog()
{
InitializeComponent();
btnOK.DialogResult = DialogResult.OK;
}
}
}
The dialog is opened from AutoCAD with the "DRAWLINEDLG" command:
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System.Windows.Forms;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
namespace InProcessDialog
{
public class Commands
{
[CommandMethod("DrawLineDlg")]
public void DrawLine()
{
var dlg = new Dialog();
if (AcAp.ShowModalDialog(dlg) == DialogResult.OK)
{
Point3d startPoint, endPoint;
if (!TryParseToPoint(dlg.StartPoint, out startPoint))
{
MessageBox.Show($"Invalid Start point.");
return;
}
if (!TryParseToPoint(dlg.EndPoint, out endPoint))
{
MessageBox.Show($"Invalid End point.");
return;
}
var doc = AcAp.DocumentManager.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
using (var tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord modelSpace = (BlockTableRecord)tr.GetObject(
SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite);
Line line = new Line(startPoint, endPoint);
modelSpace.AppendEntity(line);
tr.AddNewlyCreatedDBObject(line, true);
tr.Commit();
}
}
}
private static bool TryParseToPoint(string source, out Point3d point)
{
point = default(Point3d);
string[] array = source.Split(',');
if (array.Length < 2 || array.Length > 3)
return false;
double x, y, z = 0.0;
if (!double.TryParse(array[0], out x))
return false;
if (!double.TryParse(array[1], out y))
return false;
if (array.Length == 3 && !double.TryParse(array[2], out z))
return false;
point = new Point3d(x, y, z);
return true;
}
}
}
You can also use a modeless user interface from AutoCAD (typically a palette set)
The user control (a palette tab of the palette set) contains the same controls as the upper forms.

With modeless user interfaces, it's simpler and safer to call cusom commands from event handlersso that AutoCAD takes care of locking the document and setting the focus to AutoCAD window.
using System;
using System.Windows.Forms;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
namespace InProcessPalette
{
public partial class DrawLineTab : UserControl
{
public DrawLineTab()
{
InitializeComponent();
}
private void btnOK_Click(object sender, EventArgs e)
{
if (!IsValidPoint(txtStartPoint.Text))
{
MessageBox.Show("Invalid Start Point.");
return;
}
if (!IsValidPoint(txtEndPoint.Text))
{
MessageBox.Show("Invalid End Point.");
return;
}
var doc = AcAp.DocumentManager.MdiActiveDocument;
doc?.SendStringToExecute($"DrawLineCmd {txtStartPoint.Text} {txtEndPoint.Text} ", false, false, false);
}
private bool IsValidPoint(string source)
{
string[] array = source.Split(',');
if (array.Length < 2 || array.Length > 3)
return false;
double x;
if (!double.TryParse(array[0], out x))
return false;
if (!double.TryParse(array[1], out x))
return false;
if (array.Length == 3 && !double.TryParse(array[2], out x))
return false;
return true;
}
}
}
The palette is showned by running the "CMD_PALETTE" command, and a click on the OK button launches the "DrawLineCmd" which can also be directly called.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
using Autodesk.AutoCAD.Windows;
namespace InProcessPalette
{
public class Commands
{
static PaletteSet palette;
static bool wasVisible;
[CommandMethod("CMD_PALETTE")]
public void CmdPalette()
{
if (palette == null)
{
palette = new PaletteSet(
"Palette",
"CMD_PALETTE",
new Guid("{4E63230E-EEB0-45FF-A9A1-65D4FDEAE5DC}"));
palette.Style =
PaletteSetStyles.ShowAutoHideButton |
PaletteSetStyles.ShowCloseButton |
PaletteSetStyles.ShowPropertiesMenu;
palette.MinimumSize = new System.Drawing.Size(250, 150);
palette.Add("Draw Line", new DrawLineTab());
var docs = AcAp.DocumentManager;
docs.DocumentBecameCurrent += (s, e) =>
palette.Visible = e.Document == null ? false : wasVisible;
docs.DocumentCreated += (s, e) =>
palette.Visible = wasVisible;
docs.DocumentToBeDeactivated += (s, e) =>
wasVisible = palette.Visible;
docs.DocumentToBeDestroyed += (s, e) =>
{
wasVisible = palette.Visible;
if (docs.Count == 1)
palette.Visible = false;
};
}
palette.Visible = true;
}
[CommandMethod("DrawLineCmd")]
public void DrawLine()
{
var doc = Application.DocumentManager.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
var options = new PromptPointOptions("\nStart Point: ");
var result = ed.GetPoint(options);
if (result.Status != PromptStatus.OK)
return;
var startPoint = result.Value;
options.BasePoint = startPoint;
options.UseBasePoint = true;
options.Message = "\nend Point: ";
result = ed.GetPoint(options);
if (result.Status != PromptStatus.OK)
return;
var ucs = ed.CurrentUserCoordinateSystem;
var endPoint = result.Value.TransformBy(ucs);
startPoint = startPoint.TransformBy(ucs);
using (var tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord modelSpace = (BlockTableRecord)tr.GetObject(
SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite);
Line line = new Line(startPoint, endPoint);
modelSpace.AppendEntity(line);
tr.AddNewlyCreatedDBObject(line, true);
tr.Commit();
}
}
}
}