1 /* Unit test suite for list boxes.
2  *
3  * Copyright 2003 Ferenc Wagner
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #include <stdarg.h>
21 #include <stdio.h>
22 
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27 #include "winnls.h"
28 #include "commctrl.h"
29 
30 #include "wine/heap.h"
31 #include "wine/test.h"
32 #include "v6util.h"
33 #include "msg.h"
34 
35 enum seq_index
36 {
37     LB_SEQ_INDEX,
38     PARENT_SEQ_INDEX,
39     NUM_MSG_SEQUENCES
40 };
41 
42 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
43 
44 /* encoded MEASUREITEMSTRUCT into a WPARAM */
45 typedef struct
46 {
47     union
48     {
49         struct
50         {
51             UINT CtlType : 4;
52             UINT CtlID   : 4;
53             UINT itemID  : 4;
54             UINT wParam  : 20;
55         } item;
56         WPARAM wp;
57     } u;
58 } MEASURE_ITEM_STRUCT;
59 
60 static unsigned hash_Ly_W(const WCHAR *str)
61 {
62     unsigned hash = 0;
63 
64     for (; *str; str++)
65         hash = hash * 1664525u + (unsigned char)(*str) + 1013904223u;
66 
67     return hash;
68 }
69 
70 static unsigned hash_Ly(const char *str)
71 {
72     unsigned hash = 0;
73 
74     for (; *str; str++)
75         hash = hash * 1664525u + (unsigned char)(*str) + 1013904223u;
76 
77     return hash;
78 }
79 
80 static const char * const strings[4] = {
81     "First added",
82     "Second added",
83     "Third added",
84     "Fourth added which is very long because at some time we only had a 256 byte character buffer and "
85       "that was overflowing in one of those applications that had a common dialog file open box and tried "
86       "to add a 300 characters long custom filter string which of course the code did not like and crashed. "
87       "Just make sure this string is longer than 256 characters."
88 };
89 
90 static const char BAD_EXTENSION[] = "*.badtxt";
91 
92 #define ID_LISTBOX 1
93 
94 static LRESULT WINAPI listbox_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
95 {
96     WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
97     static LONG defwndproc_counter = 0;
98     struct message msg = { 0 };
99     LRESULT ret;
100 
101     switch (message)
102     {
103         case WM_SIZE:
104         case WM_GETTEXT:
105         case WM_PAINT:
106         case WM_ERASEBKGND:
107         case WM_WINDOWPOSCHANGING:
108         case WM_WINDOWPOSCHANGED:
109         case WM_NCCALCSIZE:
110         case WM_NCPAINT:
111         case WM_NCHITTEST:
112         case WM_DEVICECHANGE:
113             break;
114 
115         default:
116             msg.message = message;
117             msg.flags = sent|wparam|lparam;
118             if (defwndproc_counter) msg.flags |= defwinproc;
119             msg.wParam = wParam;
120             msg.lParam = lParam;
121             add_message(sequences, LB_SEQ_INDEX, &msg);
122     }
123 
124     defwndproc_counter++;
125     ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
126     defwndproc_counter--;
127 
128     return ret;
129 }
130 
131 static HWND create_listbox(DWORD add_style, HWND parent)
132 {
133     INT_PTR ctl_id = 0;
134     WNDPROC oldproc;
135     HWND handle;
136 
137     if (parent)
138         ctl_id = ID_LISTBOX;
139 
140     handle = CreateWindowA(WC_LISTBOXA, "TestList", (LBS_STANDARD & ~LBS_SORT) | add_style, 0, 0, 100, 100,
141         parent, (HMENU)ctl_id, NULL, 0);
142     ok(handle != NULL, "Failed to create listbox window.\n");
143 
144     SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[0]);
145     SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[1]);
146     SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[2]);
147     SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[3]);
148 
149     oldproc = (WNDPROC)SetWindowLongPtrA(handle, GWLP_WNDPROC, (LONG_PTR)listbox_wnd_proc);
150     SetWindowLongPtrA(handle, GWLP_USERDATA, (LONG_PTR)oldproc);
151 
152     return handle;
153 }
154 
155 struct listbox_prop
156 {
157     DWORD add_style;
158 };
159 
160 struct listbox_stat
161 {
162     int selected, anchor, caret, selcount;
163 };
164 
165 struct listbox_test
166 {
167     struct listbox_stat  init,  init_todo;
168     struct listbox_stat click, click_todo;
169     struct listbox_stat  step,  step_todo;
170     struct listbox_stat   sel,   sel_todo;
171 };
172 
173 static void listbox_query(HWND handle, struct listbox_stat *results)
174 {
175     results->selected = SendMessageA(handle, LB_GETCURSEL, 0, 0);
176     results->anchor   = SendMessageA(handle, LB_GETANCHORINDEX, 0, 0);
177     results->caret    = SendMessageA(handle, LB_GETCARETINDEX, 0, 0);
178     results->selcount = SendMessageA(handle, LB_GETSELCOUNT, 0, 0);
179 }
180 
181 static void buttonpress(HWND handle, WORD x, WORD y)
182 {
183     LPARAM lp = x + (y << 16);
184 
185     SendMessageA(handle, WM_LBUTTONDOWN, MK_LBUTTON, lp);
186     SendMessageA(handle, WM_LBUTTONUP, 0, lp);
187 }
188 
189 static void keypress(HWND handle, WPARAM keycode, BYTE scancode, BOOL extended)
190 {
191     LPARAM lp = 1 + (scancode << 16) + (extended ? KEYEVENTF_EXTENDEDKEY : 0);
192 
193     SendMessageA(handle, WM_KEYDOWN, keycode, lp);
194     SendMessageA(handle, WM_KEYUP  , keycode, lp | 0xc000000);
195 }
196 
197 #define listbox_field_ok(t, s, f, got) \
198   ok (t.s.f==got.f, "style %#x, step " #s ", field " #f \
199       ": expected %d, got %d\n", style, t.s.f, got.f)
200 
201 #define listbox_todo_field_ok(t, s, f, got) \
202   todo_wine_if (t.s##_todo.f) { listbox_field_ok(t, s, f, got); }
203 
204 #define listbox_ok(t, s, got) \
205   listbox_todo_field_ok(t, s, selected, got); \
206   listbox_todo_field_ok(t, s, anchor, got); \
207   listbox_todo_field_ok(t, s, caret, got); \
208   listbox_todo_field_ok(t, s, selcount, got)
209 
210 static void run_test(DWORD style, const struct listbox_test test)
211 {
212     static const struct message delete_seq[] =
213     {
214         { LB_DELETESTRING, sent|wparam|lparam, 0, 0 },
215         { LB_DELETESTRING, sent|wparam|lparam, 0, 0 },
216         { LB_DELETESTRING, sent|wparam|lparam, 0, 0 },
217         { LB_DELETESTRING, sent|wparam|lparam, 0, 0 },
218         { LB_RESETCONTENT, sent|wparam|lparam|defwinproc, 0, 0 },
219         { 0 }
220     };
221     struct listbox_stat answer;
222     int i, res, count;
223     RECT second_item;
224     HWND hLB;
225 
226     hLB = create_listbox (style, 0);
227 
228     listbox_query (hLB, &answer);
229     listbox_ok (test, init, answer);
230 
231     SendMessageA(hLB, LB_GETITEMRECT, 1, (LPARAM) &second_item);
232     buttonpress(hLB, (WORD)second_item.left, (WORD)second_item.top);
233 
234     listbox_query(hLB, &answer);
235     listbox_ok(test, click, answer);
236 
237     keypress(hLB, VK_DOWN, 0x50, TRUE);
238 
239     listbox_query(hLB, &answer);
240     listbox_ok(test, step, answer);
241 
242     DestroyWindow(hLB);
243 
244     hLB = create_listbox(style, 0);
245 
246     SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2));
247     listbox_query(hLB, &answer);
248     listbox_ok(test, sel, answer);
249 
250     for (i = 0; i < 4 && !(style & LBS_NODATA); i++)
251     {
252         DWORD size = SendMessageA(hLB, LB_GETTEXTLEN, i, 0);
253         int resA, resW;
254         WCHAR *txtw;
255         CHAR *txt;
256 
257         txt = heap_alloc_zero(size + 1);
258         resA = SendMessageA(hLB, LB_GETTEXT, i, (LPARAM)txt);
259         ok(!strcmp(txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]);
260 
261         txtw = heap_alloc_zero((size + 1) * sizeof(*txtw));
262         resW = SendMessageW(hLB, LB_GETTEXT, i, (LPARAM)txtw);
263         ok(resA == resW, "Unexpected text length.\n");
264         WideCharToMultiByte(CP_ACP, 0, txtw, -1, txt, size, NULL, NULL);
265         ok(!strcmp (txt, strings[i]), "Unexpected string for item %d, %s vs %s.\n", i, txt, strings[i]);
266 
267         heap_free(txtw);
268         heap_free(txt);
269     }
270 
271     /* Confirm the count of items, and that an invalid delete does not remove anything */
272     res = SendMessageA(hLB, LB_GETCOUNT, 0, 0);
273     ok(res == 4, "Expected 4 items, got %d\n", res);
274     res = SendMessageA(hLB, LB_DELETESTRING, -1, 0);
275     ok(res == LB_ERR, "Expected LB_ERR items, got %d\n", res);
276     res = SendMessageA(hLB, LB_DELETESTRING, 4, 0);
277     ok(res == LB_ERR, "Expected LB_ERR items, got %d\n", res);
278     count = SendMessageA(hLB, LB_GETCOUNT, 0, 0);
279     ok(count == 4, "Unexpected item count %d.\n", count);
280 
281     /* Emptying listbox sends a LB_RESETCONTENT to itself. */
282     flush_sequence(sequences, LB_SEQ_INDEX);
283     for (i = count; i--;)
284     {
285         res = SendMessageA(hLB, LB_DELETESTRING, 0, 0);
286         ok(res == i, "Unexpected return value %d.\n", res);
287     }
288     ok_sequence(sequences, LB_SEQ_INDEX, delete_seq, "Emptying listbox", FALSE);
289 
290     DestroyWindow(hLB);
291 }
292 
293 static void test_item_height(void)
294 {
295     INT itemHeight;
296     TEXTMETRICA tm;
297     HFONT font;
298     HWND hLB;
299     HDC hdc;
300 
301     hLB = create_listbox (0, 0);
302     ok ((hdc = GetDCEx( hLB, 0, DCX_CACHE )) != 0, "Can't get hdc\n");
303     ok ((font = GetCurrentObject(hdc, OBJ_FONT)) != 0, "Can't get the current font\n");
304     ok (GetTextMetricsA( hdc, &tm ), "Can't read font metrics\n");
305     ReleaseDC( hLB, hdc);
306 
307     ok (SendMessageA(hLB, WM_SETFONT, (WPARAM)font, 0) == 0, "Can't set font\n");
308 
309     itemHeight = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0);
310     ok (itemHeight == tm.tmHeight, "Item height wrong, got %d, expecting %d\n", itemHeight, tm.tmHeight);
311 
312     DestroyWindow (hLB);
313 
314     hLB = CreateWindowA(WC_LISTBOXA, "TestList", LBS_OWNERDRAWVARIABLE,  0, 0, 100, 100, NULL, NULL, NULL, 0);
315 
316     itemHeight = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0);
317     ok(itemHeight > 0 && itemHeight <= tm.tmHeight, "Unexpected item height %d, expected %d.\n",
318         itemHeight, tm.tmHeight);
319     itemHeight = SendMessageA(hLB, LB_GETITEMHEIGHT, 5, 0);
320     ok(itemHeight > 0 && itemHeight <= tm.tmHeight, "Unexpected item height %d, expected %d.\n",
321         itemHeight, tm.tmHeight);
322     itemHeight = SendMessageA(hLB, LB_GETITEMHEIGHT, -5, 0);
323     ok(itemHeight > 0 && itemHeight <= tm.tmHeight, "Unexpected item height %d, expected %d.\n",
324         itemHeight, tm.tmHeight);
325 
326     DestroyWindow (hLB);
327 }
328 
329 static int got_selchange;
330 
331 static LRESULT WINAPI main_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
332 {
333     static LONG defwndproc_counter = 0;
334     struct message m = { 0 };
335     LRESULT ret;
336 
337     m.message = msg;
338     m.flags = sent|wparam|lparam;
339     if (defwndproc_counter) m.flags |= defwinproc;
340     m.wParam = wParam;
341     m.lParam = lParam;
342 
343     switch (msg)
344     {
345     case WM_MEASUREITEM:
346     {
347         MEASUREITEMSTRUCT *mis = (void *)lParam;
348         BOOL is_unicode_data = FALSE;
349         MEASURE_ITEM_STRUCT mi;
350 
351         if (mis->CtlType == ODT_LISTBOX)
352         {
353             HWND ctrl = GetDlgItem(hwnd, mis->CtlID);
354             is_unicode_data = GetWindowLongA(ctrl, GWL_STYLE) & LBS_HASSTRINGS;
355         }
356 
357         mi.u.wp = 0;
358         mi.u.item.CtlType = mis->CtlType;
359         mi.u.item.CtlID = mis->CtlID;
360         mi.u.item.itemID = mis->itemID;
361         mi.u.item.wParam = wParam;
362 
363         m.wParam = mi.u.wp;
364         if (is_unicode_data)
365             m.lParam = mis->itemData ? hash_Ly_W((const WCHAR *)mis->itemData) : 0;
366         else
367             m.lParam = mis->itemData ? hash_Ly((const char *)mis->itemData) : 0;
368         add_message(sequences, PARENT_SEQ_INDEX, &m);
369 
370         ok(wParam == mis->CtlID, "got wParam=%08lx, expected %08x\n", wParam, mis->CtlID);
371         ok(mis->CtlType == ODT_LISTBOX, "mi->CtlType = %u\n", mis->CtlType);
372         ok(mis->CtlID == 1, "mi->CtlID = %u\n", mis->CtlID);
373         ok(mis->itemHeight, "mi->itemHeight = 0\n");
374 
375         break;
376     }
377     case WM_COMPAREITEM:
378     {
379         COMPAREITEMSTRUCT *cis = (COMPAREITEMSTRUCT *)lParam;
380         HWND ctrl = GetDlgItem(hwnd, cis->CtlID);
381         BOOL is_unicode_data = TRUE;
382 
383         ok(wParam == cis->CtlID, "expected %#x, got %#lx\n", cis->CtlID, wParam);
384         ok(cis->hwndItem == ctrl, "expected %p, got %p\n", ctrl, cis->hwndItem);
385         ok((int)cis->itemID1 >= 0, "expected >= 0, got %d\n", cis->itemID1);
386         ok((int)cis->itemID2 == -1, "expected -1, got %d\n", cis->itemID2);
387 
388         if (cis->CtlType == ODT_LISTBOX)
389             is_unicode_data = GetWindowLongA(ctrl, GWL_STYLE) & LBS_HASSTRINGS;
390 
391         if (is_unicode_data)
392         {
393             m.wParam = cis->itemData1 ? hash_Ly_W((const WCHAR *)cis->itemData1) : 0;
394             m.lParam = cis->itemData2 ? hash_Ly_W((const WCHAR *)cis->itemData2) : 0;
395         }
396         else
397         {
398             m.wParam = cis->itemData1 ? hash_Ly((const char *)cis->itemData1) : 0;
399             m.lParam = cis->itemData2 ? hash_Ly((const char *)cis->itemData2) : 0;
400         }
401         add_message(sequences, PARENT_SEQ_INDEX, &m);
402         break;
403     }
404     case WM_DRAWITEM:
405     {
406         RECT rc_item, rc_client, rc_clip;
407         DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam;
408 
409         ok(wParam == dis->CtlID, "got wParam=%08lx instead of %08x\n", wParam, dis->CtlID);
410         ok(dis->CtlType == ODT_LISTBOX, "wrong CtlType %04x\n", dis->CtlType);
411 
412         GetClientRect(dis->hwndItem, &rc_client);
413         GetClipBox(dis->hDC, &rc_clip);
414         ok(EqualRect(&rc_client, &rc_clip) || IsRectEmpty(&rc_clip),
415            "client rect of the listbox should be equal to the clip box,"
416            "or the clip box should be empty\n");
417 
418         SendMessageA(dis->hwndItem, LB_GETITEMRECT, dis->itemID, (LPARAM)&rc_item);
419         ok(EqualRect(&dis->rcItem, &rc_item), "item rects are not equal\n");
420 
421         break;
422     }
423 
424     case WM_COMMAND:
425         if (HIWORD( wParam ) == LBN_SELCHANGE) got_selchange++;
426         break;
427 
428     default:
429         break;
430     }
431 
432     defwndproc_counter++;
433     ret = DefWindowProcA(hwnd, msg, wParam, lParam);
434     defwndproc_counter--;
435 
436     return msg == WM_COMPAREITEM ? -1 : ret;
437 }
438 
439 static HWND create_parent( void )
440 {
441     static ATOM class;
442     WNDCLASSA cls;
443 
444     if (!class)
445     {
446         cls.style = 0;
447         cls.lpfnWndProc = main_window_proc;
448         cls.cbClsExtra = 0;
449         cls.cbWndExtra = 0;
450         cls.hInstance = GetModuleHandleA(NULL);
451         cls.hIcon = 0;
452         cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
453         cls.hbrBackground = GetStockObject(WHITE_BRUSH);
454         cls.lpszMenuName = NULL;
455         cls.lpszClassName = "main_window_class";
456         class = RegisterClassA( &cls );
457     }
458 
459     return CreateWindowExA(0, "main_window_class", NULL, WS_POPUP | WS_VISIBLE, 100, 100, 400, 400, GetDesktopWindow(),
460         0, GetModuleHandleA(NULL), NULL);
461 }
462 
463 static void test_ownerdraw(void)
464 {
465     static const DWORD styles[] =
466     {
467         0,
468         LBS_NODATA
469     };
470     HWND parent, hLB;
471     INT ret;
472     RECT rc;
473     UINT i;
474 
475     parent = create_parent();
476     ok(parent != NULL, "Failed to create parent window.\n");
477 
478     for (i = 0; i < ARRAY_SIZE(styles); i++)
479     {
480         hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE | styles[i], parent);
481         ok(hLB != NULL, "Failed to create listbox window.\n");
482 
483         SetForegroundWindow(hLB);
484         UpdateWindow(hLB);
485 
486         /* make height short enough */
487         SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
488         SetWindowPos(hLB, 0, 0, 0, 100, rc.bottom - rc.top + 1, SWP_NOZORDER | SWP_NOMOVE);
489 
490         /* make 0 item invisible */
491         SendMessageA(hLB, LB_SETTOPINDEX, 1, 0);
492         ret = SendMessageA(hLB, LB_GETTOPINDEX, 0, 0);
493         ok(ret == 1, "wrong top index %d\n", ret);
494 
495         SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
496         ok(!IsRectEmpty(&rc), "empty item rect\n");
497         ok(rc.top < 0, "rc.top is not negative (%d)\n", rc.top);
498 
499         DestroyWindow(hLB);
500 
501         /* Both FIXED and VARIABLE, FIXED should override VARIABLE. */
502         hLB = CreateWindowA(WC_LISTBOXA, "TestList", LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE | styles[i],
503             0, 0, 100, 100, NULL, NULL, NULL, 0);
504         ok(hLB != NULL, "last error 0x%08x\n", GetLastError());
505 
506         ok(GetWindowLongA(hLB, GWL_STYLE) & LBS_OWNERDRAWVARIABLE, "Unexpected window style.\n");
507 
508         ret = SendMessageA(hLB, LB_INSERTSTRING, -1, 0);
509         ok(ret == 0, "Unexpected return value %d.\n", ret);
510         ret = SendMessageA(hLB, LB_INSERTSTRING, -1, 0);
511         ok(ret == 1, "Unexpected return value %d.\n", ret);
512 
513         ret = SendMessageA(hLB, LB_SETITEMHEIGHT, 0, 13);
514         ok(ret == LB_OKAY, "Failed to set item height, %d.\n", ret);
515 
516         ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0);
517         ok(ret == 13, "Unexpected item height %d.\n", ret);
518 
519         ret = SendMessageA(hLB, LB_SETITEMHEIGHT, 1, 42);
520         ok(ret == LB_OKAY, "Failed to set item height, %d.\n", ret);
521 
522         ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0);
523         ok(ret == 42, "Unexpected item height %d.\n", ret);
524 
525         ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 1, 0);
526         ok(ret == 42, "Unexpected item height %d.\n", ret);
527 
528         DestroyWindow (hLB);
529     }
530 
531     DestroyWindow(parent);
532 }
533 
534 #define listbox_test_query(exp, got) \
535   ok(exp.selected == got.selected, "expected selected %d, got %d\n", exp.selected, got.selected); \
536   ok(exp.anchor == got.anchor, "expected anchor %d, got %d\n", exp.anchor, got.anchor); \
537   ok(exp.caret == got.caret, "expected caret %d, got %d\n", exp.caret, got.caret); \
538   ok(exp.selcount == got.selcount, "expected selcount %d, got %d\n", exp.selcount, got.selcount);
539 
540 static void test_LB_SELITEMRANGE(void)
541 {
542     static const struct listbox_stat test_nosel = { 0, LB_ERR, 0, 0 };
543     static const struct listbox_stat test_1 = { 0, LB_ERR, 0, 2 };
544     static const struct listbox_stat test_2 = { 0, LB_ERR, 0, 3 };
545     static const struct listbox_stat test_3 = { 0, LB_ERR, 0, 4 };
546     struct listbox_stat answer;
547     HWND hLB;
548     INT ret;
549 
550     hLB = create_listbox(LBS_EXTENDEDSEL, 0);
551     ok(hLB != NULL, "Failed to create listbox window.\n");
552 
553     listbox_query(hLB, &answer);
554     listbox_test_query(test_nosel, answer);
555 
556     ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2));
557     ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
558     listbox_query(hLB, &answer);
559     listbox_test_query(test_1, answer);
560 
561     SendMessageA(hLB, LB_SETSEL, FALSE, -1);
562     listbox_query(hLB, &answer);
563     listbox_test_query(test_nosel, answer);
564 
565     ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(0, 4));
566     ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
567     listbox_query(hLB, &answer);
568     listbox_test_query(test_3, answer);
569 
570     SendMessageA(hLB, LB_SETSEL, FALSE, -1);
571     listbox_query(hLB, &answer);
572     listbox_test_query(test_nosel, answer);
573 
574     ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(-5, 5));
575     ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
576     listbox_query(hLB, &answer);
577     listbox_test_query(test_nosel, answer);
578 
579     SendMessageA(hLB, LB_SETSEL, FALSE, -1);
580     listbox_query(hLB, &answer);
581     listbox_test_query(test_nosel, answer);
582 
583     ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(2, 10));
584     ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
585     listbox_query(hLB, &answer);
586     listbox_test_query(test_1, answer);
587 
588     SendMessageA(hLB, LB_SETSEL, FALSE, -1);
589     listbox_query(hLB, &answer);
590     listbox_test_query(test_nosel, answer);
591 
592     ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(4, 10));
593     ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
594     listbox_query(hLB, &answer);
595     listbox_test_query(test_nosel, answer);
596 
597     SendMessageA(hLB, LB_SETSEL, FALSE, -1);
598     listbox_query(hLB, &answer);
599     listbox_test_query(test_nosel, answer);
600 
601     ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(10, 1));
602     ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
603     listbox_query(hLB, &answer);
604     listbox_test_query(test_2, answer);
605 
606     SendMessageA(hLB, LB_SETSEL, FALSE, -1);
607     listbox_query(hLB, &answer);
608     listbox_test_query(test_nosel, answer);
609 
610     ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, -1));
611     ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
612     listbox_query(hLB, &answer);
613     listbox_test_query(test_2, answer);
614 
615     DestroyWindow(hLB);
616 }
617 
618 static void test_LB_SETCURSEL(void)
619 {
620     HWND parent, hLB;
621     INT ret;
622 
623     parent = create_parent();
624     ok(parent != NULL, "Failed to create parent window.\n");
625 
626     hLB = create_listbox(LBS_NOINTEGRALHEIGHT | WS_CHILD, parent);
627     ok(hLB != NULL, "Failed to create listbox.\n");
628 
629     SendMessageA(hLB, LB_SETITEMHEIGHT, 0, 32);
630 
631     ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
632     ok(ret == -1, "Unexpected anchor index %d.\n", ret);
633 
634     ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0);
635     ok(ret == 2, "LB_SETCURSEL returned %d instead of 2\n", ret);
636     ret = GetScrollPos(hLB, SB_VERT);
637     ok(ret == 0, "expected vscroll 0, got %d\n", ret);
638 
639     ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
640     ok(ret == -1, "Unexpected anchor index %d.\n", ret);
641 
642     ret = SendMessageA(hLB, LB_SETCURSEL, 3, 0);
643     ok(ret == 3, "LB_SETCURSEL returned %d instead of 3\n", ret);
644     ret = GetScrollPos(hLB, SB_VERT);
645     ok(ret == 1, "expected vscroll 1, got %d\n", ret);
646 
647     ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
648     ok(ret == -1, "Unexpected anchor index %d.\n", ret);
649 
650     DestroyWindow(hLB);
651 
652     hLB = create_listbox(0, 0);
653     ok(hLB != NULL, "Failed to create ListBox window.\n");
654 
655     ret = SendMessageA(hLB, LB_SETCURSEL, 1, 0);
656     ok(ret == 1, "Unexpected return value %d.\n", ret);
657 
658     ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
659     ok(ret == -1, "Unexpected anchor index %d.\n", ret);
660 
661     DestroyWindow(hLB);
662 
663     /* LBS_EXTENDEDSEL */
664     hLB = create_listbox(LBS_EXTENDEDSEL, 0);
665     ok(hLB != NULL, "Failed to create listbox.\n");
666 
667     ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
668     ok(ret == -1, "Unexpected anchor index %d.\n", ret);
669 
670     ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0);
671     ok(ret == -1, "LB_SETCURSEL returned %d instead of 2\n", ret);
672 
673     ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
674     ok(ret == -1, "Unexpected anchor index %d.\n", ret);
675 
676     DestroyWindow(hLB);
677 
678     /* LBS_MULTIPLESEL */
679     hLB = create_listbox(LBS_MULTIPLESEL, 0);
680     ok(hLB != NULL, "Failed to create listbox.\n");
681 
682     ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
683     ok(ret == -1, "Unexpected anchor index %d.\n", ret);
684 
685     ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0);
686     ok(ret == -1, "LB_SETCURSEL returned %d instead of 2\n", ret);
687 
688     ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
689     ok(ret == -1, "Unexpected anchor index %d.\n", ret);
690 
691     DestroyWindow(hLB);
692 }
693 
694 static void test_LB_SETSEL(void)
695 {
696     HWND list;
697     int ret;
698 
699     /* LBS_EXTENDEDSEL */
700     list = create_listbox(LBS_EXTENDEDSEL, 0);
701     ok(list != NULL, "Failed to create ListBox window.\n");
702 
703     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
704     ok(ret == -1, "Unexpected anchor index %d.\n", ret);
705 
706     ret = SendMessageA(list, LB_SETSEL, TRUE, 0);
707     ok(ret == 0, "Unexpected return value %d.\n", ret);
708     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
709     ok(ret == 0, "Unexpected anchor index %d.\n", ret);
710     ret = SendMessageA(list, LB_GETCARETINDEX, 0, 0);
711     ok(ret == 0, "Unexpected caret index %d.\n", ret);
712 
713     ret = SendMessageA(list, LB_SETSEL, TRUE, 1);
714     ok(ret == 0, "Unexpected return value %d.\n", ret);
715     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
716     ok(ret == 1, "Unexpected anchor index %d.\n", ret);
717     ret = SendMessageA(list, LB_GETCARETINDEX, 0, 0);
718     ok(ret == 1, "Unexpected caret index %d.\n", ret);
719 
720     ret = SendMessageA(list, LB_SETSEL, FALSE, 1);
721     ok(ret == 0, "Unexpected return value %d.\n", ret);
722     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
723     ok(ret == 1, "Unexpected anchor index %d.\n", ret);
724     ret = SendMessageA(list, LB_GETCARETINDEX, 0, 0);
725     ok(ret == 1, "Unexpected caret index %d.\n", ret);
726 
727     DestroyWindow(list);
728 
729     /* LBS_MULTIPLESEL */
730     list = create_listbox(LBS_MULTIPLESEL, 0);
731     ok(list != NULL, "Failed to create ListBox window.\n");
732 
733     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
734     ok(ret == -1, "Unexpected anchor index %d.\n", ret);
735 
736     ret = SendMessageA(list, LB_SETSEL, TRUE, 0);
737     ok(ret == 0, "Unexpected return value %d.\n", ret);
738     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
739     ok(ret == 0, "Unexpected anchor index %d.\n", ret);
740     ret = SendMessageA(list, LB_GETCARETINDEX, 0, 0);
741     ok(ret == 0, "Unexpected caret index %d.\n", ret);
742 
743     ret = SendMessageA(list, LB_SETSEL, TRUE, 1);
744     ok(ret == 0, "Unexpected return value %d.\n", ret);
745     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
746     ok(ret == 1, "Unexpected anchor index %d.\n", ret);
747     ret = SendMessageA(list, LB_GETCARETINDEX, 0, 0);
748     ok(ret == 1, "Unexpected caret index %d.\n", ret);
749 
750     ret = SendMessageA(list, LB_SETSEL, FALSE, 1);
751     ok(ret == 0, "Unexpected return value %d.\n", ret);
752     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
753     ok(ret == 1, "Unexpected anchor index %d.\n", ret);
754     ret = SendMessageA(list, LB_GETCARETINDEX, 0, 0);
755     ok(ret == 1, "Unexpected caret index %d.\n", ret);
756 
757     DestroyWindow(list);
758 }
759 
760 static void test_listbox_height(void)
761 {
762     HWND hList;
763     int r, id;
764 
765     hList = CreateWindowA( WC_LISTBOXA, "list test", 0,
766                           1, 1, 600, 100, NULL, NULL, NULL, NULL );
767     ok( hList != NULL, "failed to create listbox\n");
768 
769     id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi");
770     ok( id == 0, "item id wrong\n");
771 
772     r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 20, 0 ));
773     ok( r == 0, "send message failed\n");
774 
775     r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
776     ok( r == 20, "height wrong\n");
777 
778     r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 0, 30 ));
779     ok( r == -1, "send message failed\n");
780 
781     r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
782     ok( r == 20, "height wrong\n");
783 
784     /* Before Windows 10 1709 (or 1703?) the item height was limited to 255.
785      * Since then, with comctl32 V6 the limit is 65535.
786      */
787     r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 256, 0 ));
788     ok(r == 0 || broken(r == -1), "Failed to set item height, %d.\n", r);
789     if (r == -1)
790     {
791         r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
792         ok( r == 20, "Unexpected item height %d.\n", r);
793     }
794     else
795     {
796         r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
797         ok( r == 256, "Unexpected item height %d.\n", r);
798 
799         r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 65535, 0 ));
800         ok(r == 0, "Failed to set item height, %d.\n", r);
801 
802         r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
803         ok( r == 65535, "Unexpected item height %d.\n", r);
804     }
805 
806     r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 0xff, 0 ));
807     ok( r == 0, "send message failed\n");
808 
809     r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
810     ok( r == 0xff, "height wrong\n");
811 
812     DestroyWindow( hList );
813 }
814 
815 static void test_changing_selection_styles(void)
816 {
817     static const DWORD styles[] =
818     {
819         0,
820         LBS_NODATA | LBS_OWNERDRAWFIXED
821     };
822     static const DWORD selstyles[] =
823     {
824         0,
825         LBS_MULTIPLESEL,
826         LBS_EXTENDEDSEL,
827         LBS_MULTIPLESEL | LBS_EXTENDEDSEL
828     };
829     static const LONG selexpect_single[]  = { 0, 0, 1 };
830     static const LONG selexpect_single2[] = { 1, 0, 0 };
831     static const LONG selexpect_multi[]   = { 1, 0, 1 };
832     static const LONG selexpect_multi2[]  = { 1, 1, 0 };
833 
834     HWND parent, listbox;
835     DWORD style;
836     LONG ret;
837     UINT i, j, k;
838 
839     parent = create_parent();
840     ok(parent != NULL, "Failed to create parent window.\n");
841     for (i = 0; i < ARRAY_SIZE(styles); i++)
842     {
843         /* Test if changing selection styles affects selection storage */
844         for (j = 0; j < ARRAY_SIZE(selstyles); j++)
845         {
846             LONG setcursel_expect, selitemrange_expect, getselcount_expect;
847             const LONG *selexpect;
848 
849             listbox = CreateWindowA(WC_LISTBOXA, "TestList", styles[i] | selstyles[j] | WS_CHILD | WS_VISIBLE,
850                                     0, 0, 100, 100, parent, (HMENU)ID_LISTBOX, NULL, 0);
851             ok(listbox != NULL, "%u: Failed to create ListBox window.\n", j);
852 
853             if (selstyles[j] & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL))
854             {
855                 setcursel_expect = LB_ERR;
856                 selitemrange_expect = LB_OKAY;
857                 getselcount_expect = 2;
858                 selexpect = selexpect_multi;
859             }
860             else
861             {
862                 setcursel_expect = 2;
863                 selitemrange_expect = LB_ERR;
864                 getselcount_expect = LB_ERR;
865                 selexpect = selexpect_single;
866             }
867 
868             for (k = 0; k < ARRAY_SIZE(selexpect_multi); k++)
869             {
870                 ret = SendMessageA(listbox, LB_INSERTSTRING, -1, (LPARAM)"x");
871                 ok(ret == k, "%u: Unexpected return value %d, expected %d.\n", j, ret, k);
872             }
873             ret = SendMessageA(listbox, LB_GETCOUNT, 0, 0);
874             ok(ret == ARRAY_SIZE(selexpect_multi), "%u: Unexpected count %d.\n", j, ret);
875 
876             /* Select items with different methods */
877             ret = SendMessageA(listbox, LB_SETCURSEL, 2, 0);
878             ok(ret == setcursel_expect, "%u: Unexpected return value %d.\n", j, ret);
879             ret = SendMessageA(listbox, LB_SELITEMRANGE, TRUE, MAKELPARAM(0, 0));
880             ok(ret == selitemrange_expect, "%u: Unexpected return value %d.\n", j, ret);
881             ret = SendMessageA(listbox, LB_SELITEMRANGE, TRUE, MAKELPARAM(2, 2));
882             ok(ret == selitemrange_expect, "%u: Unexpected return value %d.\n", j, ret);
883 
884             /* Verify that the proper items are selected */
885             for (k = 0; k < ARRAY_SIZE(selexpect_multi); k++)
886             {
887                 ret = SendMessageA(listbox, LB_GETSEL, k, 0);
888                 ok(ret == selexpect[k], "%u: Unexpected selection state %d, expected %d.\n",
889                     j, ret, selexpect[k]);
890             }
891 
892             /* Now change the selection style */
893             style = GetWindowLongA(listbox, GWL_STYLE);
894             ok((style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) == selstyles[j],
895                 "%u: unexpected window styles %#x.\n", j, style);
896             if (selstyles[j] & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL))
897                 style &= ~selstyles[j];
898             else
899                 style |= LBS_MULTIPLESEL | LBS_EXTENDEDSEL;
900             SetWindowLongA(listbox, GWL_STYLE, style);
901             style = GetWindowLongA(listbox, GWL_STYLE);
902             ok(!(style & selstyles[j]), "%u: unexpected window styles %#x.\n", j, style);
903 
904             /* Verify that the same items are selected */
905             ret = SendMessageA(listbox, LB_GETSELCOUNT, 0, 0);
906             ok(ret == getselcount_expect, "%u: expected %d from LB_GETSELCOUNT, got %d\n",
907                 j, getselcount_expect, ret);
908 
909             for (k = 0; k < ARRAY_SIZE(selexpect_multi); k++)
910             {
911                 ret = SendMessageA(listbox, LB_GETSEL, k, 0);
912                 ok(ret == selexpect[k], "%u: Unexpected selection state %d, expected %d.\n",
913                     j, ret, selexpect[k]);
914             }
915 
916             /* Lastly see if we can still change the selection as before with old style */
917             if (setcursel_expect != LB_ERR) setcursel_expect = 0;
918             ret = SendMessageA(listbox, LB_SETCURSEL, 0, 0);
919             ok(ret == setcursel_expect, "%u: Unexpected return value %d.\n", j, ret);
920             ret = SendMessageA(listbox, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 1));
921             ok(ret == selitemrange_expect, "%u: Unexpected return value %d.\n", j, ret);
922             ret = SendMessageA(listbox, LB_SELITEMRANGE, FALSE, MAKELPARAM(2, 2));
923             ok(ret == selitemrange_expect, "%u: Unexpected return value %d.\n", j, ret);
924 
925             /* And verify the selections */
926             selexpect = (selstyles[j] & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) ? selexpect_multi2 : selexpect_single2;
927             ret = SendMessageA(listbox, LB_GETSELCOUNT, 0, 0);
928             ok(ret == getselcount_expect, "%u: expected %d from LB_GETSELCOUNT, got %d\n",
929                 j, getselcount_expect, ret);
930 
931             for (k = 0; k < ARRAY_SIZE(selexpect_multi); k++)
932             {
933                 ret = SendMessageA(listbox, LB_GETSEL, k, 0);
934                 ok(ret == selexpect[k], "%u: Unexpected selection state %d, expected %d.\n",
935                     j, ret, selexpect[k]);
936             }
937 
938             DestroyWindow(listbox);
939         }
940     }
941     DestroyWindow(parent);
942 }
943 
944 static void test_itemfrompoint(void)
945 {
946     /* WS_POPUP is required in order to have a more accurate size calculation (
947        without caption). LBS_NOINTEGRALHEIGHT is required in order to test
948        behavior of partially-displayed item.
949      */
950     HWND hList = CreateWindowA( WC_LISTBOXA, "list test",
951                                WS_VISIBLE|WS_POPUP|LBS_NOINTEGRALHEIGHT,
952                                1, 1, 600, 100, NULL, NULL, NULL, NULL );
953     ULONG r, id;
954     RECT rc;
955 
956     r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( /* x */ 30, /* y */ 30 ));
957     ok( r == MAKELPARAM(0xffff, 1), "Unexpected ret value %#x.\n", r );
958 
959     r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( 700, 30 ));
960     ok( r == MAKELPARAM(0xffff, 1), "Unexpected ret value %#x.\n", r );
961 
962     r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( 30, 300 ));
963     ok( r == MAKELPARAM(0xffff, 1), "Unexpected ret value %#x.\n", r );
964 
965     id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi");
966     ok( id == 0, "item id wrong\n");
967     id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi1");
968     ok( id == 1, "item id wrong\n");
969 
970     r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( /* x */ 30, /* y */ 30 ));
971     ok( r == 0x1, "ret %x\n", r );
972 
973     r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( /* x */ 30, /* y */ 601 ));
974     ok( r == MAKELPARAM(1, 1), "Unexpected ret value %#x.\n", r );
975 
976     /* Resize control so that below assertions about sizes are valid */
977     r = SendMessageA( hList, LB_GETITEMRECT, 0, (LPARAM)&rc);
978     ok( r == 1, "ret %x\n", r);
979     r = MoveWindow(hList, 1, 1, 600, (rc.bottom - rc.top + 1) * 9 / 2, TRUE);
980     ok( r != 0, "ret %x\n", r);
981 
982     id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi2");
983     ok( id == 2, "item id wrong\n");
984     id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi3");
985     ok( id == 3, "item id wrong\n");
986     id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi4");
987     ok( id == 4, "item id wrong\n");
988     id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi5");
989     ok( id == 5, "item id wrong\n");
990     id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi6");
991     ok( id == 6, "item id wrong\n");
992     id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi7");
993     ok( id == 7, "item id wrong\n");
994 
995     /* Set the listbox up so that id 1 is at the top, this leaves 5
996        partially visible at the bottom and 6, 7 are invisible */
997 
998     SendMessageA( hList, LB_SETTOPINDEX, 1, 0);
999     r = SendMessageA( hList, LB_GETTOPINDEX, 0, 0);
1000     ok( r == 1, "top %d\n", r);
1001 
1002     r = SendMessageA( hList, LB_GETITEMRECT, 5, (LPARAM)&rc);
1003     ok( r == 1, "ret %x\n", r);
1004     r = SendMessageA( hList, LB_GETITEMRECT, 6, (LPARAM)&rc);
1005     ok( r == 0, "ret %x\n", r);
1006 
1007     r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(/* x */ 10, /* y */ 10) );
1008     ok( r == 1, "ret %x\n", r);
1009 
1010     r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(1000, 10) );
1011     ok( r == 0x10001, "ret %x\n", r );
1012 
1013     r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(10, -10) );
1014     ok( r == 0x10001, "ret %x\n", r );
1015 
1016     r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(10, 100) );
1017     ok( r == 0x10005, "item %x\n", r );
1018 
1019     r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(10, 200) );
1020     ok( r == 0x10005, "item %x\n", r );
1021 
1022     DestroyWindow( hList );
1023 }
1024 
1025 static void test_listbox_item_data(void)
1026 {
1027     HWND hList;
1028     int r, id;
1029 
1030     hList = CreateWindowA( WC_LISTBOXA, "list test", 0,
1031                           1, 1, 600, 100, NULL, NULL, NULL, NULL );
1032     ok( hList != NULL, "failed to create listbox\n");
1033 
1034     id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi");
1035     ok( id == 0, "item id wrong\n");
1036 
1037     r = SendMessageA( hList, LB_SETITEMDATA, 0, MAKELPARAM( 20, 0 ));
1038     ok(r == TRUE, "LB_SETITEMDATA returned %d instead of TRUE\n", r);
1039 
1040     r = SendMessageA( hList, LB_GETITEMDATA, 0, 0);
1041     ok( r == 20, "get item data failed\n");
1042 
1043     DestroyWindow( hList );
1044 }
1045 
1046 static void test_listbox_LB_DIR(void)
1047 {
1048     char path[MAX_PATH], curdir[MAX_PATH];
1049     HWND hList;
1050     int res, itemCount;
1051     int itemCount_justFiles;
1052     int itemCount_justDrives;
1053     int itemCount_allFiles;
1054     int itemCount_allDirs;
1055     int i;
1056     char pathBuffer[MAX_PATH];
1057     char * p;
1058     char driveletter;
1059     const char *wildcard = "*";
1060     HANDLE file;
1061     BOOL ret;
1062 
1063     GetCurrentDirectoryA(ARRAY_SIZE(curdir), curdir);
1064 
1065     GetTempPathA(ARRAY_SIZE(path), path);
1066     ret = SetCurrentDirectoryA(path);
1067     ok(ret, "Failed to set current directory.\n");
1068 
1069     ret = CreateDirectoryA("lb_dir_test", NULL);
1070     ok(ret, "Failed to create test directory.\n");
1071 
1072     file = CreateFileA( "wtest1.tmp.c", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
1073     ok(file != INVALID_HANDLE_VALUE, "Error creating the test file: %d\n", GetLastError());
1074     CloseHandle( file );
1075 
1076     /* NOTE: for this test to succeed, there must be no subdirectories
1077        under the current directory. In addition, there must be at least
1078        one file that fits the wildcard w*.c . Normally, the test
1079        directory itself satisfies both conditions.
1080      */
1081     hList = CreateWindowA( WC_LISTBOXA, "list test", WS_VISIBLE|WS_POPUP,
1082                           1, 1, 600, 100, NULL, NULL, NULL, NULL );
1083     ok(hList != NULL, "Failed to create listbox window.\n");
1084 
1085     /* Test for standard usage */
1086 
1087     /* This should list all the files in the test directory. */
1088     strcpy(pathBuffer, wildcard);
1089     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1090     res = SendMessageA(hList, LB_DIR, 0, (LPARAM)pathBuffer);
1091     if (res == -1)  /* "*" wildcard doesn't work on win9x */
1092     {
1093         wildcard = "*.*";
1094         strcpy(pathBuffer, wildcard);
1095         res = SendMessageA(hList, LB_DIR, 0, (LPARAM)pathBuffer);
1096     }
1097     ok (res >= 0, "SendMessage(LB_DIR, 0, *) failed - 0x%08x\n", GetLastError());
1098 
1099     /* There should be some content in the listbox */
1100     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1101     ok (itemCount > 0, "SendMessage(LB_DIR) did NOT fill the listbox!\n");
1102     itemCount_allFiles = itemCount;
1103     ok(res + 1 == itemCount,
1104         "SendMessage(LB_DIR, 0, *) returned incorrect index (expected %d got %d)!\n",
1105         itemCount - 1, res);
1106 
1107     /* This tests behavior when no files match the wildcard */
1108     strcpy(pathBuffer, BAD_EXTENSION);
1109     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1110     res = SendMessageA(hList, LB_DIR, 0, (LPARAM)pathBuffer);
1111     ok (res == -1, "SendMessage(LB_DIR, 0, %s) returned %d, expected -1\n", BAD_EXTENSION, res);
1112 
1113     /* There should be NO content in the listbox */
1114     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1115     ok (itemCount == 0, "SendMessage(LB_DIR) DID fill the listbox!\n");
1116 
1117 
1118     /* This should list all the w*.c files in the test directory
1119      * As of this writing, this includes win.c, winstation.c, wsprintf.c
1120      */
1121     strcpy(pathBuffer, "w*.c");
1122     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1123     res = SendMessageA(hList, LB_DIR, 0, (LPARAM)pathBuffer);
1124     ok (res >= 0, "SendMessage(LB_DIR, 0, w*.c) failed - 0x%08x\n", GetLastError());
1125 
1126     /* Path specification does NOT converted to uppercase */
1127     ok (!strcmp(pathBuffer, "w*.c"),
1128         "expected no change to pathBuffer, got %s\n", pathBuffer);
1129 
1130     /* There should be some content in the listbox */
1131     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1132     ok (itemCount > 0, "SendMessage(LB_DIR) did NOT fill the listbox!\n");
1133     itemCount_justFiles = itemCount;
1134     ok(res + 1 == itemCount,
1135         "SendMessage(LB_DIR, 0, w*.c) returned incorrect index (expected %d got %d)!\n",
1136         itemCount - 1, res);
1137 
1138     /* Every single item in the control should start with a w and end in .c */
1139     for (i = 0; i < itemCount; i++)
1140     {
1141         memset(pathBuffer, 0, MAX_PATH);
1142         SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
1143         p = pathBuffer + strlen(pathBuffer);
1144         ok(((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
1145             (*(p-1) == 'c' || *(p-1) == 'C') &&
1146             (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
1147     }
1148 
1149     /* Test DDL_DIRECTORY */
1150     strcpy(pathBuffer, wildcard);
1151     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1152     res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY, (LPARAM)pathBuffer);
1153     ok (res > 0, "SendMessage(LB_DIR, DDL_DIRECTORY, *) failed - 0x%08x\n", GetLastError());
1154 
1155     /* There should be some content in the listbox.
1156      * All files plus "[..]"
1157      */
1158     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1159     itemCount_allDirs = itemCount - itemCount_allFiles;
1160     ok (itemCount >= itemCount_allFiles,
1161         "SendMessage(LB_DIR, DDL_DIRECTORY, *) filled with %d entries, expected > %d\n",
1162         itemCount, itemCount_allFiles);
1163     ok(res + 1 == itemCount,
1164         "SendMessage(LB_DIR, DDL_DIRECTORY, *) returned incorrect index (expected %d got %d)!\n",
1165         itemCount - 1, res);
1166 
1167     /* This tests behavior when no files match the wildcard */
1168     strcpy(pathBuffer, BAD_EXTENSION);
1169     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1170     res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY, (LPARAM)pathBuffer);
1171     ok (res == -1, "SendMessage(LB_DIR, DDL_DIRECTORY, %s) returned %d, expected -1\n", BAD_EXTENSION, res);
1172 
1173     /* There should be NO content in the listbox */
1174     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1175     ok (itemCount == 0, "SendMessage(LB_DIR) DID fill the listbox!\n");
1176 
1177     /* Test DDL_DIRECTORY */
1178     strcpy(pathBuffer, "w*.c");
1179     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1180     res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY, (LPARAM)pathBuffer);
1181     ok (res >= 0, "SendMessage(LB_DIR, DDL_DIRECTORY, w*.c) failed - 0x%08x\n", GetLastError());
1182 
1183     /* There should be some content in the listbox. Since the parent directory does not
1184      * fit w*.c, there should be exactly the same number of items as without DDL_DIRECTORY
1185      */
1186     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1187     ok (itemCount == itemCount_justFiles,
1188         "SendMessage(LB_DIR, DDL_DIRECTORY, w*.c) filled with %d entries, expected %d\n",
1189         itemCount, itemCount_justFiles);
1190     ok(res + 1 == itemCount,
1191         "SendMessage(LB_DIR, DDL_DIRECTORY, w*.c) returned incorrect index (expected %d got %d)!\n",
1192         itemCount - 1, res);
1193 
1194     /* Every single item in the control should start with a w and end in .c. */
1195     for (i = 0; i < itemCount; i++)
1196     {
1197         memset(pathBuffer, 0, MAX_PATH);
1198         SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
1199         p = pathBuffer + strlen(pathBuffer);
1200         ok(
1201             ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
1202             (*(p-1) == 'c' || *(p-1) == 'C') &&
1203             (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
1204     }
1205 
1206     /* Test DDL_DRIVES|DDL_EXCLUSIVE */
1207     strcpy(pathBuffer, wildcard);
1208     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1209     res = SendMessageA(hList, LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
1210     ok (res >= 0, "SendMessage(LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, *)  failed - 0x%08x\n", GetLastError());
1211 
1212     /* There should be some content in the listbox. In particular, there should
1213      * be at least one element before, since the string "[-c-]" should
1214      * have been added. Depending on the user setting, more drives might have
1215      * been added.
1216      */
1217     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1218     ok (itemCount >= 1,
1219         "SendMessage(LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, *) filled with %d entries, expected at least %d\n",
1220         itemCount, 1);
1221     itemCount_justDrives = itemCount;
1222     ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, *) returned incorrect index!\n");
1223 
1224     /* Every single item in the control should fit the format [-c-] */
1225     for (i = 0; i < itemCount; i++)
1226     {
1227         memset(pathBuffer, 0, MAX_PATH);
1228         driveletter = '\0';
1229         SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
1230         ok( strlen(pathBuffer) == 5, "Length of drive string is not 5\n" );
1231         ok( sscanf(pathBuffer, "[-%c-]", &driveletter) == 1, "Element %d (%s) does not fit [-X-]\n", i, pathBuffer);
1232         ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
1233         if (!(driveletter >= 'a' && driveletter <= 'z'))
1234         {
1235             /* Correct after invalid entry is found */
1236             itemCount_justDrives--;
1237         }
1238     }
1239 
1240     /* This tests behavior when no files match the wildcard */
1241     strcpy(pathBuffer, BAD_EXTENSION);
1242     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1243     res = SendMessageA(hList, LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
1244     ok (res == itemCount_justDrives -1, "SendMessage(LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, %s) returned %d, expected %d\n",
1245         BAD_EXTENSION, res, itemCount_justDrives -1);
1246 
1247     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1248     ok (itemCount == itemCount_justDrives, "SendMessage(LB_DIR) returned %d expected %d\n",
1249         itemCount, itemCount_justDrives);
1250 
1251     /* Test DDL_DRIVES. */
1252     strcpy(pathBuffer, wildcard);
1253     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1254     res = SendMessageA(hList, LB_DIR, DDL_DRIVES, (LPARAM)pathBuffer);
1255     ok (res > 0, "SendMessage(LB_DIR, DDL_DRIVES, *)  failed - 0x%08x\n", GetLastError());
1256 
1257     /* There should be some content in the listbox. In particular, there should
1258      * be at least one element before, since the string "[-c-]" should
1259      * have been added. Depending on the user setting, more drives might have
1260      * been added.
1261      */
1262     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1263     ok (itemCount == itemCount_justDrives + itemCount_allFiles,
1264         "SendMessage(LB_DIR, DDL_DRIVES, *) filled with %d entries, expected %d\n",
1265         itemCount, itemCount_justDrives + itemCount_allFiles);
1266     ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DRIVES, *) returned incorrect index!\n");
1267 
1268     /* This tests behavior when no files match the wildcard */
1269     strcpy(pathBuffer, BAD_EXTENSION);
1270     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1271     res = SendMessageA(hList, LB_DIR, DDL_DRIVES, (LPARAM)pathBuffer);
1272     ok (res == itemCount_justDrives -1, "SendMessage(LB_DIR, DDL_DRIVES, %s) returned %d, expected %d\n",
1273         BAD_EXTENSION, res, itemCount_justDrives -1);
1274 
1275     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1276     ok (itemCount == res + 1, "SendMessage(LB_DIR) returned %d expected %d\n", itemCount, res + 1);
1277 
1278     /* Test DDL_DRIVES. */
1279     strcpy(pathBuffer, "w*.c");
1280     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1281     res = SendMessageA(hList, LB_DIR, DDL_DRIVES, (LPARAM)pathBuffer);
1282     ok (res > 0, "SendMessage(LB_DIR, DDL_DRIVES, w*.c)  failed - 0x%08x\n", GetLastError());
1283 
1284     /* There should be some content in the listbox. In particular, there should
1285      * be at least one element before, since the string "[-c-]" should
1286      * have been added. Depending on the user setting, more drives might have
1287      * been added.
1288      */
1289     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1290     ok (itemCount == itemCount_justDrives + itemCount_justFiles,
1291         "SendMessage(LB_DIR, DDL_DRIVES, w*.c) filled with %d entries, expected %d\n",
1292         itemCount, itemCount_justDrives + itemCount_justFiles);
1293     ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DRIVES, w*.c) returned incorrect index!\n");
1294 
1295     /* Every single item in the control should fit the format [-c-], or w*.c */
1296     for (i = 0; i < itemCount; i++)
1297     {
1298         memset(pathBuffer, 0, MAX_PATH);
1299         driveletter = '\0';
1300         SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
1301         p = pathBuffer + strlen(pathBuffer);
1302         if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1)
1303         {
1304             ok( strlen(pathBuffer) == 5, "Length of drive string is not 5\n" );
1305             ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
1306         }
1307         else
1308         {
1309             ok(
1310                 ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
1311                 (*(p-1) == 'c' || *(p-1) == 'C') &&
1312                 (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
1313         }
1314     }
1315 
1316     /* Test DDL_DIRECTORY|DDL_DRIVES. This does *not* imply DDL_EXCLUSIVE */
1317     strcpy(pathBuffer, wildcard);
1318     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1319     res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES, (LPARAM)pathBuffer);
1320     ok (res > 0, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, *) failed - 0x%08x\n", GetLastError());
1321 
1322     /* There should be some content in the listbox. In particular, there should
1323      * be exactly the number of plain files, plus the number of mapped drives.
1324      */
1325     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1326     ok (itemCount == itemCount_allFiles + itemCount_justDrives + itemCount_allDirs,
1327         "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES) filled with %d entries, expected %d\n",
1328         itemCount, itemCount_allFiles + itemCount_justDrives + itemCount_allDirs);
1329     ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, w*.c) returned incorrect index!\n");
1330 
1331     /* Every single item in the control should start with a w and end in .c,
1332      * except for the "[..]" string, which should appear exactly as it is,
1333      * and the mapped drives in the format "[-X-]".
1334      */
1335     for (i = 0; i < itemCount; i++)
1336     {
1337         memset(pathBuffer, 0, MAX_PATH);
1338         driveletter = '\0';
1339         SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
1340         if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1)
1341             ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
1342     }
1343 
1344     /* This tests behavior when no files match the wildcard */
1345     strcpy(pathBuffer, BAD_EXTENSION);
1346     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1347     res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES, (LPARAM)pathBuffer);
1348     ok (res == itemCount_justDrives -1, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, %s) returned %d, expected %d\n",
1349         BAD_EXTENSION, res, itemCount_justDrives -1);
1350 
1351     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1352     ok (itemCount == res + 1, "SendMessage(LB_DIR) returned %d expected %d\n", itemCount, res + 1);
1353 
1354     /* Test DDL_DIRECTORY|DDL_DRIVES. */
1355     strcpy(pathBuffer, "w*.c");
1356     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1357     res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES, (LPARAM)pathBuffer);
1358     ok (res > 0, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, w*.c) failed - 0x%08x\n", GetLastError());
1359 
1360     /* There should be some content in the listbox. In particular, there should
1361      * be exactly the number of plain files, plus the number of mapped drives.
1362      */
1363     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1364     ok (itemCount == itemCount_justFiles + itemCount_justDrives,
1365         "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES) filled with %d entries, expected %d\n",
1366         itemCount, itemCount_justFiles + itemCount_justDrives);
1367     ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, w*.c) returned incorrect index!\n");
1368 
1369     /* Every single item in the control should start with a w and end in .c,
1370      * except the mapped drives in the format "[-X-]". The "[..]" directory
1371      * should not appear.
1372      */
1373     for (i = 0; i < itemCount; i++)
1374     {
1375         memset(pathBuffer, 0, MAX_PATH);
1376         driveletter = '\0';
1377         SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
1378         p = pathBuffer + strlen(pathBuffer);
1379         if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1)
1380             ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
1381         else
1382             ok(
1383                 ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
1384                 (*(p-1) == 'c' || *(p-1) == 'C') &&
1385                 (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
1386     }
1387 
1388     /* Test DDL_DIRECTORY|DDL_EXCLUSIVE. */
1389     strcpy(pathBuffer, wildcard);
1390     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1391     res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
1392     ok (res != -1, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, *) failed err %u\n",
1393         GetLastError());
1394 
1395     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1396     ok (itemCount == itemCount_allDirs,
1397         "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE) filled with %d entries, expected %d\n",
1398         itemCount, itemCount_allDirs);
1399     ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, *) returned incorrect index!\n");
1400 
1401     if (itemCount)
1402     {
1403         memset(pathBuffer, 0, MAX_PATH);
1404         SendMessageA(hList, LB_GETTEXT, 0, (LPARAM)pathBuffer);
1405         ok( !strcmp(pathBuffer, "[..]"), "First element is %s, not [..]\n", pathBuffer);
1406     }
1407 
1408     /* This tests behavior when no files match the wildcard */
1409     strcpy(pathBuffer, BAD_EXTENSION);
1410     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1411     res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
1412     ok (res == -1, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, %s) returned %d, expected %d\n",
1413         BAD_EXTENSION, res, -1);
1414 
1415     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1416     ok (itemCount == res + 1, "SendMessage(LB_DIR) returned %d expected %d\n", itemCount, res + 1);
1417 
1418 
1419     /* Test DDL_DIRECTORY|DDL_EXCLUSIVE. */
1420     strcpy(pathBuffer, "w*.c");
1421     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1422     res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
1423     ok (res == LB_ERR, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, w*.c) returned %d expected %d\n", res, LB_ERR);
1424 
1425     /* There should be no elements, since "[..]" does not fit w*.c */
1426     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1427     ok (itemCount == 0,
1428         "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE) filled with %d entries, expected %d\n",
1429         itemCount, 0);
1430 
1431     /* Test DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE. */
1432     strcpy(pathBuffer, wildcard);
1433     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1434     res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
1435     ok (res > 0, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, w*.c,) failed - 0x%08x\n", GetLastError());
1436 
1437     /* There should be no plain files on the listbox */
1438     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1439     ok (itemCount == itemCount_justDrives + itemCount_allDirs,
1440         "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE) filled with %d entries, expected %d\n",
1441         itemCount, itemCount_justDrives + itemCount_allDirs);
1442     ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, w*.c) returned incorrect index!\n");
1443 
1444     for (i = 0; i < itemCount; i++)
1445     {
1446         memset(pathBuffer, 0, MAX_PATH);
1447         driveletter = '\0';
1448         SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
1449         if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1)
1450             ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
1451         else
1452             ok( pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']',
1453                 "Element %d (%s) does not fit expected [...]\n", i, pathBuffer);
1454     }
1455 
1456     /* This tests behavior when no files match the wildcard */
1457     strcpy(pathBuffer, BAD_EXTENSION);
1458     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1459     res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
1460     ok (res == itemCount_justDrives -1, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, %s) returned %d, expected %d\n",
1461         BAD_EXTENSION, res, itemCount_justDrives -1);
1462 
1463     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1464     ok (itemCount == res + 1, "SendMessage(LB_DIR) returned %d expected %d\n", itemCount, res + 1);
1465 
1466     /* Test DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE. */
1467     strcpy(pathBuffer, "w*.c");
1468     SendMessageA(hList, LB_RESETCONTENT, 0, 0);
1469     res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
1470     ok (res >= 0, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, w*.c,) failed - 0x%08x\n", GetLastError());
1471 
1472     /* There should be no plain files on the listbox, and no [..], since it does not fit w*.c */
1473     itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
1474     ok (itemCount == itemCount_justDrives,
1475         "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE) filled with %d entries, expected %d\n",
1476         itemCount, itemCount_justDrives);
1477     ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, w*.c) returned incorrect index!\n");
1478 
1479     for (i = 0; i < itemCount; i++)
1480     {
1481         memset(pathBuffer, 0, MAX_PATH);
1482         driveletter = '\0';
1483         SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
1484         ok (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1, "Element %d (%s) does not fit [-X-]\n", i, pathBuffer);
1485         ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
1486     }
1487     DestroyWindow(hList);
1488 
1489     DeleteFileA( "wtest1.tmp.c" );
1490     RemoveDirectoryA("lb_dir_test");
1491 
1492     SetCurrentDirectoryA(curdir);
1493 }
1494 
1495 static HWND g_listBox;
1496 static HWND g_label;
1497 
1498 #define ID_TEST_LABEL    1001
1499 #define ID_TEST_LISTBOX  1002
1500 
1501 static BOOL on_listbox_container_create(HWND hwnd, CREATESTRUCTA *lpcs)
1502 {
1503     g_label = CreateWindowA(WC_STATICA, "Contents of static control before DlgDirList.",
1504         WS_CHILD | WS_VISIBLE, 10, 10, 512, 32, hwnd, (HMENU)ID_TEST_LABEL, NULL, 0);
1505     if (!g_label) return FALSE;
1506 
1507     g_listBox = CreateWindowA(WC_LISTBOXA, "DlgDirList test",
1508         WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_VSCROLL, 10, 60, 256, 256,
1509         hwnd, (HMENU)ID_TEST_LISTBOX, NULL, 0);
1510     if (!g_listBox) return FALSE;
1511 
1512     return TRUE;
1513 }
1514 
1515 static LRESULT CALLBACK listbox_container_window_procA(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
1516 {
1517     LRESULT result = 0;
1518 
1519     switch (uiMsg)
1520     {
1521     case WM_DESTROY:
1522         PostQuitMessage(0);
1523         break;
1524     case WM_CREATE:
1525         result = on_listbox_container_create(hwnd, (CREATESTRUCTA *)lParam) ? 0 : (LRESULT)-1;
1526         break;
1527     default:
1528         result = DefWindowProcA(hwnd, uiMsg, wParam, lParam);
1529         break;
1530     }
1531     return result;
1532 }
1533 
1534 static BOOL RegisterListboxWindowClass(HINSTANCE hInst)
1535 {
1536     WNDCLASSA cls;
1537 
1538     cls.style = 0;
1539     cls.cbClsExtra = 0;
1540     cls.cbWndExtra = 0;
1541     cls.hInstance = hInst;
1542     cls.hIcon = NULL;
1543     cls.hCursor = LoadCursorA (NULL, (LPCSTR)IDC_ARROW);
1544     cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
1545     cls.lpszMenuName = NULL;
1546     cls.lpfnWndProc = listbox_container_window_procA;
1547     cls.lpszClassName = "ListboxContainerClass";
1548     if (!RegisterClassA (&cls)) return FALSE;
1549 
1550     return TRUE;
1551 }
1552 
1553 static void test_listbox_dlgdir(void)
1554 {
1555     HINSTANCE hInst;
1556     HWND hWnd;
1557     int res, itemCount;
1558     int itemCount_allDirs;
1559     int itemCount_justFiles;
1560     int itemCount_justDrives;
1561     int i;
1562     char pathBuffer[MAX_PATH];
1563     char itemBuffer[MAX_PATH];
1564     char tempBuffer[MAX_PATH];
1565     char * p;
1566     char driveletter;
1567     HANDLE file;
1568     BOOL ret;
1569 
1570     file = CreateFileA( "wtest1.tmp.c", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
1571     ok(file != INVALID_HANDLE_VALUE, "Error creating the test file: %d\n", GetLastError());
1572     CloseHandle( file );
1573 
1574     /* NOTE: for this test to succeed, there must be no subdirectories
1575        under the current directory. In addition, there must be at least
1576        one file that fits the wildcard w*.c . Normally, the test
1577        directory itself satisfies both conditions.
1578      */
1579 
1580     hInst = GetModuleHandleA(0);
1581     ret = RegisterListboxWindowClass(hInst);
1582     ok(ret, "Failed to register test class.\n");
1583 
1584     hWnd = CreateWindowA("ListboxContainerClass", "ListboxContainerClass",
1585                     WS_OVERLAPPEDWINDOW | WS_VISIBLE,
1586             CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
1587             NULL, NULL, hInst, 0);
1588     ok(hWnd != NULL, "Failed to create container window.\n");
1589 
1590     /* Test for standard usage */
1591 
1592     /* The following should be overwritten by the directory path */
1593     SendMessageA(g_label, WM_SETTEXT, 0, (LPARAM)"default contents");
1594 
1595     /* This should list all the w*.c files in the test directory
1596      * As of this writing, this includes win.c, winstation.c, wsprintf.c
1597      */
1598     strcpy(pathBuffer, "w*.c");
1599     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, 0);
1600     ok (res == 1, "DlgDirList(*.c, 0) returned %d - expected 1 - 0x%08x\n", res, GetLastError());
1601 
1602     /* Path specification gets converted to uppercase */
1603     ok (!strcmp(pathBuffer, "W*.C"),
1604         "expected conversion to uppercase, got %s\n", pathBuffer);
1605 
1606     /* Loaded path should have overwritten the label text */
1607     SendMessageA(g_label, WM_GETTEXT, MAX_PATH, (LPARAM)pathBuffer);
1608     ok (strcmp("default contents", pathBuffer), "DlgDirList() did not modify static control!\n");
1609 
1610     /* There should be some content in the listbox */
1611     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1612     ok (itemCount > 0, "DlgDirList() did NOT fill the listbox!\n");
1613     itemCount_justFiles = itemCount;
1614 
1615     /* Every single item in the control should start with a w and end in .c */
1616     for (i = 0; i < itemCount; i++)
1617     {
1618         memset(pathBuffer, 0, MAX_PATH);
1619         SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer);
1620         p = pathBuffer + strlen(pathBuffer);
1621         ok(((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
1622             (*(p-1) == 'c' || *(p-1) == 'C') &&
1623             (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
1624     }
1625 
1626     /* Test behavior when no files match the wildcard */
1627     strcpy(pathBuffer, BAD_EXTENSION);
1628     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, 0);
1629     ok (res == 1, "DlgDirList(%s, 0) returned %d expected 1\n", BAD_EXTENSION, res);
1630 
1631     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1632     ok (itemCount == 0, "DlgDirList() DID fill the listbox!\n");
1633 
1634     /* Test DDL_DIRECTORY */
1635     strcpy(pathBuffer, "w*.c");
1636     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY);
1637     ok (res == 1, "DlgDirList(*.c, DDL_DIRECTORY) failed - 0x%08x\n", GetLastError());
1638 
1639     /* There should be some content in the listbox. In particular, there should
1640      * be exactly more elements than before, since the directories should
1641      * have been added.
1642      */
1643     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1644     itemCount_allDirs = itemCount - itemCount_justFiles;
1645     ok (itemCount >= itemCount_justFiles, "DlgDirList(DDL_DIRECTORY) filled with %d entries, expected > %d\n",
1646         itemCount, itemCount_justFiles);
1647 
1648     /* Every single item in the control should start with a w and end in .c,
1649      * except for the "[..]" string, which should appear exactly as it is.
1650      */
1651     for (i = 0; i < itemCount; i++)
1652     {
1653         memset(pathBuffer, 0, MAX_PATH);
1654         SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer);
1655         p = pathBuffer + strlen(pathBuffer);
1656         ok( (pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']') ||
1657             ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
1658             (*(p-1) == 'c' || *(p-1) == 'C') &&
1659             (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
1660     }
1661 
1662     /* Test behavior when no files match the wildcard */
1663     strcpy(pathBuffer, BAD_EXTENSION);
1664     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY);
1665     ok (res == 1, "DlgDirList(%s, DDL_DIRECTORY) returned %d expected 1\n", BAD_EXTENSION, res);
1666 
1667     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1668     ok (itemCount == itemCount_allDirs, "DlgDirList() incorrectly filled the listbox! (expected %d got %d)\n",
1669         itemCount_allDirs, itemCount);
1670     for (i = 0; i < itemCount; i++)
1671     {
1672         memset(pathBuffer, 0, MAX_PATH);
1673         SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer);
1674         ok( pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']',
1675             "Element %d (%s) does not fit requested [...]\n", i, pathBuffer);
1676     }
1677 
1678     /* Test DDL_DRIVES. At least on WinXP-SP2, this implies DDL_EXCLUSIVE */
1679     strcpy(pathBuffer, "w*.c");
1680     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DRIVES);
1681     ok (res == 1, "DlgDirList(*.c, DDL_DRIVES) failed - 0x%08x\n", GetLastError());
1682 
1683     /* There should be some content in the listbox. In particular, there should
1684      * be at least one element before, since the string "[-c-]" should
1685      * have been added. Depending on the user setting, more drives might have
1686      * been added.
1687      */
1688     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1689     ok (itemCount >= 1,
1690         "DlgDirList(DDL_DRIVES) filled with %d entries, expected at least %d\n",
1691         itemCount, 1);
1692     itemCount_justDrives = itemCount;
1693 
1694     /* Every single item in the control should fit the format [-c-] */
1695     for (i = 0; i < itemCount; i++)
1696     {
1697         memset(pathBuffer, 0, MAX_PATH);
1698         driveletter = '\0';
1699         SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer);
1700         ok( strlen(pathBuffer) == 5, "Length of drive string is not 5\n" );
1701         ok( sscanf(pathBuffer, "[-%c-]", &driveletter) == 1, "Element %d (%s) does not fit [-X-]\n", i, pathBuffer);
1702         ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
1703         if (!(driveletter >= 'a' && driveletter <= 'z')) {
1704             /* Correct after invalid entry is found */
1705             trace("removing count of invalid entry %s\n", pathBuffer);
1706             itemCount_justDrives--;
1707         }
1708     }
1709 
1710     /* Test behavior when no files match the wildcard */
1711     strcpy(pathBuffer, BAD_EXTENSION);
1712     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DRIVES);
1713     ok (res == 1, "DlgDirList(%s, DDL_DRIVES) returned %d expected 1\n", BAD_EXTENSION, res);
1714 
1715     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1716     ok (itemCount == itemCount_justDrives, "DlgDirList() incorrectly filled the listbox!\n");
1717 
1718     /* Test DDL_DIRECTORY|DDL_DRIVES. This does *not* imply DDL_EXCLUSIVE */
1719     strcpy(pathBuffer, "w*.c");
1720     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES);
1721     ok (res == 1, "DlgDirList(*.c, DDL_DIRECTORY|DDL_DRIVES) failed - 0x%08x\n", GetLastError());
1722 
1723     /* There should be some content in the listbox. In particular, there should
1724      * be exactly the number of plain files, plus the number of mapped drives,
1725      * plus one "[..]"
1726      */
1727     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1728     ok (itemCount == itemCount_justFiles + itemCount_justDrives + itemCount_allDirs,
1729         "DlgDirList(DDL_DIRECTORY|DDL_DRIVES) filled with %d entries, expected %d\n",
1730         itemCount, itemCount_justFiles + itemCount_justDrives + itemCount_allDirs);
1731 
1732     /* Every single item in the control should start with a w and end in .c,
1733      * except for the "[..]" string, which should appear exactly as it is,
1734      * and the mapped drives in the format "[-X-]".
1735      */
1736     for (i = 0; i < itemCount; i++)
1737     {
1738         memset(pathBuffer, 0, MAX_PATH);
1739         driveletter = '\0';
1740         SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer);
1741         p = pathBuffer + strlen(pathBuffer);
1742         if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1)
1743             ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
1744         else
1745             ok( (pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']') ||
1746                 ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
1747                 (*(p-1) == 'c' || *(p-1) == 'C') &&
1748                 (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
1749     }
1750 
1751     /* Test behavior when no files match the wildcard */
1752     strcpy(pathBuffer, BAD_EXTENSION);
1753     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES);
1754     ok (res == 1, "DlgDirList(%s, DDL_DIRECTORY|DDL_DRIVES) returned %d expected 1\n", BAD_EXTENSION, res);
1755 
1756     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1757     ok (itemCount == itemCount_justDrives + itemCount_allDirs,
1758         "DlgDirList() incorrectly filled the listbox! (expected %d got %d)\n",
1759         itemCount_justDrives + itemCount_allDirs, itemCount);
1760 
1761     /* Test DDL_DIRECTORY|DDL_EXCLUSIVE. */
1762     strcpy(pathBuffer, "w*.c");
1763     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_EXCLUSIVE);
1764     ok (res == 1, "DlgDirList(*.c, DDL_DIRECTORY|DDL_EXCLUSIVE) failed - 0x%08x\n", GetLastError());
1765 
1766     /* There should be exactly one element: "[..]" */
1767     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1768     ok (itemCount == itemCount_allDirs,
1769         "DlgDirList(DDL_DIRECTORY|DDL_EXCLUSIVE) filled with %d entries, expected %d\n",
1770         itemCount, itemCount_allDirs);
1771 
1772     if (itemCount && GetCurrentDirectoryA( MAX_PATH, pathBuffer ) > 3)  /* there's no [..] in drive root */
1773     {
1774         memset(pathBuffer, 0, MAX_PATH);
1775         SendMessageA(g_listBox, LB_GETTEXT, 0, (LPARAM)pathBuffer);
1776         ok( !strcmp(pathBuffer, "[..]"), "First (and only) element is not [..]\n");
1777     }
1778 
1779     /* Test behavior when no files match the wildcard */
1780     strcpy(pathBuffer, BAD_EXTENSION);
1781     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_EXCLUSIVE);
1782     ok (res == 1, "DlgDirList(%s, DDL_DIRECTORY|DDL_EXCLUSIVE) returned %d expected 1\n", BAD_EXTENSION, res);
1783 
1784     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1785     ok (itemCount == itemCount_allDirs, "DlgDirList() incorrectly filled the listbox!\n");
1786 
1787     /* Test DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE. */
1788     strcpy(pathBuffer, "w*.c");
1789     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE);
1790     ok (res == 1, "DlgDirList(*.c, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE) failed - 0x%08x\n", GetLastError());
1791 
1792     /* There should be no plain files on the listbox */
1793     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1794     ok (itemCount == itemCount_justDrives + itemCount_allDirs,
1795         "DlgDirList(DDL_DIRECTORY|DDL_EXCLUSIVE) filled with %d entries, expected %d\n",
1796         itemCount, itemCount_justDrives + itemCount_allDirs);
1797 
1798     for (i = 0; i < itemCount; i++)
1799     {
1800         memset(pathBuffer, 0, MAX_PATH);
1801         driveletter = '\0';
1802         SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer);
1803         if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1)
1804             ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
1805         else
1806             ok( pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']',
1807                 "Element %d (%s) does not fit expected [...]\n", i, pathBuffer);
1808     }
1809 
1810     /* Test behavior when no files match the wildcard */
1811     strcpy(pathBuffer, BAD_EXTENSION);
1812     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE);
1813     ok (res == 1, "DlgDirList(%s, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE) returned %d expected 1\n", BAD_EXTENSION, res);
1814 
1815     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1816     ok (itemCount == itemCount_justDrives + itemCount_allDirs, "DlgDirList() incorrectly filled the listbox!\n");
1817 
1818     /* Now test DlgDirSelectEx() in normal operation */
1819     /* Fill with everything - drives, directory and all plain files. */
1820     strcpy(pathBuffer, "*");
1821     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES);
1822     ok (res != 0, "DlgDirList(*, DDL_DIRECTORY|DDL_DRIVES) failed - 0x%08x\n", GetLastError());
1823 
1824     SendMessageA(g_listBox, LB_SETCURSEL, -1, 0); /* Unselect any current selection */
1825     memset(pathBuffer, 0, MAX_PATH);
1826     SetLastError(0xdeadbeef);
1827     res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
1828     ok (GetLastError() == 0xdeadbeef,
1829         "DlgDirSelectEx() with no selection modified last error code from 0xdeadbeef to 0x%08x\n",
1830         GetLastError());
1831     ok (res == 0, "DlgDirSelectEx() with no selection returned %d, expected 0\n", res);
1832     /* WinXP-SP2 leaves pathBuffer untouched, but Win98 fills it with garbage. */
1833     /*
1834     ok (!*pathBuffer, "DlgDirSelectEx() with no selection filled buffer with %s\n", pathBuffer);
1835     */
1836     /* Test proper drive/dir/file recognition */
1837     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1838     for (i = 0; i < itemCount; i++)
1839     {
1840         memset(itemBuffer, 0, MAX_PATH);
1841         memset(pathBuffer, 0, MAX_PATH);
1842         memset(tempBuffer, 0, MAX_PATH);
1843         driveletter = '\0';
1844         SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)itemBuffer);
1845         res = SendMessageA(g_listBox, LB_SETCURSEL, i, 0);
1846         ok (res == i, "SendMessageA(LB_SETCURSEL, %d) failed\n", i);
1847         if (sscanf(itemBuffer, "[-%c-]", &driveletter) == 1)
1848         {
1849             /* Current item is a drive letter */
1850             SetLastError(0xdeadbeef);
1851             res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
1852             ok (GetLastError() == 0xdeadbeef,
1853                "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n",
1854                 i, GetLastError());
1855             ok(res == 1, "DlgDirSelectEx() thinks %s (%s) is not a drive/directory!\n", itemBuffer, pathBuffer);
1856 
1857             /* For drive letters, DlgDirSelectEx tacks on a colon */
1858             ok (pathBuffer[0] == driveletter && pathBuffer[1] == ':' && pathBuffer[2] == '\0',
1859                 "%d: got \"%s\" expected \"%c:\"\n", i, pathBuffer, driveletter);
1860         }
1861         else if (itemBuffer[0] == '[')
1862         {
1863             /* Current item is the parent directory */
1864             SetLastError(0xdeadbeef);
1865             res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
1866             ok (GetLastError() == 0xdeadbeef,
1867                "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n",
1868                 i, GetLastError());
1869             ok(res == 1, "DlgDirSelectEx() thinks %s (%s) is not a drive/directory!\n", itemBuffer, pathBuffer);
1870 
1871             /* For directories, DlgDirSelectEx tacks on a backslash */
1872             p = pathBuffer + strlen(pathBuffer);
1873             ok (*(p-1) == '\\', "DlgDirSelectEx did NOT tack on a backslash to dir, got %s\n", pathBuffer);
1874 
1875             tempBuffer[0] = '[';
1876             lstrcpynA(tempBuffer + 1, pathBuffer, strlen(pathBuffer));
1877             strcat(tempBuffer, "]");
1878             ok (!strcmp(tempBuffer, itemBuffer), "Formatted directory should be %s, got %s\n", tempBuffer, itemBuffer);
1879         }
1880         else
1881         {
1882             /* Current item is a plain file */
1883             SetLastError(0xdeadbeef);
1884             res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
1885             ok (GetLastError() == 0xdeadbeef,
1886                "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n",
1887                 i, GetLastError());
1888             ok(res == 0, "DlgDirSelectEx() thinks %s (%s) is a drive/directory!\n", itemBuffer, pathBuffer);
1889 
1890             /* NOTE: WinXP tacks a period on all files that lack an extension. This affects
1891              * for example, "Makefile", which gets reported as "Makefile."
1892              */
1893             strcpy(tempBuffer, itemBuffer);
1894             if (strchr(tempBuffer, '.') == NULL) strcat(tempBuffer, ".");
1895             ok (!strcmp(pathBuffer, tempBuffer), "Formatted file should be %s, got %s\n", tempBuffer, pathBuffer);
1896         }
1897     }
1898 
1899     DeleteFileA( "wtest1.tmp.c" );
1900 
1901     /* Now test DlgDirSelectEx() in abnormal operation */
1902     /* Fill list with bogus entries, that look somewhat valid */
1903     SendMessageA(g_listBox, LB_RESETCONTENT, 0, 0);
1904     SendMessageA(g_listBox, LB_ADDSTRING, 0, (LPARAM)"[notexist.dir]");
1905     SendMessageA(g_listBox, LB_ADDSTRING, 0, (LPARAM)"notexist.fil");
1906     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
1907     for (i = 0; i < itemCount; i++)
1908     {
1909         memset(itemBuffer, 0, MAX_PATH);
1910         memset(pathBuffer, 0, MAX_PATH);
1911         memset(tempBuffer, 0, MAX_PATH);
1912         driveletter = '\0';
1913         SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)itemBuffer);
1914         res = SendMessageA(g_listBox, LB_SETCURSEL, i, 0);
1915         ok (res == i, "SendMessage(LB_SETCURSEL, %d) failed\n", i);
1916         if (sscanf(itemBuffer, "[-%c-]", &driveletter) == 1)
1917         {
1918             /* Current item is a drive letter */
1919             SetLastError(0xdeadbeef);
1920             res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
1921             ok (GetLastError() == 0xdeadbeef,
1922                "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n",
1923                 i, GetLastError());
1924             ok(res == 1, "DlgDirSelectEx() thinks %s (%s) is not a drive/directory!\n", itemBuffer, pathBuffer);
1925 
1926             /* For drive letters, DlgDirSelectEx tacks on a colon */
1927             ok (pathBuffer[0] == driveletter && pathBuffer[1] == ':' && pathBuffer[2] == '\0',
1928                 "%d: got \"%s\" expected \"%c:\"\n", i, pathBuffer, driveletter);
1929         }
1930         else if (itemBuffer[0] == '[')
1931         {
1932             /* Current item is the parent directory */
1933             SetLastError(0xdeadbeef);
1934             res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
1935             ok (GetLastError() == 0xdeadbeef,
1936                "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n",
1937                 i, GetLastError());
1938             ok(res == 1, "DlgDirSelectEx() thinks %s (%s) is not a drive/directory!\n", itemBuffer, pathBuffer);
1939 
1940             /* For directories, DlgDirSelectEx tacks on a backslash */
1941             p = pathBuffer + strlen(pathBuffer);
1942             ok (*(p-1) == '\\', "DlgDirSelectEx did NOT tack on a backslash to dir, got %s\n", pathBuffer);
1943 
1944             tempBuffer[0] = '[';
1945             lstrcpynA(tempBuffer + 1, pathBuffer, strlen(pathBuffer));
1946             strcat(tempBuffer, "]");
1947             ok (!strcmp(tempBuffer, itemBuffer), "Formatted directory should be %s, got %s\n", tempBuffer, itemBuffer);
1948         }
1949         else
1950         {
1951             /* Current item is a plain file */
1952             SetLastError(0xdeadbeef);
1953             res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
1954             ok (GetLastError() == 0xdeadbeef,
1955                "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n",
1956                 i, GetLastError());
1957             ok(res == 0, "DlgDirSelectEx() thinks %s (%s) is a drive/directory!\n", itemBuffer, pathBuffer);
1958 
1959             /* NOTE: WinXP and Win98 tack a period on all files that lack an extension.
1960              * This affects for example, "Makefile", which gets reported as "Makefile."
1961              */
1962             strcpy(tempBuffer, itemBuffer);
1963             if (strchr(tempBuffer, '.') == NULL) strcat(tempBuffer, ".");
1964             ok (!strcmp(pathBuffer, tempBuffer), "Formatted file should be %s, got %s\n", tempBuffer, pathBuffer);
1965         }
1966     }
1967 
1968     /* Test behavior when loading folders from root with and without wildcard */
1969     strcpy(pathBuffer, "C:\\");
1970     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE);
1971     ok(res, "DlgDirList failed to list C:\\ folders\n");
1972     ok(!strcmp(pathBuffer, "*"), "DlgDirList set the invalid path spec '%s', expected '*'\n", pathBuffer);
1973 
1974     strcpy(pathBuffer, "C:\\*");
1975     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE);
1976     ok(res, "DlgDirList failed to list C:\\* folders\n");
1977     ok(!strcmp(pathBuffer, "*"), "DlgDirList set the invalid path spec '%s', expected '*'\n", pathBuffer);
1978 
1979     /* Try loading files from an invalid folder */
1980     SetLastError(0xdeadbeef);
1981     strcpy(pathBuffer, "C:\\INVALID$$DIR");
1982     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE);
1983     ok(!res, "DlgDirList should have failed with 0 but %d was returned\n", res);
1984     ok(GetLastError() == ERROR_NO_WILDCARD_CHARACTERS,
1985        "GetLastError should return 0x589, got 0x%X\n",GetLastError());
1986 
1987     DestroyWindow(hWnd);
1988 }
1989 
1990 static void test_set_count( void )
1991 {
1992     static const DWORD styles[] =
1993     {
1994         LBS_OWNERDRAWFIXED,
1995         LBS_HASSTRINGS,
1996     };
1997     HWND parent, listbox;
1998     unsigned int i;
1999     LONG ret;
2000     RECT r;
2001 
2002     parent = create_parent();
2003     listbox = create_listbox( LBS_OWNERDRAWFIXED | LBS_NODATA | WS_CHILD | WS_VISIBLE, parent );
2004 
2005     UpdateWindow( listbox );
2006     GetUpdateRect( listbox, &r, TRUE );
2007     ok( IsRectEmpty( &r ), "got non-empty rect\n");
2008 
2009     ret = SendMessageA( listbox, LB_SETCOUNT, 100, 0 );
2010     ok( ret == 0, "got %d\n", ret );
2011     ret = SendMessageA( listbox, LB_GETCOUNT, 0, 0 );
2012     ok( ret == 100, "got %d\n", ret );
2013 
2014     GetUpdateRect( listbox, &r, TRUE );
2015     ok( !IsRectEmpty( &r ), "got empty rect\n");
2016 
2017     ValidateRect( listbox, NULL );
2018     GetUpdateRect( listbox, &r, TRUE );
2019     ok( IsRectEmpty( &r ), "got non-empty rect\n");
2020 
2021     ret = SendMessageA( listbox, LB_SETCOUNT, 99, 0 );
2022     ok( ret == 0, "got %d\n", ret );
2023 
2024     GetUpdateRect( listbox, &r, TRUE );
2025     ok( !IsRectEmpty( &r ), "got empty rect\n");
2026 
2027     ret = SendMessageA( listbox, LB_SETCOUNT, -5, 0 );
2028     ok( ret == 0, "got %d\n", ret );
2029     ret = SendMessageA( listbox, LB_GETCOUNT, 0, 0 );
2030     ok( ret == -5, "got %d\n", ret );
2031 
2032     DestroyWindow( listbox );
2033 
2034     for (i = 0; i < ARRAY_SIZE(styles); ++i)
2035     {
2036         listbox = create_listbox( styles[i] | WS_CHILD | WS_VISIBLE, parent );
2037 
2038         SetLastError( 0xdeadbeef );
2039         ret = SendMessageA( listbox, LB_SETCOUNT, 100, 0 );
2040         ok( ret == LB_ERR, "expected %d, got %d\n", LB_ERR, ret );
2041         ok( GetLastError() == 0xdeadbeef, "Unexpected error %d.\n", GetLastError() );
2042 
2043         DestroyWindow( listbox );
2044     }
2045 
2046     DestroyWindow( parent );
2047 }
2048 
2049 static void test_GetListBoxInfo(void)
2050 {
2051     static const struct message getlistboxinfo_seq[] =
2052     {
2053         { LB_GETLISTBOXINFO, sent },
2054         { 0 }
2055     };
2056     HWND listbox, parent;
2057     DWORD ret;
2058 
2059     parent = create_parent();
2060     listbox = create_listbox(WS_CHILD | WS_VISIBLE, parent);
2061 
2062     flush_sequences(sequences, NUM_MSG_SEQUENCES);
2063     ret = GetListBoxInfo(listbox);
2064     ok(ret > 0, "got %d\n", ret);
2065     ok_sequence(sequences, LB_SEQ_INDEX, getlistboxinfo_seq, "GetListBoxInfo()", FALSE);
2066 
2067     DestroyWindow(listbox);
2068     DestroyWindow(parent);
2069 }
2070 
2071 static void test_init_storage( void )
2072 {
2073     static const DWORD styles[] =
2074     {
2075         LBS_HASSTRINGS,
2076         LBS_NODATA | LBS_OWNERDRAWFIXED,
2077     };
2078     HWND parent, listbox;
2079     LONG ret, items_size;
2080     int i, j;
2081 
2082     parent = create_parent();
2083     for (i = 0; i < ARRAY_SIZE(styles); i++)
2084     {
2085         listbox = CreateWindowA(WC_LISTBOXA, "TestList", styles[i] | WS_CHILD,
2086                                 0, 0, 100, 100, parent, (HMENU)ID_LISTBOX, NULL, 0);
2087 
2088         items_size = SendMessageA(listbox, LB_INITSTORAGE, 100, 0);
2089         ok(items_size >= 100, "expected at least 100, got %d\n", items_size);
2090 
2091         ret = SendMessageA(listbox, LB_INITSTORAGE, 0, 0);
2092         ok(ret == items_size, "expected %d, got %d\n", items_size, ret);
2093 
2094         /* it doesn't grow since the space was already reserved */
2095         ret = SendMessageA(listbox, LB_INITSTORAGE, items_size, 0);
2096         ok(ret == items_size, "expected %d, got %d\n", items_size, ret);
2097 
2098         /* it doesn't shrink the reserved space */
2099         ret = SendMessageA(listbox, LB_INITSTORAGE, 42, 0);
2100         ok(ret == items_size, "expected %d, got %d\n", items_size, ret);
2101 
2102         /* now populate almost all of it so it's not reserved anymore */
2103         if (styles[i] & LBS_NODATA)
2104         {
2105             ret = SendMessageA(listbox, LB_SETCOUNT, items_size - 1, 0);
2106             ok(ret == 0, "unexpected return value %d\n", ret);
2107         }
2108         else
2109         {
2110             for (j = 0; j < items_size - 1; j++)
2111             {
2112                 ret = SendMessageA(listbox, LB_INSERTSTRING, -1, (LPARAM)"");
2113                 ok(ret == j, "expected %d, got %d\n", j, ret);
2114             }
2115         }
2116 
2117         /* we still have one more reserved slot, so it doesn't grow yet */
2118         ret = SendMessageA(listbox, LB_INITSTORAGE, 1, 0);
2119         ok(ret == items_size, "expected %d, got %d\n", items_size, ret);
2120 
2121         /* fill the slot and check again, it should grow this time */
2122         ret = SendMessageA(listbox, LB_INSERTSTRING, -1, (LPARAM)"");
2123         ok(ret == items_size - 1, "expected %d, got %d\n", items_size - 1, ret);
2124         ret = SendMessageA(listbox, LB_INITSTORAGE, 0, 0);
2125         ok(ret == items_size, "expected %d, got %d\n", items_size, ret);
2126         ret = SendMessageA(listbox, LB_INITSTORAGE, 1, 0);
2127         ok(ret > items_size, "expected it to grow past %d, got %d\n", items_size, ret);
2128 
2129         DestroyWindow(listbox);
2130     }
2131     DestroyWindow(parent);
2132 }
2133 
2134 static void test_missing_lbuttonup(void)
2135 {
2136     HWND listbox, parent, capture;
2137 
2138     parent = create_parent();
2139     listbox = create_listbox(WS_CHILD | WS_VISIBLE, parent);
2140 
2141     /* Send button down without a corresponding button up */
2142     SendMessageA(listbox, WM_LBUTTONDOWN, 0, MAKELPARAM(10, 10));
2143     capture = GetCapture();
2144     ok(capture == listbox, "got %p expected %p\n", capture, listbox);
2145 
2146     /* Capture is released and LBN_SELCHANGE sent during WM_KILLFOCUS */
2147     got_selchange = 0;
2148     SetFocus(NULL);
2149     capture = GetCapture();
2150     ok(capture == NULL, "got %p\n", capture);
2151     ok(got_selchange, "got %d\n", got_selchange);
2152 
2153     DestroyWindow(listbox);
2154     DestroyWindow(parent);
2155 }
2156 
2157 static void test_extents(void)
2158 {
2159     HWND listbox, parent;
2160     SCROLLINFO sinfo;
2161     DWORD res;
2162     BOOL br;
2163 
2164     parent = create_parent();
2165 
2166     listbox = create_listbox(WS_CHILD | WS_VISIBLE, parent);
2167 
2168     res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
2169     ok(res == 0, "Got wrong initial horizontal extent: %u\n", res);
2170 
2171     sinfo.cbSize = sizeof(sinfo);
2172     sinfo.fMask = SIF_RANGE;
2173     br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
2174     ok(br == TRUE, "GetScrollInfo failed\n");
2175     ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
2176     ok(sinfo.nMax == 100, "got wrong max: %u\n", sinfo.nMax);
2177     ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0,
2178         "List box should not have a horizontal scroll bar\n");
2179 
2180     /* horizontal extent < width */
2181     SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 64, 0);
2182 
2183     res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
2184     ok(res == 64, "Got wrong horizontal extent: %u\n", res);
2185 
2186     sinfo.cbSize = sizeof(sinfo);
2187     sinfo.fMask = SIF_RANGE;
2188     br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
2189     ok(br == TRUE, "GetScrollInfo failed\n");
2190     ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
2191     ok(sinfo.nMax == 100, "got wrong max: %u\n", sinfo.nMax);
2192     ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0,
2193         "List box should not have a horizontal scroll bar\n");
2194 
2195     /* horizontal extent > width */
2196     SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 184, 0);
2197 
2198     res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
2199     ok(res == 184, "Got wrong horizontal extent: %u\n", res);
2200 
2201     sinfo.cbSize = sizeof(sinfo);
2202     sinfo.fMask = SIF_RANGE;
2203     br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
2204     ok(br == TRUE, "GetScrollInfo failed\n");
2205     ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
2206     ok(sinfo.nMax == 100, "got wrong max: %u\n", sinfo.nMax);
2207     ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0,
2208         "List box should not have a horizontal scroll bar\n");
2209 
2210     DestroyWindow(listbox);
2211 
2212     listbox = create_listbox(WS_CHILD | WS_VISIBLE | WS_HSCROLL, parent);
2213 
2214     res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
2215     ok(res == 0, "Got wrong initial horizontal extent: %u\n", res);
2216 
2217     sinfo.cbSize = sizeof(sinfo);
2218     sinfo.fMask = SIF_RANGE;
2219     br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
2220     ok(br == TRUE, "GetScrollInfo failed\n");
2221     ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
2222     ok(sinfo.nMax == 100, "got wrong max: %u\n", sinfo.nMax);
2223     ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0,
2224         "List box should not have a horizontal scroll bar\n");
2225 
2226     /* horizontal extent < width */
2227     SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 64, 0);
2228 
2229     res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
2230     ok(res == 64, "Got wrong horizontal extent: %u\n", res);
2231 
2232     sinfo.cbSize = sizeof(sinfo);
2233     sinfo.fMask = SIF_RANGE;
2234     br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
2235     ok(br == TRUE, "GetScrollInfo failed\n");
2236     ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
2237     ok(sinfo.nMax == 63, "got wrong max: %u\n", sinfo.nMax);
2238     ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0,
2239         "List box should not have a horizontal scroll bar\n");
2240 
2241     /* horizontal extent > width */
2242     SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 184, 0);
2243 
2244     res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
2245     ok(res == 184, "Got wrong horizontal extent: %u\n", res);
2246 
2247     sinfo.cbSize = sizeof(sinfo);
2248     sinfo.fMask = SIF_RANGE;
2249     br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
2250     ok(br == TRUE, "GetScrollInfo failed\n");
2251     ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
2252     ok(sinfo.nMax == 183, "got wrong max: %u\n", sinfo.nMax);
2253     ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0,
2254         "List box should have a horizontal scroll bar\n");
2255 
2256     SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 0, 0);
2257 
2258     res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
2259     ok(res == 0, "Got wrong horizontal extent: %u\n", res);
2260 
2261     sinfo.cbSize = sizeof(sinfo);
2262     sinfo.fMask = SIF_RANGE;
2263     br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
2264     ok(br == TRUE, "GetScrollInfo failed\n");
2265     ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
2266     ok(sinfo.nMax == 0, "got wrong max: %u\n", sinfo.nMax);
2267     ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0,
2268         "List box should not have a horizontal scroll bar\n");
2269 
2270     DestroyWindow(listbox);
2271 
2272 
2273     listbox = create_listbox(WS_CHILD | WS_VISIBLE | WS_HSCROLL | LBS_DISABLENOSCROLL, parent);
2274 
2275     res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
2276     ok(res == 0, "Got wrong initial horizontal extent: %u\n", res);
2277 
2278     sinfo.cbSize = sizeof(sinfo);
2279     sinfo.fMask = SIF_RANGE;
2280     br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
2281     ok(br == TRUE, "GetScrollInfo failed\n");
2282     ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
2283     ok(sinfo.nMax == 0, "got wrong max: %u\n", sinfo.nMax);
2284     ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0,
2285         "List box should have a horizontal scroll bar\n");
2286 
2287     /* horizontal extent < width */
2288     SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 64, 0);
2289 
2290     res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
2291     ok(res == 64, "Got wrong horizontal extent: %u\n", res);
2292 
2293     sinfo.cbSize = sizeof(sinfo);
2294     sinfo.fMask = SIF_RANGE;
2295     br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
2296     ok(br == TRUE, "GetScrollInfo failed\n");
2297     ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
2298     ok(sinfo.nMax == 63, "got wrong max: %u\n", sinfo.nMax);
2299     ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0,
2300         "List box should have a horizontal scroll bar\n");
2301 
2302     /* horizontal extent > width */
2303     SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 184, 0);
2304 
2305     res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
2306     ok(res == 184, "Got wrong horizontal extent: %u\n", res);
2307 
2308     sinfo.cbSize = sizeof(sinfo);
2309     sinfo.fMask = SIF_RANGE;
2310     br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
2311     ok(br == TRUE, "GetScrollInfo failed\n");
2312     ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
2313     ok(sinfo.nMax == 183, "got wrong max: %u\n", sinfo.nMax);
2314     ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0,
2315         "List box should have a horizontal scroll bar\n");
2316 
2317     SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 0, 0);
2318 
2319     res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
2320     ok(res == 0, "Got wrong horizontal extent: %u\n", res);
2321 
2322     sinfo.cbSize = sizeof(sinfo);
2323     sinfo.fMask = SIF_RANGE;
2324     br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
2325     ok(br == TRUE, "GetScrollInfo failed\n");
2326     ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
2327     ok(sinfo.nMax == 0, "got wrong max: %u\n", sinfo.nMax);
2328     ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0,
2329         "List box should have a horizontal scroll bar\n");
2330 
2331     DestroyWindow(listbox);
2332 
2333     DestroyWindow(parent);
2334 }
2335 
2336 static void test_listbox(void)
2337 {
2338     static const struct listbox_test SS =
2339         /*   {add_style} */
2340         {{LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
2341          {     1,      1,      1, LB_ERR}, {0,0,0,0},
2342          {     2,      2,      2, LB_ERR}, {0,0,0,0},
2343          {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
2344 
2345         /* {selected, anchor,  caret, selcount}{TODO fields} */
2346     static const struct listbox_test SS_NS =
2347         {{LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
2348          {     1,      1,      1, LB_ERR}, {0,0,0,0},
2349          {     2,      2,      2, LB_ERR}, {0,0,0,0},
2350          {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
2351 
2352     static const struct listbox_test MS =
2353         {{     0, LB_ERR,      0,      0}, {0,0,0,0},
2354          {     1,      1,      1,      1}, {0,0,0,0},
2355          {     2,      1,      2,      1}, {0,0,0,0},
2356          {     0, LB_ERR,      0,      2}, {0,0,0,0}};
2357 
2358     static const struct listbox_test MS_NS =
2359         {{LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
2360          {     1,      1,      1, LB_ERR}, {0,0,0,0},
2361          {     2,      2,      2, LB_ERR}, {0,0,0,0},
2362          {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
2363 
2364     static const struct listbox_test ES =
2365         {{     0, LB_ERR,      0,      0}, {0,0,0,0},
2366          {     1,      1,      1,      1}, {0,0,0,0},
2367          {     2,      2,      2,      1}, {0,0,0,0},
2368          {     0, LB_ERR,      0,      2}, {0,0,0,0}};
2369 
2370     static const struct listbox_test ES_NS =
2371         {{LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
2372          {     1,      1,      1, LB_ERR}, {0,0,0,0},
2373          {     2,      2,      2, LB_ERR}, {0,0,0,0},
2374          {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
2375 
2376     static const struct listbox_test EMS =
2377         {{     0, LB_ERR,      0,      0}, {0,0,0,0},
2378          {     1,      1,      1,      1}, {0,0,0,0},
2379          {     2,      2,      2,      1}, {0,0,0,0},
2380          {     0, LB_ERR,      0,      2}, {0,0,0,0}};
2381 
2382     static const struct listbox_test EMS_NS =
2383         {{LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
2384          {     1,      1,      1, LB_ERR}, {0,0,0,0},
2385          {     2,      2,      2, LB_ERR}, {0,0,0,0},
2386          {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
2387 
2388     run_test(0, SS);
2389     run_test(LBS_NOSEL, SS_NS);
2390     run_test(LBS_MULTIPLESEL, MS);
2391     run_test(LBS_MULTIPLESEL | LBS_NOSEL, MS_NS);
2392     run_test(LBS_EXTENDEDSEL, ES);
2393     run_test(LBS_EXTENDEDSEL | LBS_NOSEL, ES_NS);
2394     run_test(LBS_EXTENDEDSEL | LBS_MULTIPLESEL, EMS);
2395     run_test(LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL, EMS_NS);
2396 
2397     run_test(LBS_NODATA | LBS_OWNERDRAWFIXED, SS);
2398     run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_NOSEL, SS_NS);
2399     run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_MULTIPLESEL, MS);
2400     run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_MULTIPLESEL | LBS_NOSEL, MS_NS);
2401     run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL, ES);
2402     run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_NOSEL, ES_NS);
2403     run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_MULTIPLESEL, EMS);
2404     run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL, EMS_NS);
2405 }
2406 
2407 static const struct message lb_addstring_ownerdraw_parent_seq[] =
2408 {
2409     { WM_MEASUREITEM, sent|wparam|lparam, 0x1012, 0xf30604ed },
2410     { WM_MEASUREITEM, sent|wparam|lparam, 0x1112, 0xf30604ee },
2411     { WM_MEASUREITEM, sent|wparam|lparam, 0x1212, 0xf30604ef },
2412     { 0 }
2413 };
2414 
2415 static const struct message lb_addstring_sort_parent_seq[] =
2416 {
2417     { WM_MEASUREITEM, sent|wparam|lparam, 0x1012, 0xf30604ed },
2418     { WM_COMPAREITEM, sent|wparam|lparam, 0xf30604ed, 0xf30604ee },
2419     { WM_MEASUREITEM, sent|wparam|lparam, 0x1112, 0xf30604ee },
2420     { WM_COMPAREITEM, sent|wparam|lparam, 0xf30604ed, 0xf30604ef },
2421     { WM_COMPAREITEM, sent|wparam|lparam, 0xf30604ee, 0xf30604ef },
2422     { WM_MEASUREITEM, sent|wparam|lparam, 0x1212, 0xf30604ef },
2423     { 0 }
2424 };
2425 
2426 static const struct message empty_seq[] =
2427 {
2428     { 0 }
2429 };
2430 
2431 static void test_WM_MEASUREITEM(void)
2432 {
2433     HWND parent, listbox;
2434     LRESULT data, ret;
2435 
2436     parent = create_parent();
2437     listbox = create_listbox(WS_CHILD | LBS_OWNERDRAWVARIABLE, parent);
2438 
2439     data = SendMessageA(listbox, LB_GETITEMDATA, 0, 0);
2440     ok(data == (LRESULT)strings[0], "data = %08lx, expected %p\n", data, strings[0]);
2441     DestroyWindow(parent);
2442 
2443     parent = create_parent();
2444     listbox = create_listbox(WS_CHILD | LBS_OWNERDRAWVARIABLE | LBS_HASSTRINGS, parent);
2445 
2446     data = SendMessageA(listbox, LB_GETITEMDATA, 0, 0);
2447     ok(!data, "data = %08lx\n", data);
2448 
2449     /* LBS_HASSTRINGS */
2450     parent = create_parent();
2451     listbox = CreateWindowExA(WS_EX_NOPARENTNOTIFY, WC_LISTBOXA, NULL,
2452                               WS_CHILD | LBS_NOTIFY | LBS_OWNERDRAWVARIABLE | LBS_HASSTRINGS | WS_VISIBLE,
2453                               10, 10, 80, 80, parent, (HMENU)ID_LISTBOX, 0, NULL);
2454 
2455     flush_sequences(sequences, NUM_MSG_SEQUENCES);
2456 
2457     ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 0");
2458     ok(ret == 0, "expected 0, got %ld\n", ret);
2459     ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 1");
2460     ok(ret == 1, "expected 1, got %ld\n", ret);
2461     ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 2");
2462     ok(ret == 2, "expected 2, got %ld\n", ret);
2463 
2464     ok_sequence(sequences, PARENT_SEQ_INDEX, lb_addstring_ownerdraw_parent_seq,
2465         "LB_ADDSTRING (LBS_HASSTRINGS, ownerdraw)", FALSE);
2466     DestroyWindow(listbox);
2467 
2468     /* LBS_SORT, no LBS_HASSTRINGS */
2469     listbox = CreateWindowExA(WS_EX_NOPARENTNOTIFY, WC_LISTBOXA, NULL,
2470                               WS_CHILD | LBS_NOTIFY | LBS_OWNERDRAWVARIABLE | LBS_SORT | WS_VISIBLE,
2471                               10, 10, 80, 80, parent, (HMENU)ID_LISTBOX, 0, NULL);
2472 
2473     flush_sequences(sequences, NUM_MSG_SEQUENCES);
2474 
2475     ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 0");
2476     ok(ret == 0, "expected 0, got %ld\n", ret);
2477     ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 1");
2478     ok(ret == 1, "expected 1, got %ld\n", ret);
2479     ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 2");
2480     ok(ret == 2, "expected 2, got %ld\n", ret);
2481 
2482     ok_sequence(sequences, PARENT_SEQ_INDEX, lb_addstring_sort_parent_seq, "LB_ADDSTRING (LBS_SORT)", FALSE);
2483     DestroyWindow(listbox);
2484 
2485     /* LBS_HASSTRINGS */
2486     listbox = CreateWindowExA(WS_EX_NOPARENTNOTIFY, WC_LISTBOXA, NULL,
2487                               WS_CHILD | LBS_NOTIFY | LBS_HASSTRINGS | WS_VISIBLE,
2488                               10, 10, 80, 80, parent, (HMENU)ID_LISTBOX, 0, NULL);
2489 
2490     flush_sequences(sequences, NUM_MSG_SEQUENCES);
2491 
2492     ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 2");
2493     ok(ret == 0, "expected 0, got %ld\n", ret);
2494     ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 0");
2495     ok(ret == 1, "expected 1, got %ld\n", ret);
2496     ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 1");
2497     ok(ret == 2, "expected 2, got %ld\n", ret);
2498 
2499     ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "LB_ADDSTRING (LBS_HASSTRINGS)", FALSE);
2500     DestroyWindow(listbox);
2501 
2502     /* LBS_HASSTRINGS, LBS_SORT */
2503     listbox = CreateWindowExA(WS_EX_NOPARENTNOTIFY, WC_LISTBOXA, NULL,
2504                               WS_CHILD | LBS_NOTIFY | LBS_HASSTRINGS | LBS_SORT | WS_VISIBLE,
2505                               10, 10, 80, 80, parent, (HMENU)ID_LISTBOX, 0, NULL);
2506 
2507     flush_sequences(sequences, NUM_MSG_SEQUENCES);
2508 
2509     ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 2");
2510     ok(ret == 0, "expected 0, got %ld\n", ret);
2511     ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 0");
2512     ok(ret == 0, "expected 0, got %ld\n", ret);
2513     ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 1");
2514     ok(ret == 1, "expected 1, got %ld\n", ret);
2515 
2516     ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "LB_ADDSTRING (LBS_HASSTRINGS, LBS_SORT)", FALSE);
2517     DestroyWindow(listbox);
2518 
2519     DestroyWindow(parent);
2520 }
2521 
2522 static void test_LBS_NODATA(void)
2523 {
2524     static const DWORD invalid_styles[] =
2525     {
2526         0,
2527         LBS_OWNERDRAWVARIABLE,
2528         LBS_SORT,
2529         LBS_HASSTRINGS,
2530         LBS_OWNERDRAWFIXED | LBS_SORT,
2531         LBS_OWNERDRAWFIXED | LBS_HASSTRINGS,
2532     };
2533     static const UINT invalid_idx[] = { -2, 2 };
2534     static const UINT valid_idx[] = { 0, 1 };
2535     static const ULONG_PTR zero_data;
2536     HWND listbox, parent;
2537     INT ret, text_len;
2538     unsigned int i;
2539     ULONG_PTR data;
2540     BOOL is_wow64;
2541 
2542     listbox = CreateWindowA(WC_LISTBOXA, "TestList", LBS_NODATA | LBS_OWNERDRAWFIXED | WS_VISIBLE,
2543         0, 0, 100, 100, NULL, NULL, NULL, 0);
2544     ok(listbox != NULL, "Failed to create ListBox window.\n");
2545 
2546     ret = SendMessageA(listbox, LB_INSERTSTRING, -1, 0);
2547     ok(ret == 0, "Unexpected return value %d.\n", ret);
2548     ret = SendMessageA(listbox, LB_INSERTSTRING, -1, 0);
2549     ok(ret == 1, "Unexpected return value %d.\n", ret);
2550     ret = SendMessageA(listbox, LB_GETCOUNT, 0, 0);
2551     ok(ret == 2, "Unexpected return value %d.\n", ret);
2552 
2553     /* Invalid indices. */
2554     for (i = 0; i < ARRAY_SIZE(invalid_idx); ++i)
2555     {
2556         ret = SendMessageA(listbox, LB_SETITEMDATA, invalid_idx[i], 42);
2557         ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
2558         ret = SendMessageA(listbox, LB_GETTEXTLEN, invalid_idx[i], 0);
2559         ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
2560         if (ret == LB_ERR)
2561         {
2562             ret = SendMessageA(listbox, LB_GETTEXT, invalid_idx[i], (LPARAM)&data);
2563             ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
2564         }
2565         ret = SendMessageA(listbox, LB_GETITEMDATA, invalid_idx[i], 0);
2566         ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
2567     }
2568 
2569     IsWow64Process(GetCurrentProcess(), &is_wow64);
2570 #ifdef _WIN64
2571     text_len = 8;
2572 #else
2573     text_len = is_wow64 ? 8 : 4;
2574 #endif
2575 
2576     /* Valid indices. */
2577     for (i = 0; i < ARRAY_SIZE(valid_idx); ++i)
2578     {
2579         ret = SendMessageA(listbox, LB_SETITEMDATA, valid_idx[i], 42);
2580         ok(ret == TRUE, "Unexpected return value %d.\n", ret);
2581         ret = SendMessageA(listbox, LB_GETTEXTLEN, valid_idx[i], 0);
2582     todo_wine_if(is_wow64)
2583         ok(ret == text_len, "Unexpected return value %d.\n", ret);
2584 
2585         memset(&data, 0xee, sizeof(data));
2586         ret = SendMessageA(listbox, LB_GETTEXT, valid_idx[i], (LPARAM)&data);
2587         ok(ret == sizeof(data), "Unexpected return value %d.\n", ret);
2588         ok(!memcmp(&data, &zero_data, sizeof(data)), "Unexpected item data.\n");
2589 
2590         ret = SendMessageA(listbox, LB_GETITEMDATA, valid_idx[i], 0);
2591         ok(ret == 0, "Unexpected return value %d.\n", ret);
2592     }
2593 
2594     /* More messages that don't work with LBS_NODATA. */
2595     ret = SendMessageA(listbox, LB_FINDSTRING, 1, 0);
2596     ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
2597     ret = SendMessageA(listbox, LB_FINDSTRING, 1, 42);
2598     ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
2599     ret = SendMessageA(listbox, LB_FINDSTRINGEXACT, 1, 0);
2600     ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
2601     ret = SendMessageA(listbox, LB_FINDSTRINGEXACT, 1, 42);
2602     ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
2603     ret = SendMessageA(listbox, LB_SELECTSTRING, 1, 0);
2604     ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
2605     ret = SendMessageA(listbox, LB_SELECTSTRING, 1, 42);
2606     ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
2607 
2608     DestroyWindow(listbox);
2609 
2610     /* Invalid window style combinations. */
2611     parent = create_parent();
2612     ok(parent != NULL, "Failed to create parent window.\n");
2613 
2614     for (i = 0; i < ARRAY_SIZE(invalid_styles); ++i)
2615     {
2616         DWORD style;
2617 
2618         listbox = CreateWindowA(WC_LISTBOXA, "TestList", LBS_NODATA | WS_CHILD | invalid_styles[i],
2619                 0, 0, 100, 100, parent, (HMENU)ID_LISTBOX, NULL, 0);
2620         ok(listbox != NULL, "Failed to create a listbox.\n");
2621 
2622         style = GetWindowLongA(listbox, GWL_STYLE);
2623         ok((style & invalid_styles[i]) == invalid_styles[i], "%u: unexpected window styles %#x.\n", i, style);
2624         ret = SendMessageA(listbox, LB_SETCOUNT, 100, 0);
2625         ok(ret == LB_ERR, "%u: unexpected return value %d.\n", i, ret);
2626         DestroyWindow(listbox);
2627     }
2628 
2629     DestroyWindow(parent);
2630 }
2631 
2632 START_TEST(listbox)
2633 {
2634     ULONG_PTR ctx_cookie;
2635     HANDLE hCtx;
2636 
2637     if (!load_v6_module(&ctx_cookie, &hCtx))
2638         return;
2639 
2640     init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
2641 
2642     test_listbox();
2643     test_item_height();
2644     test_ownerdraw();
2645     test_LB_SELITEMRANGE();
2646     test_LB_SETCURSEL();
2647     test_listbox_height();
2648     test_changing_selection_styles();
2649     test_itemfrompoint();
2650     test_listbox_item_data();
2651     test_listbox_LB_DIR();
2652     test_listbox_dlgdir();
2653     test_set_count();
2654     test_init_storage();
2655     test_GetListBoxInfo();
2656     test_missing_lbuttonup();
2657     test_extents();
2658     test_WM_MEASUREITEM();
2659     test_LB_SETSEL();
2660     test_LBS_NODATA();
2661 
2662     unload_v6_module(ctx_cookie, hCtx);
2663 }
2664