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"
|
||||
|
||||
/* Our internal stack structure of the window procedures to subclass */
|
||||
typedef struct _SUBCLASSPROCS {
|
||||
SUBCLASSPROC subproc;
|
||||
UINT_PTR id;
|
||||
DWORD_PTR ref;
|
||||
struct _SUBCLASSPROCS *next;
|
||||
} SUBCLASSPROCS, *LPSUBCLASSPROCS;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct {
|
||||
SUBCLASSPROC subproc;
|
||||
UINT_PTR id;
|
||||
DWORD_PTR ref;
|
||||
} SubclassProcs[31];
|
||||
int stackpos;
|
||||
int stacknum;
|
||||
int wndprocrecursion;
|
||||
SUBCLASSPROCS *SubclassProcs;
|
||||
SUBCLASSPROCS *stackpos;
|
||||
WNDPROC origproc;
|
||||
BOOL running;
|
||||
} SUBCLASS_INFO, *LPSUBCLASS_INFO;
|
||||
|
||||
/* undocumented functions */
|
||||
|
|
|
@ -116,6 +116,7 @@ extern void TREEVIEW_Unregister(void);
|
|||
extern void UPDOWN_Register(void);
|
||||
extern void UPDOWN_Unregister(void);
|
||||
|
||||
LRESULT WINAPI COMCTL32_SubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
LPSTR COMCTL32_aSubclass = NULL;
|
||||
HMODULE COMCTL32_hModule = 0;
|
||||
|
@ -1092,7 +1093,7 @@ BOOL WINAPI SetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
|
|||
UINT_PTR uIDSubclass, DWORD_PTR dwRef)
|
||||
{
|
||||
LPSUBCLASS_INFO stack;
|
||||
int n;
|
||||
LPSUBCLASSPROCS proc;
|
||||
|
||||
TRACE ("(%p, %p, %x, %lx)\n", hWnd, pfnSubclass, uIDSubclass, dwRef);
|
||||
|
||||
|
@ -1116,48 +1117,43 @@ BOOL WINAPI SetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
|
|||
/* set window procedure to our own and save the current one */
|
||||
if (IsWindowUnicode (hWnd))
|
||||
stack->origproc = (WNDPROC)SetWindowLongW (hWnd, GWL_WNDPROC,
|
||||
(LONG)DefSubclassProc);
|
||||
(LONG)COMCTL32_SubclassProc);
|
||||
else
|
||||
stack->origproc = (WNDPROC)SetWindowLongA (hWnd, GWL_WNDPROC,
|
||||
(LONG)DefSubclassProc);
|
||||
} 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;
|
||||
(LONG)COMCTL32_SubclassProc);
|
||||
}
|
||||
else {
|
||||
/* Check to see if we have called this function with the same uIDSubClass
|
||||
* and pfnSubclass */
|
||||
proc = stack->SubclassProcs;
|
||||
while (proc) {
|
||||
if ((proc->id == uIDSubclass) &&
|
||||
(proc->subproc == pfnSubclass)) {
|
||||
proc->ref = dwRef;
|
||||
return TRUE;
|
||||
}
|
||||
proc = proc->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check to see if we have called this function with the same uIDSubClass
|
||||
* and pfnSubclass */
|
||||
for (n = 0; n < stack->stacknum; n++)
|
||||
if ((stack->SubclassProcs[n].id == uIDSubclass) &&
|
||||
(stack->SubclassProcs[n].subproc == pfnSubclass)) {
|
||||
stack->SubclassProcs[n].ref = dwRef;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (stack->stacknum >= 32) {
|
||||
ERR ("We have a Subclass stack overflow, please increment size\n");
|
||||
|
||||
proc = HeapAlloc(GetProcessHeap(), 0, sizeof(SUBCLASSPROCS));
|
||||
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;
|
||||
}
|
||||
|
||||
memmove (&stack->SubclassProcs[1], &stack->SubclassProcs[0],
|
||||
sizeof(stack->SubclassProcs[0]) * stack->stacknum);
|
||||
|
||||
stack->stacknum++;
|
||||
if (stack->wndprocrecursion)
|
||||
stack->stackpos++;
|
||||
|
||||
stack->SubclassProcs[0].subproc = pfnSubclass;
|
||||
stack->SubclassProcs[0].ref = dwRef;
|
||||
stack->SubclassProcs[0].id = uIDSubclass;
|
||||
|
||||
proc->subproc = pfnSubclass;
|
||||
proc->ref = dwRef;
|
||||
proc->id = uIDSubclass;
|
||||
proc->next = stack->SubclassProcs;
|
||||
stack->SubclassProcs = proc;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -1182,7 +1178,7 @@ BOOL WINAPI GetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
|
|||
UINT_PTR uID, DWORD_PTR *pdwRef)
|
||||
{
|
||||
LPSUBCLASS_INFO stack;
|
||||
int n;
|
||||
LPSUBCLASSPROCS proc;
|
||||
|
||||
TRACE ("(%p, %p, %x, %p)\n", hWnd, pfnSubclass, uID, pdwRef);
|
||||
|
||||
|
@ -1191,12 +1187,15 @@ BOOL WINAPI GetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
|
|||
if (!stack)
|
||||
return FALSE;
|
||||
|
||||
for (n = 0; n < stack->stacknum; n++)
|
||||
if ((stack->SubclassProcs[n].id == uID) &&
|
||||
(stack->SubclassProcs[n].subproc == pfnSubclass)) {
|
||||
*pdwRef = stack->SubclassProcs[n].ref;
|
||||
proc = stack->SubclassProcs;
|
||||
while (proc) {
|
||||
if ((proc->id == uID) &&
|
||||
(proc->subproc == pfnSubclass)) {
|
||||
*pdwRef = proc->ref;
|
||||
return TRUE;
|
||||
}
|
||||
proc = proc->next;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -1220,7 +1219,9 @@ BOOL WINAPI GetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
|
|||
BOOL WINAPI RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uID)
|
||||
{
|
||||
LPSUBCLASS_INFO stack;
|
||||
int n;
|
||||
LPSUBCLASSPROCS prevproc = NULL;
|
||||
LPSUBCLASSPROCS proc;
|
||||
BOOL ret = FALSE;
|
||||
|
||||
TRACE ("(%p, %p, %x)\n", hWnd, pfnSubclass, uID);
|
||||
|
||||
|
@ -1229,8 +1230,28 @@ BOOL WINAPI RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR u
|
|||
if (!stack)
|
||||
return FALSE;
|
||||
|
||||
if ((stack->stacknum == 1) && (stack->stackpos == 1) &&
|
||||
!stack->wndprocrecursion) {
|
||||
proc = stack->SubclassProcs;
|
||||
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");
|
||||
/* clean up our heap and reset the origional window procedure */
|
||||
if (IsWindowUnicode (hWnd))
|
||||
|
@ -1239,30 +1260,51 @@ BOOL WINAPI RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR u
|
|||
SetWindowLongA (hWnd, GWL_WNDPROC, (LONG)stack->origproc);
|
||||
HeapFree (GetProcessHeap (), 0, stack);
|
||||
RemovePropA( hWnd, COMCTL32_aSubclass );
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
for (n = stack->stacknum - 1; n >= 0; n--)
|
||||
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;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* 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]
|
||||
|
@ -1284,6 +1326,8 @@ LRESULT WINAPI DefSubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
|
|||
{
|
||||
LPSUBCLASS_INFO stack;
|
||||
LRESULT ret;
|
||||
|
||||
TRACE ("(%p, 0x%08x, 0x%08x, 0x%08lx)\n", hWnd, uMsg, wParam, lParam);
|
||||
|
||||
/* retrieve our little stack from the Properties */
|
||||
stack = (LPSUBCLASS_INFO)GetPropA (hWnd, COMCTL32_aSubclass);
|
||||
|
@ -1292,41 +1336,20 @@ LRESULT WINAPI DefSubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
|
|||
return 0;
|
||||
}
|
||||
|
||||
stack->wndprocrecursion++;
|
||||
|
||||
/* If we are at the end of stack then we have to call the original
|
||||
* window procedure */
|
||||
if (stack->stackpos == stack->stacknum) {
|
||||
if (!stack->stackpos) {
|
||||
if (IsWindowUnicode (hWnd))
|
||||
ret = CallWindowProcW (stack->origproc, hWnd, uMsg, wParam, lParam);
|
||||
else
|
||||
ret = CallWindowProcA (stack->origproc, hWnd, uMsg, wParam, lParam);
|
||||
} else {
|
||||
stack->stackpos++;
|
||||
SUBCLASSPROCS proc;
|
||||
memcpy(&proc, stack->stackpos, sizeof(proc));
|
||||
stack->stackpos = stack->stackpos->next;
|
||||
/* call the Subclass procedure from the stack */
|
||||
ret = stack->SubclassProcs[stack->stackpos - 1].subproc (hWnd, uMsg, wParam, lParam,
|
||||
stack->SubclassProcs[stack->stackpos - 1].id, stack->SubclassProcs[stack->stackpos - 1].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;
|
||||
ret = proc.subproc (hWnd, uMsg, wParam, lParam,
|
||||
proc.id, proc.ref);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
Makefile
|
||||
dpa.ok
|
||||
imagelist.ok
|
||||
subclass.ok
|
||||
tab.ok
|
||||
testlist.c
|
||||
|
|
|
@ -8,6 +8,7 @@ IMPORTS = comctl32 user32 gdi32
|
|||
CTESTS = \
|
||||
dpa.c \
|
||||
imagelist.c \
|
||||
subclass.c \
|
||||
tab.c
|
||||
|
||||
@MAKE_TEST_RULES@
|
||||
|
|
|
@ -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…
Reference in New Issue