📄 stg_prop.c
字号:
return hr;
}
static HRESULT PropertyStorage_ReadSectionHeaderFromStream(IStream *stm,
PROPERTYSECTIONHEADER *hdr)
{
BYTE buf[sizeof(PROPERTYSECTIONHEADER)];
ULONG count = 0;
HRESULT hr;
assert(stm);
assert(hdr);
hr = IStream_Read(stm, buf, sizeof(buf), &count);
if (SUCCEEDED(hr))
{
if (count != sizeof(buf))
{
WARN("read only %d\n", count);
hr = STG_E_INVALIDHEADER;
}
else
{
StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
cbSection), &hdr->cbSection);
StorageUtl_ReadDWord(buf, offsetof(PROPERTYSECTIONHEADER,
cProperties), &hdr->cProperties);
}
}
TRACE("returning 0x%08x\n", hr);
return hr;
}
static HRESULT PropertyStorage_ReadFromStream(PropertyStorage_impl *This)
{
PROPERTYSETHEADER hdr;
FORMATIDOFFSET fmtOffset;
PROPERTYSECTIONHEADER sectionHdr;
LARGE_INTEGER seek;
ULONG i;
STATSTG stat;
HRESULT hr;
BYTE *buf = NULL;
ULONG count = 0;
DWORD dictOffset = 0;
This->dirty = FALSE;
This->highestProp = 0;
hr = IStream_Stat(This->stm, &stat, STATFLAG_NONAME);
if (FAILED(hr))
goto end;
if (stat.cbSize.u.HighPart)
{
WARN("stream too big\n");
/* maximum size varies, but it can't be this big */
hr = STG_E_INVALIDHEADER;
goto end;
}
if (stat.cbSize.u.LowPart == 0)
{
/* empty stream is okay */
hr = S_OK;
goto end;
}
else if (stat.cbSize.u.LowPart < sizeof(PROPERTYSETHEADER) +
sizeof(FORMATIDOFFSET))
{
WARN("stream too small\n");
hr = STG_E_INVALIDHEADER;
goto end;
}
seek.QuadPart = 0;
hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
goto end;
hr = PropertyStorage_ReadHeaderFromStream(This->stm, &hdr);
/* I've only seen reserved == 1, but the article says I shouldn't disallow
* higher values.
*/
if (hdr.wByteOrder != PROPSETHDR_BYTEORDER_MAGIC || hdr.reserved < 1)
{
WARN("bad magic in prop set header\n");
hr = STG_E_INVALIDHEADER;
goto end;
}
if (hdr.wFormat != 0 && hdr.wFormat != 1)
{
WARN("bad format version %d\n", hdr.wFormat);
hr = STG_E_INVALIDHEADER;
goto end;
}
This->format = hdr.wFormat;
memcpy(&This->clsid, &hdr.clsid, sizeof(This->clsid));
This->originatorOS = hdr.dwOSVer;
if (PROPSETHDR_OSVER_KIND(hdr.dwOSVer) == PROPSETHDR_OSVER_KIND_MAC)
WARN("File comes from a Mac, strings will probably be screwed up\n");
hr = PropertyStorage_ReadFmtIdOffsetFromStream(This->stm, &fmtOffset);
if (FAILED(hr))
goto end;
if (fmtOffset.dwOffset > stat.cbSize.u.LowPart)
{
WARN("invalid offset %d (stream length is %d)\n", fmtOffset.dwOffset,
stat.cbSize.u.LowPart);
hr = STG_E_INVALIDHEADER;
goto end;
}
/* wackiness alert: if the format ID is FMTID_DocSummaryInformation, there
* follow not one, but two sections. The first is the standard properties
* for the document summary information, and the second is user-defined
* properties. This is the only case in which multiple sections are
* allowed.
* Reading the second stream isn't implemented yet.
*/
hr = PropertyStorage_ReadSectionHeaderFromStream(This->stm, §ionHdr);
if (FAILED(hr))
goto end;
/* The section size includes the section header, so check it */
if (sectionHdr.cbSection < sizeof(PROPERTYSECTIONHEADER))
{
WARN("section header too small, got %d\n", sectionHdr.cbSection);
hr = STG_E_INVALIDHEADER;
goto end;
}
buf = HeapAlloc(GetProcessHeap(), 0, sectionHdr.cbSection -
sizeof(PROPERTYSECTIONHEADER));
if (!buf)
{
hr = STG_E_INSUFFICIENTMEMORY;
goto end;
}
hr = IStream_Read(This->stm, buf, sectionHdr.cbSection -
sizeof(PROPERTYSECTIONHEADER), &count);
if (FAILED(hr))
goto end;
TRACE("Reading %d properties:\n", sectionHdr.cProperties);
for (i = 0; SUCCEEDED(hr) && i < sectionHdr.cProperties; i++)
{
PROPERTYIDOFFSET *idOffset = (PROPERTYIDOFFSET *)(buf +
i * sizeof(PROPERTYIDOFFSET));
if (idOffset->dwOffset < sizeof(PROPERTYSECTIONHEADER) ||
idOffset->dwOffset >= sectionHdr.cbSection - sizeof(DWORD))
hr = STG_E_INVALIDPOINTER;
else
{
if (idOffset->propid >= PID_FIRST_USABLE &&
idOffset->propid < PID_MIN_READONLY && idOffset->propid >
This->highestProp)
This->highestProp = idOffset->propid;
if (idOffset->propid == PID_DICTIONARY)
{
/* Don't read the dictionary yet, its entries depend on the
* code page. Just store the offset so we know to read it
* later.
*/
dictOffset = idOffset->dwOffset;
TRACE("Dictionary offset is %d\n", dictOffset);
}
else
{
PROPVARIANT prop;
PropVariantInit(&prop);
if (SUCCEEDED(PropertyStorage_ReadProperty(This, &prop,
buf + idOffset->dwOffset - sizeof(PROPERTYSECTIONHEADER))))
{
TRACE("Read property with ID 0x%08x, type %d\n",
idOffset->propid, prop.vt);
switch(idOffset->propid)
{
case PID_CODEPAGE:
if (prop.vt == VT_I2)
This->codePage = (UINT)prop.u.iVal;
break;
case PID_LOCALE:
if (prop.vt == VT_I4)
This->locale = (LCID)prop.u.lVal;
break;
case PID_BEHAVIOR:
if (prop.vt == VT_I4 && prop.u.lVal)
This->grfFlags |= PROPSETFLAG_CASE_SENSITIVE;
/* The format should already be 1, but just in case */
This->format = 1;
break;
default:
hr = PropertyStorage_StorePropWithId(This,
idOffset->propid, &prop, This->codePage);
}
}
}
}
}
if (!This->codePage)
{
/* default to Unicode unless told not to, as specified here:
* http://msdn.microsoft.com/library/en-us/stg/stg/names_in_istorage.asp
*/
if (This->grfFlags & PROPSETFLAG_ANSI)
This->codePage = GetACP();
else
This->codePage = CP_UNICODE;
}
if (!This->locale)
This->locale = LOCALE_SYSTEM_DEFAULT;
TRACE("Code page is %d, locale is %d\n", This->codePage, This->locale);
if (dictOffset)
hr = PropertyStorage_ReadDictionary(This,
buf + dictOffset - sizeof(PROPERTYSECTIONHEADER));
end:
HeapFree(GetProcessHeap(), 0, buf);
if (FAILED(hr))
{
dictionary_destroy(This->name_to_propid);
This->name_to_propid = NULL;
dictionary_destroy(This->propid_to_name);
This->propid_to_name = NULL;
dictionary_destroy(This->propid_to_prop);
This->propid_to_prop = NULL;
}
return hr;
}
static void PropertyStorage_MakeHeader(PropertyStorage_impl *This,
PROPERTYSETHEADER *hdr)
{
assert(hdr);
StorageUtl_WriteWord((BYTE *)&hdr->wByteOrder, 0,
PROPSETHDR_BYTEORDER_MAGIC);
StorageUtl_WriteWord((BYTE *)&hdr->wFormat, 0, This->format);
StorageUtl_WriteDWord((BYTE *)&hdr->dwOSVer, 0, This->originatorOS);
StorageUtl_WriteGUID((BYTE *)&hdr->clsid, 0, &This->clsid);
StorageUtl_WriteDWord((BYTE *)&hdr->reserved, 0, 1);
}
static void PropertyStorage_MakeFmtIdOffset(PropertyStorage_impl *This,
FORMATIDOFFSET *fmtOffset)
{
assert(fmtOffset);
StorageUtl_WriteGUID((BYTE *)fmtOffset, 0, &This->fmtid);
StorageUtl_WriteDWord((BYTE *)fmtOffset, offsetof(FORMATIDOFFSET, dwOffset),
sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET));
}
static void PropertyStorage_MakeSectionHdr(DWORD cbSection, DWORD numProps,
PROPERTYSECTIONHEADER *hdr)
{
assert(hdr);
StorageUtl_WriteDWord((BYTE *)hdr, 0, cbSection);
StorageUtl_WriteDWord((BYTE *)hdr,
offsetof(PROPERTYSECTIONHEADER, cProperties), numProps);
}
static void PropertyStorage_MakePropertyIdOffset(DWORD propid, DWORD dwOffset,
PROPERTYIDOFFSET *propIdOffset)
{
assert(propIdOffset);
StorageUtl_WriteDWord((BYTE *)propIdOffset, 0, propid);
StorageUtl_WriteDWord((BYTE *)propIdOffset,
offsetof(PROPERTYIDOFFSET, dwOffset), dwOffset);
}
struct DictionaryClosure
{
HRESULT hr;
DWORD bytesWritten;
};
static BOOL PropertyStorage_DictionaryWriter(const void *key,
const void *value, void *extra, void *closure)
{
PropertyStorage_impl *This = (PropertyStorage_impl *)extra;
struct DictionaryClosure *c = (struct DictionaryClosure *)closure;
DWORD propid;
ULONG count;
assert(key);
assert(closure);
StorageUtl_WriteDWord((LPBYTE)&propid, 0, (DWORD)value);
c->hr = IStream_Write(This->stm, &propid, sizeof(propid), &count);
if (FAILED(c->hr))
goto end;
c->bytesWritten += sizeof(DWORD);
if (This->codePage == CP_UNICODE)
{
DWORD keyLen, pad = 0;
StorageUtl_WriteDWord((LPBYTE)&keyLen, 0,
(lstrlenW((LPCWSTR)key) + 1) * sizeof(WCHAR));
c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
if (FAILED(c->hr))
goto end;
c->bytesWritten += sizeof(DWORD);
/* Rather than allocate a copy, I'll swap the string to little-endian
* in-place, write it, then swap it back.
*/
PropertyStorage_ByteSwapString(key, keyLen);
c->hr = IStream_Write(This->stm, key, keyLen, &count);
PropertyStorage_ByteSwapString(key, keyLen);
if (FAILED(c->hr))
goto end;
c->bytesWritten += keyLen;
if (keyLen % sizeof(DWORD))
{
c->hr = IStream_Write(This->stm, &pad,
sizeof(DWORD) - keyLen % sizeof(DWORD), &count);
if (FAILED(c->hr))
goto end;
c->bytesWritten += sizeof(DWORD) - keyLen % sizeof(DWORD);
}
}
else
{
DWORD keyLen;
StorageUtl_WriteDWord((LPBYTE)&keyLen, 0, strlen((LPCSTR)key) + 1);
c->hr = IStream_Write(This->stm, &keyLen, sizeof(keyLen), &count);
if (FAILED(c->hr))
goto end;
c->bytesWritten += sizeof(DWORD);
c->hr = IStream_Write(This->stm, key, keyLen, &count);
if (FAILED(c->hr))
goto end;
c->bytesWritten += keyLen;
}
end:
return SUCCEEDED(c->hr);
}
#define SECTIONHEADER_OFFSET sizeof(PROPERTYSETHEADER) + sizeof(FORMATIDOFFSET)
/* Writes the dictionary to the stream. Assumes without checking that the
* dictionary isn't empty.
*/
static HRESULT PropertyStorage_WriteDictionaryToStream(
PropertyStorage_impl *This, DWORD *sectionOffset)
{
HRESULT hr;
LARGE_INTEGER seek;
PROPERTYIDOFFSET propIdOffset;
ULONG count;
DWORD dwTemp;
struct DictionaryClosure closure;
assert(sectionOffset);
/* The dictionary's always the first property written, so seek to its
* spot.
*/
seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER);
hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
goto end;
PropertyStorage_MakePropertyIdOffset(PID_DICTIONARY, *sectionOffset,
&propIdOffset);
hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
if (FAILED(hr))
goto end;
seek.QuadPart = SECTIONHEADER_OFFSET + *sectionOffset;
hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
goto end;
StorageUtl_WriteDWord((LPBYTE)&dwTemp, 0,
dictionary_num_entries(This->name_to_propid));
hr = IStream_Write(This->stm, &dwTemp, sizeof(dwTemp), &count);
if (FAILED(hr))
goto end;
*sectionOffset += sizeof(dwTemp);
closure.hr = S_OK;
closure.bytesWritten = 0;
dictionary_enumerate(This->name_to_propid, PropertyStorage_DictionaryWriter,
&closure);
hr = closure.hr;
if (FAILED(hr))
goto end;
*sectionOffset += closure.bytesWritten;
if (closure.bytesWritten % sizeof(DWORD))
{
DWORD padding = sizeof(DWORD) - closure.bytesWritten % sizeof(DWORD);
TRACE("adding %d bytes of padding\n", padding);
*sectionOffset += padding;
}
end:
return hr;
}
static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
DWORD propNum, DWORD propid, const PROPVARIANT *var, DWORD *sectionOffset)
{
HRESULT hr;
LARGE_INTEGER seek;
PROPERTYIDOFFSET propIdOffset;
ULONG count;
DWORD dwType, bytesWritten;
assert(var);
assert(sectionOffset);
TRACE("%p, %d, 0x%08x, (%d), (%d)\n", This, propNum, propid, var->vt,
*sectionOffset);
seek.QuadPart = SECTIONHEADER_OFFSET + sizeof(PROPERTYSECTIONHEADER) +
propNum * sizeof(PROPERTYIDOFFSET);
hr = IStream_Seek(This->stm, seek, STREAM_SEEK_SET, NULL);
if (FAILED(hr))
goto end;
PropertyStorage_MakePropertyIdOffset(propid, *sectionOffset, &propIdOffset);
hr = IStream_Write(This->stm, &propIdOffset, sizeof(propIdOffset), &count);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -