How to use private storage and stream in C++

By Xiaodong Liang

Note: the solution in this article uses the the wrapped functions of Inventor API with IStorage and IStream which require to make the document dirty. If you do not want to use Inventor API, please refer to the other article. 

MSDN: "The IStorage interface supports the creation and management of structured storage objects. Structured storage allows hierarchical storage of information within a single file, and is often referred to as "a file system within a file". Elements of a structured storage object are storages and streams. Storages are analogous to directories, and streams are analogous to files. Within a structured storage there will be a primary storage object that may contain substorages, possibly nested, and streams. Storages provide the structure of the object, and streams contain the data, which is manipulated through the IStream interface."

The Inventor API allows the use of Private Storages and Streams inside Inventor documents, but it encapsulates the COM Interfaces IStorage and IStream in a way that make it easier to manipulate.

For further details about the COM interfaces, please refer to the online Microsoft documentation:

IStorage: http://msdn2.microsoft.com/en-us/library/aa380015.aspx
IStream: http://msdn2.microsoft.com/en-us/library/aa380034.aspx

Important Notes:

  • When Inventor opens a file, it reads in the embedded streams and edit it in memory, and writes it back out on save. If you edit this storage using the Microsoft structured storage APIs directly on the file root storage opened off disk, your changes would be overwritten if the file was also opened in Inventor and saved after your changes.  Also changes made like this would not be realized in the in memory open document until the document is closed and reopened (if it was not saved and overwrote the changes). For this reason you should not use the Microsoft API directly on an open Inventor document, use the exposed Inventor API below instead.

  • Also, manipulating the file storage is not a transacted operation, i.e. you cannot use the undo/redo mechanism after a change. For this reason, any modification to the file storage will not set it as 'Dirty', so simply saving and closing the file through Inventor will result in the lost of these changes. In order to keep the storage modifications across sessions, you will need to manually set the 'Dirty' flag to True prior to performing the save. This is illustrated in the sample below.  

The following C++ sample contains three methods that illustrate how to create a storage and a stream inside it, write and read from it and also delete the stream. It is from a standard alone EXE. It assumes Inventor has been launched.

// manipulate Private Stream
static HRESULT PrivateStream()
{
     HRESULT Result=NOERROR;
 
    Result = ::CoInitialize (NULL); 
 
    // get active inventor application
   CLSID clsid;
   ::CLSIDFromProgID(L"Inventor.Application",
                        &clsid);
 
   CComPtr pUnk;
   ::GetActiveObject(clsid,NULL,&pUnk);
 
   CComPtr pApp;
   Result = pUnk->QueryInterface(__uuidof(Application),
                                (void**)&pApp); 
 
   // Get active Document
   CComPtr pDoc;
   Result = pApp->get_ActiveDocument(&pDoc);
 
    char outData[256];
    //COM Stream
    Result = CreatePrivateStorageAndStream(pDoc, 
                                "MyPrvStorage1", 
                                "MyStream1", 
                                "Some private stored data");
 
   if(Result != S_OK)
  {
      //ERROR: unable to create Stream
      return Result;
  }
 
   Result = ReadPrivateStorageAndStream(pDoc, 
                                    "MyPrvStorage1",
                                    "MyStream1",                                    
                                    outData);
 
   if(Result != S_OK)
  {
      //ERROR: unable to read Stream
      return Result;
  }
 
 Result = DeletePrivateStream(pDoc, 
                        "MyPrvStorage1",
                        "MyStream1");
 
 if(Result != S_OK)
 {
  //ERROR: unable to delete Stream
  return Result;
 }
 
 //Set document to Dirty prior to perform the Save
 //otherwise the stream won't be save through sessions
 pDoc->Dirty = VARIANT_TRUE;
 pDoc->Save();
 
 
wrapup:
 ::CoUninitialize(); 
}
//Create Private Storage And Stream
 static HRESULT CreatePrivateStorageAndStream(
                            CComPtr pDoc, 
                             constchar* StorageName, 
                             constchar* StreamName, 
                             constchar* data)
{
     HRESULT hr;
 
    //Try to get Private Storage, if does not exist try to create
    CComPtr pUnk;
    hr=pDoc->GetPrivateStorage(CComBSTR(StorageName),
                                 VARIANT_TRUE,&pUnk);
 
   if(hr != S_OK)
  {
       //ERROR: unable to create or open Storage
       return hr;
  }
 
  CComQIPtr pStg(pUnk);
 
  //Create stream within private storage
  CComPtr pStream = NULL;
  hr=pStg->CreateStream(CComBSTR(StreamName),
                            STGM_DIRECT|STGM_CREATE|
                            STGM_READWRITE|STGM_SHARE_EXCLUSIVE,
                            0, 0, &pStream);
 
  if(hr != S_OK)
  {
       //ERROR: unable to create Stream
      return hr;
 }
 
  ULONG lsize=0;
  lsize = strlen(data);
  hr=pStream->Write( &lsize,sizeof(int), NULL ) ;
  hr=pStream->Write( data, strlen(data), NULL ) ;
  // Save the data
  hr=pStream->Commit(STGC_DEFAULT|STGC_OVERWRITE); 
 
   //Don't forget to commit changes also in storage
   hr=pStg->Commit(STGC_DEFAULT|STGC_OVERWRITE);
 
   return hr;
}
 
// Read Private Storage And Stream
static HRESULT ReadPrivateStorageAndStream(CComPtr pDoc, 
                                constchar* StorageName, 
                                constchar* StreamName, 
                                char* outData)
{
     HRESULT hr;
 
    //Try to get Private Storage, if does not exist fail
    CComPtr pUnk;
    hr = pDoc->GetPrivateStorage(CComBSTR(StorageName),
                                    VARIANT_FALSE,&pUnk);
 
   if(hr != S_OK)
   {
      //ERROR: unable to open Storage
      return hr;
   }
 
   CComQIPtr pStg(pUnk);
 
   // Open stream within private storage
   CComPtr pStream = NULL;
   hr = pStg->OpenStream(CComBSTR(StreamName),0,
                                    STGM_DIRECT|STGM_READ|
                                    STGM_SHARE_EXCLUSIVE, 0,
                                    &pStream);
 
   if(hr != S_OK)
  {
      //ERROR: unable to open Stream
      return hr;
   }
 
   ULONG lsize = 0;
   hr = pStream->Read( &lsize,sizeof(int), NULL);
   hr = pStream->Read( outData,lsize, NULL ) ;
 
   outData[lsize] = '';
 
   return hr;
}
 
// Delete Private Stream
static HRESULT DeletePrivateStream(CComPtr pDoc,
                                constchar* StorageName, 
                                constchar* Stream2Delete)
{
    HRESULT hr ;
 
    CComPtr pUnk;
    hr = pDoc->GetPrivateStorage(CComBSTR(StorageName),
                                VARIANT_FALSE,&pUnk);
 
   if(hr != S_OK)
  {
      //ERROR: unable to open Storage
      return hr;
   }
 
   CComQIPtr pStg(pUnk);
 
   //Perform deletion of the stream element
   hr = pStg->DestroyElement(CComBSTR(Stream2Delete));
 
   pStg->Commit(STGC_DEFAULT);
 
   return hr;
}

Comments

One response to “How to use private storage and stream in C++”

  1. Thanks for sharing this with me. I’ve been looking into some new ways of storage. I feel like this article really helped me out. http://www.midwaymoving.com/secured-storage/private-storage

Leave a Reply

Discover more from Autodesk Developer Blog

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

Continue reading