Run iLogic rule from external application

By Adam Nagy

While in case of an Inventor AddIn you can use early binding to access the iLogic COM objects, in case of an external application you need to use late binding. That's because the iLogic objects are not real COM objects, but COM wrappers around .NET objects. In case of VB.NET it's been really simple from the start to do late binding, because it's been enough to declare the variables as Object instead of the specific type. Since version 4 of C# it's just as simple there because of the arrival of the dynamic keyword.

Below is the external application equivalent of the code used in this blog post: http://adndevblog.typepad.com/manufacturing/2013/04/call-ilogic-from-net.html 

static void test_iLogic()
{
  Inventor.Application oApp = 
    System.Runtime.InteropServices.Marshal.
    GetActiveObject("Inventor.Application") as Inventor.Application;
 
  //iLogic is also an addin which has its guid
  string iLogicAddinGuid = "{3BDD8D79-2179-4B11-8A5A-257B1C0263AC}";
 
  Inventor.ApplicationAddIn addin = null;
  try
  {
    // try to get iLogic addin
    addin = oApp.ApplicationAddIns.get_ItemById(iLogicAddinGuid);
  }
  catch
  {
    // any error...
  }
 
  if (addin != null)
  {
    // activate the addin
    if (!addin.Activated)
      addin.Activate();
 
    // entrance of iLogic
    dynamic _iLogicAutomation = addin.Automation;
 
    Document oCurrentDoc = oApp.ActiveDocument;
 
    dynamic myRule = null;
    //dump all rules
    foreach (dynamic eachRule in _iLogicAutomation.Rules(oCurrentDoc))
    {
      if (eachRule.Name == "MyRule")
      {
        myRule = eachRule;
        //list the code of rule to the list box
        MessageBox.Show(myRule.Text);
        break;
      }
    }
    if (myRule != null)
      _iLogicAutomation.RunRule(oCurrentDoc, "MyRule");
  }
}

In C++ it's not so simple. Fortunately I found this MSDN article which makes things a bit easier: http://support.microsoft.com/kb/238393

