|
This page documents some ObjectARX programming tips and tricks that
I've learned over the years. Most of the information presented here is
the result of my own programming work, but some input comes from
discussions on newsgroups or other forums. Contributors are listed at the end of each
page, and their contributions are denoted with a
superscripted number in brackets (e.g. [1])
in the area where they contributed information.
NOTICE:
The contents of this page are Copyright 2004 - 2007
ManuSoft, All Rights Reserved. The contents of this page
may not be reproduced under any circumstances without
permission from the author! Permission to print one
hardcopy of this page for individual use is hereby
granted.
DISCLAIMER:
ManuSoft is in no way responsible for either the accuracy
or completeness of information on this page! Neither
ManuSoft, Owen Wengerd, nor any credited or uncredited
contributors may be held liable for any damages which may
result either directly or indirectly from the use of
information on this page! In other words, use at
your own risk!
To make it less confusing when working with multiple versions of
ObjectARX, I recommend removing the ObjectARX SDK folders from your
global VC settings, and adding them instead to the project settings for
each individual project. Doing so gives you more local control over the
project, and makes it less confusing when you need different build
configurations to use different versions of ObjectARX in the same
project.
I highly recommend setting up system environment variables to point
to the locations of various versions of the ObjectARX SDK, as well as
different versions of the MFC and C/C++ runtime libraries. This allows
your build settings to specify the locations of include and library
files using environment variables, thus making your projects much more
portable across platforms and directory structures.
For example, I have every version of the ObjectARX SDK since ARX 1.0
installed on my system, and I have an environment variable pointing to
the root of every SDK version, as follows:
| Variable |
Value |
Description |
ARX1 |
E:\SDK\ARX1 |
ARX 1.0 SDK (R13) |
ARX2 |
E:\SDK\ARX202 |
ObjectARX 2.02 SDK (R14) |
ARX2000 |
E:\SDK\ARX2000 |
ObjectARX 2000 SDK |
ARX2000i |
E:\SDK\ARX2000i |
ObjectARX 2000i SDK |
ARX2002 |
E:\SDK\ARX2002 |
ObjectARX 2002 SDK |
ARX2004 |
E:\SDK\ARX2004 |
ObjectARX 2004 SDK |
ARX2005 |
E:\SDK\ARX2005 |
ObjectARX 2005 SDK |
ARX2006 |
E:\SDK\ARX2006 |
ObjectARX 2006 SDK |
ARX2007 |
E:\SDK\ARX2007 |
ObjectARX 2007 SDK |
ARX2008#32 |
E:\SDK\ARX2008.32 |
ObjectARX 2008 (x86) SDK |
ARX2008#64 |
E:\SDK\ARX2008.64 |
ObjectARX 2008 (x64) SDK |
The same thing should be done with other SDK code, including common
tool libraries that are designed and maintained internally. In addition
to the ObjectARX SDKs, I also have environment variables pointing to
each version of Visual C/C++. Those are named MSVC2,
MSVC4, MSVC42, MSVC5, MSVC6, MSVC7,
MSVC71, and MSVC8, and each environment variable points to the root
of the library (i.e. the VC98 folder for VC6, and the VC7 folder for
VC7).
Once these environment variables are set up, you can refer to
specific folders for specific SDKs in the VC6 and VC7 project settings
by using the notation $(MSVC6)\Include (to point the VC6
C/C++ runtime include files) or $(MSVC42)\MFC\Lib (for
VC4.2 MFC library files). The goal is to completely eliminate hardcoded
paths from your project settings, thus making the projects portable. In
addition to making the projects portable, this approach makes dependent
SDKs portable as well. Moving an SDK to a different location or
installing an updated SDK in a new location becomes as simple as
changing an environment variable and rebuilding the project.
Assuming environment variables have been set to point to each version
of VC, the following strings specify include and library paths for each
version of VC:
| Version |
Include Path |
| Library Path |
| VC 4.2 |
$(MSVC42)\Include;$(MSVC42)\MFC\Include |
$(MSVC42)\Lib;$(MSVC42)\MFC\Lib |
| VC 6 |
$(MSVC6)\Include;$(MSVC6)\MFC\Include;$(MSVC6)\ATL\Include |
$(MSVC6)\Lib;$(MSVC6)\MFC\Lib;$(MSVC6)\ATL\Lib |
| VC 7 |
$(MSVC7)\Include;$(MSVC7)\ATLMFC\Include |
$(MSVC7)\Lib;$(MSVC7)\ATLMFC\Lib |
| VC 7.1 |
$(MSVC71)\Include;$(MSVC71)\ATLMFC\Include |
$(MSVC71)\Lib;$(MSVC71)\ATLMFC\Lib |
| VC 8 |
$(MSVC8)\Include;$(MSVC8)\ATLMFC\Include |
$(MSVC8)\Lib;$(MSVC8)\ATLMFC\Lib |
| VC 9 |
$(MSVC9)\Include;$(MSVC9)\ATLMFC\Include |
$(MSVC9)\Lib;$(MSVC9)\ATLMFC\Lib |
With the advent of AutoCAD 2004 and Visual Studio .NET, ObjectARX
programmers are again faced with the task of moving from an older
version of a compiler (VC6) to a newer version (VC7). Rather than
maintain two versions of compilers and two versions of projects for
supporting both AutoCAD 2000/2002 and the new AutoCAD 2004, it makes
sense to use only the newest IDE to support *all* target AutoCAD
platforms.
You can easily add build configurations in to your project VC7 so that the
same project builds ObjectARX modules for both AutoCAD 2000/2002, and
AutoCAD 2004, but the trick is to force VC7 to build your AutoCAD
2000/2002 target with the correct headers and libraries. In addition,
you have to make sure your source code compiles correctly with both old
and new ObjectARX SDK versions. This will likely require some
conditional compilation if your code uses parts of the SDK that have
been modified in ObjectARX 2004.
Following are some tips for successfully building ObjectARX 2000/2002
projects in VC7.
- Don't put ObjectARX SDK folders into your global settings. Use the
procedure outlined elsewhere on this page
to define search paths in your project properties using environment
variables.
- Disable 'Edit and Continue' in all your AutoCAD 2000/2002 target
builds. Go to
project properties C/C++->General and set 'Debug Information Format'
to anything except 'Program Database for Edit & Continue' (normally
Debug builds should use 'Program Database' and Release builds should
use 'Disabled'). Next, go to
Linker->General and set 'Enable Incremental Linking' to 'No'. If you
leave this option enabled, the linker pads the output file with
uninitialized empty space, which causes the module validity checking
in AutoCAD 2002 to choke on the file and fail to load it. Note that if
you link to any libraries that were built with 'Program Database for
Edit & Continue' enabled, the linker will warn that the /EDITANDCONTINUE
switch was ignored. Just ignore the warning, or rebuild the offending
library.
- In your project properties, specify explicit paths for include
files and library files to the correct version of ObjectARX and the
VC6 version of the C/C++ runtime. Following is an example that shows
the settings for include directories (if this project used MFC, it
would also list
$(MSVC6)\MFC\Include). You must do the
same thing in settings under Linker->General for 'Additional Library
Directories', except you must specify the library directory (e.g.
$(ARX2000)\Lib and $(MSVC6)\Lib).

- VC7 includes a new feature that inserts runtime buffer overrun
checks into your Release builds by default. This runtime check uses a
VC7 runtime library function that is not available in VC6, so you need
to disable the feature when linking with VC6 libraries. To disable it,
go to project properties and select the desired configurations, then
navigate to settings for C/C++->Code Generation. Change the setting
for 'Buffer Security Check' to 'No'*.
Changing this setting prevents the following linker errors:
error LNK2019: unresolved external symbol ___security_cookie
referenced in...
error LNK2019: unresolved external symbol @__security_check_cookie@4
referenced in... * The current Microsoft Platform SDK now
includes a separate library that provides implementations for these
functions. Just link to bufferoverflowu.lib to resolve them correctly,
with no need to turn off this new compiler feature (see
KB894573).
- In VC6, casting a floating point number to a long caused the
compiler to generate a call to a hidden library function named _ftol.
VC7 generates a call to _ftol2 (obviously the implementation of this
function changed, so the name was changed to prevent version clashes).
If your source code includes a cast from double to long types, VC7
will generate a call to _ftol2, and the following linker error will
result:
error LNK2001: unresolved external symbol
__ftol2
Resolve this error by placing the following in one of your .cpp files
(StdAfx.cpp, for example):
#if (_MSC_VER >= 1300) && (_MSC_VER < 1400) && (WINVER < 0x0500)
//VC7 or 7.1, building with pre-VC7 runtime libraries
extern "C" long _ftol( double ); //defined by VC6 C libs
extern "C" long _ftol2( double dblSource ) { return _ftol( dblSource
); }
#endif
- If you use the /DELAYLOAD linker feature to delay-load implicitly
linked
DLLs, you will encounter the following linker errors when linking to
VC6 libraries:
error LNK2001: unresolved external symbol
___delayLoadHelper2@8
error LNK2001: unresolved external symbol
___delayLoadHelper2@8
These errors are caused by a change in the delay load helper
function in VC7, that was in turn the result of a change in the delay
load import descriptor that the linker embeds inside the image file. Microsoft renamed the function to
prevent the possibility of the new format produced by the VC7 linker
being linked with the old helper function (that expects the old
format). Unfortunately, AutoCAD's [brain dead] module validity testing
chokes on the new delay load import descriptor format.
The solution to this problem is twofold: first make the
linker happy by linking to the new VC7 delayimp.lib; second,
postprocess the linker-created .arx module to hide the delay load
import descriptor so AutoCAD can't choke on it.
- The first part of the solution involves renaming the VC6
delayimp.lib file* so VC7 doesn't find that one, and uses it's own
version instead. The file is located in VC98\Lib. I renamed mine to
VC6.delayimp.lib.
* Arnold Eibel sent me an email with a better solution: instead of
renaming the VC6 delayimp.lib, just specify an explicit path to the
correct file to use. Arnold's suggestion is to use "$(VcInstallDir)lib\delayimp.lib",
thus making the path portable to future versions of VC.
- The second part of the solution is not as difficult as it sounds. I made a command line utility to remove the delay load import
directory from the file. This won't affect the file's operation at
all, but it will prevent PE file viewers (such as
Dependency
Walker) from being aware of the delay loaded dependencies. Simply
download the utility
(165k) and place it somewhere in your VC7 support path (i.e.
VC7\Bin) or better yet put it in its own folder and add the new
folder to the path for executables in VC7 global options. Next, add
a post build step to run the conversion utility on the target
module. Do this in settings for Build Events->Post-Build Event, and
add this to the 'Command Line' setting:
HideDelayLoadImports.exe "$(TargetPath)"
These are the problems I have encountered so far in my conversion
process. As I encounter new problems, I will add my solutions to this
list of tips. If you have additions or comments, please don't hesitate
to
contact me via email (but please understand that sometimes I can't
reply to every email I receive).
Despite the minor numerical change from Visual C/C++ version 7 to
7.1, Microsoft made some major changes in the compiler and linker in
order to become more C++ standard compliant. These changes make it
virtually impossible to use 7.1 to build for ObjectARX 2004 and earlier
ObjectARX versions. The new
compiler chokes on the earlier versions of ATL and MFC libraries because
they were not standard compliant, yet these are required when building
for ObjectARX because of its reliance upon Autodesk libraries. What's
more, the tricks outlined above are not
enough to solve this problem.
The new features in the VC 7.1 IDE (not to mention the bug fixes) make it
more desirable than VC 7. For that reason I
wrote VC Build Hook,
a small add-in for VC 7.1 that solves the compiler version
problem by automatically switching the environment path to the VC 7
build tool directories at build time. This causes the build process to
use the VC 7 build tools instead of the incompatible ones that shipped
with VC 7.1. Note that you must have a working Visual C/C++ 7 (Visual
Studio .NET 2002) installation in order
for the path switching to work. If you only have Visual C/C++ 7.1
available, you must purchase the downgrade to Visual C/C++ 7 and install
it also, otherwise the required VC 7 build tools won't be available on
your system. This feature can be enabled for an entire solution or for
individual projects.
See the following section for information about using the VC Build
Hook utility.
I created the VC Build Hook utility in order to make it possible to
use the current Visual Studio IDE for building ObjectARX applications
that target any version of AutoCAD. This is desirable because ObjectARX
applications must be built by the Visual C/C++ build tools that
correspond to the ObjectARX SDK version for the target AutoCAD version.
VC Build Hook works by switching the executables path during a Visual
Studio build and pointing the paths to the desired version of the Visual
C/C++ build tools. In addition, VC Build Hook automatically fixes build
tool command options to account for differences between VC versions so
that build commands produced by one version of Visual Studio are
correctly interpreted by another version of the build tools. Assuming you've
already set up your projects as described
above (note that you must have a working installation of
the necessary version of Visual C/C++ build tools and runtime libraries), then you'll need to
install the current version of VC Build Hook:
Download: VCBuildHookSetup.msi
(278k)
Version 3.0.0.2 uploaded 2007-12-02 (supports VC 7.1, VC 8,
and VC 9)
Once the VC Build Hook utility is installed, it's just a matter of making some changes in
your project settings to ensure that your build succeeds. First, you
must ensure that the correct headers and libraries are included in your
project settings as outlined above.
Second, you must set the VC Build Hook extended properties for your
projects to use the correct build tools.
When VC Build Hook is started, Visual Studio solutions will
have two new extended properties: LowPriorityBuild and BuildToolVersion.

