Using /delayload to specify dependent DLL path

Let’s say you have an ObjectARX module with an implicit dependency on another DLL. Your installer puts both your ARX module and the dependent DLL into a private application folder. Windows must be able to resolve the implicit DLL dependency at load time, otherwise it will fail to load your ARX module. Alas, Windows will not automatically search for the dependent DLL in your application folder, even though your ARX module is located there. Therefore your ARX module won’t load because the dependent DLL cannot be resolved.

To address this problem, you may be tempted to add your application folder to the AutoCAD support path so that Windows can find your dependent DLL. This is a Very Bad Idea – please don’t ever do it! It imposes a completely unnecessary burden on end users, and it’s not scaleable because it could cause the maximum support path length to be exceeded. A better solution is to change the implicit dependency into an explicit dependency by using the linker’s delayload feature.

Making the change is easy. First, change your ARX project’s linker settings to delayload the dependent DLL:

delayload

Next, implement a delayload hook in one of your source files to explicitly load the dependent DLL from the same folder as your ARX module:

#include <delayimp.h>

#pragma comment(lib, "delayimp")

HMODULE MyExplicitLoadLibrary( LPCSTR pszModuleName )
{
  if( lstrcmpiA( pszModuleName, "MyDependent.dll" ) == 0 )
  {
    CHAR szPath[MAX_PATH] = "";
    //_hdllInstance is the HMODULE of *this* module
    DWORD cchPath = GetModuleFileNameA( _hdllInstance, szPath, MAX_PATH );
    while( cchPath > 0 )
    {
      switch( szPath[cchPath - 1] )
      {
        case '\':
        case '/':
        case ':':
          break;
        default:
          --cchPath;
          continue;
      }
      break; //stop searching; found path separator
    }
    lstrcpynA( szPath + cchPath, pszModuleName, MAX_PATH - cchPath );
    return LoadLibraryA( szPath ); //call with full path to dependent DLL
  }
  return NULL;
}

FARPROC WINAPI MyDliNotifyHook( unsigned dliNotify, PDelayLoadInfo pdli )
{
  if( dliNotify == dliNotePreLoadLibrary )
    return (FARPROC)MyExplicitLoadLibrary( pdli->szDll );
  return NULL;
}
extern "C" PfnDliHook __pfnDliNotifyHook2 = MyDliNotifyHook;

When you use this technique, you do have to ensure that the delayload hook is added before any function in the dependent DLL is called. This is not a problem in most cases, but it could be a consideration if your ARX module initializes global objects whose constructors must call functions in the dependent DLL. This is just one more reason why you should avoid global variables.

QuirkyPolyline: exposing foolish programmers

In AutoCAD, the AcDbCurve class defines a general parametrized curve. On a parametrized curve, points in space are mapped to real numbers (parameters) by an arbitrary function F(p) for values of p from the curve start parameter to the curve end parameter. Defining a curve this way simplifies working with complex 3D curves because one can work in the curve’s one dimensional “parameter space” instead of the more complex three dimensional cartesian space.

The choice for how to parametrize a curve entity is up to the implementor. Parameters are designed to be opaque, so consumers of parametrized curves must make no assumptions about how a specific curve implements them. The only thing guaranteed about the parameters of a curve is that all points on the curve map to a unique parameter in a continuous range from the starting parameter to the ending parameter.

AutoCAD entities such as lines, circles, arcs, polylines, rays, and others are derived from AcDbCurve. These basic curve entities have very simple and straighforward parameter mappings that can be reverse engineered with very little effort, so lazy programmers sometimes make assumptions that are not guaranteed to always be true. Even smart programmers often get tripped up on parametrized curves because they use parameter space when they should be using distance space (that is, distance along the curve). The AcDbCurve interface provides functions to translate from parameter space back and forth to distance space, as well as back and forth to three dimensional cartesian space.

Wise programmers always let the curve itself perform all translations to or from distance space or cartesian space so that their code never relies on the actual value of a parameter. Foolish programmers take shortcuts by making assumptions about parameter values. For example, it happens that in all past versions of AutoCAD, the parameter of the midpoint of a polyline segment is exactly halfway between the segment starting parameter and the segment ending parameter, and the segment starting and ending parameters are always equal to the index number of the corresponding vertex. A foolish programmer (e.g. here and here) may use this knowledge to calculate the midpoint of the segment by guessing it’s parameter. The foolish programmer’s code will fail when a case arises where that guess is wrong. It is the purpose of QuirkyPolyline to provide such a case.

QuirkyPolyline defines a QPOLY command that prompts for a selection set of lightweight polylines in AutoCAD, then converts the selected polylines to “quirky polylines” that expose a completely different parameter scheme. Quirky polylines look and act perfectly normal in all respects. If you save quirky polylines, they will be stripped of their quirkiness. There are no long term effects of making polylines quirky; it’s just a temporary change that wears off on its own.