// http://support.microsoft.com/kb/238393
// 
// AutoWrap() - Automation helper function...
// 
HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp, 
      LPOLESTR ptName, int cArgs...) 
{
  // Begin variable-argument list...
  va_list marker;
  va_start(marker, cArgs);
 
  if(!pDisp) {
    MessageBox(NULL, _T("NULL IDispatch passed to AutoWrap()"), 
      _T("Error"), 0x10010);
    _exit(0);
  }
 
  // Variables used...
  DISPPARAMS dp = { NULL, NULL, 0, 0 };
  DISPID dispidNamed = DISPID_PROPERTYPUT;
  DISPID dispID;
  HRESULT hr;
  TCHAR buf[200];
 
  // Get DISPID for name passed...
  hr = pDisp->GetIDsOfNames(
    IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID);
 
  if(FAILED(hr)) {
    _stprintf(buf, 
      _T("IDispatch::GetIDsOfNames("%s") failed w/err0x%08lx"),
      ptName, hr);
    MessageBox(NULL, buf, _T("AutoWrap()"), 0x10010);
    _exit(0);
    return hr;
  }
 
  // Allocate memory for arguments...
  VARIANT *pArgs = new VARIANT[cArgs+1];
 
  // Extract arguments...
  for(int i=0; iInvoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, 
    autoType, &dp, pvResult, NULL, NULL);
 
  if(FAILED(hr)) {
    _stprintf(buf,
      _T("IDispatch::Invoke("%s"=%08lx) failed w/err 0x%08lx"), 
      ptName, dispID, hr);
    MessageBox(NULL, buf, _T("AutoWrap()"), 0x10010);
    _exit(0);
    return hr;
  }
 
  // End variable-argument section...
  va_end(marker);
 
  delete [] pArgs;
 
  return hr;
}
 
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
  int nRetCode = 0;
 
  HRESULT Result = NOERROR;
 
  ::CoInitialize(NULL);
 
  // Access Inventor
  {
    CLSID InvAppClsid;
    Result = CLSIDFromProgID (      _T("Inventor.Application"), &InvAppClsid);
    if (FAILED(Result)) return Result;
 
    CComPtr pInvAppUnk;
    Result = ::GetActiveObject (InvAppClsid, NULL, &pInvAppUnk);
    if (FAILED (Result))
      _tprintf_s(_T("Could not get the active Inventor instancen"));
    if (FAILED(Result)) return Result;
 
    CComPtr pInvApp;
    Result = pInvAppUnk->QueryInterface(
      __uuidof(Application), (void **) &pInvApp);
    if (FAILED(Result)) return Result;
 
    CComPtr pAddIn = 
      pInvApp->ApplicationAddIns->ItemById[
        _T("{3BDD8D79-2179-4B11-8A5A-257B1C0263AC}")];
 
    // Calling iLogic functions
    {
      CComPtr pAuto = pAddIn->Automation;
 
      CComPtr pDoc = pInvApp->ActiveDocument;
 
      VARIANT ret;
      VARIANT param1;
 
      param1.vt = VT_DISPATCH;
      param1.pdispVal = pDoc;
      Result = AutoWrap(
        DISPATCH_PROPERTYGET, &ret, pAuto, _T("Rules"), 1, param1);
 
      // Rules() returns an IEnumarator - it will be wrapped as 
      // IEnumVARIANT returned by GetEnumerator()
      // http://stackoverflow.com/questions/7399447/com-use-ienumerable-in-atl-c-project
      CComPtr pRules = ret.pdispVal;
      Result = AutoWrap(
        DISPATCH_METHOD, &ret, pRules, _T("GetEnumerator"), 0);
 
      CComQIPtr pRulesEnum = ret.punkVal; 
 
      VARIANT rule;
      ULONG celt = 1, celtFetched;
      while (pRulesEnum->Next(celt, &rule, &celtFetched) == S_OK)
      {
        CComPtr pRule = rule.pdispVal;
        Result = AutoWrap(
          DISPATCH_PROPERTYGET, &ret, pRule, _T("Name"), 0);
        BSTR name = ret.bstrVal;
 
        if (_tcscmp(name, _T("MyRule")) == 0)
        {
          Result = AutoWrap(
            DISPATCH_PROPERTYGET, &ret, pRule, _T("Text"), 0);
          BSTR text = ret.bstrVal;
          MessageBox(NULL, text, _T("Rule Text"), MB_OK);
 
          VARIANT param2;
          param2.vt = VT_BSTR;
          param2.bstrVal = name;
          // Parameters need to be added in reverse order. 
          // In the C# code you'd call it like
          // _iLogicAutomation.RunRule(oCurrentDoc, "MyRule");
          Result = AutoWrap(
            DISPATCH_METHOD, &ret, pAuto, _T("RunRule"),             2, param2, param1);
        }
      }
    }
  }
::CoUninitialize();
 
  return nRetCode;}

Comments

