Creo Elements/Direct Modeling COM & .NET API

Creo Elements/Direct Modeling COM & .NET API - Tips and Hints


Troubleshooting installation issues

Typical symptoms of incomplete installation or registration

Missing .NET framework during initial installation

If the .NET framework is already deployed on the system on which you want to install Creo Elements/Direct Modeling, Creo Elements/Direct Modeling will register its object model automatically with COM and .NET during installation so that it becomes accessible for internal and external clients. If for some reason the .NET framework was not available while installing Creo Elements/Direct Modeling, you can either re-run the installation for the full Creo Elements/Direct application suite, or you can install the .NET framework manually. A copy of the required framework (minimum version: 4.0) is shipped on our product DVD. Alternatively, the .NET framework is available from Microsoft's .NET framework landing page (go to "Downloads") or from http://windowsupdate.microsoft.com/.

After installing the missing .NET framework, retry the Creo Elements/Direct Modeling registration by running the file SolidDesigner.exe in the binNT subdirectory (or binx64 for the 64-bit versions) of the Creo Elements/Direct Modeling installation directory with the command-line option -register, for example from the DOS prompt:

    C:\Program Files\PTC\Creo Elements\Direct Modeling 18.0> binNT\SolidDesigner.exe -register

COM/.NET registration fails

If COM or .NET registration somehow fails during installation and configuration, open a DOS prompt window and try this procedure:

    cd /d x:\Creo Elements/Direct Modeling\installation\path
    cd binNT
    rem cd binx64 on 64-bit platforms
    SolidDesigner.exe /RegServer

This will run the automatic registration code in Creo Elements/Direct Modeling. If any problems or errors occur during registration, a message box will appear that refers to a logfile containing further information about the cause of the problem. Submit this logfile when reporting installation problems to PTC Creo Elements\Direct support.

You can also try to execute the registration steps manually to find out more about the problem:

    cd /d x:\Creo Elements/Direct Modeling\installation\path
    cd binNT
    regasm /tlb:OsdmObjects.tlb CoCreate.OsdmObjects.dll
    regtlib x:\Creo Elements/Direct Modeling\installation\path\binNT\OsdmObjects.tlb

regtlib is a type library registration utility which is available from Microsoft. A version of regtlib called regtlibv12.exe is installed with the .NET Framework v4.0 (typical installation path: C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\regtlibv12.exe).

Registering type libraries on development systems

Running SolidDesigner.exe in /RegServer mode also registers the OsdmServer.tlb type library. If you do not have Creo Elements/Direct Modeling installed on a development system, but have the type library files available, you can register them as follows:

    cd x:\path\to\directory\containing\typelibraries
    regtlib x:\path\to\directory\containing\typelibraries\OsdmServer.tlb
    regtlib x:\path\to\directory\containing\typelibraries\OsdmObjects.tlb

COM programming hints

Many interface members return pointers to interfaces. For instance, when querying a component for its children, you get a collection of interface pointers for those children.

Clients receiving interface pointers from COM servers need to be careful with reference counting and must follow the usual COM rules.

When importing the type library using the #import statement with default settings, wrapper functions are automatically generated that internally use COM smart pointers. This can lead to subtle reference counting mishaps, as described in the Microsoft Knowledge Base article 242527 .

Here is an example of wrapper code generated by the #import statement:

    inline IDocument3DPtr IApplication::InqIActive3DDocument ( ) {
      struct IDocument3D * _result = 0;
      HRESULT _hr = raw_InqIActive3DDocument(&_result);			
      if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
      // _result contains interface pointer; refcount is 1 now
			
      return IDocument3DPtr(_result, false);
      // refcount still 1
    }

When raw_InqIActive3DDocument returns, it returns an interface pointer in _result. The reference counter for the object behind the pointer is now 1. The wrapper function returns a temporary object of type IDocument3DPtr. The constructor for this type does not call AddRef() because its second parameter is false. Now let's take a look at some code on the receiving side which contains an incorrect interface pointer assignment:

    IDocument3D *iDoc3D = app->InqIActive3DDocument(); 

The assignment to iDoc3D does not increase the refcount for the document. However, C++ will call the destructor for the temporary IDocument3DPtr object at the end of the assignment statement. The IDocument3DPtr destructor calls Release, and now the reference count for the document object is 0. Subsequent attempts to access the object will crash or return with undefined results.

As a general rule of thumb, assign returned interface pointers to smart pointers rather than raw interface pointers when using automatically generated wrapper functions. Smart pointers usually call AddRef() in their assignment operator, thereby maintaining a correct reference count.

Alternatively, specify the raw_interfaces_only attribute in the #import statement to suppress generation of wrapper functions. Then you can safely use raw interface pointers as well.

Examples:

    #import "OsdmObjects.tlb"
    IDocument3DPtr pDoc = app->InqIActive3DDocument();   // ok
    CComPtr<IDocument3D> pIDoc = app->InqIActive3DDocument(); // ok
    IDocument3D *pDoc2 = app->InqIActive3Document();  // refcount error
    
    // ----------------------------------
    
    #import "OsdmObjects.tlb" raw_interfaces_only
    IDocument3D *pDoc;
    HRESULT hr = app->InqIActive3DDocument(&pDoc);  // ok

The aforementioned Knowledge Base article explains the background of the problem and also lists other possible workarounds.

Error and exception handling

COM clients need to check return codes from API class as usual. Depending on the attributes of the #import statement, wrapper code might be generated automatically which translates COM error conditions into C++ exceptions. Clients need to properly guard themselves against such exceptions.

.NET clients will receive error information via exceptions, so .NET clients always need to deal with them. OsdmException is the base class for exceptions specific to Creo Elements/Direct Modeling.

Threading considerations

In a number of situations, OSDM calls into the add-in or client, such as when it connects to the add-in (IOsdmAddin::OnConnection) or when it executes add-in commands via IOsdmCommandTarget::Execute. In the current implementation, Creo Elements/Direct Modeling will make those calls from its main thread which is also responsible for the main message pump in Creo Elements/Direct Modeling, and for servicing requests on certain interfaces, such as IApplication.

This is usually not a problem, but can, under certain circumstances, cause deadlocks when the client code now calls back into Creo Elements/Direct Modeling. The following scenarios should be avoided:

Locking the User Interface

By default, the Creo Elements/Direct Modeling UI remains interactive while external clients call into the API. To avoid issues due to concurrent interactive access, call LockUserInterface. This will disable user interface interaction until UnlockUserInterface is called. You can call LockUserInterface more than once; the number of UnlockUserInterface calls must match the number of LockUserInterface calls to enable the user interface again.

LockUserInterface can fail when the application is currently busy; in this case, the result of subsequent calls into the API is undefined. An interactive user might be deleting or changing the model which the client is about to traverse via the API, causing the client code to crash or behave erratically. It is therefore strongly recommended to catch the error and explicitly handle it. For example, the client could postpone the API call and retry later; alternatively, it could report the issue to the user, or it could simply abort the operation.

       try {
         app->LockUserInterface();
         try {
           TraverseModel();
         } catch(...) {
           // handle error case
         }
         UnlockUserInterface();
       } catch(...) {
         // report error to user/abort/...
       }

The pseudo code above guards the model traversal against potential exceptions by enclosing it in a try/catch block. This way, it ensures that UnlockUserInterface is called even if the actual model traversal fails for some reason.


Resources and links

Introductory material for COM programming

Introductory material for .NET programming