To loop through all AutoCAD sessions, we can use System.Diagnostics.Process.GetProcessesByName("acad") to get all AutoCAD processes.
Marshal.GetActiveObject("AutoCAD.Application") always gets the last open instance of AutoCAD (which is lately active), so it would not use to get a specific session of AutoCAD.
Each AutoCAD process (session) has its own handle (MainWindowHandle) in Windows processes (Windows Task Manager -> Processes tab), so we will get an AutoCAD handle and get its COM object from there.
To get a COM object from a Windows handle number, we can use an import method AccessibleObjectFromWindow from Oleacc.dll. With the main inspiration from Andrew Whitechapel's blog, I rewrite the similar code trying to get an AutoCAD application using late binding.
The code is not working now, so everyone welcome to work more on this. The problem may come from the incorrect OBJID_NATIVEOM and Guid. The Guid I used in this code is for AutoCAD 2013. But I don’t know how to get OBJID_NATIVEOM (native object model) for AutoCAD. This is a very interesting topic for AutoCAD's late binding.
using System;
using System.Runtime.InteropServices;
using System.Text;
using AutoCAD;
namespace AcadLateBinding
{
class Program
{
static void Main(string[] args)
{
ConnectAutoCAD();
}
public delegate bool EnumChildCallback(int hwnd, ref int lParam);
private static EnumChildCallback _callback;
[DllImport("User32")]
public static extern bool EnumChildWindows(
int hWndParent, EnumChildCallback lpEnumFunc, ref int lParam);
// AccessibleObjectFromWindow gets the IDispatch pointer of an object
// that supports IAccessible, which allows us to get to the native OM.
[DllImport("Oleacc.dll")]
private static extern int AccessibleObjectFromWindow(
int hwnd, uint dwObjectID,
byte[] riid,
out AcadApplication ptr);
[DllImport("User32.dll")]
public static extern int GetClassName(
int hWnd, StringBuilder lpClassName, int nMaxCount);
public static bool EnumChildProc(int hwndChild, ref int lParam)
{
StringBuilder buf = new StringBuilder(128);
GetClassName(hwndChild, buf, 128);
if (buf.ToString() == "MDIClient")
{
lParam = hwndChild;
return false;
}
return true;
}
public static void ConnectAutoCAD()
{
// Get the first AutoCAD's main window handle.
int hwnd = (int)System.Diagnostics.Process.GetProcessesByName("acad")[0].MainWindowHandle;
// We need to enumerate the child windows to find one that
// supports accessibility. To do this, instantiate the
// delegate and wrap the callback method in it, then call
// EnumChildWindows, passing the delegate as the 2nd arg.
if (hwnd != 0)
{
int hwndChild = 0;
_callback = new EnumChildCallback(EnumChildProc);
EnumChildWindows(hwnd, _callback, ref hwndChild);
// If we found an accessible child window, call
// AccessibleObjectFromWindow, passing the constant
// OBJID_NATIVEOM (defined in winuser.h) and
// IID_IDispatch - we want an IDispatch pointer
// into the native object model.
if (hwndChild != 0)
{
const uint OBJID_NATIVEOM = 0xFFFFFFF0;
//Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}");
Guid IID_IDispatch = new Guid("{070AA05D-DFC1-4E64-8379-432269B48B07}");
AcadApplication ptr;
int hr = AccessibleObjectFromWindow(hwndChild, OBJID_NATIVEOM, IID_IDispatch.ToByteArray(), out ptr);
if (hr >= 0)
{
// If we successfully got a native OM IDispatch pointer, we can QI this for
// an AutoCAD application (using the implicit cast operator supplied in the PIA).
var acadApp = ptr.Application;
}
}
}
}
}
}
-Khoa