20
SD9660 Creating AutoCAD Cross-Platform Plug-ins Fernando Malard ofcdesk, LLC Learning Objectives Learn how to create C++ cross-platform core Learn how to create code with ObjectARX technology for Windows Learn how to create code with ObjectARX technology for Mac Learn how to use Microsoft Visual Studio and Mac OSX XCode Description This class will present strategies used to create cross-platform plug-ins, taking advantage of C++ language in order to create core source code that could be used by AutoCAD software for Windows and by AutoCAD for Mac software. We will then demonstrate how to consume this shared core code into each platform, taking advantage of each specific user interface feature (Microsoft MFC, Microsoft .NET inside Windows, and Cocoa inside Mac OSX). Your AU Experts Fernando Malard is a civil engineer who has worked with AutoCAD software and ObjectARX technology since 1996 and with Revit software since 2009. He has also been an Autodesk Developer Network member since 1997. He has worked on several AutoCAD and Revit applications for civil engineering, architecture, interior design, and geographic information system (GIS) using ObjectARX technology, C++, Microsoft .NET, JavaScript, Cocoa, and databases. Fernando has had extensive experience teaching AutoCAD, Revit, C++, Microsoft Foundation Class, Microsoft .NET, and ObjectARX technology over the last years. Today he continues to apply his skills to the design and implementation of complex Industry Solutions. Fernando has also worked with Autodesk User Group International (AUGI) communities, and he maintains a blog about ObjectARX technology. He holds a master’s degree in structural engineering from the Federal University of Minas Gerais, Brazil.

SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

Embed Size (px)

Citation preview

Page 1: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

SD9660

CreatingAutoCADCross-PlatformPlug-insFernandoMalardofcdesk,LLC

LearningObjectives• LearnhowtocreateC++cross-platformcore

• LearnhowtocreatecodewithObjectARXtechnologyforWindows

• LearnhowtocreatecodewithObjectARXtechnologyforMac

• LearnhowtouseMicrosoftVisualStudioandMacOSXXCode

DescriptionThis class will present strategies used to create cross-platform plug-ins, taking advantage of C++languageinordertocreatecoresourcecodethatcouldbeusedbyAutoCADsoftwareforWindowsandbyAutoCADforMacsoftware.Wewill thendemonstratehowtoconsumethis sharedcorecode intoeachplatform,takingadvantageofeachspecificuserinterfacefeature(MicrosoftMFC,Microsoft.NETinsideWindows,andCocoainsideMacOSX).

YourAUExpertsFernandoMalardisacivilengineerwhohasworkedwithAutoCADsoftwareandObjectARXtechnologysince 1996 and with Revit software since 2009. He has also been an Autodesk Developer Networkmember since 1997. He has worked on several AutoCAD and Revit applications for civil engineering,architecture,interiordesign,andgeographicinformationsystem(GIS)usingObjectARXtechnology,C++,Microsoft .NET, JavaScript, Cocoa, and databases. Fernando has had extensive experience teachingAutoCAD, Revit, C++,Microsoft Foundation Class,Microsoft .NET, andObjectARX technology over thelastyears.TodayhecontinuestoapplyhisskillstothedesignandimplementationofcomplexIndustrySolutions.FernandohasalsoworkedwithAutodeskUserGroup International (AUGI)communities,andhemaintainsablogaboutObjectARXtechnology.Heholdsamaster’sdegree instructuralengineeringfromtheFederalUniversityofMinasGerais,Brazil.

Page 2: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

2

LearningObjectives..........................................................................................................................1Description.......................................................................................................................................1YourAUExperts...............................................................................................................................1Introduction.....................................................................................................................................3

Requirements.......................................................................................................................................3SharedC++core................................................................................................................................3

PolymorphicTypes...............................................................................................................................4CreatingWindowsProject................................................................................................................5

ConfiguringtheSolution.......................................................................................................................5CreatingtheDBXCustomEntity...........................................................................................................6AddingtheCustomEntitytoModelSpace...........................................................................................8Creatingbasiccommandpromptinteraction......................................................................................9CreatingtheWindowsUIwithMFC.....................................................................................................9

CreatingMacOSXProject................................................................................................................11CreatingtheXcodeprojects...............................................................................................................12InstallPath..........................................................................................................................................13AdjustingtheSchemesandDebuggingtheProject...........................................................................14AddingthesharedWindowsfiles.......................................................................................................15

CreatingtheMacUIwithCocoaandObjective-C............................................................................16Conclusion......................................................................................................................................20

Page 3: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

3

IntroductionInthepast,AutodeskusedtosupportwaymoreplatformsthanWindows.OldversionsofAutoCAD(R12andR13)usedtorunintoaMacintoshcomputerbutAutodeskstoppeditssupportbackin1994.Since2010MacOSXsupportwasbackstartingwithAutoCAD2011forMacandlatelywithAutoCAD2016forMacOSX.TheAutoCADforMacUIhasnothingtodowiththeWindowsversiondueaninfinitenumberof reasons but there is a huge portion of the code written in C++ that is shared between the twoplatforms. The shared source code includes somehardworkdone to support64-bit architecture (theonly supported by AutoCAD for Mac) and also to make it way smoother to port applications fromWindows. Thingswidely used into AutoCAD forWindows plugins likeMFC classes likeCString wherewisely supportedvia tricky support libraries createdbyAutodesk.These support librariesavoided theexcessive use of #ifdef compile code switches to accommodate dynamic compilation changes into asourcecodemeanttoworkintobothPlatforms.

