Changed winproc allocation to be based only on the procedure address,

to avoid the need to keep track of winprocs for each window and class.
This commit is contained in:
Alexandre Julliard 2004-11-24 18:43:18 +00:00
parent 958f4c643e
commit 648994c3ef
6 changed files with 165 additions and 261 deletions

View File

@ -36,13 +36,6 @@ typedef enum
WIN_PROC_32W
} WINDOWPROCTYPE;
typedef enum
{
WIN_PROC_CLASS,
WIN_PROC_WINDOW,
WIN_PROC_TIMER
} WINDOWPROCUSER;
typedef struct
{
WPARAM16 wParam;
@ -60,9 +53,7 @@ typedef struct
struct tagWINDOWPROC;
extern WNDPROC16 WINPROC_GetProc( WNDPROC proc, WINDOWPROCTYPE type );
extern BOOL WINPROC_SetProc( WNDPROC *pFirst, WNDPROC func,
WINDOWPROCTYPE type, WINDOWPROCUSER user );
extern void WINPROC_FreeProc( WNDPROC proc, WINDOWPROCUSER user );
extern WNDPROC WINPROC_AllocProc( WNDPROC func, WINDOWPROCTYPE type );
extern WINDOWPROCTYPE WINPROC_GetProcType( WNDPROC proc );
extern INT WINPROC_MapMsg32ATo32W( HWND hwnd, UINT msg, WPARAM *pwparam,

View File

@ -187,20 +187,14 @@ static WNDPROC16 CLASS_SetProc( CLASS *classPtr, WNDPROC newproc, WINDOWPROCTYPE
if (!*proc || type == WIN_PROC_32W) proc = &classPtr->winprocW;
}
ret = WINPROC_GetProc( *proc, type );
WINPROC_SetProc( proc, newproc, type, WIN_PROC_CLASS );
/* now free the one that we didn't set */
*proc = WINPROC_AllocProc( newproc, type );
/* now clear the one that we didn't set */
if (classPtr->winprocA && classPtr->winprocW)
{
if (proc == &classPtr->winprocA)
{
WINPROC_FreeProc( classPtr->winprocW, WIN_PROC_CLASS );
classPtr->winprocW = 0;
}
else
{
WINPROC_FreeProc( classPtr->winprocA, WIN_PROC_CLASS );
classPtr->winprocA = 0;
}
}
return ret;
}
@ -303,8 +297,6 @@ static void CLASS_FreeClass( CLASS *classPtr )
if (classPtr->dce) DCE_FreeDCE( classPtr->dce );
if (classPtr->hbrBackground > (HBRUSH)(COLOR_GRADIENTINACTIVECAPTION + 1))
DeleteObject( classPtr->hbrBackground );
WINPROC_FreeProc( classPtr->winprocA, WIN_PROC_CLASS );
WINPROC_FreeProc( classPtr->winprocW, WIN_PROC_CLASS );
UnMapLS( classPtr->segMenuName );
HeapFree( GetProcessHeap(), 0, classPtr->menuName );
HeapFree( GetProcessHeap(), 0, classPtr );
@ -458,10 +450,8 @@ static CLASS *register_builtin( const struct builtin_class_descr *descr )
classPtr->hCursor = LoadCursorA( 0, (LPSTR)descr->cursor );
classPtr->hbrBackground = descr->brush;
if (descr->procA) WINPROC_SetProc( &classPtr->winprocA, descr->procA,
WIN_PROC_32A, WIN_PROC_CLASS );
if (descr->procW) WINPROC_SetProc( &classPtr->winprocW, descr->procW,
WIN_PROC_32W, WIN_PROC_CLASS );
if (descr->procA) classPtr->winprocA = WINPROC_AllocProc( descr->procA, WIN_PROC_32A );
if (descr->procW) classPtr->winprocW = WINPROC_AllocProc( descr->procW, WIN_PROC_32W );
release_class_ptr( classPtr );
return classPtr;
}
@ -623,8 +613,7 @@ ATOM WINAPI RegisterClassEx16( const WNDCLASSEX16 *wc )
classPtr->hCursor = HCURSOR_32(wc->hCursor);
classPtr->hbrBackground = HBRUSH_32(wc->hbrBackground);
WINPROC_SetProc( &classPtr->winprocA, (WNDPROC)wc->lpfnWndProc,
WIN_PROC_16, WIN_PROC_CLASS );
classPtr->winprocA = WINPROC_AllocProc( (WNDPROC)wc->lpfnWndProc, WIN_PROC_16 );
CLASS_SetMenuNameA( classPtr, MapSL(wc->lpszMenuName) );
release_class_ptr( classPtr );
return atom;
@ -662,7 +651,7 @@ ATOM WINAPI RegisterClassExA( const WNDCLASSEXA* wc )
classPtr->hIconSm = wc->hIconSm;
classPtr->hCursor = wc->hCursor;
classPtr->hbrBackground = wc->hbrBackground;
WINPROC_SetProc( &classPtr->winprocA, wc->lpfnWndProc, WIN_PROC_32A, WIN_PROC_CLASS );
classPtr->winprocA = WINPROC_AllocProc( wc->lpfnWndProc, WIN_PROC_32A );
CLASS_SetMenuNameA( classPtr, wc->lpszMenuName );
release_class_ptr( classPtr );
return atom;
@ -700,7 +689,7 @@ ATOM WINAPI RegisterClassExW( const WNDCLASSEXW* wc )
classPtr->hIconSm = wc->hIconSm;
classPtr->hCursor = wc->hCursor;
classPtr->hbrBackground = wc->hbrBackground;
WINPROC_SetProc( &classPtr->winprocW, wc->lpfnWndProc, WIN_PROC_32W, WIN_PROC_CLASS );
classPtr->winprocW = WINPROC_AllocProc( wc->lpfnWndProc, WIN_PROC_32W );
CLASS_SetMenuNameW( classPtr, wc->lpszMenuName );
release_class_ptr( classPtr );
return atom;

