PATCH: olepicture loading without ISTream_Stat
Marcus Meissner
marcus at jet.franken.de
Sun Oct 23 11:49:52 CDT 2005
Hi,
The revelation client from http://bugs.winehq.org/show_bug.cgi?id=2683
loads images from a IStream that provides no Stat method.
We did not handle this case, so I just added code to read all bytes
up to the end of the stream and this seems to work.
Ciao, Marcus
Changelog:
Also read OLE Picture data from IStream interfaces
with Stat method. Added testcase for this.
Index: dlls/oleaut32/olepicture.c
===================================================================
RCS file: /home/wine/wine/dlls/oleaut32/olepicture.c,v
retrieving revision 1.68
diff -u -r1.68 olepicture.c
--- dlls/oleaut32/olepicture.c 24 Aug 2005 09:44:25 -0000 1.68
+++ dlls/oleaut32/olepicture.c 23 Oct 2005 16:47:49 -0000
@@ -1056,6 +1065,7 @@
static HRESULT WINAPI OLEPictureImpl_Load(IPersistStream* iface,IStream*pStm) {
HRESULT hr = E_FAIL;
BOOL headerisdata = FALSE;
+ BOOL statfailed = FALSE;
ULONG xread, toread;
BYTE *xbuf;
DWORD header[2];
@@ -1069,15 +1079,22 @@
* out whether we do.
*
* UPDATE: the IStream can be mapped to a plain file instead of a stream in a
- * compound file. This may explain most, if not all, of the cases of "no header",
- * and the header validation should take this into account. At least in Visual Basic 6,
- * resource streams, valid headers are
+ * compound file. This may explain most, if not all, of the cases of "no
+ * header", and the header validation should take this into account.
+ * At least in Visual Basic 6, resource streams, valid headers are
* header[0] == "lt\0\0",
* header[1] == length_of_stream.
+ *
+ * Also handle streams where we do not have a working "Stat" method by
+ * reading all data until the end of the stream.
*/
hr=IStream_Stat(pStm,&statstg,STATFLAG_NONAME);
- if (hr)
- FIXME("Stat failed with hres %lx\n",hr);
+ if (hr) {
+ TRACE("stat failed with hres %lx\n",hr);
+ statfailed = TRUE;
+ /* we will read at least 8 byte ... just right below */
+ statstg.cbSize.QuadPart = 8;
+ }
hr=IStream_Read(pStm,header,8,&xread);
if (hr || xread!=8) {
FIXME("Failure while reading picture header (hr is %lx, nread is %ld).\n",hr,xread);
@@ -1104,22 +1121,50 @@
}
}
- This->datalen = toread+(headerisdata?8:0);
- xbuf = This->data = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, This->datalen);
+ if (statfailed) { /* we dont know the size ... read all we get */
+ int origsize = 4096;
+ ULONG nread = 42;
+
+ TRACE("Reading all data from stream.\n");
+ xbuf = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, origsize);
+ if (headerisdata)
+ memcpy (xbuf, &header, 8);
+ while (1) {
+ while (xread < origsize) {
+ hr = IStream_Read(pStm,xbuf+xread,origsize-xread,&nread);
+ xread+=nread;
+ if (hr || !nread)
+ break;
+ }
+ if (!nread || hr) /* done, or error */
+ break;
+ if (xread == origsize) {
+ origsize += 4096;
+ xbuf = HeapReAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, xbuf, origsize);
+ }
+ }
+ if (hr)
+ FIXME("hr in no-stat loader case is %08lx\n", hr);
+ TRACE("loaded %ld bytes.\n", xread);
+ This->datalen = xread;
+ This->data = xbuf;
+ } else {
+ This->datalen = toread+(headerisdata?8:0);
+ xbuf = This->data = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, This->datalen);
- if (headerisdata)
- memcpy (xbuf, &header, 8);
+ if (headerisdata)
+ memcpy (xbuf, &header, 8);
- while (xread < This->datalen) {
- ULONG nread;
- hr = IStream_Read(pStm,xbuf+xread,This->datalen-xread,&nread);
- xread+=nread;
- if (hr || !nread)
- break;
+ while (xread < This->datalen) {
+ ULONG nread;
+ hr = IStream_Read(pStm,xbuf+xread,This->datalen-xread,&nread);
+ xread+=nread;
+ if (hr || !nread)
+ break;
+ }
+ if (xread != This->datalen)
+ FIXME("Could only read %ld of %d bytes out of stream?\n",xread,This->datalen);
}
- if (xread != This->datalen)
- FIXME("Could only read %ld of %d bytes out of stream?\n",xread,This->datalen);
-
if (This->datalen == 0) { /* Marks the "NONE" picture */
This->desc.picType = PICTYPE_NONE;
return S_OK;
Index: dlls/oleaut32/tests/olepicture.c
===================================================================
RCS file: /home/wine/wine/dlls/oleaut32/tests/olepicture.c,v
retrieving revision 1.3
diff -u -r1.3 olepicture.c
--- dlls/oleaut32/tests/olepicture.c 27 Jun 2005 09:57:28 -0000 1.3
+++ dlls/oleaut32/tests/olepicture.c 23 Oct 2005 16:47:48 -0000
@@ -35,6 +35,7 @@
#include <winnls.h>
#include <winerror.h>
#include <winnt.h>
+#include <winternl.h>
#include <wtypes.h>
#include <olectl.h>
@@ -102,17 +103,24 @@
0x02,0x00,0x00,0x02,0x03,0x14,0x16,0x05,0x00,0x3b
};
+struct NoStatStreamImpl
+{
+ const IStreamVtbl *lpVtbl;
+ LONG ref;
+
+ HGLOBAL supportHandle;
+ ULARGE_INTEGER streamSize;
+ ULARGE_INTEGER currentPosition;
+};
+typedef struct NoStatStreamImpl NoStatStreamImpl;
+static NoStatStreamImpl* NoStatStreamImpl_Construct(HGLOBAL hGlobal);
+
static void
-test_pic(const unsigned char *imgdata, unsigned int imgsize)
+test_pic_with_stream(LPSTREAM stream, unsigned int imgsize)
{
- LPBYTE data;
- LPSTREAM stream;
IPicture* pic = NULL;
HRESULT hres;
LPVOID pvObj = NULL;
- HGLOBAL hglob;
- ULARGE_INTEGER newpos1;
- LARGE_INTEGER seekto;
OLE_HANDLE handle, hPal;
OLE_XSIZE_HIMETRIC width;
OLE_YSIZE_HIMETRIC height;
@@ -120,18 +128,6 @@
DWORD attr;
ULONG res;
- /* let the fun begin */
- hglob = GlobalAlloc (0, imgsize);
- data = GlobalLock (hglob);
- memcpy(data, imgdata, imgsize);
-
- hres = CreateStreamOnHGlobal (hglob, TRUE, &stream);
- ok (hres == S_OK, "createstreamonhglobal failed? doubt it... hres 0x%08lx\n", hres);
-
- memset(&seekto,0,sizeof(seekto));
- hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1);
- ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08lx\n", hres);
-
pvObj = NULL;
hres = pOleLoadPicture(stream, imgsize, TRUE, &IID_IPicture, &pvObj);
pic = pvObj;
@@ -141,20 +137,6 @@
if (pic == NULL)
return;
-#if 0
- hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos2);
- ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08lx\n", hres);
-
- /* The stream position here is some bytes after the image, in both wine and
- * native. But not at the correct end. -Marcus
- */
- fprintf(stderr,"newpos1 %ld, newpos2 %ld\n", newpos1.LowPart, newpos2.LowPart);
- ok ( newpos2.LowPart == imgsize,
- "seeked after end of gifimage (offset %d instead of expected %d)\n",
- newpos2.LowPart, imgsize
- );
-#endif
-
pvObj = NULL;
hres = IPicture_QueryInterface (pic, &IID_IPicture, &pvObj);
@@ -198,6 +180,34 @@
ok (res == 0, "refcount after release is %ld, but should be 1?\n", res);
}
+static void
+test_pic(const unsigned char *imgdata, unsigned int imgsize)
+{
+ LPSTREAM stream;
+ HGLOBAL hglob;
+ LPBYTE data;
+ HRESULT hres;
+ LARGE_INTEGER seekto;
+ ULARGE_INTEGER newpos1;
+
+ /* Let the fun begin */
+ hglob = GlobalAlloc (0, imgsize);
+ data = GlobalLock (hglob);
+ memcpy(data, imgdata, imgsize);
+
+ hres = CreateStreamOnHGlobal (hglob, FALSE, &stream);
+ ok (hres == S_OK, "createstreamonhglobal failed? doubt it... hres 0x%08lx\n", hres);
+
+ memset(&seekto,0,sizeof(seekto));
+ hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1);
+ ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08lx\n", hres);
+ test_pic_with_stream(stream, imgsize);
+
+ /* again with Non Statable and Non Seekable stream */
+ stream = (LPSTREAM)NoStatStreamImpl_Construct(hglob);
+ test_pic_with_stream(stream, 0);
+}
+
static void test_empty_image(void) {
LPBYTE data;
LPSTREAM stream;
@@ -288,9 +298,294 @@
test_pic(jpgimage, sizeof(jpgimage));
test_pic(bmpimage, sizeof(bmpimage));
test_pic(gif4pixel, sizeof(gif4pixel));
- /* no PNG support yet here or in older Windows...
+ /* No PNG support yet here or in older Windows...
test_pic(pngimage, sizeof(pngimage));
*/
test_empty_image();
test_empty_image_2();
}
+
+
+/* Helper functions only ... */
+
+
+static void NoStatStreamImpl_Destroy(NoStatStreamImpl* This)
+{
+ GlobalFree(This->supportHandle);
+ This->supportHandle=0;
+ HeapFree(GetProcessHeap(), 0, This);
+}
+
+static ULONG WINAPI NoStatStreamImpl_AddRef(
+ IStream* iface)
+{
+ NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
+ return InterlockedIncrement(&This->ref);
+}
+
+static HRESULT WINAPI NoStatStreamImpl_QueryInterface(
+ IStream* iface,
+ REFIID riid, /* [in] */
+ void** ppvObject) /* [iid_is][out] */
+{
+ NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
+ if (ppvObject==0) return E_INVALIDARG;
+ *ppvObject = 0;
+ if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
+ {
+ *ppvObject = (IStream*)This;
+ }
+ else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)
+ {
+ *ppvObject = (IStream*)This;
+ }
+
+ if ((*ppvObject)==0)
+ return E_NOINTERFACE;
+ NoStatStreamImpl_AddRef(iface);
+ return S_OK;
+}
+
+static ULONG WINAPI NoStatStreamImpl_Release(
+ IStream* iface)
+{
+ NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
+ ULONG newRef = InterlockedDecrement(&This->ref);
+ if (newRef==0)
+ NoStatStreamImpl_Destroy(This);
+ return newRef;
+}
+
+static HRESULT WINAPI NoStatStreamImpl_Read(
+ IStream* iface,
+ void* pv, /* [length_is][size_is][out] */
+ ULONG cb, /* [in] */
+ ULONG* pcbRead) /* [out] */
+{
+ NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
+ void* supportBuffer;
+ ULONG bytesReadBuffer;
+ ULONG bytesToReadFromBuffer;
+
+ if (pcbRead==0)
+ pcbRead = &bytesReadBuffer;
+ bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
+ supportBuffer = GlobalLock(This->supportHandle);
+ memcpy(pv, (char *) supportBuffer+This->currentPosition.u.LowPart, bytesToReadFromBuffer);
+ This->currentPosition.u.LowPart+=bytesToReadFromBuffer;
+ *pcbRead = bytesToReadFromBuffer;
+ GlobalUnlock(This->supportHandle);
+ if(*pcbRead == cb)
+ return S_OK;
+ return S_FALSE;
+}
+
+static HRESULT WINAPI NoStatStreamImpl_Write(
+ IStream* iface,
+ const void* pv, /* [size_is][in] */
+ ULONG cb, /* [in] */
+ ULONG* pcbWritten) /* [out] */
+{
+ NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
+ void* supportBuffer;
+ ULARGE_INTEGER newSize;
+ ULONG bytesWritten = 0;
+
+ if (pcbWritten == 0)
+ pcbWritten = &bytesWritten;
+ if (cb == 0)
+ return S_OK;
+ newSize.u.HighPart = 0;
+ newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
+ if (newSize.u.LowPart > This->streamSize.u.LowPart)
+ IStream_SetSize(iface, newSize);
+
+ supportBuffer = GlobalLock(This->supportHandle);
+ memcpy((char *) supportBuffer+This->currentPosition.u.LowPart, pv, cb);
+ This->currentPosition.u.LowPart+=cb;
+ *pcbWritten = cb;
+ GlobalUnlock(This->supportHandle);
+ return S_OK;
+}
+
+static HRESULT WINAPI NoStatStreamImpl_Seek(
+ IStream* iface,
+ LARGE_INTEGER dlibMove, /* [in] */
+ DWORD dwOrigin, /* [in] */
+ ULARGE_INTEGER* plibNewPosition) /* [out] */
+{
+ NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
+ ULARGE_INTEGER newPosition;
+ switch (dwOrigin)
+ {
+ case STREAM_SEEK_SET:
+ newPosition.u.HighPart = 0;
+ newPosition.u.LowPart = 0;
+ break;
+ case STREAM_SEEK_CUR:
+ newPosition = This->currentPosition;
+ break;
+ case STREAM_SEEK_END:
+ newPosition = This->streamSize;
+ break;
+ default:
+ return STG_E_INVALIDFUNCTION;
+ }
+ if (dlibMove.QuadPart < 0 && newPosition.QuadPart < -dlibMove.QuadPart)
+ return STG_E_INVALIDFUNCTION;
+ newPosition.QuadPart = RtlLargeIntegerAdd(newPosition.QuadPart, dlibMove.QuadPart);
+ if (plibNewPosition) *plibNewPosition = newPosition;
+ This->currentPosition = newPosition;
+ return S_OK;
+}
+
+static HRESULT WINAPI NoStatStreamImpl_SetSize(
+ IStream* iface,
+ ULARGE_INTEGER libNewSize) /* [in] */
+{
+ NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
+ HGLOBAL supportHandle;
+ if (libNewSize.u.HighPart != 0)
+ return STG_E_INVALIDFUNCTION;
+ if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
+ return S_OK;
+ supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
+ if (supportHandle == 0)
+ return STG_E_MEDIUMFULL;
+ This->supportHandle = supportHandle;
+ This->streamSize.u.LowPart = libNewSize.u.LowPart;
+ return S_OK;
+}
+
+static HRESULT WINAPI NoStatStreamImpl_CopyTo(
+ IStream* iface,
+ IStream* pstm, /* [unique][in] */
+ ULARGE_INTEGER cb, /* [in] */
+ ULARGE_INTEGER* pcbRead, /* [out] */
+ ULARGE_INTEGER* pcbWritten) /* [out] */
+{
+ HRESULT hr = S_OK;
+ BYTE tmpBuffer[128];
+ ULONG bytesRead, bytesWritten, copySize;
+ ULARGE_INTEGER totalBytesRead;
+ ULARGE_INTEGER totalBytesWritten;
+
+ if ( pstm == 0 )
+ return STG_E_INVALIDPOINTER;
+ totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
+ totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
+
+ while ( cb.u.LowPart > 0 )
+ {
+ if ( cb.u.LowPart >= 128 )
+ copySize = 128;
+ else
+ copySize = cb.u.LowPart;
+ IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
+ totalBytesRead.u.LowPart += bytesRead;
+ IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
+ totalBytesWritten.u.LowPart += bytesWritten;
+ if (bytesRead != bytesWritten)
+ {
+ hr = STG_E_MEDIUMFULL;
+ break;
+ }
+ if (bytesRead!=copySize)
+ cb.u.LowPart = 0;
+ else
+ cb.u.LowPart -= bytesRead;
+ }
+ if (pcbRead)
+ {
+ pcbRead->u.LowPart = totalBytesRead.u.LowPart;
+ pcbRead->u.HighPart = totalBytesRead.u.HighPart;
+ }
+
+ if (pcbWritten)
+ {
+ pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
+ pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
+ }
+ return hr;
+}
+
+static HRESULT WINAPI NoStatStreamImpl_Commit(IStream* iface,DWORD grfCommitFlags)
+{
+ return S_OK;
+}
+static HRESULT WINAPI NoStatStreamImpl_Revert(IStream* iface) { return S_OK; }
+
+static HRESULT WINAPI NoStatStreamImpl_LockRegion(
+ IStream* iface,
+ ULARGE_INTEGER libOffset, /* [in] */
+ ULARGE_INTEGER cb, /* [in] */
+ DWORD dwLockType) /* [in] */
+{
+ return S_OK;
+}
+
+static HRESULT WINAPI NoStatStreamImpl_UnlockRegion(
+ IStream* iface,
+ ULARGE_INTEGER libOffset, /* [in] */
+ ULARGE_INTEGER cb, /* [in] */
+ DWORD dwLockType) /* [in] */
+{
+ return S_OK;
+}
+
+static HRESULT WINAPI NoStatStreamImpl_Stat(
+ IStream* iface,
+ STATSTG* pstatstg, /* [out] */
+ DWORD grfStatFlag) /* [in] */
+{
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI NoStatStreamImpl_Clone(
+ IStream* iface,
+ IStream** ppstm) /* [out] */
+{
+ return E_NOTIMPL;
+}
+static const IStreamVtbl NoStatStreamImpl_Vtbl;
+
+static NoStatStreamImpl* NoStatStreamImpl_Construct(HGLOBAL hGlobal)
+{
+ NoStatStreamImpl* newStream;
+
+ newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(NoStatStreamImpl));
+ if (newStream!=0)
+ {
+ newStream->lpVtbl = &NoStatStreamImpl_Vtbl;
+ newStream->ref = 0;
+ newStream->supportHandle = hGlobal;
+
+ if (!newStream->supportHandle)
+ newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD |
+ GMEM_SHARE, 0);
+ newStream->currentPosition.u.HighPart = 0;
+ newStream->currentPosition.u.LowPart = 0;
+ newStream->streamSize.u.HighPart = 0;
+ newStream->streamSize.u.LowPart = GlobalSize(newStream->supportHandle);
+ }
+ return newStream;
+}
+
+
+static const IStreamVtbl NoStatStreamImpl_Vtbl =
+{
+ NoStatStreamImpl_QueryInterface,
+ NoStatStreamImpl_AddRef,
+ NoStatStreamImpl_Release,
+ NoStatStreamImpl_Read,
+ NoStatStreamImpl_Write,
+ NoStatStreamImpl_Seek,
+ NoStatStreamImpl_SetSize,
+ NoStatStreamImpl_CopyTo,
+ NoStatStreamImpl_Commit,
+ NoStatStreamImpl_Revert,
+ NoStatStreamImpl_LockRegion,
+ NoStatStreamImpl_UnlockRegion,
+ NoStatStreamImpl_Stat,
+ NoStatStreamImpl_Clone
+};
More information about the wine-patches
mailing list