1 /* Unit test suite for resources.
3 * Copyright 2004 Ferenc Wagner
4 * Copyright 2003, 2004 Mike McCormack
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "wine/test.h"
26 static UINT (WINAPI
*pPrivateExtractIconsA
)(LPCSTR
, int, int, int, HICON
*, UINT
*, UINT
, UINT
) = NULL
;
28 static void init_function_pointers(void)
30 HMODULE hmod
= GetModuleHandleA("user32.dll");
31 pPrivateExtractIconsA
= (void*)GetProcAddress(hmod
, "PrivateExtractIconsA");
34 static void test_LoadStringW(void)
36 HINSTANCE hInst
= GetModuleHandleA(NULL
);
37 WCHAR copiedstringw
[128], returnedstringw
[128], *resourcepointer
= NULL
;
38 char copiedstring
[128], returnedstring
[128];
39 int length1
, length2
, retvalue
, i
;
44 } string_table_tests
[] =
47 { 13, L
"&More Windows..." },
57 { 809, L
"&Try Again" },
58 { 810, L
"&Continue" },
61 /* Check that the string which is returned by LoadStringW matches
62 the string at the pointer returned by LoadStringW when called with buflen = 0 */
63 SetLastError(0xdeadbeef);
64 length1
= LoadStringW(hInst
, 2, (WCHAR
*) &resourcepointer
, 0); /* get pointer to resource. */
67 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED
)
68 win_skip( "LoadStringW not implemented\n" );
70 win_skip( "LoadStringW does not return a pointer to the resource\n" );
73 length2
= LoadStringW(hInst
, 2, returnedstringw
, ARRAY_SIZE(returnedstringw
)); /* get resource string */
74 ok(length2
> 0, "LoadStringW failed to load resource 2, ret %d, err %ld\n", length2
, GetLastError());
75 ok(length1
== length2
, "LoadStringW returned different values dependent on buflen. ret1 %d, ret2 %d\n",
77 ok(length1
> 0 && resourcepointer
!= NULL
, "LoadStringW failed to get pointer to resource 2, ret %d, err %ld\n",
78 length1
, GetLastError());
80 /* Copy the resource since it is not '\0' terminated, and add '\0' to the end */
81 if(resourcepointer
!= NULL
) /* Check that the resource pointer was loaded to avoid access violation */
83 memcpy(copiedstringw
, resourcepointer
, length1
* sizeof(WCHAR
));
84 copiedstringw
[length1
] = '\0';
85 /* check that strings match */
86 WideCharToMultiByte( CP_ACP
, 0, returnedstringw
, -1, returnedstring
, 128, NULL
, NULL
);
87 WideCharToMultiByte( CP_ACP
, 0, copiedstringw
, -1, copiedstring
, 128, NULL
, NULL
);
88 ok(!memcmp(copiedstringw
, returnedstringw
, (length2
+ 1)*sizeof(WCHAR
)),
89 "strings don't match: returnedstring = %s, copiedstring = %s\n", returnedstring
, copiedstring
);
92 /* check that calling LoadStringW with buffer = NULL returns zero */
93 retvalue
= LoadStringW(hInst
, 2, NULL
, 0);
94 ok(!retvalue
, "LoadStringW returned a non-zero value when called with buffer = NULL, retvalue = %d\n", retvalue
);
95 /* check again, with a different buflen value, that calling LoadStringW with buffer = NULL returns zero */
96 retvalue
= LoadStringW(hInst
, 2, NULL
, 128);
97 ok(!retvalue
, "LoadStringW returned a non-zero value when called with buffer = NULL, retvalue = %d\n", retvalue
);
99 /* Test builtin string table in user32. */
100 if ((PRIMARYLANGID(LANGIDFROMLCID(GetSystemDefaultLCID())) != LANG_ENGLISH
)
101 || (PRIMARYLANGID(LANGIDFROMLCID(GetThreadLocale())) != LANG_ENGLISH
))
103 skip("Skip for Non-English system.\n");
108 hInst
= GetModuleHandleW(L
"user32.dll");
109 ok(!!hInst
, "Can't get module %#lx.\n", GetLastError());
111 for (i
= 0; i
< ARRAYSIZE(string_table_tests
); i
++)
113 winetest_push_context("Test %u", i
);
115 length1
= LoadStringW(hInst
, string_table_tests
[i
].id
, returnedstringw
, ARRAY_SIZE(returnedstringw
));
116 ok(length1
== wcslen(string_table_tests
[i
].string
), "Got wrong length %d.\n", length1
);
117 ok(!wcscmp(returnedstringw
, string_table_tests
[i
].string
), "Got wrong string %s.\n",
118 debugstr_w(returnedstringw
));
120 winetest_pop_context();
125 static void test_LoadStringA (void)
127 HINSTANCE hInst
= GetModuleHandleA(NULL
);
128 static const char str
[] = "String resource"; /* same in resource.rc */
132 unsigned int expected
;
134 struct string_test tests
[] = {{sizeof buf
, sizeof str
- 1},
135 {sizeof str
, sizeof str
- 1},
136 {sizeof str
- 1, sizeof str
- 2}};
140 assert (sizeof str
< sizeof buf
);
141 for (i
= 0; i
< ARRAY_SIZE(tests
); i
++) {
142 const unsigned int bufsiz
= tests
[i
].bufsiz
;
143 const unsigned int expected
= tests
[i
].expected
;
144 const int len
= LoadStringA (hInst
, 0, buf
, bufsiz
);
146 ok (len
== expected
, "bufsiz=%d: got %d, expected %d\n",
147 bufsiz
, len
, expected
);
148 if (len
!= expected
) continue;
149 ok (!memcmp (buf
, str
, len
),
150 "bufsiz=%d: got '%s', expected '%.*s'\n",
151 bufsiz
, buf
, len
, str
);
152 ok (buf
[len
] == 0, "bufsiz=%d: NUL termination missing\n",
156 ret
= LoadStringA(hInst
, 1, buf
, sizeof(buf
) );
157 ok( ret
> 0, "LoadString failed: ret %d err %ld\n", ret
, GetLastError());
158 ret2
= LoadStringA( hInst
, MAKELONG( 1, 0x8000 ), buf
, sizeof(buf
));
159 ok( ret2
== ret
, "LoadString failed: ret %d err %ld\n", ret
, GetLastError());
160 ret2
= LoadStringA( hInst
, MAKELONG( 1, 0xffff ), buf
, sizeof(buf
));
161 ok( ret2
== ret
, "LoadString failed: ret %d err %ld\n", ret
, GetLastError());
163 ret
= LoadStringA(hInst
, 65534, buf
, sizeof(buf
) );
164 ok( ret
> 0, "LoadString failed: ret %d err %ld\n", ret
, GetLastError());
165 ret2
= LoadStringA( hInst
, MAKELONG( 65534, 0x8000 ), buf
, sizeof(buf
));
166 ok( ret2
== ret
, "LoadString failed: ret %d err %ld\n", ret
, GetLastError());
167 ret2
= LoadStringA( hInst
, MAKELONG( 65534, 0xffff ), buf
, sizeof(buf
));
168 ok( ret2
== ret
, "LoadString failed: ret %d err %ld\n", ret
, GetLastError());
170 ret
= LoadStringA(hInst
, 0, buf
, 0);
171 ok( ret
== -1 || broken(ret
== 0),
172 "LoadStringA did not return -1 when called with buflen = 0, got %d, err %ld\n",
173 ret
, GetLastError());
175 SetLastError(0xdeadbeef);
177 ret
= LoadStringA(hInst
, 1, buf
, 1);
178 ok( !ret
, "LoadString returned %d\n", ret
);
179 ok( buf
[0] == 0, "buf[0] = %c (%x)\n", buf
[0], buf
[0]);
180 ok( GetLastError() == 0xdeadbeef, "GetLastError() = %ld\n", GetLastError());
183 static void test_accel1(void)
189 /* now create our own valid accelerator table */
193 ac
[n
++].fVirt
= FVIRTKEY
| FNOINVERT
;
197 ac
[n
++].fVirt
= FNOINVERT
;
203 hAccel
= CreateAcceleratorTableA( &ac
[0], n
);
204 ok( hAccel
!= NULL
, "create accelerator table\n");
206 r
= DestroyAcceleratorTable( hAccel
);
207 ok( r
, "destroy accelerator table\n");
209 /* now try create an invalid one */
213 ac
[n
++].fVirt
= FVIRTKEY
| FNOINVERT
;
217 ac
[n
++].fVirt
= (SHORT
) 0xffff;
221 ac
[n
++].fVirt
= (SHORT
) 0xfff0;
225 ac
[n
++].fVirt
= 0x0000;
229 ac
[n
++].fVirt
= 0x0001;
231 hAccel
= CreateAcceleratorTableA( &ac
[0], n
);
232 ok( hAccel
!= NULL
, "create accelerator table\n");
234 r
= CopyAcceleratorTableA( hAccel
, NULL
, 0 );
235 ok( r
== n
, "two entries in table %u/%u\n", r
, n
);
237 r
= CopyAcceleratorTableA( hAccel
, &ac
[0], n
);
238 ok( r
== n
, "still should be two entries in table %u/%u\n", r
, n
);
241 ok( ac
[n
].cmd
== 1000, "cmd 0 not preserved got %x\n", ac
[n
].cmd
);
242 ok( ac
[n
].key
== 'A', "key 0 not preserved got %x\n", ac
[n
].key
);
243 ok( ac
[n
].fVirt
== (FVIRTKEY
| FNOINVERT
), "fVirt 0 not preserved got %x\n", ac
[n
].fVirt
);
245 if (++n
== r
) goto done
;
246 ok( ac
[n
].cmd
== 0xffff, "cmd 1 not preserved got %x\n", ac
[n
].cmd
);
247 ok( ac
[n
].key
== 0xffff, "key 1 not preserved got %x\n", ac
[n
].key
);
248 ok( ac
[n
].fVirt
== 0x007f, "fVirt 1 wrong got %x\n", ac
[n
].fVirt
);
250 if (++n
== r
) goto done
;
251 ok( ac
[n
].cmd
== 0xfff0, "cmd 2 not preserved got %x\n", ac
[n
].cmd
);
252 ok( ac
[n
].key
== 'B', "key 2 not preserved got %x\n", ac
[n
].key
);
253 ok( ac
[n
].fVirt
== 0x0070, "fVirt 2 wrong got %x\n", ac
[n
].fVirt
);
255 if (++n
== r
) goto done
;
256 ok( ac
[n
].cmd
== 0xfff0, "cmd 3 not preserved got %x\n", ac
[n
].cmd
);
257 ok( ac
[n
].key
== 'C', "key 3 not preserved got %x\n", ac
[n
].key
);
258 ok( ac
[n
].fVirt
== 0x0000, "fVirt 3 wrong got %x\n", ac
[n
].fVirt
);
260 if (++n
== r
) goto done
;
261 ok( ac
[n
].cmd
== 0xfff0, "cmd 4 not preserved got %x\n", ac
[n
].cmd
);
262 ok( ac
[n
].key
== 0xffff, "key 4 not preserved got %x\n", ac
[n
].key
);
263 ok( ac
[n
].fVirt
== 0x0001, "fVirt 4 wrong got %x\n", ac
[n
].fVirt
);
265 r
= DestroyAcceleratorTable( hAccel
);
266 ok( r
, "destroy accelerator table\n");
268 hAccel
= CreateAcceleratorTableA( &ac
[0], 0 );
269 ok( !hAccel
|| broken(hAccel
!= NULL
), /* nt4 */ "zero elements should fail\n");
271 /* these will on crash win2k
272 hAccel = CreateAcceleratorTable( NULL, 1 );
273 hAccel = CreateAcceleratorTable( &ac[0], -1 );
278 * memcmp on the tables works in Windows, but does not work in wine, as
279 * there is an extra undefined and unused byte between fVirt and the key
281 static void test_accel2(void)
297 * hac = CreateAcceleratorTable( NULL, 1 );
300 /* try a zero count */
301 hac
= CreateAcceleratorTableA( &ac
[0], 0 );
302 ok( !hac
|| broken(hac
!= NULL
), /* nt4 */ "fail\n");
303 if (!hac
) ok( !DestroyAcceleratorTable( hac
), "destroy failed\n");
305 /* creating one accelerator should work */
306 hac
= CreateAcceleratorTableA( &ac
[0], 1 );
307 ok( hac
!= NULL
, "fail\n");
308 ok( 1 == CopyAcceleratorTableA( hac
, out
, 1 ), "copy failed\n");
309 ok( DestroyAcceleratorTable( hac
), "destroy failed\n");
311 /* how about two of the same type? */
312 hac
= CreateAcceleratorTableA( &ac
[0], 2);
313 ok( hac
!= NULL
, "fail\n");
314 res
= CopyAcceleratorTableA( hac
, NULL
, 100 );
315 ok( res
== 2, "copy null failed %d\n", res
);
316 res
= CopyAcceleratorTableA( hac
, NULL
, 0 );
317 ok( res
== 2, "copy null failed %d\n", res
);
318 res
= CopyAcceleratorTableA( hac
, NULL
, 1 );
319 ok( res
== 2, "copy null failed %d\n", res
);
320 ok( 1 == CopyAcceleratorTableA( hac
, out
, 1 ), "copy 1 failed\n");
321 ok( 2 == CopyAcceleratorTableA( hac
, out
, 2 ), "copy 2 failed\n");
322 ok( DestroyAcceleratorTable( hac
), "destroy failed\n");
323 /* ok( !memcmp( ac, out, sizeof ac ), "tables different\n"); */
325 /* how about two of the same type with a non-zero key? */
328 hac
= CreateAcceleratorTableA( &ac
[0], 2);
329 ok( hac
!= NULL
, "fail\n");
330 ok( 2 == CopyAcceleratorTableA( hac
, out
, 2 ), "copy 2 failed\n");
331 ok( DestroyAcceleratorTable( hac
), "destroy failed\n");
332 /* ok( !memcmp( ac, out, sizeof ac ), "tables different\n"); */
334 /* how about two of the same type with a non-zero virtual key? */
335 ac
[0].fVirt
= FVIRTKEY
;
337 ac
[1].fVirt
= FVIRTKEY
;
339 hac
= CreateAcceleratorTableA( &ac
[0], 2);
340 ok( hac
!= NULL
, "fail\n");
341 ok( 2 == CopyAcceleratorTableA( hac
, out
, 2 ), "copy 2 failed\n");
342 /* ok( !memcmp( ac, out, sizeof ac ), "tables different\n"); */
343 ok( DestroyAcceleratorTable( hac
), "destroy failed\n");
345 /* how virtual key codes */
346 ac
[0].fVirt
= FVIRTKEY
;
347 hac
= CreateAcceleratorTableA( &ac
[0], 1);
348 ok( hac
!= NULL
, "fail\n");
349 ok( 1 == CopyAcceleratorTableA( hac
, out
, 2 ), "copy 2 failed\n");
350 /* ok( !memcmp( ac, out, sizeof ac/2 ), "tables different\n"); */
351 ok( DestroyAcceleratorTable( hac
), "destroy failed\n");
353 /* how turning on all bits? */
357 hac
= CreateAcceleratorTableA( &ac
[0], 1);
358 ok( hac
!= NULL
, "fail\n");
359 ok( 1 == CopyAcceleratorTableA( hac
, out
, 1 ), "copy 1 failed\n");
360 /* ok( memcmp( ac, out, sizeof ac/2 ), "tables not different\n"); */
361 ok( out
[0].cmd
== ac
[0].cmd
, "cmd modified\n");
362 ok( out
[0].fVirt
== (ac
[0].fVirt
&0x7f), "fVirt not modified\n");
363 ok( out
[0].key
== ac
[0].key
, "key modified\n");
364 ok( DestroyAcceleratorTable( hac
), "destroy failed\n");
366 /* how turning on all bits? */
367 memset( ac
, 0xff, sizeof ac
);
368 hac
= CreateAcceleratorTableA( &ac
[0], 2);
369 ok( hac
!= NULL
, "fail\n");
370 res
= CopyAcceleratorTableA( hac
, out
, 2 );
371 ok( res
== 2, "copy 2 failed %d\n", res
);
372 /* ok( memcmp( ac, out, sizeof ac ), "tables not different\n"); */
373 ok( out
[0].cmd
== ac
[0].cmd
, "cmd modified\n");
374 ok( out
[0].fVirt
== (ac
[0].fVirt
&0x7f), "fVirt not modified\n");
375 ok( out
[0].key
== ac
[0].key
, "key modified\n");
378 ok( out
[1].cmd
== ac
[1].cmd
, "cmd modified\n");
379 ok( out
[1].fVirt
== (ac
[1].fVirt
&0x7f), "fVirt not modified\n");
380 ok( out
[1].key
== ac
[1].key
, "key modified\n");
382 ok( DestroyAcceleratorTable( hac
), "destroy failed\n");
385 static void test_PrivateExtractIcons(void) {
386 const CHAR szShell32Dll
[] = "shell32.dll";
388 UINT i
, aIconId
[256], cIcons
, cIcons2
;
390 if (!pPrivateExtractIconsA
) return;
392 cIcons
= pPrivateExtractIconsA("", 0, 16, 16, ahIcon
, aIconId
, 1, 0);
393 ok(cIcons
== ~0u, "got %u\n", cIcons
);
395 cIcons
= pPrivateExtractIconsA("notepad.exe", 0, 16, 16, NULL
, NULL
, 1, 0);
396 ok(cIcons
== 1 || broken(cIcons
== 4) /* win11 */ ||
397 broken(cIcons
== 2) /* win2k */, "got %u\n", cIcons
);
399 ahIcon
[0] = (HICON
)0xdeadbeef;
400 cIcons
= pPrivateExtractIconsA("notepad.exe", 0, 16, 16, ahIcon
, NULL
, 1, 0);
401 ok(cIcons
== 1, "got %u\n", cIcons
);
402 ok(ahIcon
[0] != (HICON
)0xdeadbeef, "icon not set\n");
403 DestroyIcon(ahIcon
[0]);
405 ahIcon
[0] = (HICON
)0xdeadbeef;
406 aIconId
[0] = 0xdeadbeef;
407 cIcons
= pPrivateExtractIconsA("notepad.exe", 0, 16, 16, ahIcon
, aIconId
, 1, 0);
408 ok(cIcons
== 1, "got %u\n", cIcons
);
409 ok(ahIcon
[0] != (HICON
)0xdeadbeef, "icon not set\n");
410 ok(aIconId
[0] != 0xdeadbeef, "id not set\n");
411 DestroyIcon(ahIcon
[0]);
413 cIcons
= pPrivateExtractIconsA(szShell32Dll
, 0, 16, 16, NULL
, NULL
, 0, 0);
414 cIcons2
= pPrivateExtractIconsA(szShell32Dll
, 4, MAKELONG(32,16), MAKELONG(32,16),
416 ok((cIcons
== cIcons2
) && (cIcons
> 0),
417 "Icon count should be independent of requested icon sizes and base icon index! "
418 "(cIcons=%d, cIcons2=%d)\n", cIcons
, cIcons2
);
420 cIcons
= pPrivateExtractIconsA(szShell32Dll
, 0, 16, 16, ahIcon
, aIconId
, 0, 0);
421 ok(cIcons
== 0, "Zero icons requested, got cIcons=%d\n", cIcons
);
423 cIcons
= pPrivateExtractIconsA(szShell32Dll
, 0, 16, 16, ahIcon
, aIconId
, 3, 0);
424 ok(cIcons
== 3, "Three icons requested got cIcons=%d\n", cIcons
);
425 for (i
= 0; i
< cIcons
; i
++) DestroyIcon(ahIcon
[i
]);
427 /* count must be a multiple of two when getting two sizes */
428 cIcons
= pPrivateExtractIconsA(szShell32Dll
, 0, MAKELONG(16,32), MAKELONG(16,32),
429 ahIcon
, aIconId
, 3, 0);
430 ok(cIcons
== 0 /* vista */ || cIcons
== 4, "Three icons requested got cIcons=%d\n", cIcons
);
431 for (i
= 0; i
< cIcons
; i
++) DestroyIcon(ahIcon
[i
]);
433 cIcons
= pPrivateExtractIconsA(szShell32Dll
, 0, MAKELONG(16,32), MAKELONG(16,32),
434 ahIcon
, aIconId
, 4, 0);
435 ok(cIcons
== 4, "Four icons requested got cIcons=%d\n", cIcons
);
436 for (i
= 0; i
< cIcons
; i
++) DestroyIcon(ahIcon
[i
]);
439 static void test_LoadImage(void)
444 bmp
= LoadBitmapA(GetModuleHandleA(NULL
), MAKEINTRESOURCEA(100));
445 ok(bmp
!= NULL
, "Could not load a bitmap resource\n");
446 if (bmp
) DeleteObject(bmp
);
448 hres
= FindResourceA(GetModuleHandleA(NULL
), "#100", (LPCSTR
)RT_BITMAP
);
449 ok(hres
!= NULL
, "Could not find a bitmap resource with a numeric string\n");
451 bmp
= LoadBitmapA(GetModuleHandleA(NULL
), "#100");
452 ok(bmp
!= NULL
, "Could not load a bitmap resource with a numeric string\n");
453 if (bmp
) DeleteObject(bmp
);
458 init_function_pointers();
463 test_PrivateExtractIcons();