A Short Tutorial   

<<< Introduction Chapters Using COM in PS/Workshop >>>

Contents

[back to top]


2.1 Introduction

This chapter contains a tutorial that guides you through the various stages required to implement a simple module in PS/Workshop. If you follow each step in the tutorial, you will create a module that hollows a given body in PS/Workshop.

In this tutorial you will learn how to:

 

Note: If you have installed the PS/Workshop SDK, the source code for each step of this tutorial is installed in the folder <INSTALL_DIR>\Modules\Tutorial

Creating and displaying a dialog box is beyond the scope of this project. If you are interested in doing this, look at the example edge blending module provided. Alternatively, the MSDN documentation contains a number of tutorials that cover producing and displaying dialogs.

[back to top]


2.2 Creating a new project

Create a new template project using Visual Studio as follows:

Compile the project and make sure that the module loads into PS/Workshop correctly:

The Tutorial module is also listed in the Available Add-Ins list of the Add-Ins tab of the PS/Workshop Options dialog. You must close any open documents in PS/Workshop before opening the Options dialog in order to see this tab.

 

Note: On compilation you may receive the warning "/DELAYLOAD:pskernel.dll ignored; no imports found from pskernel.dll". This warning can be ignored as the module currently doesn't access any Parasolid functionality. See Chapter B, "Known Issues" for more information.

[back to top]


2.3 Adding a Hollow menu item

In this section you change the name of the default menu added by the template, and add a new Hollow command to it.

Using the ClassView in Visual Studio, locate the StartModule function within the CAddonMain class. This function is called after a module is successfully loaded and is responsible for initialising the module and adding any required menus or menu items to PS/Workshop.

Within StartModule a call is made to the PS/Workshop interface function AddMenuItem which adds a menu item to PS/Workshop. AddMenuItem contains the following arguments:

 

IPSWAddIn        *pAddIn,     
BSTR        CommandName,    
long        CommandID,     
PSW_Menu_Mode      mode

The CommandName argument controls which menu to add the item to; as well as what text should appear on any sub-item. In order to create a new Operations menu with a Hollow command you must modify the following line within StartModule:

 

CComBSTR bstrMenuItem
   (OLESTR("&Tutorial Debug\nMy NewCommand") );

should become

 

CComBSTR bstrMenuItem
   (OLESTR("&Operations\n&Hollow") );

 

Note: It is a good idea to use a CComBSTR smart pointer to encapsulate any BSTR, as this automatically frees the resources of the BSTR when it goes out of scope. For further details see Section 3.5.2, "Creating a BSTR".

The CommandID argument specifies the ID that is passed back to the module if the menu item has been chosen. This ID should be unique within a module. You need to add a new ID to associate with the new Hollow command.

In Visual Studio, define an ID called ID_ON_HOLLOW as follows:

The mode argument controls the type of menu to add to PS/Workshop. This can have one of the following values:

 

Value

Description

PSW_Menu_App

Display the menu item at the application level (i.e. when there are no documents open in PS/Workshop)

PSW_Menu_Doc

Display the menu item at the document level (i.e. only when there are one or more documents open in PS/Workshop)

For the purposes of this example, leave the default value of mode : PSW_Menu_doc.

Change the call to AddMenuItem from:

 

return m_pAppInterface->AddMenuItem( pApp, bstrMenuItem, ID_ON_MY_COMMAND, PSW_Menu_Doc);

to:

 

return m_pAppInterface->AddMenuItem( pApp, bstrMenuItem, ID_ON_HOLLOW, PSW_Menu_Doc);

Finally, compile and run the code, and open a document in PS/Workshop. You should see a new Operations menu on the PS/Workshop menu that contains a Hollow command. If you choose Operations > Hollow, nothing happens yet, since you have not defined a function to handle this event.

The full source code for CAddonMain::StartModule should be as follows:

 

