[COMCTL32_WINETEST] Sync with Wine 3.0. CORE-14225
authorAmine Khaldi <amine.khaldi@reactos.org>
Thu, 18 Jan 2018 22:49:50 +0000 (23:49 +0100)
committerAmine Khaldi <amine.khaldi@reactos.org>
Thu, 18 Jan 2018 22:49:50 +0000 (23:49 +0100)
18 files changed:
modules/rostests/winetests/comctl32/CMakeLists.txt
modules/rostests/winetests/comctl32/combo.c [new file with mode: 0644]
modules/rostests/winetests/comctl32/comboex.c [deleted file]
modules/rostests/winetests/comctl32/edit.c [new file with mode: 0644]
modules/rostests/winetests/comctl32/imagelist.c
modules/rostests/winetests/comctl32/listbox.c [new file with mode: 0644]
modules/rostests/winetests/comctl32/listview.c
modules/rostests/winetests/comctl32/misc.c
modules/rostests/winetests/comctl32/msg.h
modules/rostests/winetests/comctl32/propsheet.c
modules/rostests/winetests/comctl32/rsrc.rc
modules/rostests/winetests/comctl32/static.c [new file with mode: 0644]
modules/rostests/winetests/comctl32/syslink.c
modules/rostests/winetests/comctl32/taskdialog.c
modules/rostests/winetests/comctl32/testlist.c
modules/rostests/winetests/comctl32/tooltips.c
modules/rostests/winetests/comctl32/treeview.c
modules/rostests/winetests/comctl32/updown.c

index f72f87a..9212bc0 100644 (file)
@@ -1,16 +1,18 @@
 
 remove_definitions(-D_WIN32_WINNT=0x502 -D_WIN32_IE=0x600)
 
-add_definitions(-DUSE_WINE_TODOS)
+add_definitions(-DUSE_WINE_TODOS -DWINETEST_USE_DBGSTR_LONGLONG)
 
 list(APPEND SOURCE
     animate.c
-    comboex.c
+    combo.c
     datetime.c
     dpa.c
+    edit.c
     header.c
     imagelist.c
     ipaddress.c
+    listbox.c
     listview.c
     misc.c
     monthcal.c
@@ -19,6 +21,7 @@ list(APPEND SOURCE
     progress.c
     propsheet.c
     rebar.c
+    static.c
     status.c
     syslink.c
     tab.c
@@ -43,5 +46,10 @@ endif()
 
 set_module_type(comctl32_winetest win32cui)
 add_importlibs(comctl32_winetest comctl32 ole32 user32 gdi32 advapi32 msvcrt kernel32)
+
+if(MSVC)
+    add_importlibs(comctl32_winetest ntdll)
+endif()
+
 add_pch(comctl32_winetest precomp.h SOURCE)
 add_rostests_file(TARGET comctl32_winetest)
diff --git a/modules/rostests/winetests/comctl32/combo.c b/modules/rostests/winetests/comctl32/combo.c
new file mode 100644 (file)
index 0000000..ca493c0
--- /dev/null
@@ -0,0 +1,1194 @@
+/* Unit test suite for ComboBox and ComboBoxEx32 controls.
+ *
+ * Copyright 2005 Jason Edmeades
+ * Copyright 2007 Mikolaj Zalewski
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "precomp.h"
+
+#define EDITBOX_SEQ_INDEX  0
+#define NUM_MSG_SEQUENCES  1
+
+#define EDITBOX_ID         0
+#define COMBO_ID           1995
+
+#define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
+
+#define expect_rect(r, _left, _top, _right, _bottom) ok(r.left == _left && r.top == _top && \
+    r.bottom == _bottom && r.right == _right, "Invalid rect %s vs (%d,%d)-(%d,%d)\n", \
+    wine_dbgstr_rect(&r), _left, _top, _right, _bottom);
+
+
+static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
+
+static HWND hComboExParentWnd, hMainWnd;
+static HINSTANCE hMainHinst;
+static const char ComboExTestClass[] = "ComboExTestClass";
+
+static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
+
+#define MAX_CHARS 100
+static char *textBuffer = NULL;
+
+static BOOL received_end_edit = FALSE;
+
+static void get_combobox_info(HWND hwnd, COMBOBOXINFO *info)
+{
+    BOOL ret;
+
+    info->cbSize = sizeof(*info);
+    ret = GetComboBoxInfo(hwnd, info);
+    ok(ret, "Failed to get combobox info structure, error %d\n", GetLastError());
+}
+
+static HWND createComboEx(DWORD style) {
+   return CreateWindowExA(0, WC_COMBOBOXEXA, NULL, style, 0, 0, 300, 300,
+            hComboExParentWnd, NULL, hMainHinst, NULL);
+}
+
+static LONG addItem(HWND cbex, int idx, const char *text) {
+    COMBOBOXEXITEMA cbexItem;
+    memset(&cbexItem, 0x00, sizeof(cbexItem));
+    cbexItem.mask = CBEIF_TEXT;
+    cbexItem.iItem = idx;
+    cbexItem.pszText    = (char*)text;
+    cbexItem.cchTextMax = 0;
+    return SendMessageA(cbex, CBEM_INSERTITEMA, 0, (LPARAM)&cbexItem);
+}
+
+static LONG setItem(HWND cbex, int idx, const char *text) {
+    COMBOBOXEXITEMA cbexItem;
+    memset(&cbexItem, 0x00, sizeof(cbexItem));
+    cbexItem.mask = CBEIF_TEXT;
+    cbexItem.iItem = idx;
+    cbexItem.pszText    = (char*)text;
+    cbexItem.cchTextMax = 0;
+    return SendMessageA(cbex, CBEM_SETITEMA, 0, (LPARAM)&cbexItem);
+}
+
+static LONG delItem(HWND cbex, int idx) {
+    return SendMessageA(cbex, CBEM_DELETEITEM, idx, 0);
+}
+
+static LONG getItem(HWND cbex, int idx, COMBOBOXEXITEMA *cbItem) {
+    memset(cbItem, 0x00, sizeof(COMBOBOXEXITEMA));
+    cbItem->mask = CBEIF_TEXT;
+    cbItem->pszText      = textBuffer;
+    cbItem->iItem        = idx;
+    cbItem->cchTextMax   = 100;
+    return SendMessageA(cbex, CBEM_GETITEMA, 0, (LPARAM)cbItem);
+}
+
+static LRESULT WINAPI editbox_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
+    static LONG defwndproc_counter = 0;
+    struct message msg = { 0 };
+    LRESULT ret;
+
+    msg.message = message;
+    msg.flags = sent|wparam|lparam;
+    if (defwndproc_counter) msg.flags |= defwinproc;
+    msg.wParam = wParam;
+    msg.lParam = lParam;
+    msg.id     = EDITBOX_ID;
+
+    if (message != WM_PAINT &&
+        message != WM_ERASEBKGND &&
+        message != WM_NCPAINT &&
+        message != WM_NCHITTEST &&
+        message != WM_GETTEXT &&
+        message != WM_GETICON &&
+        message != WM_DEVICECHANGE)
+    {
+        add_message(sequences, EDITBOX_SEQ_INDEX, &msg);
+    }
+
+    defwndproc_counter++;
+    ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
+    defwndproc_counter--;
+    return ret;
+}
+
+static HWND subclass_editbox(HWND hwndComboEx)
+{
+    WNDPROC oldproc;
+    HWND hwnd;
+
+    hwnd = (HWND)SendMessageA(hwndComboEx, CBEM_GETEDITCONTROL, 0, 0);
+    oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
+                                         (LONG_PTR)editbox_subclass_proc);
+    SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc);
+
+    return hwnd;
+}
+
+static void test_comboex(void)
+{
+    HWND myHwnd = 0;
+    LONG res;
+    COMBOBOXEXITEMA cbexItem;
+    static const char *first_item  = "First Item",
+                *second_item = "Second Item",
+                *third_item  = "Third Item",
+                *middle_item = "Between First and Second Items",
+                *replacement_item = "Between First and Second Items",
+                *out_of_range_item = "Out of Range Item";
+
+    /* Allocate space for result */
+    textBuffer = HeapAlloc(GetProcessHeap(), 0, MAX_CHARS);
+
+    /* Basic comboboxex test */
+    myHwnd = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
+
+    /* Add items onto the end of the combobox */
+    res = addItem(myHwnd, -1, first_item);
+    ok(res == 0, "Adding simple item failed (%d)\n", res);
+    res = addItem(myHwnd, -1, second_item);
+    ok(res == 1, "Adding simple item failed (%d)\n", res);
+    res = addItem(myHwnd, 2, third_item);
+    ok(res == 2, "Adding simple item failed (%d)\n", res);
+    res = addItem(myHwnd, 1, middle_item);
+    ok(res == 1, "Inserting simple item failed (%d)\n", res);
+
+    /* Add an item completely out of range */
+    res = addItem(myHwnd, 99, out_of_range_item);
+    ok(res == -1, "Adding using out of range index worked unexpectedly (%d)\n", res);
+    res = addItem(myHwnd, 5, out_of_range_item);
+    ok(res == -1, "Adding using out of range index worked unexpectedly (%d)\n", res);
+    /* Removed: Causes traps on Windows XP
+       res = addItem(myHwnd, -2, "Out Of Range Item");
+       ok(res == -1, "Adding out of range worked unexpectedly (%ld)\n", res);
+     */
+
+    /* Get an item completely out of range */ 
+    res = getItem(myHwnd, 99, &cbexItem); 
+    ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText);
+    res = getItem(myHwnd, 4, &cbexItem); 
+    ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText);
+    res = getItem(myHwnd, -2, &cbexItem); 
+    ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText);
+
+    /* Get an item in range */ 
+    res = getItem(myHwnd, 0, &cbexItem); 
+    ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res);
+    ok(strcmp(first_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
+
+    res = getItem(myHwnd, 1, &cbexItem); 
+    ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res);
+    ok(strcmp(middle_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
+
+    res = getItem(myHwnd, 2, &cbexItem); 
+    ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res);
+    ok(strcmp(second_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
+
+    res = getItem(myHwnd, 3, &cbexItem); 
+    ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res);
+    ok(strcmp(third_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
+
+    /* Set an item completely out of range */ 
+    res = setItem(myHwnd, 99, replacement_item); 
+    ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res);
+    res = setItem(myHwnd, 4, replacement_item); 
+    ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res);
+    res = setItem(myHwnd, -2, replacement_item); 
+    ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res);
+
+    /* Set an item in range */ 
+    res = setItem(myHwnd, 0, replacement_item);
+    ok(res != 0, "Setting first item failed (%d)\n", res);
+    res = setItem(myHwnd, 3, replacement_item);
+    ok(res != 0, "Setting last item failed (%d)\n", res);
+
+    /* Remove items completely out of range (4 items in control at this point) */
+    res = delItem(myHwnd, -1);
+    ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res);
+    res = delItem(myHwnd, 4);
+    ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res);
+
+    /* Remove items in range (4 items in control at this point) */
+    res = delItem(myHwnd, 3);
+    ok(res == 3, "Deleting using out of range index failed (%d)\n", res);
+    res = delItem(myHwnd, 0);
+    ok(res == 2, "Deleting using out of range index failed (%d)\n", res);
+    res = delItem(myHwnd, 0);
+    ok(res == 1, "Deleting using out of range index failed (%d)\n", res);
+    res = delItem(myHwnd, 0);
+    ok(res == 0, "Deleting using out of range index failed (%d)\n", res);
+
+    /* Remove from an empty box */
+    res = delItem(myHwnd, 0);
+    ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res);
+
+
+    /* Cleanup */
+    HeapFree(GetProcessHeap(), 0, textBuffer);
+    DestroyWindow(myHwnd);
+}
+
+static void test_comboex_WM_LBUTTONDOWN(void)
+{
+    HWND hComboEx, hCombo, hEdit, hList;
+    COMBOBOXINFO cbInfo;
+    UINT x, y, item_height;
+    LRESULT result;
+    UINT i;
+    int idx;
+    RECT rect;
+    WCHAR buffer[3];
+    static const UINT choices[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72};
+    static const WCHAR stringFormat[] = {'%','2','d','\0'};
+
+    hComboEx = CreateWindowExA(0, WC_COMBOBOXEXA, NULL,
+            WS_VISIBLE|WS_CHILD|CBS_DROPDOWN, 0, 0, 200, 150,
+            hComboExParentWnd, NULL, hMainHinst, NULL);
+
+    for (i = 0; i < sizeof(choices)/sizeof(UINT); i++){
+        COMBOBOXEXITEMW cbexItem;
+        wsprintfW(buffer, stringFormat, choices[i]);
+
+        memset(&cbexItem, 0x00, sizeof(cbexItem));
+        cbexItem.mask = CBEIF_TEXT;
+        cbexItem.iItem = i;
+        cbexItem.pszText = buffer;
+        cbexItem.cchTextMax = 0;
+        ok(SendMessageW(hComboEx, CBEM_INSERTITEMW, 0, (LPARAM)&cbexItem) >= 0,
+           "Failed to add item %d\n", i);
+    }
+
+    hCombo = (HWND)SendMessageA(hComboEx, CBEM_GETCOMBOCONTROL, 0, 0);
+    hEdit = (HWND)SendMessageA(hComboEx, CBEM_GETEDITCONTROL, 0, 0);
+
+    get_combobox_info(hCombo, &cbInfo);
+    hList = cbInfo.hwndList;
+
+    ok(GetFocus() == hComboExParentWnd,
+       "Focus not on Main Window, instead on %p\n", GetFocus());
+
+    /* Click on the button to drop down the list */
+    x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2;
+    y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2;
+    result = SendMessageA(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
+    ok(result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
+       GetLastError());
+    ok(GetFocus() == hCombo ||
+       broken(GetFocus() != hCombo), /* win98 */
+       "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
+       GetFocus());
+    ok(SendMessageA(hComboEx, CB_GETDROPPEDSTATE, 0, 0),
+       "The dropdown list should have appeared after clicking the button.\n");
+    idx = SendMessageA(hCombo, CB_GETTOPINDEX, 0, 0);
+    ok(idx == 0, "For TopIndex expected %d, got %d\n", 0, idx);
+
+    result = SendMessageA(hCombo, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
+    ok(result, "WM_LBUTTONUP was not processed. LastError=%d\n",
+       GetLastError());
+    ok(GetFocus() == hCombo ||
+       broken(GetFocus() != hCombo), /* win98 */
+       "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
+       GetFocus());
+
+    /* Click on the 5th item in the list */
+    item_height = SendMessageA(hCombo, CB_GETITEMHEIGHT, 0, 0);
+    ok(GetClientRect(hList, &rect), "Failed to get list's client rect.\n");
+    x = rect.left + (rect.right-rect.left)/2;
+    y = item_height/2 + item_height*4;
+    result = SendMessageA(hList, WM_MOUSEMOVE, 0, MAKELPARAM(x, y));
+    ok(!result, "WM_MOUSEMOVE was not processed. LastError=%d\n",
+       GetLastError());
+    ok(GetFocus() == hCombo ||
+       broken(GetFocus() != hCombo), /* win98 */
+       "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
+       GetFocus());
+
+    result = SendMessageA(hList, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
+    ok(!result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
+       GetLastError());
+    ok(GetFocus() == hCombo ||
+       broken(GetFocus() != hCombo), /* win98 */
+       "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
+       GetFocus());
+    ok(SendMessageA(hComboEx, CB_GETDROPPEDSTATE, 0, 0),
+       "The dropdown list should still be visible.\n");
+
+    result = SendMessageA(hList, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
+    ok(!result, "WM_LBUTTONUP was not processed. LastError=%d\n",
+       GetLastError());
+    todo_wine ok(GetFocus() == hEdit ||
+       broken(GetFocus() == hCombo), /* win98 */
+       "Focus not on ComboBoxEx's Edit Control, instead on %p\n",
+       GetFocus());
+
+    result = SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0);
+    ok(!result ||
+       broken(result != 0), /* win98 */
+       "The dropdown list should have been rolled up.\n");
+    idx = SendMessageA(hComboEx, CB_GETCURSEL, 0, 0);
+    ok(idx == 4 ||
+       broken(idx == -1), /* win98 */
+       "Current Selection: expected %d, got %d\n", 4, idx);
+    ok(received_end_edit, "Expected to receive a CBEN_ENDEDIT message\n");
+
+    SetFocus( hComboExParentWnd );
+    ok( GetFocus() == hComboExParentWnd, "got %p\n", GetFocus() );
+    SetFocus( hComboEx );
+    ok( GetFocus() == hEdit, "got %p\n", GetFocus() );
+
+    DestroyWindow(hComboEx);
+}
+
+static void test_comboex_CB_GETLBTEXT(void)
+{
+    HWND hCombo;
+    CHAR buff[1];
+    COMBOBOXEXITEMA item;
+    LRESULT ret;
+
+    hCombo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
+
+    /* set text to null */
+    addItem(hCombo, 0, NULL);
+
+    buff[0] = 'a';
+    item.mask = CBEIF_TEXT;
+    item.iItem = 0;
+    item.pszText = buff;
+    item.cchTextMax = 1;
+    ret = SendMessageA(hCombo, CBEM_GETITEMA, 0, (LPARAM)&item);
+    ok(ret != 0, "CBEM_GETITEM failed\n");
+    ok(buff[0] == 0, "\n");
+
+    ret = SendMessageA(hCombo, CB_GETLBTEXTLEN, 0, 0);
+    ok(ret == 0, "Expected zero length\n");
+
+    ret = SendMessageA(hCombo, CB_GETLBTEXTLEN, 0, 0);
+    ok(ret == 0, "Expected zero length\n");
+
+    buff[0] = 'a';
+    ret = SendMessageA(hCombo, CB_GETLBTEXT, 0, (LPARAM)buff);
+    ok(ret == 0, "Expected zero length\n");
+    ok(buff[0] == 0, "Expected null terminator as a string, got %s\n", buff);
+
+    DestroyWindow(hCombo);
+}
+
+static void test_comboex_WM_WINDOWPOSCHANGING(void)
+{
+    HWND hCombo;
+    WINDOWPOS wp;
+    RECT rect;
+    int combo_height;
+    int ret;
+
+    hCombo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
+    ok(hCombo != NULL, "createComboEx failed\n");
+    ret = GetWindowRect(hCombo, &rect);
+    ok(ret, "GetWindowRect failed\n");
+    combo_height = rect.bottom - rect.top;
+    ok(combo_height > 0, "wrong combo height\n");
+
+    /* Test height > combo_height */
+    wp.x = rect.left;
+    wp.y = rect.top;
+    wp.cx = (rect.right - rect.left);
+    wp.cy = combo_height * 2;
+    wp.flags = 0;
+    wp.hwnd = hCombo;
+    wp.hwndInsertAfter = NULL;
+
+    ret = SendMessageA(hCombo, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp);
+    ok(ret == 0, "expected 0, got %x\n", ret);
+    ok(wp.cy == combo_height,
+            "Expected height %d, got %d\n", combo_height, wp.cy);
+
+    /* Test height < combo_height */
+    wp.x = rect.left;
+    wp.y = rect.top;
+    wp.cx = (rect.right - rect.left);
+    wp.cy = combo_height / 2;
+    wp.flags = 0;
+    wp.hwnd = hCombo;
+    wp.hwndInsertAfter = NULL;
+
+    ret = SendMessageA(hCombo, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp);
+    ok(ret == 0, "expected 0, got %x\n", ret);
+    ok(wp.cy == combo_height,
+            "Expected height %d, got %d\n", combo_height, wp.cy);
+
+    ret = DestroyWindow(hCombo);
+    ok(ret, "DestroyWindow failed\n");
+}
+
+static LRESULT ComboExTestOnNotify(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    NMHDR *hdr = (NMHDR*)lParam;
+    switch(hdr->code){
+    case CBEN_ENDEDITA:
+        {
+            NMCBEENDEDITA *edit_info = (NMCBEENDEDITA*)hdr;
+            if(edit_info->iWhy==CBENF_DROPDOWN){
+                received_end_edit = TRUE;
+            }
+            break;
+        }
+    case CBEN_ENDEDITW:
+        {
+            NMCBEENDEDITW *edit_info = (NMCBEENDEDITW*)hdr;
+            if(edit_info->iWhy==CBENF_DROPDOWN){
+                received_end_edit = TRUE;
+            }
+            break;
+        }
+    }
+    return 0;
+}
+
+static LRESULT CALLBACK ComboExTestWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    switch(msg) {
+
+    case WM_DESTROY:
+        PostQuitMessage(0);
+        break;
+    case WM_NOTIFY:
+        return ComboExTestOnNotify(hWnd,msg,wParam,lParam);
+    default:
+        return DefWindowProcA(hWnd, msg, wParam, lParam);
+    }
+    
+    return 0L;
+}
+
+static BOOL init(void)
+{
+    HMODULE hComctl32;
+    BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
+    WNDCLASSA wc;
+    INITCOMMONCONTROLSEX iccex;
+
+    hComctl32 = GetModuleHandleA("comctl32.dll");
+    pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
+    if (!pInitCommonControlsEx)
+    {
+        win_skip("InitCommonControlsEx() is missing. Skipping the tests\n");
+        return FALSE;
+    }
+    iccex.dwSize = sizeof(iccex);
+    iccex.dwICC  = ICC_USEREX_CLASSES;
+    pInitCommonControlsEx(&iccex);
+
+    pSetWindowSubclass = (void*)GetProcAddress(hComctl32, (LPSTR)410);
+
+    wc.style = CS_HREDRAW | CS_VREDRAW;
+    wc.cbClsExtra = 0;
+    wc.cbWndExtra = 0;
+    wc.hInstance = GetModuleHandleA(NULL);
+    wc.hIcon = NULL;
+    wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
+    wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
+    wc.lpszMenuName = NULL;
+    wc.lpszClassName = ComboExTestClass;
+    wc.lpfnWndProc = ComboExTestWndProc;
+    RegisterClassA(&wc);
+
+    hMainWnd = CreateWindowA("static", "Test", WS_OVERLAPPEDWINDOW, 10, 10, 300, 300, NULL, NULL, NULL, 0);
+    ShowWindow(hMainWnd, SW_SHOW);
+
+    hComboExParentWnd = CreateWindowExA(0, ComboExTestClass, "ComboEx test", WS_OVERLAPPEDWINDOW|WS_VISIBLE,
+      CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, GetModuleHandleA(NULL), 0);
+    ok(hComboExParentWnd != NULL, "failed to create parent window\n");
+
+    hMainHinst = GetModuleHandleA(NULL);
+
+    return hComboExParentWnd != NULL;
+}
+
+static void cleanup(void)
+{
+    MSG msg;
+    
+    PostMessageA(hComboExParentWnd, WM_CLOSE, 0, 0);
+    while (GetMessageA(&msg,0,0,0)) {
+        TranslateMessage(&msg);
+        DispatchMessageA(&msg);
+    }
+    
+    DestroyWindow(hComboExParentWnd);
+    UnregisterClassA(ComboExTestClass, GetModuleHandleA(NULL));
+
+    DestroyWindow(hMainWnd);
+}
+
+static void test_comboex_subclass(void)
+{
+    HWND hComboEx, hCombo, hEdit;
+
+    hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
+
+    hCombo = (HWND)SendMessageA(hComboEx, CBEM_GETCOMBOCONTROL, 0, 0);
+    ok(hCombo != NULL, "Failed to get internal combo\n");
+    hEdit = (HWND)SendMessageA(hComboEx, CBEM_GETEDITCONTROL, 0, 0);
+    ok(hEdit != NULL, "Failed to get internal edit\n");
+
+    if (pSetWindowSubclass)
+    {
+        ok(GetPropA(hCombo, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n");
+        ok(GetPropA(hEdit, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n");
+    }
+
+    DestroyWindow(hComboEx);
+}
+
+static const struct message test_setitem_edit_seq[] = {
+    { WM_SETTEXT, sent|id, 0, 0, EDITBOX_ID },
+    { EM_SETSEL, sent|id|wparam|lparam, 0,  0, EDITBOX_ID },
+    { EM_SETSEL, sent|id|wparam|lparam, 0, -1, EDITBOX_ID },
+    { 0 }
+};
+
+static void test_comboex_get_set_item(void)
+{
+    char textA[] = "test";
+    HWND hComboEx;
+    COMBOBOXEXITEMA item;
+    BOOL ret;
+
+    hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
+
+    subclass_editbox(hComboEx);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    memset(&item, 0, sizeof(item));
+    item.mask = CBEIF_TEXT;
+    item.pszText = textA;
+    item.iItem = -1;
+    ret = SendMessageA(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item);
+    expect(TRUE, ret);
+
+    ok_sequence(sequences, EDITBOX_SEQ_INDEX, test_setitem_edit_seq, "set item data for edit", FALSE);
+
+    /* get/set lParam */
+    item.mask = CBEIF_LPARAM;
+    item.iItem = -1;
+    item.lParam = 0xdeadbeef;
+    ret = SendMessageA(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item);
+    expect(TRUE, ret);
+    ok(item.lParam == 0, "Expected zero, got %lx\n", item.lParam);
+
+    item.lParam = 0x1abe11ed;
+    ret = SendMessageA(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item);
+    expect(TRUE, ret);
+
+    item.lParam = 0;
+    ret = SendMessageA(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item);
+    expect(TRUE, ret);
+    ok(item.lParam == 0x1abe11ed, "Expected 0x1abe11ed, got %lx\n", item.lParam);
+
+    DestroyWindow(hComboEx);
+}
+
+static HWND create_combobox(DWORD style)
+{
+    return CreateWindowA("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 100, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
+}
+
+static int font_height(HFONT hFont)
+{
+    TEXTMETRICA tm;
+    HFONT hFontOld;
+    HDC hDC;
+
+    hDC = CreateCompatibleDC(NULL);
+    hFontOld = SelectObject(hDC, hFont);
+    GetTextMetricsA(hDC, &tm);
+    SelectObject(hDC, hFontOld);
+    DeleteDC(hDC);
+
+    return tm.tmHeight;
+}
+
+static void test_combo_setitemheight(DWORD style)
+{
+    HWND hCombo = create_combobox(style);
+    RECT r;
+    int i;
+
+    GetClientRect(hCombo, &r);
+    expect_rect(r, 0, 0, 100, font_height(GetStockObject(SYSTEM_FONT)) + 8);
+    SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
+    MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
+    todo_wine expect_rect(r, 5, 5, 105, 105);
+
+    for (i = 1; i < 30; i++)
+    {
+        SendMessageA(hCombo, CB_SETITEMHEIGHT, -1, i);
+        GetClientRect(hCombo, &r);
+        ok((r.bottom - r.top) == (i + 6), "Unexpected client rect height.\n");
+    }
+
+    DestroyWindow(hCombo);
+}
+
+static void test_combo_setfont(DWORD style)
+{
+    HFONT hFont1, hFont2;
+    HWND hCombo;
+    RECT r;
+    int i;
+
+    hCombo = create_combobox(style);
+    hFont1 = CreateFontA(10, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
+    hFont2 = CreateFontA(8, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
+
+    GetClientRect(hCombo, &r);
+    expect_rect(r, 0, 0, 100, font_height(GetStockObject(SYSTEM_FONT)) + 8);
+    SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
+    MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
+    todo_wine expect_rect(r, 5, 5, 105, 105);
+
+    /* The size of the dropped control is initially equal to the size
+       of the window when it was created.  The size of the calculated
+       dropped area changes only by how much the selection area
+       changes, not by how much the list area changes.  */
+    if (font_height(hFont1) == 10 && font_height(hFont2) == 8)
+    {
+        SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
+        GetClientRect(hCombo, &r);
+        expect_rect(r, 0, 0, 100, 18);
+        SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
+        MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
+        todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont1)));
+
+        SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont2, FALSE);
+        GetClientRect(hCombo, &r);
+        expect_rect(r, 0, 0, 100, 16);
+        SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
+        MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
+        todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont2)));
+
+        SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
+        GetClientRect(hCombo, &r);
+        expect_rect(r, 0, 0, 100, 18);
+        SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
+        MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
+        todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont1)));
+    }
+    else
+    {
+        ok(0, "Expected Marlett font heights 10/8, got %d/%d\n",
+           font_height(hFont1), font_height(hFont2));
+    }
+
+    for (i = 1; i < 30; i++)
+    {
+        HFONT hFont = CreateFontA(i, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
+        int height = font_height(hFont);
+
+        SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont, FALSE);
+        GetClientRect(hCombo, &r);
+        ok((r.bottom - r.top) == (height + 8), "Unexpected client rect height.\n");
+        SendMessageA(hCombo, WM_SETFONT, 0, FALSE);
+        DeleteObject(hFont);
+    }
+
+    DestroyWindow(hCombo);
+    DeleteObject(hFont1);
+    DeleteObject(hFont2);
+}
+
+static LRESULT (CALLBACK *old_parent_proc)(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+static LPCSTR expected_edit_text;
+static LPCSTR expected_list_text;
+static BOOL selchange_fired;
+
+static LRESULT CALLBACK parent_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+    case WM_COMMAND:
+        switch (wparam)
+        {
+            case MAKEWPARAM(COMBO_ID, CBN_SELCHANGE):
+            {
+                HWND hCombo = (HWND)lparam;
+                char list[20], edit[20];
+                int idx;
+
+                memset(list, 0, sizeof(list));
+                memset(edit, 0, sizeof(edit));
+
+                idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0);
+                SendMessageA(hCombo, CB_GETLBTEXT, idx, (LPARAM)list);
+                SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
+
+                ok(!strcmp(edit, expected_edit_text), "edit: got %s, expected %s\n",
+                    edit, expected_edit_text);
+                ok(!strcmp(list, expected_list_text), "list: got %s, expected %s\n",
+                    list, expected_list_text);
+
+                selchange_fired = TRUE;
+            }
+            break;
+        }
+        break;
+    }
+
+    return CallWindowProcA(old_parent_proc, hwnd, msg, wparam, lparam);
+}
+
+static void test_selection(DWORD style, const char * const text[], const int *edit, const int *list)
+{
+    HWND hCombo;
+    INT idx;
+
+    hCombo = create_combobox(style);
+
+    SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[0]);
+    SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[1]);
+    SendMessageA(hCombo, CB_SETCURSEL, -1, 0);
+
+    old_parent_proc = (void *)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc);
+
+    idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0);
+    ok(idx == -1, "expected selection -1, got %d\n", idx);
+
+    /* keyboard navigation */
+
+    expected_list_text = text[list[0]];
+    expected_edit_text = text[edit[0]];
+    selchange_fired = FALSE;
+    SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0);
+    ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
+
+    expected_list_text = text[list[1]];
+    expected_edit_text = text[edit[1]];
+    selchange_fired = FALSE;
+    SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0);
+    ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
+
+    expected_list_text = text[list[2]];
+    expected_edit_text = text[edit[2]];
+    selchange_fired = FALSE;
+    SendMessageA(hCombo, WM_KEYDOWN, VK_UP, 0);
+    ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
+
+    /* programmatic navigation */
+
+    expected_list_text = text[list[3]];
+    expected_edit_text = text[edit[3]];
+    selchange_fired = FALSE;
+    SendMessageA(hCombo, CB_SETCURSEL, list[3], 0);
+    ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
+
+    expected_list_text = text[list[4]];
+    expected_edit_text = text[edit[4]];
+    selchange_fired = FALSE;
+    SendMessageA(hCombo, CB_SETCURSEL, list[4], 0);
+    ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
+
+    SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
+    DestroyWindow(hCombo);
+}
+
+static void test_combo_CBN_SELCHANGE(void)
+{
+    static const char * const text[] = { "alpha", "beta", "" };
+    static const int sel_1[] = { 2, 0, 1, 0, 1 };
+    static const int sel_2[] = { 0, 1, 0, 0, 1 };
+
+    test_selection(CBS_SIMPLE, text, sel_1, sel_2);
+    test_selection(CBS_DROPDOWN, text, sel_1, sel_2);
+    test_selection(CBS_DROPDOWNLIST, text, sel_2, sel_2);
+}
+
+static void test_combo_changesize(DWORD style)
+{
+    INT ddheight, clheight, ddwidth, clwidth;
+    HWND hCombo;
+    RECT rc;
+
+    hCombo = create_combobox(style);
+
+    /* get initial measurements */
+    GetClientRect( hCombo, &rc);
+    clheight = rc.bottom - rc.top;
+    clwidth = rc.right - rc.left;
+    SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
+    ddheight = rc.bottom - rc.top;
+    ddwidth = rc.right - rc.left;
+    /* use MoveWindow to move & resize the combo */
+    /* first make it slightly smaller */
+    MoveWindow( hCombo, 10, 10, clwidth - 2, clheight - 2, TRUE);
+    GetClientRect( hCombo, &rc);
+    ok( rc.right - rc.left == clwidth - 2, "clientrect width is %d vs %d\n",
+            rc.right - rc.left, clwidth - 2);
+    ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n",
+                rc.bottom - rc.top, clheight);
+    SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
+    ok( rc.right - rc.left == clwidth - 2, "drop-down rect width is %d vs %d\n",
+            rc.right - rc.left, clwidth - 2);
+    ok( rc.bottom - rc.top == ddheight, "drop-down rect height is %d vs %d\n",
+            rc.bottom - rc.top, ddheight);
+    ok( rc.right - rc.left == ddwidth -2, "drop-down rect width is %d vs %d\n",
+            rc.right - rc.left, ddwidth - 2);
+    /* new cx, cy is slightly bigger than the initial values */
+    MoveWindow( hCombo, 10, 10, clwidth + 2, clheight + 2, TRUE);
+    GetClientRect( hCombo, &rc);
+    ok( rc.right - rc.left == clwidth + 2, "clientrect width is %d vs %d\n",
+            rc.right - rc.left, clwidth + 2);
+    ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n",
+            rc.bottom - rc.top, clheight);
+    SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
+    ok( rc.right - rc.left == clwidth + 2, "drop-down rect width is %d vs %d\n",
+            rc.right - rc.left, clwidth + 2);
+    todo_wine {
+        ok( rc.bottom - rc.top == clheight + 2, "drop-down rect height is %d vs %d\n",
+                rc.bottom - rc.top, clheight + 2);
+    }
+
+    ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, -1, 0);
+    ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
+    ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
+    ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
+
+    ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0);
+    ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
+    ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
+    ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
+
+    ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth - 1, 0);
+    ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
+    ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
+    ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
+
+    ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth << 1, 0);
+    ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
+    ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
+    ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
+
+    ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0);
+    ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
+    ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
+    ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
+
+    ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 1, 0);
+    ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
+    ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
+    ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
+
+    DestroyWindow(hCombo);
+}
+
+static void test_combo_editselection(void)
+{
+    COMBOBOXINFO cbInfo;
+    INT start, end;
+    char edit[20];
+    HWND hCombo;
+    HWND hEdit;
+    DWORD len;
+
+    /* Build a combo */
+    hCombo = create_combobox(CBS_SIMPLE);
+
+    get_combobox_info(hCombo, &cbInfo);
+    hEdit = cbInfo.hwndItem;
+
+    /* Initially combo selection is empty*/
+    len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
+    ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
+    ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len));
+
+    /* Set some text, and press a key to replace it */
+    edit[0] = 0x00;
+    SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason1");
+    SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
+    ok(strcmp(edit, "Jason1")==0, "Unexpected text retrieved %s\n", edit);
+
+    /* Now what is the selection - still empty */
+    SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
+    ok(start==0, "Unexpected start position for selection %d\n", start);
+    ok(end==0, "Unexpected end position for selection %d\n", end);
+    len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
+    ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
+    ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len));
+
+    /* Give it focus, and it gets selected */
+    SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
+    SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
+    ok(start==0, "Unexpected start position for selection %d\n", start);
+    ok(end==6, "Unexpected end position for selection %d\n", end);
+    len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
+    ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
+    ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len));
+
+    /* Now emulate a key press */
+    edit[0] = 0x00;
+    SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001);
+    SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
+    ok(strcmp(edit, "A")==0, "Unexpected text retrieved %s\n", edit);
+
+    len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
+    ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len));
+    ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len));
+
+    /* Now what happens when it gets more focus a second time - it doesn't reselect */
+    SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
+    len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
+    ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len));
+    ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len));
+    DestroyWindow(hCombo);
+
+    /* Start again - Build a combo */
+    hCombo = create_combobox(CBS_SIMPLE);
+    get_combobox_info(hCombo, &cbInfo);
+    hEdit = cbInfo.hwndItem;
+
+    /* Set some text and give focus so it gets selected */
+    edit[0] = 0x00;
+    SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason2");
+    SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
+    ok(strcmp(edit, "Jason2")==0, "Unexpected text retrieved %s\n", edit);
+
+    SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
+
+    /* Now what is the selection */
+    SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
+    ok(start==0, "Unexpected start position for selection %d\n", start);
+    ok(end==6, "Unexpected end position for selection %d\n", end);
+    len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
+    ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
+    ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len));
+
+    /* Now change the selection to the apparently invalid start -1, end -1 and
+       show it means no selection (ie start -1) but cursor at end              */
+    SendMessageA(hCombo, CB_SETEDITSEL, 0, -1);
+    edit[0] = 0x00;
+    SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001);
+    SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
+    ok(strcmp(edit, "Jason2A")==0, "Unexpected text retrieved %s\n", edit);
+    DestroyWindow(hCombo);
+}
+
+static WNDPROC edit_window_proc;
+static long setsel_start = 1, setsel_end = 1;
+static HWND hCBN_SetFocus, hCBN_KillFocus;
+
+static LRESULT CALLBACK combobox_subclass_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    if (msg == EM_SETSEL)
+    {
+        setsel_start = wParam;
+        setsel_end = lParam;
+    }
+    return CallWindowProcA(edit_window_proc, hwnd, msg, wParam, lParam);
+}
+
+static LRESULT CALLBACK test_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    switch (msg)
+    {
+    case WM_COMMAND:
+        switch (HIWORD(wParam))
+        {
+        case CBN_SETFOCUS:
+            hCBN_SetFocus = (HWND)lParam;
+            break;
+        case CBN_KILLFOCUS:
+            hCBN_KillFocus = (HWND)lParam;
+            break;
+        }
+        break;
+    case WM_NEXTDLGCTL:
+        SetFocus((HWND)wParam);
+        break;
+    }
+    return CallWindowProcA(old_parent_proc, hwnd, msg, wParam, lParam);
+}
+
+static void test_combo_editselection_focus(DWORD style)
+{
+    static const char wine_test[] = "Wine Test";
+    HWND hCombo, hEdit, hButton;
+    char buffer[16] = {0};
+    COMBOBOXINFO cbInfo;
+    DWORD len;
+
+    hCombo = create_combobox(style);
+    get_combobox_info(hCombo, &cbInfo);
+    hEdit = cbInfo.hwndItem;
+
+    hButton = CreateWindowA("Button", "OK", WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,
+                            5, 50, 100, 20, hMainWnd, NULL,
+                            (HINSTANCE)GetWindowLongPtrA(hMainWnd, GWLP_HINSTANCE), NULL);
+
+    old_parent_proc = (WNDPROC)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)test_window_proc);
+    edit_window_proc = (WNDPROC)SetWindowLongPtrA(hEdit, GWLP_WNDPROC, (ULONG_PTR)combobox_subclass_proc);
+
+    SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
+    ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
+    todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
+    ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus);
+    ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n");
+
+    SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE);
+    ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
+    todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
+    ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus);
+    ok(GetFocus() == hButton, "hButton should have keyboard focus\n");
+
+    SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)wine_test);
+    SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hCombo, TRUE);
+    ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
+    todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
+    ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus);
+    ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n");
+    SendMessageA(hCombo, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
+    ok(!strcmp(buffer, wine_test), "Unexpected text in edit control; got '%s'\n", buffer);
+
+    SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE);
+    ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
+    todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
+    ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus);
+    ok(GetFocus() == hButton, "hButton should have keyboard focus\n");
+    len = SendMessageA(hCombo, CB_GETEDITSEL, 0, 0);
+    ok(len == 0, "Unexpected text selection; start: %u, end: %u\n", LOWORD(len), HIWORD(len));
+
+    SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
+    DestroyWindow(hButton);
+    DestroyWindow(hCombo);
+}
+
+static void test_combo_listbox_styles(DWORD cb_style)
+{
+    DWORD style, exstyle, expect_style, expect_exstyle;
+    COMBOBOXINFO info;
+    HWND combo;
+
+    expect_style = WS_CHILD|WS_CLIPSIBLINGS|LBS_COMBOBOX|LBS_HASSTRINGS|LBS_NOTIFY;
+    if (cb_style == CBS_SIMPLE)
+    {
+        expect_style |= WS_VISIBLE;
+        expect_exstyle = WS_EX_CLIENTEDGE;
+    }
+    else
+    {
+        expect_style |= WS_BORDER;
+        expect_exstyle = WS_EX_TOOLWINDOW;
+    }
+
+    combo = create_combobox(cb_style);
+    get_combobox_info(combo, &info);
+
+    style = GetWindowLongW( info.hwndList, GWL_STYLE );
+    exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
+    ok(style == expect_style, "%08x: got %08x\n", cb_style, style);
+    ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle);
+
+    if (cb_style != CBS_SIMPLE)
+        expect_exstyle |= WS_EX_TOPMOST;
+
+    SendMessageW(combo, CB_SHOWDROPDOWN, TRUE, 0 );
+    style = GetWindowLongW( info.hwndList, GWL_STYLE );
+    exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
+    ok(style == (expect_style | WS_VISIBLE), "%08x: got %08x\n", cb_style, style);
+    ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle);
+
+    SendMessageW(combo, CB_SHOWDROPDOWN, FALSE, 0 );
+    style = GetWindowLongW( info.hwndList, GWL_STYLE );
+    exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
+    ok(style == expect_style, "%08x: got %08x\n", cb_style, style);
+    ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle);
+
+    DestroyWindow(combo);
+}
+
+static void test_combo_WS_VSCROLL(void)
+{
+    HWND hCombo, hList;
+    COMBOBOXINFO info;
+    DWORD style;
+    int i;
+
+    hCombo = create_combobox(CBS_DROPDOWNLIST);
+
+    get_combobox_info(hCombo, &info);
+    hList = info.hwndList;
+
+    for (i = 0; i < 3; i++)
+    {
+        char buffer[2];
+        sprintf(buffer, "%d", i);
+        SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer);
+    }
+
+    style = GetWindowLongA(info.hwndList, GWL_STYLE);
+    SetWindowLongA(hList, GWL_STYLE, style | WS_VSCROLL);
+
+    SendMessageA(hCombo, CB_SHOWDROPDOWN, TRUE, 0);
+    SendMessageA(hCombo, CB_SHOWDROPDOWN, FALSE, 0);
+
+    style = GetWindowLongA(hList, GWL_STYLE);
+    ok((style & WS_VSCROLL) != 0, "Style does not include WS_VSCROLL\n");
+
+    DestroyWindow(hCombo);
+}
+
+START_TEST(combo)
+{
+    ULONG_PTR ctx_cookie;
+    HANDLE hCtx;
+
+    if (!init())
+        return;
+
+    init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    /* ComboBoxEx32 tests. */
+    test_comboex();
+    test_comboex_WM_LBUTTONDOWN();
+    test_comboex_CB_GETLBTEXT();
+    test_comboex_WM_WINDOWPOSCHANGING();
+    test_comboex_subclass();
+    test_comboex_get_set_item();
+
+    if (!load_v6_module(&ctx_cookie, &hCtx))
+    {
+        cleanup();
+        return;
+    }
+
+    /* ComboBox control tests. */
+    test_combo_WS_VSCROLL();
+    test_combo_setfont(CBS_DROPDOWN);
+    test_combo_setfont(CBS_DROPDOWNLIST);
+    test_combo_setitemheight(CBS_DROPDOWN);
+    test_combo_setitemheight(CBS_DROPDOWNLIST);
+    test_combo_CBN_SELCHANGE();
+    test_combo_changesize(CBS_DROPDOWN);
+    test_combo_changesize(CBS_DROPDOWNLIST);
+    test_combo_editselection();
+    test_combo_editselection_focus(CBS_SIMPLE);
+    test_combo_editselection_focus(CBS_DROPDOWN);
+    test_combo_listbox_styles(CBS_SIMPLE);
+    test_combo_listbox_styles(CBS_DROPDOWN);
+    test_combo_listbox_styles(CBS_DROPDOWNLIST);
+
+    cleanup();
+    unload_v6_module(ctx_cookie, hCtx);
+}
diff --git a/modules/rostests/winetests/comctl32/comboex.c b/modules/rostests/winetests/comctl32/comboex.c
deleted file mode 100644 (file)
index 947a871..0000000
+++ /dev/null
@@ -1,608 +0,0 @@
-/* Unit test suite for comboex control.
- *
- * Copyright 2005 Jason Edmeades
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#include "precomp.h"
-
-#define EDITBOX_SEQ_INDEX  0
-#define NUM_MSG_SEQUENCES  1
-
-#define EDITBOX_ID         0
-
-#define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
-
-static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
-
-static HWND hComboExParentWnd;
-static HINSTANCE hMainHinst;
-static const char ComboExTestClass[] = "ComboExTestClass";
-
-static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
-
-#define MAX_CHARS 100
-static char *textBuffer = NULL;
-
-static BOOL received_end_edit = FALSE;
-
-static HWND createComboEx(DWORD style) {
-   return CreateWindowExA(0, WC_COMBOBOXEXA, NULL, style, 0, 0, 300, 300,
-            hComboExParentWnd, NULL, hMainHinst, NULL);
-}
-
-static LONG addItem(HWND cbex, int idx, const char *text) {
-    COMBOBOXEXITEMA cbexItem;
-    memset(&cbexItem, 0x00, sizeof(cbexItem));
-    cbexItem.mask = CBEIF_TEXT;
-    cbexItem.iItem = idx;
-    cbexItem.pszText    = (char*)text;
-    cbexItem.cchTextMax = 0;
-    return SendMessageA(cbex, CBEM_INSERTITEMA, 0, (LPARAM)&cbexItem);
-}
-
-static LONG setItem(HWND cbex, int idx, const char *text) {
-    COMBOBOXEXITEMA cbexItem;
-    memset(&cbexItem, 0x00, sizeof(cbexItem));
-    cbexItem.mask = CBEIF_TEXT;
-    cbexItem.iItem = idx;
-    cbexItem.pszText    = (char*)text;
-    cbexItem.cchTextMax = 0;
-    return SendMessageA(cbex, CBEM_SETITEMA, 0, (LPARAM)&cbexItem);
-}
-
-static LONG delItem(HWND cbex, int idx) {
-    return SendMessageA(cbex, CBEM_DELETEITEM, idx, 0);
-}
-
-static LONG getItem(HWND cbex, int idx, COMBOBOXEXITEMA *cbItem) {
-    memset(cbItem, 0x00, sizeof(COMBOBOXEXITEMA));
-    cbItem->mask = CBEIF_TEXT;
-    cbItem->pszText      = textBuffer;
-    cbItem->iItem        = idx;
-    cbItem->cchTextMax   = 100;
-    return SendMessageA(cbex, CBEM_GETITEMA, 0, (LPARAM)cbItem);
-}
-
-static LRESULT WINAPI editbox_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
-{
-    WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
-    static LONG defwndproc_counter = 0;
-    struct message msg = { 0 };
-    LRESULT ret;
-
-    msg.message = message;
-    msg.flags = sent|wparam|lparam;
-    if (defwndproc_counter) msg.flags |= defwinproc;
-    msg.wParam = wParam;
-    msg.lParam = lParam;
-    msg.id     = EDITBOX_ID;
-
-    if (message != WM_PAINT &&
-        message != WM_ERASEBKGND &&
-        message != WM_NCPAINT &&
-        message != WM_NCHITTEST &&
-        message != WM_GETTEXT &&
-        message != WM_GETICON &&
-        message != WM_DEVICECHANGE)
-    {
-        add_message(sequences, EDITBOX_SEQ_INDEX, &msg);
-    }
-
-    defwndproc_counter++;
-    ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
-    defwndproc_counter--;
-    return ret;
-}
-
-static HWND subclass_editbox(HWND hwndComboEx)
-{
-    WNDPROC oldproc;
-    HWND hwnd;
-
-    hwnd = (HWND)SendMessageA(hwndComboEx, CBEM_GETEDITCONTROL, 0, 0);
-    oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
-                                         (LONG_PTR)editbox_subclass_proc);
-    SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc);
-
-    return hwnd;
-}
-
-static void test_comboboxex(void) {
-    HWND myHwnd = 0;
-    LONG res;
-    COMBOBOXEXITEMA cbexItem;
-    static const char *first_item  = "First Item",
-                *second_item = "Second Item",
-                *third_item  = "Third Item",
-                *middle_item = "Between First and Second Items",
-                *replacement_item = "Between First and Second Items",
-                *out_of_range_item = "Out of Range Item";
-
-    /* Allocate space for result */
-    textBuffer = HeapAlloc(GetProcessHeap(), 0, MAX_CHARS);
-
-    /* Basic comboboxex test */
-    myHwnd = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
-
-    /* Add items onto the end of the combobox */
-    res = addItem(myHwnd, -1, first_item);
-    ok(res == 0, "Adding simple item failed (%d)\n", res);
-    res = addItem(myHwnd, -1, second_item);
-    ok(res == 1, "Adding simple item failed (%d)\n", res);
-    res = addItem(myHwnd, 2, third_item);
-    ok(res == 2, "Adding simple item failed (%d)\n", res);
-    res = addItem(myHwnd, 1, middle_item);
-    ok(res == 1, "Inserting simple item failed (%d)\n", res);
-
-    /* Add an item completely out of range */
-    res = addItem(myHwnd, 99, out_of_range_item);
-    ok(res == -1, "Adding using out of range index worked unexpectedly (%d)\n", res);
-    res = addItem(myHwnd, 5, out_of_range_item);
-    ok(res == -1, "Adding using out of range index worked unexpectedly (%d)\n", res);
-    /* Removed: Causes traps on Windows XP
-       res = addItem(myHwnd, -2, "Out Of Range Item");
-       ok(res == -1, "Adding out of range worked unexpectedly (%ld)\n", res);
-     */
-
-    /* Get an item completely out of range */ 
-    res = getItem(myHwnd, 99, &cbexItem); 
-    ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText);
-    res = getItem(myHwnd, 4, &cbexItem); 
-    ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText);
-    res = getItem(myHwnd, -2, &cbexItem); 
-    ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText);
-
-    /* Get an item in range */ 
-    res = getItem(myHwnd, 0, &cbexItem); 
-    ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res);
-    ok(strcmp(first_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
-
-    res = getItem(myHwnd, 1, &cbexItem); 
-    ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res);
-    ok(strcmp(middle_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
-
-    res = getItem(myHwnd, 2, &cbexItem); 
-    ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res);
-    ok(strcmp(second_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
-
-    res = getItem(myHwnd, 3, &cbexItem); 
-    ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res);
-    ok(strcmp(third_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
-
-    /* Set an item completely out of range */ 
-    res = setItem(myHwnd, 99, replacement_item); 
-    ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res);
-    res = setItem(myHwnd, 4, replacement_item); 
-    ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res);
-    res = setItem(myHwnd, -2, replacement_item); 
-    ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res);
-
-    /* Set an item in range */ 
-    res = setItem(myHwnd, 0, replacement_item);
-    ok(res != 0, "Setting first item failed (%d)\n", res);
-    res = setItem(myHwnd, 3, replacement_item);
-    ok(res != 0, "Setting last item failed (%d)\n", res);
-
-    /* Remove items completely out of range (4 items in control at this point) */
-    res = delItem(myHwnd, -1);
-    ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res);
-    res = delItem(myHwnd, 4);
-    ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res);
-
-    /* Remove items in range (4 items in control at this point) */
-    res = delItem(myHwnd, 3);
-    ok(res == 3, "Deleting using out of range index failed (%d)\n", res);
-    res = delItem(myHwnd, 0);
-    ok(res == 2, "Deleting using out of range index failed (%d)\n", res);
-    res = delItem(myHwnd, 0);
-    ok(res == 1, "Deleting using out of range index failed (%d)\n", res);
-    res = delItem(myHwnd, 0);
-    ok(res == 0, "Deleting using out of range index failed (%d)\n", res);
-
-    /* Remove from an empty box */
-    res = delItem(myHwnd, 0);
-    ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res);
-
-
-    /* Cleanup */
-    HeapFree(GetProcessHeap(), 0, textBuffer);
-    DestroyWindow(myHwnd);
-}
-
-static void test_WM_LBUTTONDOWN(void)
-{
-    HWND hComboEx, hCombo, hEdit, hList;
-    COMBOBOXINFO cbInfo;
-    UINT x, y, item_height;
-    LRESULT result;
-    UINT i;
-    int idx;
-    RECT rect;
-    WCHAR buffer[3];
-    static const UINT choices[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72};
-    static const WCHAR stringFormat[] = {'%','2','d','\0'};
-    BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
-
-    pGetComboBoxInfo = (void*)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
-    if (!pGetComboBoxInfo){
-        win_skip("GetComboBoxInfo is not available\n");
-        return;
-    }
-
-    hComboEx = CreateWindowExA(0, WC_COMBOBOXEXA, NULL,
-            WS_VISIBLE|WS_CHILD|CBS_DROPDOWN, 0, 0, 200, 150,
-            hComboExParentWnd, NULL, hMainHinst, NULL);
-
-    for (i = 0; i < sizeof(choices)/sizeof(UINT); i++){
-        COMBOBOXEXITEMW cbexItem;
-        wsprintfW(buffer, stringFormat, choices[i]);
-
-        memset(&cbexItem, 0x00, sizeof(cbexItem));
-        cbexItem.mask = CBEIF_TEXT;
-        cbexItem.iItem = i;
-        cbexItem.pszText = buffer;
-        cbexItem.cchTextMax = 0;
-        ok(SendMessageW(hComboEx, CBEM_INSERTITEMW, 0, (LPARAM)&cbexItem) >= 0,
-           "Failed to add item %d\n", i);
-    }
-
-    hCombo = (HWND)SendMessageA(hComboEx, CBEM_GETCOMBOCONTROL, 0, 0);
-    hEdit = (HWND)SendMessageA(hComboEx, CBEM_GETEDITCONTROL, 0, 0);
-
-    cbInfo.cbSize = sizeof(COMBOBOXINFO);
-    result = pGetComboBoxInfo(hCombo, &cbInfo);
-    ok(result, "Failed to get combobox info structure. LastError=%d\n",
-       GetLastError());
-    hList = cbInfo.hwndList;
-
-    ok(GetFocus() == hComboExParentWnd,
-       "Focus not on Main Window, instead on %p\n", GetFocus());
-
-    /* Click on the button to drop down the list */
-    x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2;
-    y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2;
-    result = SendMessageA(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
-    ok(result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
-       GetLastError());
-    ok(GetFocus() == hCombo ||
-       broken(GetFocus() != hCombo), /* win98 */
-       "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
-       GetFocus());
-    ok(SendMessageA(hComboEx, CB_GETDROPPEDSTATE, 0, 0),
-       "The dropdown list should have appeared after clicking the button.\n");
-    idx = SendMessageA(hCombo, CB_GETTOPINDEX, 0, 0);
-    ok(idx == 0, "For TopIndex expected %d, got %d\n", 0, idx);
-
-    result = SendMessageA(hCombo, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
-    ok(result, "WM_LBUTTONUP was not processed. LastError=%d\n",
-       GetLastError());
-    ok(GetFocus() == hCombo ||
-       broken(GetFocus() != hCombo), /* win98 */
-       "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
-       GetFocus());
-
-    /* Click on the 5th item in the list */
-    item_height = SendMessageA(hCombo, CB_GETITEMHEIGHT, 0, 0);
-    ok(GetClientRect(hList, &rect), "Failed to get list's client rect.\n");
-    x = rect.left + (rect.right-rect.left)/2;
-    y = item_height/2 + item_height*4;
-    result = SendMessageA(hList, WM_MOUSEMOVE, 0, MAKELPARAM(x, y));
-    ok(!result, "WM_MOUSEMOVE was not processed. LastError=%d\n",
-       GetLastError());
-    ok(GetFocus() == hCombo ||
-       broken(GetFocus() != hCombo), /* win98 */
-       "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
-       GetFocus());
-
-    result = SendMessageA(hList, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
-    ok(!result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
-       GetLastError());
-    ok(GetFocus() == hCombo ||
-       broken(GetFocus() != hCombo), /* win98 */
-       "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
-       GetFocus());
-    ok(SendMessageA(hComboEx, CB_GETDROPPEDSTATE, 0, 0),
-       "The dropdown list should still be visible.\n");
-
-    result = SendMessageA(hList, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
-    ok(!result, "WM_LBUTTONUP was not processed. LastError=%d\n",
-       GetLastError());
-    todo_wine ok(GetFocus() == hEdit ||
-       broken(GetFocus() == hCombo), /* win98 */
-       "Focus not on ComboBoxEx's Edit Control, instead on %p\n",
-       GetFocus());
-
-    result = SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0);
-    ok(!result ||
-       broken(result != 0), /* win98 */
-       "The dropdown list should have been rolled up.\n");
-    idx = SendMessageA(hComboEx, CB_GETCURSEL, 0, 0);
-    ok(idx == 4 ||
-       broken(idx == -1), /* win98 */
-       "Current Selection: expected %d, got %d\n", 4, idx);
-    ok(received_end_edit, "Expected to receive a CBEN_ENDEDIT message\n");
-
-    SetFocus( hComboExParentWnd );
-    ok( GetFocus() == hComboExParentWnd, "got %p\n", GetFocus() );
-    SetFocus( hComboEx );
-    ok( GetFocus() == hEdit, "got %p\n", GetFocus() );
-
-    DestroyWindow(hComboEx);
-}
-
-static void test_CB_GETLBTEXT(void)
-{
-    HWND hCombo;
-    CHAR buff[1];
-    COMBOBOXEXITEMA item;
-    LRESULT ret;
-
-    hCombo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
-
-    /* set text to null */
-    addItem(hCombo, 0, NULL);
-
-    buff[0] = 'a';
-    item.mask = CBEIF_TEXT;
-    item.iItem = 0;
-    item.pszText = buff;
-    item.cchTextMax = 1;
-    ret = SendMessageA(hCombo, CBEM_GETITEMA, 0, (LPARAM)&item);
-    ok(ret != 0, "CBEM_GETITEM failed\n");
-    ok(buff[0] == 0, "\n");
-
-    ret = SendMessageA(hCombo, CB_GETLBTEXTLEN, 0, 0);
-    ok(ret == 0, "Expected zero length\n");
-
-    ret = SendMessageA(hCombo, CB_GETLBTEXTLEN, 0, 0);
-    ok(ret == 0, "Expected zero length\n");
-
-    buff[0] = 'a';
-    ret = SendMessageA(hCombo, CB_GETLBTEXT, 0, (LPARAM)buff);
-    ok(ret == 0, "Expected zero length\n");
-    ok(buff[0] == 0, "Expected null terminator as a string, got %s\n", buff);
-
-    DestroyWindow(hCombo);
-}
-
-static void test_WM_WINDOWPOSCHANGING(void)
-{
-    HWND hCombo;
-    WINDOWPOS wp;
-    RECT rect;
-    int combo_height;
-    int ret;
-
-    hCombo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
-    ok(hCombo != NULL, "createComboEx failed\n");
-    ret = GetWindowRect(hCombo, &rect);
-    ok(ret, "GetWindowRect failed\n");
-    combo_height = rect.bottom - rect.top;
-    ok(combo_height > 0, "wrong combo height\n");
-
-    /* Test height > combo_height */
-    wp.x = rect.left;
-    wp.y = rect.top;
-    wp.cx = (rect.right - rect.left);
-    wp.cy = combo_height * 2;
-    wp.flags = 0;
-    wp.hwnd = hCombo;
-    wp.hwndInsertAfter = NULL;
-
-    ret = SendMessageA(hCombo, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp);
-    ok(ret == 0, "expected 0, got %x\n", ret);
-    ok(wp.cy == combo_height,
-            "Expected height %d, got %d\n", combo_height, wp.cy);
-
-    /* Test height < combo_height */
-    wp.x = rect.left;
-    wp.y = rect.top;
-    wp.cx = (rect.right - rect.left);
-    wp.cy = combo_height / 2;
-    wp.flags = 0;
-    wp.hwnd = hCombo;
-    wp.hwndInsertAfter = NULL;
-
-    ret = SendMessageA(hCombo, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp);
-    ok(ret == 0, "expected 0, got %x\n", ret);
-    ok(wp.cy == combo_height,
-            "Expected height %d, got %d\n", combo_height, wp.cy);
-
-    ret = DestroyWindow(hCombo);
-    ok(ret, "DestroyWindow failed\n");
-}
-
-static LRESULT ComboExTestOnNotify(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
-    NMHDR *hdr = (NMHDR*)lParam;
-    switch(hdr->code){
-    case CBEN_ENDEDITA:
-        {
-            NMCBEENDEDITA *edit_info = (NMCBEENDEDITA*)hdr;
-            if(edit_info->iWhy==CBENF_DROPDOWN){
-                received_end_edit = TRUE;
-            }
-            break;
-        }
-    case CBEN_ENDEDITW:
-        {
-            NMCBEENDEDITW *edit_info = (NMCBEENDEDITW*)hdr;
-            if(edit_info->iWhy==CBENF_DROPDOWN){
-                received_end_edit = TRUE;
-            }
-            break;
-        }
-    }
-    return 0;
-}
-
-static LRESULT CALLBACK ComboExTestWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
-    switch(msg) {
-
-    case WM_DESTROY:
-        PostQuitMessage(0);
-        break;
-    case WM_NOTIFY:
-        return ComboExTestOnNotify(hWnd,msg,wParam,lParam);
-    default:
-        return DefWindowProcA(hWnd, msg, wParam, lParam);
-    }
-    
-    return 0L;
-}
-
-static BOOL init(void)
-{
-    HMODULE hComctl32;
-    BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
-    WNDCLASSA wc;
-    INITCOMMONCONTROLSEX iccex;
-
-    hComctl32 = GetModuleHandleA("comctl32.dll");
-    pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
-    if (!pInitCommonControlsEx)
-    {
-        win_skip("InitCommonControlsEx() is missing. Skipping the tests\n");
-        return FALSE;
-    }
-    iccex.dwSize = sizeof(iccex);
-    iccex.dwICC  = ICC_USEREX_CLASSES;
-    pInitCommonControlsEx(&iccex);
-
-    pSetWindowSubclass = (void*)GetProcAddress(hComctl32, (LPSTR)410);
-
-    wc.style = CS_HREDRAW | CS_VREDRAW;
-    wc.cbClsExtra = 0;
-    wc.cbWndExtra = 0;
-    wc.hInstance = GetModuleHandleA(NULL);
-    wc.hIcon = NULL;
-    wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
-    wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
-    wc.lpszMenuName = NULL;
-    wc.lpszClassName = ComboExTestClass;
-    wc.lpfnWndProc = ComboExTestWndProc;
-    RegisterClassA(&wc);
-
-    hComboExParentWnd = CreateWindowExA(0, ComboExTestClass, "ComboEx test", WS_OVERLAPPEDWINDOW|WS_VISIBLE,
-      CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, GetModuleHandleA(NULL), 0);
-    ok(hComboExParentWnd != NULL, "failed to create parent window\n");
-
-    hMainHinst = GetModuleHandleA(NULL);
-
-    return hComboExParentWnd != NULL;
-}
-
-static void cleanup(void)
-{
-    MSG msg;
-    
-    PostMessageA(hComboExParentWnd, WM_CLOSE, 0, 0);
-    while (GetMessageA(&msg,0,0,0)) {
-        TranslateMessage(&msg);
-        DispatchMessageA(&msg);
-    }
-    
-    DestroyWindow(hComboExParentWnd);
-    UnregisterClassA(ComboExTestClass, GetModuleHandleA(NULL));
-}
-
-static void test_comboboxex_subclass(void)
-{
-    HWND hComboEx, hCombo, hEdit;
-
-    hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
-
-    hCombo = (HWND)SendMessageA(hComboEx, CBEM_GETCOMBOCONTROL, 0, 0);
-    ok(hCombo != NULL, "Failed to get internal combo\n");
-    hEdit = (HWND)SendMessageA(hComboEx, CBEM_GETEDITCONTROL, 0, 0);
-    ok(hEdit != NULL, "Failed to get internal edit\n");
-
-    if (pSetWindowSubclass)
-    {
-        ok(GetPropA(hCombo, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n");
-        ok(GetPropA(hEdit, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n");
-    }
-
-    DestroyWindow(hComboEx);
-}
-
-static const struct message test_setitem_edit_seq[] = {
-    { WM_SETTEXT, sent|id, 0, 0, EDITBOX_ID },
-    { EM_SETSEL, sent|id|wparam|lparam, 0,  0, EDITBOX_ID },
-    { EM_SETSEL, sent|id|wparam|lparam, 0, -1, EDITBOX_ID },
-    { 0 }
-};
-
-static void test_get_set_item(void)
-{
-    char textA[] = "test";
-    HWND hComboEx;
-    COMBOBOXEXITEMA item;
-    BOOL ret;
-
-    hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
-
-    subclass_editbox(hComboEx);
-
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
-
-    memset(&item, 0, sizeof(item));
-    item.mask = CBEIF_TEXT;
-    item.pszText = textA;
-    item.iItem = -1;
-    ret = SendMessageA(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item);
-    expect(TRUE, ret);
-
-    ok_sequence(sequences, EDITBOX_SEQ_INDEX, test_setitem_edit_seq, "set item data for edit", FALSE);
-
-    /* get/set lParam */
-    item.mask = CBEIF_LPARAM;
-    item.iItem = -1;
-    item.lParam = 0xdeadbeef;
-    ret = SendMessageA(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item);
-    expect(TRUE, ret);
-    ok(item.lParam == 0, "Expected zero, got %lx\n", item.lParam);
-
-    item.lParam = 0x1abe11ed;
-    ret = SendMessageA(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item);
-    expect(TRUE, ret);
-
-    item.lParam = 0;
-    ret = SendMessageA(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item);
-    expect(TRUE, ret);
-    ok(item.lParam == 0x1abe11ed, "Expected 0x1abe11ed, got %lx\n", item.lParam);
-
-    DestroyWindow(hComboEx);
-}
-
-START_TEST(comboex)
-{
-    if (!init())
-        return;
-
-    init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
-
-    test_comboboxex();
-    test_WM_LBUTTONDOWN();
-    test_CB_GETLBTEXT();
-    test_WM_WINDOWPOSCHANGING();
-    test_comboboxex_subclass();
-    test_get_set_item();
-
-    cleanup();
-}
diff --git a/modules/rostests/winetests/comctl32/edit.c b/modules/rostests/winetests/comctl32/edit.c
new file mode 100644 (file)
index 0000000..4719624
--- /dev/null
@@ -0,0 +1,3024 @@
+/* Unit test suite for edit control.
+ *
+ * Copyright 2004 Vitaliy Margolen
+ * Copyright 2005 C. Scott Ananian
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "precomp.h"
+
+#ifndef ES_COMBO
+#define ES_COMBO 0x200
+#endif
+
+#define ID_EDITTESTDBUTTON 0x123
+#define ID_EDITTEST2 99
+#define MAXLEN 200
+
+struct edit_notify {
+    int en_change, en_maxtext, en_update;
+};
+
+static struct edit_notify notifications;
+
+static INT_PTR CALLBACK multi_edit_dialog_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    static int num_ok_commands = 0;
+    switch (msg)
+    {
+        case WM_INITDIALOG:
+        {
+            HWND hedit = GetDlgItem(hdlg, 1000);
+            SetFocus(hedit);
+            switch (lparam)
+            {
+                /* test cases related to bug 12319 */
+                case 0:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+                case 1:
+                    PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+                case 2:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001);
+                    PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+
+                /* test cases for pressing enter */
+                case 3:
+                    num_ok_commands = 0;
+                    PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1);
+                    break;
+
+                default:
+                    break;
+            }
+            break;
+        }
+
+        case WM_COMMAND:
+            if (HIWORD(wparam) != BN_CLICKED)
+                break;
+
+            switch (LOWORD(wparam))
+            {
+                case IDOK:
+                    num_ok_commands++;
+                    break;
+
+                default:
+                    break;
+            }
+            break;
+
+        case WM_USER:
+        {
+            HWND hfocus = GetFocus();
+            HWND hedit = GetDlgItem(hdlg, 1000);
+            HWND hedit2 = GetDlgItem(hdlg, 1001);
+            HWND hedit3 = GetDlgItem(hdlg, 1002);
+
+            if (wparam != 0xdeadbeef)
+                break;
+
+            switch (lparam)
+            {
+                case 0:
+                    if (hfocus == hedit)
+                        EndDialog(hdlg, 1111);
+                    else if (hfocus == hedit2)
+                        EndDialog(hdlg, 2222);
+                    else if (hfocus == hedit3)
+                        EndDialog(hdlg, 3333);
+                    else
+                        EndDialog(hdlg, 4444);
+                    break;
+                case 1:
+                    if ((hfocus == hedit) && (num_ok_commands == 0))
+                        EndDialog(hdlg, 11);
+                    else
+                        EndDialog(hdlg, 22);
+                    break;
+                default:
+                    EndDialog(hdlg, 5555);
+            }
+            break;
+        }
+
+        case WM_CLOSE:
+            EndDialog(hdlg, 333);
+            break;
+
+        default:
+            break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK edit_dialog_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+        case WM_INITDIALOG:
+        {
+            HWND hedit = GetDlgItem(hdlg, 1000);
+            SetFocus(hedit);
+            switch (lparam)
+            {
+                /* from bug 11841 */
+                case 0:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
+                    break;
+                case 1:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
+                    break;
+                case 2:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1);
+                    break;
+
+                /* more test cases for WM_CHAR */
+                case 3:
+                    PostMessageA(hedit, WM_CHAR, VK_ESCAPE, 0x10001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+                case 4:
+                    PostMessageA(hedit, WM_CHAR, VK_RETURN, 0x1c0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+                case 5:
+                    PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+
+                /* more test cases for WM_KEYDOWN + WM_CHAR */
+                case 6:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
+                    PostMessageA(hedit, WM_CHAR, VK_ESCAPE, 0x10001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+                case 7:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
+                    PostMessageA(hedit, WM_CHAR, VK_RETURN, 0x1c0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1);
+                    break;
+                case 8:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001);
+                    PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1);
+                    break;
+
+                /* multiple tab tests */
+                case 9:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001);
+                    PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 2);
+                    break;
+                case 10:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001);
+                    PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001);
+                    PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 2);
+                    break;
+
+                default:
+                    break;
+            }
+            break;
+        }
+
+        case WM_COMMAND:
+            if (HIWORD(wparam) != BN_CLICKED)
+                break;
+
+            switch (LOWORD(wparam))
+            {
+                case IDOK:
+                    EndDialog(hdlg, 111);
+                    break;
+
+                case IDCANCEL:
+                    EndDialog(hdlg, 222);
+                    break;
+
+                default:
+                    break;
+            }
+            break;
+
+        case WM_USER:
+        {
+            int len;
+            HWND hok = GetDlgItem(hdlg, IDOK);
+            HWND hcancel = GetDlgItem(hdlg, IDCANCEL);
+            HWND hedit = GetDlgItem(hdlg, 1000);
+            HWND hfocus = GetFocus();
+
+            if (wparam != 0xdeadbeef)
+                break;
+
+            switch (lparam)
+            {
+                case 0:
+                    len = SendMessageA(hedit, WM_GETTEXTLENGTH, 0, 0);
+                    if (len == 0)
+                        EndDialog(hdlg, 444);
+                    else
+                        EndDialog(hdlg, 555);
+                    break;
+
+                case 1:
+                    len = SendMessageA(hedit, WM_GETTEXTLENGTH, 0, 0);
+                    if ((hfocus == hok) && len == 0)
+                        EndDialog(hdlg, 444);
+                    else
+                        EndDialog(hdlg, 555);
+                    break;
+
+                case 2:
+                    if (hfocus == hok)
+                        EndDialog(hdlg, 11);
+                    else if (hfocus == hcancel)
+                        EndDialog(hdlg, 22);
+                    else if (hfocus == hedit)
+                        EndDialog(hdlg, 33);
+                    else
+                        EndDialog(hdlg, 44);
+                    break;
+
+                default:
+                    EndDialog(hdlg, 555);
+            }
+            break;
+        }
+
+        case WM_CLOSE:
+            EndDialog(hdlg, 333);
+            break;
+
+        default:
+            break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK edit_singleline_dialog_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+        case WM_INITDIALOG:
+        {
+            HWND hedit = GetDlgItem(hdlg, 1000);
+            SetFocus(hedit);
+            switch (lparam)
+            {
+                /* test cases for WM_KEYDOWN */
+                case 0:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
+                    break;
+                case 1:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
+                    break;
+                case 2:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1);
+                    break;
+
+                /* test cases for WM_CHAR */
+                case 3:
+                    PostMessageA(hedit, WM_CHAR, VK_ESCAPE, 0x10001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+                case 4:
+                    PostMessageA(hedit, WM_CHAR, VK_RETURN, 0x1c0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+                case 5:
+                    PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+
+                /* test cases for WM_KEYDOWN + WM_CHAR */
+                case 6:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
+                    PostMessageA(hedit, WM_CHAR, VK_ESCAPE, 0x10001);
+                    break;
+                case 7:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
+                    PostMessageA(hedit, WM_CHAR, VK_RETURN, 0x1c0001);
+                    break;
+                case 8:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001);
+                    PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1);
+                    break;
+
+                default:
+                    break;
+            }
+            break;
+        }
+
+        case WM_COMMAND:
+            if (HIWORD(wparam) != BN_CLICKED)
+                break;
+
+            switch (LOWORD(wparam))
+            {
+                case IDOK:
+                    EndDialog(hdlg, 111);
+                    break;
+
+                case IDCANCEL:
+                    EndDialog(hdlg, 222);
+                    break;
+
+                default:
+                    break;
+            }
+            break;
+
+        case WM_USER:
+        {
+            HWND hok = GetDlgItem(hdlg, IDOK);
+            HWND hedit = GetDlgItem(hdlg, 1000);
+            HWND hfocus = GetFocus();
+            int len = SendMessageA(hedit, WM_GETTEXTLENGTH, 0, 0);
+
+            if (wparam != 0xdeadbeef)
+                break;
+
+            switch (lparam)
+            {
+                case 0:
+                    if ((hfocus == hedit) && len == 0)
+                        EndDialog(hdlg, 444);
+                    else
+                        EndDialog(hdlg, 555);
+                    break;
+
+                case 1:
+                    if ((hfocus == hok) && len == 0)
+                        EndDialog(hdlg, 444);
+                    else
+                        EndDialog(hdlg, 555);
+                    break;
+
+                default:
+                    EndDialog(hdlg, 55);
+            }
+            break;
+        }
+
+        case WM_CLOSE:
+            EndDialog(hdlg, 333);
+            break;
+
+        default:
+            break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK edit_wantreturn_dialog_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+        case WM_INITDIALOG:
+        {
+            HWND hedit = GetDlgItem(hdlg, 1000);
+            SetFocus(hedit);
+            switch (lparam)
+            {
+                /* test cases for WM_KEYDOWN */
+                case 0:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
+                    break;
+                case 1:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+                case 2:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1);
+                    break;
+
+                /* test cases for WM_CHAR */
+                case 3:
+                    PostMessageA(hedit, WM_CHAR, VK_ESCAPE, 0x10001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+                case 4:
+                    PostMessageA(hedit, WM_CHAR, VK_RETURN, 0x1c0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 2);
+                    break;
+                case 5:
+                    PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+
+                /* test cases for WM_KEYDOWN + WM_CHAR */
+                case 6:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
+                    PostMessageA(hedit, WM_CHAR, VK_ESCAPE, 0x10001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0);
+                    break;
+                case 7:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
+                    PostMessageA(hedit, WM_CHAR, VK_RETURN, 0x1c0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 2);
+                    break;
+                case 8:
+                    PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001);
+                    PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001);
+                    PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1);
+                    break;
+
+                default:
+                    break;
+            }
+            break;
+        }
+
+        case WM_COMMAND:
+            if (HIWORD(wparam) != BN_CLICKED)
+                break;
+
+            switch (LOWORD(wparam))
+            {
+                case IDOK:
+                    EndDialog(hdlg, 111);
+                    break;
+
+                case IDCANCEL:
+                    EndDialog(hdlg, 222);
+                    break;
+
+                default:
+                    break;
+            }
+            break;
+
+        case WM_USER:
+        {
+            HWND hok = GetDlgItem(hdlg, IDOK);
+            HWND hedit = GetDlgItem(hdlg, 1000);
+            HWND hfocus = GetFocus();
+            int len = SendMessageA(hedit, WM_GETTEXTLENGTH, 0, 0);
+
+            if (wparam != 0xdeadbeef)
+                break;
+
+            switch (lparam)
+            {
+                case 0:
+                    if ((hfocus == hedit) && len == 0)
+                        EndDialog(hdlg, 444);
+                    else
+                        EndDialog(hdlg, 555);
+                    break;
+
+                case 1:
+                    if ((hfocus == hok) && len == 0)
+                        EndDialog(hdlg, 444);
+                    else
+                        EndDialog(hdlg, 555);
+                    break;
+
+                case 2:
+                    if ((hfocus == hedit) && len == 2)
+                        EndDialog(hdlg, 444);
+                    else
+                        EndDialog(hdlg, 555);
+                    break;
+
+                default:
+                    EndDialog(hdlg, 55);
+            }
+            break;
+        }
+
+        case WM_CLOSE:
+            EndDialog(hdlg, 333);
+            break;
+
+        default:
+            break;
+    }
+
+    return FALSE;
+}
+
+static HINSTANCE hinst;
+static HWND hwndET2;
+static const char szEditTest2Class[] = "EditTest2Class";
+static const char szEditTest3Class[] = "EditTest3Class";
+static const char szEditTest4Class[] = "EditTest4Class";
+static const char szEditTextPositionClass[] = "EditTextPositionWindowClass";
+
+static HWND create_editcontrol (DWORD style, DWORD exstyle)
+{
+    HWND handle;
+
+    handle = CreateWindowExA(exstyle,
+                         "EDIT",
+                         "Test Text",
+                         style,
+                         10, 10, 300, 300,
+                         NULL, NULL, hinst, NULL);
+    ok (handle != NULL, "CreateWindow EDIT Control failed\n");
+    assert (handle);
+    if (winetest_interactive)
+       ShowWindow (handle, SW_SHOW);
+    return handle;
+}
+
+static HWND create_editcontrolW(DWORD style, DWORD exstyle)
+{
+    static const WCHAR testtextW[] = {'T','e','s','t',' ','t','e','x','t',0};
+    HWND handle;
+
+    handle = CreateWindowExW(exstyle, WC_EDITW, testtextW, style, 10, 10, 300, 300,
+        NULL, NULL, hinst, NULL);
+    ok(handle != NULL, "Failed to create Edit control.\n");
+    return handle;
+}
+
+static HWND create_child_editcontrol (DWORD style, DWORD exstyle)
+{
+    HWND parentWnd;
+    HWND editWnd;
+    RECT rect;
+    BOOL b;
+    SetRect(&rect, 0, 0, 300, 300);
+    b = AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
+    ok(b, "AdjustWindowRect failed\n");
+
+    parentWnd = CreateWindowExA(0,
+                            szEditTextPositionClass,
+                            "Edit Test",
+                            WS_OVERLAPPEDWINDOW,
+                            CW_USEDEFAULT, CW_USEDEFAULT,
+                            rect.right - rect.left, rect.bottom - rect.top,
+                            NULL, NULL, hinst, NULL);
+    ok (parentWnd != NULL, "CreateWindow EDIT Test failed\n");
+    assert(parentWnd);
+
+    editWnd = CreateWindowExA(exstyle,
+                            "EDIT",
+                            "Test Text",
+                            WS_CHILD | style,
+                            0, 0, 300, 300,
+                            parentWnd, NULL, hinst, NULL);
+    ok (editWnd != NULL, "CreateWindow EDIT Test Text failed\n");
+    assert(editWnd);
+    if (winetest_interactive)
+        ShowWindow (parentWnd, SW_SHOW);
+    return editWnd;
+}
+
+static void destroy_child_editcontrol (HWND hwndEdit)
+{
+    if (GetParent(hwndEdit))
+        DestroyWindow(GetParent(hwndEdit));
+    else {
+        trace("Edit control has no parent!\n");
+        DestroyWindow(hwndEdit);
+    }
+}
+
+static LONG get_edit_style (HWND hwnd)
+{
+    return GetWindowLongA( hwnd, GWL_STYLE ) & (
+       ES_LEFT |
+/* FIXME: not implemented
+       ES_CENTER |
+       ES_RIGHT |
+       ES_OEMCONVERT |
+*/
+       ES_MULTILINE |
+       ES_UPPERCASE |
+       ES_LOWERCASE |
+       ES_PASSWORD |
+       ES_AUTOVSCROLL |
+       ES_AUTOHSCROLL |
+       ES_NOHIDESEL |
+       ES_COMBO |
+       ES_READONLY |
+       ES_WANTRETURN |
+       ES_NUMBER
+       );
+}
+
+static void set_client_height(HWND Wnd, unsigned Height)
+{
+    RECT ClientRect, WindowRect;
+
+    GetWindowRect(Wnd, &WindowRect);
+    GetClientRect(Wnd, &ClientRect);
+    SetWindowPos(Wnd, NULL, 0, 0,
+                 WindowRect.right - WindowRect.left,
+                 Height + (WindowRect.bottom - WindowRect.top) -
+                 (ClientRect.bottom - ClientRect.top),
+                 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
+
+    /* Workaround for a bug in Windows' edit control
+       (multi-line mode) */
+    GetWindowRect(Wnd, &WindowRect);
+    SetWindowPos(Wnd, NULL, 0, 0,
+                 WindowRect.right - WindowRect.left + 1,
+                 WindowRect.bottom - WindowRect.top + 1,
+                 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
+    SetWindowPos(Wnd, NULL, 0, 0,
+                 WindowRect.right - WindowRect.left,
+                 WindowRect.bottom - WindowRect.top,
+                 SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
+
+    GetClientRect(Wnd, &ClientRect);
+    ok(ClientRect.bottom - ClientRect.top == Height,
+        "The client height should be %d, but is %d\n",
+        Height, ClientRect.bottom - ClientRect.top);
+}
+
+static void test_edit_control_1(void)
+{
+    HWND hwEdit;
+    MSG msMessage;
+    int i;
+    LONG r;
+
+    msMessage.message = WM_KEYDOWN;
+
+    trace("EDIT: Single line\n");
+    hwEdit = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    r = get_edit_style(hwEdit);
+    ok(r == (ES_AUTOVSCROLL | ES_AUTOHSCROLL), "Wrong style expected 0xc0 got: 0x%x\n", r);
+    for (i = 0; i < 65535; i++)
+    {
+        msMessage.wParam = i;
+        r = SendMessageA(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage);
+        ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS),
+            "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS got %x\n", r);
+    }
+    DestroyWindow(hwEdit);
+
+    trace("EDIT: Single line want returns\n");
+    hwEdit = create_editcontrol(ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    r = get_edit_style(hwEdit);
+    ok(r == (ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN), "Wrong style expected 0x10c0 got: 0x%x\n", r);
+    for (i = 0; i < 65535; i++)
+    {
+        msMessage.wParam = i;
+        r = SendMessageA(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage);
+        ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS),
+            "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS got %x\n", r);
+    }
+    DestroyWindow(hwEdit);
+
+    trace("EDIT: Multiline line\n");
+    hwEdit = create_editcontrol(ES_MULTILINE | WS_VSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    r = get_edit_style(hwEdit);
+    ok(r == (ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE), "Wrong style expected 0xc4 got: 0x%x\n", r);
+    for (i = 0; i < 65535; i++)
+    {
+        msMessage.wParam = i;
+        r = SendMessageA(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage);
+        ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS),
+            "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS got %x\n", r);
+    }
+    DestroyWindow(hwEdit);
+
+    trace("EDIT: Multi line want returns\n");
+    hwEdit = create_editcontrol(ES_MULTILINE | WS_VSCROLL | ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    r = get_edit_style(hwEdit);
+    ok(r == (ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE), "Wrong style expected 0x10c4 got: 0x%x\n", r);
+    for (i = 0; i < 65535; i++)
+    {
+        msMessage.wParam = i;
+        r = SendMessageA(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage);
+        ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS),
+            "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS got %x\n", r);
+    }
+    DestroyWindow(hwEdit);
+}
+
+/* WM_SETTEXT is implemented by selecting all text, and then replacing the
+ * selection.  This test checks that the first 'select all' doesn't generate
+ * an UPDATE message which can escape and (via a handler) change the
+ * selection, which would cause WM_SETTEXT to break.  This old bug
+ * was fixed 18-Mar-2005; we check here to ensure it doesn't regress.
+ */
+static void test_edit_control_2(void)
+{
+    HWND hwndMain, phwnd;
+    char szLocalString[MAXLEN];
+    LONG r, w = 150, h = 50;
+    POINT cpos;
+
+    /* Create main and edit windows. */
+    hwndMain = CreateWindowA(szEditTest2Class, "ET2", WS_OVERLAPPEDWINDOW,
+                            0, 0, 200, 200, NULL, NULL, hinst, NULL);
+    assert(hwndMain);
+    if (winetest_interactive)
+        ShowWindow (hwndMain, SW_SHOW);
+
+    hwndET2 = CreateWindowA("EDIT", NULL,
+                           WS_CHILD|WS_BORDER|ES_LEFT|ES_AUTOHSCROLL,
+                           0, 0, w, h, /* important this not be 0 size. */
+                           hwndMain, (HMENU) ID_EDITTEST2, hinst, NULL);
+    assert(hwndET2);
+    if (winetest_interactive)
+        ShowWindow (hwndET2, SW_SHOW);
+
+    trace("EDIT: SETTEXT atomicity\n");
+    /* Send messages to "type" in the word 'foo'. */
+    r = SendMessageA(hwndET2, WM_CHAR, 'f', 1);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+    r = SendMessageA(hwndET2, WM_CHAR, 'o', 1);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+    r = SendMessageA(hwndET2, WM_CHAR, 'o', 1);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+    /* 'foo' should have been changed to 'bar' by the UPDATE handler. */
+    GetWindowTextA(hwndET2, szLocalString, MAXLEN);
+    ok(strcmp(szLocalString, "bar")==0,
+       "Wrong contents of edit: %s\n", szLocalString);
+
+    /* try setting the caret before it's visible */
+    r = SetCaretPos(0, 0);
+    todo_wine ok(0 == r, "SetCaretPos succeeded unexpectedly, expected: 0, got: %d\n", r);
+    phwnd = SetFocus(hwndET2);
+    ok(phwnd != NULL, "SetFocus failed unexpectedly, expected non-zero, got NULL\n");
+    r = SetCaretPos(0, 0);
+    ok(1 == r, "SetCaretPos failed unexpectedly, expected: 1, got: %d\n", r);
+    r = GetCaretPos(&cpos);
+    ok(1 == r, "GetCaretPos failed unexpectedly, expected: 1, got: %d\n", r);
+    ok(cpos.x == 0 && cpos.y == 0, "Wrong caret position, expected: (0,0), got: (%d,%d)\n", cpos.x, cpos.y);
+    r = SetCaretPos(-1, -1);
+    ok(1 == r, "SetCaretPos failed unexpectedly, expected: 1, got: %d\n", r);
+    r = GetCaretPos(&cpos);
+    ok(1 == r, "GetCaretPos failed unexpectedly, expected: 1, got: %d\n", r);
+    ok(cpos.x == -1 && cpos.y == -1, "Wrong caret position, expected: (-1,-1), got: (%d,%d)\n", cpos.x, cpos.y);
+    r = SetCaretPos(w << 1, h << 1);
+    ok(1 == r, "SetCaretPos failed unexpectedly, expected: 1, got: %d\n", r);
+    r = GetCaretPos(&cpos);
+    ok(1 == r, "GetCaretPos failed unexpectedly, expected: 1, got: %d\n", r);
+    ok(cpos.x == (w << 1) && cpos.y == (h << 1), "Wrong caret position, expected: (%d,%d), got: (%d,%d)\n", w << 1, h << 1, cpos.x, cpos.y);
+    r = SetCaretPos(w, h);
+    ok(1 == r, "SetCaretPos failed unexpectedly, expected: 1, got: %d\n", r);
+    r = GetCaretPos(&cpos);
+    ok(1 == r, "GetCaretPos failed unexpectedly, expected: 1, got: %d\n", r);
+    ok(cpos.x == w && cpos.y == h, "Wrong caret position, expected: (%d,%d), got: (%d,%d)\n", w, h, cpos.x, cpos.y);
+    r = SetCaretPos(w - 1, h - 1);
+    ok(1 == r, "SetCaretPos failed unexpectedly, expected: 1, got: %d\n", r);
+    r = GetCaretPos(&cpos);
+    ok(1 == r, "GetCaretPos failed unexpectedly, expected: 1, got: %d\n", r);
+    ok(cpos.x == (w - 1) && cpos.y == (h - 1), "Wrong caret position, expected: (%d,%d), got: (%d,%d)\n", w - 1, h - 1, cpos.x, cpos.y);
+
+    DestroyWindow(hwndET2);
+    DestroyWindow(hwndMain);
+}
+
+static void ET2_check_change(void)
+{
+   char szLocalString[MAXLEN];
+   /* This EN_UPDATE handler changes any 'foo' to 'bar'. */
+   GetWindowTextA(hwndET2, szLocalString, MAXLEN);
+   if (!strcmp(szLocalString, "foo"))
+   {
+       strcpy(szLocalString, "bar");
+       SendMessageA(hwndET2, WM_SETTEXT, 0, (LPARAM)szLocalString);
+   }
+   /* always leave the cursor at the end. */
+   SendMessageA(hwndET2, EM_SETSEL, MAXLEN - 1, MAXLEN - 1);
+}
+
+static void ET2_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
+{
+    if (id == ID_EDITTEST2 && codeNotify == EN_UPDATE)
+        ET2_check_change();
+}
+
+static LRESULT CALLBACK ET2_WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch (iMsg)
+    {
+    case WM_COMMAND:
+        ET2_OnCommand(hwnd, LOWORD(wParam), (HWND)lParam, HIWORD(wParam));
+        break;
+    }
+    return DefWindowProcA(hwnd, iMsg, wParam, lParam);
+}
+
+static void zero_notify(void)
+{
+    notifications.en_change = 0;
+    notifications.en_maxtext = 0;
+    notifications.en_update = 0;
+}
+
+#define test_notify(enchange, enmaxtext, enupdate) \
+do { \
+    ok(notifications.en_change == enchange, "expected %d EN_CHANGE notifications, " \
+    "got %d\n", enchange, notifications.en_change); \
+    ok(notifications.en_maxtext == enmaxtext, "expected %d EN_MAXTEXT notifications, " \
+    "got %d\n", enmaxtext, notifications.en_maxtext); \
+    ok(notifications.en_update == enupdate, "expected %d EN_UPDATE notifications, " \
+    "got %d\n", enupdate, notifications.en_update); \
+} while(0)
+
+static LRESULT CALLBACK edit3_wnd_procA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    switch (msg)
+    {
+        case WM_COMMAND:
+            switch (HIWORD(wParam))
+            {
+                case EN_MAXTEXT:
+                    notifications.en_maxtext++;
+                    break;
+                case EN_UPDATE:
+                    notifications.en_update++;
+                    break;
+                case EN_CHANGE:
+                    notifications.en_change++;
+                    break;
+            }
+            break;
+    }
+    return DefWindowProcA(hWnd, msg, wParam, lParam);
+}
+
+/* Test behaviour of WM_SETTEXT, WM_REPLACESEL and notifications sent in response
+ * to these messages.
+ */
+static void test_edit_control_3(void)
+{
+    static const char *str = "this is a long string.";
+    static const char *str2 = "this is a long string.\r\nthis is a long string.\r\nthis is a long string.\r\nthis is a long string.";
+    HWND hWnd, hParent;
+    int len, dpi;
+    HDC hDC;
+
+    hDC = GetDC(NULL);
+    dpi = GetDeviceCaps(hDC, LOGPIXELSY);
+    ReleaseDC(NULL, hDC);
+
+    trace("EDIT: Test notifications\n");
+
+    hParent = CreateWindowExA(0,
+              szEditTest3Class,
+              NULL,
+              0,
+              CW_USEDEFAULT, CW_USEDEFAULT, 10, 10,
+              NULL, NULL, NULL, NULL);
+    assert(hParent);
+
+    trace("EDIT: Single line, no ES_AUTOHSCROLL\n");
+    hWnd = CreateWindowExA(0,
+              "EDIT",
+              NULL,
+              0,
+              10, 10, 50, 50,
+              hParent, NULL, NULL, NULL);
+    assert(hWnd);
+
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    if (len == lstrlenA(str)) /* Win 8 */
+        test_notify(1, 0, 1);
+    else
+        test_notify(1, 1, 1);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a");
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(1 == len, "wrong text length, expected 1, got %d\n", len);
+    test_notify(1, 0, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    len = SendMessageA(hWnd, EM_GETSEL, 0, 0);
+    ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
+    ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len));
+    SendMessageA(hParent, WM_SETFOCUS, 0, (LPARAM)hWnd);
+    len = SendMessageA(hWnd, EM_GETSEL, 0, 0);
+    ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
+    ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len));
+
+    SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+    test_notify(1, 1, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    DestroyWindow(hWnd);
+
+    trace("EDIT: Single line, ES_AUTOHSCROLL\n");
+    hWnd = CreateWindowExA(0,
+              "EDIT",
+              NULL,
+              ES_AUTOHSCROLL,
+              10, 10, 50, 50,
+              hParent, NULL, NULL, NULL);
+    assert(hWnd);
+
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+    test_notify(1, 1, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    DestroyWindow(hWnd);
+
+    trace("EDIT: Multline, no ES_AUTOHSCROLL, no ES_AUTOVSCROLL\n");
+    hWnd = CreateWindowExA(0,
+              "EDIT",
+              NULL,
+              ES_MULTILINE,
+              10, 10, (50 * dpi) / 96, (50 * dpi) / 96,
+              hParent, NULL, NULL, NULL);
+    assert(hWnd);
+
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    if (len == lstrlenA(str)) /* Win 8 */
+        test_notify(1, 0, 1);
+    else
+    {
+        ok(0 == len, "text should have been truncated, expected 0, got %d\n", len);
+        test_notify(1, 1, 1);
+    }
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a");
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(1 == SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0), "wrong text length, expected 1, got %d\n", len);
+    test_notify(1, 0, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(0, 0, 0);
+
+    SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+    test_notify(1, 1, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(0, 0, 0);
+
+    DestroyWindow(hWnd);
+
+    trace("EDIT: Multline, ES_AUTOHSCROLL, no ES_AUTOVSCROLL\n");
+    hWnd = CreateWindowExA(0,
+              "EDIT",
+              NULL,
+              ES_MULTILINE | ES_AUTOHSCROLL,
+              10, 10, (50 * dpi) / 96, (50 * dpi) / 96,
+              hParent, NULL, NULL, NULL);
+    assert(hWnd);
+
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(0 == len, "text should have been truncated, expected 0, got %d\n", len);
+    test_notify(1, 1, 1);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a");
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(1 == SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0), "wrong text length, expected 1, got %d\n", len);
+    test_notify(1, 0, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+    test_notify(0, 0, 0);
+
+    SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+    test_notify(1, 1, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+    test_notify(0, 0, 0);
+
+    DestroyWindow(hWnd);
+
+    trace("EDIT: Multline, ES_AUTOHSCROLL and ES_AUTOVSCROLL\n");
+    hWnd = CreateWindowExA(0,
+              "EDIT",
+              NULL,
+              ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
+              10, 10, 50, 50,
+              hParent, NULL, NULL, NULL);
+    assert(hWnd);
+
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+    test_notify(0, 0, 0);
+
+    SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+    test_notify(1, 1, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+    test_notify(0, 0, 0);
+
+    DestroyWindow(hWnd);
+}
+
+static void test_char_from_pos(void)
+{
+    int lo, hi, mid, ret, i;
+    HWND hwEdit;
+
+    hwEdit = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"aa");
+    lo = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 0, 0));
+    hi = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 1, 0));
+    mid = lo + (hi - lo) / 2;
+
+    for (i = lo; i < mid; i++)
+    {
+       ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i));
+       ok(0 == ret, "expected 0 got %d\n", ret);
+    }
+
+    for (i = mid; i <= hi; i++)
+    {
+       ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i));
+       ok(1 == ret, "expected 1 got %d\n", ret);
+    }
+
+    ret = SendMessageA(hwEdit, EM_POSFROMCHAR, 2, 0);
+    ok(-1 == ret, "expected -1 got %d\n", ret);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(ES_RIGHT | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"aa");
+    lo = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 0, 0));
+    hi = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 1, 0));
+    mid = lo + (hi - lo) / 2;
+
+    for (i = lo; i < mid; i++)
+    {
+       ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i));
+       ok(0 == ret, "expected 0 got %d\n", ret);
+    }
+
+    for (i = mid; i <= hi; i++)
+    {
+       ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i));
+       ok(1 == ret, "expected 1 got %d\n", ret);
+    }
+
+    ret = SendMessageA(hwEdit, EM_POSFROMCHAR, 2, 0);
+    ok(-1 == ret, "expected -1 got %d\n", ret);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(ES_CENTER | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"aa");
+    lo = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 0, 0));
+    hi = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 1, 0));
+    mid = lo + (hi - lo) / 2;
+
+    for (i = lo; i < mid; i++)
+    {
+       ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i));
+       ok(0 == ret, "expected 0 got %d\n", ret);
+    }
+
+    for (i = mid; i <= hi; i++)
+    {
+       ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i));
+       ok(1 == ret, "expected 1 got %d\n", ret);
+    }
+
+    ret = SendMessageA(hwEdit, EM_POSFROMCHAR, 2, 0);
+    ok(-1 == ret, "expected -1 got %d\n", ret);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"aa");
+    lo = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 0, 0));
+    hi = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 1, 0));
+    mid = lo + (hi - lo) / 2 + 1;
+
+    for (i = lo; i < mid; i++)
+    {
+       ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i));
+       ok((0 == ret || 1 == ret /* Vista */), "expected 0 or 1 got %d\n", ret);
+    }
+
+    for (i = mid; i <= hi; i++)
+    {
+       ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i));
+       ok(1 == ret, "expected 1 got %d\n", ret);
+    }
+
+    ret = SendMessageA(hwEdit, EM_POSFROMCHAR, 2, 0);
+    ok(-1 == ret, "expected -1 got %d\n", ret);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(ES_MULTILINE | ES_RIGHT | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"aa");
+    lo = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 0, 0));
+    hi = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 1, 0));
+    mid = lo + (hi - lo) / 2 + 1;
+
+    for (i = lo; i < mid; i++)
+    {
+       ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i));
+       ok((0 == ret || 1 == ret /* Vista */), "expected 0 or 1 got %d\n", ret);
+    }
+
+    for (i = mid; i <= hi; i++)
+    {
+       ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i));
+       ok(1 == ret, "expected 1 got %d\n", ret);
+    }
+
+    ret = SendMessageA(hwEdit, EM_POSFROMCHAR, 2, 0);
+    ok(-1 == ret, "expected -1 got %d\n", ret);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(ES_MULTILINE | ES_CENTER | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"aa");
+    lo = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 0, 0));
+    hi = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 1, 0));
+    mid = lo + (hi - lo) / 2 + 1;
+
+    for (i = lo; i < mid; i++)
+    {
+       ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i));
+       ok((0 == ret || 1 == ret /* Vista */), "expected 0 or 1 got %d\n", ret);
+    }
+
+    for (i = mid; i <= hi; i++)
+    {
+       ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i));
+       ok(1 == ret, "expected 1 got %d\n", ret);
+    }
+
+    ret = SendMessageA(hwEdit, EM_POSFROMCHAR, 2, 0);
+    ok(-1 == ret, "expected -1 got %d\n", ret);
+    DestroyWindow(hwEdit);
+}
+
+/* Test if creating edit control without ES_AUTOHSCROLL and ES_AUTOVSCROLL
+ * truncates text that doesn't fit.
+ */
+static void test_edit_control_5(void)
+{
+    static const char *str = "test\r\ntest";
+    HWND parentWnd;
+    HWND hWnd;
+    int len;
+    RECT rc1 = { 10, 10, 11, 11};
+    RECT rc;
+
+    /* first show that a non-child won't do for this test */
+    hWnd = CreateWindowExA(0,
+              "EDIT",
+              str,
+              0,
+              10, 10, 1, 1,
+              NULL, NULL, NULL, NULL);
+    assert(hWnd);
+    /* size of non-child edit control is (much) bigger than requested */
+    GetWindowRect( hWnd, &rc);
+    ok( rc.right - rc.left > 20, "size of the window (%d) is smaller than expected\n",
+            rc.right - rc.left);
+    DestroyWindow(hWnd);
+    /* so create a parent, and give it edit controls children to test with */
+    parentWnd = CreateWindowExA(0,
+                            szEditTextPositionClass,
+                            "Edit Test", WS_VISIBLE |
+                            WS_OVERLAPPEDWINDOW,
+                            CW_USEDEFAULT, CW_USEDEFAULT,
+                            250, 250,
+                            NULL, NULL, hinst, NULL);
+    assert(parentWnd);
+    ShowWindow( parentWnd, SW_SHOW);
+    /* single line */
+    hWnd = CreateWindowExA(0,
+              "EDIT",
+              str, WS_VISIBLE | WS_BORDER |
+              WS_CHILD,
+              rc1.left, rc1.top, rc1.right - rc1.left, rc1.bottom - rc1.top,
+              parentWnd, NULL, NULL, NULL);
+    assert(hWnd);
+    GetClientRect( hWnd, &rc);
+    ok( rc.right == rc1.right - rc1.left && rc.bottom == rc1.bottom - rc1.top,
+            "Client rectangle not the expected size %s\n", wine_dbgstr_rect( &rc ));
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    DestroyWindow(hWnd);
+    /* multi line */
+    hWnd = CreateWindowExA(0,
+              "EDIT",
+              str,
+              WS_CHILD | ES_MULTILINE,
+              rc1.left, rc1.top, rc1.right - rc1.left, rc1.bottom - rc1.top,
+              parentWnd, NULL, NULL, NULL);
+    assert(hWnd);
+    GetClientRect( hWnd, &rc);
+    ok( rc.right == rc1.right - rc1.left && rc.bottom == rc1.bottom - rc1.top,
+            "Client rectangle not the expected size %s\n", wine_dbgstr_rect( &rc ));
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    DestroyWindow(hWnd);
+    DestroyWindow(parentWnd);
+}
+
+/* Test WM_GETTEXT processing
+ * after destroy messages
+ */
+static void test_edit_control_6(void)
+{
+    static const char *str = "test\r\ntest";
+    char buf[MAXLEN];
+    HWND hWnd;
+    LONG ret;
+
+    hWnd = CreateWindowExA(0, "EDIT", "Test", 0, 10, 10, 1, 1, NULL, NULL, hinst, NULL);
+    ok(hWnd != NULL, "Failed to create edit control.\n");
+
+    ret = SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+    ok(ret == TRUE, "Expected %d, got %d\n", TRUE, ret);
+    ret = SendMessageA(hWnd, WM_GETTEXT, MAXLEN, (LPARAM)buf);
+    ok(ret == strlen(str), "Expected %s, got len %d\n", str, ret);
+    ok(!strcmp(buf, str), "Expected %s, got %s\n", str, buf);
+
+    buf[0] = 0;
+    ret = SendMessageA(hWnd, WM_DESTROY, 0, 0);
+todo_wine
+    ok(ret == 1, "Unexpected return value %d\n", ret);
+    ret = SendMessageA(hWnd, WM_GETTEXT, MAXLEN, (LPARAM)buf);
+    ok(ret == strlen(str), "Expected %s, got len %d\n", str, ret);
+    ok(!strcmp(buf, str), "Expected %s, got %s\n", str, buf);
+
+    buf[0] = 0;
+    ret = SendMessageA(hWnd, WM_NCDESTROY, 0, 0);
+    ok(ret == 0, "Expected 0, got %d\n", ret);
+    ret = SendMessageA(hWnd, WM_GETTEXT, MAXLEN, (LPARAM)buf);
+todo_wine {
+    ok(ret == strlen("Test"), "Unexpected text length %d\n", ret);
+    ok(!strcmp(buf, "Test"), "Unexpected text %s\n", buf);
+}
+    DestroyWindow(hWnd);
+}
+
+static void test_edit_control_limittext(void)
+{
+    HWND hwEdit;
+    DWORD r;
+
+    /* Test default limit for single-line control */
+    trace("EDIT: buffer limit for single-line\n");
+    hwEdit = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    r = SendMessageA(hwEdit, EM_GETLIMITTEXT, 0, 0);
+    ok(r == 30000, "Incorrect default text limit, expected 30000 got %u\n", r);
+    SendMessageA(hwEdit, EM_SETLIMITTEXT, 0, 0);
+    r = SendMessageA(hwEdit, EM_GETLIMITTEXT, 0, 0);
+    ok( r == 2147483646, "got limit %u (expected 2147483646)\n", r);
+    DestroyWindow(hwEdit);
+
+    /* Test default limit for multi-line control */
+    trace("EDIT: buffer limit for multi-line\n");
+    hwEdit = create_editcontrol(ES_MULTILINE | WS_VSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    r = SendMessageA(hwEdit, EM_GETLIMITTEXT, 0, 0);
+    ok(r == 30000, "Incorrect default text limit, expected 30000 got %u\n", r);
+    SendMessageA(hwEdit, EM_SETLIMITTEXT, 0, 0);
+    r = SendMessageA(hwEdit, EM_GETLIMITTEXT, 0, 0);
+    ok( r == 4294967295U, "got limit %u (expected 4294967295)\n", r);
+    DestroyWindow(hwEdit);
+}
+
+/* Test EM_SCROLL */
+static void test_edit_control_scroll(void)
+{
+    static const char *single_line_str = "a";
+    static const char *multiline_str = "Test\r\nText";
+    HWND hwEdit;
+    LONG ret;
+
+    /* Check the return value when EM_SCROLL doesn't scroll
+     * anything. Should not return true unless any lines were actually
+     * scrolled. */
+    hwEdit = CreateWindowA(
+              "EDIT",
+              single_line_str,
+              WS_VSCROLL | ES_MULTILINE,
+              1, 1, 100, 100,
+              NULL, NULL, hinst, NULL);
+
+    assert(hwEdit);
+
+    ret = SendMessageA(hwEdit, EM_SCROLL, SB_PAGEDOWN, 0);
+    ok(!ret, "Returned %x, expected 0.\n", ret);
+
+    ret = SendMessageA(hwEdit, EM_SCROLL, SB_PAGEUP, 0);
+    ok(!ret, "Returned %x, expected 0.\n", ret);
+
+    ret = SendMessageA(hwEdit, EM_SCROLL, SB_LINEUP, 0);
+    ok(!ret, "Returned %x, expected 0.\n", ret);
+
+    ret = SendMessageA(hwEdit, EM_SCROLL, SB_LINEDOWN, 0);
+    ok(!ret, "Returned %x, expected 0.\n", ret);
+
+    DestroyWindow (hwEdit);
+
+    /* SB_PAGEDOWN while at the beginning of a buffer with few lines
+       should not cause EM_SCROLL to return a negative value of
+       scrolled lines that would put us "before" the beginning. */
+    hwEdit = CreateWindowA(
+                "EDIT",
+                multiline_str,
+                WS_VSCROLL | ES_MULTILINE,
+                0, 0, 100, 100,
+                NULL, NULL, hinst, NULL);
+    assert(hwEdit);
+
+    ret = SendMessageA(hwEdit, EM_SCROLL, SB_PAGEDOWN, 0);
+    ok(!ret, "Returned %x, expected 0.\n", ret);
+
+    DestroyWindow (hwEdit);
+}
+
+static void test_margins_usefontinfo(UINT charset)
+{
+    INT margins, threshold, expect, empty_expect, small_expect;
+    HWND hwnd;
+    HDC hdc;
+    SIZE size;
+    BOOL cjk;
+    LOGFONTA lf;
+    HFONT hfont;
+    RECT rect;
+
+    memset(&lf, 0, sizeof(lf));
+    lf.lfHeight = -11;
+    lf.lfWeight = FW_NORMAL;
+    lf.lfCharSet = charset;
+    strcpy(lf.lfFaceName, "Tahoma");
+
+    hfont = CreateFontIndirectA(&lf);
+    ok(hfont != NULL, "got %p\n", hfont);
+
+    /* Big window rectangle */
+    hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, 5000, 1000, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "got %p\n", hwnd);
+    GetClientRect(hwnd, &rect);
+    ok(!IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect));
+
+    hdc = GetDC(hwnd);
+    hfont = SelectObject(hdc, hfont);
+    size.cx = GdiGetCharDimensions( hdc, NULL, &size.cy );
+    expect = MAKELONG(size.cx / 2, size.cx / 2);
+    small_expect = 0;
+    empty_expect = size.cx >= 28 ? small_expect : expect;
+
+    charset = GetTextCharset(hdc);
+    switch (charset)
+    {
+    case SHIFTJIS_CHARSET:
+    case HANGUL_CHARSET:
+    case GB2312_CHARSET:
+    case CHINESEBIG5_CHARSET:
+        cjk = TRUE;
+        break;
+    default:
+        cjk = FALSE;
+    }
+
+    hfont = SelectObject(hdc, hfont);
+    ReleaseDC(hwnd, hdc);
+
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == 0, "got %x\n", margins);
+    SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    if (!cjk)
+        ok(margins == expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
+    else
+    {
+        ok(HIWORD(margins) > 0 && LOWORD(margins) > 0, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
+        expect = empty_expect = small_expect = margins;
+    }
+    DestroyWindow(hwnd);
+
+    threshold = (size.cx / 2 + size.cx) * 2;
+
+    /* Size below which non-cjk margins are zero */
+    hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, threshold - 1, 100, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "got %p\n", hwnd);
+    GetClientRect(hwnd, &rect);
+    ok(!IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect));
+
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == 0, "got %x\n", margins);
+
+    SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == small_expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
+    DestroyWindow(hwnd);
+
+    /* Size at which non-cjk margins become non-zero */
+    hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, threshold, 100, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "got %p\n", hwnd);
+    GetClientRect(hwnd, &rect);
+    ok(!IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect));
+
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == 0, "got %x\n", margins);
+
+    SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
+    DestroyWindow(hwnd);
+
+    /* Empty rect */
+    hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "got %p\n", hwnd);
+    GetClientRect(hwnd, &rect);
+    ok(IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect));
+
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == 0, "got %x\n", margins);
+
+    SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == empty_expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
+    DestroyWindow(hwnd);
+
+    DeleteObject(hfont);
+}
+
+static void test_margins(void)
+{
+    DWORD old_margins, new_margins;
+    RECT old_rect, new_rect;
+    INT old_right_margin;
+    HWND hwEdit;
+
+    hwEdit = create_editcontrol(WS_BORDER | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+
+    old_margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
+    old_right_margin = HIWORD(old_margins);
+
+    /* Check if setting the margins works */
+
+    SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN, MAKELONG(10, 0));
+    new_margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(new_margins) == 10, "Wrong left margin: %d\n", LOWORD(new_margins));
+    ok(HIWORD(new_margins) == old_right_margin, "Wrong right margin: %d\n", HIWORD(new_margins));
+
+    SendMessageA(hwEdit, EM_SETMARGINS, EC_RIGHTMARGIN, MAKELONG(0, 10));
+    new_margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(new_margins) == 10, "Wrong left margin: %d\n", LOWORD(new_margins));
+    ok(HIWORD(new_margins) == 10, "Wrong right margin: %d\n", HIWORD(new_margins));
+
+    /* The size of the rectangle must decrease if we increase the margin */
+
+    SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(5, 5));
+    SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&old_rect);
+    SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(15, 20));
+    SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect);
+    ok(new_rect.left == old_rect.left + 10, "The left border of the rectangle is wrong\n");
+    ok(new_rect.right == old_rect.right - 15, "The right border of the rectangle is wrong\n");
+    ok(new_rect.top == old_rect.top, "The top border of the rectangle must not change\n");
+    ok(new_rect.bottom == old_rect.bottom, "The bottom border of the rectangle must not change\n");
+
+    /* If we set the margin to same value as the current margin,
+       the rectangle must not change */
+
+    SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(10, 10));
+    SetRect(&old_rect, 1, 1, 99, 99);
+    SendMessageA(hwEdit, EM_SETRECT, 0, (LPARAM)&old_rect);
+    SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&old_rect);
+    SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(10, 10));
+    SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect);
+    ok(EqualRect(&old_rect, &new_rect), "The border of the rectangle has changed\n");
+
+    /* The lParam argument of the WM_SIZE message should be ignored. */
+
+    SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&old_rect);
+    SendMessageA(hwEdit, WM_SIZE, SIZE_RESTORED, 0);
+    SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect);
+    ok(EqualRect(&old_rect, &new_rect), "The border of the rectangle has changed\n");
+    SendMessageA(hwEdit, WM_SIZE, SIZE_MINIMIZED, 0);
+    SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect);
+    ok(EqualRect(&old_rect, &new_rect), "The border of the rectangle has changed\n");
+    SendMessageA(hwEdit, WM_SIZE, SIZE_MAXIMIZED, 0);
+    SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect);
+    ok(EqualRect(&old_rect, &new_rect), "The border of the rectangle has changed\n");
+    SendMessageA(hwEdit, WM_SIZE, SIZE_RESTORED, MAKELONG(10, 10));
+    SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect);
+    ok(EqualRect(&old_rect, &new_rect), "The border of the rectangle has changed\n");
+
+    DestroyWindow (hwEdit);
+
+    test_margins_usefontinfo(ANSI_CHARSET);
+    test_margins_usefontinfo(EASTEUROPE_CHARSET);
+
+    test_margins_usefontinfo(SHIFTJIS_CHARSET);
+    test_margins_usefontinfo(HANGUL_CHARSET);
+    test_margins_usefontinfo(CHINESEBIG5_CHARSET);
+    /* Don't test JOHAB_CHARSET.  Treated as CJK by Win 8,
+       but not by < Win 8 and Win 10. */
+
+    test_margins_usefontinfo(DEFAULT_CHARSET);
+}
+
+static INT CALLBACK find_font_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
+{
+    return 0;
+}
+
+static void test_margins_font_change(void)
+{
+    DWORD margins, font_margins, ret;
+    HFONT hfont, hfont2;
+    HWND hwEdit;
+    LOGFONTA lf;
+    HDC hdc;
+
+    hdc = GetDC(0);
+    ret = EnumFontFamiliesA(hdc, "Arial", find_font_proc, 0);
+    ReleaseDC(0, hdc);
+    if (ret)
+    {
+        trace("Arial not found - skipping font change margin tests\n");
+        return;
+    }
+
+    hwEdit = create_child_editcontrol(0, 0);
+
+    SetWindowPos(hwEdit, NULL, 10, 10, 1000, 100, SWP_NOZORDER | SWP_NOACTIVATE);
+
+    memset(&lf, 0, sizeof(lf));
+    strcpy(lf.lfFaceName, "Arial");
+    lf.lfHeight = 16;
+    lf.lfCharSet = DEFAULT_CHARSET;
+    hfont = CreateFontIndirectA(&lf);
+    lf.lfHeight = 30;
+    hfont2 = CreateFontIndirectA(&lf);
+
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0);
+    font_margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(font_margins) != 0, "got %d\n", LOWORD(font_margins));
+    ok(HIWORD(font_margins) != 0, "got %d\n", HIWORD(font_margins));
+
+    /* With 'small' edit controls, test that the margin doesn't get set */
+    SetWindowPos(hwEdit, NULL, 10, 10, 16, 100, SWP_NOZORDER | SWP_NOACTIVATE);
+    SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0,0));
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0);
+    margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(margins) == 0, "got %d\n", LOWORD(margins));
+    ok(HIWORD(margins) == 0, "got %d\n", HIWORD(margins));
+
+    SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(1,0));
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0);
+    margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(margins) == 1, "got %d\n", LOWORD(margins));
+    ok(HIWORD(margins) == 0, "got %d\n", HIWORD(margins));
+
+    SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(1,1));
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0);
+    margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(margins) == 1, "got %d\n", LOWORD(margins));
+    ok(HIWORD(margins) == 1, "got %d\n", HIWORD(margins));
+
+    SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO,EC_USEFONTINFO));
+    margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(margins) == 1, "got %d\n", LOWORD(margins));
+    ok(HIWORD(margins) == 1, "got %d\n", HIWORD(margins));
+
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont2, 0);
+    margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(margins) == 1, "got %d\n", LOWORD(margins));
+    ok(HIWORD(margins) == 1, "got %d\n", HIWORD(margins));
+
+    /* Above a certain size threshold then the margin is updated */
+    SetWindowPos(hwEdit, NULL, 10, 10, 1000, 100, SWP_NOZORDER | SWP_NOACTIVATE);
+    SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(1,0));
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0);
+    margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(margins) == LOWORD(font_margins), "got %d\n", LOWORD(margins));
+    ok(HIWORD(margins) == HIWORD(font_margins), "got %d\n", HIWORD(margins));
+
+    SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(1,1));
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0);
+    margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(margins) == LOWORD(font_margins), "got %d\n", LOWORD(margins));
+    ok(HIWORD(margins) == HIWORD(font_margins), "got %d\n", HIWORD(margins));
+
+    SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO,EC_USEFONTINFO));
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0);
+    margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(margins) == LOWORD(font_margins), "got %d\n", LOWORD(margins));
+    ok(HIWORD(margins) == HIWORD(font_margins), "got %d\n", HIWORD(margins));
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont2, 0);
+    margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(margins) != LOWORD(font_margins), "got %d\n", LOWORD(margins));
+    ok(HIWORD(margins) != HIWORD(font_margins), "got %d\n", HIWORD(margins));
+
+    SendMessageA(hwEdit, WM_SETFONT, 0, 0);
+
+    DeleteObject(hfont2);
+    DeleteObject(hfont);
+    destroy_child_editcontrol(hwEdit);
+
+}
+
+#define edit_pos_ok(exp, got, txt) \
+    ok(exp == got, "wrong " #txt " expected %d got %d\n", exp, got);
+
+#define check_pos(hwEdit, set_height, test_top, test_height, test_left) \
+do { \
+    RECT format_rect; \
+    int left_margin; \
+    set_client_height(hwEdit, set_height); \
+    SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM) &format_rect); \
+    left_margin = LOWORD(SendMessageA(hwEdit, EM_GETMARGINS, 0, 0)); \
+    edit_pos_ok(test_top, format_rect.top, vertical position); \
+    edit_pos_ok((int)test_height, format_rect.bottom - format_rect.top, height); \
+    edit_pos_ok(test_left, format_rect.left - left_margin, left); \
+} while(0)
+
+static void test_text_position_style(DWORD style)
+{
+    HWND hwEdit;
+    HFONT font, oldFont;
+    HDC dc;
+    TEXTMETRICA metrics;
+    INT b, bm, b2, b3;
+    BOOL xb, single_line = !(style & ES_MULTILINE);
+
+    b = GetSystemMetrics(SM_CYBORDER) + 1;
+    b2 = 2 * b;
+    b3 = 3 * b;
+    bm = b2 - 1;
+
+    /* Get a stock font for which we can determine the metrics */
+    font = GetStockObject(SYSTEM_FONT);
+    ok (font != NULL, "GetStockObject SYSTEM_FONT failed\n");
+    dc = GetDC(NULL);
+    ok (dc != NULL, "GetDC() failed\n");
+    oldFont = SelectObject(dc, font);
+    xb = GetTextMetricsA(dc, &metrics);
+    ok (xb, "GetTextMetrics failed\n");
+    SelectObject(dc, oldFont);
+    ReleaseDC(NULL, dc);
+
+    /* Windows' edit control has some bugs in multi-line mode:
+     * - Sometimes the format rectangle doesn't get updated
+     *   (see workaround in set_client_height())
+     * - If the height of the control is smaller than the height of a text
+     *   line, the format rectangle is still as high as a text line
+     *   (higher than the client rectangle) and the caret is not shown
+     */
+
+    /* Edit controls that are in a parent window */
+
+    hwEdit = create_child_editcontrol(style | WS_VISIBLE, 0);
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE);
+    if (single_line)
+    check_pos(hwEdit, metrics.tmHeight -  1, 0, metrics.tmHeight - 1, 0);
+    check_pos(hwEdit, metrics.tmHeight     , 0, metrics.tmHeight    , 0);
+    check_pos(hwEdit, metrics.tmHeight +  1, 0, metrics.tmHeight    , 0);
+    check_pos(hwEdit, metrics.tmHeight +  2, 0, metrics.tmHeight    , 0);
+    check_pos(hwEdit, metrics.tmHeight + 10, 0, metrics.tmHeight    , 0);
+    destroy_child_editcontrol(hwEdit);
+
+    hwEdit = create_child_editcontrol(style | WS_BORDER | WS_VISIBLE, 0);
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE);
+    if (single_line)
+    check_pos(hwEdit, metrics.tmHeight -  1, 0, metrics.tmHeight - 1, b);
+    check_pos(hwEdit, metrics.tmHeight     , 0, metrics.tmHeight    , b);
+    check_pos(hwEdit, metrics.tmHeight +  1, 0, metrics.tmHeight    , b);
+    check_pos(hwEdit, metrics.tmHeight + bm, 0, metrics.tmHeight    , b);
+    check_pos(hwEdit, metrics.tmHeight + b2, b, metrics.tmHeight    , b);
+    check_pos(hwEdit, metrics.tmHeight + b3, b, metrics.tmHeight    , b);
+    destroy_child_editcontrol(hwEdit);
+
+    hwEdit = create_child_editcontrol(style | WS_VISIBLE, WS_EX_CLIENTEDGE);
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE);
+    if (single_line)
+    check_pos(hwEdit, metrics.tmHeight -  1, 0, metrics.tmHeight - 1, 1);
+    check_pos(hwEdit, metrics.tmHeight     , 0, metrics.tmHeight    , 1);
+    check_pos(hwEdit, metrics.tmHeight +  1, 0, metrics.tmHeight    , 1);
+    check_pos(hwEdit, metrics.tmHeight +  2, 1, metrics.tmHeight    , 1);
+    check_pos(hwEdit, metrics.tmHeight + 10, 1, metrics.tmHeight    , 1);
+    destroy_child_editcontrol(hwEdit);
+
+    hwEdit = create_child_editcontrol(style | WS_BORDER | WS_VISIBLE, WS_EX_CLIENTEDGE);
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE);
+    if (single_line)
+    check_pos(hwEdit, metrics.tmHeight -  1, 0, metrics.tmHeight - 1, 1);
+    check_pos(hwEdit, metrics.tmHeight     , 0, metrics.tmHeight    , 1);
+    check_pos(hwEdit, metrics.tmHeight +  1, 0, metrics.tmHeight    , 1);
+    check_pos(hwEdit, metrics.tmHeight +  2, 1, metrics.tmHeight    , 1);
+    check_pos(hwEdit, metrics.tmHeight + 10, 1, metrics.tmHeight    , 1);
+    destroy_child_editcontrol(hwEdit);
+
+
+    /* Edit controls that are popup windows */
+
+    hwEdit = create_editcontrol(style | WS_POPUP, 0);
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE);
+    if (single_line)
+    check_pos(hwEdit, metrics.tmHeight -  1, 0, metrics.tmHeight - 1, 0);
+    check_pos(hwEdit, metrics.tmHeight     , 0, metrics.tmHeight    , 0);
+    check_pos(hwEdit, metrics.tmHeight +  1, 0, metrics.tmHeight    , 0);
+    check_pos(hwEdit, metrics.tmHeight +  2, 0, metrics.tmHeight    , 0);
+    check_pos(hwEdit, metrics.tmHeight + 10, 0, metrics.tmHeight    , 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(style | WS_POPUP | WS_BORDER, 0);
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE);
+    if (single_line)
+    check_pos(hwEdit, metrics.tmHeight -  1, 0, metrics.tmHeight - 1, b);
+    check_pos(hwEdit, metrics.tmHeight     , 0, metrics.tmHeight    , b);
+    check_pos(hwEdit, metrics.tmHeight +  1, 0, metrics.tmHeight    , b);
+    check_pos(hwEdit, metrics.tmHeight + bm, 0, metrics.tmHeight    , b);
+    check_pos(hwEdit, metrics.tmHeight + b2, b, metrics.tmHeight    , b);
+    check_pos(hwEdit, metrics.tmHeight + b3, b, metrics.tmHeight    , b);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(style | WS_POPUP, WS_EX_CLIENTEDGE);
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE);
+    if (single_line)
+    check_pos(hwEdit, metrics.tmHeight -  1, 0, metrics.tmHeight - 1, 1);
+    check_pos(hwEdit, metrics.tmHeight     , 0, metrics.tmHeight    , 1);
+    check_pos(hwEdit, metrics.tmHeight +  1, 0, metrics.tmHeight    , 1);
+    check_pos(hwEdit, metrics.tmHeight +  2, 1, metrics.tmHeight    , 1);
+    check_pos(hwEdit, metrics.tmHeight + 10, 1, metrics.tmHeight    , 1);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(style | WS_POPUP | WS_BORDER, WS_EX_CLIENTEDGE);
+    SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE);
+    if (single_line)
+    check_pos(hwEdit, metrics.tmHeight -  1, 0, metrics.tmHeight - 1, 1);
+    check_pos(hwEdit, metrics.tmHeight     , 0, metrics.tmHeight    , 1);
+    check_pos(hwEdit, metrics.tmHeight +  1, 0, metrics.tmHeight    , 1);
+    check_pos(hwEdit, metrics.tmHeight +  2, 1, metrics.tmHeight    , 1);
+    check_pos(hwEdit, metrics.tmHeight + 10, 1, metrics.tmHeight    , 1);
+    DestroyWindow(hwEdit);
+}
+
+static void test_text_position(void)
+{
+    trace("EDIT: Text position (Single line)\n");
+    test_text_position_style(ES_AUTOHSCROLL | ES_AUTOVSCROLL);
+    trace("EDIT: Text position (Multi line)\n");
+    test_text_position_style(ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL);
+}
+
+static void test_espassword(void)
+{
+    HWND hwEdit;
+    LONG r;
+    char buffer[1024];
+    const char* password = "secret";
+
+    hwEdit = create_editcontrol(ES_PASSWORD, 0);
+    r = get_edit_style(hwEdit);
+    ok(r == ES_PASSWORD, "Wrong style expected ES_PASSWORD got: 0x%x\n", r);
+    /* set text */
+    r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) password);
+    ok(r == TRUE, "Expected: %d, got: %d\n", TRUE, r);
+
+    /* select all, cut (ctrl-x) */
+    SendMessageA(hwEdit, EM_SETSEL, 0, -1);
+    r = SendMessageA(hwEdit, WM_CHAR, 24, 0);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+
+    /* get text */
+    r = SendMessageA(hwEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
+    ok(r == strlen(password), "Expected: %s, got len %d\n", password, r);
+    ok(strcmp(buffer, password) == 0, "expected %s, got %s\n", password, buffer);
+
+    r = OpenClipboard(hwEdit);
+    ok(r == TRUE, "expected %d, got %d\n", TRUE, r);
+    r = EmptyClipboard();
+    ok(r == TRUE, "expected %d, got %d\n", TRUE, r);
+    r = CloseClipboard();
+    ok(r == TRUE, "expected %d, got %d\n", TRUE, r);
+
+    /* select all, copy (ctrl-c) and paste (ctrl-v) */
+    SendMessageA(hwEdit, EM_SETSEL, 0, -1);
+    r = SendMessageA(hwEdit, WM_CHAR, 3, 0);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+    r = SendMessageA(hwEdit, WM_CHAR, 22, 0);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+
+    /* get text */
+    buffer[0] = 0;
+    r = SendMessageA(hwEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
+    ok(r == 0, "Expected: 0, got: %d\n", r);
+    ok(strcmp(buffer, "") == 0, "expected empty string, got %s\n", buffer);
+
+    DestroyWindow(hwEdit);
+}
+
+static void test_undo(void)
+{
+    HWND hwEdit;
+    LONG r;
+    DWORD cpMin, cpMax;
+    char buffer[1024];
+    const char* text = "undo this";
+
+    hwEdit = create_editcontrol(0, 0);
+    r = get_edit_style(hwEdit);
+    ok(0 == r, "Wrong style expected 0x%x got: 0x%x\n", 0, r);
+
+    /* set text */
+    r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) text);
+    ok(TRUE == r, "Expected: %d, got: %d\n", TRUE, r);
+
+    /* select all, */
+    cpMin = cpMax = 0xdeadbeef;
+    SendMessageA(hwEdit, EM_SETSEL, 0, -1);
+    r = SendMessageA(hwEdit, EM_GETSEL, (WPARAM) &cpMin, (LPARAM) &cpMax);
+    ok((strlen(text) << 16) == r, "Unexpected length %d\n", r);
+    ok(0 == cpMin, "Expected: %d, got %d\n", 0, cpMin);
+    ok(9 == cpMax, "Expected: %d, got %d\n", 9, cpMax);
+
+    /* cut (ctrl-x) */
+    r = SendMessageA(hwEdit, WM_CHAR, 24, 0);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+
+    /* get text */
+    buffer[0] = 0;
+    r = SendMessageA(hwEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
+    ok(0 == r, "Expected: %d, got len %d\n", 0, r);
+    ok(0 == strcmp(buffer, ""), "expected %s, got %s\n", "", buffer);
+
+    /* undo (ctrl-z) */
+    r = SendMessageA(hwEdit, WM_CHAR, 26, 0);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+
+    /* get text */
+    buffer[0] = 0;
+    r = SendMessageA(hwEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
+    ok(strlen(text) == r, "Unexpected length %d\n", r);
+    ok(0 == strcmp(buffer, text), "expected %s, got %s\n", text, buffer);
+
+    /* undo again (ctrl-z) */
+    r = SendMessageA(hwEdit, WM_CHAR, 26, 0);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+
+    /* get text */
+    buffer[0] = 0;
+    r = SendMessageA(hwEdit, WM_GETTEXT, 1024, (LPARAM) buffer);
+    ok(r == 0, "Expected: %d, got len %d\n", 0, r);
+    ok(0 == strcmp(buffer, ""), "expected %s, got %s\n", "", buffer);
+
+    DestroyWindow(hwEdit);
+}
+
+static void test_enter(void)
+{
+    char buffer[16];
+    HWND hwEdit;
+    LONG r;
+
+    /* multiline */
+    hwEdit = create_editcontrol(ES_MULTILINE, 0);
+    r = get_edit_style(hwEdit);
+    ok(ES_MULTILINE == r, "Wrong style expected ES_MULTILINE got: 0x%x\n", r);
+
+    /* set text */
+    r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) "");
+    ok(TRUE == r, "Expected: %d, got: %d\n", TRUE, r);
+
+    r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+
+    /* get text */
+    buffer[0] = 0;
+    r = SendMessageA(hwEdit, WM_GETTEXT, 16, (LPARAM) buffer);
+    ok(2 == r, "Expected: %d, got len %d\n", 2, r);
+    ok(0 == strcmp(buffer, "\r\n"), "expected \"\\r\\n\", got \"%s\"\n", buffer);
+
+    DestroyWindow (hwEdit);
+
+    /* single line */
+    hwEdit = create_editcontrol(0, 0);
+    r = get_edit_style(hwEdit);
+    ok(0 == r, "Wrong style expected 0x%x got: 0x%x\n", 0, r);
+
+    /* set text */
+    r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) "");
+    ok(TRUE == r, "Expected: %d, got: %d\n", TRUE, r);
+
+    r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+
+    /* get text */
+    buffer[0] = 0;
+    r = SendMessageA(hwEdit, WM_GETTEXT, 16, (LPARAM) buffer);
+    ok(0 == r, "Expected: %d, got len %d\n", 0, r);
+    ok(0 == strcmp(buffer, ""), "expected \"\", got \"%s\"\n", buffer);
+
+    DestroyWindow(hwEdit);
+
+    /* single line with ES_WANTRETURN */
+    hwEdit = create_editcontrol(ES_WANTRETURN, 0);
+    r = get_edit_style(hwEdit);
+    ok(ES_WANTRETURN == r, "Wrong style expected ES_WANTRETURN got: 0x%x\n", r);
+
+    /* set text */
+    r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) "");
+    ok(TRUE == r, "Expected: %d, got: %d\n", TRUE, r);
+
+    r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+
+    /* get text */
+    buffer[0] = 0;
+    r = SendMessageA(hwEdit, WM_GETTEXT, 16, (LPARAM) buffer);
+    ok(0 == r, "Expected: %d, got len %d\n", 0, r);
+    ok(0 == strcmp(buffer, ""), "expected \"\", got \"%s\"\n", buffer);
+
+    DestroyWindow(hwEdit);
+}
+
+static void test_tab(void)
+{
+    char buffer[16];
+    HWND hwEdit;
+    LONG r;
+
+    /* multiline */
+    hwEdit = create_editcontrol(ES_MULTILINE, 0);
+    r = get_edit_style(hwEdit);
+    ok(ES_MULTILINE == r, "Wrong style expected ES_MULTILINE got: 0x%x\n", r);
+
+    /* set text */
+    r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) "");
+    ok(TRUE == r, "Expected: %d, got: %d\n", TRUE, r);
+
+    r = SendMessageA(hwEdit, WM_CHAR, VK_TAB, 0);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+
+    /* get text */
+    buffer[0] = 0;
+    r = SendMessageA(hwEdit, WM_GETTEXT, 16, (LPARAM) buffer);
+    ok(1 == r, "Expected: %d, got len %d\n", 1, r);
+    ok(0 == strcmp(buffer, "\t"), "expected \"\\t\", got \"%s\"\n", buffer);
+
+    DestroyWindow(hwEdit);
+
+    /* single line */
+    hwEdit = create_editcontrol(0, 0);
+    r = get_edit_style(hwEdit);
+    ok(0 == r, "Wrong style expected 0x%x got: 0x%x\n", 0, r);
+
+    /* set text */
+    r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) "");
+    ok(TRUE == r, "Expected: %d, got: %d\n", TRUE, r);
+
+    r = SendMessageA(hwEdit, WM_CHAR, VK_TAB, 0);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+
+    /* get text */
+    buffer[0] = 0;
+    r = SendMessageA(hwEdit, WM_GETTEXT, 16, (LPARAM) buffer);
+    ok(0 == r, "Expected: %d, got len %d\n", 0, r);
+    ok(0 == strcmp(buffer, ""), "expected \"\", got \"%s\"\n", buffer);
+
+    DestroyWindow(hwEdit);
+}
+
+static void test_edit_dialog(void)
+{
+    int r;
+
+    /* from bug 11841 */
+    r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 0);
+    ok(333 == r, "Expected %d, got %d\n", 333, r);
+    r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 1);
+    ok(111 == r, "Expected %d, got %d\n", 111, r);
+    r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 2);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+
+    /* more tests for WM_CHAR */
+    r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 3);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 4);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 5);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+
+    /* more tests for WM_KEYDOWN + WM_CHAR */
+    r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 6);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 7);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 8);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+
+    /* tests with an editable edit control */
+    r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 0);
+    ok(333 == r, "Expected %d, got %d\n", 333, r);
+    r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 1);
+    ok(111 == r, "Expected %d, got %d\n", 111, r);
+    r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 2);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+
+    /* tests for WM_CHAR */
+    r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 3);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 4);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 5);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+
+    /* tests for WM_KEYDOWN + WM_CHAR */
+    r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 6);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 7);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 8);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+
+    /* multiple tab tests */
+    r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 9);
+    ok(22 == r, "Expected %d, got %d\n", 22, r);
+    r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 10);
+    ok(33 == r, "Expected %d, got %d\n", 33, r);
+}
+
+static void test_multi_edit_dialog(void)
+{
+    int r;
+
+    /* test for multiple edit dialogs (bug 12319) */
+    r = DialogBoxParamA(hinst, "MULTI_EDIT_DIALOG", NULL, multi_edit_dialog_proc, 0);
+    ok(2222 == r, "Expected %d, got %d\n", 2222, r);
+    r = DialogBoxParamA(hinst, "MULTI_EDIT_DIALOG", NULL, multi_edit_dialog_proc, 1);
+    ok(1111 == r, "Expected %d, got %d\n", 1111, r);
+    r = DialogBoxParamA(hinst, "MULTI_EDIT_DIALOG", NULL, multi_edit_dialog_proc, 2);
+    ok(2222 == r, "Expected %d, got %d\n", 2222, r);
+    r = DialogBoxParamA(hinst, "MULTI_EDIT_DIALOG", NULL, multi_edit_dialog_proc, 3);
+    ok(11 == r, "Expected %d, got %d\n", 11, r);
+}
+
+static void test_wantreturn_edit_dialog(void)
+{
+    int r;
+
+    /* tests for WM_KEYDOWN */
+    r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 0);
+    ok(333 == r, "Expected %d, got %d\n", 333, r);
+    r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 1);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 2);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+
+    /* tests for WM_CHAR */
+    r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 3);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 4);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 5);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+
+    /* tests for WM_KEYDOWN + WM_CHAR */
+    r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 6);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 7);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 8);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+}
+
+static void test_singleline_wantreturn_edit_dialog(void)
+{
+    int r;
+
+    /* tests for WM_KEYDOWN */
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 0);
+    ok(222 == r, "Expected %d, got %d\n", 222, r);
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 1);
+    ok(111 == r, "Expected %d, got %d\n", 111, r);
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 2);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+
+    /* tests for WM_CHAR */
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 3);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 4);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 5);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+
+    /* tests for WM_KEYDOWN + WM_CHAR */
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 6);
+    ok(222 == r, "Expected %d, got %d\n", 222, r);
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 7);
+    ok(111 == r, "Expected %d, got %d\n", 111, r);
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 8);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+
+    /* tests for WM_KEYDOWN */
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 0);
+    ok(222 == r, "Expected %d, got %d\n", 222, r);
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 1);
+    ok(111 == r, "Expected %d, got %d\n", 111, r);
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 2);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+
+    /* tests for WM_CHAR */
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 3);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 4);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 5);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+
+    /* tests for WM_KEYDOWN + WM_CHAR */
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 6);
+    ok(222 == r, "Expected %d, got %d\n", 222, r);
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 7);
+    ok(111 == r, "Expected %d, got %d\n", 111, r);
+    r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 8);
+    ok(444 == r, "Expected %d, got %d\n", 444, r);
+}
+
+static int child_edit_wmkeydown_num_messages = 0;
+static INT_PTR CALLBACK child_edit_wmkeydown_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+        case WM_DESTROY:
+        case WM_NCDESTROY:
+            break;
+
+        default:
+            child_edit_wmkeydown_num_messages++;
+            break;
+    }
+
+    return FALSE;
+}
+
+static void test_child_edit_wmkeydown(void)
+{
+    HWND hwEdit, hwParent;
+    int r;
+
+    hwEdit = create_child_editcontrol(0, 0);
+    hwParent = GetParent(hwEdit);
+    SetWindowLongPtrA(hwParent, GWLP_WNDPROC, (LONG_PTR)child_edit_wmkeydown_proc);
+    r = SendMessageA(hwEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    ok(0 == child_edit_wmkeydown_num_messages, "expected 0, got %d\n", child_edit_wmkeydown_num_messages);
+    destroy_child_editcontrol(hwEdit);
+}
+
+static BOOL got_en_setfocus = FALSE;
+static BOOL got_wm_capturechanged = FALSE;
+static LRESULT (CALLBACK *p_edit_proc)(HWND, UINT, WPARAM, LPARAM);
+
+static LRESULT CALLBACK edit4_wnd_procA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    switch (msg) {
+        case WM_COMMAND:
+            switch (HIWORD(wParam))
+            {
+                case EN_SETFOCUS:
+                    got_en_setfocus = TRUE;
+                    break;
+            }
+            break;
+        case WM_CAPTURECHANGED:
+            if (hWnd != (HWND)lParam)
+            {
+                got_wm_capturechanged = TRUE;
+                EndMenu();
+            }
+            break;
+    }
+    return DefWindowProcA(hWnd, msg, wParam, lParam);
+}
+
+struct context_menu_messages
+{
+    unsigned int wm_command, em_setsel;
+};
+
+static struct context_menu_messages menu_messages;
+
+static LRESULT CALLBACK child_edit_menu_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    switch (msg)
+    {
+    case WM_ENTERIDLE:
+        if (wParam == MSGF_MENU)
+        {
+            HWND hwndMenu = (HWND)lParam;
+            MENUBARINFO mbi = { sizeof(mbi) };
+            if (GetMenuBarInfo(hwndMenu, OBJID_CLIENT, 0, &mbi))
+            {
+                MENUITEMINFOA mii = { sizeof(MENUITEMINFOA), MIIM_STATE };
+                if (GetMenuItemInfoA(mbi.hMenu, EM_SETSEL, FALSE, &mii))
+                {
+                    if (mii.fState & MFS_HILITE)
+                    {
+                        PostMessageA(hwnd, WM_KEYDOWN, VK_RETURN, 0x1c0001);
+                        PostMessageA(hwnd, WM_KEYUP, VK_RETURN, 0x1c0001);
+                    }
+                    else
+                    {
+                        PostMessageA(hwnd, WM_KEYDOWN, VK_DOWN, 0x500001);
+                        PostMessageA(hwnd, WM_KEYUP, VK_DOWN, 0x500001);
+                    }
+                }
+            }
+        }
+        break;
+    case WM_COMMAND:
+        menu_messages.wm_command++;
+        break;
+    case EM_SETSEL:
+        menu_messages.em_setsel++;
+        break;
+    }
+    return CallWindowProcA(p_edit_proc, hwnd, msg, wParam, lParam);
+}
+
+static void test_contextmenu(void)
+{
+    HWND hwndMain, hwndEdit;
+    MSG msg;
+
+    hwndMain = CreateWindowA(szEditTest4Class, "ET4", WS_OVERLAPPEDWINDOW|WS_VISIBLE,
+                            0, 0, 200, 200, NULL, NULL, hinst, NULL);
+    assert(hwndMain);
+
+    hwndEdit = CreateWindowA("EDIT", NULL,
+                           WS_CHILD|WS_BORDER|WS_VISIBLE|ES_LEFT|ES_AUTOHSCROLL,
+                           0, 0, 150, 50, /* important this not be 0 size. */
+                           hwndMain, (HMENU) ID_EDITTEST2, hinst, NULL);
+    assert(hwndEdit);
+
+    SetFocus(NULL);
+    SetCapture(hwndMain);
+    SendMessageA(hwndEdit, WM_CONTEXTMENU, (WPARAM)hwndEdit, MAKEWORD(10, 10));
+    ok(got_en_setfocus, "edit box didn't get focused\n");
+    ok(got_wm_capturechanged, "main window capture did not change\n");
+
+    DestroyWindow(hwndEdit);
+
+    hwndEdit = CreateWindowA("EDIT", "Test Text",
+                             WS_CHILD | WS_BORDER | WS_VISIBLE,
+                             0, 0, 100, 100,
+                             hwndMain, NULL, hinst, NULL);
+    memset(&menu_messages, 0, sizeof(menu_messages));
+    p_edit_proc = (void*)SetWindowLongPtrA(hwndEdit, GWLP_WNDPROC,
+                                           (ULONG_PTR)child_edit_menu_proc);
+
+    SetFocus(hwndEdit);
+    SendMessageA(hwndEdit, WM_SETTEXT, 0, (LPARAM)"foo");
+    SendMessageA(hwndEdit, WM_CONTEXTMENU, (WPARAM)hwndEdit, MAKEWORD(-1, -1));
+    while (PeekMessageA(&msg, hwndEdit, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+    ok(menu_messages.wm_command == 0,
+       "Expected no WM_COMMAND messages, got %d\n", menu_messages.wm_command);
+    ok(menu_messages.em_setsel == 1,
+       "Expected 1 EM_SETSEL message, got %d\n", menu_messages.em_setsel);
+
+    DestroyWindow(hwndEdit);
+    DestroyWindow(hwndMain);
+}
+
+static BOOL register_classes(void)
+{
+    WNDCLASSA test2;
+    WNDCLASSA test3;
+    WNDCLASSA test4;
+    WNDCLASSA text_position;
+
+    test2.style = 0;
+    test2.lpfnWndProc = ET2_WndProc;
+    test2.cbClsExtra = 0;
+    test2.cbWndExtra = 0;
+    test2.hInstance = hinst;
+    test2.hIcon = NULL;
+    test2.hCursor = LoadCursorA (NULL, (LPCSTR)IDC_ARROW);
+    test2.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+    test2.lpszMenuName = NULL;
+    test2.lpszClassName = szEditTest2Class;
+    if (!RegisterClassA(&test2)) return FALSE;
+
+    test3.style = 0;
+    test3.lpfnWndProc = edit3_wnd_procA;
+    test3.cbClsExtra = 0;
+    test3.cbWndExtra = 0;
+    test3.hInstance = hinst;
+    test3.hIcon = 0;
+    test3.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
+    test3.hbrBackground = GetStockObject(WHITE_BRUSH);
+    test3.lpszMenuName = NULL;
+    test3.lpszClassName = szEditTest3Class;
+    if (!RegisterClassA(&test3)) return FALSE;
+
+    test4.style = 0;
+    test4.lpfnWndProc = edit4_wnd_procA;
+    test4.cbClsExtra = 0;
+    test4.cbWndExtra = 0;
+    test4.hInstance = hinst;
+    test4.hIcon = NULL;
+    test4.hCursor = LoadCursorA (NULL, (LPCSTR)IDC_ARROW);
+    test4.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+    test4.lpszMenuName = NULL;
+    test4.lpszClassName = szEditTest4Class;
+    if (!RegisterClassA(&test4)) return FALSE;
+
+    text_position.style = CS_HREDRAW | CS_VREDRAW;
+    text_position.cbClsExtra = 0;
+    text_position.cbWndExtra = 0;
+    text_position.hInstance = hinst;
+    text_position.hIcon = NULL;
+    text_position.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
+    text_position.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
+    text_position.lpszMenuName = NULL;
+    text_position.lpszClassName = szEditTextPositionClass;
+    text_position.lpfnWndProc = DefWindowProcA;
+    if (!RegisterClassA(&text_position)) return FALSE;
+
+    return TRUE;
+}
+
+static void UnregisterWindowClasses (void)
+{
+    UnregisterClassA(szEditTest2Class, hinst);
+    UnregisterClassA(szEditTest3Class, hinst);
+    UnregisterClassA(szEditTest4Class, hinst);
+    UnregisterClassA(szEditTextPositionClass, hinst);
+}
+
+static void test_fontsize(void)
+{
+    HWND hwEdit;
+    HFONT hfont;
+    HDC hDC;
+    LOGFONTA lf;
+    LONG r;
+    char szLocalString[MAXLEN];
+    int dpi;
+
+    hDC = GetDC(NULL);
+    dpi = GetDeviceCaps(hDC, LOGPIXELSY);
+    ReleaseDC(NULL, hDC);
+
+    memset(&lf,0,sizeof(LOGFONTA));
+    strcpy(lf.lfFaceName,"Arial");
+    lf.lfHeight = -300; /* taller than the edit box */
+    lf.lfWeight = 500;
+    hfont = CreateFontIndirectA(&lf);
+
+    trace("EDIT: Oversized font (Multi line)\n");
+    hwEdit= CreateWindowA("EDIT", NULL, ES_MULTILINE|ES_AUTOHSCROLL,
+                           0, 0, (150 * dpi) / 96, (50 * dpi) / 96, NULL, NULL,
+                           hinst, NULL);
+
+    SendMessageA(hwEdit,WM_SETFONT,(WPARAM)hfont,0);
+
+    if (winetest_interactive)
+        ShowWindow (hwEdit, SW_SHOW);
+
+    r = SendMessageA(hwEdit, WM_CHAR, 'A', 1);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+    r = SendMessageA(hwEdit, WM_CHAR, 'B', 1);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+    r = SendMessageA(hwEdit, WM_CHAR, 'C', 1);
+    ok(1 == r, "Expected: %d, got: %d\n", 1, r);
+
+    GetWindowTextA(hwEdit, szLocalString, MAXLEN);
+    ok(strcmp(szLocalString, "ABC")==0,
+       "Wrong contents of edit: %s\n", szLocalString);
+
+    r = SendMessageA(hwEdit, EM_POSFROMCHAR,0,0);
+    ok(r != -1,"EM_POSFROMCHAR failed index 0\n");
+    r = SendMessageA(hwEdit, EM_POSFROMCHAR,1,0);
+    ok(r != -1,"EM_POSFROMCHAR failed index 1\n");
+    r = SendMessageA(hwEdit, EM_POSFROMCHAR,2,0);
+    ok(r != -1,"EM_POSFROMCHAR failed index 2\n");
+    r = SendMessageA(hwEdit, EM_POSFROMCHAR,3,0);
+    ok(r == -1,"EM_POSFROMCHAR succeeded index 3\n");
+
+    DestroyWindow (hwEdit);
+    DeleteObject(hfont);
+}
+
+struct dialog_mode_messages
+{
+    int wm_getdefid, wm_close, wm_command, wm_nextdlgctl;
+};
+
+static struct dialog_mode_messages dm_messages;
+
+static void zero_dm_messages(void)
+{
+    dm_messages.wm_command      = 0;
+    dm_messages.wm_close        = 0;
+    dm_messages.wm_getdefid     = 0;
+    dm_messages.wm_nextdlgctl   = 0;
+}
+
+#define test_dm_messages(wmcommand, wmclose, wmgetdefid, wmnextdlgctl) \
+    ok(dm_messages.wm_command == wmcommand, "expected %d WM_COMMAND messages, " \
+    "got %d\n", wmcommand, dm_messages.wm_command); \
+    ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE messages, " \
+    "got %d\n", wmclose, dm_messages.wm_close); \
+    ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID messages, " \
+    "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\
+    ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL messages, " \
+    "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl)
+
+static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch (iMsg)
+    {
+        case WM_COMMAND:
+            dm_messages.wm_command++;
+            break;
+        case DM_GETDEFID:
+            dm_messages.wm_getdefid++;
+            return MAKELONG(ID_EDITTESTDBUTTON, DC_HASDEFID);
+        case WM_NEXTDLGCTL:
+            dm_messages.wm_nextdlgctl++;
+            break;
+        case WM_CLOSE:
+            dm_messages.wm_close++;
+            break;
+    }
+
+    return DefWindowProcA(hwnd, iMsg, wParam, lParam);
+}
+
+static void test_dialogmode(void)
+{
+    HWND hwEdit, hwParent, hwButton;
+    MSG msg= {0};
+    int len, r;
+    hwEdit = create_child_editcontrol(ES_MULTILINE, 0);
+
+    r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0x1c0001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    len = SendMessageA(hwEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok(11 == len, "expected 11, got %d\n", len);
+
+    r = SendMessageA(hwEdit, WM_GETDLGCODE, 0, 0);
+    ok(0x8d == r, "expected 0x8d, got 0x%x\n", r);
+
+    r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0x1c0001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    len = SendMessageA(hwEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok(13 == len, "expected 13, got %d\n", len);
+
+    r = SendMessageA(hwEdit, WM_GETDLGCODE, 0, (LPARAM)&msg);
+    ok(0x8d == r, "expected 0x8d, got 0x%x\n", r);
+    r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0x1c0001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    len = SendMessageA(hwEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok(13 == len, "expected 13, got %d\n", len);
+
+    r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0x1c0001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    len = SendMessageA(hwEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok(13 == len, "expected 13, got %d\n", len);
+
+    destroy_child_editcontrol(hwEdit);
+
+    hwEdit = create_editcontrol(ES_MULTILINE, 0);
+
+    r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0x1c0001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    len = SendMessageA(hwEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok(11 == len, "expected 11, got %d\n", len);
+
+    msg.hwnd = hwEdit;
+    msg.message = WM_KEYDOWN;
+    msg.wParam = VK_BACK;
+    msg.lParam = 0xe0001;
+    r = SendMessageA(hwEdit, WM_GETDLGCODE, VK_BACK, (LPARAM)&msg);
+    ok(0x8d == r, "expected 0x8d, got 0x%x\n", r);
+
+    r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0x1c0001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    len = SendMessageA(hwEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok(11 == len, "expected 11, got %d\n", len);
+
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_child_editcontrol(0, 0);
+    hwParent = GetParent(hwEdit);
+    SetWindowLongPtrA(hwParent, GWLP_WNDPROC, (LONG_PTR)dialog_mode_wnd_proc);
+
+    zero_dm_messages();
+    r = SendMessageA(hwEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    test_dm_messages(0, 0, 0, 0);
+    zero_dm_messages();
+
+    r = SendMessageA(hwEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    test_dm_messages(0, 0, 0, 0);
+    zero_dm_messages();
+
+    msg.hwnd = hwEdit;
+    msg.message = WM_KEYDOWN;
+    msg.wParam = VK_TAB;
+    msg.lParam = 0xf0001;
+    r = SendMessageA(hwEdit, WM_GETDLGCODE, VK_TAB, (LPARAM)&msg);
+    ok(0x89 == r, "expected 0x89, got 0x%x\n", r);
+    test_dm_messages(0, 0, 0, 0);
+    zero_dm_messages();
+
+    r = SendMessageA(hwEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    test_dm_messages(0, 0, 0, 0);
+    zero_dm_messages();
+
+    destroy_child_editcontrol(hwEdit);
+
+    hwEdit = create_child_editcontrol(ES_MULTILINE, 0);
+    hwParent = GetParent(hwEdit);
+    SetWindowLongPtrA(hwParent, GWLP_WNDPROC, (LONG_PTR)dialog_mode_wnd_proc);
+
+    r = SendMessageA(hwEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    test_dm_messages(0, 0, 0, 0);
+    zero_dm_messages();
+
+    msg.hwnd = hwEdit;
+    msg.message = WM_KEYDOWN;
+    msg.wParam = VK_ESCAPE;
+    msg.lParam = 0x10001;
+    r = SendMessageA(hwEdit, WM_GETDLGCODE, VK_ESCAPE, (LPARAM)&msg);
+    ok(0x8d == r, "expected 0x8d, got 0x%x\n", r);
+    test_dm_messages(0, 0, 0, 0);
+    zero_dm_messages();
+
+    r = SendMessageA(hwEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    test_dm_messages(0, 0, 0, 0);
+    zero_dm_messages();
+
+    r = SendMessageA(hwEdit, WM_KEYDOWN, VK_TAB, 0xf0001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    test_dm_messages(0, 0, 0, 1);
+    zero_dm_messages();
+
+    r = SendMessageA(hwEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    test_dm_messages(0, 0, 1, 0);
+    zero_dm_messages();
+
+    hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON,
+        100, 100, 50, 20, hwParent, (HMENU)ID_EDITTESTDBUTTON, hinst, NULL);
+    ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError());
+
+    r = SendMessageA(hwEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001);
+    ok(1 == r, "expected 1, got %d\n", r);
+    test_dm_messages(0, 0, 1, 1);
+    zero_dm_messages();
+
+    DestroyWindow(hwButton);
+    destroy_child_editcontrol(hwEdit);
+}
+
+static void test_EM_GETHANDLE(void)
+{
+    static const WCHAR str1W[] = {'1','1','1','1','+','1','1','1','1','+','1','1','1','1','#',0};
+    static const WCHAR str2W[] = {'2','2','2','2','-','2','2','2','2','-','2','2','2','2','-','2','2','2','2','#',0};
+    static const char str0[] = "untouched";
+    static const char str1[] = "1111+1111+1111#";
+    static const char str1_1[] = "2111+1111+1111#";
+    static const char str2[] = "2222-2222-2222-2222#";
+    static const char str3[] = "3333*3333*3333*3333*3333#";
+    CHAR    current[42];
+    HWND    hEdit;
+    HLOCAL  hmem;
+    HLOCAL  hmem2;
+    HLOCAL  halloc;
+    WCHAR  *buffer;
+    int     len;
+    int     r;
+
+    trace("EDIT: EM_GETHANDLE\n");
+
+    /* EM_GETHANDLE is not supported for a single line edit control */
+    hEdit = create_editcontrol(WS_BORDER, 0);
+    ok(hEdit != NULL, "got %p (expected != NULL)\n", hEdit);
+
+    hmem = (HGLOBAL) SendMessageA(hEdit, EM_GETHANDLE, 0, 0);
+    ok(hmem == NULL, "got %p (expected NULL)\n", hmem);
+    DestroyWindow(hEdit);
+
+    /* EM_GETHANDLE needs a multiline edit control */
+    hEdit = create_editcontrol(WS_BORDER | ES_MULTILINE, 0);
+    ok(hEdit != NULL, "got %p (expected != NULL)\n", hEdit);
+
+    /* set some text */
+    r = SendMessageA(hEdit, WM_SETTEXT, 0, (LPARAM)str1);
+    len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok((r == 1) && (len == lstrlenA(str1)), "got %d and %d (expected 1 and %d)\n", r, len, lstrlenA(str1));
+
+    lstrcpyA(current, str0);
+    r = SendMessageA(hEdit, WM_GETTEXT, sizeof(current), (LPARAM)current);
+    ok((r == lstrlenA(str1)) && !lstrcmpA(current, str1),
+        "got %d and \"%s\" (expected %d and \"%s\")\n", r, current, lstrlenA(str1), str1);
+
+    hmem = (HGLOBAL) SendMessageA(hEdit, EM_GETHANDLE, 0, 0);
+    ok(hmem != NULL, "got %p (expected != NULL)\n", hmem);
+    /* The buffer belongs to the app now. According to MSDN, the app has to LocalFree the
+       buffer, LocalAlloc a new buffer and pass it to the edit control with EM_SETHANDLE. */
+
+    buffer = LocalLock(hmem);
+    ok(buffer != NULL, "got %p (expected != NULL)\n", buffer);
+    len = lstrlenW(buffer);
+todo_wine
+    ok(len == lstrlenW(str1W) && !lstrcmpW(buffer, str1W), "Unexpected buffer contents %s, length %d.\n",
+        wine_dbgstr_w(buffer), len);
+    LocalUnlock(hmem);
+
+    /* See if WM_GETTEXTLENGTH/WM_GETTEXT still work. */
+    len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok(len == lstrlenA(str1), "Unexpected text length %d.\n", len);
+
+    lstrcpyA(current, str0);
+    r = SendMessageA(hEdit, WM_GETTEXT, sizeof(current), (LPARAM)current);
+    ok((r == lstrlenA(str1)) && !lstrcmpA(current, str1),
+        "Unexpected retval %d and text \"%s\" (expected %d and \"%s\")\n", r, current, lstrlenA(str1), str1);
+
+    /* Application altered buffer contents, see if WM_GETTEXTLENGTH/WM_GETTEXT pick that up. */
+    buffer = LocalLock(hmem);
+    ok(buffer != NULL, "got %p (expected != NULL)\n", buffer);
+    buffer[0] = '2';
+    LocalUnlock(hmem);
+
+    len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok(len == lstrlenA(str1_1), "Unexpected text length %d.\n", len);
+
+    lstrcpyA(current, str0);
+    r = SendMessageA(hEdit, WM_GETTEXT, sizeof(current), (LPARAM)current);
+todo_wine
+    ok(r == lstrlenA(str1_1) && !lstrcmpA(current, str1_1),
+        "Unexpected retval %d and text \"%s\" (expected %d and \"%s\")\n", r, current, lstrlenA(str1_1), str1_1);
+
+    /* See if WM_SETTEXT/EM_REPLACESEL work. */
+    r = SendMessageA(hEdit, WM_SETTEXT, 0, (LPARAM)str1);
+    ok(r, "Failed to set text.\n");
+
+    buffer = LocalLock(hmem);
+todo_wine
+    ok(buffer != NULL && buffer[0] == '1', "Unexpected buffer contents\n");
+    LocalUnlock(hmem);
+
+    r = SendMessageA(hEdit, EM_REPLACESEL, 0, (LPARAM)str1_1);
+    ok(r, "Failed to replace selection.\n");
+
+    buffer = LocalLock(hmem);
+todo_wine
+    ok(buffer != NULL && buffer[0] == '2', "Unexpected buffer contents\n");
+    LocalUnlock(hmem);
+
+    /* use LocalAlloc first to get a different handle */
+    halloc = LocalAlloc(LMEM_MOVEABLE, 42);
+    ok(halloc != NULL, "got %p (expected != NULL)\n", halloc);
+    /* prepare our new memory */
+    buffer = LocalLock(halloc);
+    ok(buffer != NULL, "got %p (expected != NULL)\n", buffer);
+    lstrcpyW(buffer, str2W);
+    LocalUnlock(halloc);
+
+    /* LocalFree the old memory handle before EM_SETHANDLE the new handle */
+    LocalFree(hmem);
+    /* use LocalAlloc after the LocalFree to likely consume the handle */
+    hmem2 = LocalAlloc(LMEM_MOVEABLE, 42);
+    ok(hmem2 != NULL, "got %p (expected != NULL)\n", hmem2);
+
+    SendMessageA(hEdit, EM_SETHANDLE, (WPARAM)halloc, 0);
+
+    len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0);
+todo_wine
+    ok(len == lstrlenA(str2), "got %d (expected %d)\n", len, lstrlenA(str2));
+
+    lstrcpyA(current, str0);
+    r = SendMessageA(hEdit, WM_GETTEXT, sizeof(current), (LPARAM)current);
+todo_wine
+    ok(r == lstrlenA(str2) && !lstrcmpA(current, str2),
+        "got %d and \"%s\" (expected %d and \"%s\")\n", r, current, lstrlenA(str2), str2);
+
+    /* set a different text */
+    r = SendMessageA(hEdit, WM_SETTEXT, 0, (LPARAM)str3);
+    len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok((r == 1) && (len == lstrlenA(str3)), "got %d and %d (expected 1 and %d)\n", r, len, lstrlenA(str3));
+
+    lstrcpyA(current, str0);
+    r = SendMessageA(hEdit, WM_GETTEXT, sizeof(current), (LPARAM)current);
+    ok((r == lstrlenA(str3)) && !lstrcmpA(current, str3),
+        "got %d and \"%s\" (expected %d and \"%s\")\n", r, current, lstrlenA(str3), str3);
+
+    LocalFree(hmem2);
+    DestroyWindow(hEdit);
+
+    /* Some apps have bugs ... */
+    hEdit = create_editcontrol(WS_BORDER | ES_MULTILINE, 0);
+
+    /* set some text */
+    r = SendMessageA(hEdit, WM_SETTEXT, 0, (LPARAM)str1);
+    len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok((r == 1) && (len == lstrlenA(str1)), "got %d and %d (expected 1 and %d)\n", r, len, lstrlenA(str1));
+
+    /* everything is normal up to EM_GETHANDLE */
+    hmem = (HGLOBAL) SendMessageA(hEdit, EM_GETHANDLE, 0, 0);
+    /* Some messages still work while other messages fail.
+       After LocalFree the memory handle, messages can crash the app */
+
+    /* A buggy editor used EM_GETHANDLE twice */
+    hmem2 = (HGLOBAL) SendMessageA(hEdit, EM_GETHANDLE, 0, 0);
+    ok(hmem2 == hmem, "got %p (expected %p)\n", hmem2, hmem);
+
+    /* Let the edit control free the memory handle */
+    SendMessageA(hEdit, EM_SETHANDLE, (WPARAM)hmem2, 0);
+
+    DestroyWindow(hEdit);
+}
+
+static void test_paste(void)
+{
+    static const char *str = "this is a simple text";
+    static const char *str2 = "first line\r\nsecond line";
+    HWND hEdit, hMultilineEdit;
+    HANDLE hmem, hmem_ret;
+    char *buffer;
+    int r, len;
+
+    hEdit = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    hMultilineEdit = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE, 0);
+
+    /* Prepare clipboard data with simple text */
+    hmem = GlobalAlloc(GMEM_MOVEABLE, 255);
+    ok(hmem != NULL, "got %p (expected != NULL)\n", hmem);
+    buffer = GlobalLock(hmem);
+    ok(buffer != NULL, "got %p (expected != NULL)\n", buffer);
+    strcpy(buffer, str);
+    GlobalUnlock(hmem);
+
+    r = OpenClipboard(hEdit);
+    ok(r == TRUE, "expected %d, got %d\n", TRUE, r);
+    r = EmptyClipboard();
+    ok(r == TRUE, "expected %d, got %d\n", TRUE, r);
+    hmem_ret = SetClipboardData(CF_TEXT, hmem);
+    ok(hmem_ret == hmem, "expected %p, got %p\n", hmem, hmem_ret);
+    r = CloseClipboard();
+    ok(r == TRUE, "expected %d, got %d\n", TRUE, r);
+
+    /* Paste single line */
+    SendMessageA(hEdit, WM_SETTEXT, 0, (LPARAM)"");
+    r = SendMessageA(hEdit, WM_PASTE, 0, 0);
+    len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok(strlen(str) == len, "got %d\n", len);
+
+    /* Prepare clipboard data with multiline text */
+    hmem = GlobalAlloc(GMEM_MOVEABLE, 255);
+    ok(hmem != NULL, "got %p (expected != NULL)\n", hmem);
+    buffer = GlobalLock(hmem);
+    ok(buffer != NULL, "got %p (expected != NULL)\n", buffer);
+    strcpy(buffer, str2);
+    GlobalUnlock(hmem);
+
+    r = OpenClipboard(hEdit);
+    ok(r == TRUE, "expected %d, got %d\n", TRUE, r);
+    r = EmptyClipboard();
+    ok(r == TRUE, "expected %d, got %d\n", TRUE, r);
+    hmem_ret = SetClipboardData(CF_TEXT, hmem);
+    ok(hmem_ret == hmem, "expected %p, got %p\n", hmem, hmem_ret);
+    r = CloseClipboard();
+    ok(r == TRUE, "expected %d, got %d\n", TRUE, r);
+
+    /* Paste multiline text in singleline edit - should be cut */
+    SendMessageA(hEdit, WM_SETTEXT, 0, (LPARAM)"");
+    r = SendMessageA(hEdit, WM_PASTE, 0, 0);
+    len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok(strlen("first line") == len, "got %d\n", len);
+
+    /* Paste multiline text in multiline edit */
+    SendMessageA(hMultilineEdit, WM_SETTEXT, 0, (LPARAM)"");
+    r = SendMessageA(hMultilineEdit, WM_PASTE, 0, 0);
+    len = SendMessageA(hMultilineEdit, WM_GETTEXTLENGTH, 0, 0);
+    ok(strlen(str2) == len, "got %d\n", len);
+
+    /* Cleanup */
+    DestroyWindow(hEdit);
+    DestroyWindow(hMultilineEdit);
+}
+
+static void test_EM_GETLINE(void)
+{
+    HWND hwnd[2];
+    int i;
+
+    hwnd[0] = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+    hwnd[1] = create_editcontrolW(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+
+    for (i = 0; i < sizeof(hwnd)/sizeof(hwnd[0]); i++)
+    {
+        static const WCHAR strW[] = {'t','e','x','t',0};
+        static const char *str = "text";
+        WCHAR buffW[16];
+        char buff[16];
+        int r;
+
+    todo_wine_if(i == 0)
+        ok(IsWindowUnicode(hwnd[i]), "Expected unicode window.\n");
+
+        SendMessageA(hwnd[i], WM_SETTEXT, 0, (LPARAM)str);
+
+        memset(buff, 0, sizeof(buff));
+        *(WORD *)buff = sizeof(buff);
+        r = SendMessageA(hwnd[i], EM_GETLINE, 0, (LPARAM)buff);
+        ok(r == strlen(str), "Failed to get a line %d.\n", r);
+        ok(!strcmp(buff, str), "Unexpected line data %s.\n", buff);
+
+        memset(buff, 0, sizeof(buff));
+        *(WORD *)buff = sizeof(buff);
+        r = SendMessageA(hwnd[i], EM_GETLINE, 1, (LPARAM)buff);
+        ok(r == strlen(str), "Failed to get a line %d.\n", r);
+        ok(!strcmp(buff, str), "Unexpected line data %s.\n", buff);
+
+        memset(buffW, 0, sizeof(buffW));
+        *(WORD *)buffW = sizeof(buffW)/sizeof(buffW[0]);
+        r = SendMessageW(hwnd[i], EM_GETLINE, 0, (LPARAM)buffW);
+        ok(r == lstrlenW(strW), "Failed to get a line %d.\n", r);
+        ok(!lstrcmpW(buffW, strW), "Unexpected line data %s.\n", wine_dbgstr_w(buffW));
+
+        memset(buffW, 0, sizeof(buffW));
+        *(WORD *)buffW = sizeof(buffW)/sizeof(buffW[0]);
+        r = SendMessageW(hwnd[i], EM_GETLINE, 1, (LPARAM)buffW);
+        ok(r == lstrlenW(strW), "Failed to get a line %d.\n", r);
+        ok(!lstrcmpW(buffW, strW), "Unexpected line data %s.\n", wine_dbgstr_w(buffW));
+
+        DestroyWindow(hwnd[i]);
+    }
+}
+
+START_TEST(edit)
+{
+    ULONG_PTR ctx_cookie;
+    HANDLE hCtx;
+    BOOL b;
+
+    if (!load_v6_module(&ctx_cookie, &hCtx))
+        return;
+
+    hinst = GetModuleHandleA(NULL);
+    b = register_classes();
+    ok(b, "Failed to register test classes.\n");
+    if (!b) return;
+
+    test_edit_control_1();
+    test_edit_control_2();
+    test_edit_control_3();
+    test_char_from_pos();
+    test_edit_control_5();
+    test_edit_control_6();
+    test_edit_control_limittext();
+    test_edit_control_scroll();
+    test_margins();
+    test_margins_font_change();
+    test_text_position();
+    test_espassword();
+    test_undo();
+    test_enter();
+    test_tab();
+    test_edit_dialog();
+    test_multi_edit_dialog();
+    test_wantreturn_edit_dialog();
+    test_singleline_wantreturn_edit_dialog();
+    test_child_edit_wmkeydown();
+    test_fontsize();
+    test_dialogmode();
+    test_contextmenu();
+    test_EM_GETHANDLE();
+    test_paste();
+    test_EM_GETLINE();
+
+    UnregisterWindowClasses();
+
+    unload_v6_module(ctx_cookie, hCtx);
+}
index 57e7f5c..55466ac 100644 (file)
@@ -49,6 +49,7 @@ typedef struct _ILHEAD
 #include "poppack.h"
 
 static HIMAGELIST (WINAPI *pImageList_Create)(int, int, UINT, int, int);
+static BOOL (WINAPI *pImageList_Destroy)(HIMAGELIST);
 static int (WINAPI *pImageList_Add)(HIMAGELIST, HBITMAP, HBITMAP);
 static BOOL (WINAPI *pImageList_DrawIndirect)(IMAGELISTDRAWPARAMS*);
 static BOOL (WINAPI *pImageList_SetImageCount)(HIMAGELIST,UINT);
@@ -56,6 +57,22 @@ static HRESULT (WINAPI *pImageList_CoCreateInstance)(REFCLSID,const IUnknown *,
     REFIID,void **);
 static HRESULT (WINAPI *pHIMAGELIST_QueryInterface)(HIMAGELIST,REFIID,void **);
 static int (WINAPI *pImageList_SetColorTable)(HIMAGELIST,int,int,RGBQUAD*);
+static DWORD (WINAPI *pImageList_GetFlags)(HIMAGELIST);
+static BOOL (WINAPI *pImageList_BeginDrag)(HIMAGELIST, int, int, int);
+static HIMAGELIST (WINAPI *pImageList_GetDragImage)(POINT *, POINT *);
+static void (WINAPI *pImageList_EndDrag)(void);
+static INT (WINAPI *pImageList_GetImageCount)(HIMAGELIST);
+static BOOL (WINAPI *pImageList_SetDragCursorImage)(HIMAGELIST, int, int, int);
+static BOOL (WINAPI *pImageList_GetIconSize)(HIMAGELIST, int *, int *);
+static BOOL (WINAPI *pImageList_Remove)(HIMAGELIST, int);
+static INT (WINAPI *pImageList_ReplaceIcon)(HIMAGELIST, int, HICON);
+static BOOL (WINAPI *pImageList_Replace)(HIMAGELIST, int, HBITMAP, HBITMAP);
+static HIMAGELIST (WINAPI *pImageList_Merge)(HIMAGELIST, int, HIMAGELIST, int, int, int);
+static BOOL (WINAPI *pImageList_GetImageInfo)(HIMAGELIST, int, IMAGEINFO *);
+static BOOL (WINAPI *pImageList_Write)(HIMAGELIST, IStream *);
+static HIMAGELIST (WINAPI *pImageList_Read)(IStream *);
+static BOOL (WINAPI *pImageList_Copy)(HIMAGELIST, int, HIMAGELIST, int, UINT);
+static HIMAGELIST (WINAPI *pImageList_LoadImageW)(HINSTANCE, LPCWSTR, int, int, COLORREF, UINT, UINT);
 
 static HINSTANCE hinst;
 
@@ -69,6 +86,11 @@ static void force_redraw(HWND hwnd)
     Sleep(1000);
 }
 
+static BOOL is_v6_test(void)
+{
+    return pHIMAGELIST_QueryInterface != NULL;
+}
+
 /* These macros build cursor/bitmap data in 4x4 pixel blocks */
 #define B(x,y) ((x?0xf0:0)|(y?0xf:0))
 #define ROW1(a,b,c,d,e,f,g,h) B(a,b),B(c,d),B(e,f),B(g,h)
@@ -112,14 +134,16 @@ static const BYTE bitmap_bits[48*48/8] =
 static HIMAGELIST createImageList(int cx, int cy)
 {
     /* Create an ImageList and put an image into it */
-    HIMAGELIST himl = ImageList_Create(cx, cy, ILC_COLOR, 1, 1);
+    HIMAGELIST himl = pImageList_Create(cx, cy, ILC_COLOR, 1, 1);
     HBITMAP hbm = CreateBitmap(48, 48, 1, 1, bitmap_bits);
-    ImageList_Add(himl, hbm, NULL);
+
+    ok(himl != NULL, "Failed to create image list, %d x %d.\n", cx, cy);
+    pImageList_Add(himl, hbm, NULL);
     DeleteObject(hbm);
     return himl;
 }
 
-static HWND create_a_window(void)
+static HWND create_window(void)
 {
     char className[] = "bmwnd";
     char winName[]   = "Test Bitmap";
@@ -246,41 +270,41 @@ static void test_begindrag(void)
     int count;
     POINT hotspot;
 
-    count = ImageList_GetImageCount(himl);
+    count = pImageList_GetImageCount(himl);
     ok(count > 2, "Tests need an ImageList with more than 2 images\n");
 
     /* Two BeginDrag() without EndDrag() in between */
-    ret = ImageList_BeginDrag(himl, 1, 0, 0);
-    drag = ImageList_GetDragImage(NULL, NULL);
+    ret = pImageList_BeginDrag(himl, 1, 0, 0);
+    drag = pImageList_GetDragImage(NULL, NULL);
     ok(ret && drag, "ImageList_BeginDrag() failed\n");
-    ret = ImageList_BeginDrag(himl, 0, 3, 5);
+    ret = pImageList_BeginDrag(himl, 0, 3, 5);
     ok(!ret, "ImageList_BeginDrag() returned TRUE\n");
-    drag = ImageList_GetDragImage(NULL, &hotspot);
+    drag = pImageList_GetDragImage(NULL, &hotspot);
     ok(!!drag, "No active ImageList drag left\n");
     ok(hotspot.x == 0 && hotspot.y == 0, "New ImageList drag was created\n");
-    ImageList_EndDrag();
-    drag = ImageList_GetDragImage(NULL, NULL);
+    pImageList_EndDrag();
+    drag = pImageList_GetDragImage(NULL, NULL);
     ok(!drag, "ImageList drag was not destroyed\n");
 
     /* Invalid image index */
-    ImageList_BeginDrag(himl, 0, 0, 0);
-    ret = ImageList_BeginDrag(himl, count, 3, 5);
+    pImageList_BeginDrag(himl, 0, 0, 0);
+    ret = pImageList_BeginDrag(himl, count, 3, 5);
     ok(!ret, "ImageList_BeginDrag() returned TRUE\n");
-    drag = ImageList_GetDragImage(NULL, &hotspot);
+    drag = pImageList_GetDragImage(NULL, &hotspot);
     ok(drag && hotspot.x == 0 && hotspot.y == 0, "Active drag should not have been canceled\n");
-    ImageList_EndDrag();
-    drag = ImageList_GetDragImage(NULL, NULL);
+    pImageList_EndDrag();
+    drag = pImageList_GetDragImage(NULL, NULL);
     ok(!drag, "ImageList drag was not destroyed\n");
     /* Invalid negative image indexes succeed */
-    ret = ImageList_BeginDrag(himl, -17, 0, 0);
-    drag = ImageList_GetDragImage(NULL, NULL);
+    ret = pImageList_BeginDrag(himl, -17, 0, 0);
+    drag = pImageList_GetDragImage(NULL, NULL);
     ok(ret && drag, "ImageList drag was created\n");
-    ImageList_EndDrag();
-    ret = ImageList_BeginDrag(himl, -1, 0, 0);
-    drag = ImageList_GetDragImage(NULL, NULL);
+    pImageList_EndDrag();
+    ret = pImageList_BeginDrag(himl, -1, 0, 0);
+    drag = pImageList_GetDragImage(NULL, NULL);
     ok(ret && drag, "ImageList drag was created\n");
-    ImageList_EndDrag();
-    ImageList_Destroy(himl);
+    pImageList_EndDrag();
+    pImageList_Destroy(himl);
 }
 
 static void test_hotspot(void)
@@ -304,7 +328,7 @@ static void test_hotspot(void)
     int i, j, ret;
     HIMAGELIST himl1 = createImageList(SIZEX1, SIZEY1);
     HIMAGELIST himl2 = createImageList(SIZEX2, SIZEY2);
-    HWND hwnd = create_a_window();
+    HWND hwnd = create_window();
 
 
     for (i = 0; i < HOTSPOTS_MAX; i++) {
@@ -318,25 +342,25 @@ static void test_hotspot(void)
             HIMAGELIST himlNew;
             POINT ppt;
 
-            ret = ImageList_BeginDrag(himl1, 0, dx1, dy1);
+            ret = pImageList_BeginDrag(himl1, 0, dx1, dy1);
             ok(ret != 0, "BeginDrag failed for { %d, %d }\n", dx1, dy1);
             sprintf(loc, "BeginDrag (%d,%d)\n", i, j);
             show_image(hwnd, himl1, 0, max(SIZEX1, SIZEY1), loc, TRUE);
 
             /* check merging the dragged image with a second image */
-            ret = ImageList_SetDragCursorImage(himl2, 0, dx2, dy2);
+            ret = pImageList_SetDragCursorImage(himl2, 0, dx2, dy2);
             ok(ret != 0, "SetDragCursorImage failed for {%d, %d}{%d, %d}\n",
                     dx1, dy1, dx2, dy2);
             sprintf(loc, "SetDragCursorImage (%d,%d)\n", i, j);
             show_image(hwnd, himl2, 0, max(SIZEX2, SIZEY2), loc, TRUE);
 
             /* check new hotspot, it should be the same like the old one */
-            himlNew = ImageList_GetDragImage(NULL, &ppt);
+            himlNew = pImageList_GetDragImage(NULL, &ppt);
             ok(ppt.x == dx1 && ppt.y == dy1,
                     "Expected drag hotspot [%d,%d] got [%d,%d]\n",
                     dx1, dy1, ppt.x, ppt.y);
             /* check size of new dragged image */
-            ImageList_GetIconSize(himlNew, &newx, &newy);
+            pImageList_GetIconSize(himlNew, &newx, &newy);
             correctx = max(SIZEX1, max(SIZEX2 + dx2, SIZEX1 - dx2));
             correcty = max(SIZEY1, max(SIZEY2 + dy2, SIZEY1 - dy2));
             ok(newx == correctx && newy == correcty,
@@ -344,7 +368,7 @@ static void test_hotspot(void)
                     correctx, correcty, newx, newy);
             sprintf(loc, "GetDragImage (%d,%d)\n", i, j);
             show_image(hwnd, himlNew, 0, max(correctx, correcty), loc, TRUE);
-            ImageList_EndDrag();
+            pImageList_EndDrag();
         }
     }
 #undef SIZEX1
@@ -352,8 +376,8 @@ static void test_hotspot(void)
 #undef SIZEX2
 #undef SIZEY2
 #undef HOTSPOTS_MAX
-    ImageList_Destroy(himl2);
-    ImageList_Destroy(himl1);
+    pImageList_Destroy(himl2);
+    pImageList_Destroy(himl1);
     DestroyWindow(hwnd);
 }
 
@@ -366,7 +390,7 @@ static void test_add_remove(void)
     HICON hicon3 ;
 
     /* create an imagelist to play with */
-    himl = ImageList_Create(84, 84, ILC_COLOR16, 0, 3);
+    himl = pImageList_Create(84, 84, ILC_COLOR16, 0, 3);
     ok(himl!=0,"failed to create imagelist\n");
 
     /* load the icons to add to the image list */
@@ -378,41 +402,41 @@ static void test_add_remove(void)
     ok(hicon3 != 0, "no hicon3\n");
 
     /* remove when nothing exists */
-    ok(!ImageList_Remove(himl,0),"removed nonexistent icon\n");
+    ok(!pImageList_Remove(himl, 0), "Removed nonexistent icon.\n");
     /* removing everything from an empty imagelist should succeed */
-    ok(ImageList_RemoveAll(himl),"removed nonexistent icon\n");
+    ok(pImageList_Remove(himl, -1), "Removed nonexistent icon\n");
 
     /* add three */
-    ok(0==ImageList_AddIcon(himl, hicon1),"failed to add icon1\n");
-    ok(1==ImageList_AddIcon(himl, hicon2),"failed to add icon2\n");
-    ok(2==ImageList_AddIcon(himl, hicon3),"failed to add icon3\n");
+    ok(0 == pImageList_ReplaceIcon(himl, -1, hicon1), "Failed to add icon1.\n");
+    ok(1 == pImageList_ReplaceIcon(himl, -1, hicon2), "Failed to add icon2.\n");
+    ok(2 == pImageList_ReplaceIcon(himl, -1, hicon3), "Failed to add icon3.\n");
 
     /* remove an index out of range */
-    ok(!ImageList_Remove(himl,4711),"removed nonexistent icon\n");
+    ok(!pImageList_Remove(himl, 4711), "removed nonexistent icon\n");
 
     /* remove three */
-    ok(ImageList_Remove(himl,0),"can't remove 0\n");
-    ok(ImageList_Remove(himl,0),"can't remove 0\n");
-    ok(ImageList_Remove(himl,0),"can't remove 0\n");
+    ok(pImageList_Remove(himl, 0), "Can't remove 0\n");
+    ok(pImageList_Remove(himl, 0), "Can't remove 0\n");
+    ok(pImageList_Remove(himl, 0), "Can't remove 0\n");
 
     /* remove one extra */
-    ok(!ImageList_Remove(himl,0),"removed nonexistent icon\n");
+    ok(!pImageList_Remove(himl, 0), "Removed nonexistent icon.\n");
 
     /* destroy it */
-    ok(ImageList_Destroy(himl),"destroy imagelist failed\n");
+    ok(pImageList_Destroy(himl), "Failed to destroy imagelist.\n");
 
-    ok(-1==ImageList_AddIcon((HIMAGELIST)0xdeadbeef, hicon1),"don't crash on bad handle\n");
+    ok(-1 == pImageList_ReplaceIcon((HIMAGELIST)0xdeadbeef, -1, hicon1), "Don't crash on bad handle\n");
 
-    ok(DestroyIcon(hicon1),"icon 1 wasn't deleted\n");
-    ok(DestroyIcon(hicon2),"icon 2 wasn't deleted\n");
-    ok(DestroyIcon(hicon3),"icon 3 wasn't deleted\n");
+    ok(DestroyIcon(hicon1), "Failed to destroy icon 1.\n");
+    ok(DestroyIcon(hicon2), "Failed to destroy icon 2.\n");
+    ok(DestroyIcon(hicon3), "Failed to destroy icon 3.\n");
 }
 
 static void test_imagecount(void)
 {
     HIMAGELIST himl;
 
-    ok(0==ImageList_GetImageCount((HIMAGELIST)0xdeadbeef),"don't crash on bad handle\n");
+    ok(0 == pImageList_GetImageCount((HIMAGELIST)0xdeadbeef), "don't crash on bad handle\n");
 
     if (!pImageList_SetImageCount)
     {
@@ -420,17 +444,17 @@ static void test_imagecount(void)
         return;
     }
 
-    himl = ImageList_Create(84, 84, ILC_COLOR16, 0, 3);
-    ok(himl!=0,"failed to create imagelist\n");
+    himl = pImageList_Create(84, 84, ILC_COLOR16, 0, 3);
+    ok(himl != 0, "Failed to create imagelist.\n");
 
     ok(pImageList_SetImageCount(himl, 3), "couldn't increase image count\n");
-    ok(ImageList_GetImageCount(himl) == 3, "invalid image count after increase\n");
+    ok(pImageList_GetImageCount(himl) == 3, "invalid image count after increase\n");
     ok(pImageList_SetImageCount(himl, 1), "couldn't decrease image count\n");
-    ok(ImageList_GetImageCount(himl) == 1, "invalid image count after decrease to 1\n");
+    ok(pImageList_GetImageCount(himl) == 1, "invalid image count after decrease to 1\n");
     ok(pImageList_SetImageCount(himl, 0), "couldn't decrease image count\n");
-    ok(ImageList_GetImageCount(himl) == 0, "invalid image count after decrease to 0\n");
+    ok(pImageList_GetImageCount(himl) == 0, "invalid image count after decrease to 0\n");
 
-    ok(ImageList_Destroy(himl), "destroy imagelist failed\n");
+    ok(pImageList_Destroy(himl), "Failed to destroy imagelist.\n");
 }
 
 static void test_DrawIndirect(void)
@@ -451,13 +475,13 @@ static void test_DrawIndirect(void)
         return;
     }
 
-    hwndfortest = create_a_window();
+    hwndfortest = create_window();
     hdc = GetDC(hwndfortest);
     ok(hdc!=NULL, "couldn't get DC\n");
 
     /* create an imagelist to play with */
-    himl = ImageList_Create(48, 48, ILC_COLOR16, 0, 3);
-    ok(himl!=0,"failed to create imagelist\n");
+    himl = pImageList_Create(48, 48, ILC_COLOR16, 0, 3);
+    ok(himl != 0, "Failed to create imagelist.\n");
 
     /* load the icons to add to the image list */
     hbm1 = CreateBitmap(48, 48, 1, 1, bitmap_bits);
@@ -468,14 +492,14 @@ static void test_DrawIndirect(void)
     ok(hbm3 != 0, "no bitmap 3\n");
 
     /* add three */
-    ok(0==ImageList_Add(himl, hbm1, 0),"failed to add bitmap 1\n");
-    ok(1==ImageList_Add(himl, hbm2, 0),"failed to add bitmap 2\n");
+    ok(0 == ImageList_Add(himl, hbm1, 0),"failed to add bitmap 1\n");
+    ok(1 == ImageList_Add(himl, hbm2, 0),"failed to add bitmap 2\n");
 
     if (pImageList_SetImageCount)
     {
         ok(pImageList_SetImageCount(himl,3),"Setimage count failed\n");
         /*ok(2==ImageList_Add(himl, hbm3, NULL),"failed to add bitmap 3\n"); */
-        ok(ImageList_Replace(himl, 2, hbm3, 0),"failed to replace bitmap 3\n");
+        ok(pImageList_Replace(himl, 2, hbm3, 0),"failed to replace bitmap 3\n");
     }
 
     memset(&imldp, 0, sizeof (imldp));
@@ -504,12 +528,12 @@ static void test_DrawIndirect(void)
     ok(!pImageList_DrawIndirect(&imldp),"should fail\n");
 
     /* remove three */
-    ok(ImageList_Remove(himl, 0), "removing 1st bitmap\n");
-    ok(ImageList_Remove(himl, 0), "removing 2nd bitmap\n");
-    ok(ImageList_Remove(himl, 0), "removing 3rd bitmap\n");
+    ok(pImageList_Remove(himl, 0), "removing 1st bitmap\n");
+    ok(pImageList_Remove(himl, 0), "removing 2nd bitmap\n");
+    ok(pImageList_Remove(himl, 0), "removing 3rd bitmap\n");
 
     /* destroy it */
-    ok(ImageList_Destroy(himl),"destroy imagelist failed\n");
+    ok(pImageList_Destroy(himl), "Failed to destroy imagelist.\n");
 
     /* bitmaps should not be deleted by the imagelist */
     ok(DeleteObject(hbm1),"bitmap 1 can't be deleted\n");
@@ -550,12 +574,12 @@ static void test_merge_colors(void)
 
     for (i = 0; i < 8; i++)
     {
-        himl[i] = ImageList_Create(32, 32, sizes[i], 0, 3);
+        himl[i] = pImageList_Create(32, 32, sizes[i], 0, 3);
         ok(himl[i] != NULL, "failed to create himl[%d]\n", i);
-        ok(0 == ImageList_AddIcon(himl[i], hicon1), "add icon1 to himl[%d] failed\n", i);
+        ok(0 == pImageList_ReplaceIcon(himl[i], -1, hicon1), "Failed to add icon1 to himl[%d].\n", i);
         if (i == 0 || i == 1 || i == 7)
         {
-            ImageList_GetImageInfo(himl[i], 0, &info);
+            pImageList_GetImageInfo(himl[i], 0, &info);
             sizes[i] = get_color_format(info.hbmImage);
         }
     }
@@ -563,10 +587,10 @@ static void test_merge_colors(void)
     for (i = 0; i < 8; i++)
         for (j = 0; j < 8; j++)
         {
-            hmerge = ImageList_Merge(himl[i], 0, himl[j], 0, 0, 0);
+            hmerge = pImageList_Merge(himl[i], 0, himl[j], 0, 0, 0);
             ok(hmerge != NULL, "merge himl[%d], himl[%d] failed\n", i, j);
 
-            ImageList_GetImageInfo(hmerge, 0, &info);
+            pImageList_GetImageInfo(hmerge, 0, &info);
             bpp = get_color_format(info.hbmImage);
             /* ILC_COLOR[X] is defined as [X] */
             if (i == 4 && j == 7)
@@ -577,23 +601,23 @@ static void test_merge_colors(void)
                     "wrong biBitCount %d when merging lists %d (%d) and %d (%d)\n", bpp, i, sizes[i], j, sizes[j]);
             ok(info.hbmMask != 0, "Imagelist merged from %d and %d had no mask\n", i, j);
 
-            if (hmerge) ImageList_Destroy(hmerge);
+            pImageList_Destroy(hmerge);
         }
 
     for (i = 0; i < 8; i++)
-        ImageList_Destroy(himl[i]);
+        pImageList_Destroy(himl[i]);
 }
 
 static void test_merge(void)
 {
     HIMAGELIST himl1, himl2, hmerge;
     HICON hicon1;
-    HWND hwnd = create_a_window();
+    HWND hwnd = create_window();
 
-    himl1 = ImageList_Create(32,32,0,0,3);
+    himl1 = pImageList_Create(32, 32, 0, 0, 3);
     ok(himl1 != NULL,"failed to create himl1\n");
 
-    himl2 = ImageList_Create(32,32,0,0,3);
+    himl2 = pImageList_Create(32, 32, 0, 0, 3);
     ok(himl2 != NULL,"failed to create himl2\n");
 
     hicon1 = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
@@ -602,60 +626,60 @@ static void test_merge(void)
     if (!himl1 || !himl2 || !hicon1)
         return;
 
-    ok(0==ImageList_AddIcon(himl2, hicon1),"add icon1 to himl2 failed\n");
+    ok(0 == pImageList_ReplaceIcon(himl2, -1, hicon1), "Failed to add icon1 to himl2.\n");
     check_bits(hwnd, himl2, 0, 32, icon_bits, "add icon1 to himl2");
 
     /* If himl1 has no images, merge still succeeds */
-    hmerge = ImageList_Merge(himl1, -1, himl2, 0, 0, 0);
+    hmerge = pImageList_Merge(himl1, -1, himl2, 0, 0, 0);
     ok(hmerge != NULL, "merge himl1,-1 failed\n");
     check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl1,-1");
-    if (hmerge) ImageList_Destroy(hmerge);
+    pImageList_Destroy(hmerge);
 
-    hmerge = ImageList_Merge(himl1, 0, himl2, 0, 0, 0);
+    hmerge = pImageList_Merge(himl1, 0, himl2, 0, 0, 0);
     ok(hmerge != NULL,"merge himl1,0 failed\n");
     check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl1,0");
-    if (hmerge) ImageList_Destroy(hmerge);
+    pImageList_Destroy(hmerge);
 
     /* Same happens if himl2 is empty */
-    ImageList_Destroy(himl2);
-    himl2 = ImageList_Create(32,32,0,0,3);
+    pImageList_Destroy(himl2);
+    himl2 = pImageList_Create(32, 32, 0, 0, 3);
     ok(himl2 != NULL,"failed to recreate himl2\n");
     if (!himl2)
         return;
 
-    hmerge = ImageList_Merge(himl1, -1, himl2, -1, 0, 0);
+    hmerge = pImageList_Merge(himl1, -1, himl2, -1, 0, 0);
     ok(hmerge != NULL, "merge himl2,-1 failed\n");
     check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl2,-1");
-    if (hmerge) ImageList_Destroy(hmerge);
+    pImageList_Destroy(hmerge);
 
-    hmerge = ImageList_Merge(himl1, -1, himl2, 0, 0, 0);
+    hmerge = pImageList_Merge(himl1, -1, himl2, 0, 0, 0);
     ok(hmerge != NULL, "merge himl2,0 failed\n");
     check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl2,0");
-    if (hmerge) ImageList_Destroy(hmerge);
+    pImageList_Destroy(hmerge);
 
     /* Now try merging an image with itself */
-    ok(0==ImageList_AddIcon(himl2, hicon1),"re-add icon1 to himl2 failed\n");
+    ok(0 == pImageList_ReplaceIcon(himl2, -1, hicon1), "Failed to re-add icon1 to himl2.\n");
 
-    hmerge = ImageList_Merge(himl2, 0, himl2, 0, 0, 0);
+    hmerge = pImageList_Merge(himl2, 0, himl2, 0, 0, 0);
     ok(hmerge != NULL, "merge himl2 with itself failed\n");
     check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl2 with itself");
-    if (hmerge) ImageList_Destroy(hmerge);
+    pImageList_Destroy(hmerge);
 
     /* Try merging 2 different image lists */
-    ok(0==ImageList_AddIcon(himl1, hicon1),"add icon1 to himl1 failed\n");
+    ok(0 == pImageList_ReplaceIcon(himl1, -1, hicon1), "Failed to add icon1 to himl1.\n");
 
-    hmerge = ImageList_Merge(himl1, 0, himl2, 0, 0, 0);
+    hmerge = pImageList_Merge(himl1, 0, himl2, 0, 0, 0);
     ok(hmerge != NULL, "merge himl1 with himl2 failed\n");
     check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl1 with himl2");
-    if (hmerge) ImageList_Destroy(hmerge);
+    pImageList_Destroy(hmerge);
 
-    hmerge = ImageList_Merge(himl1, 0, himl2, 0, 8, 16);
+    hmerge = pImageList_Merge(himl1, 0, himl2, 0, 8, 16);
     ok(hmerge != NULL, "merge himl1 with himl2 8,16 failed\n");
     check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl1 with himl2, 8,16");
-    if (hmerge) ImageList_Destroy(hmerge);
+    pImageList_Destroy(hmerge);
 
-    ImageList_Destroy(himl1);
-    ImageList_Destroy(himl2);
+    pImageList_Destroy(himl1);
+    pImageList_Destroy(himl2);
     DestroyIcon(hicon1);
     DestroyWindow(hwnd);
 }
@@ -708,11 +732,23 @@ static HRESULT STDMETHODCALLTYPE Test_Stream_Write(IStream *iface, const void *p
     return IStream_Write(stream->stream, pv, cb, pcbWritten);
 }
 
-static HRESULT STDMETHODCALLTYPE Test_Stream_Seek(IStream *iface, LARGE_INTEGER dlibMove,
-                                                  DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
+static HRESULT STDMETHODCALLTYPE Test_Stream_Seek(IStream *iface, LARGE_INTEGER offset, DWORD origin,
+        ULARGE_INTEGER *new_pos)
 {
-    ok(0, "unexpected call\n");
-    return E_NOTIMPL;
+    struct memstream *stream = impl_from_IStream(iface);
+
+    if (is_v6_test())
+    {
+        ok(origin == STREAM_SEEK_CUR, "Unexpected origin %d.\n", origin);
+        ok(offset.QuadPart == 0, "Unexpected offset %s.\n", wine_dbgstr_longlong(offset.QuadPart));
+        ok(new_pos != NULL, "Unexpected out position pointer.\n");
+        return IStream_Seek(stream->stream, offset, origin, new_pos);
+    }
+    else
+    {
+        ok(0, "unexpected call\n");
+        return E_NOTIMPL;
+    }
 }
 
 static HRESULT STDMETHODCALLTYPE Test_Stream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
@@ -788,8 +824,11 @@ static const IStreamVtbl Test_Stream_Vtbl =
 
 static void init_memstream(struct memstream *stream)
 {
+    HRESULT hr;
+
     stream->IStream_iface.lpVtbl = &Test_Stream_Vtbl;
-    CreateStreamOnHGlobal(NULL, TRUE, &stream->stream);
+    hr = CreateStreamOnHGlobal(NULL, TRUE, &stream->stream);
+    ok(hr == S_OK, "Failed to create a stream, hr %#x.\n", hr);
 }
 
 static void cleanup_memstream(struct memstream *stream)
@@ -797,15 +836,13 @@ static void cleanup_memstream(struct memstream *stream)
     IStream_Release(stream->stream);
 }
 
-
 static INT DIB_GetWidthBytes( int width, int bpp )
 {
     return ((width * bpp + 31) / 8) & ~3;
 }
 
-static ULONG check_bitmap_data(const char *bm_data, ULONG bm_data_size,
-                               INT width, INT height, INT bpp,
-                               const char *comment)
+static ULONG check_bitmap_data(const ILHEAD *header, const char *bm_data,
+    ULONG bm_data_size, const SIZE *bmpsize, INT bpp, const char *comment)
 {
     const BITMAPFILEHEADER *bmfh = (const BITMAPFILEHEADER *)bm_data;
     const BITMAPINFOHEADER *bmih = (const BITMAPINFOHEADER *)(bm_data + sizeof(*bmfh));
@@ -821,13 +858,13 @@ static ULONG check_bitmap_data(const char *bm_data, ULONG bm_data_size,
     ok(bmfh->bfOffBits == hdr_size, "wrong bfOffBits 0x%02x\n", bmfh->bfOffBits);
 
     ok(bmih->biSize == sizeof(*bmih), "wrong biSize %d\n", bmih->biSize);
-    ok(bmih->biWidth == width, "wrong biWidth %d (expected %d)\n", bmih->biWidth, width);
-    ok(bmih->biHeight == height, "wrong biHeight %d (expected %d)\n", bmih->biHeight, height);
     ok(bmih->biPlanes == 1, "wrong biPlanes %d\n", bmih->biPlanes);
     ok(bmih->biBitCount == bpp, "wrong biBitCount %d\n", bmih->biBitCount);
 
     image_size = DIB_GetWidthBytes(bmih->biWidth, bmih->biBitCount) * bmih->biHeight;
     ok(bmih->biSizeImage == image_size, "wrong biSizeImage %u\n", bmih->biSizeImage);
+    ok(bmih->biWidth == bmpsize->cx && bmih->biHeight == bmpsize->cy, "Unexpected bitmap size %d x %d, "
+            "expected %d x %d\n", bmih->biWidth, bmih->biHeight, bmpsize->cx, bmpsize->cy);
 
 if (0)
 {
@@ -842,15 +879,23 @@ if (0)
     return hdr_size + image_size;
 }
 
-static void check_ilhead_data(const char *ilh_data, INT cx, INT cy, INT cur, INT max, INT grow, INT flags)
+static BOOL is_v6_header(const ILHEAD *header)
 {
-    const ILHEAD *ilh = (const ILHEAD *)ilh_data;
+    return (header->usVersion & 0xff00) == 0x600;
+}
 
+static void check_ilhead_data(const ILHEAD *ilh, INT cx, INT cy, INT cur, INT max, INT grow, INT flags)
+{
     ok(ilh->usMagic == IMAGELIST_MAGIC, "wrong usMagic %4x (expected %02x)\n", ilh->usMagic, IMAGELIST_MAGIC);
-    ok(ilh->usVersion == 0x101, "wrong usVersion %x (expected 0x101)\n", ilh->usVersion);
+    ok(ilh->usVersion == 0x101 ||
+            ilh->usVersion == 0x600 || /* WinXP/W2k3 */
+            ilh->usVersion == 0x620, "Unknown usVersion %#x.\n", ilh->usVersion);
     ok(ilh->cCurImage == cur, "wrong cCurImage %d (expected %d)\n", ilh->cCurImage, cur);
-    ok(ilh->cMaxImage == max, "wrong cMaxImage %d (expected %d)\n", ilh->cMaxImage, max);
-    ok(ilh->cGrow == grow, "wrong cGrow %d (expected %d)\n", ilh->cGrow, grow);
+    if (!is_v6_header(ilh))
+    {
+        ok(ilh->cMaxImage == max, "wrong cMaxImage %d (expected %d)\n", ilh->cMaxImage, max);
+        ok(ilh->cGrow == grow, "Unexpected cGrow %d (expected %d)\n", ilh->cGrow, grow);
+    }
     ok(ilh->cx == cx, "wrong cx %d (expected %d)\n", ilh->cx, cx);
     ok(ilh->cy == cy, "wrong cy %d (expected %d)\n", ilh->cy, cy);
     ok(ilh->bkcolor == CLR_NONE, "wrong bkcolor %x\n", ilh->bkcolor);
@@ -895,56 +940,78 @@ static HBITMAP create_bitmap(INT cx, INT cy, COLORREF color, const char *comment
     return hbmp;
 }
 
+static inline void imagelist_get_bitmap_size(const ILHEAD *header, SIZE *sz)
+{
+    const int tile_count = 4;
+
+    if (is_v6_header(header))
+    {
+        sz->cx = header->cx;
+        sz->cy = header->cMaxImage * header->cy;
+    }
+    else
+    {
+        sz->cx = header->cx * tile_count;
+        sz->cy = ((header->cMaxImage + tile_count - 1) / tile_count) * header->cy;
+    }
+}
+
 static void check_iml_data(HIMAGELIST himl, INT cx, INT cy, INT cur, INT max, INT grow,
-                           INT width, INT height, INT flags, const char *comment)
+        INT flags, const char *comment)
 {
     INT ret, cxx, cyy, size;
     struct memstream stream;
+    const ILHEAD *header;
     LARGE_INTEGER mv;
     HIMAGELIST himl2;
     HGLOBAL hglobal;
     STATSTG stat;
     char *data;
     HRESULT hr;
+    SIZE bmpsize;
+    BOOL b;
 
-    ret = ImageList_GetImageCount(himl);
+    ret = pImageList_GetImageCount(himl);
     ok(ret == cur, "%s: expected image count %d got %d\n", comment, cur, ret);
 
-    ret = ImageList_GetIconSize(himl, &cxx, &cyy);
+    ret = pImageList_GetIconSize(himl, &cxx, &cyy);
     ok(ret, "ImageList_GetIconSize failed\n");
     ok(cxx == cx, "%s: wrong cx %d (expected %d)\n", comment, cxx, cx);
     ok(cyy == cy, "%s: wrong cy %d (expected %d)\n", comment, cyy, cy);
 
     init_memstream(&stream);
-    ret = ImageList_Write(himl, &stream.IStream_iface);
-    ok(ret, "%s: ImageList_Write failed\n", comment);
+    b = pImageList_Write(himl, &stream.IStream_iface);
+    ok(b, "%s: ImageList_Write failed\n", comment);
 
     hr = GetHGlobalFromStream(stream.stream, &hglobal);
     ok(hr == S_OK, "%s: Failed to get hglobal, %#x\n", comment, hr);
 
-    IStream_Stat(stream.stream, &stat, STATFLAG_NONAME);
+    hr = IStream_Stat(stream.stream, &stat, STATFLAG_NONAME);
+    ok(hr == S_OK, "Stat() failed, hr %#x.\n", hr);
 
     data = GlobalLock(hglobal);
 
     ok(data != 0, "%s: ImageList_Write didn't write any data\n", comment);
     ok(stat.cbSize.LowPart > sizeof(ILHEAD), "%s: ImageList_Write wrote not enough data\n", comment);
 
-    check_ilhead_data(data, cx, cy, cur, max, grow, flags);
-    size = check_bitmap_data(data + sizeof(ILHEAD), stat.cbSize.LowPart - sizeof(ILHEAD),
-            width, height, flags & 0xfe, comment);
-    if (size < stat.cbSize.LowPart - sizeof(ILHEAD))  /* mask is present */
+    header = (const ILHEAD *)data;
+    check_ilhead_data(header, cx, cy, cur, max, grow, flags);
+    imagelist_get_bitmap_size(header, &bmpsize);
+    size = check_bitmap_data(header, data + sizeof(ILHEAD), stat.cbSize.LowPart - sizeof(ILHEAD),
+            &bmpsize, flags & 0xfe, comment);
+    if (!is_v6_header(header) && size < stat.cbSize.LowPart - sizeof(ILHEAD))  /* mask is present */
     {
         ok( flags & ILC_MASK, "%s: extra data %u/%u but mask not expected\n", comment, stat.cbSize.LowPart, size );
-        check_bitmap_data(data + sizeof(ILHEAD) + size, stat.cbSize.LowPart - sizeof(ILHEAD) - size,
-                          width, height, 1, comment);
+        check_bitmap_data(header, data + sizeof(ILHEAD) + size, stat.cbSize.LowPart - sizeof(ILHEAD) - size,
+            &bmpsize, 1, comment);
     }
 
     /* rewind and reconstruct from stream */
     mv.QuadPart = 0;
     IStream_Seek(stream.stream, mv, STREAM_SEEK_SET, NULL);
-    himl2 = ImageList_Read(&stream.IStream_iface);
+    himl2 = pImageList_Read(&stream.IStream_iface);
     ok(himl2 != NULL, "%s: Failed to deserialize imagelist\n", comment);
-    ImageList_Destroy(himl2);
+    pImageList_Destroy(himl2);
 
     GlobalUnlock(hglobal);
     cleanup_memstream(&stream);
@@ -959,37 +1026,37 @@ static void image_list_init(HIMAGELIST himl)
     static const struct test_data
     {
         BYTE grey;
-        INT cx, cy, cur, max, grow, width, height, bpp;
+        INT cx, cy, cur, max, grow, bpp;
         const char *comment;
     } td[] =
     {
-        { 255, BMP_CX, BMP_CX, 1, 2, 4, BMP_CX * 4, BMP_CX * 1, 24, "total 1" },
-        { 170, BMP_CX, BMP_CX, 2, 7, 4, BMP_CX * 4, BMP_CX * 2, 24, "total 2" },
-        { 85, BMP_CX, BMP_CX, 3, 7, 4, BMP_CX * 4, BMP_CX * 2, 24, "total 3" },
-        { 0, BMP_CX, BMP_CX, 4, 7, 4, BMP_CX * 4, BMP_CX * 2, 24, "total 4" },
-        { 0, BMP_CX, BMP_CX, 5, 7, 4, BMP_CX * 4, BMP_CX * 2, 24, "total 5" },
-        { 85, BMP_CX, BMP_CX, 6, 7, 4, BMP_CX * 4, BMP_CX * 2, 24, "total 6" },
-        { 170, BMP_CX, BMP_CX, 7, 12, 4, BMP_CX * 4, BMP_CX * 3, 24, "total 7" },
-        { 255, BMP_CX, BMP_CX, 8, 12, 4, BMP_CX * 4, BMP_CX * 3, 24, "total 8" },
-        { 255, BMP_CX, BMP_CX, 9, 12, 4, BMP_CX * 4, BMP_CX * 3, 24, "total 9" },
-        { 170, BMP_CX, BMP_CX, 10, 12, 4, BMP_CX * 4, BMP_CX * 3, 24, "total 10" },
-        { 85, BMP_CX, BMP_CX, 11, 12, 4, BMP_CX * 4, BMP_CX * 3, 24, "total 11" },
-        { 0, BMP_CX, BMP_CX, 12, 17, 4, BMP_CX * 4, BMP_CX * 5, 24, "total 12" },
-        { 0, BMP_CX, BMP_CX, 13, 17, 4, BMP_CX * 4, BMP_CX * 5, 24, "total 13" },
-        { 85, BMP_CX, BMP_CX, 14, 17, 4, BMP_CX * 4, BMP_CX * 5, 24, "total 14" },
-        { 170, BMP_CX, BMP_CX, 15, 17, 4, BMP_CX * 4, BMP_CX * 5, 24, "total 15" },
-        { 255, BMP_CX, BMP_CX, 16, 17, 4, BMP_CX * 4, BMP_CX * 5, 24, "total 16" },
-        { 255, BMP_CX, BMP_CX, 17, 22, 4, BMP_CX * 4, BMP_CX * 6, 24, "total 17" },
-        { 170, BMP_CX, BMP_CX, 18, 22, 4, BMP_CX * 4, BMP_CX * 6, 24, "total 18" },
-        { 85, BMP_CX, BMP_CX, 19, 22, 4, BMP_CX * 4, BMP_CX * 6, 24, "total 19" },
-        { 0, BMP_CX, BMP_CX, 20, 22, 4, BMP_CX * 4, BMP_CX * 6, 24, "total 20" },
-        { 0, BMP_CX, BMP_CX, 21, 22, 4, BMP_CX * 4, BMP_CX * 6, 24, "total 21" },
-        { 85, BMP_CX, BMP_CX, 22, 27, 4, BMP_CX * 4, BMP_CX * 7, 24, "total 22" },
-        { 170, BMP_CX, BMP_CX, 23, 27, 4, BMP_CX * 4, BMP_CX * 7, 24, "total 23" },
-        { 255, BMP_CX, BMP_CX, 24, 27, 4, BMP_CX * 4, BMP_CX * 7, 24, "total 24" }
+        { 255, BMP_CX, BMP_CX, 1, 2, 4, 24, "total 1" },
+        { 170, BMP_CX, BMP_CX, 2, 7, 4, 24, "total 2" },
+        { 85, BMP_CX, BMP_CX, 3, 7, 4, 24, "total 3" },
+        { 0, BMP_CX, BMP_CX, 4, 7, 4, 24, "total 4" },
+        { 0, BMP_CX, BMP_CX, 5, 7, 4, 24, "total 5" },
+        { 85, BMP_CX, BMP_CX, 6, 7, 4, 24, "total 6" },
+        { 170, BMP_CX, BMP_CX, 7, 12, 4, 24, "total 7" },
+        { 255, BMP_CX, BMP_CX, 8, 12, 4, 24, "total 8" },
+        { 255, BMP_CX, BMP_CX, 9, 12, 4, 24, "total 9" },
+        { 170, BMP_CX, BMP_CX, 10, 12, 4, 24, "total 10" },
+        { 85, BMP_CX, BMP_CX, 11, 12, 4, 24, "total 11" },
+        { 0, BMP_CX, BMP_CX, 12, 17, 4, 24, "total 12" },
+        { 0, BMP_CX, BMP_CX, 13, 17, 4, 24, "total 13" },
+        { 85, BMP_CX, BMP_CX, 14, 17, 4, 24, "total 14" },
+        { 170, BMP_CX, BMP_CX, 15, 17, 4, 24, "total 15" },
+        { 255, BMP_CX, BMP_CX, 16, 17, 4, 24, "total 16" },
+        { 255, BMP_CX, BMP_CX, 17, 22, 4, 24, "total 17" },
+        { 170, BMP_CX, BMP_CX, 18, 22, 4, 24, "total 18" },
+        { 85, BMP_CX, BMP_CX, 19, 22, 4, 24, "total 19" },
+        { 0, BMP_CX, BMP_CX, 20, 22, 4, 24, "total 20" },
+        { 0, BMP_CX, BMP_CX, 21, 22, 4, 24, "total 21" },
+        { 85, BMP_CX, BMP_CX, 22, 27, 4, 24, "total 22" },
+        { 170, BMP_CX, BMP_CX, 23, 27, 4, 24, "total 23" },
+        { 255, BMP_CX, BMP_CX, 24, 27, 4, 24, "total 24" }
     };
 
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 2, 4, BMP_CX * 4, BMP_CX * 1, ILC_COLOR24, "total 0");
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 2, 4, ILC_COLOR24, "total 0");
 
 #define add_bitmap(grey) \
     sprintf(comment, "%d", n++); \
@@ -1000,8 +1067,7 @@ static void image_list_init(HIMAGELIST himl)
     for (i = 0; i < sizeof(td)/sizeof(td[0]); i++)
     {
         add_bitmap(td[i].grey);
-        check_iml_data(himl, td[i].cx, td[i].cy, td[i].cur, td[i].max, td[i].grow,
-                       td[i].width, td[i].height, td[i].bpp, td[i].comment);
+        check_iml_data(himl, td[i].cx, td[i].cy, td[i].cur, td[i].max, td[i].grow, td[i].bpp, td[i].comment);
     }
 #undef add_bitmap
 }
@@ -1013,211 +1079,201 @@ static void test_imagelist_storage(void)
     HICON icon;
     INT ret;
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 1, 1);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 1, 1);
     ok(himl != 0, "ImageList_Create failed\n");
 
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 2, 4, BMP_CX * 4, BMP_CX * 1, ILC_COLOR24, "empty");
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 2, 4, ILC_COLOR24, "empty");
 
     image_list_init(himl);
-    check_iml_data(himl, BMP_CX, BMP_CX, 24, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "orig");
+    check_iml_data(himl, BMP_CX, BMP_CX, 24, 27, 4, ILC_COLOR24, "orig");
 
-    ret = ImageList_Remove(himl, 4);
+    ret = pImageList_Remove(himl, 4);
     ok(ret, "ImageList_Remove failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 23, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "1");
+    check_iml_data(himl, BMP_CX, BMP_CX, 23, 27, 4, ILC_COLOR24, "1");
 
-    ret = ImageList_Remove(himl, 5);
+    ret = pImageList_Remove(himl, 5);
     ok(ret, "ImageList_Remove failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 22, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "2");
+    check_iml_data(himl, BMP_CX, BMP_CX, 22, 27, 4, ILC_COLOR24, "2");
 
-    ret = ImageList_Remove(himl, 6);
+    ret = pImageList_Remove(himl, 6);
     ok(ret, "ImageList_Remove failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 21, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "3");
+    check_iml_data(himl, BMP_CX, BMP_CX, 21, 27, 4, ILC_COLOR24, "3");
 
-    ret = ImageList_Remove(himl, 7);
+    ret = pImageList_Remove(himl, 7);
     ok(ret, "ImageList_Remove failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 20, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "4");
+    check_iml_data(himl, BMP_CX, BMP_CX, 20, 27, 4, ILC_COLOR24, "4");
 
-    ret = ImageList_Remove(himl, -2);
+    ret = pImageList_Remove(himl, -2);
     ok(!ret, "ImageList_Remove(-2) should fail\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 20, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "5");
+    check_iml_data(himl, BMP_CX, BMP_CX, 20, 27, 4, ILC_COLOR24, "5");
 
-    ret = ImageList_Remove(himl, 20);
+    ret = pImageList_Remove(himl, 20);
     ok(!ret, "ImageList_Remove(20) should fail\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 20, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "6");
+    check_iml_data(himl, BMP_CX, BMP_CX, 20, 27, 4, ILC_COLOR24, "6");
 
-    ret = ImageList_Remove(himl, -1);
+    ret = pImageList_Remove(himl, -1);
     ok(ret, "ImageList_Remove(-1) failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 4, 4, BMP_CX * 4, BMP_CX * 1, ILC_COLOR24, "7");
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 4, 4, ILC_COLOR24, "7");
 
-    ret = ImageList_Destroy(himl);
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
     /* test ImageList_Create storage allocation */
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 0, 32);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 0, 32);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 1, 32, BMP_CX * 4, BMP_CX * 1, ILC_COLOR24, "init 0 grow 32");
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 1, 32, ILC_COLOR24, "init 0 grow 32");
     hbm = create_bitmap(BMP_CX * 9, BMP_CX, 0, "9");
-    ret = ImageList_Add(himl, hbm, NULL);
+    ret = pImageList_Add(himl, hbm, NULL);
     ok(ret == 0, "ImageList_Add returned %d, expected 0\n", ret);
-    check_iml_data(himl, BMP_CX, BMP_CX, 1, 34, 32, BMP_CX * 4, BMP_CX * 9, ILC_COLOR24, "add 1 x 9");
+    check_iml_data(himl, BMP_CX, BMP_CX, 1, 34, 32, ILC_COLOR24, "add 1 x 9");
     DeleteObject(hbm);
-    ret = ImageList_Destroy(himl);
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 4, 4);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 4, 4);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR24, "init 4 grow 4");
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR24, "init 4 grow 4");
     hbm = create_bitmap(BMP_CX, BMP_CX * 9, 0, "9");
-    ret = ImageList_Add(himl, hbm, NULL);
+    ret = pImageList_Add(himl, hbm, NULL);
     ok(ret == 0, "ImageList_Add returned %d, expected 0\n", ret);
-    check_iml_data(himl, BMP_CX, BMP_CX, 9, 15, 4, BMP_CX * 4, BMP_CX * 4, ILC_COLOR24, "add 9 x 1");
-    ret = ImageList_Add(himl, hbm, NULL);
+    check_iml_data(himl, BMP_CX, BMP_CX, 9, 15, 4, ILC_COLOR24, "add 9 x 1");
+    ret = pImageList_Add(himl, hbm, NULL);
     ok(ret == 9, "ImageList_Add returned %d, expected 9\n", ret);
-    check_iml_data(himl, BMP_CX, BMP_CX, 18, 25, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "add 9 x 1");
+    check_iml_data(himl, BMP_CX, BMP_CX, 18, 25, 4, ILC_COLOR24, "add 9 x 1");
     DeleteObject(hbm);
-    ret = ImageList_Destroy(himl);
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 207, 209);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 207, 209);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 208, 212, BMP_CX * 4, BMP_CX * 52, ILC_COLOR24, "init 207 grow 209");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 208, 212, ILC_COLOR24, "init 207 grow 209");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 209, 207);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 209, 207);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 210, 208, BMP_CX * 4, BMP_CX * 53, ILC_COLOR24, "init 209 grow 207");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 210, 208, ILC_COLOR24, "init 209 grow 207");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 14, 4);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 14, 4);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 15, 4, BMP_CX * 4, BMP_CX * 4, ILC_COLOR24, "init 14 grow 4");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 15, 4, ILC_COLOR24, "init 14 grow 4");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 5, 9);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 5, 9);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 6, 12, BMP_CX * 4, BMP_CX * 2, ILC_COLOR24, "init 5 grow 9");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 6, 12, ILC_COLOR24, "init 5 grow 9");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 9, 5);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 9, 5);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 10, 8, BMP_CX * 4, BMP_CX * 3, ILC_COLOR24, "init 9 grow 5");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 10, 8, ILC_COLOR24, "init 9 grow 5");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 2, 4);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 2, 4);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 4, BMP_CX * 4, BMP_CX * 1, ILC_COLOR24, "init 2 grow 4");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 4, ILC_COLOR24, "init 2 grow 4");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 4, 2);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 4, 2);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR24, "init 4 grow 2");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR24, "init 4 grow 2");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR8, 4, 2);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR8, 4, 2);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR8, "bpp 8");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR8, "bpp 8");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4, 4, 2);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4, 4, 2);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR4, "bpp 4");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR4, "bpp 4");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, 0, 4, 2);
+    himl = pImageList_Create(BMP_CX, BMP_CX, 0, 4, 2);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR4, "bpp default");
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR4, "bpp default");
     icon = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
-    ok( ImageList_AddIcon(himl, icon) == 0,"failed to add icon\n");
-    ok( ImageList_AddIcon(himl, icon) == 1,"failed to add icon\n");
+    ok( pImageList_ReplaceIcon(himl, -1, icon) == 0, "Failed to add icon.\n");
+    ok( pImageList_ReplaceIcon(himl, -1, icon) == 1, "Failed to add icon.\n");
     DestroyIcon( icon );
-    check_iml_data(himl, BMP_CX, BMP_CX, 2, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR4, "bpp default");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 2, 5, 4, ILC_COLOR4, "bpp default");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24|ILC_MASK, 4, 2);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24|ILC_MASK, 4, 2);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR24|ILC_MASK,
-                   "bpp 24 + mask");
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR24|ILC_MASK, "bpp 24 + mask");
     icon = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
-    ok( ImageList_AddIcon(himl, icon) == 0,"failed to add icon\n");
-    ok( ImageList_AddIcon(himl, icon) == 1,"failed to add icon\n");
+    ok( pImageList_ReplaceIcon(himl, -1, icon) == 0, "Failed to add icon.\n");
+    ok( pImageList_ReplaceIcon(himl, -1, icon) == 1, "Failed to add icon.\n");
     DestroyIcon( icon );
-    check_iml_data(himl, BMP_CX, BMP_CX, 2, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR24|ILC_MASK,
-                   "bpp 24 + mask");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 2, 5, 4, ILC_COLOR24|ILC_MASK, "bpp 24 + mask");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 4, 2);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 4, 2);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR4|ILC_MASK,
-                   "bpp 4 + mask");
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR4|ILC_MASK, "bpp 4 + mask");
     icon = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
-    ok( ImageList_AddIcon(himl, icon) == 0,"failed to add icon\n");
-    ok( ImageList_AddIcon(himl, icon) == 1,"failed to add icon\n");
+    ok( pImageList_ReplaceIcon(himl, -1, icon) == 0, "Failed to add icon.\n");
+    ok( pImageList_ReplaceIcon(himl, -1, icon) == 1, "Failed to add icon.\n");
     DestroyIcon( icon );
-    check_iml_data(himl, BMP_CX, BMP_CX, 2, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR4|ILC_MASK,
-                   "bpp 4 + mask");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 2, 5, 4, ILC_COLOR4|ILC_MASK, "bpp 4 + mask");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, 99);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, 99);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 100, BMP_CX * 4, BMP_CX, ILC_COLOR4|ILC_MASK,
-                   "init 2 grow 99");
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99");
     icon = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
-    ok( ImageList_AddIcon(himl, icon) == 0,"failed to add icon\n");
-    ok( ImageList_AddIcon(himl, icon) == 1,"failed to add icon\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 2, 3, 100, BMP_CX * 4, BMP_CX, ILC_COLOR4|ILC_MASK,
-                   "init 2 grow 99 2 icons");
-    ok( ImageList_AddIcon(himl, icon) == 2,"failed to add icon\n");
+    ok( pImageList_ReplaceIcon(himl, -1, icon) == 0, "Failed to add icon.\n");
+    ok( pImageList_ReplaceIcon(himl, -1, icon) == 1, "Failed to add icon.\n");
+    check_iml_data(himl, BMP_CX, BMP_CX, 2, 3, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99 2 icons");
+    ok( pImageList_ReplaceIcon(himl, -1, icon) == 2, "Failed to add icon\n");
     DestroyIcon( icon );
-    check_iml_data(himl, BMP_CX, BMP_CX, 3, 104, 100, BMP_CX * 4, BMP_CX * 104/4, ILC_COLOR4|ILC_MASK,
-                   "init 2 grow 99 3 icons");
-    ok( ImageList_Remove(himl, -1) == TRUE,"failed to remove icon\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 100, 100, BMP_CX * 4, BMP_CX * 100/4, ILC_COLOR4|ILC_MASK,
-                   "init 2 grow 99 empty");
-    ok( ImageList_SetImageCount(himl, 22) == TRUE,"failed to set image count\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 22, 23, 100, BMP_CX * 4, BMP_CX * 24/4, ILC_COLOR4|ILC_MASK,
-                   "init 2 grow 99 set count 22");
-    ok( ImageList_SetImageCount(himl, 0) == TRUE,"failed to set image count\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 1, 100, BMP_CX * 4, BMP_CX, ILC_COLOR4|ILC_MASK,
-                   "init 2 grow 99 set count 0");
-    ok( ImageList_SetImageCount(himl, 42) == TRUE,"failed to set image count\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 42, 43, 100, BMP_CX * 4, BMP_CX * 44/4, ILC_COLOR4|ILC_MASK,
-                   "init 2 grow 99 set count 42");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 3, 104, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99 3 icons");
+    ok( pImageList_Remove(himl, -1) == TRUE, "Failed to remove icon.\n");
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 100, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99 empty");
+    ok( pImageList_SetImageCount(himl, 22) == TRUE, "Failed to set image count.\n");
+    check_iml_data(himl, BMP_CX, BMP_CX, 22, 23, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99 set count 22");
+    ok( pImageList_SetImageCount(himl, 0) == TRUE, "Failed to set image count.\n");
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 1, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99 set count 0");
+    ok( pImageList_SetImageCount(himl, 42) == TRUE, "Failed to set image count.\n");
+    check_iml_data(himl, BMP_CX, BMP_CX, 42, 43, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99 set count 42");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, 65536+12);
+    himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, -20);
     ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 12, BMP_CX * 4, BMP_CX, ILC_COLOR4|ILC_MASK,
-                   "init 2 grow 65536+12");
-    ret = ImageList_Destroy(himl);
+    check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 4, ILC_COLOR4|ILC_MASK, "init 2 grow -20");
+    ret = pImageList_Destroy(himl);
     ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, 65535);
-    ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 0, BMP_CX * 4, BMP_CX, ILC_COLOR4|ILC_MASK,
-                   "init 2 grow 65535");
-    ret = ImageList_Destroy(himl);
-    ok(ret, "ImageList_Destroy failed\n");
+    /* Version 6 implementation hangs on large grow values. */
+    if (!is_v6_test())
+    {
+        himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, 65536+12);
+        ok(himl != 0, "ImageList_Create failed\n");
+        check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 12, ILC_COLOR4|ILC_MASK, "init 2 grow 65536+12");
+        ret = pImageList_Destroy(himl);
+        ok(ret, "ImageList_Destroy failed\n");
 
-    himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, -20);
-    ok(himl != 0, "ImageList_Create failed\n");
-    check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 4, BMP_CX * 4, BMP_CX, ILC_COLOR4|ILC_MASK,
-                   "init 2 grow -20");
-    ret = ImageList_Destroy(himl);
-    ok(ret, "ImageList_Destroy failed\n");
+        himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, 65535);
+        ok(himl != 0, "ImageList_Create failed\n");
+        check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 0, ILC_COLOR4|ILC_MASK, "init 2 grow 65535");
+        ret = pImageList_Destroy(himl);
+        ok(ret, "ImageList_Destroy failed\n");
+    }
 }
 
 static void test_shell_imagelist(void)
@@ -1553,7 +1609,7 @@ static void test_iimagelist(void)
     if (hr == S_OK)
         IImageList_Release(imgl);
 
-    ImageList_Destroy(himl);
+    pImageList_Destroy(himl);
 
     /* IImageList2 */
     hr = pImageList_CoCreateInstance(&CLSID_ImageList, NULL, &IID_IImageList2, (void**)&imagelist);
@@ -1566,81 +1622,6 @@ static void test_iimagelist(void)
     IImageList2_Release(imagelist);
 }
 
-static void test_hotspot_v6(void)
-{
-    struct hotspot {
-        int dx;
-        int dy;
-    };
-
-#define SIZEX1 47
-#define SIZEY1 31
-#define SIZEX2 11
-#define SIZEY2 17
-#define HOTSPOTS_MAX 4       /* Number of entries in hotspots */
-    static const struct hotspot hotspots[HOTSPOTS_MAX] = {
-        { 10, 7 },
-        { SIZEX1, SIZEY1 },
-        { -9, -8 },
-        { -7, 35 }
-    };
-    int i, j;
-    HIMAGELIST himl1 = createImageList(SIZEX1, SIZEY1);
-    HIMAGELIST himl2 = createImageList(SIZEX2, SIZEY2);
-    IImageList *imgl1, *imgl2;
-    HRESULT hr;
-
-    /* cast to IImageList */
-    imgl1 = (IImageList *) himl1;
-    imgl2 = (IImageList *) himl2;
-
-    for (i = 0; i < HOTSPOTS_MAX; i++) {
-        for (j = 0; j < HOTSPOTS_MAX; j++) {
-            int dx1 = hotspots[i].dx;
-            int dy1 = hotspots[i].dy;
-            int dx2 = hotspots[j].dx;
-            int dy2 = hotspots[j].dy;
-            int correctx, correcty, newx, newy;
-            char loc[256];
-            IImageList *imglNew;
-            POINT ppt;
-
-            hr = IImageList_BeginDrag(imgl1, 0, dx1, dy1);
-            ok(SUCCEEDED(hr), "BeginDrag failed for { %d, %d }\n", dx1, dy1);
-            sprintf(loc, "BeginDrag (%d,%d)\n", i, j);
-
-            /* check merging the dragged image with a second image */
-            hr = IImageList_SetDragCursorImage(imgl2, (IUnknown *) imgl2, 0, dx2, dy2);
-            ok(SUCCEEDED(hr), "SetDragCursorImage failed for {%d, %d}{%d, %d}\n",
-                    dx1, dy1, dx2, dy2);
-            sprintf(loc, "SetDragCursorImage (%d,%d)\n", i, j);
-
-            /* check new hotspot, it should be the same like the old one */
-            hr = IImageList_GetDragImage(imgl2, NULL, &ppt, &IID_IImageList, (PVOID *) &imglNew);
-            ok(SUCCEEDED(hr), "GetDragImage failed\n");
-            ok(ppt.x == dx1 && ppt.y == dy1,
-                    "Expected drag hotspot [%d,%d] got [%d,%d]\n",
-                    dx1, dy1, ppt.x, ppt.y);
-            /* check size of new dragged image */
-            IImageList_GetIconSize(imglNew, &newx, &newy);
-            correctx = max(SIZEX1, max(SIZEX2 + dx2, SIZEX1 - dx2));
-            correcty = max(SIZEY1, max(SIZEY2 + dy2, SIZEY1 - dy2));
-            ok(newx == correctx && newy == correcty,
-                    "Expected drag image size [%d,%d] got [%d,%d]\n",
-                    correctx, correcty, newx, newy);
-            sprintf(loc, "GetDragImage (%d,%d)\n", i, j);
-            IImageList_EndDrag(imgl2);
-        }
-    }
-#undef SIZEX1
-#undef SIZEY1
-#undef SIZEX2
-#undef SIZEY2
-#undef HOTSPOTS_MAX
-    IImageList_Release(imgl2);
-    IImageList_Release(imgl1);
-}
-
 static void test_IImageList_Add_Remove(void)
 {
     IImageList *imgl;
@@ -1748,7 +1729,7 @@ static void test_IImageList_Draw(void)
     HDC hdc;
     int ret;
 
-    hwndfortest = create_a_window();
+    hwndfortest = create_window();
     hdc = GetDC(hwndfortest);
     ok(hdc!=NULL, "couldn't get DC\n");
 
@@ -1826,7 +1807,7 @@ static void test_IImageList_Merge(void)
     HIMAGELIST himl1, himl2;
     IImageList *imgl1, *imgl2, *merge;
     HICON hicon1;
-    HWND hwnd = create_a_window();
+    HWND hwnd = create_window();
     HRESULT hr;
     int ret;
 
@@ -1913,94 +1894,89 @@ static void test_iconsize(void)
     INT cx, cy;
     BOOL ret;
 
-    himl = ImageList_Create(16, 16, ILC_COLOR16, 0, 3);
+    himl = pImageList_Create(16, 16, ILC_COLOR16, 0, 3);
     /* null pointers, not zero imagelist dimensions */
-    ret = ImageList_GetIconSize(himl, NULL, NULL);
+    ret = pImageList_GetIconSize(himl, NULL, NULL);
     ok(!ret, "got %d\n", ret);
 
     /* doesn't touch return pointers */
     cx = 0x1abe11ed;
-    ret = ImageList_GetIconSize(himl, &cx, NULL);
+    ret = pImageList_GetIconSize(himl, &cx, NULL);
     ok(!ret, "got %d\n", ret);
     ok(cx == 0x1abe11ed, "got %d\n", cx);
 
     cy = 0x1abe11ed;
-    ret = ImageList_GetIconSize(himl, NULL, &cy);
+    ret = pImageList_GetIconSize(himl, NULL, &cy);
     ok(!ret, "got %d\n", ret);
     ok(cy == 0x1abe11ed, "got %d\n", cy);
 
-    ImageList_Destroy(himl);
+    pImageList_Destroy(himl);
 
-    ret = ImageList_GetIconSize((HIMAGELIST)0xdeadbeef, &cx, &cy);
+    ret = pImageList_GetIconSize((HIMAGELIST)0xdeadbeef, &cx, &cy);
     ok(!ret, "got %d\n", ret);
 }
 
 static void test_create_destroy(void)
 {
     HIMAGELIST himl;
-    IImageList *imgl;
     INT cx, cy;
     BOOL rc;
-    HRESULT hr;
     INT ret;
 
     /* list with zero or negative image dimensions */
-    himl = ImageList_Create(0, 0, ILC_COLOR16, 0, 3);
+    himl = pImageList_Create(0, 0, ILC_COLOR16, 0, 3);
     ok(himl == NULL, "got %p\n", himl);
 
-    himl = ImageList_Create(0, 16, ILC_COLOR16, 0, 3);
+    himl = pImageList_Create(0, 16, ILC_COLOR16, 0, 3);
     ok(himl == NULL, "got %p\n", himl);
 
-    himl = ImageList_Create(16, 0, ILC_COLOR16, 0, 3);
+    himl = pImageList_Create(16, 0, ILC_COLOR16, 0, 3);
     ok(himl == NULL, "got %p\n", himl);
 
-    himl = ImageList_Create(16, -1, ILC_COLOR16, 0, 3);
+    himl = pImageList_Create(16, -1, ILC_COLOR16, 0, 3);
     ok(himl == NULL, "got %p\n", himl);
 
-    himl = ImageList_Create(-1, 16, ILC_COLOR16, 0, 3);
+    himl = pImageList_Create(-1, 16, ILC_COLOR16, 0, 3);
     ok(himl == NULL, "got %p\n", himl);
 
-    rc = ImageList_Destroy((HIMAGELIST)0xdeadbeef);
+    rc = pImageList_Destroy((HIMAGELIST)0xdeadbeef);
     ok(rc == FALSE, "ImageList_Destroy(0xdeadbeef) should fail and not crash\n");
 
     /* DDB image lists */
-    himl = ImageList_Create(0, 14, ILC_COLORDDB, 4, 4);
+    himl = pImageList_Create(0, 14, ILC_COLORDDB, 4, 4);
     ok(himl != NULL, "got %p\n", himl);
-    imgl = (IImageList*)himl;
-    IImageList_GetIconSize(imgl, &cx, &cy);
+
+    pImageList_GetIconSize(himl, &cx, &cy);
     ok (cx == 0, "Wrong cx (%i)\n", cx);
     ok (cy == 14, "Wrong cy (%i)\n", cy);
-    ImageList_Destroy(himl);
+    pImageList_Destroy(himl);
 
-    himl = ImageList_Create(0, 0, ILC_COLORDDB, 4, 4);
+    himl = pImageList_Create(0, 0, ILC_COLORDDB, 4, 4);
     ok(himl != NULL, "got %p\n", himl);
-    imgl = (IImageList*)himl;
-    IImageList_GetIconSize(imgl, &cx, &cy);
+    pImageList_GetIconSize(himl, &cx, &cy);
     ok (cx == 0, "Wrong cx (%i)\n", cx);
     ok (cy == 0, "Wrong cy (%i)\n", cy);
-    ImageList_Destroy(himl);
+    pImageList_Destroy(himl);
 
-    himl = ImageList_Create(0, 0, ILC_COLORDDB, 0, 4);
+    himl = pImageList_Create(0, 0, ILC_COLORDDB, 0, 4);
     ok(himl != NULL, "got %p\n", himl);
-    imgl = (IImageList*)himl;
-    IImageList_GetIconSize(imgl, &cx, &cy);
+    pImageList_GetIconSize(himl, &cx, &cy);
     ok (cx == 0, "Wrong cx (%i)\n", cx);
     ok (cy == 0, "Wrong cy (%i)\n", cy);
 
-    hr = IImageList_SetImageCount(imgl, 3);
-    ok(hr == S_OK, "got 0x%08x\n", hr);
-    hr = IImageList_GetImageCount(imgl, &ret);
-    ok(hr == S_OK && ret == 3, "invalid image count after increase\n");
+    pImageList_SetImageCount(himl, 3);
+    ret = pImageList_GetImageCount(himl);
+    ok(ret == 3, "Unexpected image count after increase\n");
 
     /* Trying to actually add an image causes a crash on Windows */
-    ImageList_Destroy(himl);
+    pImageList_Destroy(himl);
 
     /* Negative values fail */
-    himl = ImageList_Create(-1, -1, ILC_COLORDDB, 4, 4);
+    himl = pImageList_Create(-1, -1, ILC_COLORDDB, 4, 4);
     ok(himl == NULL, "got %p\n", himl);
-    himl = ImageList_Create(-1, 1, ILC_COLORDDB, 4, 4);
+    himl = pImageList_Create(-1, 1, ILC_COLORDDB, 4, 4);
     ok(himl == NULL, "got %p\n", himl);
-    himl = ImageList_Create(1, -1, ILC_COLORDDB, 4, 4);
+    himl = pImageList_Create(1, -1, ILC_COLORDDB, 4, 4);
     ok(himl == NULL, "got %p\n", himl);
 }
 
@@ -2013,7 +1989,7 @@ static void check_color_table(const char *name, HDC hdc, HIMAGELIST himl, UINT i
     BITMAPINFO *bmi = (BITMAPINFO *)bmi_buffer;
     int i, depth = ilc & 0xfe;
 
-    ret = ImageList_GetImageInfo(himl, 0, &info);
+    ret = pImageList_GetImageInfo(himl, 0, &info);
     ok(ret, "got %d\n", ret);
     ok(info.hbmImage != NULL, "got %p\n", info.hbmImage);
 
@@ -2031,9 +2007,9 @@ static void check_color_table(const char *name, HDC hdc, HIMAGELIST himl, UINT i
         ok((bmi->bmiColors[i].rgbRed == expect[i].rgbRed &&
             bmi->bmiColors[i].rgbGreen == expect[i].rgbGreen &&
             bmi->bmiColors[i].rgbBlue == expect[i].rgbBlue) ||
-           (broken_expect && broken(bmi->bmiColors[i].rgbRed == broken_expect[i].rgbRed &&
+           broken(bmi->bmiColors[i].rgbRed == broken_expect[i].rgbRed &&
                   bmi->bmiColors[i].rgbGreen == broken_expect[i].rgbGreen &&
-                  bmi->bmiColors[i].rgbBlue == broken_expect[i].rgbBlue)),
+                  bmi->bmiColors[i].rgbBlue == broken_expect[i].rgbBlue),
            "%d: %s: got color[%d] %02x %02x %02x expect %02x %02x %02x\n", depth, name, i,
            bmi->bmiColors[i].rgbRed, bmi->bmiColors[i].rgbGreen, bmi->bmiColors[i].rgbBlue,
            expect[i].rgbRed, expect[i].rgbGreen, expect[i].rgbBlue);
@@ -2097,7 +2073,7 @@ static void test_color_table(UINT ilc)
 
     get_default_color_table(hdc, ilc & 0xfe, default_table);
 
-    himl = ImageList_Create(16, 16, ilc, 0, 3);
+    himl = pImageList_Create(16, 16, ilc, 0, 3);
     ok(himl != NULL, "got %p\n", himl);
 
     memset(bmi_buffer, 0, sizeof(bmi_buffer));
@@ -2131,17 +2107,17 @@ static void test_color_table(UINT ilc)
     dib32 = CreateDIBSection(hdc, bmi, DIB_RGB_COLORS, NULL, NULL, 0);
 
     /* add 32 first then 8.  This won't set the color table */
-    ret = ImageList_Add(himl, dib32, NULL);
+    ret = pImageList_Add(himl, dib32, NULL);
     ok(ret == 0, "got %d\n", ret);
-    ret = ImageList_Add(himl, dib8, NULL);
+    ret = pImageList_Add(himl, dib8, NULL);
     ok(ret == 1, "got %d\n", ret);
 
     check_color_table("add 32, 8", hdc, himl, ilc, default_table, NULL);
 
     /* since the previous _Adds didn't set the color table, this one will */
-    ret = ImageList_Remove(himl, -1);
+    ret = pImageList_Remove(himl, -1);
     ok(ret, "got %d\n", ret);
-    ret = ImageList_Add(himl, dib8, NULL);
+    ret = pImageList_Add(himl, dib8, NULL);
     ok(ret == 0, "got %d\n", ret);
 
     memset(rgb, 0, sizeof(rgb));
@@ -2152,18 +2128,18 @@ static void test_color_table(UINT ilc)
 
     /* remove all, add 4. Color table remains the same since it's inplicitly
        been set by the previous _Add */
-    ret = ImageList_Remove(himl, -1);
+    ret = pImageList_Remove(himl, -1);
     ok(ret, "got %d\n", ret);
-    ret = ImageList_Add(himl, dib4, NULL);
+    ret = pImageList_Add(himl, dib4, NULL);
     ok(ret == 0, "got %d\n", ret);
     check_color_table("remove all, add 4", hdc, himl, ilc, rgb, default_table);
 
-    ImageList_Destroy(himl);
-    himl = ImageList_Create(16, 16, ilc, 0, 3);
+    pImageList_Destroy(himl);
+    himl = pImageList_Create(16, 16, ilc, 0, 3);
     ok(himl != NULL, "got %p\n", himl);
 
     /* add 4 */
-    ret = ImageList_Add(himl, dib4, NULL);
+    ret = pImageList_Add(himl, dib4, NULL);
     ok(ret == 0, "got %d\n", ret);
 
     memset(rgb, 0, 16 * sizeof(rgb[0]));
@@ -2177,12 +2153,12 @@ static void test_color_table(UINT ilc)
 
     check_color_table("add 4", hdc, himl, ilc, rgb, default_table);
 
-    ImageList_Destroy(himl);
-    himl = ImageList_Create(16, 16, ilc, 0, 3);
+    pImageList_Destroy(himl);
+    himl = pImageList_Create(16, 16, ilc, 0, 3);
     ok(himl != NULL, "got %p\n", himl);
 
     /* set color table, add 8 */
-    ret = ImageList_Remove(himl, -1);
+    ret = pImageList_Remove(himl, -1);
     ok(ret, "got %d\n", ret);
     memset(rgb, 0, sizeof(rgb));
     rgb[0].rgbRed = 0xcc;
@@ -2190,7 +2166,7 @@ static void test_color_table(UINT ilc)
     ret = pImageList_SetColorTable(himl, 0, 2, rgb);
     ok(ret == 2, "got %d\n", ret);
     /* the table is set, so this doesn't change it */
-    ret = ImageList_Add(himl, dib8, NULL);
+    ret = pImageList_Add(himl, dib8, NULL);
     ok(ret == 0, "got %d\n", ret);
 
     memcpy(rgb + 2, default_table + 2, 254 * sizeof(rgb[0]));
@@ -2200,7 +2176,7 @@ static void test_color_table(UINT ilc)
     DeleteObject(dib8);
     DeleteObject(dib4);
     DeleteDC(hdc);
-    ImageList_Destroy(himl);
+    pImageList_Destroy(himl);
 }
 
 static void test_copy(void)
@@ -2209,21 +2185,41 @@ static void test_copy(void)
     BOOL ret;
     int count;
 
-    dst = ImageList_Create(5, 11, ILC_COLOR, 1, 1);
-    count = ImageList_GetImageCount(dst);
+    dst = pImageList_Create(5, 11, ILC_COLOR, 1, 1);
+    count = pImageList_GetImageCount(dst);
     ok(!count, "ImageList not empty.\n");
     src = createImageList(7, 13);
-    count = ImageList_GetImageCount(src);
+    count = pImageList_GetImageCount(src);
     ok(count > 2, "Tests need an ImageList with more than 2 images\n");
 
     /* ImageList_Copy() cannot copy between two ImageLists */
-    ret = ImageList_Copy(dst, 0, src, 2, ILCF_MOVE);
+    ret = pImageList_Copy(dst, 0, src, 2, ILCF_MOVE);
     ok(!ret, "ImageList_Copy() should have returned FALSE\n");
-    count = ImageList_GetImageCount(dst);
+    count = pImageList_GetImageCount(dst);
     ok(count == 0, "Expected no image in dst ImageList, got %d\n", count);
 
-    ImageList_Destroy(dst);
-    ImageList_Destroy(src);
+    pImageList_Destroy(dst);
+    pImageList_Destroy(src);
+}
+
+static void test_loadimage(void)
+{
+    HIMAGELIST list;
+    DWORD flags;
+
+    list = pImageList_LoadImageW( hinst, MAKEINTRESOURCEW(IDB_BITMAP_128x15), 16, 1, CLR_DEFAULT,
+                                 IMAGE_BITMAP, LR_CREATEDIBSECTION );
+    ok( list != NULL, "got %p\n", list );
+    flags = pImageList_GetFlags( list );
+    ok( flags == (ILC_COLOR4 | ILC_MASK), "got %08x\n", flags );
+    pImageList_Destroy( list );
+
+    list = pImageList_LoadImageW( hinst, MAKEINTRESOURCEW(IDB_BITMAP_128x15), 16, 1, CLR_NONE,
+                                 IMAGE_BITMAP, LR_CREATEDIBSECTION );
+    ok( list != NULL, "got %p\n", list );
+    flags = pImageList_GetFlags( list );
+    ok( flags == ILC_COLOR4, "got %08x\n", flags );
+    pImageList_Destroy( list );
 }
 
 static void test_IImageList_Clone(void)
@@ -2233,7 +2229,7 @@ static void test_IImageList_Clone(void)
     HRESULT hr;
     ULONG ref;
 
-    himl = ImageList_Create(16, 16, ILC_COLOR16, 0, 3);
+    himl = pImageList_Create(16, 16, ILC_COLOR16, 0, 3);
     imgl = (IImageList*)himl;
 
 if (0)
@@ -2257,7 +2253,7 @@ static void test_IImageList_GetBkColor(void)
     COLORREF color;
     HRESULT hr;
 
-    himl = ImageList_Create(16, 16, ILC_COLOR16, 0, 3);
+    himl = pImageList_Create(16, 16, ILC_COLOR16, 0, 3);
     imgl = (IImageList*)himl;
 
 if (0)
@@ -2279,7 +2275,7 @@ static void test_IImageList_SetBkColor(void)
     COLORREF color;
     HRESULT hr;
 
-    himl = ImageList_Create(16, 16, ILC_COLOR16, 0, 3);
+    himl = pImageList_Create(16, 16, ILC_COLOR16, 0, 3);
     imgl = (IImageList*)himl;
 
 if (0)
@@ -2309,7 +2305,7 @@ static void test_IImageList_GetImageCount(void)
     int count;
     HRESULT hr;
 
-    himl = ImageList_Create(16, 16, ILC_COLOR16, 0, 3);
+    himl = pImageList_Create(16, 16, ILC_COLOR16, 0, 3);
     imgl = (IImageList*)himl;
 
 if (0)
@@ -2333,7 +2329,7 @@ static void test_IImageList_GetIconSize(void)
     int cx, cy;
     HRESULT hr;
 
-    himl = ImageList_Create(16, 16, ILC_COLOR16, 0, 3);
+    himl = pImageList_Create(16, 16, ILC_COLOR16, 0, 3);
     imgl = (IImageList*)himl;
 
     hr = IImageList_GetIconSize(imgl, NULL, NULL);
@@ -2348,17 +2344,47 @@ static void test_IImageList_GetIconSize(void)
     IImageList_Release(imgl);
 }
 
+static void init_functions(void)
+{
+    HMODULE hComCtl32 = GetModuleHandleA("comctl32.dll");
+
+#define X(f) p##f = (void*)GetProcAddress(hComCtl32, #f);
+#define X2(f, ord) p##f = (void*)GetProcAddress(hComCtl32, (const char *)ord);
+    X(ImageList_Create);
+    X(ImageList_Destroy);
+    X(ImageList_Add);
+    X(ImageList_DrawIndirect);
+    X(ImageList_SetImageCount);
+    X(ImageList_SetImageCount);
+    X2(ImageList_SetColorTable, 390);
+    X(ImageList_GetFlags);
+    X(ImageList_BeginDrag);
+    X(ImageList_GetDragImage);
+    X(ImageList_EndDrag);
+    X(ImageList_GetImageCount);
+    X(ImageList_SetDragCursorImage);
+    X(ImageList_GetIconSize);
+    X(ImageList_Remove);
+    X(ImageList_ReplaceIcon);
+    X(ImageList_Replace);
+    X(ImageList_Merge);
+    X(ImageList_GetImageInfo);
+    X(ImageList_Write);
+    X(ImageList_Read);
+    X(ImageList_Copy);
+    X(ImageList_LoadImageW);
+    X(ImageList_CoCreateInstance);
+    X(HIMAGELIST_QueryInterface);
+#undef X
+#undef X2
+}
+
 START_TEST(imagelist)
 {
     ULONG_PTR ctx_cookie;
     HANDLE hCtx;
 
-    HMODULE hComCtl32 = GetModuleHandleA("comctl32.dll");
-    pImageList_Create = NULL;   /* These are not needed for non-v6.0 tests*/
-    pImageList_Add = NULL;
-    pImageList_DrawIndirect = (void*)GetProcAddress(hComCtl32, "ImageList_DrawIndirect");
-    pImageList_SetImageCount = (void*)GetProcAddress(hComCtl32, "ImageList_SetImageCount");
-    pImageList_SetColorTable = (void*)GetProcAddress(hComCtl32, (const char*)390);
+    init_functions();
 
     hinst = GetModuleHandleA(NULL);
 
@@ -2377,31 +2403,32 @@ START_TEST(imagelist)
     test_color_table(ILC_COLOR4);
     test_color_table(ILC_COLOR8);
     test_copy();
-
-    FreeLibrary(hComCtl32);
+    test_loadimage();
 
     /* Now perform v6 tests */
-
     if (!load_v6_module(&ctx_cookie, &hCtx))
         return;
 
-    /* Reload comctl32 */
-    hComCtl32 = LoadLibraryA("comctl32.dll");
-    pImageList_Create = (void*)GetProcAddress(hComCtl32, "ImageList_Create");
-    pImageList_Add = (void*)GetProcAddress(hComCtl32, "ImageList_Add");
-    pImageList_DrawIndirect = (void*)GetProcAddress(hComCtl32, "ImageList_DrawIndirect");
-    pImageList_SetImageCount = (void*)GetProcAddress(hComCtl32, "ImageList_SetImageCount");
-    pImageList_CoCreateInstance = (void*)GetProcAddress(hComCtl32, "ImageList_CoCreateInstance");
-    pHIMAGELIST_QueryInterface = (void*)GetProcAddress(hComCtl32, "HIMAGELIST_QueryInterface");
+    init_functions();
 
     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 
     /* Do v6.0 tests */
+    test_add_remove();
+    test_imagecount();
+    test_DrawIndirect();
+    test_merge();
+    test_imagelist_storage();
+    test_iconsize();
+    test_color_table(ILC_COLOR4);
+    test_color_table(ILC_COLOR8);
+    test_copy();
+    test_loadimage();
+
     test_ImageList_DrawIndirect();
     test_shell_imagelist();
     test_iimagelist();
 
-    test_hotspot_v6();
     test_IImageList_Add_Remove();
     test_IImageList_Get_SetImageCount();
     test_IImageList_Draw();
diff --git a/modules/rostests/winetests/comctl32/listbox.c b/modules/rostests/winetests/comctl32/listbox.c
new file mode 100644 (file)
index 0000000..d24aaa2
--- /dev/null
@@ -0,0 +1,1851 @@
+/* Unit test suite for list boxes.
+ *
+ * Copyright 2003 Ferenc Wagner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "precomp.h"
+
+static const char * const strings[4] = {
+    "First added",
+    "Second added",
+    "Third added",
+    "Fourth added which is very long because at some time we only had a 256 byte character buffer and "
+      "that was overflowing in one of those applications that had a common dialog file open box and tried "
+      "to add a 300 characters long custom filter string which of course the code did not like and crashed. "
+      "Just make sure this string is longer than 256 characters."
+};
+
+static const char BAD_EXTENSION[] = "*.badtxt";
+
+static HWND create_listbox(DWORD add_style, HWND parent)
+{
+    INT_PTR ctl_id = 0;
+    HWND handle;
+
+    if (parent)
+      ctl_id=1;
+
+    handle = CreateWindowA("LISTBOX", "TestList", (LBS_STANDARD & ~LBS_SORT) | add_style, 0, 0, 100, 100,
+        parent, (HMENU)ctl_id, NULL, 0);
+    ok(handle != NULL, "Failed to create listbox window.\n");
+
+    SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[0]);
+    SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[1]);
+    SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[2]);
+    SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[3]);
+
+    return handle;
+}
+
+struct listbox_prop
+{
+    DWORD add_style;
+};
+
+struct listbox_stat
+{
+    int selected, anchor, caret, selcount;
+};
+
+struct listbox_test
+{
+    struct listbox_prop prop;
+    struct listbox_stat  init,  init_todo;
+    struct listbox_stat click, click_todo;
+    struct listbox_stat  step,  step_todo;
+    struct listbox_stat   sel,   sel_todo;
+};
+
+static void listbox_query(HWND handle, struct listbox_stat *results)
+{
+    results->selected = SendMessageA(handle, LB_GETCURSEL, 0, 0);
+    results->anchor   = SendMessageA(handle, LB_GETANCHORINDEX, 0, 0);
+    results->caret    = SendMessageA(handle, LB_GETCARETINDEX, 0, 0);
+    results->selcount = SendMessageA(handle, LB_GETSELCOUNT, 0, 0);
+}
+
+static void buttonpress(HWND handle, WORD x, WORD y)
+{
+    LPARAM lp = x + (y << 16);
+
+    SendMessageA(handle, WM_LBUTTONDOWN, MK_LBUTTON, lp);
+    SendMessageA(handle, WM_LBUTTONUP, 0, lp);
+}
+
+static void keypress(HWND handle, WPARAM keycode, BYTE scancode, BOOL extended)
+{
+    LPARAM lp = 1 + (scancode << 16) + (extended ? KEYEVENTF_EXTENDEDKEY : 0);
+
+    SendMessageA(handle, WM_KEYDOWN, keycode, lp);
+    SendMessageA(handle, WM_KEYUP  , keycode, lp | 0xc000000);
+}
+
+#define listbox_field_ok(t, s, f, got) \
+  ok (t.s.f==got.f, "style %#x, step " #s ", field " #f \
+      ": expected %d, got %d\n", (unsigned int)t.prop.add_style, \
+      t.s.f, got.f)
+
+#define listbox_todo_field_ok(t, s, f, got) \
+  todo_wine_if (t.s##_todo.f) { listbox_field_ok(t, s, f, got); }
+
+#define listbox_ok(t, s, got) \
+  listbox_todo_field_ok(t, s, selected, got); \
+  listbox_todo_field_ok(t, s, anchor, got); \
+  listbox_todo_field_ok(t, s, caret, got); \
+  listbox_todo_field_ok(t, s, selcount, got)
+
+static void run_test(const struct listbox_test test)
+{
+    struct listbox_stat answer;
+    HWND hLB=create_listbox (test.prop.add_style, 0);
+    RECT second_item;
+    int i, res;
+
+    listbox_query (hLB, &answer);
+    listbox_ok (test, init, answer);
+
+    SendMessageA(hLB, LB_GETITEMRECT, 1, (LPARAM) &second_item);
+    buttonpress(hLB, (WORD)second_item.left, (WORD)second_item.top);
+
+    listbox_query(hLB, &answer);
+    listbox_ok(test, click, answer);
+
+    keypress(hLB, VK_DOWN, 0x50, TRUE);
+
+    listbox_query(hLB, &answer);
+    listbox_ok(test, step, answer);
+
+    DestroyWindow(hLB);
+
+    hLB = create_listbox(test.prop.add_style, 0);
+
+    SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2));
+    listbox_query(hLB, &answer);
+    listbox_ok(test, sel, answer);
+
+    for (i = 0; i < 4; i++)
+    {
+        DWORD size = SendMessageA(hLB, LB_GETTEXTLEN, i, 0);
+        int resA, resW;
+        WCHAR *txtw;
+        CHAR *txt;
+
+        txt = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size + 1);
+        resA = SendMessageA(hLB, LB_GETTEXT, i, (LPARAM)txt);
+        ok(!strcmp(txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]);
+
+        txtw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (size + 1) * sizeof(*txtw));
+        resW = SendMessageW(hLB, LB_GETTEXT, i, (LPARAM)txtw);
+        if (resA != resW)
+            trace("SendMessageW(LB_GETTEXT) not supported on this platform (resA=%d resW=%d), skipping...\n", resA, resW);
+        else
+        {
+            WideCharToMultiByte(CP_ACP, 0, txtw, -1, txt, size, NULL, NULL);
+            ok(!strcmp (txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]);
+        }
+
+        HeapFree(GetProcessHeap(), 0, txtw);
+        HeapFree(GetProcessHeap(), 0, txt);
+    }
+
+    /* Confirm the count of items, and that an invalid delete does not remove anything */
+    res = SendMessageA(hLB, LB_GETCOUNT, 0, 0);
+    ok(res == 4, "Expected 4 items, got %d\n", res);
+    res = SendMessageA(hLB, LB_DELETESTRING, -1, 0);
+    ok(res == LB_ERR, "Expected LB_ERR items, got %d\n", res);
+    res = SendMessageA(hLB, LB_DELETESTRING, 4, 0);
+    ok(res == LB_ERR, "Expected LB_ERR items, got %d\n", res);
+    res = SendMessageA(hLB, LB_GETCOUNT, 0, 0);
+    ok(res == 4, "Expected 4 items, got %d\n", res);
+
+    DestroyWindow(hLB);
+}
+
+static void test_item_height(void)
+{
+    INT itemHeight;
+    TEXTMETRICA tm;
+    HFONT font;
+    HWND hLB;
+    HDC hdc;
+
+    hLB = create_listbox (0, 0);
+    ok ((hdc = GetDCEx( hLB, 0, DCX_CACHE )) != 0, "Can't get hdc\n");
+    ok ((font = GetCurrentObject(hdc, OBJ_FONT)) != 0, "Can't get the current font\n");
+    ok (GetTextMetricsA( hdc, &tm ), "Can't read font metrics\n");
+    ReleaseDC( hLB, hdc);
+
+    ok (SendMessageA(hLB, WM_SETFONT, (WPARAM)font, 0) == 0, "Can't set font\n");
+
+    itemHeight = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0);
+    ok (itemHeight == tm.tmHeight, "Item height wrong, got %d, expecting %d\n", itemHeight, tm.tmHeight);
+
+    DestroyWindow (hLB);
+
+    hLB = CreateWindowA("LISTBOX", "TestList", LBS_OWNERDRAWVARIABLE,  0, 0, 100, 100, NULL, NULL, NULL, 0);
+
+    itemHeight = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0);
+    ok(itemHeight > 0 && itemHeight <= tm.tmHeight, "Unexpected item height %d, expected %d.\n",
+        itemHeight, tm.tmHeight);
+    itemHeight = SendMessageA(hLB, LB_GETITEMHEIGHT, 5, 0);
+    ok(itemHeight > 0 && itemHeight <= tm.tmHeight, "Unexpected item height %d, expected %d.\n",
+        itemHeight, tm.tmHeight);
+    itemHeight = SendMessageA(hLB, LB_GETITEMHEIGHT, -5, 0);
+    ok(itemHeight > 0 && itemHeight <= tm.tmHeight, "Unexpected item height %d, expected %d.\n",
+        itemHeight, tm.tmHeight);
+
+    DestroyWindow (hLB);
+}
+
+static int got_selchange;
+
+static LRESULT WINAPI main_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+    case WM_DRAWITEM:
+    {
+        RECT rc_item, rc_client, rc_clip;
+        DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lparam;
+
+        ok(wparam == dis->CtlID, "got wParam=%08lx instead of %08x\n", wparam, dis->CtlID);
+        ok(dis->CtlType == ODT_LISTBOX, "wrong CtlType %04x\n", dis->CtlType);
+
+        GetClientRect(dis->hwndItem, &rc_client);
+        GetClipBox(dis->hDC, &rc_clip);
+        ok(EqualRect(&rc_client, &rc_clip) || IsRectEmpty(&rc_clip),
+           "client rect of the listbox should be equal to the clip box,"
+           "or the clip box should be empty\n");
+
+        SendMessageA(dis->hwndItem, LB_GETITEMRECT, dis->itemID, (LPARAM)&rc_item);
+        ok(EqualRect(&dis->rcItem, &rc_item), "item rects are not equal\n");
+
+        break;
+    }
+
+    case WM_COMMAND:
+        if (HIWORD( wparam ) == LBN_SELCHANGE) got_selchange++;
+        break;
+
+    default:
+        break;
+    }
+
+    return DefWindowProcA(hwnd, msg, wparam, lparam);
+}
+
+static HWND create_parent( void )
+{
+    static ATOM class;
+    WNDCLASSA cls;
+
+    if (!class)
+    {
+        cls.style = 0;
+        cls.lpfnWndProc = main_window_proc;
+        cls.cbClsExtra = 0;
+        cls.cbWndExtra = 0;
+        cls.hInstance = GetModuleHandleA(NULL);
+        cls.hIcon = 0;
+        cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
+        cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+        cls.lpszMenuName = NULL;
+        cls.lpszClassName = "main_window_class";
+        class = RegisterClassA( &cls );
+    }
+
+    return CreateWindowExA(0, "main_window_class", NULL, WS_POPUP | WS_VISIBLE, 100, 100, 400, 400, GetDesktopWindow(),
+        0, GetModuleHandleA(NULL), NULL);
+}
+
+static void test_ownerdraw(void)
+{
+    HWND parent, hLB;
+    INT ret;
+    RECT rc;
+
+    parent = create_parent();
+    assert(parent);
+
+    hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE, parent);
+    assert(hLB);
+
+    SetForegroundWindow(hLB);
+    UpdateWindow(hLB);
+
+    /* make height short enough */
+    SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
+    SetWindowPos(hLB, 0, 0, 0, 100, rc.bottom - rc.top + 1, SWP_NOZORDER | SWP_NOMOVE);
+
+    /* make 0 item invisible */
+    SendMessageA(hLB, LB_SETTOPINDEX, 1, 0);
+    ret = SendMessageA(hLB, LB_GETTOPINDEX, 0, 0);
+    ok(ret == 1, "wrong top index %d\n", ret);
+
+    SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
+    ok(!IsRectEmpty(&rc), "empty item rect\n");
+    ok(rc.top < 0, "rc.top is not negative (%d)\n", rc.top);
+
+    DestroyWindow(hLB);
+    DestroyWindow(parent);
+}
+
+#define listbox_test_query(exp, got) \
+  ok(exp.selected == got.selected, "expected selected %d, got %d\n", exp.selected, got.selected); \
+  ok(exp.anchor == got.anchor, "expected anchor %d, got %d\n", exp.anchor, got.anchor); \
+  ok(exp.caret == got.caret, "expected caret %d, got %d\n", exp.caret, got.caret); \
+  ok(exp.selcount == got.selcount, "expected selcount %d, got %d\n", exp.selcount, got.selcount);
+
+static void test_LB_SELITEMRANGE(void)
+{
+    static const struct listbox_stat test_nosel = { 0, LB_ERR, 0, 0 };
+    static const struct listbox_stat test_1 = { 0, LB_ERR, 0, 2 };
+    static const struct listbox_stat test_2 = { 0, LB_ERR, 0, 3 };
+    static const struct listbox_stat test_3 = { 0, LB_ERR, 0, 4 };
+    struct listbox_stat answer;
+    HWND hLB;
+    INT ret;
+
+    hLB = create_listbox(LBS_EXTENDEDSEL, 0);
+    assert(hLB);
+
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_nosel, answer);
+
+    ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2));
+    ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_1, answer);
+
+    SendMessageA(hLB, LB_SETSEL, FALSE, -1);
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_nosel, answer);
+
+    ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(0, 4));
+    ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_3, answer);
+
+    SendMessageA(hLB, LB_SETSEL, FALSE, -1);
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_nosel, answer);
+
+    ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(-5, 5));
+    ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_nosel, answer);
+
+    SendMessageA(hLB, LB_SETSEL, FALSE, -1);
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_nosel, answer);
+
+    ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(2, 10));
+    ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_1, answer);
+
+    SendMessageA(hLB, LB_SETSEL, FALSE, -1);
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_nosel, answer);
+
+    ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(4, 10));
+    ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_nosel, answer);
+
+    SendMessageA(hLB, LB_SETSEL, FALSE, -1);
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_nosel, answer);
+
+    ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(10, 1));
+    ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_2, answer);
+
+    SendMessageA(hLB, LB_SETSEL, FALSE, -1);
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_nosel, answer);
+
+    ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, -1));
+    ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+    listbox_query(hLB, &answer);
+    listbox_test_query(test_2, answer);
+
+    DestroyWindow(hLB);
+}
+
+static void test_LB_SETCURSEL(void)
+{
+    HWND parent, hLB;
+    INT ret;
+
+    parent = create_parent();
+    ok(parent != NULL, "Failed to create parent window.\n");
+
+    hLB = create_listbox(LBS_NOINTEGRALHEIGHT | WS_CHILD, parent);
+    ok(hLB != NULL, "Failed to create listbox.\n");
+
+    SendMessageA(hLB, LB_SETITEMHEIGHT, 0, 32);
+
+    ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0);
+    ok(ret == 2, "LB_SETCURSEL returned %d instead of 2\n", ret);
+    ret = GetScrollPos(hLB, SB_VERT);
+    ok(ret == 0, "expected vscroll 0, got %d\n", ret);
+
+    ret = SendMessageA(hLB, LB_SETCURSEL, 3, 0);
+    ok(ret == 3, "LB_SETCURSEL returned %d instead of 3\n", ret);
+    ret = GetScrollPos(hLB, SB_VERT);
+    ok(ret == 1, "expected vscroll 1, got %d\n", ret);
+
+    DestroyWindow(hLB);
+}
+
+static void test_listbox_height(void)
+{
+    HWND hList;
+    int r, id;
+
+    hList = CreateWindowA( "ListBox", "list test", 0,
+                          1, 1, 600, 100, NULL, NULL, NULL, NULL );
+    ok( hList != NULL, "failed to create listbox\n");
+
+    id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi");
+    ok( id == 0, "item id wrong\n");
+
+    r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 20, 0 ));
+    ok( r == 0, "send message failed\n");
+
+    r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
+    ok( r == 20, "height wrong\n");
+
+    r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 0, 30 ));
+    ok( r == -1, "send message failed\n");
+
+    r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
+    ok( r == 20, "height wrong\n");
+
+    r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 256, 0 ));
+    ok( r == -1, "Failed to set item height, %d.\n", r);
+
+    r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
+    ok( r == 20, "Unexpected item height %d.\n", r);
+
+    r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 0xff, 0 ));
+    ok( r == 0, "send message failed\n");
+
+    r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
+    ok( r == 0xff, "height wrong\n");
+
+    DestroyWindow( hList );
+}
+
+static void test_itemfrompoint(void)
+{
+    /* WS_POPUP is required in order to have a more accurate size calculation (
+       without caption). LBS_NOINTEGRALHEIGHT is required in order to test
+       behavior of partially-displayed item.
+     */
+    HWND hList = CreateWindowA( "ListBox", "list test",
+                               WS_VISIBLE|WS_POPUP|LBS_NOINTEGRALHEIGHT,
+                               1, 1, 600, 100, NULL, NULL, NULL, NULL );
+    ULONG r, id;
+    RECT rc;
+
+    /* For an empty listbox win2k returns 0x1ffff, win98 returns 0x10000, nt4 returns 0xffffffff */
+    r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( /* x */ 30, /* y */ 30 ));
+    ok( r == 0x1ffff || r == 0x10000 || r == 0xffffffff, "ret %x\n", r );
+
+    r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( 700, 30 ));
+    ok( r == 0x1ffff || r == 0x10000 || r == 0xffffffff, "ret %x\n", r );
+
+    r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( 30, 300 ));
+    ok( r == 0x1ffff || r == 0x10000 || r == 0xffffffff, "ret %x\n", r );
+
+    id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi");
+    ok( id == 0, "item id wrong\n");
+    id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi1");
+    ok( id == 1, "item id wrong\n");
+
+    r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( /* x */ 30, /* y */ 30 ));
+    ok( r == 0x1, "ret %x\n", r );
+
+    r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( /* x */ 30, /* y */ 601 ));
+    ok( r == 0x10001, "ret %x\n", r );
+
+    /* Resize control so that below assertions about sizes are valid */
+    r = SendMessageA( hList, LB_GETITEMRECT, 0, (LPARAM)&rc);
+    ok( r == 1, "ret %x\n", r);
+    r = MoveWindow(hList, 1, 1, 600, (rc.bottom - rc.top + 1) * 9 / 2, TRUE);
+    ok( r != 0, "ret %x\n", r);
+
+    id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi2");
+    ok( id == 2, "item id wrong\n");
+    id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi3");
+    ok( id == 3, "item id wrong\n");
+    id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi4");
+    ok( id == 4, "item id wrong\n");
+    id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi5");
+    ok( id == 5, "item id wrong\n");
+    id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi6");
+    ok( id == 6, "item id wrong\n");
+    id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi7");
+    ok( id == 7, "item id wrong\n");
+
+    /* Set the listbox up so that id 1 is at the top, this leaves 5
+       partially visible at the bottom and 6, 7 are invisible */
+
+    SendMessageA( hList, LB_SETTOPINDEX, 1, 0);
+    r = SendMessageA( hList, LB_GETTOPINDEX, 0, 0);
+    ok( r == 1, "top %d\n", r);
+
+    r = SendMessageA( hList, LB_GETITEMRECT, 5, (LPARAM)&rc);
+    ok( r == 1, "ret %x\n", r);
+    r = SendMessageA( hList, LB_GETITEMRECT, 6, (LPARAM)&rc);
+    ok( r == 0, "ret %x\n", r);
+
+    r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(/* x */ 10, /* y */ 10) );
+    ok( r == 1, "ret %x\n", r);
+
+    r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(1000, 10) );
+    ok( r == 0x10001, "ret %x\n", r );
+
+    r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(10, -10) );
+    ok( r == 0x10001, "ret %x\n", r );
+
+    r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(10, 100) );
+    ok( r == 0x10005, "item %x\n", r );
+
+    r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(10, 200) );
+    ok( r == 0x10005, "item %x\n", r );
+
+    DestroyWindow( hList );
+}
+
+static void test_listbox_item_data(void)
+{
+    HWND hList;
+    int r, id;
+
+    hList = CreateWindowA( "ListBox", "list test", 0,
+                          1, 1, 600, 100, NULL, NULL, NULL, NULL );
+    ok( hList != NULL, "failed to create listbox\n");
+
+    id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi");
+    ok( id == 0, "item id wrong\n");
+
+    r = SendMessageA( hList, LB_SETITEMDATA, 0, MAKELPARAM( 20, 0 ));
+    ok(r == TRUE, "LB_SETITEMDATA returned %d instead of TRUE\n", r);
+
+    r = SendMessageA( hList, LB_GETITEMDATA, 0, 0);
+    ok( r == 20, "get item data failed\n");
+
+    DestroyWindow( hList );
+}
+
+static void test_listbox_LB_DIR(void)
+{
+    HWND hList;
+    int res, itemCount;
+    int itemCount_justFiles;
+    int itemCount_justDrives;
+    int itemCount_allFiles;
+    int itemCount_allDirs;
+    int i;
+    char pathBuffer[MAX_PATH];
+    char * p;
+    char driveletter;
+    const char *wildcard = "*";
+    HANDLE file;
+
+    file = CreateFileA( "wtest1.tmp.c", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
+    ok(file != INVALID_HANDLE_VALUE, "Error creating the test file: %d\n", GetLastError());
+    CloseHandle( file );
+
+    /* NOTE: for this test to succeed, there must be no subdirectories
+       under the current directory. In addition, there must be at least
+       one file that fits the wildcard w*.c . Normally, the test
+       directory itself satisfies both conditions.
+     */
+    hList = CreateWindowA( "ListBox", "list test", WS_VISIBLE|WS_POPUP,
+                          1, 1, 600, 100, NULL, NULL, NULL, NULL );
+    assert(hList);
+
+    /* Test for standard usage */
+
+    /* This should list all the files in the test directory. */
+    strcpy(pathBuffer, wildcard);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, 0, (LPARAM)pathBuffer);
+    if (res == -1)  /* "*" wildcard doesn't work on win9x */
+    {
+        wildcard = "*.*";
+        strcpy(pathBuffer, wildcard);
+        res = SendMessageA(hList, LB_DIR, 0, (LPARAM)pathBuffer);
+    }
+    ok (res >= 0, "SendMessage(LB_DIR, 0, *) failed - 0x%08x\n", GetLastError());
+
+    /* There should be some content in the listbox */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount > 0, "SendMessage(LB_DIR) did NOT fill the listbox!\n");
+    itemCount_allFiles = itemCount;
+    ok(res + 1 == itemCount,
+        "SendMessage(LB_DIR, 0, *) returned incorrect index (expected %d got %d)!\n",
+        itemCount - 1, res);
+
+    /* This tests behavior when no files match the wildcard */
+    strcpy(pathBuffer, BAD_EXTENSION);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, 0, (LPARAM)pathBuffer);
+    ok (res == -1, "SendMessage(LB_DIR, 0, %s) returned %d, expected -1\n", BAD_EXTENSION, res);
+
+    /* There should be NO content in the listbox */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == 0, "SendMessage(LB_DIR) DID fill the listbox!\n");
+
+
+    /* This should list all the w*.c files in the test directory
+     * As of this writing, this includes win.c, winstation.c, wsprintf.c
+     */
+    strcpy(pathBuffer, "w*.c");
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, 0, (LPARAM)pathBuffer);
+    ok (res >= 0, "SendMessage(LB_DIR, 0, w*.c) failed - 0x%08x\n", GetLastError());
+
+    /* Path specification does NOT converted to uppercase */
+    ok (!strcmp(pathBuffer, "w*.c"),
+        "expected no change to pathBuffer, got %s\n", pathBuffer);
+
+    /* There should be some content in the listbox */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount > 0, "SendMessage(LB_DIR) did NOT fill the listbox!\n");
+    itemCount_justFiles = itemCount;
+    ok(res + 1 == itemCount,
+        "SendMessage(LB_DIR, 0, w*.c) returned incorrect index (expected %d got %d)!\n",
+        itemCount - 1, res);
+
+    /* Every single item in the control should start with a w and end in .c */
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        p = pathBuffer + strlen(pathBuffer);
+        ok(((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
+            (*(p-1) == 'c' || *(p-1) == 'C') &&
+            (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
+    }
+
+    /* Test DDL_DIRECTORY */
+    strcpy(pathBuffer, wildcard);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY, (LPARAM)pathBuffer);
+    ok (res > 0, "SendMessage(LB_DIR, DDL_DIRECTORY, *) failed - 0x%08x\n", GetLastError());
+
+    /* There should be some content in the listbox.
+     * All files plus "[..]"
+     */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    itemCount_allDirs = itemCount - itemCount_allFiles;
+    ok (itemCount >= itemCount_allFiles,
+        "SendMessage(LB_DIR, DDL_DIRECTORY, *) filled with %d entries, expected > %d\n",
+        itemCount, itemCount_allFiles);
+    ok(res + 1 == itemCount,
+        "SendMessage(LB_DIR, DDL_DIRECTORY, *) returned incorrect index (expected %d got %d)!\n",
+        itemCount - 1, res);
+
+    /* This tests behavior when no files match the wildcard */
+    strcpy(pathBuffer, BAD_EXTENSION);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY, (LPARAM)pathBuffer);
+    ok (res == -1, "SendMessage(LB_DIR, DDL_DIRECTORY, %s) returned %d, expected -1\n", BAD_EXTENSION, res);
+
+    /* There should be NO content in the listbox */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == 0, "SendMessage(LB_DIR) DID fill the listbox!\n");
+
+    /* Test DDL_DIRECTORY */
+    strcpy(pathBuffer, "w*.c");
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY, (LPARAM)pathBuffer);
+    ok (res >= 0, "SendMessage(LB_DIR, DDL_DIRECTORY, w*.c) failed - 0x%08x\n", GetLastError());
+
+    /* There should be some content in the listbox. Since the parent directory does not
+     * fit w*.c, there should be exactly the same number of items as without DDL_DIRECTORY
+     */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_justFiles,
+        "SendMessage(LB_DIR, DDL_DIRECTORY, w*.c) filled with %d entries, expected %d\n",
+        itemCount, itemCount_justFiles);
+    ok(res + 1 == itemCount,
+        "SendMessage(LB_DIR, DDL_DIRECTORY, w*.c) returned incorrect index (expected %d got %d)!\n",
+        itemCount - 1, res);
+
+    /* Every single item in the control should start with a w and end in .c. */
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        p = pathBuffer + strlen(pathBuffer);
+        ok(
+            ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
+            (*(p-1) == 'c' || *(p-1) == 'C') &&
+            (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
+    }
+
+    /* Test DDL_DRIVES|DDL_EXCLUSIVE */
+    strcpy(pathBuffer, wildcard);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
+    ok (res >= 0, "SendMessage(LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, *)  failed - 0x%08x\n", GetLastError());
+
+    /* There should be some content in the listbox. In particular, there should
+     * be at least one element before, since the string "[-c-]" should
+     * have been added. Depending on the user setting, more drives might have
+     * been added.
+     */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount >= 1,
+        "SendMessage(LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, *) filled with %d entries, expected at least %d\n",
+        itemCount, 1);
+    itemCount_justDrives = itemCount;
+    ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, *) returned incorrect index!\n");
+
+    /* Every single item in the control should fit the format [-c-] */
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        driveletter = '\0';
+        SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        ok( strlen(pathBuffer) == 5, "Length of drive string is not 5\n" );
+        ok( sscanf(pathBuffer, "[-%c-]", &driveletter) == 1, "Element %d (%s) does not fit [-X-]\n", i, pathBuffer);
+        ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
+        if (!(driveletter >= 'a' && driveletter <= 'z'))
+        {
+            /* Correct after invalid entry is found */
+            itemCount_justDrives--;
+        }
+    }
+
+    /* This tests behavior when no files match the wildcard */
+    strcpy(pathBuffer, BAD_EXTENSION);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
+    ok (res == itemCount_justDrives -1, "SendMessage(LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, %s) returned %d, expected %d\n",
+        BAD_EXTENSION, res, itemCount_justDrives -1);
+
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_justDrives, "SendMessage(LB_DIR) returned %d expected %d\n",
+        itemCount, itemCount_justDrives);
+
+    /* Test DDL_DRIVES. */
+    strcpy(pathBuffer, wildcard);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DRIVES, (LPARAM)pathBuffer);
+    ok (res > 0, "SendMessage(LB_DIR, DDL_DRIVES, *)  failed - 0x%08x\n", GetLastError());
+
+    /* There should be some content in the listbox. In particular, there should
+     * be at least one element before, since the string "[-c-]" should
+     * have been added. Depending on the user setting, more drives might have
+     * been added.
+     */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_justDrives + itemCount_allFiles,
+        "SendMessage(LB_DIR, DDL_DRIVES, *) filled with %d entries, expected %d\n",
+        itemCount, itemCount_justDrives + itemCount_allFiles);
+    ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DRIVES, *) returned incorrect index!\n");
+
+    /* This tests behavior when no files match the wildcard */
+    strcpy(pathBuffer, BAD_EXTENSION);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DRIVES, (LPARAM)pathBuffer);
+    ok (res == itemCount_justDrives -1, "SendMessage(LB_DIR, DDL_DRIVES, %s) returned %d, expected %d\n",
+        BAD_EXTENSION, res, itemCount_justDrives -1);
+
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == res + 1, "SendMessage(LB_DIR) returned %d expected %d\n", itemCount, res + 1);
+
+    /* Test DDL_DRIVES. */
+    strcpy(pathBuffer, "w*.c");
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DRIVES, (LPARAM)pathBuffer);
+    ok (res > 0, "SendMessage(LB_DIR, DDL_DRIVES, w*.c)  failed - 0x%08x\n", GetLastError());
+
+    /* There should be some content in the listbox. In particular, there should
+     * be at least one element before, since the string "[-c-]" should
+     * have been added. Depending on the user setting, more drives might have
+     * been added.
+     */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_justDrives + itemCount_justFiles,
+        "SendMessage(LB_DIR, DDL_DRIVES, w*.c) filled with %d entries, expected %d\n",
+        itemCount, itemCount_justDrives + itemCount_justFiles);
+    ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DRIVES, w*.c) returned incorrect index!\n");
+
+    /* Every single item in the control should fit the format [-c-], or w*.c */
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        driveletter = '\0';
+        SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        p = pathBuffer + strlen(pathBuffer);
+        if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1)
+        {
+            ok( strlen(pathBuffer) == 5, "Length of drive string is not 5\n" );
+            ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
+        }
+        else
+        {
+            ok(
+                ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
+                (*(p-1) == 'c' || *(p-1) == 'C') &&
+                (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
+        }
+    }
+
+    /* Test DDL_DIRECTORY|DDL_DRIVES. This does *not* imply DDL_EXCLUSIVE */
+    strcpy(pathBuffer, wildcard);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES, (LPARAM)pathBuffer);
+    ok (res > 0, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, *) failed - 0x%08x\n", GetLastError());
+
+    /* There should be some content in the listbox. In particular, there should
+     * be exactly the number of plain files, plus the number of mapped drives.
+     */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_allFiles + itemCount_justDrives + itemCount_allDirs,
+        "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES) filled with %d entries, expected %d\n",
+        itemCount, itemCount_allFiles + itemCount_justDrives + itemCount_allDirs);
+    ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, w*.c) returned incorrect index!\n");
+
+    /* Every single item in the control should start with a w and end in .c,
+     * except for the "[..]" string, which should appear exactly as it is,
+     * and the mapped drives in the format "[-X-]".
+     */
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        driveletter = '\0';
+        SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1)
+            ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
+    }
+
+    /* This tests behavior when no files match the wildcard */
+    strcpy(pathBuffer, BAD_EXTENSION);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES, (LPARAM)pathBuffer);
+    ok (res == itemCount_justDrives -1, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, %s) returned %d, expected %d\n",
+        BAD_EXTENSION, res, itemCount_justDrives -1);
+
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == res + 1, "SendMessage(LB_DIR) returned %d expected %d\n", itemCount, res + 1);
+
+    /* Test DDL_DIRECTORY|DDL_DRIVES. */
+    strcpy(pathBuffer, "w*.c");
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES, (LPARAM)pathBuffer);
+    ok (res > 0, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, w*.c) failed - 0x%08x\n", GetLastError());
+
+    /* There should be some content in the listbox. In particular, there should
+     * be exactly the number of plain files, plus the number of mapped drives.
+     */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_justFiles + itemCount_justDrives,
+        "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES) filled with %d entries, expected %d\n",
+        itemCount, itemCount_justFiles + itemCount_justDrives);
+    ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, w*.c) returned incorrect index!\n");
+
+    /* Every single item in the control should start with a w and end in .c,
+     * except the mapped drives in the format "[-X-]". The "[..]" directory
+     * should not appear.
+     */
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        driveletter = '\0';
+        SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        p = pathBuffer + strlen(pathBuffer);
+        if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1)
+            ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
+        else
+            ok(
+                ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
+                (*(p-1) == 'c' || *(p-1) == 'C') &&
+                (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
+    }
+
+    /* Test DDL_DIRECTORY|DDL_EXCLUSIVE. */
+    strcpy(pathBuffer, wildcard);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
+    ok (res != -1 || broken(res == -1), "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, *) failed err %u\n",
+        GetLastError());
+
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_allDirs,
+        "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE) filled with %d entries, expected %d\n",
+        itemCount, itemCount_allDirs);
+    ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, *) returned incorrect index!\n");
+
+    if (itemCount && GetCurrentDirectoryA( MAX_PATH, pathBuffer ) > 3)  /* there's no [..] in drive root */
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        SendMessageA(hList, LB_GETTEXT, 0, (LPARAM)pathBuffer);
+        ok( !strcmp(pathBuffer, "[..]"), "First element is not [..]\n");
+    }
+
+    /* This tests behavior when no files match the wildcard */
+    strcpy(pathBuffer, BAD_EXTENSION);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
+    ok (res == -1, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, %s) returned %d, expected %d\n",
+        BAD_EXTENSION, res, -1);
+
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == res + 1, "SendMessage(LB_DIR) returned %d expected %d\n", itemCount, res + 1);
+
+
+    /* Test DDL_DIRECTORY|DDL_EXCLUSIVE. */
+    strcpy(pathBuffer, "w*.c");
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
+    ok (res == LB_ERR, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, w*.c) returned %d expected %d\n", res, LB_ERR);
+
+    /* There should be no elements, since "[..]" does not fit w*.c */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == 0,
+        "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE) filled with %d entries, expected %d\n",
+        itemCount, 0);
+
+    /* Test DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE. */
+    strcpy(pathBuffer, wildcard);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
+    ok (res > 0, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, w*.c,) failed - 0x%08x\n", GetLastError());
+
+    /* There should be no plain files on the listbox */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_justDrives + itemCount_allDirs,
+        "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE) filled with %d entries, expected %d\n",
+        itemCount, itemCount_justDrives + itemCount_allDirs);
+    ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, w*.c) returned incorrect index!\n");
+
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        driveletter = '\0';
+        SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1)
+            ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
+        else
+            ok( pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']',
+                "Element %d (%s) does not fit expected [...]\n", i, pathBuffer);
+    }
+
+    /* This tests behavior when no files match the wildcard */
+    strcpy(pathBuffer, BAD_EXTENSION);
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
+    ok (res == itemCount_justDrives -1, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, %s) returned %d, expected %d\n",
+        BAD_EXTENSION, res, itemCount_justDrives -1);
+
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == res + 1, "SendMessage(LB_DIR) returned %d expected %d\n", itemCount, res + 1);
+
+    /* Test DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE. */
+    strcpy(pathBuffer, "w*.c");
+    SendMessageA(hList, LB_RESETCONTENT, 0, 0);
+    res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer);
+    ok (res >= 0, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, w*.c,) failed - 0x%08x\n", GetLastError());
+
+    /* There should be no plain files on the listbox, and no [..], since it does not fit w*.c */
+    itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_justDrives,
+        "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE) filled with %d entries, expected %d\n",
+        itemCount, itemCount_justDrives);
+    ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, w*.c) returned incorrect index!\n");
+
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        driveletter = '\0';
+        SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        ok (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1, "Element %d (%s) does not fit [-X-]\n", i, pathBuffer);
+        ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
+    }
+    DestroyWindow(hList);
+
+    DeleteFileA( "wtest1.tmp.c" );
+}
+
+static HWND g_listBox;
+static HWND g_label;
+
+#define ID_TEST_LABEL    1001
+#define ID_TEST_LISTBOX  1002
+
+static BOOL on_listbox_container_create(HWND hwnd, CREATESTRUCTA *lpcs)
+{
+    g_label = CreateWindowA("Static", "Contents of static control before DlgDirList.",
+        WS_CHILD | WS_VISIBLE, 10, 10, 512, 32, hwnd, (HMENU)ID_TEST_LABEL, NULL, 0);
+    if (!g_label) return FALSE;
+
+    g_listBox = CreateWindowA("ListBox", "DlgDirList test",
+        WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_VSCROLL, 10, 60, 256, 256,
+        hwnd, (HMENU)ID_TEST_LISTBOX, NULL, 0);
+    if (!g_listBox) return FALSE;
+
+    return TRUE;
+}
+
+static LRESULT CALLBACK listbox_container_window_procA(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
+{
+    LRESULT result = 0;
+
+    switch (uiMsg)
+    {
+    case WM_DESTROY:
+        PostQuitMessage(0);
+        break;
+    case WM_CREATE:
+        result = on_listbox_container_create(hwnd, (CREATESTRUCTA *)lParam) ? 0 : (LRESULT)-1;
+        break;
+    default:
+        result = DefWindowProcA(hwnd, uiMsg, wParam, lParam);
+        break;
+    }
+    return result;
+}
+
+static BOOL RegisterListboxWindowClass(HINSTANCE hInst)
+{
+    WNDCLASSA cls;
+
+    cls.style = 0;
+    cls.cbClsExtra = 0;
+    cls.cbWndExtra = 0;
+    cls.hInstance = hInst;
+    cls.hIcon = NULL;
+    cls.hCursor = LoadCursorA (NULL, (LPCSTR)IDC_ARROW);
+    cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+    cls.lpszMenuName = NULL;
+    cls.lpfnWndProc = listbox_container_window_procA;
+    cls.lpszClassName = "ListboxContainerClass";
+    if (!RegisterClassA (&cls)) return FALSE;
+
+    return TRUE;
+}
+
+static void test_listbox_dlgdir(void)
+{
+    HINSTANCE hInst;
+    HWND hWnd;
+    int res, itemCount;
+    int itemCount_allDirs;
+    int itemCount_justFiles;
+    int itemCount_justDrives;
+    int i;
+    char pathBuffer[MAX_PATH];
+    char itemBuffer[MAX_PATH];
+    char tempBuffer[MAX_PATH];
+    char * p;
+    char driveletter;
+    HANDLE file;
+
+    file = CreateFileA( "wtest1.tmp.c", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
+    ok(file != INVALID_HANDLE_VALUE, "Error creating the test file: %d\n", GetLastError());
+    CloseHandle( file );
+
+    /* NOTE: for this test to succeed, there must be no subdirectories
+       under the current directory. In addition, there must be at least
+       one file that fits the wildcard w*.c . Normally, the test
+       directory itself satisfies both conditions.
+     */
+
+    hInst = GetModuleHandleA(0);
+    if (!RegisterListboxWindowClass(hInst)) assert(0);
+
+    hWnd = CreateWindowA("ListboxContainerClass", "ListboxContainerClass",
+                    WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+            NULL, NULL, hInst, 0);
+    ok(hWnd != NULL, "Failed to create container window.\n");
+
+    /* Test for standard usage */
+
+    /* The following should be overwritten by the directory path */
+    SendMessageA(g_label, WM_SETTEXT, 0, (LPARAM)"default contents");
+
+    /* This should list all the w*.c files in the test directory
+     * As of this writing, this includes win.c, winstation.c, wsprintf.c
+     */
+    strcpy(pathBuffer, "w*.c");
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, 0);
+    ok (res == 1, "DlgDirList(*.c, 0) returned %d - expected 1 - 0x%08x\n", res, GetLastError());
+
+    /* Path specification gets converted to uppercase */
+    ok (!strcmp(pathBuffer, "W*.C"),
+        "expected conversion to uppercase, got %s\n", pathBuffer);
+
+    /* Loaded path should have overwritten the label text */
+    SendMessageA(g_label, WM_GETTEXT, MAX_PATH, (LPARAM)pathBuffer);
+    ok (strcmp("default contents", pathBuffer), "DlgDirList() did not modify static control!\n");
+
+    /* There should be some content in the listbox */
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    ok (itemCount > 0, "DlgDirList() did NOT fill the listbox!\n");
+    itemCount_justFiles = itemCount;
+
+    /* Every single item in the control should start with a w and end in .c */
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        p = pathBuffer + strlen(pathBuffer);
+        ok(((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
+            (*(p-1) == 'c' || *(p-1) == 'C') &&
+            (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
+    }
+
+    /* Test behavior when no files match the wildcard */
+    strcpy(pathBuffer, BAD_EXTENSION);
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, 0);
+    ok (res == 1, "DlgDirList(%s, 0) returned %d expected 1\n", BAD_EXTENSION, res);
+
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    ok (itemCount == 0, "DlgDirList() DID fill the listbox!\n");
+
+    /* Test DDL_DIRECTORY */
+    strcpy(pathBuffer, "w*.c");
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY);
+    ok (res == 1, "DlgDirList(*.c, DDL_DIRECTORY) failed - 0x%08x\n", GetLastError());
+
+    /* There should be some content in the listbox. In particular, there should
+     * be exactly more elements than before, since the directories should
+     * have been added.
+     */
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    itemCount_allDirs = itemCount - itemCount_justFiles;
+    ok (itemCount >= itemCount_justFiles, "DlgDirList(DDL_DIRECTORY) filled with %d entries, expected > %d\n",
+        itemCount, itemCount_justFiles);
+
+    /* Every single item in the control should start with a w and end in .c,
+     * except for the "[..]" string, which should appear exactly as it is.
+     */
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        p = pathBuffer + strlen(pathBuffer);
+        ok( (pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']') ||
+            ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
+            (*(p-1) == 'c' || *(p-1) == 'C') &&
+            (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
+    }
+
+    /* Test behavior when no files match the wildcard */
+    strcpy(pathBuffer, BAD_EXTENSION);
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY);
+    ok (res == 1, "DlgDirList(%s, DDL_DIRECTORY) returned %d expected 1\n", BAD_EXTENSION, res);
+
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_allDirs, "DlgDirList() incorrectly filled the listbox! (expected %d got %d)\n",
+        itemCount_allDirs, itemCount);
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        ok( pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']',
+            "Element %d (%s) does not fit requested [...]\n", i, pathBuffer);
+    }
+
+    /* Test DDL_DRIVES. At least on WinXP-SP2, this implies DDL_EXCLUSIVE */
+    strcpy(pathBuffer, "w*.c");
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DRIVES);
+    ok (res == 1, "DlgDirList(*.c, DDL_DRIVES) failed - 0x%08x\n", GetLastError());
+
+    /* There should be some content in the listbox. In particular, there should
+     * be at least one element before, since the string "[-c-]" should
+     * have been added. Depending on the user setting, more drives might have
+     * been added.
+     */
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    ok (itemCount >= 1,
+        "DlgDirList(DDL_DRIVES) filled with %d entries, expected at least %d\n",
+        itemCount, 1);
+    itemCount_justDrives = itemCount;
+
+    /* Every single item in the control should fit the format [-c-] */
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        driveletter = '\0';
+        SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        ok( strlen(pathBuffer) == 5, "Length of drive string is not 5\n" );
+        ok( sscanf(pathBuffer, "[-%c-]", &driveletter) == 1, "Element %d (%s) does not fit [-X-]\n", i, pathBuffer);
+        ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
+        if (!(driveletter >= 'a' && driveletter <= 'z')) {
+            /* Correct after invalid entry is found */
+            trace("removing count of invalid entry %s\n", pathBuffer);
+            itemCount_justDrives--;
+        }
+    }
+
+    /* Test behavior when no files match the wildcard */
+    strcpy(pathBuffer, BAD_EXTENSION);
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DRIVES);
+    ok (res == 1, "DlgDirList(%s, DDL_DRIVES) returned %d expected 1\n", BAD_EXTENSION, res);
+
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_justDrives, "DlgDirList() incorrectly filled the listbox!\n");
+
+    /* Test DDL_DIRECTORY|DDL_DRIVES. This does *not* imply DDL_EXCLUSIVE */
+    strcpy(pathBuffer, "w*.c");
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES);
+    ok (res == 1, "DlgDirList(*.c, DDL_DIRECTORY|DDL_DRIVES) failed - 0x%08x\n", GetLastError());
+
+    /* There should be some content in the listbox. In particular, there should
+     * be exactly the number of plain files, plus the number of mapped drives,
+     * plus one "[..]"
+     */
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_justFiles + itemCount_justDrives + itemCount_allDirs,
+        "DlgDirList(DDL_DIRECTORY|DDL_DRIVES) filled with %d entries, expected %d\n",
+        itemCount, itemCount_justFiles + itemCount_justDrives + itemCount_allDirs);
+
+    /* Every single item in the control should start with a w and end in .c,
+     * except for the "[..]" string, which should appear exactly as it is,
+     * and the mapped drives in the format "[-X-]".
+     */
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        driveletter = '\0';
+        SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        p = pathBuffer + strlen(pathBuffer);
+        if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1)
+            ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
+        else
+            ok( (pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']') ||
+                ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') &&
+                (*(p-1) == 'c' || *(p-1) == 'C') &&
+                (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer);
+    }
+
+    /* Test behavior when no files match the wildcard */
+    strcpy(pathBuffer, BAD_EXTENSION);
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES);
+    ok (res == 1, "DlgDirList(%s, DDL_DIRECTORY|DDL_DRIVES) returned %d expected 1\n", BAD_EXTENSION, res);
+
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_justDrives + itemCount_allDirs,
+        "DlgDirList() incorrectly filled the listbox! (expected %d got %d)\n",
+        itemCount_justDrives + itemCount_allDirs, itemCount);
+
+    /* Test DDL_DIRECTORY|DDL_EXCLUSIVE. */
+    strcpy(pathBuffer, "w*.c");
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_EXCLUSIVE);
+    ok (res == 1, "DlgDirList(*.c, DDL_DIRECTORY|DDL_EXCLUSIVE) failed - 0x%08x\n", GetLastError());
+
+    /* There should be exactly one element: "[..]" */
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_allDirs,
+        "DlgDirList(DDL_DIRECTORY|DDL_EXCLUSIVE) filled with %d entries, expected %d\n",
+        itemCount, itemCount_allDirs);
+
+    if (itemCount && GetCurrentDirectoryA( MAX_PATH, pathBuffer ) > 3)  /* there's no [..] in drive root */
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        SendMessageA(g_listBox, LB_GETTEXT, 0, (LPARAM)pathBuffer);
+        ok( !strcmp(pathBuffer, "[..]"), "First (and only) element is not [..]\n");
+    }
+
+    /* Test behavior when no files match the wildcard */
+    strcpy(pathBuffer, BAD_EXTENSION);
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_EXCLUSIVE);
+    ok (res == 1, "DlgDirList(%s, DDL_DIRECTORY|DDL_EXCLUSIVE) returned %d expected 1\n", BAD_EXTENSION, res);
+
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_allDirs, "DlgDirList() incorrectly filled the listbox!\n");
+
+    /* Test DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE. */
+    strcpy(pathBuffer, "w*.c");
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE);
+    ok (res == 1, "DlgDirList(*.c, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE) failed - 0x%08x\n", GetLastError());
+
+    /* There should be no plain files on the listbox */
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_justDrives + itemCount_allDirs,
+        "DlgDirList(DDL_DIRECTORY|DDL_EXCLUSIVE) filled with %d entries, expected %d\n",
+        itemCount, itemCount_justDrives + itemCount_allDirs);
+
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(pathBuffer, 0, MAX_PATH);
+        driveletter = '\0';
+        SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer);
+        if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1)
+            ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter);
+        else
+            ok( pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']',
+                "Element %d (%s) does not fit expected [...]\n", i, pathBuffer);
+    }
+
+    /* Test behavior when no files match the wildcard */
+    strcpy(pathBuffer, BAD_EXTENSION);
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE);
+    ok (res == 1, "DlgDirList(%s, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE) returned %d expected 1\n", BAD_EXTENSION, res);
+
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    ok (itemCount == itemCount_justDrives + itemCount_allDirs, "DlgDirList() incorrectly filled the listbox!\n");
+
+    /* Now test DlgDirSelectEx() in normal operation */
+    /* Fill with everything - drives, directory and all plain files. */
+    strcpy(pathBuffer, "*");
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES);
+    ok (res != 0, "DlgDirList(*, DDL_DIRECTORY|DDL_DRIVES) failed - 0x%08x\n", GetLastError());
+
+    SendMessageA(g_listBox, LB_SETCURSEL, -1, 0); /* Unselect any current selection */
+    memset(pathBuffer, 0, MAX_PATH);
+    SetLastError(0xdeadbeef);
+    res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
+    ok (GetLastError() == 0xdeadbeef,
+        "DlgDirSelectEx() with no selection modified last error code from 0xdeadbeef to 0x%08x\n",
+        GetLastError());
+    ok (res == 0, "DlgDirSelectEx() with no selection returned %d, expected 0\n", res);
+    /* WinXP-SP2 leaves pathBuffer untouched, but Win98 fills it with garbage. */
+    /*
+    ok (strlen(pathBuffer) == 0, "DlgDirSelectEx() with no selection filled buffer with %s\n", pathBuffer);
+    */
+    /* Test proper drive/dir/file recognition */
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(itemBuffer, 0, MAX_PATH);
+        memset(pathBuffer, 0, MAX_PATH);
+        memset(tempBuffer, 0, MAX_PATH);
+        driveletter = '\0';
+        SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)itemBuffer);
+        res = SendMessageA(g_listBox, LB_SETCURSEL, i, 0);
+        ok (res == i, "SendMessageA(LB_SETCURSEL, %d) failed\n", i);
+        if (sscanf(itemBuffer, "[-%c-]", &driveletter) == 1)
+        {
+            /* Current item is a drive letter */
+            SetLastError(0xdeadbeef);
+            res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
+            ok (GetLastError() == 0xdeadbeef,
+               "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n",
+                i, GetLastError());
+            ok(res == 1, "DlgDirSelectEx() thinks %s (%s) is not a drive/directory!\n", itemBuffer, pathBuffer);
+
+            /* For drive letters, DlgDirSelectEx tacks on a colon */
+            ok (pathBuffer[0] == driveletter && pathBuffer[1] == ':' && pathBuffer[2] == '\0',
+                "%d: got \"%s\" expected \"%c:\"\n", i, pathBuffer, driveletter);
+        }
+        else if (itemBuffer[0] == '[')
+        {
+            /* Current item is the parent directory */
+            SetLastError(0xdeadbeef);
+            res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
+            ok (GetLastError() == 0xdeadbeef,
+               "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n",
+                i, GetLastError());
+            ok(res == 1, "DlgDirSelectEx() thinks %s (%s) is not a drive/directory!\n", itemBuffer, pathBuffer);
+
+            /* For directories, DlgDirSelectEx tacks on a backslash */
+            p = pathBuffer + strlen(pathBuffer);
+            ok (*(p-1) == '\\', "DlgDirSelectEx did NOT tack on a backslash to dir, got %s\n", pathBuffer);
+
+            tempBuffer[0] = '[';
+            lstrcpynA(tempBuffer + 1, pathBuffer, strlen(pathBuffer));
+            strcat(tempBuffer, "]");
+            ok (!strcmp(tempBuffer, itemBuffer), "Formatted directory should be %s, got %s\n", tempBuffer, itemBuffer);
+        }
+        else
+        {
+            /* Current item is a plain file */
+            SetLastError(0xdeadbeef);
+            res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
+            ok (GetLastError() == 0xdeadbeef,
+               "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n",
+                i, GetLastError());
+            ok(res == 0, "DlgDirSelectEx() thinks %s (%s) is a drive/directory!\n", itemBuffer, pathBuffer);
+
+            /* NOTE: WinXP tacks a period on all files that lack an extension. This affects
+             * for example, "Makefile", which gets reported as "Makefile."
+             */
+            strcpy(tempBuffer, itemBuffer);
+            if (strchr(tempBuffer, '.') == NULL) strcat(tempBuffer, ".");
+            ok (!strcmp(pathBuffer, tempBuffer), "Formatted file should be %s, got %s\n", tempBuffer, pathBuffer);
+        }
+    }
+
+    DeleteFileA( "wtest1.tmp.c" );
+
+    /* Now test DlgDirSelectEx() in abnormal operation */
+    /* Fill list with bogus entries, that look somewhat valid */
+    SendMessageA(g_listBox, LB_RESETCONTENT, 0, 0);
+    SendMessageA(g_listBox, LB_ADDSTRING, 0, (LPARAM)"[notexist.dir]");
+    SendMessageA(g_listBox, LB_ADDSTRING, 0, (LPARAM)"notexist.fil");
+    itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);
+    for (i = 0; i < itemCount; i++)
+    {
+        memset(itemBuffer, 0, MAX_PATH);
+        memset(pathBuffer, 0, MAX_PATH);
+        memset(tempBuffer, 0, MAX_PATH);
+        driveletter = '\0';
+        SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)itemBuffer);
+        res = SendMessageA(g_listBox, LB_SETCURSEL, i, 0);
+        ok (res == i, "SendMessage(LB_SETCURSEL, %d) failed\n", i);
+        if (sscanf(itemBuffer, "[-%c-]", &driveletter) == 1)
+        {
+            /* Current item is a drive letter */
+            SetLastError(0xdeadbeef);
+            res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
+            ok (GetLastError() == 0xdeadbeef,
+               "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n",
+                i, GetLastError());
+            ok(res == 1, "DlgDirSelectEx() thinks %s (%s) is not a drive/directory!\n", itemBuffer, pathBuffer);
+
+            /* For drive letters, DlgDirSelectEx tacks on a colon */
+            ok (pathBuffer[0] == driveletter && pathBuffer[1] == ':' && pathBuffer[2] == '\0',
+                "%d: got \"%s\" expected \"%c:\"\n", i, pathBuffer, driveletter);
+        }
+        else if (itemBuffer[0] == '[')
+        {
+            /* Current item is the parent directory */
+            SetLastError(0xdeadbeef);
+            res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
+            ok (GetLastError() == 0xdeadbeef,
+               "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n",
+                i, GetLastError());
+            ok(res == 1, "DlgDirSelectEx() thinks %s (%s) is not a drive/directory!\n", itemBuffer, pathBuffer);
+
+            /* For directories, DlgDirSelectEx tacks on a backslash */
+            p = pathBuffer + strlen(pathBuffer);
+            ok (*(p-1) == '\\', "DlgDirSelectEx did NOT tack on a backslash to dir, got %s\n", pathBuffer);
+
+            tempBuffer[0] = '[';
+            lstrcpynA(tempBuffer + 1, pathBuffer, strlen(pathBuffer));
+            strcat(tempBuffer, "]");
+            ok (!strcmp(tempBuffer, itemBuffer), "Formatted directory should be %s, got %s\n", tempBuffer, itemBuffer);
+        }
+        else
+        {
+            /* Current item is a plain file */
+            SetLastError(0xdeadbeef);
+            res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX);
+            ok (GetLastError() == 0xdeadbeef,
+               "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n",
+                i, GetLastError());
+            ok(res == 0, "DlgDirSelectEx() thinks %s (%s) is a drive/directory!\n", itemBuffer, pathBuffer);
+
+            /* NOTE: WinXP and Win98 tack a period on all files that lack an extension.
+             * This affects for example, "Makefile", which gets reported as "Makefile."
+             */
+            strcpy(tempBuffer, itemBuffer);
+            if (strchr(tempBuffer, '.') == NULL) strcat(tempBuffer, ".");
+            ok (!strcmp(pathBuffer, tempBuffer), "Formatted file should be %s, got %s\n", tempBuffer, pathBuffer);
+        }
+    }
+
+    /* Test behavior when loading folders from root with and without wildcard */
+    strcpy(pathBuffer, "C:\\");
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE);
+    ok(res, "DlgDirList failed to list C:\\ folders\n");
+    todo_wine ok(!strcmp(pathBuffer, "*"), "DlgDirList set the invalid path spec '%s', expected '*'\n", pathBuffer);
+
+    strcpy(pathBuffer, "C:\\*");
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE);
+    ok(res, "DlgDirList failed to list C:\\* folders\n");
+    ok(!strcmp(pathBuffer, "*"), "DlgDirList set the invalid path spec '%s', expected '*'\n", pathBuffer);
+
+    /* Try loading files from an invalid folder */
+    SetLastError(0xdeadbeef);
+    strcpy(pathBuffer, "C:\\INVALID$$DIR");
+    res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE);
+    todo_wine ok(!res, "DlgDirList should have failed with 0 but %d was returned\n", res);
+    todo_wine ok(GetLastError() == ERROR_NO_WILDCARD_CHARACTERS,
+       "GetLastError should return 0x589, got 0x%X\n",GetLastError());
+
+    DestroyWindow(hWnd);
+}
+
+static void test_set_count( void )
+{
+    HWND parent, listbox;
+    LONG ret;
+    RECT r;
+
+    parent = create_parent();
+    listbox = create_listbox( LBS_OWNERDRAWFIXED | LBS_NODATA | WS_CHILD | WS_VISIBLE, parent );
+
+    UpdateWindow( listbox );
+    GetUpdateRect( listbox, &r, TRUE );
+    ok( IsRectEmpty( &r ), "got non-empty rect\n");
+
+    ret = SendMessageA( listbox, LB_SETCOUNT, 100, 0 );
+    ok( ret == 0, "got %d\n", ret );
+    ret = SendMessageA( listbox, LB_GETCOUNT, 0, 0 );
+    ok( ret == 100, "got %d\n", ret );
+
+    GetUpdateRect( listbox, &r, TRUE );
+    ok( !IsRectEmpty( &r ), "got empty rect\n");
+
+    ValidateRect( listbox, NULL );
+    GetUpdateRect( listbox, &r, TRUE );
+    ok( IsRectEmpty( &r ), "got non-empty rect\n");
+
+    ret = SendMessageA( listbox, LB_SETCOUNT, 99, 0 );
+    ok( ret == 0, "got %d\n", ret );
+
+    GetUpdateRect( listbox, &r, TRUE );
+    ok( !IsRectEmpty( &r ), "got empty rect\n");
+
+    DestroyWindow( listbox );
+    DestroyWindow( parent );
+}
+
+static int lb_getlistboxinfo;
+
+static LRESULT WINAPI listbox_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
+
+    if (message == LB_GETLISTBOXINFO)
+        lb_getlistboxinfo++;
+
+    return CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
+}
+
+static void test_GetListBoxInfo(void)
+{
+    HWND listbox, parent;
+    WNDPROC oldproc;
+    DWORD ret;
+
+    parent = create_parent();
+    listbox = create_listbox(WS_CHILD | WS_VISIBLE, parent);
+
+    oldproc = (WNDPROC)SetWindowLongPtrA(listbox, GWLP_WNDPROC, (LONG_PTR)listbox_subclass_proc);
+    SetWindowLongPtrA(listbox, GWLP_USERDATA, (LONG_PTR)oldproc);
+
+    lb_getlistboxinfo = 0;
+    ret = GetListBoxInfo(listbox);
+    ok(ret > 0, "got %d\n", ret);
+    ok(lb_getlistboxinfo == 1, "got %d\n", lb_getlistboxinfo);
+
+    DestroyWindow(listbox);
+    DestroyWindow(parent);
+}
+
+static void test_missing_lbuttonup(void)
+{
+    HWND listbox, parent, capture;
+
+    parent = create_parent();
+    listbox = create_listbox(WS_CHILD | WS_VISIBLE, parent);
+
+    /* Send button down without a corresponding button up */
+    SendMessageA(listbox, WM_LBUTTONDOWN, 0, MAKELPARAM(10, 10));
+    capture = GetCapture();
+    ok(capture == listbox, "got %p expected %p\n", capture, listbox);
+
+    /* Capture is released and LBN_SELCHANGE sent during WM_KILLFOCUS */
+    got_selchange = 0;
+    SetFocus(NULL);
+    capture = GetCapture();
+    ok(capture == NULL, "got %p\n", capture);
+    ok(got_selchange, "got %d\n", got_selchange);
+
+    DestroyWindow(listbox);
+    DestroyWindow(parent);
+}
+
+static void test_extents(void)
+{
+    HWND listbox, parent;
+    SCROLLINFO sinfo;
+    DWORD res;
+    BOOL br;
+
+    parent = create_parent();
+
+    listbox = create_listbox(WS_CHILD | WS_VISIBLE, parent);
+
+    res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
+    ok(res == 0, "Got wrong initial horizontal extent: %u\n", res);
+
+    sinfo.cbSize = sizeof(sinfo);
+    sinfo.fMask = SIF_RANGE;
+    br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
+    ok(br == TRUE, "GetScrollInfo failed\n");
+    ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
+    ok(sinfo.nMax == 100, "got wrong max: %u\n", sinfo.nMax);
+    ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0,
+        "List box should not have a horizontal scroll bar\n");
+
+    /* horizontal extent < width */
+    SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 64, 0);
+
+    res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
+    ok(res == 64, "Got wrong horizontal extent: %u\n", res);
+
+    sinfo.cbSize = sizeof(sinfo);
+    sinfo.fMask = SIF_RANGE;
+    br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
+    ok(br == TRUE, "GetScrollInfo failed\n");
+    ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
+    ok(sinfo.nMax == 100, "got wrong max: %u\n", sinfo.nMax);
+    ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0,
+        "List box should not have a horizontal scroll bar\n");
+
+    /* horizontal extent > width */
+    SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 184, 0);
+
+    res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
+    ok(res == 184, "Got wrong horizontal extent: %u\n", res);
+
+    sinfo.cbSize = sizeof(sinfo);
+    sinfo.fMask = SIF_RANGE;
+    br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
+    ok(br == TRUE, "GetScrollInfo failed\n");
+    ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
+    ok(sinfo.nMax == 100, "got wrong max: %u\n", sinfo.nMax);
+    ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0,
+        "List box should not have a horizontal scroll bar\n");
+
+    DestroyWindow(listbox);
+
+    listbox = create_listbox(WS_CHILD | WS_VISIBLE | WS_HSCROLL, parent);
+
+    res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
+    ok(res == 0, "Got wrong initial horizontal extent: %u\n", res);
+
+    sinfo.cbSize = sizeof(sinfo);
+    sinfo.fMask = SIF_RANGE;
+    br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
+    ok(br == TRUE, "GetScrollInfo failed\n");
+    ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
+    ok(sinfo.nMax == 100, "got wrong max: %u\n", sinfo.nMax);
+    ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0,
+        "List box should not have a horizontal scroll bar\n");
+
+    /* horizontal extent < width */
+    SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 64, 0);
+
+    res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
+    ok(res == 64, "Got wrong horizontal extent: %u\n", res);
+
+    sinfo.cbSize = sizeof(sinfo);
+    sinfo.fMask = SIF_RANGE;
+    br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
+    ok(br == TRUE, "GetScrollInfo failed\n");
+    ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
+    ok(sinfo.nMax == 63, "got wrong max: %u\n", sinfo.nMax);
+    ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0,
+        "List box should not have a horizontal scroll bar\n");
+
+    /* horizontal extent > width */
+    SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 184, 0);
+
+    res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
+    ok(res == 184, "Got wrong horizontal extent: %u\n", res);
+
+    sinfo.cbSize = sizeof(sinfo);
+    sinfo.fMask = SIF_RANGE;
+    br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
+    ok(br == TRUE, "GetScrollInfo failed\n");
+    ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
+    ok(sinfo.nMax == 183, "got wrong max: %u\n", sinfo.nMax);
+    ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0,
+        "List box should have a horizontal scroll bar\n");
+
+    SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 0, 0);
+
+    res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
+    ok(res == 0, "Got wrong horizontal extent: %u\n", res);
+
+    sinfo.cbSize = sizeof(sinfo);
+    sinfo.fMask = SIF_RANGE;
+    br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
+    ok(br == TRUE, "GetScrollInfo failed\n");
+    ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
+    ok(sinfo.nMax == 0, "got wrong max: %u\n", sinfo.nMax);
+    ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0,
+        "List box should not have a horizontal scroll bar\n");
+
+    DestroyWindow(listbox);
+
+
+    listbox = create_listbox(WS_CHILD | WS_VISIBLE | WS_HSCROLL | LBS_DISABLENOSCROLL, parent);
+
+    res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
+    ok(res == 0, "Got wrong initial horizontal extent: %u\n", res);
+
+    sinfo.cbSize = sizeof(sinfo);
+    sinfo.fMask = SIF_RANGE;
+    br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
+    ok(br == TRUE, "GetScrollInfo failed\n");
+    ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
+    ok(sinfo.nMax == 0, "got wrong max: %u\n", sinfo.nMax);
+    ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0,
+        "List box should have a horizontal scroll bar\n");
+
+    /* horizontal extent < width */
+    SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 64, 0);
+
+    res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
+    ok(res == 64, "Got wrong horizontal extent: %u\n", res);
+
+    sinfo.cbSize = sizeof(sinfo);
+    sinfo.fMask = SIF_RANGE;
+    br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
+    ok(br == TRUE, "GetScrollInfo failed\n");
+    ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
+    ok(sinfo.nMax == 63, "got wrong max: %u\n", sinfo.nMax);
+    ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0,
+        "List box should have a horizontal scroll bar\n");
+
+    /* horizontal extent > width */
+    SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 184, 0);
+
+    res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
+    ok(res == 184, "Got wrong horizontal extent: %u\n", res);
+
+    sinfo.cbSize = sizeof(sinfo);
+    sinfo.fMask = SIF_RANGE;
+    br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
+    ok(br == TRUE, "GetScrollInfo failed\n");
+    ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
+    ok(sinfo.nMax == 183, "got wrong max: %u\n", sinfo.nMax);
+    ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0,
+        "List box should have a horizontal scroll bar\n");
+
+    SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 0, 0);
+
+    res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0);
+    ok(res == 0, "Got wrong horizontal extent: %u\n", res);
+
+    sinfo.cbSize = sizeof(sinfo);
+    sinfo.fMask = SIF_RANGE;
+    br = GetScrollInfo(listbox, SB_HORZ, &sinfo);
+    ok(br == TRUE, "GetScrollInfo failed\n");
+    ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin);
+    ok(sinfo.nMax == 0, "got wrong max: %u\n", sinfo.nMax);
+    ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0,
+        "List box should have a horizontal scroll bar\n");
+
+    DestroyWindow(listbox);
+
+    DestroyWindow(parent);
+}
+
+static void test_listbox(void)
+{
+    static const struct listbox_test SS =
+        /*   {add_style} */
+        {{0},
+         {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+         {     1,      1,      1, LB_ERR}, {0,0,0,0},
+         {     2,      2,      2, LB_ERR}, {0,0,0,0},
+         {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
+
+        /* {selected, anchor,  caret, selcount}{TODO fields} */
+    static const struct listbox_test SS_NS =
+        {{LBS_NOSEL},
+         {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+         {     1,      1,      1, LB_ERR}, {0,0,0,0},
+         {     2,      2,      2, LB_ERR}, {0,0,0,0},
+         {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
+
+    static const struct listbox_test MS =
+        {{LBS_MULTIPLESEL},
+         {     0, LB_ERR,      0,      0}, {0,0,0,0},
+         {     1,      1,      1,      1}, {0,0,0,0},
+         {     2,      1,      2,      1}, {0,0,0,0},
+         {     0, LB_ERR,      0,      2}, {0,0,0,0}};
+
+    static const struct listbox_test MS_NS =
+        {{LBS_MULTIPLESEL | LBS_NOSEL},
+         {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+         {     1,      1,      1, LB_ERR}, {0,0,0,0},
+         {     2,      2,      2, LB_ERR}, {0,0,0,0},
+         {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
+
+    static const struct listbox_test ES =
+        {{LBS_EXTENDEDSEL},
+         {     0, LB_ERR,      0,      0}, {0,0,0,0},
+         {     1,      1,      1,      1}, {0,0,0,0},
+         {     2,      2,      2,      1}, {0,0,0,0},
+         {     0, LB_ERR,      0,      2}, {0,0,0,0}};
+
+    static const struct listbox_test ES_NS =
+        {{LBS_EXTENDEDSEL | LBS_NOSEL},
+         {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+         {     1,      1,      1, LB_ERR}, {0,0,0,0},
+         {     2,      2,      2, LB_ERR}, {0,0,0,0},
+         {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
+
+    static const struct listbox_test EMS =
+        {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL},
+         {     0, LB_ERR,      0,      0}, {0,0,0,0},
+         {     1,      1,      1,      1}, {0,0,0,0},
+         {     2,      2,      2,      1}, {0,0,0,0},
+         {     0, LB_ERR,      0,      2}, {0,0,0,0}};
+
+    static const struct listbox_test EMS_NS =
+        {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL},
+         {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+         {     1,      1,      1, LB_ERR}, {0,0,0,0},
+         {     2,      2,      2, LB_ERR}, {0,0,0,0},
+         {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
+
+    run_test(SS);
+    run_test(SS_NS);
+    run_test(MS);
+    run_test(MS_NS);
+    run_test(ES);
+    run_test(ES_NS);
+    run_test(EMS);
+    run_test(EMS_NS);
+}
+
+START_TEST(listbox)
+{
+    ULONG_PTR ctx_cookie;
+    HANDLE hCtx;
+
+    if (!load_v6_module(&ctx_cookie, &hCtx))
+        return;
+
+    test_listbox();
+    test_item_height();
+    test_ownerdraw();
+    test_LB_SELITEMRANGE();
+    test_LB_SETCURSEL();
+    test_listbox_height();
+    test_itemfrompoint();
+    test_listbox_item_data();
+    test_listbox_LB_DIR();
+    test_listbox_dlgdir();
+    test_set_count();
+    test_GetListBoxInfo();
+    test_missing_lbuttonup();
+    test_extents();
+
+    unload_v6_module(ctx_cookie, hCtx);
+}
index 2210fb3..90fce74 100644 (file)
@@ -187,6 +187,12 @@ static const struct message empty_seq[] = {
     { 0 }
 };
 
+static const struct message parent_focus_change_ownerdata_seq[] = {
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+    { WM_NOTIFY, sent|id, 0, 0, LVN_GETDISPINFOA },
+    { 0 }
+};
+
 static const struct message forward_erasebkgnd_parent_seq[] = {
     { WM_ERASEBKGND, sent },
     { 0 }
@@ -628,14 +634,6 @@ static LRESULT WINAPI listview_subclass_proc(HWND hwnd, UINT message, WPARAM wPa
     LRESULT ret;
     struct message msg;
 
-    /* some debug output for style changing */
-    if ((message == WM_STYLECHANGING ||
-         message == WM_STYLECHANGED) && lParam)
-    {
-        STYLESTRUCT *style = (STYLESTRUCT*)lParam;
-        trace("\told style: 0x%08x, new style: 0x%08x\n", style->styleOld, style->styleNew);
-    }
-
     msg.message = message;
     msg.flags = sent|wparam|lparam;
     if (defwndproc_counter) msg.flags |= defwinproc;
@@ -1517,6 +1515,8 @@ static LRESULT CALLBACK create_test_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam,
 
 static void test_create(void)
 {
+    static const WCHAR testtextW[] = {'t','e','s','t',' ','t','e','x','t',0};
+    char buff[16];
     HWND hList;
     HWND hHeader;
     LONG_PTR ret;
@@ -1537,15 +1537,6 @@ static void test_create(void)
     hList = CreateWindowA("MyListView32", "Test", WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, GetModuleHandleA(NULL), 0);
     ok((HIMAGELIST)SendMessageA(hList, LVM_GETIMAGELIST, 0, 0) == test_create_imagelist, "Image list not obtained\n");
     hHeader = (HWND)SendMessageA(hList, LVM_GETHEADER, 0, 0);
-
-    if (!IsWindow(hHeader))
-    {
-        /* version 4.0 */
-        win_skip("LVM_GETHEADER not implemented. Skipping.\n");
-        DestroyWindow(hList);
-        return;
-    }
-
     ok(IsWindow(hHeader) && IsWindowVisible(hHeader), "Listview not in report mode\n");
     ok(hHeader == GetDlgItem(hList, 0), "Expected header as dialog item\n");
     DestroyWindow(hList);
@@ -1728,6 +1719,23 @@ static void test_create(void)
     ok_sequence(sequences, PARENT_SEQ_INDEX, create_ownerdrawfixed_parent_seq,
                 "created with LVS_OWNERDRAWFIXED|LVS_REPORT - parent seq", FALSE);
     DestroyWindow(hList);
+
+    /* Test that window text is preserved. */
+    hList = CreateWindowExA(0, WC_LISTVIEWA, "test text", WS_CHILD | WS_BORDER | WS_VISIBLE,
+        0, 0, 100, 100, hwndparent, NULL, GetModuleHandleA(NULL), NULL);
+    ok(hList != NULL, "Failed to create ListView window.\n");
+    *buff = 0;
+    GetWindowTextA(hList, buff, sizeof(buff));
+    ok(!strcmp(buff, "test text"), "Unexpected window text %s.\n", buff);
+    DestroyWindow(hList);
+
+    hList = CreateWindowExW(0, WC_LISTVIEWW, testtextW, WS_CHILD | WS_BORDER | WS_VISIBLE,
+        0, 0, 100, 100, hwndparent, NULL, GetModuleHandleA(NULL), NULL);
+    ok(hList != NULL, "Failed to create ListView window.\n");
+    *buff = 0;
+    GetWindowTextA(hList, buff, sizeof(buff));
+    ok(!strcmp(buff, "test text"), "Unexpected window text %s.\n", buff);
+    DestroyWindow(hList);
 }
 
 static void test_redraw(void)
@@ -1826,16 +1834,12 @@ static LRESULT WINAPI cd_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM
                 clr = GetBkColor(nmlvcd->nmcd.hdc);
                 ok(nmlvcd->clrTextBk == CLR_DEFAULT, "got 0x%x\n", nmlvcd->clrTextBk);
                 ok(nmlvcd->clrText == RGB(0, 255, 0), "got 0x%x\n", nmlvcd->clrText);
-                if (!(GetWindowLongW(nmhdr->hwndFrom, GWL_STYLE) & LVS_SHOWSELALWAYS))
-                {
-                    todo_wine_if(nmlvcd->iSubItem)
-                        ok(clr == c0ffee, "clr=%.8x\n", clr);
-                }
+                todo_wine_if(nmlvcd->iSubItem)
+                    ok(clr == c0ffee, "clr=%.8x\n", clr);
                 return CDRF_NOTIFYPOSTPAINT;
             case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM:
                 clr = GetBkColor(nmlvcd->nmcd.hdc);
-                if (!(GetWindowLongW(nmhdr->hwndFrom, GWL_STYLE) & LVS_SHOWSELALWAYS))
-                    todo_wine ok(clr == c0ffee, "clr=%.8x\n", clr);
+                todo_wine ok(clr == c0ffee, "clr=%.8x\n", clr);
                 ok(nmlvcd->clrTextBk == CLR_DEFAULT, "got 0x%x\n", nmlvcd->clrTextBk);
                 ok(nmlvcd->clrText == RGB(0, 255, 0), "got 0x%x\n", nmlvcd->clrText);
                 return CDRF_DODEFAULT;
@@ -1851,7 +1855,6 @@ static void test_customdraw(void)
 {
     HWND hwnd;
     WNDPROC oldwndproc;
-    LVITEMA item;
 
     hwnd = create_listview_control(LVS_REPORT);
 
@@ -1871,18 +1874,6 @@ static void test_customdraw(void)
     UpdateWindow(hwnd);
     ok_sequence(sequences, PARENT_CD_SEQ_INDEX, parent_report_cd_seq, "parent customdraw, LVS_REPORT", FALSE);
 
-    /* check colors when item is selected */
-    SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) | LVS_SHOWSELALWAYS);
-    item.mask = LVIF_STATE;
-    item.stateMask = LVIS_SELECTED;
-    item.state = LVIS_SELECTED;
-    SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item);
-
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
-    InvalidateRect(hwnd, NULL, TRUE);
-    UpdateWindow(hwnd);
-    ok_sequence(sequences, PARENT_CD_SEQ_INDEX, parent_report_cd_seq, "parent customdraw, LVS_REPORT, selection", FALSE);
-
     DestroyWindow(hwnd);
 
     hwnd = create_listview_control(LVS_LIST);
@@ -1931,13 +1922,6 @@ static void test_icon_spacing(void)
        "Expected %d, got %d\n", MAKELONG(w, h), r);
 
     r = SendMessageA(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(25, 35));
-    if (r == 0)
-    {
-        /* version 4.0 */
-        win_skip("LVM_SETICONSPACING unimplemented. Skipping.\n");
-        DestroyWindow(hwnd);
-        return;
-    }
     expect(MAKELONG(20,30), r);
 
     r = SendMessageA(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(-1,-1));
@@ -4386,19 +4370,11 @@ static void test_notifyformat(void)
     r = SendMessageA(hwnd, LVM_SETUNICODEFORMAT, 1, 0);
     expect(0, r);
     r = SendMessageA(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
-    if (r == 1)
-    {
-        r = SendMessageA(hwnd, LVM_SETUNICODEFORMAT, 0, 0);
-        expect(1, r);
-        r = SendMessageA(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
-        expect(0, r);
-    }
-    else
-    {
-        win_skip("LVM_GETUNICODEFORMAT is unsupported\n");
-        DestroyWindow(hwnd);
-        return;
-    }
+    ok(r == 1, "Unexpected return value %d.\n", r);
+    r = SendMessageA(hwnd, LVM_SETUNICODEFORMAT, 0, 0);
+    expect(1, r);
+    r = SendMessageA(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
+    expect(0, r);
 
     DestroyWindow(hwnd);
 
@@ -4978,13 +4954,18 @@ static void test_LVS_EX_TRANSPARENTBKGND(void)
 
 static void test_approximate_viewrect(void)
 {
-    HWND hwnd;
-    DWORD ret;
+    static CHAR test[] = "abracadabra, a very long item label";
+    DWORD item_width, item_height, header_height;
+    static CHAR column_header[] = "Header";
+    unsigned const column_width = 100;
+    DWORD ret, item_count;
     HIMAGELIST himl;
-    HBITMAP hbmp;
     LVITEMA itema;
-    static CHAR test[] = "abracadabra, a very long item label";
+    LVCOLUMNA col;
+    HBITMAP hbmp;
+    HWND hwnd;
 
+    /* LVS_ICON */
     hwnd = create_listview_control(LVS_ICON);
     himl = ImageList_Create(40, 40, 0, 4, 4);
     ok(himl != NULL, "failed to create imagelist\n");
@@ -5003,12 +4984,7 @@ static void test_approximate_viewrect(void)
     expect(0, ret);
 
     ret = SendMessageA(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(75, 75));
-    if (ret == 0)
-    {
-        /* version 4.0 */
-        win_skip("LVM_SETICONSPACING unimplemented. Skipping.\n");
-        return;
-    }
+    ok(ret != 0, "Unexpected return value %#x.\n", ret);
 
     ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 11, MAKELPARAM(100,100));
     expect(MAKELONG(77,827), ret);
@@ -5046,6 +5022,129 @@ static void test_approximate_viewrect(void)
     expect(MAKELONG(152,152), ret);
 
     DestroyWindow(hwnd);
+
+    /* LVS_REPORT */
+    hwnd = create_listview_control(LVS_REPORT);
+
+    /* Empty control without columns */
+    ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 0, MAKELPARAM(100, 100));
+todo_wine
+    ok(LOWORD(ret) == 0, "Unexpected width %d.\n", LOWORD(ret));
+    ok(HIWORD(ret) != 0, "Unexpected height %d.\n", HIWORD(ret));
+
+    ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 0, 0);
+    ok(LOWORD(ret) == 0, "Unexpected width %d.\n", LOWORD(ret));
+todo_wine
+    ok(HIWORD(ret) != 0, "Unexpected height %d.\n", HIWORD(ret));
+
+    header_height = HIWORD(ret);
+
+    ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 1, 0);
+    ok(LOWORD(ret) == 0, "Unexpected width %d.\n", LOWORD(ret));
+todo_wine
+    ok(HIWORD(ret) > header_height, "Unexpected height %d.\n", HIWORD(ret));
+
+    item_height = HIWORD(ret) - header_height;
+
+    ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -2, 0);
+    ok(LOWORD(ret) == 0, "Unexpected width %d.\n", LOWORD(ret));
+    ok(HIWORD(ret) == (header_height - 2 * item_height), "Unexpected height %d.\n", HIWORD(ret)) ;
+
+    ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -1, 0);
+    ok(LOWORD(ret) == 0, "Unexpected width %d.\n", LOWORD(ret));
+    ok(HIWORD(ret) == header_height, "Unexpected height.\n");
+    ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 2, 0);
+    ok(LOWORD(ret) == 0, "Unexpected width %d.\n", LOWORD(ret));
+    ok(HIWORD(ret) == header_height + 2 * item_height, "Unexpected height %d.\n", HIWORD(ret));
+
+    /* Insert column */
+    col.mask = LVCF_TEXT | LVCF_WIDTH;
+    col.pszText = column_header;
+    col.cx = column_width;
+    ret = SendMessageA(hwnd, LVM_INSERTCOLUMNA, 0, (LPARAM)&col);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+
+    /* Empty control with column */
+    ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 0, 0);
+todo_wine {
+    ok(LOWORD(ret) >= column_width, "Unexpected width %d.\n", LOWORD(ret));
+    ok(HIWORD(ret) != 0, "Unexpected height %d.\n", HIWORD(ret));
+}
+    header_height = HIWORD(ret);
+    item_width = LOWORD(ret);
+
+    ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 1, 0);
+    ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret));
+todo_wine
+    ok(HIWORD(ret) > header_height, "Unexpected height %d.\n", HIWORD(ret));
+
+    item_height = HIWORD(ret) - header_height;
+
+    ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -2, 0);
+    ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret));
+    ok(HIWORD(ret) == header_height - 2 * item_height, "Unexpected height %d.\n", HIWORD(ret));
+
+    ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -1, 0);
+    ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret));
+    ok(HIWORD(ret) == header_height, "Unexpected height %d.\n", HIWORD(ret));
+
+    ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 2, 0);
+    ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret));
+    ok(HIWORD(ret) == header_height + 2 * item_height, "Unexpected height %d.\n", HIWORD(ret));
+
+    for (item_count = 1; item_count <= 2; ++item_count)
+    {
+        itema.mask = LVIF_TEXT;
+        itema.iItem = 0;
+        itema.iSubItem = 0;
+        itema.pszText = test;
+        ret = SendMessageA(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&itema);
+        ok(ret == 0, "Unexpected return value %d.\n", ret);
+
+        ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 0, 0);
+        ok(LOWORD(ret) >= column_width, "Unexpected width %d.\n", LOWORD(ret));
+    todo_wine
+        ok(HIWORD(ret) != 0, "Unexpected height %d.\n", HIWORD(ret));
+
+        header_height = HIWORD(ret);
+        item_width = LOWORD(ret);
+
+        ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 1, 0);
+        ok(LOWORD(ret) == item_width, "Unexpected width %d, item %d\n", LOWORD(ret), item_count - 1);
+        ok(HIWORD(ret) > header_height, "Unexpected height %d. item %d.\n", HIWORD(ret),  item_count - 1);
+
+        item_height = HIWORD(ret) - header_height;
+
+        ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -2, 0);
+        ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret));
+    todo_wine
+        ok(HIWORD(ret) == header_height - 2 * item_height, "Unexpected height %d.\n", HIWORD(ret));
+
+        ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -1, 0);
+        ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret));
+        ok(HIWORD(ret) == header_height + item_count * item_height, "Unexpected height %d.\n", HIWORD(ret));
+
+        ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 2, 0);
+        ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret));
+        ok(HIWORD(ret) == header_height + 2 * item_height, "Unexpected height %d.\n", HIWORD(ret));
+
+        ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 2, MAKELONG(item_width * 2, header_height + 3 * item_height));
+        ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret));
+        ok(HIWORD(ret) == header_height + 2 * item_height, "Unexpected height %d.\n", HIWORD(ret));
+
+        ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -2, MAKELONG(item_width * 2, 0));
+        ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret));
+    todo_wine
+        ok(HIWORD(ret) == header_height - 2 * item_height, "Unexpected height %d.\n", HIWORD(ret));
+
+        ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -2, MAKELONG(-1, -1));
+        ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret));
+    todo_wine
+        ok(HIWORD(ret) == header_height - 2 * item_height, "Unexpected height %d.\n", HIWORD(ret));
+    }
+
+    DestroyWindow(hwnd);
+
 }
 
 static void test_finditem(void)
@@ -5102,12 +5201,6 @@ static void test_finditem(void)
     fi.flags = LVFI_SUBSTRING;
     fi.psz = f;
     r = SendMessageA(hwnd, LVM_FINDITEMA, -1, (LPARAM)&fi);
-    if (r == -1)
-    {
-        win_skip("LVFI_SUBSTRING not supported\n");
-        DestroyWindow(hwnd);
-        return;
-    }
     expect(0, r);
     strcpy(f, "f");
     fi.flags = LVFI_SUBSTRING;
@@ -5895,6 +5988,7 @@ static void test_oneclickactivate(void)
 
 static void test_callback_mask(void)
 {
+    LVITEMA item;
     DWORD mask;
     HWND hwnd;
     BOOL ret;
@@ -5911,6 +6005,209 @@ static void test_callback_mask(void)
     ok(mask == ~0u, "got 0x%08x\n", mask);
 
     DestroyWindow(hwnd);
+
+    /* LVS_OWNERDATA, mask LVIS_FOCUSED */
+    hwnd = create_listview_control(LVS_REPORT | LVS_OWNERDATA);
+
+    mask = SendMessageA(hwnd, LVM_GETCALLBACKMASK, 0, 0);
+    ok(mask == 0, "Unexpected callback mask %#x.\n", mask);
+
+    ret = SendMessageA(hwnd, LVM_SETCALLBACKMASK, LVIS_FOCUSED, 0);
+    ok(ret, "Failed to set callback mask, %d\n", ret);
+
+    mask = SendMessageA(hwnd, LVM_GETCALLBACKMASK, 0, 0);
+    ok(mask == LVIS_FOCUSED, "Unexpected callback mask %#x.\n", mask);
+
+    ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 1, 0);
+    ok(ret, "Failed to set item count.\n");
+
+    ret = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0);
+    ok(ret == -1, "Unexpected selection mark, %d\n", ret);
+
+    item.stateMask = LVIS_FOCUSED;
+    item.state = LVIS_FOCUSED;
+    ret = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item);
+    ok(ret, "Failed to set item state.\n");
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED);
+todo_wine
+    ok(ret == 0, "Unexpected focused item, ret %d\n", ret);
+
+    ret = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0);
+todo_wine
+    ok(ret == 0, "Unexpected selection mark, %d\n", ret);
+
+    ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 0, 0);
+    ok(ret, "Failed to set item count.\n");
+
+    ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED);
+    ok(ret == -1, "Unexpected focused item, ret %d\n", ret);
+
+    ret = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0);
+    ok(ret == -1, "Unexpected selection mark, %d\n", ret);
+
+    ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 1, 0);
+    ok(ret, "Failed to set item count.\n");
+
+    ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED);
+    ok(ret == -1, "Unexpected focused item, ret %d\n", ret);
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "parent seq, owner data/focus 1", FALSE);
+
+    /* LVS_OWNDERDATA, empty mask */
+    ret = SendMessageA(hwnd, LVM_SETCALLBACKMASK, 0, 0);
+    ok(ret, "Failed to set callback mask, %d\n", ret);
+
+    ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 1, 0);
+    ok(ret, "Failed to set item count.\n");
+
+    ret = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0);
+    ok(ret == -1, "Unexpected selection mark, %d\n", ret);
+
+    item.stateMask = LVIS_FOCUSED;
+    item.state = LVIS_FOCUSED;
+    ret = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item);
+    ok(ret, "Failed to set item state.\n");
+
+    ret = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0);
+    ok(ret == 0, "Unexpected selection mark, %d\n", ret);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED);
+    ok(ret == 0, "Unexpected focused item, ret %d\n", ret);
+
+    ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 0, 0);
+    ok(ret, "Failed to set item count.\n");
+
+    ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED);
+    ok(ret == -1, "Unexpected focused item, ret %d\n", ret);
+
+    ret = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0);
+todo_wine
+    ok(ret == -1, "Unexpected selection mark, %d\n", ret);
+
+    ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 1, 0);
+    ok(ret, "Failed to set item count.\n");
+
+    ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED);
+    ok(ret == -1, "Unexpected focused item, ret %d\n", ret);
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "parent seq, owner data/focus 2", FALSE);
+
+    /* 2 items, focus on index 0, reduce to 1 item. */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 2, 0);
+    ok(ret, "Failed to set item count.\n");
+
+    ret = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item);
+    ok(ret, "Failed to set item state.\n");
+
+    ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED);
+    ok(ret == 0, "Unexpected focused item, ret %d\n", ret);
+
+    ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 1, 0);
+    ok(ret, "Failed to set item count.\n");
+
+    ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED);
+    ok(ret == 0, "Unexpected focused item, ret %d\n", ret);
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_focus_change_ownerdata_seq,
+        "parent seq, owner data/focus 3", TRUE);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_state_image(void)
+{
+    static const DWORD styles[] =
+    {
+        LVS_ICON,
+        LVS_REPORT,
+        LVS_SMALLICON,
+        LVS_LIST,
+    };
+    int i;
+
+    for (i = 0; i < sizeof(styles)/sizeof(styles[0]); i++)
+    {
+        static char text[] = "Item";
+        static char subtext[] = "Subitem";
+        char buff[16];
+        LVITEMA item;
+        HWND hwnd;
+        int r;
+
+        hwnd = create_listview_control(styles[i]);
+
+        insert_column(hwnd, 0);
+        insert_column(hwnd, 1);
+
+        item.mask = LVIF_TEXT;
+        item.iItem = 0;
+        item.iSubItem = 0;
+        item.pszText = text;
+        r = SendMessageA(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item);
+        ok(r == 0, "Failed to insert an item.\n");
+
+        item.mask = LVIF_STATE;
+        item.state = INDEXTOSTATEIMAGEMASK(1) | LVIS_SELECTED;
+        item.stateMask = LVIS_STATEIMAGEMASK | LVIS_SELECTED;
+        item.iItem = 0;
+        item.iSubItem = 0;
+        r = SendMessageA(hwnd, LVM_SETITEMA, 0, (LPARAM)&item);
+        ok(r, "Failed to set item state.\n");
+
+        item.mask = LVIF_TEXT;
+        item.iItem = 0;
+        item.iSubItem = 1;
+        item.pszText = subtext;
+        r = SendMessageA(hwnd, LVM_SETITEMA, 0, (LPARAM)&item);
+        ok(r, "Failed to set subitem text.\n");
+
+        item.mask = LVIF_STATE;
+        item.stateMask = LVIS_STATEIMAGEMASK | LVIS_SELECTED;
+        item.state = 0;
+        item.iItem = 0;
+        item.iSubItem = 0;
+        r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+        ok(r, "Failed to get item state.\n");
+        ok(item.state == (INDEXTOSTATEIMAGEMASK(1) | LVIS_SELECTED), "Unexpected item state %#x.\n", item.state);
+
+        item.mask = LVIF_STATE;
+        item.stateMask = LVIS_STATEIMAGEMASK | LVIS_SELECTED;
+        item.state = INDEXTOSTATEIMAGEMASK(2);
+        item.iItem = 0;
+        item.iSubItem = 1;
+        r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+        ok(r, "Failed to get subitem state.\n");
+    todo_wine
+        ok(item.state == 0, "Unexpected state %#x.\n", item.state);
+
+        item.mask = LVIF_STATE;
+        item.stateMask = LVIS_STATEIMAGEMASK | LVIS_SELECTED;
+        item.state = INDEXTOSTATEIMAGEMASK(2);
+        item.iItem = 0;
+        item.iSubItem = 2;
+        r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+        ok(r, "Failed to get subitem state.\n");
+    todo_wine
+        ok(item.state == 0, "Unexpected state %#x.\n", item.state);
+
+        item.mask = LVIF_TEXT;
+        item.iItem = 0;
+        item.iSubItem = 1;
+        item.pszText = buff;
+        item.cchTextMax = sizeof(buff);
+        r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+        ok(r, "Failed to get subitem text %d.\n", r);
+        ok(!strcmp(buff, subtext), "Unexpected subitem text %s.\n", buff);
+
+        DestroyWindow(hwnd);
+    }
 }
 
 START_TEST(listview)
@@ -5985,6 +6282,7 @@ START_TEST(listview)
     test_header_proc();
     test_oneclickactivate();
     test_callback_mask();
+    test_state_image();
 
     if (!load_v6_module(&ctx_cookie, &hCtx))
     {
@@ -6003,6 +6301,26 @@ START_TEST(listview)
     test_multiselect();
     test_insertitem();
     test_header_proc();
+