Sempai-Dami1
Sempai-Dami1

Reputation: 11

How to create multiple property storage interfaces within an IStorage using these interfaces (IStorage, IPropertySetStorage, IPropertyStorage)?

I can create the IPropertySetStorage and IPropertyStorage interfaces using known FMTIDs (FMTID_SummaryInformation, FMTID_DocSummaryInformation, FMTID_UserDefinedProperties), but I cannot create my own FMTID file name. According to MSDN, the FMTID is the name of the stream that contains the properties within a set. Simply changing the stream's name would create a mismatch because IPropertySetStorage::Create(...) and IPropertySetStorage::Open(...) only uses the FMTID/GUID, which is internally translated to the file name. The common FMTIDs mentioned above are recognized, even though they would not translate to the appropriate file names if you were somehow able to force the algorithm to process them.

I worked out the problem, but I will still post the question since IStorage, IPropertySetStorage and IPropertyStorage do not currently have much useful search hits on StackOverflow or the Internet with respect to how to create your own FMTIDs using text. The algorithm on MSDN is interesting, but it leaves out some critical details, which I hope will be explained here.

For a success(hr), these conditions must be met:

Why? According to the algorithm on MSDN, the GUID is broken down to 128bits + 2 bits, subdivided into 5 bits. The 5 bits allows 32 possible characters per every 5 bits. The algorithm also explains that 130bits / 5bits gives you 27 characters to generate a filename (critical note: 128bits / 5bits gives you 26.6 characters, the last character of the file name is limited). So, 00000 is 'a', 00001 is 'b', and so on. When the algorithm gets to the last character to process, there are 2 extra bits. If the last character is not 00xxx then the algorithm fails. Thus, the last character must be (00000 = a) to (00111 = h). Somehow, internally, the algorithm and reverse algorithm can process upper and lower case letters so A-H are also acceptable as the last character and a-z, A-Z and 0-5 are acceptable as the first 25 letters in the filename. In summation, the algorithm actually gives you 25 5-bit characters and a partial 3-bit character to work with for a FMTID file name.

To ensure your file name is acceptable for the algorithm, you can condition the fname like this:

HRESULT hr1 = S_OK;

HRESULT hr2 = S_OK;

FMTID my_fmtid1 = GUID_NULL;

FMTID my_fmtid2 = GUID_NULL;

DWORD grfMode_new = (STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE);  

DWORD grfMode_open = (STGM_READWRITE | STGM_SHARE_EXCLUSIVE);  

DWORD grfFlags = PROPSETFLAG_DEFAULT;

WCHAR fmtid_fname1[28] = L"\005abcdefghijklmnopqrstuvwxy0\0";

WCHAR fmtid_fname2[28] = L"\005QWERTYUIOPASDFGHJKLZXCVBNM\0";

// condition 1: the file name must start with \005

fmtid_fname1[0] = L'\005'; 

// condition 2a, 2b: the file name must be 27 characters (this includes the \005)

// I chose 'z' but the appended characters can be a-z, A-Z, 0-5

StringCbCatW(fmtid_fname1, sizeof(fmtid_fname1), L"zzzzzzzzzzzzzzzzzzzzzzzzzz"); 

// condition 2c: the last character fname[26] must be a-h or A-H

// I chose 'a' as my preferred last character

fmtid_fname1[26] = L'a'; 

// revisit condition 2a: the file name must be 27 characters (this includes the \005)

fmtid_fname[27] = 0;

// at minimum the fname will be something like L"\005zzzzzzzzzzzzzzzzzzzzzzzzza"

hr1 = PropStgNameToFmtId(fmtid_fname1, &my_fmtid1); 

hr2 = PropStgNameToFmtId(fmtid_fname2, &my_fmtid2); 

if (my_fmtid1 == GUID_NULL) { /* recondition_your_fname1_message */ }

if (my_fmtid2 == GUID_NULL) { /* recondition_your_fname2_message */ }

// According to MSDN: the clsid should be the same as the fmtid

hr1 = pPropSet->Create(my_fmtid1, &my_fmtid1, grfFlags, grfMode_new, &pPropStrgGoup1);

hr2 = pPropSet->Create(my_fmtid2, &my_fmtid2, grfFlags, grfMode_new, &pPropStrgGoup2);

...now you've created two property set files using your own home brew file name scheme!

Upvotes: 0

Views: 139

Answers (1)

Dragan Radovac
Dragan Radovac

Reputation: 11

I would just like to build on Sempai-Dami1 Q/A and also extend my gratitude for the implementation .

The property storage operations I perform include writing and reading two LPWSTR properties and a third property as an array of LPWSTR. All properties are identified using PRSPEC_PROPID in the PROPSPEC array. To use a string for a name, simply replace PRSPEC_PROPID with PRSPEC_LPWSTR and the Id with your string.

