Bones, clones, and cyclones

My blogging has taken a nosedive in recent years, but I’m happy to report that I am doing great. I’ve been 100% focused on making sure there’s a viable alternative DWG platform for all you lost, lonely, and depressed CAD users out there. I’m going to keep this short and sweet, because as I write this I am still 100% focused on the task: putting finishing touches on BricsCAD V19 to be released “soon”.

I’ll get straight to the point. Yes, Bricsys has been acquired by Hexagon, but you would hardly know this if you worked inside Bricsys. Inside the company, the same people are doing the same work today that they were doing last month. Literally nothing has changed, at least not yet. Bricsys will change, yes, and nobody can be sure just how it will change, but I assure you that my focus will not change.

Look, the AutoCAD writing is on the wall. Life in the nursing home has not been kind to AutoCAD, and things are only going to get worse as Autodesk turns up the morphine drip. I’ve said for years that BricsCAD is the best alternative, and today it is more true than ever. To steal a line from Bricsys CEO Erik de Keyser, BricsCAD is not a clone, but a cyclone!

Turning off InfoCenter in AutoCAD

Are you looking for a way to turn off the annoying InfoCenter that slows down (or sometimes locks up) your AutoCAD? Of course you are! Hop on over to the ManuSoft Freebies page and install AcadInfoCenterOff.msi, the most popular ManuSoft download of all time.

2017-03-21: Now updated with support for AutoCAD 2018 and AutoCAD LT 2018!

Writing a Custom Debugger on Windows

Recently I had to debug an intermittent access violation exception in one of my AutoCAD plug-ins. I needed to get the exception while the debugger was attached so I could break the process and analyze the state of memory before AutoCAD’s global exception handler got control. Unfortunately, sometimes it took several hundred runs before the exception occurred.

To make matters worse, AutoCAD was throwing exceptions on shutdown that had nothing to do with my code. If I started AutoCAD manually under the Visual Studio debugger, I had to dismiss those shutdown exceptions manually every time. I needed a way to script the task of starting AutoCAD under the debugger hundreds of times in a row until the exception finally occurred.

After some futile attempts to script the Visual Studio debugger, I decided to just write my own custom debugger. With my own custom debugger, I could easily write code to decide at runtime which exceptions to ignore and which were of interest. The exception I was interested in always occurred while accessing a memory location that ended in 0x30, so my custom debugger ignores access violations that don’t fit the pattern.

I had also planned to see if I could suspend the main thread, detach my custom debugger, and manually attach the Visual Studio debugger when my target exception was triggered so that I could utilize the more advanced VS debugger UI. In the end I never got around to trying the debugger switch because I discovered the problem by simply displaying some relevant details in a simple message box when the target exception occurred.

As you can see, I hardcoded the application path and command line, which includes a script file that simply quit AutoCAD after it started. I then used a batch file to run the custom debugger in a loop, thereby starting and quitting AutoCAD each time through the loop. I made this debugger a console app, but it could just as well be a window app.

I don’t claim to know anything about writing debuggers; this is just something I cobbled together after doing a bit of searching on the web. I decided to share the code in case someone else runs into a similar debugging scenario.