HRESULT CAddonMain::StartModule( IPSWAddIn* pApp )
{

ATLASSERT( pApp );

HRESULT hr = E_FAIL;

CComBSTR bstrMenuItem(OLESTR("&Operations\n&Hollow") );

return m_pAppInterface->AddMenuItem( pApp, bstrMenuItem, ID_ON_HOLLOW, PSW_Menu_Doc);

}

For more information about adding menu items to PS/Workshop, see Chapter 7, "Adding a New Menu Item to PS/Workshop".

[back to top]


2.4 Adding a handler for the OnCommand event

So far you have created an Operations menu containing a Hollow command. If you choose Operations > Hollow, this calls the IPSWEvents::OnCommand function on the module with the ID which you passed to the IPSWApp::AddMenuItem function (ID_ON_HOLLOW).

By default, a PS/Workshop module routes this message from the CAddinImpl class to the CAddonMain::OnCmdMsg function, which can either handle the event itself or call CAddonDoc::OnCmdMsg. This function in turn can either handle the event or pass it to CAddonView::OnCmdMsg to handle. The class you choose to handle the event depends on the functionality you want for the command.

In this case, to make the Hollow command functional, you must add a handler at the document level - i.e. the CAddonDoc class - since the command performs a hollow operation that acts on the parts in a document.

For a complete description of event handling, see Chapter 4, "Event Handling Within A Module". You may find it useful to set a number of break points in the code and debug the module to better understand the event handling process.

Add the following private function to the CAddonDoc class using the Visual Studio ClassWizard ( View > ClassWizard).

 

HRESULT CAddonDoc::OnHollow()
{
  AfxMessageBox("CAddonDoc::OnHollow reached");
  return S_OK;
}

For the time being, CAddonDoc::OnHollow simply displays a message when it is called. Section 2.5 describes how to add code that performs the hollow operation itself.

The ID of the new command is passed to CAddonDoc::OnCmdMsg as an argument. The OnCmdMsg function uses a switch statement to check whether it should handle the command and which function to route it to. In order to correctly handle the hollow command you need to modify this switch statement such that when it receives the ID_ON_HOLLOW ID it will call the CAddonDoc::OnHollow handler function.

To do this, add the following case into the switch statement:

 

  switch( nID )
  {
  case ID_ON_MY_COMMAND:
    OnMyFunction();
    hr = S_OK;
    break;
  case ID_ON_HOLLOW:        // Our newly added case statement
    OnHollow();    
    hr = S_OK;
    break;
  default:
    break;
  }

Compile and run the code again, and open a document in PS/Workshop . Choose Operations > Hollow to display the message box and confirm that CAddonDoc::OnHollow has been successfully called.

[back to top]


2.5 Writing code to perform the hollow

You now have the framework of the hollow operation in place, and you can add code to perform the hollow itself. To do this you need to complete the following steps:

To perform the hollow operation, add the following code in place of the body of the CAddonDoc::OnHollow function.

// set our return argument
HRESULT hr = E_FAIL;
  
// Changes the cursor to an hourglass for the duration of the
// function to show the user that something is happening
CWaitCursor cursor;

// The member variables m_nParts and m_pkParts contain copies of
// the parts in the associated PS/Workshop document. These
// variables are automatically initialised and should be kept up
// to date with any changes in the number of parts in the
// document

// In this case if we have no parts in the document then there 
// is no point continuing

if ( m_nParts == 0 )
{
  AfxMessageBox("CAddonDoc::OnHollow->No parts to hollow!");
  return S_OK;
}
// Clear the last error. This is for our rather simplistic error
// handling routine which is used later on
PK_LOGICAL_t  pk_was_error = PK_LOGICAL_true;
PK_ERROR_sf_t pk_error_sf;
PK_ERROR_clear_last( &pk_was_error );
// Obtain any selected faces in the document - these will be
// pierced during the hollow operation. 
int     nFaces = 0;
PK_FACE_t*     faces = NULL;

// Get the number of selected faces
hr = m_pSelectionList->get_Count( PK_CLASS_face, &nFaces );
if ( FAILED( hr ) )
return hr;