More than a simple port to Mac OSX, AutoCAD for Mac feels like a native application with someremarkablefeaturesnotpresentintotheWindowsversion.Eventhough,theoverallaspectofAutoCADhasn’tchangedsomuchallowinguserstoeasilymigratefromoneplatformtoanother.

FromtheAutoCADPluginDeveloper’sperspective,itisveryimportanttounderstandthosedifferencesand how they could potentially affect their applications.More than that, it is very important to takeadvantagesofeachPlatform’sexclusive featuresanduse themtoempoweryourapplicationsso theyalsofeelveryfluidandnativetothesePlatforms.

EvenbyhavingsharedC++sourcecodetheMacOSXpluginwillrequirenewlanguagesandconceptstobe considered likeObjective-C andCocoa. Thiswill add some new challenges to developers becausethosenewlanguageshavedifferentsyntaxandrequirementsthatDeveloperswillneedtounderstand,supportandconciliatewithWindowsspecificsourcecode.

RequirementsWewilluseintothisclasstheversion2016forbothWindowsandMac.InsideWindowsthiswillrequireDeveloperstouseVisualStudio2012.InsideMacOSX,eventhenativeAutoCADMaccodewascreatedwithXcode5,wewilluseXcode6.4.ItispossibletodevelopandmaintainsharedPlatformapplicationsworkingontwodifferentcomputersrunningthespecificsystembutitisextremelypainful.Itisstronglyrecommendedtoadoptonesystemcapableofemulatethetwosystemsintoasinglemachine.

Mac OSX does require specific Apple Hardware to run (not considering unsupported system hacksfloatingaroundbut I’mprettysureyouwon’tadd thiscomplication toyourdailywork).Evenbeingalimitation you can run Windows inside a Mac OSX system using Virtual Machine solutions (I wouldrecommendeitherParallelsDesktoporVMWareFusionwhicharethetwomostusedandreliable).BydoingthatyouwillbeabletorunbothOperatingSystems intoasinglemachinethusmakingyour lifemucheasier.

SharedC++coreTheideaofasharedC++coretobeconsumedandusedbybothPlatformversionsofanyapplicationsissubjected to some requirements like the Platform Architecture (32/64-bit), supported languages andcompilers.ItispossibletosharecompiledstaticlibrariesbutherewewillfocusonsharedsourcecodesuitabletobecompiledintoWindowsthroughVisualStudioandintoMacOSXthroughXcode.

Page 4: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

4

IfyouareplanningtoportyourexistingapplicationtoAutoCADforMacandalsokeepitgoingintotheWindows System, some intensive work may be necessary to separate and reorganize your codeaccordingly.Ifyoubindyourinterfacetothecodelogic,notusingappropriateSoftwareDesignPatternslikeMVC,youwillprobablyfacesomeissues. Inotherhand, if it isabrandnewcode,youwillbe justfine.

InsideXcode, theCPP files arewell recognizedand compiledbut tobe able to connect thepureC++code (andObjectARX SDK) to specificMacOSX APIs likeCocoa, youwill need to do it via .MM fileswhichare fileswhereC++codecan livewithObjective-C/Cocoa. Further,XcodeUIdesign reinforcingtheuseofMVCDesignPatternsoyourinterfaceswillneedtofollowthispattern.Hereiswhereanewchallenge arises, how to share an application logic layer and bing it to two completely different UIstrategies.

HereisabriefAutoCADandPlatformAPIcomparison:

API/Library Windows MacOSXObjectARXC++ Ok Ok

Objective-C/Cocoa Notsupported OkWin32 Ok PartialMFC Ok Limitedsupport

ActiveX/COM Ok Notsupported.NET Ok NotsupportedLISP Ok OkDCL Ok NotsupportedQt Ok Ok

Javascript Ok Notyet

PolymorphicTypesOnceyouplantodevelopapplicationstobesupportedandexecutedbydifferentplatformsyouneedtobeawareofthoseplatformdifferences.Theuseof integernumbers,being8,16or32-bitandalsotheuse of the long type, are subjected to the Architecture of each platform. Autodesk does provide thePolymorphictypesexactly tosupport thisscenariosoanumbersavedasa long integer intoWindowswillproperlyworkintotheMac.Thepolymorphictypescanbeaccessedthroughthe“adesk.h”includefileavailableonbothMacandWindows.

UnlikeWindows-64,onOSX-64,alongis64-bits.Anycodethatassumessizeof(int)==sizeof(long)willpotentiallyhavebugs.ThefixistoconsistentlyuseAdesk::Int32andAdesk::UInt32inplaceoflongssowecannormalize thisallbetweenthe twoplatforms.Remember that thestoragesizeofnumberwilldependontheirdeclarationsoifyouplantostoreanumberthatvariesfrom0to100,Adesk::Int32istoobigforthatandyouwillwastestoragespace.

