Site Tools


Adding RhinoScript Support to Rhino Plug-ins

C++

Developer: C++
Summary: Demonstrates how to add RhinoScript support to C++ plug-ins.

Note for .NET developers: The .NET equivalent to this article can be found at SdkExtendRhinoScript.

With the Rhino 4.0 SDK, it is now possible to write plug-ins that extend the RhinoScript scripting tool. To do this, a plug-in must do the following:

  1. Create a COM object that implements the IDispatch interface.
  2. Override the CRhinoPlugIn::GetPlugInObjectInterface() to give plug-ins, such as RhinoScript, access to this object.

Example

In the following example, we create a new plug-in named TestScript that supports RhinoScript scripting. If you are familiar with creating C++ projects that support COM automation and you just want to review the sample plug-in source code, then you can download it here.

Also, this example is not intended to teach COM Automation or to explain how the .IDL file work or what Variants are. There is plenty of information in MSDN and on the Internet that explain these areas.

Step 1: Create a plug-in that supports Automation

Launch Visual C++ 2005 and create a new Rhino Plug-in project named TestScript. On the Plug-in Settings page of the Rhino Plug-in Appwizard dialog box, make sure to check the Automation check box. Once the Appwizard has completed creating the skeleton project, build the project.

testscript1.jpg

Step 2: Create a COM object that supports IDispatch

Create a new MFC class by running the MFC Class Wizard. Name the class CTestScriptObject. This class should be derived from CCmdTarget. Also, under Automation, select the Automation radio button. Note, this COM object will not be creatable by type ID because Rhino plug-ins (DLLs) are dependent on Rhino.

testscript2.jpg

Step 3: Add methods to your object

An object is not useful unless you expose methods or properties. In this example, we create a new method that lets the scripter add point objects to Rhino. Within Class View, select your new object's interface and add a new method using the Add Method Wizard. Give the new method the name AddPoint. It should have a single VARIANT* argument and return a VARIANT.

testscript3.jpg testscript4.jpg

Step 3: Implement your methods

The Add Method Wizard will create a placeholder for our AddPoint method in TestScriptObject.cpp. Add the following code to your new method:

VARIANT CTestScriptObject::AddPoint(VARIANT* vaPoint)
{
  VARIANT vaResult;
  VariantInit(&vaResult);
  V_VT(&vaResult) = VT_NULL;
 
  if( vaPoint )
  {
    ON_3dPoint pt;
    if( VariantToPoint(*vaPoint, pt) )
    {
      CRhinoDoc* doc = RhinoApp().ActiveDoc();
      if( doc )
      {
        CRhinoPointObject* obj = doc->AddPointObject( pt );
        if( obj )
        {
          doc->Redraw();
          ON_wString wstr;
          ON_UuidToString( obj->Attributes().m_uuid, wstr );
          CString str(wstr);
          V_VT(&vaResult) = VT_BSTR;
          vaResult.bstrVal = str.AllocSysString();
        }
      }
    }
  }
 
  return vaResult;
}

In the above code, the VARIANT* argument is converted to an ON_3dPoint using the VariantToPoint() function. One of the biggest challenges to creating RhinoScript accessible objects is converting the COM Variant data type to an openNURBS data type and back. Fortunately, I have done the work for you. The sample project above includes several utility functions to help you convert Variant data type to openNURBS data types. There are also functions to do just the opposite. Just look for the VariantUtilities.h/cpp files.

Once the Variant is converted to an ON_3dPoint, the code adds the point to Rhino. But, just like RhinoScript's AddPoint method, this method also returns the object's unique identifier.

Step 4: Allow access to your object

Now that you have a COM object that implements IDispatch and has at least one method, we can allow access to it. The easiest way is to put a copy of your new COM object on your plug-in object as a data member. For example:

class CTestScriptPlugIn : public CRhinoUtilityPlugIn
{
public:
  CTestScriptPlugIn();
  ~CTestScriptPlugIn();
 
  // Required overrides
  const wchar_t* PlugInName() const;
  const wchar_t* PlugInVersion() const;
  GUID PlugInID() const;
  BOOL OnLoadPlugIn();
  void OnUnloadPlugIn();
 
  // Optional overrides
  LPUNKNOWN GetPlugInObjectInterface( const ON_UUID& iid );
 
private:
  ON_wString m_plugin_version;
  CTestScriptObject m_object;
};

Then, allow access to the object by overriding the GetPlugInObjectInterface() virtual function. For example:

LPUNKNOWN CTestScriptPlugIn::GetPlugInObjectInterface( const ON_UUID& iid )
{
  LPUNKNOWN lpUnknown = 0;
 
  if( iid == IID_IUnknown )
    lpUnknown = m_object.GetInterface( &IID_IUnknown );
 
  else if( iid == IID_IDispatch )
    lpUnknown = m_object.GetInterface( &IID_IDispatch );
 
   if( lpUnknown )
    m_object.ExternalAddRef();
 
  return lpUnknown;
}

Note, RhinoScript will only request an IDispatch object from your plug-in. Also, because our object is a data member of our plug-in object, we must increment our object's reference counter. Otherwise, when VBScript“ releases our object, which will decrement the reference counter, our object will be destroyed. This will cause your plug-in to crash.

Step 5: Implement your methods

Once you have implemented your methods, you can test them. Launch Rhino and use the PlugInManager command to install your new plug-in. Then, use RhinoScript's EditScript dialog to test the methods in your plug-in's object.

The following code demonstrates how to get our plug-in's scriptable object and run the AddPoint method.

testscript5.jpg


developer/sdksamples/getpluginobjectinterface.txt · Last modified: 2020/08/14 (external edit)