Make SetWindowSubclass behave with SetWindowLong subclasses.
Allow unlimited number of subclasses. Correct issue when SendMessage is called from within a subclass proc. Add regression test.
This commit is contained in:
parent
6b1e83281b
commit
59302aed35
@ -146,17 +146,19 @@ BOOL Str_SetPtrAtoW (LPWSTR *lppDest, LPCSTR lpSrc);
|
|||||||
#define WINE_FILEVERSIONSTR "5.80"
|
#define WINE_FILEVERSIONSTR "5.80"
|
||||||
|
|
||||||
/* Our internal stack structure of the window procedures to subclass */
|
/* Our internal stack structure of the window procedures to subclass */
|
||||||
typedef struct
|
typedef struct _SUBCLASSPROCS {
|
||||||
{
|
|
||||||
struct {
|
|
||||||
SUBCLASSPROC subproc;
|
SUBCLASSPROC subproc;
|
||||||
UINT_PTR id;
|
UINT_PTR id;
|
||||||
DWORD_PTR ref;
|
DWORD_PTR ref;
|
||||||
} SubclassProcs[31];
|
struct _SUBCLASSPROCS *next;
|
||||||
int stackpos;
|
} SUBCLASSPROCS, *LPSUBCLASSPROCS;
|
||||||
int stacknum;
|
|
||||||
int wndprocrecursion;
|
typedef struct
|
||||||
|
{
|
||||||
|
SUBCLASSPROCS *SubclassProcs;
|
||||||
|
SUBCLASSPROCS *stackpos;
|
||||||
WNDPROC origproc;
|
WNDPROC origproc;
|
||||||
|
BOOL running;
|
||||||
} SUBCLASS_INFO, *LPSUBCLASS_INFO;
|
} SUBCLASS_INFO, *LPSUBCLASS_INFO;
|
||||||
|
|
||||||
/* undocumented functions */
|
/* undocumented functions */
|
||||||
|
@ -116,6 +116,7 @@ extern void TREEVIEW_Unregister(void);
|
|||||||
extern void UPDOWN_Register(void);
|
extern void UPDOWN_Register(void);
|
||||||
extern void UPDOWN_Unregister(void);
|
extern void UPDOWN_Unregister(void);
|
||||||
|
|
||||||
|
LRESULT WINAPI COMCTL32_SubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
LPSTR COMCTL32_aSubclass = NULL;
|
LPSTR COMCTL32_aSubclass = NULL;
|
||||||
HMODULE COMCTL32_hModule = 0;
|
HMODULE COMCTL32_hModule = 0;
|
||||||
@ -1092,7 +1093,7 @@ BOOL WINAPI SetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
|
|||||||
UINT_PTR uIDSubclass, DWORD_PTR dwRef)
|
UINT_PTR uIDSubclass, DWORD_PTR dwRef)
|
||||||
{
|
{
|
||||||
LPSUBCLASS_INFO stack;
|
LPSUBCLASS_INFO stack;
|
||||||
int n;
|
LPSUBCLASSPROCS proc;
|
||||||
|
|
||||||
TRACE ("(%p, %p, %x, %lx)\n", hWnd, pfnSubclass, uIDSubclass, dwRef);
|
TRACE ("(%p, %p, %x, %lx)\n", hWnd, pfnSubclass, uIDSubclass, dwRef);
|
||||||
|
|
||||||
@ -1116,47 +1117,42 @@ BOOL WINAPI SetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
|
|||||||
/* set window procedure to our own and save the current one */
|
/* set window procedure to our own and save the current one */
|
||||||
if (IsWindowUnicode (hWnd))
|
if (IsWindowUnicode (hWnd))
|
||||||
stack->origproc = (WNDPROC)SetWindowLongW (hWnd, GWL_WNDPROC,
|
stack->origproc = (WNDPROC)SetWindowLongW (hWnd, GWL_WNDPROC,
|
||||||
(LONG)DefSubclassProc);
|
(LONG)COMCTL32_SubclassProc);
|
||||||
else
|
else
|
||||||
stack->origproc = (WNDPROC)SetWindowLongA (hWnd, GWL_WNDPROC,
|
stack->origproc = (WNDPROC)SetWindowLongA (hWnd, GWL_WNDPROC,
|
||||||
(LONG)DefSubclassProc);
|
(LONG)COMCTL32_SubclassProc);
|
||||||
} else {
|
|
||||||
WNDPROC current;
|
|
||||||
if (IsWindowUnicode (hWnd))
|
|
||||||
current = (WNDPROC)GetWindowLongW (hWnd, GWL_WNDPROC);
|
|
||||||
else
|
|
||||||
current = (WNDPROC)GetWindowLongA (hWnd, GWL_WNDPROC);
|
|
||||||
|
|
||||||
if (current != DefSubclassProc) {
|
|
||||||
ERR ("Application has subclassed with our procedure, then manually, then with us again. The current implementation can't handle this.\n");
|
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
|
|
||||||
/* Check to see if we have called this function with the same uIDSubClass
|
/* Check to see if we have called this function with the same uIDSubClass
|
||||||
* and pfnSubclass */
|
* and pfnSubclass */
|
||||||
for (n = 0; n < stack->stacknum; n++)
|
proc = stack->SubclassProcs;
|
||||||
if ((stack->SubclassProcs[n].id == uIDSubclass) &&
|
while (proc) {
|
||||||
(stack->SubclassProcs[n].subproc == pfnSubclass)) {
|
if ((proc->id == uIDSubclass) &&
|
||||||
stack->SubclassProcs[n].ref = dwRef;
|
(proc->subproc == pfnSubclass)) {
|
||||||
|
proc->ref = dwRef;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
proc = proc->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (stack->stacknum >= 32) {
|
proc = HeapAlloc(GetProcessHeap(), 0, sizeof(SUBCLASSPROCS));
|
||||||
ERR ("We have a Subclass stack overflow, please increment size\n");
|
if (!proc) {
|
||||||
|
ERR ("Failed to allocate subclass entry in stack\n");
|
||||||
|
if (IsWindowUnicode (hWnd))
|
||||||
|
SetWindowLongW (hWnd, GWL_WNDPROC, (LONG)stack->origproc);
|
||||||
|
else
|
||||||
|
SetWindowLongA (hWnd, GWL_WNDPROC, (LONG)stack->origproc);
|
||||||
|
HeapFree (GetProcessHeap (), 0, stack);
|
||||||
|
RemovePropA( hWnd, COMCTL32_aSubclass );
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
memmove (&stack->SubclassProcs[1], &stack->SubclassProcs[0],
|
proc->subproc = pfnSubclass;
|
||||||
sizeof(stack->SubclassProcs[0]) * stack->stacknum);
|
proc->ref = dwRef;
|
||||||
|
proc->id = uIDSubclass;
|
||||||
stack->stacknum++;
|
proc->next = stack->SubclassProcs;
|
||||||
if (stack->wndprocrecursion)
|
stack->SubclassProcs = proc;
|
||||||
stack->stackpos++;
|
|
||||||
|
|
||||||
stack->SubclassProcs[0].subproc = pfnSubclass;
|
|
||||||
stack->SubclassProcs[0].ref = dwRef;
|
|
||||||
stack->SubclassProcs[0].id = uIDSubclass;
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -1182,7 +1178,7 @@ BOOL WINAPI GetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
|
|||||||
UINT_PTR uID, DWORD_PTR *pdwRef)
|
UINT_PTR uID, DWORD_PTR *pdwRef)
|
||||||
{
|
{
|
||||||
LPSUBCLASS_INFO stack;
|
LPSUBCLASS_INFO stack;
|
||||||
int n;
|
LPSUBCLASSPROCS proc;
|
||||||
|
|
||||||
TRACE ("(%p, %p, %x, %p)\n", hWnd, pfnSubclass, uID, pdwRef);
|
TRACE ("(%p, %p, %x, %p)\n", hWnd, pfnSubclass, uID, pdwRef);
|
||||||
|
|
||||||
@ -1191,12 +1187,15 @@ BOOL WINAPI GetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
|
|||||||
if (!stack)
|
if (!stack)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
for (n = 0; n < stack->stacknum; n++)
|
proc = stack->SubclassProcs;
|
||||||
if ((stack->SubclassProcs[n].id == uID) &&
|
while (proc) {
|
||||||
(stack->SubclassProcs[n].subproc == pfnSubclass)) {
|
if ((proc->id == uID) &&
|
||||||
*pdwRef = stack->SubclassProcs[n].ref;
|
(proc->subproc == pfnSubclass)) {
|
||||||
|
*pdwRef = proc->ref;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
proc = proc->next;
|
||||||
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@ -1220,7 +1219,9 @@ BOOL WINAPI GetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
|
|||||||
BOOL WINAPI RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uID)
|
BOOL WINAPI RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uID)
|
||||||
{
|
{
|
||||||
LPSUBCLASS_INFO stack;
|
LPSUBCLASS_INFO stack;
|
||||||
int n;
|
LPSUBCLASSPROCS prevproc = NULL;
|
||||||
|
LPSUBCLASSPROCS proc;
|
||||||
|
BOOL ret = FALSE;
|
||||||
|
|
||||||
TRACE ("(%p, %p, %x)\n", hWnd, pfnSubclass, uID);
|
TRACE ("(%p, %p, %x)\n", hWnd, pfnSubclass, uID);
|
||||||
|
|
||||||
@ -1229,8 +1230,28 @@ BOOL WINAPI RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR u
|
|||||||
if (!stack)
|
if (!stack)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if ((stack->stacknum == 1) && (stack->stackpos == 1) &&
|
proc = stack->SubclassProcs;
|
||||||
!stack->wndprocrecursion) {
|
while (proc) {
|
||||||
|
if ((proc->id == uID) &&
|
||||||
|
(proc->subproc == pfnSubclass)) {
|
||||||
|
|
||||||
|
if (!prevproc)
|
||||||
|
stack->SubclassProcs = proc->next;
|
||||||
|
else
|
||||||
|
prevproc->next = proc->next;
|
||||||
|
|
||||||
|
if (stack->stackpos == proc)
|
||||||
|
stack->stackpos = stack->stackpos->next;
|
||||||
|
|
||||||
|
HeapFree (GetProcessHeap (), 0, proc);
|
||||||
|
ret = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prevproc = proc;
|
||||||
|
proc = proc->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stack->SubclassProcs && !stack->running) {
|
||||||
TRACE("Last Subclass removed, cleaning up\n");
|
TRACE("Last Subclass removed, cleaning up\n");
|
||||||
/* clean up our heap and reset the origional window procedure */
|
/* clean up our heap and reset the origional window procedure */
|
||||||
if (IsWindowUnicode (hWnd))
|
if (IsWindowUnicode (hWnd))
|
||||||
@ -1239,30 +1260,51 @@ BOOL WINAPI RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR u
|
|||||||
SetWindowLongA (hWnd, GWL_WNDPROC, (LONG)stack->origproc);
|
SetWindowLongA (hWnd, GWL_WNDPROC, (LONG)stack->origproc);
|
||||||
HeapFree (GetProcessHeap (), 0, stack);
|
HeapFree (GetProcessHeap (), 0, stack);
|
||||||
RemovePropA( hWnd, COMCTL32_aSubclass );
|
RemovePropA( hWnd, COMCTL32_aSubclass );
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (n = stack->stacknum - 1; n >= 0; n--)
|
return ret;
|
||||||
if ((stack->SubclassProcs[n].id == uID) &&
|
|
||||||
(stack->SubclassProcs[n].subproc == pfnSubclass)) {
|
|
||||||
if (n != stack->stacknum)
|
|
||||||
/* Fill the hole in the stack */
|
|
||||||
memmove (&stack->SubclassProcs[n], &stack->SubclassProcs[n + 1],
|
|
||||||
sizeof(stack->SubclassProcs[0]) * (stack->stacknum - n));
|
|
||||||
stack->SubclassProcs[n].subproc = NULL;
|
|
||||||
stack->SubclassProcs[n].ref = 0;
|
|
||||||
stack->SubclassProcs[n].id = 0;
|
|
||||||
|
|
||||||
stack->stacknum--;
|
|
||||||
if (n < stack->stackpos && stack->wndprocrecursion)
|
|
||||||
stack->stackpos--;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* COMCTL32_SubclassProc (internal)
|
||||||
|
*
|
||||||
|
* Window procedure for all subclassed windows.
|
||||||
|
* Saves the current subclassing stack position to support nested messages
|
||||||
|
*/
|
||||||
|
LRESULT WINAPI COMCTL32_SubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
LPSUBCLASS_INFO stack;
|
||||||
|
LPSUBCLASSPROCS proc;
|
||||||
|
LRESULT ret;
|
||||||
|
|
||||||
|
TRACE ("(%p, 0x%08x, 0x%08x, 0x%08lx)\n", hWnd, uMsg, wParam, lParam);
|
||||||
|
|
||||||
|
stack = (LPSUBCLASS_INFO)GetPropA (hWnd, COMCTL32_aSubclass);
|
||||||
|
if (!stack) {
|
||||||
|
ERR ("Our sub classing stack got erased for %p!! Nothing we can do\n", hWnd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save our old stackpos to properly handle nested messages */
|
||||||
|
proc = stack->stackpos;
|
||||||
|
stack->stackpos = stack->SubclassProcs;
|
||||||
|
stack->running = TRUE;
|
||||||
|
ret = DefSubclassProc(hWnd, uMsg, wParam, lParam);
|
||||||
|
stack->running = FALSE;
|
||||||
|
stack->stackpos = proc;
|
||||||
|
|
||||||
|
if (!stack->SubclassProcs) {
|
||||||
|
TRACE("Last Subclass removed, cleaning up\n");
|
||||||
|
/* clean up our heap and reset the origional window procedure */
|
||||||
|
if (IsWindowUnicode (hWnd))
|
||||||
|
SetWindowLongW (hWnd, GWL_WNDPROC, (LONG)stack->origproc);
|
||||||
|
else
|
||||||
|
SetWindowLongA (hWnd, GWL_WNDPROC, (LONG)stack->origproc);
|
||||||
|
HeapFree (GetProcessHeap (), 0, stack);
|
||||||
|
RemovePropA( hWnd, COMCTL32_aSubclass );
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* DefSubclassProc [COMCTL32.413]
|
* DefSubclassProc [COMCTL32.413]
|
||||||
@ -1285,6 +1327,8 @@ LRESULT WINAPI DefSubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
|
|||||||
LPSUBCLASS_INFO stack;
|
LPSUBCLASS_INFO stack;
|
||||||
LRESULT ret;
|
LRESULT ret;
|
||||||
|
|
||||||
|
TRACE ("(%p, 0x%08x, 0x%08x, 0x%08lx)\n", hWnd, uMsg, wParam, lParam);
|
||||||
|
|
||||||
/* retrieve our little stack from the Properties */
|
/* retrieve our little stack from the Properties */
|
||||||
stack = (LPSUBCLASS_INFO)GetPropA (hWnd, COMCTL32_aSubclass);
|
stack = (LPSUBCLASS_INFO)GetPropA (hWnd, COMCTL32_aSubclass);
|
||||||
if (!stack) {
|
if (!stack) {
|
||||||
@ -1292,41 +1336,20 @@ LRESULT WINAPI DefSubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
stack->wndprocrecursion++;
|
|
||||||
|
|
||||||
/* If we are at the end of stack then we have to call the original
|
/* If we are at the end of stack then we have to call the original
|
||||||
* window procedure */
|
* window procedure */
|
||||||
if (stack->stackpos == stack->stacknum) {
|
if (!stack->stackpos) {
|
||||||
if (IsWindowUnicode (hWnd))
|
if (IsWindowUnicode (hWnd))
|
||||||
ret = CallWindowProcW (stack->origproc, hWnd, uMsg, wParam, lParam);
|
ret = CallWindowProcW (stack->origproc, hWnd, uMsg, wParam, lParam);
|
||||||
else
|
else
|
||||||
ret = CallWindowProcA (stack->origproc, hWnd, uMsg, wParam, lParam);
|
ret = CallWindowProcA (stack->origproc, hWnd, uMsg, wParam, lParam);
|
||||||
} else {
|
} else {
|
||||||
stack->stackpos++;
|
SUBCLASSPROCS proc;
|
||||||
|
memcpy(&proc, stack->stackpos, sizeof(proc));
|
||||||
|
stack->stackpos = stack->stackpos->next;
|
||||||
/* call the Subclass procedure from the stack */
|
/* call the Subclass procedure from the stack */
|
||||||
ret = stack->SubclassProcs[stack->stackpos - 1].subproc (hWnd, uMsg, wParam, lParam,
|
ret = proc.subproc (hWnd, uMsg, wParam, lParam,
|
||||||
stack->SubclassProcs[stack->stackpos - 1].id, stack->SubclassProcs[stack->stackpos - 1].ref);
|
proc.id, proc.ref);
|
||||||
stack->stackpos--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We finished the recursion, so let's reinitalize the stack position to
|
|
||||||
* beginning */
|
|
||||||
if ((--stack->wndprocrecursion) == 0) {
|
|
||||||
stack->stackpos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we removed the last entry in our stack while a window procedure was
|
|
||||||
* running then we have to clean up */
|
|
||||||
if ((stack->stackpos == 0) && (stack->stacknum == 0)) {
|
|
||||||
TRACE("Last Subclass removed, cleaning up\n");
|
|
||||||
/* clean up our heap and reset the origional window procedure */
|
|
||||||
if (IsWindowUnicode (hWnd))
|
|
||||||
SetWindowLongW (hWnd, GWL_WNDPROC, (LONG)stack->origproc);
|
|
||||||
else
|
|
||||||
SetWindowLongA (hWnd, GWL_WNDPROC, (LONG)stack->origproc);
|
|
||||||
HeapFree (GetProcessHeap (), 0, stack);
|
|
||||||
RemovePropA( hWnd, COMCTL32_aSubclass );
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
Makefile
|
Makefile
|
||||||
dpa.ok
|
dpa.ok
|
||||||
imagelist.ok
|
imagelist.ok
|
||||||
|
subclass.ok
|
||||||
tab.ok
|
tab.ok
|
||||||
testlist.c
|
testlist.c
|
||||||
|
@ -8,6 +8,7 @@ IMPORTS = comctl32 user32 gdi32
|
|||||||
CTESTS = \
|
CTESTS = \
|
||||||
dpa.c \
|
dpa.c \
|
||||||
imagelist.c \
|
imagelist.c \
|
||||||
|
subclass.c \
|
||||||
tab.c
|
tab.c
|
||||||
|
|
||||||
@MAKE_TEST_RULES@
|
@MAKE_TEST_RULES@
|
||||||
|
298
dlls/comctl32/tests/subclass.c
Normal file
298
dlls/comctl32/tests/subclass.c
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
/* Unit tests for subclassed windows.
|
||||||
|
*
|
||||||
|
* Copyright 2004 Kevin Koltzau
|
||||||
|
*
|
||||||
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#define _WIN32_WINNT 0x0501 /* For SetWindowSubclass/etc */
|
||||||
|
|
||||||
|
#include "windef.h"
|
||||||
|
#include "winbase.h"
|
||||||
|
#include "wingdi.h"
|
||||||
|
#include "winuser.h"
|
||||||
|
#include "commctrl.h"
|
||||||
|
|
||||||
|
#include "wine/test.h"
|
||||||
|
|
||||||
|
static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
|
||||||
|
static BOOL (WINAPI *pRemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);
|
||||||
|
static LRESULT (WINAPI *pDefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
|
||||||
|
|
||||||
|
#define SEND_NEST 0x01
|
||||||
|
#define DELETE_SELF 0x02
|
||||||
|
#define DELETE_PREV 0x04
|
||||||
|
|
||||||
|
struct message {
|
||||||
|
int procnum; /* WndProc id message is expected from */
|
||||||
|
WPARAM wParam; /* expected value of wParam */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int sequence_cnt, sequence_size;
|
||||||
|
static struct message* sequence;
|
||||||
|
|
||||||
|
static const struct message Sub_BasicTest[] = {
|
||||||
|
{ 2, 1 },
|
||||||
|
{ 1, 1 },
|
||||||
|
{ 2, 2 },
|
||||||
|
{ 1, 2 },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct message Sub_DeletedTest[] = {
|
||||||
|
{ 2, 1 },
|
||||||
|
{ 1, 1 },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct message Sub_AfterDeletedTest[] = {
|
||||||
|
{ 1, 1 },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct message Sub_OldAfterNewTest[] = {
|
||||||
|
{ 3, 1 },
|
||||||
|
{ 2, 1 },
|
||||||
|
{ 1, 1 },
|
||||||
|
{ 3, 2 },
|
||||||
|
{ 2, 2 },
|
||||||
|
{ 1, 2 },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct message Sub_MixTest[] = {
|
||||||
|
{ 3, 1 },
|
||||||
|
{ 4, 1 },
|
||||||
|
{ 2, 1 },
|
||||||
|
{ 1, 1 },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct message Sub_MixAndNestTest[] = {
|
||||||
|
{ 3, 1 },
|
||||||
|
{ 4, 1 },
|
||||||
|
{ 3, 2 },
|
||||||
|
{ 4, 2 },
|
||||||
|
{ 2, 2 },
|
||||||
|
{ 1, 2 },
|
||||||
|
{ 2, 1 },
|
||||||
|
{ 1, 1 },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct message Sub_MixNestDelTest[] = {
|
||||||
|
{ 3, 1 },
|
||||||
|
{ 4, 1 },
|
||||||
|
{ 3, 2 },
|
||||||
|
{ 2, 2 },
|
||||||
|
{ 1, 2 },
|
||||||
|
{ 2, 1 },
|
||||||
|
{ 1, 1 },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct message Sub_MixDelPrevTest[] = {
|
||||||
|
{ 3, 1 },
|
||||||
|
{ 5, 1 },
|
||||||
|
{ 2, 1 },
|
||||||
|
{ 1, 1 },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static void add_message(const struct message *msg)
|
||||||
|
{
|
||||||
|
if (!sequence)
|
||||||
|
{
|
||||||
|
sequence_size = 10;
|
||||||
|
sequence = HeapAlloc( GetProcessHeap(), 0, sequence_size * sizeof (struct message) );
|
||||||
|
}
|
||||||
|
if (sequence_cnt == sequence_size)
|
||||||
|
{
|
||||||
|
sequence_size *= 2;
|
||||||
|
sequence = HeapReAlloc( GetProcessHeap(), 0, sequence, sequence_size * sizeof (struct message) );
|
||||||
|
}
|
||||||
|
assert(sequence);
|
||||||
|
|
||||||
|
sequence[sequence_cnt].wParam = msg->wParam;
|
||||||
|
sequence[sequence_cnt].procnum = msg->procnum;
|
||||||
|
|
||||||
|
sequence_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flush_sequence()
|
||||||
|
{
|
||||||
|
HeapFree(GetProcessHeap(), 0, sequence);
|
||||||
|
sequence = 0;
|
||||||
|
sequence_cnt = sequence_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ok_sequence(const struct message *expected, const char *context)
|
||||||
|
{
|
||||||
|
static const struct message end_of_sequence = { 0, 0 };
|
||||||
|
const struct message *actual;
|
||||||
|
|
||||||
|
add_message(&end_of_sequence);
|
||||||
|
|
||||||
|
actual = sequence;
|
||||||
|
|
||||||
|
while(expected->procnum && actual->procnum)
|
||||||
|
{
|
||||||
|
ok(expected->procnum == actual->procnum,
|
||||||
|
"%s: the procnum %d was expected, but got procnum %d instead\n",
|
||||||
|
context, expected->procnum, actual->procnum);
|
||||||
|
ok(expected->wParam == actual->wParam,
|
||||||
|
"%s: in procnum %d expecting wParam 0x%x got 0x%x\n",
|
||||||
|
context, expected->procnum, expected->wParam, actual->wParam);
|
||||||
|
expected++;
|
||||||
|
actual++;
|
||||||
|
}
|
||||||
|
ok(!expected->procnum, "Recieved less messages then expected\n");
|
||||||
|
ok(!actual->procnum, "Recieved more messages then expected\n");
|
||||||
|
flush_sequence();
|
||||||
|
}
|
||||||
|
|
||||||
|
static LRESULT WINAPI WndProc1(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
struct message msg;
|
||||||
|
|
||||||
|
if(message == WM_USER) {
|
||||||
|
msg.wParam = wParam;
|
||||||
|
msg.procnum = 1;
|
||||||
|
add_message(&msg);
|
||||||
|
}
|
||||||
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static WNDPROC origProc3;
|
||||||
|
static LRESULT WINAPI WndProc3(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
struct message msg;
|
||||||
|
|
||||||
|
if(message == WM_USER) {
|
||||||
|
msg.wParam = wParam;
|
||||||
|
msg.procnum = 3;
|
||||||
|
add_message(&msg);
|
||||||
|
}
|
||||||
|
return CallWindowProc(origProc3, hwnd, message, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
static LRESULT WINAPI WndProcSub(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uldSubclass, DWORD_PTR dwRefData)
|
||||||
|
{
|
||||||
|
struct message msg;
|
||||||
|
|
||||||
|
if(message == WM_USER) {
|
||||||
|
msg.wParam = wParam;
|
||||||
|
msg.procnum = uldSubclass;
|
||||||
|
add_message(&msg);
|
||||||
|
|
||||||
|
if(lParam) {
|
||||||
|
if(dwRefData & DELETE_SELF) {
|
||||||
|
pRemoveWindowSubclass(hwnd, WndProcSub, uldSubclass);
|
||||||
|
pRemoveWindowSubclass(hwnd, WndProcSub, uldSubclass);
|
||||||
|
}
|
||||||
|
if(dwRefData & DELETE_PREV)
|
||||||
|
pRemoveWindowSubclass(hwnd, WndProcSub, uldSubclass-1);
|
||||||
|
if(dwRefData & SEND_NEST)
|
||||||
|
SendMessage(hwnd, WM_USER, wParam+1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pDefSubclassProc(hwnd, message, wParam, lParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_subclass()
|
||||||
|
{
|
||||||
|
HWND hwnd = CreateWindowExA(0, "TestSubclass", "Test subclass", WS_OVERLAPPEDWINDOW,
|
||||||
|
100, 100, 200, 200, 0, 0, 0, NULL);
|
||||||
|
assert(hwnd);
|
||||||
|
|
||||||
|
pSetWindowSubclass(hwnd, WndProcSub, 2, 0);
|
||||||
|
SendMessage(hwnd, WM_USER, 1, 0);
|
||||||
|
SendMessage(hwnd, WM_USER, 2, 0);
|
||||||
|
ok_sequence(Sub_BasicTest, "Basic");
|
||||||
|
|
||||||
|
pSetWindowSubclass(hwnd, WndProcSub, 2, DELETE_SELF);
|
||||||
|
SendMessage(hwnd, WM_USER, 1, 1);
|
||||||
|
ok_sequence(Sub_DeletedTest, "Deleted");
|
||||||
|
|
||||||
|
SendMessage(hwnd, WM_USER, 1, 0);
|
||||||
|
ok_sequence(Sub_AfterDeletedTest, "After Deleted");
|
||||||
|
|
||||||
|
pSetWindowSubclass(hwnd, WndProcSub, 2, 0);
|
||||||
|
origProc3 = (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, (LONG)WndProc3);
|
||||||
|
SendMessage(hwnd, WM_USER, 1, 0);
|
||||||
|
SendMessage(hwnd, WM_USER, 2, 0);
|
||||||
|
ok_sequence(Sub_OldAfterNewTest, "Old after New");
|
||||||
|
|
||||||
|
pSetWindowSubclass(hwnd, WndProcSub, 4, 0);
|
||||||
|
SendMessage(hwnd, WM_USER, 1, 0);
|
||||||
|
ok_sequence(Sub_MixTest, "Mix");
|
||||||
|
|
||||||
|
/* Now the fun starts */
|
||||||
|
pSetWindowSubclass(hwnd, WndProcSub, 4, SEND_NEST);
|
||||||
|
SendMessage(hwnd, WM_USER, 1, 1);
|
||||||
|
ok_sequence(Sub_MixAndNestTest, "Mix and nest");
|
||||||
|
|
||||||
|
pSetWindowSubclass(hwnd, WndProcSub, 4, SEND_NEST | DELETE_SELF);
|
||||||
|
SendMessage(hwnd, WM_USER, 1, 1);
|
||||||
|
ok_sequence(Sub_MixNestDelTest, "Mix, nest, del");
|
||||||
|
|
||||||
|
pSetWindowSubclass(hwnd, WndProcSub, 4, 0);
|
||||||
|
pSetWindowSubclass(hwnd, WndProcSub, 5, DELETE_PREV);
|
||||||
|
SendMessage(hwnd, WM_USER, 1, 1);
|
||||||
|
ok_sequence(Sub_MixDelPrevTest, "Mix and del prev");
|
||||||
|
|
||||||
|
DestroyWindow(hwnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL RegisterWindowClasses(void)
|
||||||
|
{
|
||||||
|
WNDCLASSA cls;
|
||||||
|
|
||||||
|
cls.style = 0;
|
||||||
|
cls.lpfnWndProc = WndProc1;
|
||||||
|
cls.cbClsExtra = 0;
|
||||||
|
cls.cbWndExtra = 0;
|
||||||
|
cls.hInstance = GetModuleHandleA(0);
|
||||||
|
cls.hIcon = 0;
|
||||||
|
cls.hCursor = NULL;
|
||||||
|
cls.hbrBackground = NULL;
|
||||||
|
cls.lpszMenuName = NULL;
|
||||||
|
cls.lpszClassName = "TestSubclass";
|
||||||
|
if(!RegisterClassA(&cls)) return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
START_TEST(subclass)
|
||||||
|
{
|
||||||
|
HMODULE hdll;
|
||||||
|
|
||||||
|
hdll = GetModuleHandleA("comctl32.dll");
|
||||||
|
assert(hdll);
|
||||||
|
pSetWindowSubclass = (void*)GetProcAddress(hdll, "SetWindowSubclass");
|
||||||
|
pRemoveWindowSubclass = (void*)GetProcAddress(hdll, "RemoveWindowSubclass");
|
||||||
|
pDefSubclassProc = (void*)GetProcAddress(hdll, "DefSubclassProc");
|
||||||
|
|
||||||
|
if(!pSetWindowSubclass || !pRemoveWindowSubclass || !pDefSubclassProc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(!RegisterWindowClasses()) assert(0);
|
||||||
|
|
||||||
|
test_subclass();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user