PTC Creo Parametric – using VB API from C++

A truly Geek Post on how to call PTC Creo Parametric API from C++ native code

Foreground

Nexus (our design process integration and optimization solution) introduced a Direct Integration node for PTC Creo Parametric from version 2.9 onward. Despite we have been impressed by the large set of documentation and example available on the web on how to call PTC Creo API for Visual Basic, very little example on how to link PTC Creo from native C and CPP sources have been found.
This post collects our main findings. Please note this is neither intended to be an exhaustive document nor to cover all the aspect of PTC Creo API(s). Our goal is to provide a first starting point and (we hope) some useful hints.

PTC Creo Prerequisites

In order to use PTC Creo Automation you need to register COM API. Exhaustive explanations on how to do that can be found here:

To summarize, in order to work with the COM API of PTC Creo Parametric you need to:

  • install VB API along with your local installation of PTC Creo
  • make sure COM API has been properly registered, by launching at least once the vb_api_register.bat file located in your main PTC installation folder (typically ‹creo_installation_dir›/Parametric/bin.. Be sure you launched the script with Administrator Privilages, otherwise PTC API classes may not register correctly within your Windows System register
  • make sure your process will have access to the environmental variable PRO_COMM_MSG_EXE, pointing at the full path of the executable file pro_comm_msg.exe. which is typically located in ‹creo_installation_dir›\Common Files\‹datecode›\‹machine type›\obj\pro_comm_msg.exe, where ‹machine type› is i486_nt for 32-bit Windows and x86e_win64 for 64-bit Windows

Importing PTC Creo COM Symbols

In order to use the PTC COM API from native source codes (typically C or CPP) and to compile and link your application correctly, you need to export and decode the COM symbols used by PTC Creo.
As PTC Creo does not provide original include files for their COM-based API (or at least we haven’t been able to find them), you will need to generate them on the fly. This will instruct the compiler to generate a file named pfclscom.tlh at runtime containing all the COM signatures exported by the PTC Creo API in a C flavor format.

#ifndef nxPTC_CreoParametric_COMS_h__
#define nxPTC_CreoParametric_COMS_h__

#include "atlsafe.h"
#include "atlbase.h"
#import "pfclscom.exe" auto_rename, no_namespace, named_guids

#endif // nxPTC_CreoParametric_COMS_h__

Calling PTC Creo application from COM

In this section we will report few code sections to be used to accomplish specific operations. Mode in particular:

  1. launching PTC Creo engine (no gui) from your CPP application
  2. closing PTC Creo engine
  3. open a PTC Parametric model
  4. get and set a real PTC Parameter (with model regeneration)
  5. export in IGES format

we will embed the code sections within the utility class below:

#include "nxPTC_CreoParametric_COMS.h"

#define LAUNCHER "C:\\Programs\\PTC\\Creo 3.0\\F000\\Parametric\\bin\\parametric.exe"

class PtcCreoCaller {
  private:
    ICpfcAsyncConnectionPtr ptcApp;   // the main PTC Creo COM server
    IpfcAsyncConnectionPtr ptcCnn;    // the current connection
    IpfcModelPtr ptcModel;            // the current model (if any)
    
  public:
    PtcCreoCaller()                   { this->openConnection(); }
    ~PtcCreoCaller()                  { this->closeConnection(); }

    bool openModel(std::wstring& swModel);
    bool saveAsIges(std::wstring& swName);
    double getParameterValue(std::wstring& swName);
    bool setParameterValue(std::wstring& swName, double value, bool generate=false);  
  
  private:
    void openConnection();
    void closeConnection();     
    IpfcBaseParameterPtr getDoubleParameter(std::string& swName);                
};
Launching PTC Creo engine (no gui) from your CPP application
void PtcCreoCaller::openConnection() {
   HRESULT isOk = this->ptcApp.CreateInstance(L"pfcls.pfcAsyncConnection"); 
   if( isOk!=S_OK ) {
      std::cerr << "Unable to locate PTC CREO COM server" << std::endl;
   
   std::wstring sRunner = LAUNCHER; 
   sRunner += " -g:no_graphics -i:rpc_input";   
   
   BSTR bsCommand = SysAllocStringLen(sRunner.data(),sRunner.size());
   this->ptcCnn = this->ptcApp->Start(bsCommand, L"." );
   if( !this->ptcCnn )
      std::cerr << "Unable to start PTC CREO client" << std::endl;
}

>> Back To Top

Closing PTC Creo engine
void PtcCreoCaller::closeConnection() {
   try {
      if( this->ptcCnn ) {
         if( this->ptcCnn->IsRunning()==VARIANT_TRUE )
            this->ptcCnn->End();
         this->ptcCnn->Disconnect(5);
      }
      if( this->ptcApp ) 
         this->ptcApp.Detach();
   } catch(...) {
   }
}

>> Back To Top

Open a PTC Parametric model
bool PtcCreoCaller::openModel(std::wstring& swName) {
   if( !this->ptcCnn ) 
      return false;  // not connected

   IpfcBaseSessionPtr pSession = (IpfcBaseSessionPtr)this->ptcCnn->GetSession();

   /* update the way the section will respond to model updates
      by enabling resolve-auto */
   pSession->raw_SetConfigOption(L"regen_failure_handling",L"resolve_mode");

   /* create model descriptor from user provided file and load in memory 
      the model whose local file name has been provided as input */
   BSTR bsName = SysAllocStringLen(swName.data(),swName.size());

   ICpfcModelDescriptorPtr descriptor; 
   descriptor.CreateInstance(L"pfcls.pfcModelDescriptor");
   IpfcModelDescriptorPtr pModelDescriptor;
   if( descriptor->raw_CreateFromFileName(bsName , &pModelDescriptor) != S_OK ) 
      return false;  // unable to create model descriptor based on provided filename 

   if( pSession->raw_GetModelFromDescr(pModelDescriptor, &this->ptcModel) !=S_OK ) {
      pSession->raw_ChangeDirectory( pModelDescriptor->GetPath() );
      pSession->raw_RetrieveModel(pModelDescriptor, &this->ptcModel);
   }
   return (this->ptcModel!=NULL); 
}

>> Back To Top

Get and set a real PTC Parameter (with model regeneration)

This is an utility (private) function designed to return a Parameter defined in the opened PTC CREO model (if any). The function will only return parameters whose kind has been defined as DOUBLE.

IpfcBaseParameterPtr PtcCreoCaller::getDoubleParameter(std::wstring& swName) {
   if( !this->ptcModel )  return NULL;  // no model
   
   IpfcParameterOwnerPtr pParamOwner = (IpfcParameterOwnerPtr)this->ptcModel;
   if( !pParamOwner )  return NULL;     // model is not a Parameter owner
   
   BSTR bsName = SysAllocStringLen(swName.data(),swName.size());
   IpfcBaseParameterPtr pParam = pParamOwner->GetParam(bsName);
   if( !pParam ) return NULL;           // no such parameter 

   IpfcParamValuePtr pValue = pParam->GetValue();
   if( pValue && pValue->Getdiscr()==EpfcPARAM_DOUBLE ) 
      return pParam;                    // good and double parameter
   return NULL;                         // not a double parameter
}

This method will return the value of the DOUBLE parameter whose name has been provided as input. The function returns NAN if no such parameter is defined in the current model or if the parameter is not a DOUBLE one.

double PtcCreoCaller::getParameterValue(std::wstring& swName) {
   IpfcBaseParameterPtr pParam = this->getDoubleParameter(swName);
   return (pParam ? pParam->GetValue()->getDoubleValue() : std::sqrt(-1));
}

The function below allows setting the value of the DOUBLE parameter whose user name is provided as input. If flag generate is set to true, the function also attempts to regenerate the whole model to accommodate and cascade changes produced by the new parameter value.

bool PtcCreoCaller::setParameterValue(std::wstring& swName, double value, bool generate) {
   IpfcBaseParameterPtr pParam = this->getDoubleParameter(swName);
   if( !pParam ) 
      return false;

   IpfcParamValuePtr pValue = pParam->GetValue();
   pValue->put_DoubleValue( value );
   pParam->PutValue(pValue); 

   if( generate ) {
      VARIANT flag; flag.vt=VT_NULL;
      IpfcSolidPtr pSolid = (IpfcSolidPtr)this->ptcModel;
      return (pSolid->Regenerate( flag ) == S_OK);      
   }
   return true;
}

>> Back To Top

Export in IGES format

The function below allows exporting the model currently loaded in memory in IGES format. Note that the COM API of PTC Creo Parametric by default allow exporting only in the working directory of the current section. To export on other directory you should either change the working directory of the current section or move the generated file in the final target location.

bool PtcCreoCaller::saveAsIges(std::wstring& swName) {
   if( !this->ptcModel ) 
      return false;   // no model

   // build exporting instructions for the IGES file
   ICpfcGeometryFlagsPtr pGeomCreator; 
   pGeomCreator.CreateInstance(L"pfcls.pfcGeometryFlags"); 
   IpfcGeometryFlagsPtr pFlags = pGeomCreator->Create();
   pFlags->PutAsSolids(VARIANT_TRUE);

   ICpfcIGES3DNewExportInstructionsPtr pCreator;
   HRESULT isOk = pCreator.CreateInstance(L"pfcls.pfcIGES3DNewExportInstructions"); 
   IpfcExportInstructionsPtr pInstruction = 
      (isOk==S_OK ? pCreator->Create(EpfcModelType::EpfcMDL_ASSEMBLY,pFlags) : NULL);
   if( !pInstruction  )
      return false;    // unable to configure exporter

   BSTR bsName = SysAllocStringLen(swName.data(),swName.size());
   return (this->ptcModel->raw_Export(bsName , pInstruction)==S_OK);
}

>> Back To Top

Disclaimer

iChrome, Nexus, Shaper, Grapheme and related logos are trademarks of iChrome Ltd ans all rights are reserved. Any other trademark herein referred to is property of its respective trademark holder and iChrome does not claim any right on it. More in particular, PTC and PTC Creo are trademarks and products of PTC Plc – official website: https://www.ptc.com.