diff --git a/dlls/ole32/ole32.spec b/dlls/ole32/ole32.spec index 52d40f7d3c8..fedcc0e0086 100644 --- a/dlls/ole32/ole32.spec +++ b/dlls/ole32/ole32.spec @@ -96,6 +96,7 @@ @ stdcall -private DllUnregisterServer() @ stdcall DoDragDrop(ptr ptr long ptr) @ stub EnableHookObject +@ stdcall FmtIdToPropStgName(ptr wstr) @ stdcall FreePropVariantArray(long ptr) @ stdcall GetClassFile(wstr ptr) @ stdcall GetConvertStg(ptr) @@ -212,6 +213,7 @@ @ stdcall OleUninitialize() @ stub OpenOrCreateStream @ stdcall ProgIDFromCLSID(ptr ptr) +@ stdcall PropStgNameToFmtId(wstr ptr) @ stdcall PropSysAllocString(wstr) @ stdcall PropSysFreeString(wstr) @ stdcall PropVariantClear(ptr) diff --git a/dlls/ole32/stg_prop.c b/dlls/ole32/stg_prop.c index 248bd41d819..302840ad6b5 100644 --- a/dlls/ole32/stg_prop.c +++ b/dlls/ole32/stg_prop.c @@ -1796,70 +1796,6 @@ static HRESULT PropertyStorage_ConstructEmpty(IStream *stm, * Implementation of IPropertySetStorage */ -#define BITS_PER_BYTE 8 -#define CHARMASK 0x1f -#define BITS_IN_CHARMASK 5 - -/* Converts rfmtid to a string and returns the resulting string. If rfmtid - * is a well-known FMTID, it just returns a static string. Otherwise it - * creates the appropriate string name in str, which must be 27 characters - * in length, and returns str. - * Based on the algorithm described here: - * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp - */ -static LPCWSTR format_id_to_name(REFFMTID rfmtid, LPWSTR str) -{ - static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345"; - static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y', - 'I','n','f','o','r','m','a','t','i','o','n',0 }; - static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t', - 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n', - 0 }; - LPCWSTR ret; - - if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid)) - ret = szSummaryInfo; - else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid)) - ret = szDocSummaryInfo; - else - { - BYTE *fmtptr; - WCHAR *pstr = str; - ULONG bitsRemaining = BITS_PER_BYTE; - - *pstr++ = 5; - for (fmtptr = (BYTE *)rfmtid; fmtptr < (BYTE *)rfmtid + sizeof(FMTID); ) - { - ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining); - - if (bitsRemaining >= BITS_IN_CHARMASK) - { - *pstr = (WCHAR)(fmtMap[i & CHARMASK]); - if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' && - *pstr <= 'z') - *pstr += 'A' - 'a'; - pstr++; - bitsRemaining -= BITS_IN_CHARMASK; - if (bitsRemaining == 0) - { - fmtptr++; - bitsRemaining = BITS_PER_BYTE; - } - } - else - { - if (++fmtptr < (BYTE *)rfmtid + sizeof(FMTID)) - i |= *fmtptr << bitsRemaining; - *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]); - } - } - *pstr = 0; - ret = str; - } - TRACE("returning %s\n", debugstr_w(ret)); - return ret; -} - /************************************************************************ * IPropertySetStorage_fnQueryInterface (IUnknown) * @@ -1910,8 +1846,7 @@ static HRESULT WINAPI IPropertySetStorage_fnCreate( IPropertyStorage** ppprstg) { _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg); - WCHAR nameBuf[27]; - LPCWSTR name = NULL; + WCHAR name[CCH_MAX_PROPSTG_NAME]; IStream *stm = NULL; HRESULT r; @@ -1941,7 +1876,9 @@ static HRESULT WINAPI IPropertySetStorage_fnCreate( goto end; } - name = format_id_to_name(rfmtid, nameBuf); + r = FmtIdToPropStgName(rfmtid, name); + if (FAILED(r)) + goto end; r = IStorage_CreateStream( (IStorage*)This, name, grfMode, 0, 0, &stm ); if (FAILED(r)) @@ -1965,8 +1902,7 @@ static HRESULT WINAPI IPropertySetStorage_fnOpen( { _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg); IStream *stm = NULL; - WCHAR nameBuf[27]; - LPCWSTR name = NULL; + WCHAR name[CCH_MAX_PROPSTG_NAME]; HRESULT r; TRACE("%p %s %08lx %p\n", This, debugstr_guid(rfmtid), grfMode, ppprstg); @@ -1985,7 +1921,9 @@ static HRESULT WINAPI IPropertySetStorage_fnOpen( goto end; } - name = format_id_to_name(rfmtid, nameBuf); + r = FmtIdToPropStgName(rfmtid, name); + if (FAILED(r)) + goto end; r = IStorage_OpenStream((IStorage*) This, name, 0, grfMode, 0, &stm ); if (FAILED(r)) @@ -2007,17 +1945,17 @@ static HRESULT WINAPI IPropertySetStorage_fnDelete( { _ICOM_THIS_From_IPropertySetStorage(StorageImpl, ppstg); IStorage *stg = NULL; - WCHAR nameBuf[27]; - LPCWSTR name = NULL; + WCHAR name[CCH_MAX_PROPSTG_NAME]; + HRESULT r; TRACE("%p %s\n", This, debugstr_guid(rfmtid)); if (!rfmtid) return E_INVALIDARG; - name = format_id_to_name(rfmtid, nameBuf); - if (!name) - return STG_E_FILENOTFOUND; + r = FmtIdToPropStgName(rfmtid, name); + if (FAILED(r)) + return r; stg = (IStorage*) This; return IStorage_DestroyElement(stg, name); @@ -2068,3 +2006,172 @@ static IPropertyStorageVtbl IPropertyStorage_Vtbl = IPropertyStorage_fnSetClass, IPropertyStorage_fnStat, }; + +/*********************************************************************** + * Format ID <-> name conversion + */ +static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y', + 'I','n','f','o','r','m','a','t','i','o','n',0 }; +static const WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t', + 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0 }; + +#define BITS_PER_BYTE 8 +#define CHARMASK 0x1f +#define BITS_IN_CHARMASK 5 +#define NUM_ALPHA_CHARS 26 + +/*********************************************************************** + * FmtIdToPropStgName [ole32.@] + * Returns the storage name of the format ID rfmtid. + * PARAMS + * rfmtid [I] Format ID for which to return a storage name + * str [O] Storage name associated with rfmtid. + * + * RETURNS + * E_INVALIDARG if rfmtid or str i NULL, S_OK otherwise. + * + * NOTES + * str must be at least CCH_MAX_PROPSTG_NAME characters in length. + * Based on the algorithm described here: + * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp + */ +HRESULT WINAPI FmtIdToPropStgName(const FMTID *rfmtid, LPOLESTR str) +{ + static const char fmtMap[] = "abcdefghijklmnopqrstuvwxyz012345"; + + TRACE("%s, %p\n", debugstr_guid(rfmtid), str); + + if (!rfmtid) return E_INVALIDARG; + if (!str) return E_INVALIDARG; + + if (IsEqualGUID(&FMTID_SummaryInformation, rfmtid)) + lstrcpyW(str, szSummaryInfo); + else if (IsEqualGUID(&FMTID_DocSummaryInformation, rfmtid)) + lstrcpyW(str, szDocSummaryInfo); + else if (IsEqualGUID(&FMTID_UserDefinedProperties, rfmtid)) + lstrcpyW(str, szDocSummaryInfo); + else + { + BYTE *fmtptr; + WCHAR *pstr = str; + ULONG bitsRemaining = BITS_PER_BYTE; + + *pstr++ = 5; + for (fmtptr = (BYTE *)rfmtid; fmtptr < (BYTE *)rfmtid + sizeof(FMTID); ) + { + ULONG i = *fmtptr >> (BITS_PER_BYTE - bitsRemaining); + + if (bitsRemaining >= BITS_IN_CHARMASK) + { + *pstr = (WCHAR)(fmtMap[i & CHARMASK]); + if (bitsRemaining == BITS_PER_BYTE && *pstr >= 'a' && + *pstr <= 'z') + *pstr += 'A' - 'a'; + pstr++; + bitsRemaining -= BITS_IN_CHARMASK; + if (bitsRemaining == 0) + { + fmtptr++; + bitsRemaining = BITS_PER_BYTE; + } + } + else + { + if (++fmtptr < (BYTE *)rfmtid + sizeof(FMTID)) + i |= *fmtptr << bitsRemaining; + *pstr++ = (WCHAR)(fmtMap[i & CHARMASK]); + bitsRemaining += BITS_PER_BYTE - BITS_IN_CHARMASK; + } + } + *pstr = 0; + } + TRACE("returning %s\n", debugstr_w(str)); + return S_OK; +} + +/*********************************************************************** + * PropStgNameToFmtId [ole32.@] + * Returns the format ID corresponding to the given name. + * PARAMS + * str [I] Storage name to convert to a format ID. + * rfmtid [O] Format ID corresponding to str. + * + * RETURNS + * E_INVALIDARG if rfmtid or str is NULL or if str can't be converted to + * a format ID, S_OK otherwise. + * + * NOTES + * Based on the algorithm described here: + * http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp + */ +HRESULT WINAPI PropStgNameToFmtId(const LPOLESTR str, FMTID *rfmtid) +{ + HRESULT hr = E_INVALIDARG; + + TRACE("%s, %p\n", debugstr_w(str), rfmtid); + + if (!rfmtid) return E_INVALIDARG; + if (!str) return E_INVALIDARG; + + if (!lstrcmpiW(str, szDocSummaryInfo)) + { + memcpy(rfmtid, &FMTID_DocSummaryInformation, sizeof(*rfmtid)); + hr = S_OK; + } + else if (!lstrcmpiW(str, szSummaryInfo)) + { + memcpy(rfmtid, &FMTID_SummaryInformation, sizeof(*rfmtid)); + hr = S_OK; + } + else + { + ULONG bits; + BYTE *fmtptr = (BYTE *)rfmtid - 1; + const WCHAR *pstr = str; + + memset(rfmtid, 0, sizeof(*rfmtid)); + for (bits = 0; bits < sizeof(FMTID) * BITS_PER_BYTE; + bits += BITS_IN_CHARMASK) + { + ULONG bitsUsed = bits % BITS_PER_BYTE, bitsStored; + WCHAR wc; + + if (bitsUsed == 0) + fmtptr++; + wc = *++pstr - 'A'; + if (wc > NUM_ALPHA_CHARS) + { + wc += 'A' - 'a'; + if (wc > NUM_ALPHA_CHARS) + { + wc += 'a' - '0' + NUM_ALPHA_CHARS; + if (wc > CHARMASK) + { + WARN("invalid character (%d)\n", *pstr); + goto end; + } + } + } + *fmtptr |= wc << bitsUsed; + bitsStored = min(BITS_PER_BYTE - bitsUsed, BITS_IN_CHARMASK); + if (bitsStored < BITS_IN_CHARMASK) + { + wc >>= BITS_PER_BYTE - bitsUsed; + if (bits + bitsStored == sizeof(FMTID) * BITS_PER_BYTE) + { + if (wc != 0) + { + WARN("extra bits\n"); + goto end; + } + break; + } + fmtptr++; + *fmtptr |= (BYTE)wc; + } + } + hr = S_OK; + } +end: + return hr; +} diff --git a/dlls/ole32/tests/stg_prop.c b/dlls/ole32/tests/stg_prop.c index 9a546d6ff4e..43d9dc9379c 100644 --- a/dlls/ole32/tests/stg_prop.c +++ b/dlls/ole32/tests/stg_prop.c @@ -244,7 +244,77 @@ static void testProps(void) DeleteFileW(filename); } +static void testFmtId(void) +{ + WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y', + 'I','n','f','o','r','m','a','t','i','o','n',0 }; + WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t', + 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n', + 0 }; + WCHAR szIID_IPropSetStg[] = { 5,'0','j','a','a','a','a','a', + 'a','A','a','a','a','a','a','d','a','A','a','a','a','a','a','a','a','G', + 'c',0 }; + WCHAR name[32]; + FMTID fmtid; + HRESULT hr; + + hr = FmtIdToPropStgName(NULL, name); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr); + hr = FmtIdToPropStgName(&FMTID_SummaryInformation, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr); + hr = FmtIdToPropStgName(&FMTID_SummaryInformation, name); + ok(SUCCEEDED(hr), "FmtIdToPropStgName failed: 0x%08lx\n", hr); + ok(!memcmp(name, szSummaryInfo, (lstrlenW(szSummaryInfo) + 1) * + sizeof(WCHAR)), "Got wrong name for FMTID_SummaryInformation\n"); + hr = FmtIdToPropStgName(&FMTID_DocSummaryInformation, name); + ok(SUCCEEDED(hr), "FmtIdToPropStgName failed: 0x%08lx\n", hr); + ok(!memcmp(name, szDocSummaryInfo, (lstrlenW(szDocSummaryInfo) + 1) * + sizeof(WCHAR)), "Got wrong name for FMTID_DocSummaryInformation\n"); + hr = FmtIdToPropStgName(&FMTID_UserDefinedProperties, name); + ok(SUCCEEDED(hr), "FmtIdToPropStgName failed: 0x%08lx\n", hr); + ok(!memcmp(name, szDocSummaryInfo, (lstrlenW(szDocSummaryInfo) + 1) * + sizeof(WCHAR)), "Got wrong name for FMTID_DocSummaryInformation\n"); + hr = FmtIdToPropStgName(&IID_IPropertySetStorage, name); + ok(SUCCEEDED(hr), "FmtIdToPropStgName failed: 0x%08lx\n", hr); + ok(!memcmp(name, szIID_IPropSetStg, (lstrlenW(szIID_IPropSetStg) + 1) * + sizeof(WCHAR)), "Got wrong name for IID_IPropertySetStorage\n"); + + /* test args first */ + hr = PropStgNameToFmtId(NULL, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr); + hr = PropStgNameToFmtId(NULL, &fmtid); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr); + hr = PropStgNameToFmtId(szDocSummaryInfo, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08lx\n", hr); + /* test the known format IDs */ + hr = PropStgNameToFmtId(szSummaryInfo, &fmtid); + ok(SUCCEEDED(hr), "PropStgNameToFmtId failed: 0x%08lx\n", hr); + ok(!memcmp(&fmtid, &FMTID_SummaryInformation, sizeof(fmtid)), + "Got unexpected FMTID, expected FMTID_SummaryInformation\n"); + hr = PropStgNameToFmtId(szDocSummaryInfo, &fmtid); + ok(SUCCEEDED(hr), "PropStgNameToFmtId failed: 0x%08lx\n", hr); + ok(!memcmp(&fmtid, &FMTID_DocSummaryInformation, sizeof(fmtid)), + "Got unexpected FMTID, expected FMTID_DocSummaryInformation\n"); + /* test another GUID */ + hr = PropStgNameToFmtId(szIID_IPropSetStg, &fmtid); + ok(SUCCEEDED(hr), "PropStgNameToFmtId failed: 0x%08lx\n", hr); + ok(!memcmp(&fmtid, &IID_IPropertySetStorage, sizeof(fmtid)), + "Got unexpected FMTID, expected IID_IPropertySetStorage\n"); + /* now check case matching */ + CharUpperW(szDocSummaryInfo + 1); + hr = PropStgNameToFmtId(szDocSummaryInfo, &fmtid); + ok(SUCCEEDED(hr), "PropStgNameToFmtId failed: 0x%08lx\n", hr); + ok(!memcmp(&fmtid, &FMTID_DocSummaryInformation, sizeof(fmtid)), + "Got unexpected FMTID, expected FMTID_DocSummaryInformation\n"); + CharUpperW(szIID_IPropSetStg + 1); + hr = PropStgNameToFmtId(szIID_IPropSetStg, &fmtid); + ok(SUCCEEDED(hr), "PropStgNameToFmtId failed: 0x%08lx\n", hr); + ok(!memcmp(&fmtid, &IID_IPropertySetStorage, sizeof(fmtid)), + "Got unexpected FMTID, expected IID_IPropertySetStorage\n"); +} + START_TEST(stg_prop) { testProps(); + testFmtId(); }