The LowPriorityBuild property is only available for solutions and
can be either 'Yes', 'No', or 'NotSet'. Currently, VC Build Hook
makes all builds low priority unless this property is set to 'No'. A low
priority build means that other tasks will receive more processor time
during a build (at the expense of build time, of course). The
BuildToolVersion property is available for solutions or projects,
and may be set to 'VC7', 'VC8', or 'Default'. A setting of 'Default' for
projects causes the solution property to be inherited. If the project
and solution are both set to 'Default', then the build tool paths will
not be affected and Visual Studio will use the default build tools.
When using Visual Studio 2005 or later IDE:
- Disable parallel builds! The VC Build Hook utility does
not work correctly if two simultaneous builds are using different
settings, so you should disable this feature in Visual Studio
global options. To do this, go to Tools->Options, open
'Projects and Solutions', select the 'Build and Run' tab, and change
the 'maximum number of parallel project builds' to 1. In my
experience, Visual Studio is more stable when parallel builds are
disabled, so there is a benefit to disabling this feature that is
unrelated to VC Build Hook.
When using the VC 7 build tools:
- Disable the Manifest Tool. This is easy to do, but not obvious
or well
documented. Right-click on a project and select 'Tool Build
Order...', then select 'All Configurations' in the configuration
dropdown and dismiss the warning dialog, then uncheck the box beside 'Manifest Tool'.
Using the IDE, you have to do this one project at a time.
- In linker settings, on the 'Manifest File' tab, set 'Generate
Manifest' to 'No'. This disables manifest file generation.
- Disable error reporting. In C/C++ settings, on the 'Advanced'
tab, set 'Error Reporting' to 'Default'. Do the same in linker
settings, on the 'Advanced' tab.
- Don't use Unicode response files. Change this in C/C++ settings
on the 'General' tab by setting 'Use UNICODE Response Files' to
'No'. Do the same in linker settings, also on the 'General' tab. The
VC Build Hook utility will automatically convert Unicode response
files to ANSI files if they are being sent to a VC 7 build tool, so
this step is not strictly required, however the build will be
slightly less efficient if the files need to be converted.
- If your project uses precompiled headers, there is one additional issue
to deal with. Visual Studio 2005 removed support for the "Automatically
Generate" (/YX) precompiled header option. This is not a problem when
using the VC 8 or later build tools, but when using the VC 7 build tools, it
can make the build process unbearably slow. Luckily it's not difficult
to re-enable automatic use of precompiled headers when building with VC
7 build tools. To re-enable this feature in a project that is using
the VC 7 build tools, you need to edit the 'Precompiled Header' tab of the
C/C++ settings. First, make sure precompiled headers are disabled
('Create/Use Precompiled Header' should be set to 'Not Using Precompiled
Headers'), then set the 'Precompiled Header File' to '
$(IntDir)\$(TargetName).pch'
for all configurations, then go to the 'Command Line' tab and add
'/YXStdAfx.h' to the 'Additional options' field at the bottom.
Voila! Precompiled headers should now work just as they did when
building with the Visual Studio .NET 2003 IDE.
I need to stress again that these steps only work
without error after you have
implemented the practices I've discussed in
the previous sections.
A common complaint among ObjectARX programmers is that Visual C/C++
doesn't display IntelliSense context menus for classes defined in the
ObjectARX headers. The trick is to include all the ObjectARX headers
files somewhere in your solution. I created a new folder called
ARXHeaders with subdirectories for every version of the ObjectARX
SDK:
ARXHeaders
ARX2
ARX202
ARX2000
ARX2000i
ARX2002
ARX2004
ARX2005
ARX2006
ARX2007
ARX2008
In each subdirectory, create a new empty static library project named
e.g. 'ARX2007' and add every header file (*.h) from that version of the ObjectARX SDK to the project. It's important to give each project a
unique name so that the project names don't collide when more than one
is added to a single solution. Since there are no source files, there is
no output file for these projects, and building them is a no-op.
Nevertheless, adding one of these projects to an existing solution
causes VC to "see" the ObjectARX classes and code definitions for use in
IntelliSense. Another benefit of including the ObjectARX headers in the
solution is the ability to use the class browser in VC to browse the
ObjectARX SDK. Browsing can reveal some interesting undocumented
functions, and it also proves very useful for providing a readable
overview of class hierarchies and object definitions.
In projects that target multiple versions of AutoCAD (and therefore
use multiple versions of the ObjectARX SDK) you can add the ARXHeaders
project for each target version of ObjectARX. While adding multiple SDK
versions won't tell VC which version of the SDK is used in a given
source file or project, it does make each version separately available
in the VC class browser for easy browsing.
The registry demand loading feature in AutoCAD is a great method for
loading ObjectARX modules when AutoCAD starts, and it's relatively easy
to create the demand loading settings from an installation program or
MSI file at install time. Unfortunately it only works for .arx files
(and also with .NET dlls since AutoCAD 2006). Some years ago, I created a
small ObjectARX module called LoadVLX. The purpose of this module was to
work as a stub to load an associated .vlx file of the same base name in
the same folder. The idea is to rename the VLXLoad.arx to the name of
the associated .vlx file (i.e. MyApp.arx will look for and load
MyApp.vlx in the same directory), then create demand load registry
settings to load the renamed .arx file. Voila, the associated .vlx file
gets loaded as if it was being demand loaded from the registry.
The VLXLoad source code demonstrates an important technique that I
often see questions about in Autodesk's ObjectARX discussion group: how
to get the full path to the .arx file from code within the file. The
secret is to use the Windows API function GetModuleFileName(), passing
as its first argument the current module instance handle, which is
available in an ObjectARX module as _hdllInstance (note that it's _h,
two lower case L's, then followed by an upper case I for Instance).
To make things easy, I've uploaded here the complete source code
along with solution files for both Visual Studio 2002 (AutoCAD 2000 -
2006) and Visual Studio 2005 (AutoCAD 2007 - 2008). These solutions should
build with no changes so long as you've defined environment variables as
described elsewhere on this page. The
modules for each target AutoCAD version are built with the same name, so
be careful not to get them confused. If you look at the source code,
you'll notice that they also define a lisp callable function named (GetPathOf<basefilename>)
that returns the directory from where the .arx module loaded. This also
demonstrates another technique: defining an ADS (Lisp callable) function
by "calculating" the function name at runtime.
Download: VLXLoadSource.zip (87k)
Version 1.0.0.1 uploaded 2006-12-04
If you just want the .arx modules, download the latest versions from
my Freebies page. Note
that the original binaries on the Freebies page do not contain file
version information, and they are built completely in Visual Studio 2005
with the help of my VCBuildHook utility.
<None>
|