8 responses to “Run iLogic rule from external application”

  1. Hi,
    I am using .net to implement this logic.
    everything worked fine once but not now.
    I getting at line where we try to activate the add-in.
    Did you ever face this error or have any idea why this is occuring.
    Regards,
    Manoj

  2. I cannot reproduce the issue now, but I’ve seen it a few times that the iLogic module did not load properly and the iLogic UI elements did not show up. I can imagine that in such a case the Activate() method would fail as well. If the iLogic UI components show up fine in your case then I don’t know why it would fail since then the add-in should be active already.

  3. Hello
    I have some questions:
    1. How to get SDK for iLogic plugin ?
    2. How to get list of forms from iLogic plugin ?
    3. How to get a pointer to IiLogicForm interface (Forms list) ?
    4. How to get IID’s list of iLogic plugin interfaces (I Can’t find TLB) ?
    5. How to get the correct IUnknown interface from the iLogic plugin ?
    C/C++ (MFC) only plz !!!
    Thank You

  4. Mike Deck Avatar
    Mike Deck

    Hi Bjegosh,
    There is no TLB for the iLogic plugin. It’s possible that late binding is the only way to call it from unmanaged C++.
    Are you developing for Inventor on the desktop or for Forge? Or both?
    Thanks,
    Mike

  5. Hi Mike,
    … simple utility (not plug-in) for win7/10 on MFC vs2015
    C#:
    iLogicForm oILForm = new iLogicForm(oDoc,_invApp); <– Unfortunately class, not IiLogicForm interface !!!
    C++ (MFC):
    I don’t know how to get the equivalent of C# code to get a list of forms from iLogic
    I can’t get the interface because I don’t know IID for IiLogicForm interface
    … and this interface is not registered in Windows registry
    Thanks

  6. Mike Deck Avatar
    Mike Deck

    Hi Bjegosh,
    I don’t have VS2015 installed, but I put together a test program to do it. It’s an MFC executable and it uses C++/CLI to create a new iLogicForm. iLogicForm is hust a C# class. It’s not exposed to COM.
    Here’s the relevant source code. Please post your question on the Inventor Customization forum https://forums.autodesk.com/t5/inventor-customization/bd-p/120 and I can post the full project there.
    #include “stdafx.h”
    #include “iLogicFormsTest.h”
    #include
    #include
    #using
    #using
    #using
    #using
    using namespace System::Runtime::InteropServices;
    using namespace Autodesk::iLogic::Interfaces;
    HRESULT iLogicFormsTest::testForms()
    {
    int nRetCode = 0;
    HRESULT Result = NOERROR;
    // Access Inventor
    {
    CLSID InvAppClsid;
    Result = CLSIDFromProgID(
    _T(“Inventor.Application”), &InvAppClsid);
    if (FAILED(Result)) return Result;
    CComPtr pInvAppUnk;
    Result = ::GetActiveObject(InvAppClsid, NULL, &pInvAppUnk);
    if (FAILED(Result))
    _tprintf_s(_T(“Could not get the active Inventor instance\n”));
    if (FAILED(Result)) return Result;
    CComPtr pInvApp;
    Result = pInvAppUnk->QueryInterface(
    __uuidof(Application), (void **)&pInvApp);
    if (FAILED(Result)) return Result;
    CComPtr pAddIn =
    pInvApp->ApplicationAddIns->ItemById[
    _T(“{3BDD8D79-2179-4B11-8A5A-257B1C0263AC}”)];
    // Call iLogic functions using C++/CLI
    {
    // Get a managed reference to the application:
    System::IntPtr appPointer(pInvApp);
    Inventor::Application ^ managedApp = dynamic_cast (Marshal::GetObjectForIUnknown(appPointer));
    Inventor::Document ^ managedDoc = managedApp->ActiveDocument;
    auto forms = gcnew iLogic::iLogicForm(managedDoc, managedApp);
    auto formNames = forms->FormNames;
    for each (System::String ^ formName in formNames)
    {
    std::wstring formNameStd = msclr::interop::marshal_as(formName);
    wchar_t msg[1024];
    swprintf(msg, sizeof(msg), L”form name %s\n”, formNameStd.c_str());
    ::OutputDebugStringW(msg);
    }
    }
    }
    return S_OK;
    }

  7. Mike Deck Avatar
    Mike Deck

    Formatting has removed angle brackets in the code above.
    Please post a question on the Customization forum and we can continue the discussion there.

  8. Bjegosh Avatar
    Bjegosh

    Thanks for the answer
    Formatting no problem, i understand your code
    I already have a post on Customization forum
    I don’t want to make a copy of the topic
    Name: “[C++] The IiLogicForm interface IID for QueryInterface”
    ID: 9926179

Leave a Reply to Adam NagyCancel reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading