TheMaster

While I won't get into the underlying reasons, I will say that the way that Autodesk restructured ObjectARX and its managed wrapper APIs serves the strategic objective of hindering release-independent source code portability and with that, the ability for developers to target multiple product releases with a single source code stream. In fact, they have pretty much made it nearly impossible for developers to continue to target AutoCAD 2012 and AutoCAD 2013 with the same source code.

Note that Kean Walmsly makes little mention of the fact that the changes in AutoCAD 2013's managed API break existing source code portability, and seems to trivialize that, something that most professional developers regard as a material 'breach of contract'. His article on the subject also seems to confuse 'binary compatibility' with source code compatibility/portability. The two are not the same. Binary compatibility is where the same build of a project can be run on different product releases. Source code portability is the ability to compile the same source code to multiple distributions targeting multiple product releases. While we have come to expect binary compatibility to be broken in each major release, we were not expecting source code to be broken to the extent that it has been broken in AutoCAD 2013, and with that, the ability to continue ongoing development of projects that target both AutoCAD 2010-2012 and 2013 has become more than most will be willing to bear.

This code is provided as an example showing one strategy for maximizing source code portability between AutocAD R18 and R19. The basic strategy involves abstracting out differences between APIs in different releases, by exposing a version-independent wrapper API that determines which underlying native API must be called at runtime, and compiles a delegate that calls the appropriate API. Note that this approach uses compiled expressions to achieve the best performance, but a similar approach could be used that involved the use of standard Reflection for calling the underlying APIs. The code is largely untested and you should use it at your own risk.

TheMaster

After examining the AutoCAD 2013 migration quagmire further, I've concluded that the above strategy and code is useful mainly when there is binary compatibility between the supported product releases (e.g., where the same build can be loaded into all supported AutoCAD versions).