int _tmain(int argc, _TCHAR* argv[])
{
STARTUPINFO si;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory( &pi, sizeof(pi) );
static TCHAR szCmdLine[] = _T(" /nologo /b C:\Test\quit.scr");
CreateProcess ( _T("C:\Program Files\Autodesk\AutoCAD 2014\acad.exe"), szCmdLine, NULL, NULL, FALSE, DEBUG_ONLY_THIS_PROCESS, NULL,NULL, &si, &pi );

DWORD dwContinueStatus = DBG_CONTINUE;
DEBUG_EVENT debug_event = {0};
bool bFirstBreakpoint = false;
bool bContinueDebugging = true;
while( bContinueDebugging )
{
if( !WaitForDebugEvent( &debug_event, INFINITE ) )
return 0;
//process event
switch( debug_event.dwDebugEventCode )
{
case EXIT_PROCESS_DEBUG_EVENT :
{
bContinueDebugging = false;
}
break;

case EXCEPTION_DEBUG_EVENT:
{
dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
EXCEPTION_DEBUG_INFO& exception = debug_event.u.Exception;
switch( exception.ExceptionRecord.ExceptionCode )
{
case STATUS_BREAKPOINT:
{
if( !bFirstBreakpoint )
{
bFirstBreakpoint = true;
break;
}
dwContinueStatus = DBG_EXCEPTION_HANDLED;
DebugActiveProcessStop(pi.dwProcessId);
bContinueDebugging = false;
MessageBox(NULL,_T("Debug break!"), _T("Breakpoint"), MB_OK);
}
break;

case STATUS_ACCESS_VIOLATION:
if( exception.dwFirstChance == 1 )
{
if( ((DWORD_PTR)exception.ExceptionRecord.ExceptionAddress & 0xFF) == 0x30 )
{
DebugActiveProcessStop(pi.dwProcessId);
DWORD64 Rip = (DWORD64)-1;
HANDLE hThread = OpenThread( THREAD_ALL_ACCESS, FALSE, debug_event.dwThreadId );
CONTEXT Context;
Context.ContextFlags = CONTEXT_ALL;
if( GetThreadContext(hThread, &Context) )
Rip = Context.Rip;
MEMORY_BASIC_INFORMATION mbi = { 0 };
VirtualQueryEx( pi.hProcess, exception.ExceptionRecord.ExceptionAddress, &mbi, 0x36 );
TCHAR szMsg[4096];
_stprintf_s( szMsg, _T("Access violation (%016p) at address %016prnMem State:rnBaseAddress=%016prnAllocationBase=%016prnAllocationProtect=%08xrnRegionSize=%08xrnState=%08xrnProtect=%08xrnType=%08x"), exception.ExceptionRecord.ExceptionCode, exception.ExceptionRecord.ExceptionAddress, mbi.BaseAddress, mbi.AllocationBase, mbi.AllocationProtect, mbi.RegionSize, mbi.State, mbi.Protect, mbi.Type );
MessageBox( NULL, szMsg, _T("Exception"), MB_OK );

}
bContinueDebugging = false;
}
break;

default:
break;
}
}
break;
}
ContinueDebugEvent( debug_event.dwProcessId, debug_event.dwThreadId, dwContinueStatus );
}

return 0;
}

AutoCAD 2015: Managing the Application Manager

AutoCAD 2015 includes a new feature called Application Manager. I’m sure it serves a lofty purpose, but it comes across a lot like the slimy Norton and Adobe updaters that are really just Trojans in disguise. It gets installed by default, with no option to prevent installation. To Autodesk’s credit, they do provide instructions for preventing installation of Application Manager, and instructions for removing it after the fact. Uninstalling requires several additional clicks, as if they really, really want you to think twice before taking such a drastic measure.

I don’t want anything starting when I log into Windows except the bare minimum, so I uninstalled Application Manager forthwith. It can be installed and started manually if I decide to use it later.


appmgrsettings

If you decide to use Application Manager, there are some configurable settings. There is even a UI for most of the settings (such as disabling the automatic startup), but there’s a catch: to use the UI for changing settings, you first have to agree to the Autodesk Privacy Statement (and give Autodesk access to information about your installed software). I’m sure this is just an oversight, but the paranoid will not find it comforting.

Application Manager settings are stored in a plain text file, located by default at:

“%AppData%AutodeskAutodesk Application Manager.ini”

If you don’t want to agree to Autodesk’s terms, you can still change settings by editing the .ini file in a text editor like Notepad (just enter the file path above in Start -> Run). For example, change the line to AutoRun=false if you want to disable “Automatically start when you log into the computer”. Note that this setting is somewhat misleading: Application Manager always starts regardless, but it quickly exits again if AutoRun is set to false. If you already agreed to the privacy policy, but have since changed your mind, you can set PrivacyPolicyLevel=0.