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