Making quirky polylines is useful for testing code that works with parametrized curves. If your code makes assumptions that it shouldn’t, quirky polylines will expose the error of your ways. Over the years, I have had arguments with people who insisted that their assumption had been validated by extensive testing. My counterargument is that someone, somewhere (possibly even Autodesk itself) already has or eventually will generate a derived entity where the assumption is not true. In addition to providing a proving ground, QuirkyPolyline is designed to demonstrate to the naysayers that unusual parametrization implementations can exist in the wild.

The download contains ARX modules for AutoCAD 2000 through AutoCAD 2015. To use QuirkyPolyline, just load the module that matches your version and architecture of AutoCAD. Create a lightweight polyline with the PLINE command, then use the QPOLY command to convert the normal polyline to a quirky polyline. You can use the LIST command to verify that it did become quirky, but you should notice no other detectable changes.

[Note: the download has since been updated and moved to the ARX freebies page]

Here is a useful little lisp function that defines a PP command for displaying the parameter value of a picked point on a selected curve entity. You can use this to explore other curve entities, and to see how the polyline’s parametrization changes after it becomes quirky.


(vl-load-com)
(defun C:PP (/ sel pointoncurve param)
(if (setq sel (entsel))
(progn
(setq pointoncurve (apply 'vlax-curve-getclosestpointto sel))
(setq param (vlax-curve-getparamatpoint (car sel) pointoncurve))
(princ (strcat "nParam = " (rtos param)))
)
)
(princ)
)

I hope this helps you avoid the pitfalls of parametric curve code. Remember, don’t be a foolish programmer!

Polymorphic bit flags in C++

I often encounter cases in ObjectARX programming where numerous boolean flags need to be persisted as part of an AutoCAD database object. For filing and for passing around to other functions, it’s most efficient to package those bit flags into a single unsigned integer. Below is an example that demonstrates how to use a union inside a containing class that can be simultaneously used as either a single unsigned integer or as individual booleans.


// first declare a compact bitflag structure as bool values
struct FlagsAsBools
{
bool flag1 : 1;
bool flag2 : 1;
bool flag3 : 1;
bool flag4 : 1;
bool flag5 : 1;
bool flag6 : 1;
bool flag7 : 1;
bool flag8 : 1;
bool flag9 : 1;
bool flag10 : 1;
bool flag11 : 1;
bool flag12 : 1;
};

// now the containing class with useful constructors and operators
class Flags
{
protected:
// and the hidden union that actually contains the flags
union _Flags
{
FlagsAsBools asBools;
Adesk::UInt32 asUInt; //space for up to 32 flags
} mFlags;
public:
Flags()
{
mFlags.asUInt = 0;
}
Flags(Adesk::UInt32 flags)
{
mFlags.asUInt = flags;
}
Flags(const FlagsAsBools& flags)
{
mFlags.asBools = flags;
}
operator Adesk::UInt32 () const { return mFlags.asUInt; }
const FlagsAsBools& asBools() const { return mFlags.asBools; }
FlagsAsBools& asBools() { return mFlags.asBools; }
};

// now a class that contains the flags
class Settings
{
AcString msSomeStringSetting;
bool mbSomeBoolSetting;
Flags mFlags;
public:
Settings () : mbSomeBoolSetting(false) {}

LPCTSTR someStringSetting() const { return msSomeStringSetting; }
void setSomeStringSetting(LPCTSTR someStringSetting) { msSomeStringSetting = someStringSetting; }
bool someBoolSetting() const { return mbSomeBoolSetting; }
void setSomeBoolSetting(bool someBoolSetting) { mbSomeBoolSetting = someBoolSetting; }
Flags flags() const { return mFlags; }
void setFlags(Flags flags) { mFlags = flags; }
};

// now use the flags as an argument to a function in different forms
void DoStuffBasedOnFlags( Adesk::UInt32 flags )
{
if( Flags(flags).asBools().flag7 )
{// do something when flag7 is set
}
}

void DoOtherStuffBasedOnFlags( const Flags& flags )
{
if( flags.asBools().flag1 )
{// do something when flag1 is set
}
}

void TestIt()
{
Settings& MySettings = GetSettings();
Flags flags = MySettings.flags();
if( flags.asBools().flag4 )
{// do something when flag4 is set
}
DoStuffBasedOnFlags( flags );
DoOtherStuffBasedOnFlags( flags );
flags.asBools().flag2 = true;
MySettings.setFlags( flags ); //save the new settings
}

Checking function return values

A lot of you are guilty of not checking return values from API function calls. I’ll bet you have a good excuse, like “it’s a lot of extra typing for no reason”, or “that function should never return an error code”.

I check return values religiously, use asserts liberally, and never assume that the implementation details of a function won’t change in the future. I think you should do the same.

In all my years of programming, I’ve never heard someone complain that their project failed because they spent too much extra time typing code to verify return values — but I have heard of projects failing because sloppy code made it almost impossible to diagnose and fix problems.

I see a lot of “sample code” posted on the internet in forums and blogs that omit error checking for brevity or to make the code clearer to read. That’s a bad idea, in my opinion. Sample code should be good code, not just clean code.

I will grant you that there are cases where error checking is not needed. For example, in the following code there is no need to verify the result of the call to acdbGetObjectId() because failure will be immediately evident in the call to acdbOpenObject() when idSelected is still null:

Acad::ErrorStatus MyFunction()
{
ads_name anameSelected;
ads_point apointSelected;
int nResult = acedEntSel( ACRX_T(“nSelect an object: “), anameSelected, apointSelected );
if( nResult != RTNORM ) return eNotAnEntity; //or whatever
AcDbObjectId idSelected;
acdbGetObjectId( idSelected, anameSelected ); //ignoring return value!
assert( !idSelected.isNull() ); //check it nevertheless in the debug build
AcDbEntity* pEnt = NULL;
Acad::ErrorStatus es = acdbOpenObject( pEnt, idSelected, AcDb::kForRead );
if( es != Acad::eOk ) return es;
// do something with pEnt
pEnt->close();
//I confess that I don’t bother checking the
//result of AcDbObject::close() unless it matters
return Acad::eOk;
}

There are legitimate cases where error checking is not needed, but I think especially sample code posted on the internet should encourage good programming habits by checking the result of every function that returns one.

Registering an ARX/BRX module as a COM server

If you’re developing ARX modules that need to be registered as a COM server, you’re faced with some decisions about how to register them. In the old days before anyone cared about user permissions, registration could be safely accomplished at runtime, even via AutoLISP. Unfortunately runtime COM server registration just doesn’t work reliably any more under limited user accounts, not to mention registry redirection on 64-bit platforms. There is only one reliable way to register COM servers, and that’s doing it at install time under elevated privileges.

A normal Windows application might accomplish install-time registration by calling RegSvr32 after the files are copied to the destination folder, but this doesn’t work with ObjectARX or BRX modules. The reason it doesn’t work is because ARX modules cannot be loaded outside the AutoCAD or Bricscad process. RegSvr32 will fail with errors like “The module <filename> failed to load” or the even less helpful “The specified module could not be found.” There are some kludgy ways to get RegSvr32 to work anyway, but the simplest and most reliable way to solve this problem is to forget RegSvr32 and just add the needed registry values manually into your installer script. It’s not difficult, though it might be time consuming initially if your server implements a lot of COM objects.

At a minimum, a COM server must register at least one COM class under HKCRCLSID. If the COM server needs to support OLE Automation (e.g. VBA, AutoLISP) it must register a type library in HKCRTypeLib. In most cases, this is all that is needed, however you may also need to register a “ProgID” in HKCR if a script or in-process module needs to be able to connect to the server via human-readable ProgID.

Most installation script software can import Windows registry script (.reg) files, so it’s often helpful (and more maintainable) to create a registry script file manually, then simply import the .reg file into the installation script. If you use Visual Studio deployment projects, this is the only way to batch import registry values into the installation script. I’ve provided a minimal template for a registry script that registers a single COM object and ProgID. The template uses special characters as placeholders for the actual values: $$ is the human readable ProgID name, ## is the COM object’s CLSID, ** is the filename of your ARX module, and %% is the type library GUID. The type library and object class version numbers should match your actual version numbers as well, and of course [TARGETDIR] is the installation target directory that will be resolved at install time.

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT$$.1]
@=”$$ Class”

[HKEY_CLASSES_ROOT$$.1CLSID]
@=”{##}”

[HKEY_CLASSES_ROOTCLSID{##}]
@=”$$ Class”

[HKEY_CLASSES_ROOTCLSID{##}InprocServer32]
@=”[TARGETDIR]**.arx”
“ThreadingModel”=”Apartment”

[HKEY_CLASSES_ROOTCLSID{##}ProgID]
@=”$$.1″

[HKEY_CLASSES_ROOTCLSID{##}Programmable]

[HKEY_CLASSES_ROOTCLSID{##}TypeLib]
@=”{%%}”

[HKEY_CLASSES_ROOTCLSID{##}VersionIndependentProgID]
@=”$$”

[HKEY_CLASSES_ROOTTypeLib{%%}]

[HKEY_CLASSES_ROOTTypeLib{%%}1.0]
@=”$$ 1.0 Type Library”

[HKEY_CLASSES_ROOTTypeLib{%%}1.0]

[HKEY_CLASSES_ROOTTypeLib{%%}1.0win32]
@=”[TARGETDIR]**.arx”

[HKEY_CLASSES_ROOTTypeLib{%%}1.0FLAGS]
@=”0″

[HKEY_CLASSES_ROOTTypeLib{%%}1.0HELPDIR]
@=”[TARGETDIR]”

It’s really not that complicated for a single module. The work can be tedious to create all the values for multiple modules in scenarios where you support multiple AutoCAD and/or Bricscad versions or platforms, but these values typically don’t change over the life of the project, so it only needs to be done once. Once you have created the registry values manually, disable all other forms of COM server registration such as wizard-generated runtime registration or automatic “self-registration” at install time. A bonus side effect of the manual registration approach is that a normal uninstall reliably removes all traces of the COM server registration.