// now get the actual selected faces
if ( nFaces )
{
  // allocate our array to store the selected faces in 
  faces = new PK_FACE_t[ nFaces ];
  if ( faces == NULL )      // check if the allocation succeeded
    return E_OUTOFMEMORY;
  // there are two methods to obtain the faces required:
  // Call the IPSWSelectionList->get_Item function for each face 
  // this has a performance disadvantage in that it makes n 
  // separate calls to PS/Workshop to get all the faces

  for ( int i = 0; i < nFaces; i++ )
  {
     hr = 
            m_pSelectionList->get_Item( PK_CLASS_face, i, &faces[ i ] );
    if ( FAILED( hr ) )
    {
      // delete our array
      delete [] faces;
      return hr;
    }
  }

  // Alternatively, get all the faces in one call to PS/Workshop.
  // For this we need to obtain the enumerator for the list
  // (IPSWEnumSelectionList).
  /*  
CComPtr<IPSWEnumSelectionList> pEnumSel = NULL;
hr = m_pSelectionList->get__NewEnum( PK_CLASS_face, (IUnknown**)&pEnumSel );
  if ( FAILED( hr ) )
  {
    // free our array and return
    delete [] faces;
    return hr;
  }
  // we can now get all the faces at one time
  hr = pEnumSel->Next( nFaces, faces, NULL );
  if ( FAILED( hr ) )
  {
    delete [] faces;
    return hr; 
  }
  */
}
// create a mark to rollback to in case the hollow fails
PK_PMARK_t pmark = PK_ENTITY_null;
m_pRollback->MakePMark( &pmark );

// set the options for the hollow (here we are simply using the
// defaults).
PK_BODY_hollow_o_t hollowOptions;
PK_BODY_hollow_o_m( hollowOptions );

// fill in any faces to be pierced
if ( nFaces > 0 )
{
  hollowOptions.n_pierce_faces = nFaces;
  hollowOptions.pierce_faces = faces;
}
// and the return arguments
PK_TOPOL_track_r_t tracking;
PK_TOPOL_local_r_t results;

// finally call the function - here we are assuming that we only 
// have one body in the document

PK_BODY_hollow_2( m_pkParts[0], 0.001, 1.0e-6, &hollowOptions, &tracking, &results );

// Now we check to see if the previous function succeeded or not
PK_ERROR_ask_last( &pk_was_error, &pk_error_sf );
if( PK_LOGICAL_true == pk_was_error ) 
{ 
    CString pk_err_str = pk_error_sf.function;
    pk_err_str = pk_err_str + "\n returns \n";
    pk_err_str = pk_err_str + pk_error_sf.code_token;
    AfxMessageBox( pk_err_str, MB_OK | MB_ICONSTOP );
    if ( pk_error_sf.severity != PK_ERROR_mild ) 
    {
      // Then the model may be corrupted and we need to rollback
      m_pRollback->RollbackTo( &pmark, 1 );
      hr = E_FAIL;
    }
  }
// We also need to check the result structure to see if the 
// function succeeded
  else if ( results.status != PK_local_status_ok_c ) 
  {
// Then again we need to rollback because it has failed 
    AfxMessageBox("Hollow Failed");
    m_pRollback->RollbackTo( &pmark, 1 );
    hr = E_FAIL;
  }  
  else
  {
// The hollow succeeded so force an update of PS/Workshop
    m_pDocInterface->Update( TRUE );
    // Delete the created pmark
    m_pRollback->DeletePMark( pmark );
    hr = S_OK;
  }

// Now we can free up some memory
  
  // our faces array
if ( nFaces > 0 )
  {
    nFaces = 0;
    delete [] faces;
    faces = NULL;
  }

  // Our return arguments from the hollow
  PK_TOPOL_track_r_f( &tracking );
  PK_TOPOL_local_r_f( &results );

// And finally return the result of the hollow operation
return hr;
 

[back to top]

<<< Introduction Chapters Using COM in PS/Workshop >>>