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