com (lx_com.hpp)

From The Foundry MODO SDK wiki
Jump to: navigation, search
There are security restrictions on this page


COM Fundamentals

COM stands for "Component Object Model" which is a binary standard for polymorphic object-oriented components. The nexus SDK is based on COM, and the nexus system itself is a collection of extensible COM objects. There are many web sites and books available about COM, too numerous to list here. Just use Google.

Conventions

Declarations in the nexus C SDK follow strict conventions for the names of macros, types, values and interfaces. Declarations all start with "LX" (for Luxology), followed by a single letter code that indicates the type of the declaration. The exceptions are LxResult which is so common the name is a tiny bit shorter, and interfaces, which by COM convention always start with "I".

(1) C SDK Conventions
 LxResult                equivalent to HRESULT in COM
 LXt{name}               simple C type or struct declaration
 LXt{name}ID             pointer type, typically to the base struct name
 LXx{...}                expansion macro which becomes in-line code
 LXi{...}                integer codes such as enums or other selections
 LXf{...}                bit flags or masks
 LXs{...}                strings
 LXe_{...}               error and success code values (LxResult)
 LXu_{class}             GUID string macro for a class
 LXa_{class}             class alias string macro (extensible classes)
 LXs{class}_{name}       type tag strings, given by a name for the given class
 
 ILx{ifc}                interface (VTable) for an object
 ILx{ifc}ID              object implementing the given interface (pointer
                         to pointer to VTable)

The C conventions are followed in any header with the ".h" suffix, and are all ordinary C. There are additional conventions used in the C++ SDK which are composed of headers with the ".hpp" suffix.

(2) C++ SDK Conventions
 CLx{name}               class declaration
 CLxLoc_{ifc}            raw localization class for an interface
 CLxUser_{ifc}           user class, derived from CLxLoc_{ifc}
 CLxImpl_{ifc}           implementation class for an interface
 CLxIfc_{ifc}<T>         interface wrapper template for CLxImpl_{ifc}
 lx::{name}()            utility function (declared in namespace "lx")
 thisModule              global object representing the plug-in module
 initialize()            module startup entry point

Common Types

GUIDs are structures containing 128 bits of pure noise, hopefully unique throughout all time and space. They are used to identify interfaces and object classes.

(3) SDK: LXtGUID struct
 unsigned int             x4;
 unsigned short           x2[2];
 unsigned char            x1[8];

Since using a GUID directly is a bit of a pain, we define them using strings in what Microsoft calls "registry format". This is the GUID for IUnknown.

(4) SDK: Declarations
 #define LXu_UNKNOWN     "00000000-0000-0000-0000-0000000000C4"

An object is a pointer. The first element of the pointer is the vTable, so an object of type IUnknown (all COM objects) is a pointer to a pointer to the IUnknown vTable.

(5) SDK: Reference Types
 typedef void *                   LXtObjectID;
 typedef struct vt_ILxUnknown **  ILxUnknownID;

An ID code is a 32-bit word, with values constructed by passing four characters to the macro below.

(6) SDK: Types
 typedef unsigned int             LXtID4;

(7) SDK: Declarations
 #define LXxID4(a,b,c,d)         ((a) << 24 | (b) << 16 | (c) << 8 | (d))

Because "override" is a non-standard keyword, we only define it for systems that can support it. Otherwise this macro is defined as blank.

(8) SDK: override keyword
 #define LXx_OVERRIDE override

IUnknown and Reference Counting

This is the IUnknown interface. In COM terminology, an "interface" describes the layout of a table containing method function vectors, or "vTable." Three functions at the start of all COM object vTables control polymorphism and reference counting. The LXxMETHOD macro allows us to control the function declaration across different systems or compilers, although it is just the default for now.

(9) SDK: Types
 #define LXxMETHOD(rv,fn)        rv (*fn)
 struct st_LXtGUID;
 typedef struct vt_ILxUnknown {
                 LXxMETHOD (LxResult,
         QueryInterface) (
                 LXtObjectID              self,
                 const struct st_LXtGUID *iid,
                 void                   **ppvObj);
 
                 LXxMETHOD (unsigned int,
         AddRef) (
                 LXtObjectID              self);
 
                 LXxMETHOD (unsigned int,
         Release) (
                 LXtObjectID              self);
 } ILxUnknown;

The lifecycle of an interface is controlled by reference counting. Incorrect reference counting can result in memory leaks if there are too many references, and crashes if there are too few and an interface is released while it's still in use. It is important to follow the local rules for reference counting, and make sure that all of your reference changes balance out.

(1a) If you are passed an interface as an argument to your method, you do not own it. It is owned by the caller and should not be released.

(1b) Conversely, if you pass an interface as an argument to a method on some other interface, you still own it and must release it when you are done with it.

(2a) If you call a method that returns an interface through a "void **ppvObj" argument, the object has a reference added for you. Whether it was created from scratch or is simply a reference to an object that is logically owned by someone else, the interface that is returned is now yours and you must release it when done. QueryInterface() is no exception; if you query for a new interface you must release it.

(2b) If you return objects indirectly through a ppvObj pointer you must add-ref it for the benefit of the caller. Even if it is part of your internal data structure and you logically own it, it must be add-refed so that the caller gets to own a reference to it as well.

(3) Interfaces returned as the actual return value of a method (rather than through an indirect handle) have not been add-refed and still belong to the original object. These are considered to be "peeks" at the object's internal state rather than an implicit copy.

(4) Circular references are disallowed. If object A has a reference to object B, and object B holds a reference to object A, then neither object will ever be freed. A will hold the last reference to B and vice versa. The "ownership" graph must be strictly acyclic. So if A and B must reference each other then an ownership relation must be described. If A owns B then A holds a reference to B and B has a "backpointer" to A. Backpointers are interfaces but are not reference counted.

GUIDs

A service interface for converting between GUID strings and LXtGUID structs is exported as part of the application global.

Translate
takes a GUID string (either the LXu_* long format or an LXa_* alias) and returns a pointer to an LXtGUID. The returned pointer is non-volatile and can be stored.
GetName
takes a pointer to an LXtGUID struct and returns the name for the GUID. This will be the short alias for the GUID if it exists.
Compare
takes two LXtGUID pointers and returns a ranking metric for sorting them. The result is similar to strcmp().
Fixed
takes a GUID pointer that may be volatile and returns a non-volatile version from the global pool.
Class
takes a GUID pointer and returns the class GUID, if any.
ClassName
takes a GUID pointer and returns the name for the class, if any.

(10) SDK: ILxGUIDService interface
         LXxMETHOD( const LXtGUID *,
 Translate) (
         LXtObjectID              self,
         const char              *guidStr);
 
         LXxMETHOD( const char *,
 GetName) (
         LXtObjectID              self,
         const LXtGUID           *guid);
 
         LXxMETHOD( int,
 Compare) (
         LXtObjectID              self,
         const LXtGUID           *guid1,
         const LXtGUID           *guid2);
 
         LXxMETHOD( const LXtGUID *,
 Fixed) (
         LXtObjectID              self,
         const LXtGUID           *guid);
 
         LXxMETHOD( const LXtGUID *,
 Class) (
         LXtObjectID              self,
         const LXtGUID           *guid);
 
         LXxMETHOD( const char *,
 ClassName) (
         LXtObjectID              self,
         const LXtGUID           *guid);

(11) SDK: Declarations
 #define LXu_GUIDSERVICE         "B11826F1-A6BC-48B4-909B-5F6D01938327"
 #define LXa_GUIDSERVICE         "guidservice"

(12) User Service Class: GUIDService method

Empty GUID service Python user class.

(13) PY: GUIDService method
 pass