The polymorphic types are compiled accordingly to the Platform Definition _ADESK_MAC_ and_ADESK_WINDOWS_.Youshouldalsousethesetwodefinitionstomakeportionsofyourcodesuitabletothespecificplatform.

Page 5: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

5

CreatingWindowsProjectOurfirststepwillbethesharedcoremodule.ThismodulewillbeasimpleDBXmodulethatwillcontainacustomentity.WewillcreatetheprojectinsideVisualStudio2012withObjectARX2016.

Herearetherequirements:

- MakesureyouhaveAutoCAD2016installed;- MakesureyouhaveObjectARX2016alsoinstalled;- UseVisualStudio2012;

We will create a blank solution called “AUCrossPlatform” where wewilladdboththeDBXandARXprojects.TheDBXprojectwillbenamedas“AUDbxWindows”andtheARXprojectnamedas“AUArxWindows”.

TheprojectswerecreatedusingAutodeskObjectARXWizards2016availablehere:

http://images.autodesk.com/adsk/files/ObjectARXWizards-2016.zip

ConfiguringtheSolution

NotethatforeachPlatformbuildconfiguration(Win32andx64)youhavetheappropriatefilesreferenced.Thiswillensurethenecessary include filesand library filesare found.TheObjectARXSDKhasspecificincludeandbinaryfilesforboth.

With both ARX and DBX projects created, nowwe have to configure the appropriate projectdependencybetweenthem.YoucandothisbyrightclickingattheAUArxWindowsprojectnode,selectthe “Project Dependencies…” option. At the dependencies dialog, select theAUDbxWindows projectfromthelist.

Onceyouproperlycreate thedependency it is time toadd theDBX library reference into theARXproject so theycanproperly link.ThiswillallowtheARXmodule toconsumetheclassesdefinedintotheDBXprojectatruntime.GototheARXProjectSettings;selectat thetopdialogcomboboxes“All Configurations” and “All Platforms”. Then go to Configuration Properties > Linker > Input >AdditionalDependenciesandaddthefollowingreference:

$(SolutionDir)\AUDbxWindows\$(PlatformTarget)\$(Configuration)\*.lib

Page 6: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

6

CreatingtheDBXCustomEntityNow thatwe have our projects configured it is time to create a simple customentity to be used fortesting. We will use the ObjectARX ClassWizard to create the class. To do that, right click theAUDbxWindows project, go to “Add >” and select the “Class...” option.Once inside the “Add Class”dialog,select“ObjectARX”inthe“VisualC++”nodeandthenselect“ArxWizCustomObject”atthelist.Fillthe“Name”with“AUDbxEntity”.ClickAdd.

At the newdialog, fill the class namewith “AUDbxEntity” and select “AcDbCurve” as the base class.ClickNextandacceptthedefaultswith“DwgProtocol”checked.ClickNextagain,nothingcheckedandthenFinish.Thefollowing3imagesillustratetheObjectARXClassWizardsteps:

Page 7: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

7

Next,wewilldeclarethenecessaryclassmembersattheAUDbxEntity.hfile:

Adesk::Int16 _iTemperature; AcString _sText; AcGePoint3d _pts[2];

Andwewillcreatetheiraccessmethods:

void SetTemperature(Adesk::Int16 iTemperature); Adesk::Int16 GetTemperature() const; void SetText(const ACHAR* sText); const ACHAR* GetText() const; void SetStartPoint(AcGePoint3d pt); AcGePoint3d GetStartPoint() const; void SetEndPoint(AcGePoint3d pt); AcGePoint3d GetEndPoint() const; Basicallyeachmethodwillsetorretrievethememberdata.Notethatweneedtousetheproperassertmethod so AutoCAD will take care of file paging process when those data members change(assertReadEnabled() /assertWriteEnabled()).Wewillalsoneedtopage In/Out thedatethroughthedwgfilermethods:

// AUDbxEntity::dwgOutFields pFiler->writeInt16(_iTemperature); pFiler->writeString(_sText); pFiler->writePoint3d(_pts[0]); pFiler->writePoint3d(_pts[1]);

// AUDbxEntity::dwgInFields pFiler->readInt16(&_iTemperature); pFiler->readString(_sText); pFiler->readPoint3d(&_pts[0]); pFiler->readPoint3d(&_pts[1]);

Tobeable to seeour customentityat the screenwewill implementa simpledrawing routine insidesubViewportDraw() method but first we need to return Adesk::kFalse from the subWorldDraw()methodsotheviewportversiongetcalled:

Adesk::Boolean AUDbxEntity::subWorldDraw (AcGiWorldDraw *mode) { assertReadEnabled () ; //------ Returning Adesk::kFalse here will force viewportDraw() call return (Adesk::kFalse) ; }

Next,wecancreatethedrawingroutines.Basicallywewilltraceasimplestraightlinefromstartpointtothe end point. Using this direction,wewill then draw the Text, alignedwith this line, containing the_sText contentand the_iTemperature value.The linewillbedrawnusing color1 (RED) and the textcolor3(GREEN).ThiscanbeachievedbysettingthecolortothesubEntityTraits()whichisresponsibleforconfiguringthecurrentdrawinggraphics:

void AUDbxEntity::subViewportDraw (AcGiViewportDraw *mode) { assertReadEnabled () ;

Page 8: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

8

AcDbPolyline plBuff; plBuff.addVertexAt(0,_pts[0].convert2d(AcGePlane::kXYPlane)); plBuff.addVertexAt(1,_pts[1].convert2d(AcGePlane::kXYPlane)); mode->subEntityTraits().setColor(1); mode->geometry().pline(plBuff); AcString strMsg; strMsg.format(_T("%s > %d"),_sText.constPtr(),_iTemperature); AcGeVector3d vDir = _pts[1] - _pts[0]; AcGeVector3d vecPerp = vDir.perpVector().normalize(); double dLen = vDir.length(); mode->subEntityTraits().setColor(3); mode->geometry().text(_pts[0] + vecPerp*(dLen / 20.0), AcGeVector3d::kZAxis,vDir,(dLen / 10.0),1.0,0,strMsg); }

AddingtheCustomEntitytoModelSpaceThe recipe for adding a newentity to theModel Space is quite simple.Weneed to open theModelSpace forwrite, instantiateourentity throughapointer, set itspropertiesandadd it toModelSpaceclosingtheentity’spointerattheend.WewillcreateautilitymethodinsidetheacrxEntryPoint.cppfile(projectAUArxWindows)attheCAUArxWindowsAppclass:

// Entity util static void CreateCustomEntity(ads_point pti, ads_point ptj, ACHAR* pText, int iTemp) { AUDbxEntity* pEntity = new AUDbxEntity(); pEntity->SetStartPoint(asPnt3d(pti)); pEntity->SetEndPoint(asPnt3d(ptj)); pEntity->SetText(pText); pEntity->SetTemperature(iTemp); AcDbBlockTable *pBlockTable = NULL; acdbHostApplicationServices()->workingDatabase() ->getSymbolTable(pBlockTable, AcDb::kForRead); AcDbBlockTableRecord *pBlockTableRecord = NULL; pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord, AcDb::kForWrite); pBlockTable->close(); AcDbObjectId entId; pBlockTableRecord->appendAcDbEntity(entId, pEntity); pEntity->close(); pBlockTableRecord->close(); }

TheAUArxWindowsprojectwon’trecognizethecustomentityclassunlessweadditsheaderfile,whichislocatedinsidetheAUDbxWindows:

#include "..\AUDbxWindows\AUDbxEntity.h"

Page 9: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

9

CreatingbasiccommandpromptinteractionToallowtheusertointeractwithourapplicationandcreateaninstanceoftheDBXcustomentity,wewillneedtoprovideacommandregisteredatAutoCADcommandstackandperformsomebasiccallstoaced…()commandmethods:

// Prompt version static void AuMyGroupAUCROSSPROMPT () { ads_point pti, ptj; if (acedGetPoint(NULL, _T("\nClick at the start point:"), pti) != RTNORM) return; if (acedGetPoint(pti, _T("\nClick at the end point:"), ptj) != RTNORM) return; ACHAR pText[255] = _T(""); if (acedGetString(1,_T("\nEnter the Text Message:"),pText) != RTNORM) return; int iTemp = 0; if (acedGetInt(_T("\nEnter the Temperature:"),&iTemp) != RTNORM) return; CreateCustomEntity(pti, ptj, pText, iTemp); }

Further,thiscommandneedstoberegisteredattheendofacrxEntryPoint.cppfile:

IMPLEMENT_ARX_ENTRYPOINT(CAUArxWindowsApp)

ACED_ARXCOMMAND_ENTRY_AUTO(CAUArxWindowsApp, AuMyGroup, AUCROSSPROMPT, AUCROSSPROMPT, ACRX_CMD_MODAL, NULL)

CreatingtheWindowsUIwithMFCNowthatwehaveourpromptversion,let’screateaMFCdialogsowecanprovidesomeUIcapabilitiestoourAUArxWindowsmodule.AseparatefilewillhandletheMFCdialogsowecanskip its inclusioninside theMacOSXprojectwherewewill implementanotherUIusingOSX specific technology calledCocoa.

Thedialogwillbesimpleandcontainjusttwofieldstoinputthecustomentity’stextandtemperature.TheywillbebothanEditcontrolandwillreturnastringvaluewhenthedialogcloses.AfterthedialogisclosedtheuserwillthenspecifythestartandendpointsattheAutoCADscreen.

To add a newMFC dialog to our AUArxWindows project we need to first create its Resource byaccessingtheProjectResourceView.Oncethere,youcanrightclickthe“AUArxWindows.rc”nodeandselectAddResource…todisplaytheResourcetypeselectiondialog.Insidethisdialog,simplyselecttheDialogitemandthenclicktheNewbutton.

A new blank dialog will be created with default OK and Cancel buttons. It does have the defaultidentificationnamedas “IDD_DIALOG1”.Wewill change this to“IDD_AUDIALOG”.Now, right clickatthedialogcenterscreenandselect“AddClass...”.TheMFCClassWizarddialogwillappear.Changetheclass name to “AUArxWindowsDlg” and the base class to “CDialog”. The suggested .h and .cpp filenameswillbeupdatedaccordingly.ClickFinish.

Page 10: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

10

Now,gotothejustcreated“AUArxWindowsDlg.h”fileandaddtheincludefortheresourcefile:

// AUArxWindowsDlg dialog #include "Resource.h"

ThedialogiscreatedandnowweneedtoaddtwoeditboxestoreceivetheuserinputbeforecreatingourcustomentityintoModelSpace.AfteraddingtheEditcontrols,addastatictextinfrontofeachone.Adjustthevisuallayouttocontrolsgetalignedandreducethedialogsizeaccordingly.YoucankeepboththeOKandCancelbuttons.Changethenameofyourdialogasyouwish.AlltheseoperationsaredoneviaVisualStudioPropertiespaletteandthecontrolscanbeinsertedviaToolboxpalette.

ItisrecommendedtochangetheEditboxidentifications,wewilluseIDC_EDIT_TEXTforthetextinputbox and IDC_EDIT_TEMP for the temperature input box. The latter we will also change its property“Number”toTrue.

Now right click at each Edit control and select “Add Variable…” naming them as “edtText” and“edtTemp”.Next, double click at bothOKandCancel buttons so yougenerate amethod callback forthem.

Page 11: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

11

Further,addsomeotherlocalvariablestocarrythevaluesoutofthedialog.WewillneedtoincludethedialogheaderfileintotheacrxEntryPoint.cppfile:

#include "AUArxWindowsDlg.h" Thenewcommandwillopenthedialogthenretrievethevaluesback:

// Dialog version static void AuMyGroupAUCROSSDLG() { AUArxWindowsDlg dlg(CWnd::FromHandle(adsw_acadMainWnd())); if (dlg.DoModal() == IDOK) { ads_point pti, ptj; if (acedGetPoint(NULL, _T("\nClick at the start point:"), pti) != RTNORM) return; if (acedGetPoint(pti, _T("\nClick at the end point:"), ptj) != RTNORM) return; CreateCustomEntity(pti, ptj, dlg._sText.GetBuffer(), dlg._iTemp); } }

AndhereisthecommandnewmacrotobeaddedtotheendofacrxEntryPoint.cppfile:

ACED_ARXCOMMAND_ENTRY_AUTO(CAUArxWindowsApp, AuMyGroup, AUCROSSDLG, AUCROSSDLG, ACRX_CMD_MODAL, NULL)

ThiscompletestheWindowsportionofourCrossPlatformapplicationsofromnowonwewilldealwiththeMacOSXsideoftheapplication.Therewewillreusethenon-UIportionofourWindowscode(bothDBXandARX)andwewillcreateanewUItoreplicatetheMFCdialogwehaveinsideWindows.

CreatingMacOSXProjectTobeabletocompileandbuildthesameapplicationandmakeitworkwithAutoCAD2016forMacOSXwewill need to useXcode compiler. AutoCAD 2016 is builtwith Xcode 5, base SDK 10.9 and deploytargetis10.9.WewilluseXcode6.4forthissampleproject.TheObjectARXlibrariesarecompatiblewithXcode6.4sowewon’thaveanyproblems.

AutoCADused to supportMacOS in the past and a relatively big portion ofAutoCAD source code isC/C++friendlybeingcompatiblewithMacOS.Otherthanthat,Autodeskhadahardworkadaptingthelatest AutoCAD versions to Mac OSX beginning with natural platform differences (like the lack ofWindowsAPI)andsomeadditionalchallengeslikefeaturesnotportableatall(likeCOMinterfaces).Tomake the job easier to Developers coming fromWindows, Autodesk created a good amount of C++headersupport files thusallowingusers toreusegreatpartof theirsourcecode.This is trueevenforcomplexWindowsclasseslikeCString.

Page 12: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

12

As a compiler and IDE, Xcode is quite different from Visual Studio and it has some advantages andlimitationswhenwecomparebothsidebyside.Hereisabriefcomparisonoftheiressentialfeatures:

Feature VisualStudio XcodeIntegratedEnvironment Solution(.sln) Workspace(.xcworkspace)

Project VC++Project(.vcxproj) XcodeProject(.xcodeproj)Configuration SolutionConfiguration SchemesOutputFiles .arx/.dbx/.crx .bundle/.dbx

ProjectConfigurationFile .props .xcconfigPlatformsupport Win32/x64 x86_64Resources/Dialogs .rc/MFC .xib/CocoaC++sourcefile .CPP .CPP/.MMResourceeditor ResourceView InterfaceBuilder

ApplicationDesignPattern anypattern MVCbydefault

ToproperlyportourexistingWindowscodeandcomplete itwithXcodespecific fileswewillneed tofirstsetasharedfolder(seenbyeitherMacOSXorWindows)wherebothsourcecodefileswillcoexist.NotethatAutodeskdoesprovidethe_ADESK_MAC_definition,whichwillhelpustodefineportionsofourcodetocompileornot,dependingonwhichCompiler isconsuming it.Forsomecompilerspecificfiles,wedon’tneedtoevenincludethemintotheothercompilerthusavoidingunnecessaryprocessingorcodedetourvia#ifdefinstructions.ThisisthecaseofMFCdialogfilesinVisualStudioandCocoafilesinXcode.

EachcompilerwillalsorequiredifferentprojectsettingstoaccommodateyourdeploymentstrategyandmultipleObjectARXversionsupport.NotethatAutoCADforMacrequiresax64machinesothereisn’tsupportto32-bitcodethusreducingourmultipleprojectsettingsinXcodebyahalf.

CreatingtheXcodeprojectsWe will first create a blank Workspace into Xcode to accommodate our two projects. Open Xcode,ignore theWelcome screen, go toFile >New>Workspace… then locate yourVisual Studio SolutionfolderandsavetheWorkspaceas“AUCrossPlatform.xcworkspace”.WithinthisblankWorkspace,gottoFile>NewandselectNewProject…toopenthetemplateselectiondialog.Attheleft,underOSX,selectAutodesk then choose “DbxFramework” and clickNext. Note that you need to install ObjectARX forMacSDKtobeabletoseethetemplates:

Page 13: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

13

NametheProductas“AUDbxMac”andfilltheotherfieldsasyouwish.ClickNext.

ItwillsuggesttherootfolderofyourrecentlycreatedblankWorkspace.AcceptthatandclickCreatetofinish. A new folder with your product namewill be created and also a folder called “Build” will becreatedatyourWorkspace’srootfolder.Thiswillbethebuildoutputfolder.

Now,createthesecondProject intothesameWorkspacebyrightclickingattheblankleftspace.Thistimeselectsthe“ArxWithCocoa”template,whichistheARXcorrespondingprojecttype.ClickNextandname it as “AUArxMac”. Click Next again and then click Create (it will default again using theWorkspace’srootfoldertosavetheproject).YoushouldendupwithaWorkspacelikethis:

InstallPathNowwe need to change one important thing into the xcconfig files. Dynamic Libraries are resolvedinsideMacOSX in adifferentwaywhen compared toWindows. Eachdependency receives apath towhateveritlinkstoo.AswewillneedtolinkourBundle(ARX)withtheDBXtoimportourcustomentityweneedtoassuretheARXmodulecanfindtheDBXat loadtime. Ifyoujust lettheXcodetocompilewiththedefaultconfiguration,theresultinglinkpathwillbe:

$(LOCAL_LIBRARY_DIR)/Frameworkswhichresolvesto/Library/Frameworks

But,itwillrequireyoutodeployacopyofyourmoduleintothisfoldereverytimeyoubuilditandthiswouldbeveryannoying(nottomentiontheimpactofdebuggingtheapplication).Toavoidthat,wewill

Page 14: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

14

usethespecialinstallpathcalled“@rpath“.StartinginMacOSX10.5,thisspecialpath,whenplacedinfrontofaninstallname,willtellthedynamiclinkertosearchalistoflocationsforthelibrary.Thatlistisembeddedintotheapplication,andcanthereforebecontrolledbytheapplication'sbuildprocess,notbytheframework.Tofixthis,simplyopenthe“dbx_common.xcconfig“fileintotheAUDbxMacprojectandaddthiscodeattheend:

INSTALL_PATH=@rpath

This will predefine the Installation Directory to be resolved as mentioned above. Repeat themodificationintothe“arx_common.xcconfig“fileintotheAUArxMacproject.

AdjustingtheSchemesandDebuggingtheProjectNowwewillconfigureourSchemessowecanhaveabetterenvironmenttobuildanddebugourcodeonAutoCAD2016.Wewill keeponly1 Schemewhichwill compile theprojectAUArxMac (whichwillautomaticallycompiletheDBXmoduleaswedidastaticreferencetothatproject).

Select theAUDbxMac scheme, click at the “-” button, then selectDelete.Now select theAUArxMacscheme,clickattheEdit…buttontoenter intotheSchemeEditordialog.SelectBuildsteponthe leftpanel and, underTargets area, add theAUDbxMac target to the list soboth get compiledwhen youbuildthisScheme.

Page 15: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

15

After that, go to the Run step to specify the Application as the Executable, in this case, “AutoCAD2016.app”.NowyoushouldbereadytobuildalltheSchemesaccordingly.

AddingthesharedWindowsfilesNowwewilladdthefilescreatedbytheVisualStudioProjectswewanttoshare.WewillstartwiththeAUDbxMacproject.Remove(anddelete)the“dbxmain.cpp”filefromthe“Classes”folder.Now,addtothe same folder, the existing files “AUDbxEntity.h/.cpp”, “acrxEntryPoint.cpp” and “StdAfx.h/.cpp”.MakesureyoucheckbothTargetsatthebottomofXcodeAddFiles…dialog.

Now go to the AUArxMac project, remove and delete the “arxmain.cpp” file, and add the files:“acrxEntryPoint.cpp”, “DocData.h/.cpp”and “StdAfx.h/.cpp”.Don’t add theMFCDialog filesbecausewe will create a new interface for them. Now, right click the “Frameworks” folder and add theAuDbxMacproject file soourAUArxMacprojectcreatesadependency to theDBXprojectwhereourcustomentityis.

Once yourprojects are linked you cannowdrag theDBXmodule to the “LinkBinaryWith Libraries”areathusmakingtheARXmoduletolinkwithDBX:

Page 16: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

16

Finally, we need to adjust the specific Windows code to be ignored by the Xcode. We will use the_ADESK_MAC_definitionforthat.Hereishowtheincludecodeareashouldlooklikeatthebeginningof“acrxEntryPoint.cpp”file(theexternmethodwillbeusedlater):

#ifdef _ADESK_MAC_ #include "../AUDbxWindows/AUDbxEntity.h" extern bool ShowAUArxCocoa(AcString& txtEntity, int& iTemp); #else #include "resource.h" #include "..\AUDbxWindows\AUDbxEntity.h" #include "AUArxWindowsDlg.h" #endif

In this file,addthe_ADESK_MAC_ inside themethodAuMyGroupAUCROSSDLG()aroundthecodetoshowtheMFCdialogbecausewewill ignore itwhencompilingforMacOSX.DothesamearoundtheACED_ARXCOMMAND_ENTRY_AUTO()macrocallforthiscommand.

YoucannowBuildtheAUArxMacprojectwhichwillfirstbuildtheAUDbxMacproject.The2moduleswillbegeneratedandlinked.GoaheadandopenAutoCAD2016,loadfirsttheDBXmoduleandthentheBundle.Runthepromptversioncommandandcreatethecustomentity.

CreatingtheMacUIwithCocoaandObjective-CAswesaidbefore,MacOSXUIisdrivenbyCocoaprogramminglanguagesowewilluseittoreproducethesamedialoginterfacewecreatedusingMFCattheWindowsportionofourProject.Thefirstthingtodo istocreatetheresourcefile.Todothat,rightclickat“Classes”folder inAUArxMacproject,selectNewFile…andclickatthe“CocoaClass”optionunderOSX/Sourcenodeattheleftpanel.Selectthe“Objective-C” languageoption,nametheclassas“AUArxMacDlg”subclassof“NSWindowController”.Checkthe“AlsocreateXIBfileforuserinterface”optionsoitcreatesthecorrespondingresourcefile.ClickNextandconfirmtheTargetischeckedatthebottomofthisdialog.ClickCreate.Threenewfileswillbecreatedintoyourproject:“AUArxMacDlg.xib,AUArxMacDlg.handAUArxMacDlg.m”.

Clickon“AUArxMacDlg.xib”file,ablankWindowwillappear.Now,wewilladdthenecessarycontrolstryingtomimicwhatwedidinVisualStudio.First,addtwobuttonstothedialog,one“OK”andanother“Cancel”. They can be found at the Xcode right panel as “PushButton” item. Rename them through“Attributes Inspector” tab (right panel). Now add 2 “Label” and 2 “Text Field” controls. Adjust theLayoutandresizethewindowaccordingly.TrytoconfigureitsvisualascloseaspossibletowhatyoudidinVisualStudio.

NowwewillconnecttheButtonsandEditfieldstotheWindowsControllerclass.Todothat,clickattheXIB fileandthen,at the toprightXcode icon list, select thesecond (“AssistantEditor”).Onceyoudothat the central panel will split in two vertical panels with theXIB at the left and its correspondingWindowsController .h file (in thiscase, the file“AUArxMacDlg.h”).To insert theoutlets/actions fortheButtons,pressandholdtheCTRLkey,clickandholdtheOKbuttonandstarttodragittotherightwithinthe“@interface”declaration.

Page 17: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

17

When you see the “InsertOutlet orAction” label, release the button and a small dialogwill appear.Select “Action” as the connection type, NSButton as the Type and name it as “OK_clicked”. ClickConnectandnowyoushouldhavetheactioncallbackdeclarationaddedtoyourcontrollerclass.RepeattheprocesstotheCancelbutton:

- (IBAction)OK_clicked:(NSButton *)sender; - (IBAction)Cancel_clicked:(NSButton *)sender; Usethesametechniquetogeneratethe“Outlet”forthetheTextFieldcontrols: @property (assign) IBOutlet NSTextField *txtEntity; @property (assign) IBOutlet NSTextField *txtTemp;

BuildtheAUArxMacprojecttocheckifeverythingisok.Next,wewilladdthecodetodisplaythedialogfromtheARXcommandcallback.AstheARXcommandcallback isdefinedintoaC++codefilewewillneed to change the dialog definition file type. It was created as “Objective-C Source”with the “.m”extension.Wewillneedtochangeitto“Objective-C++Source”bychangingthe“.mm”extension.Todothat,simplyselectthe“AuArxMacDlg.m”fileclickitsotherenamein-placeeditorturnson,andchangethefileextensionto“.mm”.

We need now to change the default NSWindow base class by a simple custom class derived fromNSWindowtooverridethecanBecomeMainWindowmethodtoreturnNO:

AUArxMacDlg.h:

@interface AUArxBaseWindow : NSWindow{ } @end

AUArxMacDlg.mm:

// Base Window class @implementation AUArxBaseWindow - (BOOL)canBecomeMainWindow { return NO; } @end

Page 18: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

18

Further,wewilladd2placeholdervariablestoourdialogclass.Changethe@interfaceofAUArxMacDlgasfollows:

@interface AUArxMacDlg : NSWindowController{ @public NSString* _sTextEntity; @public int _iTemp; }

Now, select the XIB file, click at theWindow component, go to the “Identity Inspector” tab, under“CustomClass”,select“AUArxBaseWindow”fromthedropdown:

GobacktotheAUArxMacDlg.mm file,addthemethodawakeFromNib()thatwillallowusto initializethedialogcontrols.Atthebuttonevents,wewilljustclosethedialogwith1forOKand0forCancel.

TheWindowcallerwillthenbeabletofigureouthowtheuserdidfinishthedialog:

- (void)awakeFromNib { // Initialize here if (self) { [self.txtEntity setStringValue:_sTextEntity]; [self.txtTemp setIntegerValue:_iTemp]; } } - (IBAction)OK_clicked:(NSButton *)sender { [[self window] orderOut:nil]; // hides it [NSApp stopModalWithCode:1]; } - (IBAction)Cancel_clicked:(NSButton *)sender { [[self window] orderOut:nil]; // hides it [NSApp stopModalWithCode:0]; }

Page 19: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

19

Now,wewilladdaC++functionintothis.mmfiletodothebridgebetweentheObjective-ClanguageandC++.ThisfunctionwillallocatetheWindow,initializethelocalvariablesandthenrunthisdialogasaModalWindowreturning0or1value.

#include "AcString.h" bool ShowAUArxCocoa(AcString& txtEntity, int& iTemp) { AUArxMacDlg* pWnd = [AUArxMacDlg alloc]; const wchar_t* pText = txtEntity; pWnd->_sTextEntity = [[[NSString alloc] initWithBytes:pText length:wcslen(pText)*sizeof(wchar_t) encoding:NSUTF32LittleEndianStringEncoding] autorelease]; pWnd->_iTemp = iTemp; [pWnd initWithWindowNibName:@"AUArxMacDlg"]; int iRes = [NSApp runModalForWindow:pWnd.window]; iTemp = [pWnd txtTemp].intValue; NSData* d = [[pWnd txtEntity].stringValue dataUsingEncoding:NSUTF32LittleEndianStringEncoding]; std::wstring sRet = std::wstring((wchar_t *)[d bytes], [d length]/sizeof(wchar_t)); txtEntity = sRet.c_str(); [pWnd release]; return (iRes > 0); }

Page 20: SD9660 Creating AutoCAD Cross-Platform Plug-insaucache.autodesk.com/au2015/sessionsFiles/9660/7891/handout_9660... · SD9660 Creating AutoCAD Cross-Platform Plug-ins ... portion of

CreatingAutoCADCross-PlatformPlug-ins

20

The last step is now change theAuMyGroupAUCROSSDLG()method sowe can open theMFC dialogwhen running in Windows and the Cocoa window when running in Mac OSX. Here is the modifiedmethodcode:

static void AuMyGroupAUCROSSDLG() { bool bCreate = false; AcString txtEntity = _T("Autodesk"); int iTemp = 14; #ifdef _ADESK_MAC_ bCreate = ShowAUArxCocoa(txtEntity, iTemp); #else AUArxWindowsDlg dlg(CWnd::FromHandle(adsw_acadMainWnd())); bCreate = (dlg.DoModal() == IDOK); txtEntity.format(_T("%s"),dlg._sText.GetBuffer()); iTemp = dlg._iTemp; #endif if (bCreate) { ads_point pti, ptj; if (acedGetPoint(NULL, _T("\nClick at the start point:"), pti) != RTNORM) return; if (acedGetPoint(pti, _T("\nClick at the end point:"), ptj) != RTNORM) return; CreateCustomEntity(pti, ptj, (ACHAR*)txtEntity.constPtr(), iTemp); } else acutPrintf(_T("\nDialog cancelled.")); }

As a final touch, select theXIB file at the Project browser, go to the Attributes Inspector tab, underControls, uncheck theClose,Resize andMinimize options. Build theWorkspace again andperformanewtest.

ConclusionThereismuchmorethanwhatwepresentedhere.AcomplexCrossPlatformPluginwilldemandmanyotheractionsregardingdeployment,menus, installersanddocumentation.Themain ideaof thisclasswastoprovideaninitialkick-offsoyoucanatleastunderstandallthechallengesinvolvedhere.AstheAutoCADforMac family increasesandexpand itsuserbase (as theMacOSXoperatingsystem itself)moreandmoreuserswilldemandforsolutionstargetingbothWindowsandMac.

Thereisn’tindeedonebestandultimateplatformtoworkon.ThereismuchmorearoundtheAutoCADproduct itself that may cause impact and change the way you use your machine. We have a hugeInternetwavehittingtheshorewithtonsofmobileandCloudsolutions.Thiswilladdanextralevelofcomplexitytoourwork.