View File

@ -247,7 +247,6 @@ static LRESULT DEFDLG_Proc( HWND hwnd, UINT msg, WPARAM wParam,
}
if (dlgInfo->hUserFont) DeleteObject( dlgInfo->hUserFont );
if (dlgInfo->hMenu) DestroyMenu( dlgInfo->hMenu );
WINPROC_FreeProc( DEFDLG_GetDlgProc( hwnd ), WIN_PROC_WINDOW );
HeapFree( GetProcessHeap(), 0, dlgInfo );
}
/* Window clean-up */

View File

@ -77,7 +77,6 @@ static void TIMER_ClearTimer( TIMER * pTimer )
pTimer->msg = 0;
pTimer->id = 0;
pTimer->timeout = 0;
WINPROC_FreeProc( pTimer->proc, WIN_PROC_TIMER );
}
@ -172,7 +171,7 @@ static UINT_PTR TIMER_SetTimer( HWND hwnd, UINT_PTR id, UINT timeout,
if (!hwnd) id = i + 1;
if (proc) WINPROC_SetProc( &winproc, proc, type, WIN_PROC_TIMER );
if (proc) winproc = WINPROC_AllocProc( proc, type );
SERVER_START_REQ( set_win_timer )
{

View File

@ -650,7 +650,6 @@ LRESULT WIN_DestroyWindow( HWND hwnd )
}
DCE_FreeWindowDCE( hwnd ); /* Always do this to catch orphaned DCs */
if (USER_Driver.pDestroyWindow) USER_Driver.pDestroyWindow( hwnd );
WINPROC_FreeProc( wndPtr->winproc, WIN_PROC_WINDOW );
wndPtr->class = NULL;
wndPtr->dwMagic = 0; /* Mark it as invalid */
WIN_ReleaseWndPtr( wndPtr );
@ -2058,7 +2057,7 @@ static LONG_PTR WIN_SetWindowLong( HWND hwnd, INT offset, LONG_PTR newval,
}
case GWLP_WNDPROC:
retval = (ULONG_PTR)WINPROC_GetProc( wndPtr->winproc, type );
WINPROC_SetProc( &wndPtr->winproc, (WNDPROC)newval, type, WIN_PROC_WINDOW );
wndPtr->winproc = WINPROC_AllocProc( (WNDPROC)newval, type );
WIN_ReleasePtr( wndPtr );
return retval;
case GWLP_ID:
@ -2070,7 +2069,7 @@ static LONG_PTR WIN_SetWindowLong( HWND hwnd, INT offset, LONG_PTR newval,
{
WNDPROC *ptr = (WNDPROC *)((char *)wndPtr->wExtra + DWLP_DLGPROC);
retval = (ULONG_PTR)WINPROC_GetProc( *ptr, type );
WINPROC_SetProc( ptr, (WNDPROC)newval, type, WIN_PROC_WINDOW );
*ptr = WINPROC_AllocProc( (WNDPROC)newval, type );
WIN_ReleasePtr( wndPtr );
return retval;
}

View File

@ -47,6 +47,8 @@ WINE_DECLARE_DEBUG_CHANNEL(msg);
WINE_DECLARE_DEBUG_CHANNEL(relay);
WINE_DEFAULT_DEBUG_CHANNEL(win);
#ifdef __i386__
#include "pshpack1.h"
/* Window procedure 16-to-32-bit thunk */
@ -78,8 +80,28 @@ typedef struct
BYTE jmp; /* jmp proc (relative jump) */
WNDPROC proc;
} WINPROC_JUMP;
#include "poppack.h"
#else /* __i386__ */
typedef struct
{
WNDPROC proc;
} WINPROC_THUNK_FROM16;
typedef struct
{
WNDPROC16 proc;
} WINPROC_THUNK_FROM32;
typedef struct
{
WNDPROC proc;
} WINPROC_JUMP;
#endif /* __i386__ */
typedef union
{
WINPROC_THUNK_FROM16 t_from16;
@ -88,21 +110,11 @@ typedef union
typedef struct tagWINDOWPROC
{
WINPROC_THUNK thunk; /* Thunk */
WINPROC_JUMP jmp; /* Jump */
struct tagWINDOWPROC *next; /* Next window proc */
UINT magic; /* Magic number */
WINDOWPROCTYPE type; /* Function type */
WINDOWPROCUSER user; /* Function user */
WINPROC_THUNK thunk; /* Thunk */
WINPROC_JUMP jmp; /* Jump */
BYTE type; /* Function type */
} WINDOWPROC;
#define WINPROC_MAGIC ('W' | ('P' << 8) | ('R' << 16) | ('C' << 24))
#define WINPROC_THUNKPROC(pproc) \
(((pproc)->type == WIN_PROC_16) ? \
(WNDPROC16)((pproc)->thunk.t_from32.proc) : \
(WNDPROC16)((pproc)->thunk.t_from16.proc))
static LRESULT WINAPI WINPROC_CallProc32ATo16( WNDPROC16 func, HWND hwnd,
UINT msg, WPARAM wParam,
LPARAM lParam );
@ -113,7 +125,6 @@ static LRESULT WINAPI WINPROC_CallProc32WTo16( WNDPROC16 func, HWND hwnd,
#define MAX_WINPROCS (0x10000 / sizeof(WINDOWPROC))
static WINDOWPROC winproc_array[MAX_WINPROCS];
static WINDOWPROC *winproc_first_free;
static UINT winproc_used;
static CRITICAL_SECTION winproc_cs;
@ -125,34 +136,109 @@ static CRITICAL_SECTION_DEBUG critsect_debug =
};
static CRITICAL_SECTION winproc_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
/* allocate a window procedure from the global array */
static WINDOWPROC *alloc_winproc(void)
{
WINDOWPROC *ret = NULL;
EnterCriticalSection( &winproc_cs );
if ((ret = winproc_first_free))
winproc_first_free = ret->next;
else if (winproc_used < MAX_WINPROCS)
ret = &winproc_array[winproc_used++];
LeaveCriticalSection( &winproc_cs );
return ret;
}
static void free_winproc( WINDOWPROC *proc )
{
EnterCriticalSection( &winproc_cs );
proc->magic = 0;
proc->next = winproc_first_free;
winproc_first_free = proc;
LeaveCriticalSection( &winproc_cs );
}
static BOOL is_valid_winproc( WINDOWPROC *proc )
{
if (proc < winproc_array || proc >= winproc_array + MAX_WINPROCS) return FALSE;
/* check array limits */
if (proc < winproc_array || proc >= winproc_array + winproc_used) return FALSE;
/* check alignment */
if (proc != winproc_array + (proc - winproc_array)) return FALSE;
return (proc->magic == WINPROC_MAGIC);
return (proc->type != WIN_PROC_INVALID);
}
/* find an existing winproc for a given function and type */
/* FIXME: probably should do something more clever than a linear search */
static inline WINDOWPROC *find_winproc( WNDPROC func, WINDOWPROCTYPE type )
{
unsigned int i;
if (type == WIN_PROC_16)
{
for (i = 0; i < winproc_used; i++)
{
if (winproc_array[i].type == type &&
winproc_array[i].thunk.t_from32.proc == (WNDPROC16)func)
return &winproc_array[i];
}
}
else
{
for (i = 0; i < winproc_used; i++)
{
if (winproc_array[i].type == type &&
winproc_array[i].thunk.t_from16.proc == func)
return &winproc_array[i];
}
}
return NULL;
}
/* initialize a new winproc */
static inline void set_winproc( WINDOWPROC *proc, WNDPROC func, WINDOWPROCTYPE type )
{
#ifdef __i386__
static FARPROC16 relay_32A, relay_32W;
switch(type)
{
case WIN_PROC_16:
proc->thunk.t_from32.popl_eax = 0x58; /* popl %eax */
proc->thunk.t_from32.pushl_func = 0x68; /* pushl $proc */
proc->thunk.t_from32.proc = (WNDPROC16)func;
proc->thunk.t_from32.pushl_eax = 0x50; /* pushl %eax */
proc->thunk.t_from32.jmp = 0xe9; /* jmp relay*/
proc->thunk.t_from32.relay = /* relative jump */
(void(*)())((DWORD)WINPROC_CallProc32ATo16 -
(DWORD)(&proc->thunk.t_from32.relay + 1));
break;
case WIN_PROC_32A:
if (!relay_32A) relay_32A = GetProcAddress16( GetModuleHandle16("user"),
"__wine_call_wndproc_32A" );
proc->thunk.t_from16.popl_eax = 0x58; /* popl %eax */
proc->thunk.t_from16.pushl_func = 0x68; /* pushl $proc */
proc->thunk.t_from16.proc = func;
proc->thunk.t_from16.pushl_eax = 0x50; /* pushl %eax */
proc->thunk.t_from16.ljmp = 0xea; /* ljmp relay*/
proc->thunk.t_from16.relay_offset = OFFSETOF(relay_32A);
proc->thunk.t_from16.relay_sel = SELECTOROF(relay_32A);
proc->jmp.jmp = 0xe9;
/* Fixup relative jump */
proc->jmp.proc = (WNDPROC)((DWORD)func - (DWORD)(&proc->jmp.proc + 1));
break;
case WIN_PROC_32W:
if (!relay_32W) relay_32W = GetProcAddress16( GetModuleHandle16("user"),
"__wine_call_wndproc_32W" );
proc->thunk.t_from16.popl_eax = 0x58; /* popl %eax */
proc->thunk.t_from16.pushl_func = 0x68; /* pushl $proc */
proc->thunk.t_from16.proc = func;
proc->thunk.t_from16.pushl_eax = 0x50; /* pushl %eax */
proc->thunk.t_from16.ljmp = 0xea; /* ljmp relay*/
proc->thunk.t_from16.relay_offset = OFFSETOF(relay_32W);
proc->thunk.t_from16.relay_sel = SELECTOROF(relay_32W);
proc->jmp.jmp = 0xe9;
/* Fixup relative jump */
proc->jmp.proc = (WNDPROC)((char *)func - (char *)(&proc->jmp.proc + 1));
break;
default:
/* Should not happen */
break;
}
#else /* __i386__ */
switch(type)
{
case WIN_PROC_16:
proc->thunk.t_from32.proc = (WNDPROC16)func;
break;
case WIN_PROC_32A:
case WIN_PROC_32W:
proc->thunk.t_from16.proc = func;
break;
default:
/* Should not happen */
break;
}
#endif /* __i386__ */
proc->type = type;
}
static WORD get_winproc_selector(void)
@ -437,96 +523,15 @@ static WINDOWPROC *WINPROC_GetPtr( WNDPROC handle )
/* Check for a segmented pointer */
if (!IsBadReadPtr16( (SEGPTR)handle, sizeof(proc->thunk) ))
{
ptr = MapSL( (SEGPTR)handle );
/* It must be the thunk address */
proc = (WINDOWPROC *)(ptr - (int)&((WINDOWPROC *)0)->thunk);
if (is_valid_winproc(proc)) return proc;
}
ptr = MapSL( (SEGPTR)handle );
/* It must be the thunk address */
proc = (WINDOWPROC *)(ptr - (int)&((WINDOWPROC *)0)->thunk);
if (is_valid_winproc(proc)) return proc;
return NULL;
}
/**********************************************************************
* WINPROC_AllocWinProc
*
* Allocate a new window procedure.
*/
static WINDOWPROC *WINPROC_AllocWinProc( WNDPROC func, WINDOWPROCTYPE type,
WINDOWPROCUSER user )
{
static FARPROC16 relay_32A, relay_32W;
WINDOWPROC *proc, *oldproc;
/* Allocate a window procedure */
if (!(proc = alloc_winproc())) return 0;
/* Check if the function is already a win proc */
if ((oldproc = WINPROC_GetPtr( func )))
{
*proc = *oldproc;
}
else
{
switch(type)
{
case WIN_PROC_16:
proc->thunk.t_from32.popl_eax = 0x58; /* popl %eax */
proc->thunk.t_from32.pushl_func = 0x68; /* pushl $proc */
proc->thunk.t_from32.proc = (WNDPROC16)func;
proc->thunk.t_from32.pushl_eax = 0x50; /* pushl %eax */
proc->thunk.t_from32.jmp = 0xe9; /* jmp relay*/
proc->thunk.t_from32.relay = /* relative jump */
(void(*)())((DWORD)WINPROC_CallProc32ATo16 -
(DWORD)(&proc->thunk.t_from32.relay + 1));
break;
case WIN_PROC_32A:
if (!relay_32A) relay_32A = GetProcAddress16( GetModuleHandle16("user"),
"__wine_call_wndproc_32A" );
proc->thunk.t_from16.popl_eax = 0x58; /* popl %eax */
proc->thunk.t_from16.pushl_func = 0x68; /* pushl $proc */
proc->thunk.t_from16.proc = func;
proc->thunk.t_from16.pushl_eax = 0x50; /* pushl %eax */
proc->thunk.t_from16.ljmp = 0xea; /* ljmp relay*/
proc->thunk.t_from16.relay_offset = OFFSETOF(relay_32A);
proc->thunk.t_from16.relay_sel = SELECTOROF(relay_32A);
proc->jmp.jmp = 0xe9;
/* Fixup relative jump */
proc->jmp.proc = (WNDPROC)((DWORD)func - (DWORD)(&proc->jmp.proc + 1));
break;
case WIN_PROC_32W:
if (!relay_32W) relay_32W = GetProcAddress16( GetModuleHandle16("user"),
"__wine_call_wndproc_32W" );
proc->thunk.t_from16.popl_eax = 0x58; /* popl %eax */
proc->thunk.t_from16.pushl_func = 0x68; /* pushl $proc */
proc->thunk.t_from16.proc = func;
proc->thunk.t_from16.pushl_eax = 0x50; /* pushl %eax */
proc->thunk.t_from16.ljmp = 0xea; /* ljmp relay*/
proc->thunk.t_from16.relay_offset = OFFSETOF(relay_32W);
proc->thunk.t_from16.relay_sel = SELECTOROF(relay_32W);
proc->jmp.jmp = 0xe9;
/* Fixup relative jump */
proc->jmp.proc = (WNDPROC)((char *)func - (char *)(&proc->jmp.proc + 1));
break;
default:
/* Should not happen */
break;
}
proc->magic = WINPROC_MAGIC;
proc->type = type;
proc->user = user;
}
proc->next = NULL;
TRACE("(%p,%d): returning %p\n", func, type, proc );
return proc;
}
/**********************************************************************
* WINPROC_GetProc
*
@ -560,119 +565,43 @@ WNDPROC16 WINPROC_GetProc( WNDPROC proc, WINDOWPROCTYPE type )
/**********************************************************************
* WINPROC_SetProc
* WINPROC_AllocProc
*
* Set the window procedure for a window or class. There are
* three tree classes of winproc callbacks:
*
* 1) class -> wp - not subclassed
* class -> wp -> wp -> wp -> wp - SetClassLong()
* / /
* 2) window -' / - not subclassed
* window -> wp -> wp ' - SetWindowLong()
*
* 3) timer -> wp - SetTimer()
*
* Initially, winproc of the window points to the current winproc
* thunk of its class. Subclassing prepends a new thunk to the
* window winproc chain at the head of the list. Thus, window thunk
* list includes class thunks and the latter are preserved when the
* window is destroyed.
* Allocate a window procedure for a window or class.
*
* Note that allocated winprocs are never freed; the idea is that even if an app creates a
* lot of windows, it will usually only have a limited number of window procedures, so the
* array won't grow too large, and this way we avoid the need to track allocations per window.
*/
BOOL WINPROC_SetProc( WNDPROC *pFirst, WNDPROC func,
WINDOWPROCTYPE type, WINDOWPROCUSER user )
WNDPROC WINPROC_AllocProc( WNDPROC func, WINDOWPROCTYPE type )
{
BOOL bRecycle = FALSE;
WINDOWPROC *proc, **ppPrev;
WINDOWPROC *proc;
/* Check if function is already in the list */
if (!func) return NULL;
ppPrev = (WINDOWPROC **)pFirst;
proc = WINPROC_GetPtr( func );
while (*ppPrev)
EnterCriticalSection( &winproc_cs );
/* check if the function is already a win proc */
if (!(proc = WINPROC_GetPtr( func )))
{
if (proc)
/* then check if we already have a winproc for that function */
if (!(proc = find_winproc( func, type )))
{
if (*ppPrev == proc)
if (winproc_used >= MAX_WINPROCS)
FIXME( "too many winprocs, cannot allocate one for %p/%d\n", func, type );
else
{
if ((*ppPrev)->user != user)
{
/* terminal thunk is being restored */
WINPROC_FreeProc( *pFirst, (*ppPrev)->user );
*(WINDOWPROC **)pFirst = *ppPrev;
return TRUE;
}
bRecycle = TRUE;
break;
}
}
else
{
if (((*ppPrev)->type == type) &&
(func == (WNDPROC)WINPROC_THUNKPROC(*ppPrev)))
{
if((*ppPrev)->user == user)
{
bRecycle = TRUE;
}
else
{
WINPROC_FreeProc( (WNDPROC)*ppPrev, user );
*ppPrev = NULL;
}
break;
proc = &winproc_array[winproc_used++];
set_winproc( proc, func, type );
TRACE( "allocated %p for %p/%d (%d/%d used)\n",
proc, func, type, winproc_used, MAX_WINPROCS );
}
}
/* WPF_CLASS thunk terminates window thunk list */
if ((*ppPrev)->user != user) break;
ppPrev = &(*ppPrev)->next;
else TRACE( "reusing %p for %p/%d\n", proc, func, type );
}
if (bRecycle)
{
/* Extract this thunk from the list */
proc = *ppPrev;
*ppPrev = proc->next;
}
else /* Allocate a new one */
{
if (proc) /* Was already a win proc */
{
type = proc->type;
func = (WNDPROC)WINPROC_THUNKPROC(proc);
}
proc = WINPROC_AllocWinProc( func, type, user );
if (!proc) return FALSE;
}
/* Add the win proc at the head of the list */
TRACE("(%p,%p,%d): res=%p\n", *pFirst, func, type, proc );
proc->next = *(WINDOWPROC **)pFirst;
*(WINDOWPROC **)pFirst = proc;
return TRUE;
}
/**********************************************************************
* WINPROC_FreeProc
*
* Free a list of win procs.
*/
void WINPROC_FreeProc( WNDPROC proc, WINDOWPROCUSER user )
{
WINDOWPROC *ptr = (WINDOWPROC *)proc;
while (ptr)
{
WINDOWPROC *next = ptr->next;
if (ptr->user != user) break;
TRACE("freeing %p (%d)\n", ptr, user);
free_winproc( ptr );
ptr = next;
}
LeaveCriticalSection( &winproc_cs );
return (WNDPROC)proc;
}
@ -683,9 +612,7 @@ void WINPROC_FreeProc( WNDPROC proc, WINDOWPROCUSER user )
*/
WINDOWPROCTYPE WINPROC_GetProcType( WNDPROC proc )
{
if (!proc ||
(((WINDOWPROC *)proc)->magic != WINPROC_MAGIC))
return WIN_PROC_INVALID;
if (!proc) return WIN_PROC_INVALID;
return ((WINDOWPROC *)proc)->type;
}
/**********************************************************************