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<IUnknown> pUnk;
::GetActiveObject(clsid,NULL,&pUnk);
CComPtr<Application> pApp;
Result = pUnk->QueryInterface(__uuidof(Application),
(void**)&pApp);
// Get active Document
CComPtr<Document> 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<Document> pDoc,
constchar* StorageName,
constchar* StreamName,
constchar* data)
{
HRESULT hr;
//Try to get Private Storage, if does not exist try to create
CComPtr<IUnknown> pUnk;
hr=pDoc->GetPrivateStorage(CComBSTR(StorageName),
VARIANT_TRUE,&pUnk);
if(hr != S_OK)
{
//ERROR: unable to create or open Storage
return hr;
}
CComQIPtr<IStorage> pStg(pUnk);
//Create stream within private storage
CComPtr<IStream> 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<Document> pDoc,
constchar* StorageName,
constchar* StreamName,
char* outData)
{
HRESULT hr;
//Try to get Private Storage, if does not exist fail
CComPtr<IUnknown> pUnk;
hr = pDoc->GetPrivateStorage(CComBSTR(StorageName),
VARIANT_FALSE,&pUnk);
if(hr != S_OK)
{
//ERROR: unable to open Storage
return hr;
}
CComQIPtr<IStorage> pStg(pUnk);
// Open stream within private storage
CComPtr<IStream> 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] = '\0';
return hr;
}
// Delete Private Stream
static HRESULT DeletePrivateStream(CComPtr<Document> pDoc,
constchar* StorageName,
constchar* Stream2Delete)
{
HRESULT hr ;
CComPtr<IUnknown> pUnk;
hr = pDoc->GetPrivateStorage(CComBSTR(StorageName),
VARIANT_FALSE,&pUnk);
if(hr != S_OK)
{
//ERROR: unable to open Storage
return hr;
}
CComQIPtr<IStorage> pStg(pUnk);
//Perform deletion of the stream element
hr = pStg->DestroyElement(CComBSTR(Stream2Delete));
pStg->Commit(STGC_DEFAULT);
return hr;
}