Adding a Custom Object or Entity to an Object Model

If your COM wrapper class encapsulates a custom object or entity, you need to modify the ATL-generated code to support the appropriate interfaces. The ObjectARX samples\com\AsdkSquareWrapper_dg\square directory contains an example of a wrapper class for a custom entity written as a separate DLL.

You also must make sure the ObjectARX component satisfies the ActiveX Automation requirements, and you must extend your wrapper application to support the ObjectARX components. The following sections provide procedures that show how this is done:

To create an Automation wrapper project for a custom object or entity

  1. Set up your project according to the steps in Setting Up an ATL Project File.
  2. In stdafx.h, immediately after #include <atlcom.h>, include acad24.h, dbmain.h, and any other necessary ObjectARX header files.
  3. In stdafx.cpp, add the following code at the end of the file:
#include "acadi_i.c"

The project you just created contains a considerable amount of generated code. Some of this code must be extended for ObjectARX usage, as demonstrated in the next procedure.

To adapt the ATL-generated code for ObjectARX compatibility

  1. In the COM object header file, include axtempl.h (the main ActiveX Automation template header file) and the header file(s) for your custom objects or entities.
  2. Change the derivation of the COM object or entity by removing the IDispatchImpl part of the derivation and replacing it with one of the following declarations:
// For a custom object.
//
public IAcadObjectDispatchImpl<CWrapperClass,
    &CLSID_WrapperClass,IWrapperClass,
    &IID_IWrapperClass,&LIBID_LIBRARYLib>
 
// For a custom entity.
//
public IAcadEntityDispatchImpl<CWrapperClass,
    &CLSID_WrapperClass,IWrapperClass,
    &IID_IWrapperClass,&LIBID_LIBRARYLib>

Add the following entries to the COM_MAP:

COM_INTERFACE_ENTRY(IAcadBaseObject)
COM_INTERFACE_ENTRY(IAcadObject)
COM_INTERFACE_ENTRY(IAcadEntity) // For an entity only.
COM_INTERFACE_ENTRY(IRetrieveApplication)
COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer) // Only 
                                 // necessary to support events.

You must implement a special function in your COM code to expose custom objects and entities. The following procedure demonstrates how to provide this support.

To add ObjectARX custom object and custom entity support

Add the following required override to the public declarations of your COM class:

// IAcadBaseObjectImpl
//
virtual HRESULT 
CreateNewObject(
    AcDbObjectId& objId, 
    AcDbObjectID& ownerId, 
    TCHAR* keyName);

This abstract function is a public member of the IAcadBaseObjectImpl class. Your COM wrapper class inherits IAcadBaseObjectImpl through either IAcadObjectDispatchImpl or IAcadEntityDispatchImpl. CreateNewObject must be overridden in order to add default objects to the database.

Implement the CreateNewObject() function and any other object-specific or entity-specific functions.

The following example shows the implementation of CreateNewObject() from AsdkSquareWrapper:

HRESULT CAsdkSquareWrapper::CreateNewObject(
    AcDbObjectId& objId, 
    AcDbObjectId& ownerId, 
    TCHAR* keyName)
{
    try 
    {
        AcAxDocLock docLock(ownerId, AcAxDocLock::kNormal);
        Acad::ErrorStatus es;
        AcDbObjectPointer<AsdkSquare> pSq;
        if((es = pSq.create()) != Acad::eOk)
            throw es;
        AcDbDatabase* pDb = ownerId.database();
        pSq->setDatabaseDefaults(pDb);
        AcDbBlockTableRecordPointer 
            pBlockTableRecord(ownerId, AcDb::kForWrite);
        if((es = pBlockTableRecord.openStatus()) != Acad::eOk)
            throw es;
        if((es = pBlockTableRecord->
            appendAcDbEntity(objId, pSq.object())) != Acad::eOk)
            throw es;
    }
    catch(const Acad::ErrorStatus)
    {
        //To become more sophisticated 
        //
        return Error(L"Failed to create square",
            IID_IAsdkSquareWrapper, E_FAIL);
    }
    return S_OK;
}

Add the desired ActiveX methods and properties to your wrapper class as described in To expose functionality through ActiveX.

In order for your objects' Automation interfaces to be defined correctly, you must revise the generated Interface Definition Language (IDL) file.

To adapt the IDL file for ObjectARX usage

In the IDL file, change the interface derivation for your COM object from IDispatch to IAcadObject for a custom object, or to IAcadEntity for a custom entity. Here is an example from the AsdkSquareLib.idl sample file:

interface IAsdkSquareWrapper : IAcadEntity

Add the following code after importlib stdole32.tlb and importlib stdole2.tlb:

importlib("acax24enu.tlb"); // revise the path to match your
                            // own AutoCAD installation

Make sure you substitute the path that matches your AutoCAD installation.

In the section of the IDL file that corresponds to your wrapper coclass, add [source] interface IAcadObjectEvents; after the [default] line in order to support events.

Move the acax24enu.tlb section to the top of the IDL file, and move your custom object code so that it is within that section.

The IDL file now appears similar to the following code:

import "oaidl.idl";
import "ocidl.idl";
[
    uuid(800F70A1-6DE9-11D2-A7A6-0060B0872457),
    version(1.0),
    helpstring("AsdkSquareLib 1.0 Type Library")
]
library ASDKSQUARELIBLib
{
    importlib("stdole32.tlb");
    importlib("stdole2.tlb");
    importlib("acax23enu.tlb");
    [
        object,
        uuid(800F70AD-6DE9-11D2-A7A6-0060B0872457),
        dual,
        helpstring("IAsdkSquareWrapper Interface"),
        pointer_default(unique)
    ]
    interface IAsdkSquareWrapper : IAcadEntity
    {
        [propget, id(1), helpstring("property Number")] 
            HRESULT Number([out, retval] short *pVal);
        [propput, id(1), helpstring("property Number")] 
            HRESULT Number([in] short newVal);
    };
    [
        uuid(800F70AE-6DE9-11D2-A7A6-0060B0872457),
        helpstring("AsdkSquareWrapper Class")
    ]
    coclass AsdkSquareWrapper
    {
        [default] interface IAsdkSquareWrapper;
        [source] interface IAcadObjectEvents;
    };
};
Note: The IDL file modifications will cause the compiler to issue a warning stating that the interface does not conform. You can ignore this message.

Next, make sure that your ObjectARX component implements the necessary overrides to support ActiveX Automation. If you are building separate COM and ObjectARX DLLs, switch to the ObjectARX project to perform the next procedure.

The AutoCAD Properties palette uses the getClassID() function to retrieve your custom entity's type information. See Properties Palette API for information on implementing Properties palette functionality. If you do not override getClassID() as described below, the Properties palette displays only base class information for your custom entity. However, if you override getClassID() but do not implement OPM interfaces for the custom entity, the Properties palette cannot display either your own class information or the base class data. See Obtaining a CLSID for a Custom Class for more information on getClassID().

To prepare your ObjectARX project for ActiveX compatibility

In every ObjectARX-based class being wrapped, override the getClassID() function:

Acad::ErrorStatus
AcDbMyClass::getClassID(CLSID* pClsid) const
{
    *pClsid = CLSID_WrapperClass; // replace CLSID_WrapperClass with                                   // your COM class CLSID
    return Acad::eOk;
}

In each file that contains an override for getClassId(), include the necessary COM header files, as well as the compiler-generated ID file:

#include <objbase.h>
#include <initguid.h>
#include "library_i.c" // File containing definitions of the
                       // IIDs and CLSIDs for the COM project.
                       // “library” is replaced by your COM project
                       // name. This file resides in your COM
                       // project directory.

Build and register the COM application according to the steps in Building and Registering a COM DLL. If you are building separate COM and ObjectARX or ObjectDBX DLLs, build the ObjectARX or ObjectDBX module before building the COM application.