Reputation: 11
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:
the file name must start with \005
the file name must be 27 characters (this includes the \005
)
the other 26 characters must be a-z
, A-Z
, 0-5
the last character fname[26]
must be a-h
or A-H
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
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.
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
}
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
}
#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
}
Console window showing results
Upvotes: 1