6) Creating a part document and a sketch

Top  Previous  Next

The first thing most users do with Inventor is to create a sketch inside a part document. Here is my function for creating a new Part document in Inventor:


HRESULT CreateNewPartDoc(CComPtr<Application> pInventorApp,

                        CComPtr<PartDocument> &pPartDoc,

                        const wchar_t* const pszPartName)


   HRESULT hr = S_OK;


   CComVariant pSubDocType;

   CComBSTR sTemplate;


   hr = pInventorApp->GetTemplateFile (kPartDocumentObject, // We are creating a PartDocument

                                       kMetricSystemOfMeasure, // mm





   if(hr != S_OK) {

       return hr;



   gLogger.Printf(ekLogMsg, L"Template file is <%s>\n", CString(sTemplate));


   CComPtr<Document> pDoc;

   hr = pInventorApp->Documents->Add (kPartDocumentObject,





   if (hr != S_OK) {

       return hr;



   pPartDoc = CComQIPtr<PartDocument>(pDoc);


   if ((pszPartName != nullptr) && (wcslen (pszPartName) > 0)) {

       BSTR bstrName ;

       pDoc->get_DisplayName (&bstrName) ;

       pPartDoc->DisplayName = pszPartName ;

       pDoc->get_DisplayName (&bstrName) ;

       ::SysFreeString (bstrName) ;



   return hr;



I'm assuming that we will always use wide char, wchar_t, UNICODE or CStrings for our string data. In the function above the part name is passed as a wchar_t pointer.


A "part" is usually a collection if solid objects with holes or cuts or extrusions. An assembly is usually a collection of parts, or more formally a set of occurrences of parts. See here for the file types in Inventor.


Unfortunately, the part document isn't really where all the "stuff" is. All the "stuff" is inside a PartComponentDefinition:



As you can see the PartComponentDefinition contains a lot of lists, and one of the lists contains sketches, and we'll create a new sketch in that list. But before we do that we have to get hold of the PartComponentDefinition. That is done like this:


   // Get the component definition of the part, where all the "stuff" is...

   CComPtr<PartComponentDefinition> pPartCompDef = nullptr ;

   hRes = pPartDoc->get_ComponentDefinition(&pPartCompDef) ;

   if (FAILED(hRes) || (pPartCompDef == nullptr)) {

       ShowCOMError (hRes,L" get_ComponentDefinition failed.");



I'll go into some detail about the these four lines because they illustrate how the Inventor API, C++ and COM talk to each other.


Many of the variables in C++ programs using COM are smart pointers. In the above example the smart pointer is pPartCompDef. Using smart pointers like this means that you don't need to allocate and deallocate them explicitly, it is all done by the smartness of the pointer. So when we do this:


   CComPtr<PartComponentDefinition>pPartCompDef = nullptr ;


we don't need to delete or release pPartCompDef after use, it will all happen automatically when the pointer goes out of scope.


Also note that I've initialised it to nullptr. This is my own personal style, it means it is initialised even if I forget to call a function to initialise it.


In general many functions we call in the Inventor API take the address of a pointer and initialise it, as well as returning a status. For example:


   hRes = pPartDoc->get_ComponentDefinition(&pPartCompDef) ;


pPartCompDef will be initialised to point at an object, and you'll get a return value placed in hRes. Why two return values? Because in some circumstances the pointer returned can be validly nullptr. hRes might return S_OK, because the function did not fail, but the pointer could still be nullptr because the object you were looking for does not exist. It is for that reason that I always look at both return values:


   if (FAILED(hRes) || (pPartCompDef == nullptr)) {


Click here for more about S_OK, HRESULT and FAILED.


7) Some tips...



Text, images and diagrams © 2021 Owen F. Ransen. All rights reserved. (But copy the source code as much as you want!)