However, since that is not the case between AutoCAD 2013 and previous releases, here is a far better solution to the problem of maximizing application source code portability between AutoCAD 2013 and AutoCAD 2012 and earlier. The most compelling aspect of this solution is that it completely eliminates the need for release-dependent code and the messy conditional compilation directives (#if/#else/#endif) needed to include/exclude release-specific code depending on the target AutoCAD release.

' these all work Public Function GetTextStyleId(ByVal Instance As DBText) As ObjectId If textStyleProperty IsNot Nothing Then Return DirectCast(textStyleProperty.GetValue(Instance, Nothing), ObjectId) Else Throw New MissingMemberException() End If End Function

Public Function GetTextStyleId(ByVal Instance As MText) As ObjectId If mtextStyleProperty IsNot Nothing Then Return DirectCast(mtextStyleProperty.GetValue(Instance, Nothing), ObjectId) Else Throw New MissingMemberException() End If End Function

Public Sub SetTextStyleId(ByVal Instance As DBText, ByVal value As ObjectId) If textStyleProperty IsNot Nothing Then textStyleProperty.SetValue(instance, value, Nothing) Else Throw New MissingMemberException() End If End Sub

Public Sub SetTextStyleId(ByVal Instance As MText, ByVal value As ObjectId) If mtextStyleProperty IsNot Nothing Then mtextStyleProperty.SetValue(instance, value, Nothing) Else Throw New MissingMemberException() End If End Sub

Public Sub New() textStyleProperty = GetType(DBText).GetProperty("TextStyle") If textStyleProperty Is Nothing Then textStyleProperty = GetType(DBText).GetProperty("TextStyleId") End If mtextStyleProperty = GetType(MText).GetProperty("TextStyle") If mtextStyleProperty Is Nothing Then mtextStyleProperty = GetType(MText).GetProperty("TextStyleId") End If

' this part may be working but the associated code in GetAcadDocument doesn't and the query takes way too long Dim ns As String = "Autodesk.AutoCAD.ApplicationServices.DocumentExtension" Dim namespaceFound As Object

namespaceFound = (From assembly In AppDomain.CurrentDomain.GetAssemblies() From type In assembly.GetTypes() Where type.Namespace = ns Select type).Any() If namespaceFound IsNot Nothing Then DocumentExtensionClass = Activator.CreateInstance(namespaceFound) End If If DocumentExtensionClass IsNot Nothing Then GetAcadDocumentMethod = DocumentExtensionClass.GetMethod("GetAcadDocument") End If

' this works for pre-2013 AcadDocumentProperty = GetType(Autodesk.AutoCAD.ApplicationServices.Document).GetProperty("AcadDocument")

End Sub

Public Function GetAcadDocument(ByVal WhichDocument As Document) As Object GetAcadDocument = Nothing ' this doesn't work I think I have to replace Invoke(Nothing, params) with Invoke(DocumentExtensionClass, params) ' but I abandoned this before testing because the query took so long If GetAcadDocumentMethod IsNot Nothing Then Dim params(0 To 0) As Object params(0) = WhichDocument Return DirectCast(GetAcadDocumentMethod.Invoke(Nothing, params), Object) End If ' this works for pre-2013 If AcadDocumentProperty IsNot Nothing Then Return DirectCast(AcadDocumentProperty.GetValue(WhichDocument, Nothing), Object) End If End Function

1. Is there any document that lists all the breaking changes and new bits added to the AutoCAD .NET API over the years? I know about the "What's New" section in the arxmgd.chm files for each year but is this collated in one place?

2. Versions: where do you draw the line, given that .NET is supported in v2006, v2009 changed quite a few things and v2012 was the first to support .NET 4.0 ? Me, I've currently added v2007 & 8 to the mix but I'm tempted to drop them because of missing parts in the API and also because customers using versions that old are more likely to be Freetards or Pathological customers, best avoided and/or dealt with using a long stick. My gut-feeling is start with v2010. Yours?

3. Further to the missing bits (properties, methods etc) I am using extension methods to fill the holes as per TT's ideas and code here. This is not a question.

4. How do you set up Projects / Solutions to cater for different builds?

I have butchered a Visual Studio template .csproj file (and a .csproj.user file which must be added later) to use conditionals and wildcards to set up different builds with different Library and .NET framework references. It's a little fragile because Visual Studio likes to fill in the Wildcards when it builds the Template into a Project so I also leave the original wildcards in comments so I can re-butcher them. Source Control is a must if you try this.

4a. Now that I have mentioned that Visual Studio template I really should tidy it up (that should be interesting, TT knows what I'm talking about here) and post it here if anyone is interested. Interested?

TheMaster

I don't remember the thread, but I described one of the ways I dealt with projects that target multiple releases, which is to store release-specific source files in different folders along with a 'common' folder that is used in all releases, and then build separate projects that add those files as links (so they're not copied to the project folder).

The threads mentioned here have most of what I would repeat here. There are some new things in later versions of .NET (like Linq.Expression) that allows you to avoid the overhead of reflection when you have two incompatible APIs, but I've yet to resort to that.

I don't know of any document that describes the differences between API releases, and in that regards, you're kind of on your own, but doing a diff on two different releases of the same assembly will show them.

This time around I'm focusing on propagating source code to destination Projects which control all their own build settings. Apparently some (all) of us though that 72 builds in one project was a little unmanageable </YesItWas>.

There's an extensive readme there so I won't duplicate it here. I'd love to hear any thoughts, suggestions, WTFs.My earlier works left here to serve as a warning for others..and also because there are a couple of useful tips in there....

My methodology (as of writing - this can change without notice) for supporting different versions involves using one project with different builds. One of my project actually has 36 builds, all using the same code. Well, it seemed like a good idea at the time.

A Visual Studio project file is just an XML file, so don't be shy about editing it by hand. Here is a the MSDN landing page for more info

I need to massage my Visual Studio project template a fair bit for public consumption, add a wizard to fix a few hard-coded fragilities and also write up an explanation for what it does and why. Here's a teaser in the meantime, just a few excerpts for the 2007 version. I also have builds for 2013, 2012 (both build on .NET 4.0) & 2010 (v3.5) ...

The whole .csproj file is >410 lines. There is also a .vstemplate file that defines all the parameters. There's also a batch file that builds the project template and installs it in the Visual Studio folder, plus a template for the Command.cs file.

The XML comments are there because the Visual Studio project generator tends to strip out wildcards and just make a list of what is there at the time. I prefer the wildcards so I don't have to fix every project in existence if I add, remove or rename a file in the linked folders.

Z: is a mapped drive because the VS project maker changes the links to relative ones. That's a bit fragile for my liking - if you move the project all the links break. I haven't tried it with an environment variable yet :: Edit, yes I have and it works ::: which would a easier to manage than mapping a duplicate drive.

ReSharper will complain when you switch between pre-2013 & 2013-onwards builds because of the extra DLL (AcCoreMgd.dll). Also, the observant among you probably want to tell me that v2007 is x86-only so an "Any CPU" build will generate build warnings. Yeah, I know. It's on the list.

There's more to it than just this but I hope it gives you an idea of how this alternative works.

Edit: I have since broken the monolithic csproj file into several smaller ones, based on what is unchanged between projects. I then import them into the specific project I am working on. The bonus is that Visual Studio doesn't seem to alter imported files so I don't lose any of the wildcard links.

The order you import them is important, if there are any cascading dependencies then they need to be declared before you can use them.

============================== EDIT March 3rd, 2015 =================

I don't do it this way any more, well not as confusingly. Perhaps. These days I have 2 CSPROJ files I import for each version of AutoCAD (NanoCAD, BricsCAD etc. too), the first file I import defines the build configuration name, sets the compilation constants (eg. ACAD2015) and sets the output path and sets the Debug settings. The second file I import sets the project references based on the Compilation constants. The main project file has no output paths or AutoCAD References. Here is an example...Main Project file (the first part of it)

A Visual Studio project file is just an XML file, so don't be shy about editing it by hand. Here is a the MSDN landing page for more info. Yes, I already said this at the top but that's actually the "easy" way to do this. If you are using Notepad++ then try this Syntax Highlighter.

I just saw this thread, and noticed the mention of using partial classes.That approach is what I have done so far, and I don't really like it.Code in two files for same class is annoying.You end up with a separate partial code file for each release, and also a separate project that assembles them.Then different references in the main project that uses the helper libraries.