ObjectARX 2010: Dealing With Missing Exports

In the new ObjectARX 2010 SDK, Autodesk has added some new virtual member functions that are not exported as they should be. For example, the AcGiFaceData class has had two new virtual functions added for setting and getting the face transparency: class AcGiFaceData: public AcRxObject { //[… deleted for brevity] ACDB_PORT virtual AcDbObjectId materials() const; ACDB_PORT virtual AcGiMapper mappers() const virtual void setTransparency(const AcCmTransparency transparency); virtual AcCmTransparency transparency() const private: AcGiImpFaceData mpAcGiImpFaceData; }; As you can see, whoever added the new functions neglected to prefix them with the ACDB_PORT macro. ACDB_PORT evaluates to __declspec(export), which tells the compiler to export the function. Since the macro is missing, the new functions are not exported from acdb18.dll. Since these are virtual functions, you won’t have any problems calling them through a pointer to an AcGiFaceData object that was constructed by AutoCAD. The problem arises when you derive a class from AcGiFaceData. Since the functions are not exported, the linker has no way of resolving their address for creating the virtual function table of your derived class. This results in linker errors: acrxEntryPoint.obj : error LNK2001: unresolved external symbol “public: virtual void __thiscall AcGiFaceData::setTransparency(class AcCmTransparency const )” (?setTransparency@AcGiFaceData@@UAEXPBVAcCmTransparency@@@Z) acrxEntryPoint.obj : error LNK2001: unresolved external symbol “public: virtual class AcCmTransparency * __thiscall AcGiFaceData::transparency(void)const " (?transparency@AcGiFaceData@@UBEPAVAcCmTransparency@@XZ)Following is an example that results in these errors: class AcGiFaceDataEx: public AcGiFaceData { public: AcGiFaceDataEx() {} ~AcGiFaceDataEx() {} } Test; The only solution is to provide an implementation of the missing functions. In this case, it could be accomplished by something like this: class AcGiFaceDataEx: public AcGiFaceData { AcCmTransparency mpTransparency; public: AcGiFaceDataEx() : mpTransparency( NULL ) {} ~AcGiFaceDataEx() { delete mpTransparency; } virtual void setTransparency(const AcCmTransparency transparency) { delete mpTransparency; mpTransparency = (transparency? new AcCmTransparency( transparency ) : NULL); } virtual AcCmTransparency transparency() const { return mpTransparency; } } Test;This will fix the linker errors, but there is no guarantee that it will work as intended. AutoCAD might access its internal transparency value directly without calling through the member functions, which means it would never “see” the transparency set through the replacement member functions. Furthermore, the addition of the new pointer member changes the size of the class, which causes AcGiFaceDataEx arrays to have a different memory footprint than AcGiFaceData arrays. Lastly, what if Autodesk fixes the problem in a future AutoCAD service pack? The ideal solution should not change the size of the class. It should check at runtime whether the function is exported, then use the exported function if it exists. That way, code that is written now will use the exported function if and when it becomes available in a future version of AutoCAD. When the function is not exported, an alternate implementation must be provided. This is not an unusual scenario, and the solution I present for the specific case of AcGiFaceData can be adapted to the more general problem. In the AcGiFaceData case, the missing functions are virtual functions. Knowing this, it is possible to use a trick to get the address of the real function. In the code below, the function getAcGiFaceData_vtable() constructs a temporary AcGiFaceData object, from which it extracts a pointer to the object’s virtual function table. The virtual function table is just an array of function pointers, so the address of the desired function can be obtained by indexing into the virtual function table. The question is, how far? By counting virtual functions and data members starting from the top of the class hierarchy: in this case, 6 virtual functions in AcRxObject plus 16 virtual functions in AcGiFaceData = 22. Note that obtaining a function pointer this way relies on Visual C++ implementation details, but this is safe to do since all ObjectARX modules must be compiled in Visual C++. Following is my solution to the missing AcGiFaceData functions: #pragma warning(push) #pragma warning(disable: 4608) template < typename Src, typename Dest > Dest force_cast( Src src ) { union _convertor { Dest d; Src s; _convertor() : d(0), s(0) {} } convertor; convertor.s = src; return convertor.d; } #pragma warning(pop) static FARPROC getAcGiFaceData_vtable() { static FARPROC rfVTable = (FARPROC**)&AcGiFaceData(); return (rfVTable? rfVTable : NULL); } void AcGiFaceData::setTransparency( const AcCmTransparency transparency ) { typedef void (AcGiFaceData::F_setTransparency)( const AcCmTransparency ); static F_setTransparency pfSetTransparency = force_cast< FARPROC, F_setTransparency >(GetProcAddress( GetModuleHandleA( “acdb18.dll”), “?setTransparency@AcGiFaceData@@UEAAXPEBVAcCmTransparency@@@Z” )); if( !pfSetTransparency ) { static FARPROC rfVTable = getAcGiFaceData_vtable(); if( rfVTable ) pfSetTransparency = force_cast< FARPROC, F_setTransparency >( rfVTable[22] ); } if( pfSetTransparency ) (this->pfSetTransparency)( transparency ); } AcCmTransparency AcGiFaceData::transparency() const { typedef AcCmTransparency (AcGiFaceData::F_transparency)() const; static F_transparency pfTransparency = force_cast< FARPROC, F_transparency >(GetProcAddress( GetModuleHandleA( “acdb18.dll”), “?transparency@AcGiFaceData@@UEBAPEAVAcCmTransparency@@XZ” )); if( !pfTransparency ) { static FARPROC rfVTable = getAcGiFaceData_vtable(); if( rfVTable ) pfTransparency = force_cast< FARPROC, F_transparency >( rfVTable[23] ); } if( pfTransparency ) return (this->*pfTransparency)(); return NULL; }

March 28, 2009 · 4 min · Owen Wengerd

Debugging ObjectARX: Break on Exception

I have presented a class entitled High Octane ObjectARX at Autodesk University the past two years. In 2006 the focus was on project organization, and last year I focused more on techniques for supporting multiple versions of AutoCAD with a single Visual Studio solution, touching briefly on testing and profiling. For 2008 I plan to focus on debugging. ...

May 12, 2008 · 2 min · Owen Wengerd

VC Build Hook utility for Visual Studio 2008

The new Visual Studio 2008 has been released, and to celebrate, I’ve updated VC Build Hook. The updated utility allows you to use the new IDE while still building with older versions of the build tools. Why is this necessary? If you build your AutoCAD 2007/2008 ObjectARX application with the Visual C/C++ 9.0 tools, AutoCAD will display a warning message about an incompatible module when you try to load it. You can use VC Build Hook to target all previous AutoCAD versions from a single Visual Studio 2008 solution (if you have the correct version of Visual C/C++ installed alongside Visual Studio 2008). In my testing so far, existing Visual Studio 2005 projects build without problems in Visual Studio 2008 when VC Build Hook is installed and the new ‘BuildToolVersion’ property is set to the correct version.

November 26, 2007 · 1 min · Owen Wengerd

Visual Studio Build Configuration Tip

I’ve received several emails recently from users of my VC Build Hook utility that mention switching the ‘UseVC7Paths’ setting on and off depending on the build configuration they are working on. If you are doing this, you need to change your project structure! ...

June 15, 2007 · 2 min · Owen Wengerd

My ManuSoft manifest manifesto

With the advent of Visual Studio 2005 and automatically generated manifest files, the topic of when and how to use manifest files comes up occasionally. Since a default ObjectARX wizard generated project in Visual Studio 2005 generates an embedded manifest by default, most people don’t even think about it. Of course that was the whole idea – the manifest would ensure that the correct dependent DLLs get loaded, and voila, no need to think about it. One of my programming axioms applies in this case: TANSTAAFL. The problem is that ObjectARX applications are not in charge. AutoCAD is in charge, and it will decide which VC runtime and MFC runtime DLLs to load. If your ObjectARX application loads a different VC or MFC runtime than the ones AutoCAD is using, you’ll encounter big problems. I’ve always advised disabling the manifest completely (see my ObjectARX Tips page) to avoid such a possibility. Unfortunately, there’s a catch. The problem is that linking to the ATL80.DLL file isn’t possible by just disabling the manifest and doing nothing more. ATL is now a side-by-side (SxS) assembly, and it no longer lives on the Windows support path, so a standard un-manifested DLL won’t be able to find it. The preferred solution is to link statically to ATL (see Configuration -> General -> ‘Use of ATL’ in VS 2005 project properties) and avoid the problem altogether. If you only have one module that uses ATL, this is always the best solution. The less desirable solution is to add a manually-created manifest that specifies the desired ATL SxS assembly, but ignores the VC and MFC runtime DLLs. You’ll still need to decide whether to make ATL shared or private, and in either case you must distribute ATL with your application to ensure that it is available when your application is deployed. You can cheat, and let VS generate your manifest file (instruct it to not embed the file), then just edit the resulting .manifest file to remove references to the VC and MFC assemblies. For example, here’s one that I generated (you’ll need to generate your own to ensure that the manifest matches the version you are redistributing): <?xml version=“1.0” encoding=“UTF-8” standalone=“yes”?> <assembly xmlns=“urn:schemas-microsoft-com:asm.v1” manifestversion=“1.0”> <dependency> <dependentassembly> <assemblyidentity type=“win32” name=“Microsoft.VC80.ATL” version=“8.0.50727.762” processorarchitecture=“x86” publickeytoken=“1fc8b3b9a1e18e3b”> </assemblyidentity> </dependentassembly> </dependency> </assembly> In my work, I’ve had very little need for manifests. The VC optimizer is able to strip the unused portions of statically linked ATL out of your code, leaving a much smaller footprint than the entire assembly would require. This eliminates the need to redistribute ATL with your application, thus reducing the overall potential for deployment issues. Nevertheless, there are exceptions to every rule, and your mileage may vary.

January 11, 2007 · 3 min · Owen Wengerd

I'll take web sites for $200, Alex: Part I

I’m one of those throwbacks that learned HTML by typing it in Notepad. I’ve since moved up to using the Visual Studio editor; it does syntax coloring, error highlighting, and has a “design” mode for previewing the page, yet it’s a very utilitarian editor that I feel comfortable with. I do use FrontPage when I need to manage connections between multiple HTML pages, but mostly in raw HTML mode. When I use WYSIWYG design mode in FrontPage, I inevitably end up cleaning out a lot of unnecessary junk that it includes in the generated HTML. I think it’s fair to say that my obsession with clean HTML results in utilitarian, functional, and standards conformant presentations – but with a decided lack of graphic appeal. The ManuSoft and CADLock web sites are examples of this utilitarian approach. The ManuSoft site uses no fancy graphics and relies very little on client side scripting, and it supports a hierarchical navigation system using only standard hyperlinks. I like that minimalist approach, but the price for clean HTML being served to clients is a lot of work on the server to maintain the site. As a result, I don’t update the site very often because it’s just too difficult. This blog was the first step toward realizing a goal of making it easier to add new content. After all, the raison d’être of blogs is to minimize the latency between the writer’s stream of consciousness and words on the web by making it irresistibly easy to add new content. This is precisely why blogs have become so popular. Unfortunately, I soon found limitations with my blog. Tabular lists of data still require manual HTML input, it is difficult to customize the content area outside the individual posts on the blog page, and most aspects of the hosted blog software are outside my control. I wanted more. The Autodesk vs. Open Design Alliance lawsuit gave me an excuse to take the next step: implement the “blog” concept across an entire web site with software that I control. So, I decided to swallow my pride and learn how to create an entire web site that would be so easy to update that I would actually update it frequently – even if it meant messy HTML code. Stay tuned for Part II, choosing a hosting service and deciding which software to use.

January 8, 2007 · 2 min · Owen Wengerd

VCBuildHook updated for VS 2005 SP1

I’ve installed Visual Studio 2005 SP1 and have been busy testing all my projects to make sure they didn’t break. Microsoft did fix at least one of the bugs I reported (the resource editor choked on resource strings that contained an embedded NULL character), but the IDE still crashes fairly consistently if I initiate a large solution build while the Intellisense database is being rebuilt. SP1 also managed to break my VCBuildHook utility, so I’ve uploaded an updated version (2.0.3.0) for use with SP1.

December 19, 2006 · 1 min · Owen Wengerd

Visual Studio 2005 Service Pack 1 is here!

Finally, the long awaited service pack 1 for Visual Studio 2005 is released! The service pack has been in the pipeline since soon after Visual Studio 2005 shipped last November, with release dates slipping month after month. The latest official release date was announced as “early 2007”. Perhaps this was an intentional attempt to alleviate some of the waiting pains with a suprise release (or maybe just the Microsoft rumor mill on autopilot). Be prepared for a long install. The download page warns that it could take an hour or more to verify digital signatures before the service pack installation even begins.

December 18, 2006 · 1 min · Owen Wengerd

Visual Studio Version Hunt

If you’ve been programming with ObjectARX for a while, you already know that each version of the ObjectARX SDK only works with a specific version of Visual Studio. Mazhar Basa touches on the subject in his article Components of ObjectARX Applications (http://www.dailyautocad.com/objectarx/components-of-objectarx-applications/). My VCBuildHook utility makes it possible to build for multiple target AutoCAD versions from a single Visual Studio 2005 solution, but this utility still needs the older versions of Visual Studio to be installed on the same system in order to perform its magic. The problem is, legitimate licenses of older versions of Visual Studio are hard to come by. Back in the Visual Studio .NET 2003 days, you could purchase a new license of VS 2003, then get a free license downgrade from Microsoft that allowed you to install Visual Studio 2002 side by side on the same system. That combination allowed you to target AutoCAD versions as far back as AutoCAD 2004. Unfortunately, Microsoft stopped offering the VS 2002 downgrade license when Visual Studio 2005 began shipping in November of 2006. The end result is that new ObjectARX programmers are faced with a dilemma if they need to target anything older than AutoCAD 2007. One of the attendees at my “High-Octane ObjectARX” class at AU 2006 reminded me that MSDN (Microsoft Developer Network) members can download older versions of Visual Studio from the MSDN subscriber download center. I have verified that Visual Studio .NET 2002 (VS 2002) is available for download to MSDN Professional subscribers, but I could not find Visual Studio 6.0, which is required to target AutoCAD 2000 - 2002. Visual Studio 4.2 (required for AutoCAD R14) is available though, so maybe I’m just not looking in the right place. If you are a professional software developer, whether commercial or in-house, you should consider subscribing to MSDN for various reasons. If you’re an ObjectARX programmer and need to target older versions of AutoCAD, MSDN may be the only way to obtain the older Visual Studio build tools and libraries that you’ll need.

December 8, 2006 · 2 min · Owen Wengerd