I've enclosed all variables within each operation and have only done so for demonstration purposes. This means that you will have to set the filename in both Write and Read methods.

Also, be aware that the code was written with Visual Studio 98 using the C++ IDE. The reason for this was to ensure backward compatibility.


write.h

void
static
Write()
{       
    HRESULT hresult;                                    // result from property storage methods

    WCHAR* filename = L"d:\\storage.txt";               // write properties to the filename

    FMTID fmtid = {                                     // property set identifier FMTID { D170DF2E-1117-11D2-0XAA-0100805FFE11B8 }
        0xd170df2e, 0x1117, 0x11d2, { 
            0xaa, 0x01,  0x00,  0x80, 
            0x5f,  0xfe,  0x11,  0xb8 } };

    const int propspec_count = 3;                       // count of PROPSPEC/PROPVARIANT

    PROPSPEC propspec[propspec_count] = { {             // property identifier as array of PRSPEC_PROPID : (105|106|107)
        PRSPEC_PROPID, 105 }, {                         // first property identifier  
        PRSPEC_PROPID, 106 }, {                         // second property identifier 
        PRSPEC_PROPID, 107 } };                         // third property identifier 

    PROPVARIANT propvariant[propspec_count];            // array of PROPVARIANT that equal the number of PROPSPEC 

    IPropertySetStorage* propertysetstorage;            // pointer to IPropertySetStorage in nominated file 
    IPropertyStorage* propertystorage;                  // pointer to IPropertyStorage in nominated file    

    try
    {
        propvariant[0].vt = VT_LPWSTR;                  // first property vt type as LPWSTR
        propvariant[0].pwszVal = L"First value";        // property value as LPWSTR

        propvariant[1].vt = VT_LPWSTR;                  // second property vt type as LPWSTR
        propvariant[1].pwszVal = L"Second value";       // property value as LPWSTR

        propvariant[2].vt = VT_VECTOR | VT_LPWSTR;      // third property vt type as VT_VECTOR | VT_LPWSTR
        propvariant[2].calpwstr.cElems = 3;             // number of items in vector
        propvariant[2].calpwstr.pElems = 
            new LPWSTR[3]();                            // array of LPWSTR with a size of 3 (set it to the number of items you want)
        propvariant[2].calpwstr.pElems[0] = L"Item 1";  // item value as LPWSTR
        propvariant[2].calpwstr.pElems[1] = L"Item 2";  // item value as LPWSTR
        propvariant[2].calpwstr.pElems[2] = L"Item 3";  // item value as LPWSTR

        hresult =
            StgOpenStorageEx(                           // open file property set storage for read/write and return reference to IPropertySetStorage
                filename,                               // name and path of the file that holds property storage
                STGM_READWRITE | STGM_SHARE_EXCLUSIVE,  // open file for read/write operations exclusively
                STGFMT_ANY,                             // STGFMT_(STORAGE|0)(NATIVE|1)(FILE|3)(ANY|4)(DOCFILE|5)
                false,                                  // attributes set false
                NULL,                                   // options
                NULL,                                   // security descriptor 
                IID_IPropertySetStorage,                // interface definition
                (void**)&propertysetstorage);           // reference to a collection of IPropertySetStorage

        if (FAILED(hresult))
            throw L"Failed to open for write";

        hresult =
            propertysetstorage->Open(                   // open property set storage for read/write and return reference to IPropertyStorage
                fmtid,                                  // identifier of the property set as FMTID 
                STGM_READWRITE | STGM_SHARE_EXCLUSIVE,  // open property storage for read/write operations
                &propertystorage);                      // reference to IPropertyStorage

        if (FAILED(hresult))
            hresult =
                propertysetstorage->Create(             // create property set for read/write and return reference to IPropertyStorage
                    fmtid,                              // identifier of the property set as FMTID
                    &fmtid,                             // reference of the property set as FMTID
                    PROPSETFLAG_DEFAULT,                // PROPSETFLAG_(DEFAULT|0)(NONSIMPLE|1)(ANSI|2)(BUFFERED|4)
                    STGM_CREATE 
                        | STGM_READWRITE 
                        | STGM_SHARE_EXCLUSIVE,         // create property set storage with read/write operations
                    &propertystorage);                  // return reference to IPropertyStorage

        if (FAILED(hresult))
            throw L"Failed to open for write";
        
        hresult =
            propertystorage->WriteMultiple(             // write properties in arrays PROPVARIANT/PROPSPEC using propspec_count
                propspec_count,                         // count of PROPSPEC/PROPVARIANT
                propspec,                               // array of PROPSPEC with kind (propid or lpwstr) for each item
                propvariant,                            // array of PROPVARIANT with vt and value for each item
                false);                                 // write name first set to false

        if (FAILED(hresult))
            throw L"Failed to write values";

        hresult =
            propertystorage->Commit(                    // commit properties to property storage
                false);                                 // set flags for commit to false

        if (FAILED(hresult))
            throw L"Failed to commit";

        wprintf(                                        // print propertie values to console
            L"\n%s\n\t%s\n\t%s\n\tThird value",
            L"Writing:",
            propvariant[0].pwszVal,
            propvariant[1].pwszVal);

        for (unsigned int i = 0; 
                i < propvariant[2].calpwstr.cElems; 
                    i++)
            wprintf(                                    // print array items to console
                L"\n\t\t%s",
                propvariant[2].calpwstr.pElems[i]);

        wprintf(
            L"\n");
    }   
    catch (WCHAR* error)
    { 
        wprintf(
            L"\nError : %s\n\n", 
            error);
    }

    if (propertysetstorage != NULL)
        propertysetstorage->Release();                  // release property set storage

    if (propertystorage != NULL)
        propertystorage->Release();                     // release property storage
}

