mcicda: Exclude unused headers.
[wine] / dlls / oleaut32 / tests / olepicture.c
1 /*
2  * OLEPICTURE test program
3  *
4  * Copyright 2005 Marcus Meissner
5  *
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <math.h>
25 #include <float.h>
26 #include <time.h>
27
28 #define COBJMACROS
29
30 #include <wine/test.h>
31 #include <windef.h>
32 #include <winbase.h>
33 #include <winuser.h>
34 #include <wingdi.h>
35 #include <winnls.h>
36 #include <winerror.h>
37 #include <winnt.h>
38
39 #include <wtypes.h>
40 #include <olectl.h>
41 #include <objidl.h>
42
43 static HMODULE hOleaut32;
44
45 static HRESULT (WINAPI *pOleLoadPicture)(LPSTREAM,LONG,BOOL,REFIID,LPVOID*);
46
47 #define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr)
48
49 /* 1x1 pixel gif */
50 static const unsigned char gifimage[35] = {
51 0x47,0x49,0x46,0x38,0x37,0x61,0x01,0x00,0x01,0x00,0x80,0x00,0x00,0xff,0xff,0xff,
52 0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x02,0x02,0x44,
53 0x01,0x00,0x3b
54 };
55
56 /* 1x1 pixel jpg */
57 static const unsigned char jpgimage[285] = {
58 0xff,0xd8,0xff,0xe0,0x00,0x10,0x4a,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x01,0x2c,
59 0x01,0x2c,0x00,0x00,0xff,0xdb,0x00,0x43,0x00,0x05,0x03,0x04,0x04,0x04,0x03,0x05,
60 0x04,0x04,0x04,0x05,0x05,0x05,0x06,0x07,0x0c,0x08,0x07,0x07,0x07,0x07,0x0f,0x0b,
61 0x0b,0x09,0x0c,0x11,0x0f,0x12,0x12,0x11,0x0f,0x11,0x11,0x13,0x16,0x1c,0x17,0x13,
62 0x14,0x1a,0x15,0x11,0x11,0x18,0x21,0x18,0x1a,0x1d,0x1d,0x1f,0x1f,0x1f,0x13,0x17,
63 0x22,0x24,0x22,0x1e,0x24,0x1c,0x1e,0x1f,0x1e,0xff,0xdb,0x00,0x43,0x01,0x05,0x05,
64 0x05,0x07,0x06,0x07,0x0e,0x08,0x08,0x0e,0x1e,0x14,0x11,0x14,0x1e,0x1e,0x1e,0x1e,
65 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
66 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
67 0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0xff,0xc0,
68 0x00,0x11,0x08,0x00,0x01,0x00,0x01,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11,
69 0x01,0xff,0xc4,0x00,0x15,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
70 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0xff,0xc4,0x00,0x14,0x10,0x01,0x00,0x00,
71 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xc4,
72 0x00,0x14,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
73 0x00,0x00,0x00,0x00,0xff,0xc4,0x00,0x14,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
74 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xda,0x00,0x0c,0x03,0x01,
75 0x00,0x02,0x11,0x03,0x11,0x00,0x3f,0x00,0xb2,0xc0,0x07,0xff,0xd9
76 };
77
78 /* 1x1 pixel png */
79 static const unsigned char pngimage[285] = {
80 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
81 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x08,0x02,0x00,0x00,0x00,0x90,0x77,0x53,
82 0xde,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00,0x00,0x0b,0x13,0x00,0x00,0x0b,
83 0x13,0x01,0x00,0x9a,0x9c,0x18,0x00,0x00,0x00,0x07,0x74,0x49,0x4d,0x45,0x07,0xd5,
84 0x06,0x03,0x0f,0x07,0x2d,0x12,0x10,0xf0,0xfd,0x00,0x00,0x00,0x0c,0x49,0x44,0x41,
85 0x54,0x08,0xd7,0x63,0xf8,0xff,0xff,0x3f,0x00,0x05,0xfe,0x02,0xfe,0xdc,0xcc,0x59,
86 0xe7,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82
87 };
88
89 /* 1x1 pixel bmp */
90 static const unsigned char bmpimage[66] = {
91 0x42,0x4d,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x28,0x00,
92 0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00,
93 0x00,0x00,0x04,0x00,0x00,0x00,0x12,0x0b,0x00,0x00,0x12,0x0b,0x00,0x00,0x02,0x00,
94 0x00,0x00,0x02,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0x00,
95 0x00,0x00
96 };
97
98 /* 2x2 pixel gif */
99 static const unsigned char gif4pixel[42] = {
100 0x47,0x49,0x46,0x38,0x37,0x61,0x02,0x00,0x02,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,
101 0x39,0x62,0xfc,0xff,0x1a,0xe5,0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x02,0x00,
102 0x02,0x00,0x00,0x02,0x03,0x14,0x16,0x05,0x00,0x3b
103 };
104
105 struct NoStatStreamImpl
106 {
107         const IStreamVtbl       *lpVtbl;   
108         LONG                    ref;
109
110         HGLOBAL                 supportHandle;
111         ULARGE_INTEGER          streamSize;
112         ULARGE_INTEGER          currentPosition;
113 };
114 typedef struct NoStatStreamImpl NoStatStreamImpl;
115 static NoStatStreamImpl* NoStatStreamImpl_Construct(HGLOBAL hGlobal);
116
117 static void
118 test_pic_with_stream(LPSTREAM stream, unsigned int imgsize)
119 {
120         IPicture*       pic = NULL;
121         HRESULT         hres;
122         LPVOID          pvObj = NULL;
123         OLE_HANDLE      handle, hPal;
124         OLE_XSIZE_HIMETRIC      width;
125         OLE_YSIZE_HIMETRIC      height;
126         short           type;
127         DWORD           attr;
128         ULONG           res;
129
130         pvObj = NULL;
131         hres = pOleLoadPicture(stream, imgsize, TRUE, &IID_IPicture, &pvObj);
132         pic = pvObj;
133
134         ok(hres == S_OK,"OLP (NULL,..) does not return 0, but 0x%08x\n",hres);
135         ok(pic != NULL,"OLP (NULL,..) returns NULL, instead of !NULL\n");
136         if (pic == NULL)
137                 return;
138
139         pvObj = NULL;
140         hres = IPicture_QueryInterface (pic, &IID_IPicture, &pvObj);
141
142         ok(hres == S_OK,"IPicture_QI does not return S_OK, but 0x%08x\n", hres);
143         ok(pvObj != NULL,"IPicture_QI does return NULL, instead of a ptr\n");
144
145         IPicture_Release ((IPicture*)pvObj);
146
147         handle = 0;
148         hres = IPicture_get_Handle (pic, &handle);
149         ok(hres == S_OK,"IPicture_get_Handle does not return S_OK, but 0x%08x\n", hres);
150         ok(handle != 0, "IPicture_get_Handle returns a NULL handle, but it should be non NULL\n");
151
152         width = 0;
153         hres = IPicture_get_Width (pic, &width);
154         ok(hres == S_OK,"IPicture_get_Width does not return S_OK, but 0x%08x\n", hres);
155         ok(width != 0, "IPicture_get_Width returns 0, but it should not be 0.\n");
156
157         height = 0;
158         hres = IPicture_get_Height (pic, &height);
159         ok(hres == S_OK,"IPicture_get_Height does not return S_OK, but 0x%08x\n", hres);
160         ok(height != 0, "IPicture_get_Height returns 0, but it should not be 0.\n");
161
162         type = 0;
163         hres = IPicture_get_Type (pic, &type);
164         ok(hres == S_OK,"IPicture_get_Type does not return S_OK, but 0x%08x\n", hres);
165         ok(type == PICTYPE_BITMAP, "IPicture_get_Type returns %d, but it should be PICTYPE_BITMAP(%d).\n", type, PICTYPE_BITMAP);
166
167         attr = 0;
168         hres = IPicture_get_Attributes (pic, &attr);
169         ok(hres == S_OK,"IPicture_get_Attributes does not return S_OK, but 0x%08x\n", hres);
170         ok(attr == 0, "IPicture_get_Attributes returns %d, but it should be 0.\n", attr);
171
172         hPal = 0;
173         hres = IPicture_get_hPal (pic, &hPal);
174         ok(hres == S_OK,"IPicture_get_hPal does not return S_OK, but 0x%08x\n", hres);
175         /* a single pixel b/w image has no palette */
176         ok(hPal == 0, "IPicture_get_hPal returns %d, but it should be 0.\n", hPal);
177
178         res = IPicture_Release (pic);
179         ok (res == 0, "refcount after release is %d, but should be 0?\n", res);
180 }
181
182 static void
183 test_pic(const unsigned char *imgdata, unsigned int imgsize)
184 {
185         LPSTREAM        stream;
186         HGLOBAL         hglob;
187         LPBYTE          data;
188         HRESULT         hres;
189         LARGE_INTEGER   seekto;
190         ULARGE_INTEGER  newpos1;
191         DWORD *         header;
192         unsigned int    i,j;
193
194         /* Let the fun begin */
195         hglob = GlobalAlloc (0, imgsize);
196         data = GlobalLock (hglob);
197         memcpy(data, imgdata, imgsize);
198         GlobalUnlock(hglob); data = NULL;
199
200         hres = CreateStreamOnHGlobal (hglob, FALSE, &stream);
201         ok (hres == S_OK, "createstreamonhglobal failed? doubt it... hres 0x%08x\n", hres);
202
203         memset(&seekto,0,sizeof(seekto));
204         hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1);
205         ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08x\n", hres);
206         test_pic_with_stream(stream, imgsize);
207         
208         IStream_Release(stream);
209
210         /* again with Non Statable and Non Seekable stream */
211         stream = (LPSTREAM)NoStatStreamImpl_Construct(hglob);
212         hglob = 0;  /* Non-statable impl always deletes on release */
213         test_pic_with_stream(stream, 0);
214
215         IStream_Release(stream);
216         for (i = 1; i <= 8; i++) {
217                 /* more fun!!! */
218                 hglob = GlobalAlloc (0, imgsize + i * (2 * sizeof(DWORD)));
219                 data = GlobalLock (hglob);
220                 header = (DWORD *)data;
221
222                 /* multiple copies of header */
223                 memcpy(data,"lt\0\0",4);
224                 header[1] = imgsize;
225                 for (j = 2; j <= i; j++) {
226                         memcpy(&(header[2 * (j - 1)]), header, 2 * sizeof(DWORD));
227                 }
228                 memcpy(data + i * (2 * sizeof(DWORD)), imgdata, imgsize);
229                 GlobalUnlock(hglob); data = NULL;
230
231                 hres = CreateStreamOnHGlobal (hglob, FALSE, &stream);
232                 ok (hres == S_OK, "createstreamonhglobal failed? doubt it... hres 0x%08x\n", hres);
233
234                 memset(&seekto,0,sizeof(seekto));
235                 hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1);
236                 ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08x\n", hres);
237                 test_pic_with_stream(stream, imgsize);
238         
239                 IStream_Release(stream);
240
241                 /* again with Non Statable and Non Seekable stream */
242                 stream = (LPSTREAM)NoStatStreamImpl_Construct(hglob);
243                 hglob = 0;  /* Non-statable impl always deletes on release */
244                 test_pic_with_stream(stream, 0);
245
246                 IStream_Release(stream);
247         }
248 }
249
250 static void test_empty_image(void) {
251         LPBYTE          data;
252         LPSTREAM        stream;
253         IPicture*       pic = NULL;
254         HRESULT         hres;
255         LPVOID          pvObj = NULL;
256         HGLOBAL         hglob;
257         OLE_HANDLE      handle;
258         ULARGE_INTEGER  newpos1;
259         LARGE_INTEGER   seekto;
260         short           type;
261
262         /* Empty image. Happens occasionally in VB programs. */
263         hglob = GlobalAlloc (0, 8);
264         data = GlobalLock (hglob);
265         memcpy(data,"lt\0\0",4);
266         ((DWORD*)data)[1] = 0;
267         hres = CreateStreamOnHGlobal (hglob, TRUE, &stream);
268         ok (hres == S_OK, "CreatestreamOnHGlobal failed? doubt it... hres 0x%08x\n", hres);
269
270         memset(&seekto,0,sizeof(seekto));
271         hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1);
272         ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08x\n", hres);
273
274         pvObj = NULL;
275         hres = pOleLoadPicture(stream, 8, TRUE, &IID_IPicture, &pvObj);
276         pic = pvObj;
277         ok(hres == S_OK,"empty picture not loaded, hres 0x%08x\n", hres);
278         ok(pic != NULL,"empty picture not loaded, pic is NULL\n");
279
280         hres = IPicture_get_Type (pic, &type);
281         ok (hres == S_OK,"empty picture get type failed with hres 0x%08x\n", hres);
282         ok (type == PICTYPE_NONE,"type is %d, but should be PICTYPE_NONE(0)\n", type);
283
284         hres = IPicture_get_Handle (pic, &handle);
285         ok (hres == S_OK,"empty picture get handle failed with hres 0x%08x\n", hres);
286         ok (handle == 0, "empty picture get handle did not return 0, but 0x%08x\n", handle);
287         IPicture_Release (pic);
288 }
289
290 static void test_empty_image_2(void) {
291         LPBYTE          data;
292         LPSTREAM        stream;
293         IPicture*       pic = NULL;
294         HRESULT         hres;
295         LPVOID          pvObj = NULL;
296         HGLOBAL         hglob;
297         ULARGE_INTEGER  newpos1;
298         LARGE_INTEGER   seekto;
299         short           type;
300
301         /* Empty image at random stream position. */
302         hglob = GlobalAlloc (0, 200);
303         data = GlobalLock (hglob);
304         data += 42;
305         memcpy(data,"lt\0\0",4);
306         ((DWORD*)data)[1] = 0;
307         hres = CreateStreamOnHGlobal (hglob, TRUE, &stream);
308         ok (hres == S_OK, "CreatestreamOnHGlobal failed? doubt it... hres 0x%08x\n", hres);
309
310         memset(&seekto,0,sizeof(seekto));
311         seekto.u.LowPart = 42;
312         hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1);
313         ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08x\n", hres);
314
315         pvObj = NULL;
316         hres = pOleLoadPicture(stream, 8, TRUE, &IID_IPicture, &pvObj);
317         pic = pvObj;
318         ok(hres == S_OK,"empty picture not loaded, hres 0x%08x\n", hres);
319         ok(pic != NULL,"empty picture not loaded, pic is NULL\n");
320
321         hres = IPicture_get_Type (pic, &type);
322         ok (hres == S_OK,"empty picture get type failed with hres 0x%08x\n", hres);
323         ok (type == PICTYPE_NONE,"type is %d, but should be PICTYPE_NONE(0)\n", type);
324
325         IPicture_Release (pic);
326 }
327
328 static void test_Invoke(void)
329 {
330     IPictureDisp *picdisp;
331     HRESULT hr;
332     VARIANTARG vararg;
333     DISPPARAMS dispparams;
334     VARIANT varresult;
335     IStream *stream;
336     HGLOBAL hglob;
337     void *data;
338
339         hglob = GlobalAlloc (0, sizeof(gifimage));
340         data = GlobalLock(hglob);
341         memcpy(data, gifimage, sizeof(gifimage));
342     GlobalUnlock(hglob);
343
344         hr = CreateStreamOnHGlobal (hglob, FALSE, &stream);
345     ok_ole_success(hr, "CreateStreamOnHGlobal");
346
347         hr = pOleLoadPicture(stream, sizeof(gifimage), TRUE, &IID_IPictureDisp, (void **)&picdisp);
348     IStream_Release(stream);
349     ok_ole_success(hr, "OleLoadPicture");
350
351     V_VT(&vararg) = VT_BOOL;
352     V_BOOL(&vararg) = VARIANT_FALSE;
353     dispparams.cNamedArgs = 0;
354     dispparams.rgdispidNamedArgs = NULL;
355     dispparams.cArgs = 1;
356     dispparams.rgvarg = &vararg;
357     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_IPictureDisp, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
358     ok(hr == DISP_E_UNKNOWNNAME, "IPictureDisp_Invoke should have returned DISP_E_UNKNOWNNAME instead of 0x%08x\n", hr);
359     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_IUnknown, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
360     ok(hr == DISP_E_UNKNOWNNAME, "IPictureDisp_Invoke should have returned DISP_E_UNKNOWNNAME instead of 0x%08x\n", hr);
361
362     dispparams.cArgs = 0;
363     dispparams.rgvarg = NULL;
364     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
365     ok(hr == DISP_E_BADPARAMCOUNT, "IPictureDisp_Invoke should have returned DISP_E_BADPARAMCOUNT instead of 0x%08x\n", hr);
366
367     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYPUT, NULL, NULL, NULL, NULL);
368     ok(hr == DISP_E_PARAMNOTOPTIONAL, "IPictureDisp_Invoke should have returned DISP_E_PARAMNOTOPTIONAL instead of 0x%08x\n", hr);
369
370     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, NULL, NULL, NULL, NULL);
371     ok(hr == DISP_E_PARAMNOTOPTIONAL, "IPictureDisp_Invoke should have returned DISP_E_PARAMNOTOPTIONAL instead of 0x%08x\n", hr);
372
373     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, NULL, &varresult, NULL, NULL);
374     ok(hr == DISP_E_PARAMNOTOPTIONAL, "IPictureDisp_Invoke should have returned DISP_E_PARAMNOTOPTIONAL instead of 0x%08x\n", hr);
375
376     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL);
377     ok_ole_success(hr, "IPictureDisp_Invoke");
378     ok(V_VT(&varresult) == VT_I4, "V_VT(&varresult) should have been VT_UINT instead of %d\n", V_VT(&varresult));
379
380     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_METHOD, &dispparams, &varresult, NULL, NULL);
381     ok(hr == DISP_E_MEMBERNOTFOUND, "IPictureDisp_Invoke should have returned DISP_E_MEMBERNOTFOUND instead of 0x%08x\n", hr);
382
383     hr = IPictureDisp_Invoke(picdisp, 0xdeadbeef, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL);
384     ok(hr == DISP_E_MEMBERNOTFOUND, "IPictureDisp_Invoke should have returned DISP_E_MEMBERNOTFOUND instead of 0x%08x\n", hr);
385
386     dispparams.cArgs = 1;
387     dispparams.rgvarg = &vararg;
388     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL);
389     ok(hr == DISP_E_BADPARAMCOUNT, "IPictureDisp_Invoke should have returned DISP_E_BADPARAMCOUNT instead of 0x%08x\n", hr);
390
391     dispparams.cArgs = 1;
392     dispparams.rgvarg = &vararg;
393     hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL);
394     ok(hr == DISP_E_BADPARAMCOUNT, "IPictureDisp_Invoke should have returned DISP_E_BADPARAMCOUNT instead of 0x%08x\n", hr);
395
396     IPictureDisp_Release(picdisp);
397 }
398
399 START_TEST(olepicture)
400 {
401         hOleaut32 = LoadLibraryA("oleaut32.dll");
402         pOleLoadPicture = (void*)GetProcAddress(hOleaut32, "OleLoadPicture");
403         if (!pOleLoadPicture)
404             return;
405
406         /* Test regular 1x1 pixel images of gif, jpg, bmp type */
407         test_pic(gifimage, sizeof(gifimage));
408         test_pic(jpgimage, sizeof(jpgimage));
409         test_pic(bmpimage, sizeof(bmpimage));
410         test_pic(gif4pixel, sizeof(gif4pixel));
411         /* FIXME: No PNG support yet in Wine or in older Windows... */
412         if (0) test_pic(pngimage, sizeof(pngimage));
413         test_empty_image();
414         test_empty_image_2();
415
416         test_Invoke();
417 }
418
419
420 /* Helper functions only ... */
421
422
423 static void NoStatStreamImpl_Destroy(NoStatStreamImpl* This)
424 {
425   GlobalFree(This->supportHandle);
426   This->supportHandle=0;
427   HeapFree(GetProcessHeap(), 0, This);
428 }
429
430 static ULONG WINAPI NoStatStreamImpl_AddRef(
431                 IStream* iface)
432 {
433   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
434   return InterlockedIncrement(&This->ref);
435 }
436
437 static HRESULT WINAPI NoStatStreamImpl_QueryInterface(
438                   IStream*     iface,
439                   REFIID         riid,        /* [in] */
440                   void**         ppvObject)   /* [iid_is][out] */
441 {
442   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
443   if (ppvObject==0) return E_INVALIDARG;
444   *ppvObject = 0;
445   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
446   {
447     *ppvObject = (IStream*)This;
448   }
449   else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)
450   {
451     *ppvObject = (IStream*)This;
452   }
453
454   if ((*ppvObject)==0)
455     return E_NOINTERFACE;
456   NoStatStreamImpl_AddRef(iface);
457   return S_OK;
458 }
459
460 static ULONG WINAPI NoStatStreamImpl_Release(
461                 IStream* iface)
462 {
463   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
464   ULONG newRef = InterlockedDecrement(&This->ref);
465   if (newRef==0)
466     NoStatStreamImpl_Destroy(This);
467   return newRef;
468 }
469
470 static HRESULT WINAPI NoStatStreamImpl_Read(
471                   IStream*     iface,
472                   void*          pv,        /* [length_is][size_is][out] */
473                   ULONG          cb,        /* [in] */
474                   ULONG*         pcbRead)   /* [out] */
475 {
476   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
477   void* supportBuffer;
478   ULONG bytesReadBuffer;
479   ULONG bytesToReadFromBuffer;
480
481   if (pcbRead==0)
482     pcbRead = &bytesReadBuffer;
483   bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
484   supportBuffer = GlobalLock(This->supportHandle);
485   memcpy(pv, (char *) supportBuffer+This->currentPosition.u.LowPart, bytesToReadFromBuffer);
486   This->currentPosition.u.LowPart+=bytesToReadFromBuffer;
487   *pcbRead = bytesToReadFromBuffer;
488   GlobalUnlock(This->supportHandle);
489   if(*pcbRead == cb)
490     return S_OK;
491   return S_FALSE;
492 }
493
494 static HRESULT WINAPI NoStatStreamImpl_Write(
495                   IStream*     iface,
496                   const void*    pv,          /* [size_is][in] */
497                   ULONG          cb,          /* [in] */
498                   ULONG*         pcbWritten)  /* [out] */
499 {
500   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
501   void*          supportBuffer;
502   ULARGE_INTEGER newSize;
503   ULONG          bytesWritten = 0;
504
505   if (pcbWritten == 0)
506     pcbWritten = &bytesWritten;
507   if (cb == 0)
508     return S_OK;
509   newSize.u.HighPart = 0;
510   newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
511   if (newSize.u.LowPart > This->streamSize.u.LowPart)
512    IStream_SetSize(iface, newSize);
513
514   supportBuffer = GlobalLock(This->supportHandle);
515   memcpy((char *) supportBuffer+This->currentPosition.u.LowPart, pv, cb);
516   This->currentPosition.u.LowPart+=cb;
517   *pcbWritten = cb;
518   GlobalUnlock(This->supportHandle);
519   return S_OK;
520 }
521
522 static HRESULT WINAPI NoStatStreamImpl_Seek(
523                   IStream*      iface,
524                   LARGE_INTEGER   dlibMove,         /* [in] */
525                   DWORD           dwOrigin,         /* [in] */
526                   ULARGE_INTEGER* plibNewPosition) /* [out] */
527 {
528   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
529   ULARGE_INTEGER newPosition;
530   switch (dwOrigin)
531   {
532     case STREAM_SEEK_SET:
533       newPosition.u.HighPart = 0;
534       newPosition.u.LowPart = 0;
535       break;
536     case STREAM_SEEK_CUR:
537       newPosition = This->currentPosition;
538       break;
539     case STREAM_SEEK_END:
540       newPosition = This->streamSize;
541       break;
542     default:
543       return STG_E_INVALIDFUNCTION;
544   }
545   if (dlibMove.QuadPart < 0 && newPosition.QuadPart < -dlibMove.QuadPart)
546       return STG_E_INVALIDFUNCTION;
547   newPosition.QuadPart += dlibMove.QuadPart;
548   if (plibNewPosition) *plibNewPosition = newPosition;
549   This->currentPosition = newPosition;
550   return S_OK;
551 }
552
553 static HRESULT WINAPI NoStatStreamImpl_SetSize(
554                                      IStream*      iface,
555                                      ULARGE_INTEGER  libNewSize)   /* [in] */
556 {
557   NoStatStreamImpl* const This=(NoStatStreamImpl*)iface;
558   HGLOBAL supportHandle;
559   if (libNewSize.u.HighPart != 0)
560     return STG_E_INVALIDFUNCTION;
561   if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
562     return S_OK;
563   supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
564   if (supportHandle == 0)
565     return STG_E_MEDIUMFULL;
566   This->supportHandle = supportHandle;
567   This->streamSize.u.LowPart = libNewSize.u.LowPart;
568   return S_OK;
569 }
570
571 static HRESULT WINAPI NoStatStreamImpl_CopyTo(
572                                     IStream*      iface,
573                                     IStream*      pstm,         /* [unique][in] */
574                                     ULARGE_INTEGER  cb,           /* [in] */
575                                     ULARGE_INTEGER* pcbRead,      /* [out] */
576                                     ULARGE_INTEGER* pcbWritten)   /* [out] */
577 {
578   HRESULT        hr = S_OK;
579   BYTE           tmpBuffer[128];
580   ULONG          bytesRead, bytesWritten, copySize;
581   ULARGE_INTEGER totalBytesRead;
582   ULARGE_INTEGER totalBytesWritten;
583
584   if ( pstm == 0 )
585     return STG_E_INVALIDPOINTER;
586   totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
587   totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
588
589   while ( cb.u.LowPart > 0 )
590   {
591     if ( cb.u.LowPart >= 128 )
592       copySize = 128;
593     else
594       copySize = cb.u.LowPart;
595     IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
596     totalBytesRead.u.LowPart += bytesRead;
597     IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
598     totalBytesWritten.u.LowPart += bytesWritten;
599     if (bytesRead != bytesWritten)
600     {
601       hr = STG_E_MEDIUMFULL;
602       break;
603     }
604     if (bytesRead!=copySize)
605       cb.u.LowPart = 0;
606     else
607       cb.u.LowPart -= bytesRead;
608   }
609   if (pcbRead)
610   {
611     pcbRead->u.LowPart = totalBytesRead.u.LowPart;
612     pcbRead->u.HighPart = totalBytesRead.u.HighPart;
613   }
614
615   if (pcbWritten)
616   {
617     pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
618     pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
619   }
620   return hr;
621 }
622
623 static HRESULT WINAPI NoStatStreamImpl_Commit(IStream* iface,DWORD grfCommitFlags)
624 {
625   return S_OK;
626 }
627 static HRESULT WINAPI NoStatStreamImpl_Revert(IStream* iface) { return S_OK; }
628
629 static HRESULT WINAPI NoStatStreamImpl_LockRegion(
630                   IStream*       iface,
631                   ULARGE_INTEGER libOffset,   /* [in] */
632                   ULARGE_INTEGER cb,          /* [in] */
633                   DWORD          dwLockType)  /* [in] */
634 {
635   return S_OK;
636 }
637
638 static HRESULT WINAPI NoStatStreamImpl_UnlockRegion(
639                   IStream*       iface,
640                   ULARGE_INTEGER libOffset,   /* [in] */
641                   ULARGE_INTEGER cb,          /* [in] */
642                   DWORD          dwLockType)  /* [in] */
643 {
644   return S_OK;
645 }
646
647 static HRESULT WINAPI NoStatStreamImpl_Stat(
648                   IStream*     iface,
649                   STATSTG*     pstatstg,     /* [out] */
650                   DWORD        grfStatFlag)  /* [in] */
651 {
652   return E_NOTIMPL;
653 }
654
655 static HRESULT WINAPI NoStatStreamImpl_Clone(
656                   IStream*     iface,
657                   IStream**    ppstm) /* [out] */
658 {
659   return E_NOTIMPL;
660 }
661 static const IStreamVtbl NoStatStreamImpl_Vtbl;
662
663 /*
664     Build an object that implements IStream, without IStream_Stat capabilities.
665     Receives a memory handle with data buffer. If memory handle is non-null,
666     it is assumed to be unlocked, otherwise an internal memory handle is allocated.
667     In any case the object takes ownership of memory handle and will free it on
668     object release.
669  */
670 static NoStatStreamImpl* NoStatStreamImpl_Construct(HGLOBAL hGlobal)
671 {
672   NoStatStreamImpl* newStream;
673
674   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(NoStatStreamImpl));
675   if (newStream!=0)
676   {
677     newStream->lpVtbl = &NoStatStreamImpl_Vtbl;
678     newStream->ref    = 1;
679     newStream->supportHandle = hGlobal;
680
681     if (!newStream->supportHandle)
682       newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD |
683                                              GMEM_SHARE, 0);
684     newStream->currentPosition.u.HighPart = 0;
685     newStream->currentPosition.u.LowPart = 0;
686     newStream->streamSize.u.HighPart = 0;
687     newStream->streamSize.u.LowPart  = GlobalSize(newStream->supportHandle);
688   }
689   return newStream;
690 }
691
692
693 static const IStreamVtbl NoStatStreamImpl_Vtbl =
694 {
695     NoStatStreamImpl_QueryInterface,
696     NoStatStreamImpl_AddRef,
697     NoStatStreamImpl_Release,
698     NoStatStreamImpl_Read,
699     NoStatStreamImpl_Write,
700     NoStatStreamImpl_Seek,
701     NoStatStreamImpl_SetSize,
702     NoStatStreamImpl_CopyTo,
703     NoStatStreamImpl_Commit,
704     NoStatStreamImpl_Revert,
705     NoStatStreamImpl_LockRegion,
706     NoStatStreamImpl_UnlockRegion,
707     NoStatStreamImpl_Stat,
708     NoStatStreamImpl_Clone
709 };