SDK Basics

From The Foundry MODO SDK wiki
Revision as of 07:37, 17 February 2012 by Shf (Talk | contribs)

Jump to: navigation, search

COM

The nexus SDK is implemented in COM, but as a C++ developer you mostly don't need to know any of that. Whew! That's a good thing because while COM is relatively simple, dealing with raw COM objects is quite complicated. Fortunately the C++ wrapper classes hide most of the complexity. There are, however, a few concepts that are important to understand at a high level.

ILxUnknownID

The methods that you implement will take the ILxUnknownID datatype as argument for any object. This is the general object type, and can be transformed into specific interfaces based on your needs. This is a COM thing. All objects have multiple interfaces and need to be queried before they can be used. It's a little like dynamic casting in C++, except that the types are known at runtime.

Likewise some of the service methods you will be calling also take ILxUnknownID arguments. Fortunately the wrapper classes can cast to that type implicitly so there is very little that needs to be done on your side to support this. Just know that you can pass a wrapper object and it will be converted to the right type.

Reference Counting

COM objects manage their existence by reference counting. Again, this is mostly handled for you by the wrapper classes. When you initialize a wrapper with an ILxUnknownID object a reference is added, and when the wrapper changes or goes out of scope the reference is released.

User Wrappers

Objects in the SDK are mostly accessed through two kinds of wrappers.

Services

are interfaces provided by nexus to access internal state. The wrappers are exceptionally easy to use; you just declare them and they are ready to go. The constructor does all the work of hooking the wrapper object to the real interface.
        CLxUser_SelectionService  sel_srv;

        now = sel_srv.GetTime ();

Localized Objects

Localized objects come into the plug-in from nexus, often as ILxUnknownID argument pointers. These need to be localized by initializing a class wrapper. For example, this method is passed a StringTag Object as unknown, which is then localized using the interface wrapper.

method (
        ILxUnknownID             thing)
{
        CLxUser_StringTag        tags (thing);

        n = tags.Count ();
        ...
}

The wrapper can also be initialized using the set() method, and its return value or the test() method can be used to determine if the initialization succeeded. (Lowercase methods operate on the wrapper, uppercase on the actual object.)

        if (!tags.set (thing))
                return LXe_NOTFOUND;

        ...

        if (tags.test ())
                tags.Get (tag_id, &value);

Using set() increments a reference to the object which is decremented when the wrapper releases the object, such as when it goes out of scope. There are cases when it's useful to "steal" the reference that's already been incremented by a previous call. For example, using raw allocation methods -- the ones that take an LXtObjectID as a ppvObj indirect argument -- add a reference to the returned object. The take() method transfers ownership of that reference to the wrapper.

        LXtObjectID               obj;
        LxResult                  rc;

        rc = source.Allocate (&obj);
        if (LXx_FAIL (rc))
                return false;

        return wrap.take (obj);

Exported Objects