read.h

void
static
Read()
{
    HRESULT hresult;                                // result from property storage methods

    WCHAR* filename = L"d:\\storage.txt";           // read properties from the filename

    FMTID fmtid = {                                 // property set identifier FMTID { D170DF2E-1117-11D2-0XAA-0100805FFE11B8 }
        0xd170df2e, 0x1117, 0x11d2, { 
            0xaa, 0x01,  0x00,  0x80, 
            0x5f,  0xfe,  0x11,  0xb8 } };

    const int propspec_count = 3;                   // count of PROPSPEC/PROPVARIANT

    PROPSPEC propspec[propspec_count] = { {         // property identifier as array of PRSPEC_PROPID : (105|106|107)
        PRSPEC_PROPID, 105 }, {                     // first property identifier  
        PRSPEC_PROPID, 106 }, {                     // second property identifier 
        PRSPEC_PROPID, 107 } };                     // third property identifier 

    PROPVARIANT propvariant[propspec_count];        // array of PROPVARIANT that equal the number of PROPSPEC 

    IPropertySetStorage* propertysetstorage;        // pointer to IPropertySetStorage in nominated file 
    IPropertyStorage* propertystorage;              // pointer to IPropertyStorage in nominated file    

    try
    {
        hresult =
            StgOpenStorageEx(
                filename,                           // name and path of the file that holds property storage
                STGM_READ | STGM_SHARE_DENY_WRITE,  // open file for read operations and can be shared
                STGFMT_ANY,                         // STGFMT_(STORAGE|0)(NATIVE|1)(FILE|3)(ANY|4)(DOCFILE|5)
                false,                              // attributes set to false
                NULL,                               // options
                NULL,                               // security descriptor 
                IID_IPropertySetStorage,            // interface definition
                (void**)&propertysetstorage);       // reference to a collection of IPropertySetStorage

        if (FAILED(hresult))
            throw L"Failed to open for read";

        hresult =
            propertysetstorage->Open(
                fmtid,                              // identifier of the property set as FMTID 
                STGM_READ | STGM_SHARE_EXCLUSIVE,   // open file for read operations and can be shared
                &propertystorage);                  // reference to IPropertyStorage

        if (FAILED(hresult))
            throw L"Failed to open for read";

        hresult =
            propertystorage->ReadMultiple(
                propspec_count,                     // count of PROPSPEC/PROPVARIANT
                propspec,                           // vector of PROPSPEC with kind (propid or lpwstr) for each item
                propvariant);                       // vector of PROPVARIANT with vt and value for each item

        if (FAILED(hresult))
            throw L"Failed to read values";

        wprintf(                                    // print propertie values to console
            L"\n%s\n\t%s\n\t%s\n\tThird value",
            L"Reading:",
            propvariant[0].pwszVal,
            propvariant[1].pwszVal);

        for (unsigned int i = 0; 
                i < propvariant[2].calpwstr.cElems; 
                i++)
            wprintf(                                // print array items to console
                L"\n\t\t%s",
                propvariant[2].calpwstr.pElems[i]);

        wprintf(
            L"\n");
    }   
    catch (WCHAR* error)
    { 
        wprintf(
            L"\nError : %s\n\n", 
            error);
    }

    if (propertysetstorage != NULL)
        propertysetstorage->Release();              // release property set storage

    if (propertystorage != NULL)
        propertystorage->Release();                 // release property storage
}

storage.cpp

#include <stdio.h>
#include <ole2.h>
#include "write.h"
#include "read.h"

#pragma comment(lib, "ole32.lib")

extern 
void main()
{           
    Write();            // write properties to file (d:\\storage.txt)
    Read();             // read properties from file (d:\\storage.txt)
    
    char* input = 
        gets(new char); // read the character from the console
}

Result

Console window showing results

Upvotes: 1

Related Questions