user32: Pre-allocate the window procedure for the MDI client class.
[wine] / dlls / user32 / winproc.c
1 /*
2  * Window procedure callbacks
3  *
4  * Copyright 1995 Martin von Loewis
5  * Copyright 1996 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <assert.h>
26 #include <stdarg.h>
27 #include <string.h>
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "controls.h"
32 #include "win.h"
33 #include "user_private.h"
34 #include "wine/unicode.h"
35 #include "wine/debug.h"
36
37 WINE_DECLARE_DEBUG_CHANNEL(msg);
38 WINE_DECLARE_DEBUG_CHANNEL(relay);
39 WINE_DEFAULT_DEBUG_CHANNEL(win);
40
41 typedef struct tagWINDOWPROC
42 {
43     WNDPROC        procA;    /* ASCII window proc */
44     WNDPROC        procW;    /* Unicode window proc */
45 } WINDOWPROC;
46
47 #define MAX_WINPROCS  4096
48 #define BUILTIN_WINPROCS 9  /* first BUILTIN_WINPROCS entries are reserved for builtin procs */
49 #define MAX_WINPROC_RECURSION  64
50 #define WINPROC_PROC16  ((WINDOWPROC *)1)  /* placeholder for 16-bit window procs */
51
52 static LRESULT WINAPI ButtonWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
53 static LRESULT WINAPI ButtonWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
54 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
55 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
56 LRESULT WINAPI EditWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
57 static LRESULT WINAPI EditWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
58 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
59 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
60 static LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
61 static LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
62 static LRESULT WINAPI ScrollBarWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
63 static LRESULT WINAPI ScrollBarWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
64 static LRESULT WINAPI StaticWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
65 static LRESULT WINAPI StaticWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
66
67 static WINDOWPROC winproc_array[MAX_WINPROCS] =
68 {
69     { ButtonWndProcA, ButtonWndProcW },        /* WINPROC_BUTTON */
70     { ComboWndProcA, ComboWndProcW },          /* WINPROC_COMBO */
71     { DefDlgProcA, DefDlgProcW },              /* WINPROC_DIALOG */
72     { EditWndProcA, EditWndProcW },            /* WINPROC_EDIT */
73     { ListBoxWndProcA, ListBoxWndProcW },      /* WINPROC_LISTBOX */
74     { MDIClientWndProcA, MDIClientWndProcW },  /* WINPROC_MDICLIENT */
75     { ScrollBarWndProcA, ScrollBarWndProcW },  /* WINPROC_SCROLLBAR */
76     { StaticWndProcA, StaticWndProcW },        /* WINPROC_STATIC */
77 };
78
79 static UINT builtin_used = NB_BUILTIN_WINPROCS;
80 static UINT winproc_used = BUILTIN_WINPROCS;
81
82 static CRITICAL_SECTION winproc_cs;
83 static CRITICAL_SECTION_DEBUG critsect_debug =
84 {
85     0, 0, &winproc_cs,
86     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
87       0, 0, { (DWORD_PTR)(__FILE__ ": winproc_cs") }
88 };
89 static CRITICAL_SECTION winproc_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
90
91 static inline void *get_buffer( void *static_buffer, size_t size, size_t need )
92 {
93     if (size >= need) return static_buffer;
94     return HeapAlloc( GetProcessHeap(), 0, need );
95 }
96
97 static inline void free_buffer( void *static_buffer, void *buffer )
98 {
99     if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
100 }
101
102 /* find an existing winproc for a given function and type */
103 /* FIXME: probably should do something more clever than a linear search */
104 static inline WINDOWPROC *find_winproc( WNDPROC funcA, WNDPROC funcW )
105 {
106     unsigned int i;
107
108     for (i = 0; i < builtin_used; i++)
109     {
110         /* match either proc, some apps confuse A and W */
111         if (funcA && winproc_array[i].procA != funcA && winproc_array[i].procW != funcA) continue;
112         if (funcW && winproc_array[i].procA != funcW && winproc_array[i].procW != funcW) continue;
113         return &winproc_array[i];
114     }
115     for (i = BUILTIN_WINPROCS; i < winproc_used; i++)
116     {
117         if (funcA && winproc_array[i].procA != funcA) continue;
118         if (funcW && winproc_array[i].procW != funcW) continue;
119         return &winproc_array[i];
120     }
121     return NULL;
122 }
123
124 /* return the window proc for a given handle, or NULL for an invalid handle,
125  * or WINPROC_PROC16 for a handle to a 16-bit proc. */
126 static inline WINDOWPROC *handle_to_proc( WNDPROC handle )
127 {
128     UINT index = LOWORD(handle);
129     if ((ULONG_PTR)handle >> 16 != WINPROC_HANDLE) return NULL;
130     if (index >= MAX_WINPROCS) return WINPROC_PROC16;
131     if (index >= winproc_used) return NULL;
132     return &winproc_array[index];
133 }
134
135 /* create a handle for a given window proc */
136 static inline WNDPROC proc_to_handle( WINDOWPROC *proc )
137 {
138     return (WNDPROC)(ULONG_PTR)((proc - winproc_array) | (WINPROC_HANDLE << 16));
139 }
140
141 /* allocate and initialize a new winproc */
142 static inline WINDOWPROC *alloc_winproc( WNDPROC funcA, WNDPROC funcW )
143 {
144     WINDOWPROC *proc;
145
146     /* check if the function is already a win proc */
147     if (funcA && (proc = handle_to_proc( funcA ))) return proc;
148     if (funcW && (proc = handle_to_proc( funcW ))) return proc;
149     if (!funcA && !funcW) return NULL;
150
151     EnterCriticalSection( &winproc_cs );
152
153     /* check if we already have a winproc for that function */
154     if (!(proc = find_winproc( funcA, funcW )))
155     {
156         if (funcA && funcW)
157         {
158             assert( builtin_used < BUILTIN_WINPROCS );
159             proc = &winproc_array[builtin_used++];
160             proc->procA = funcA;
161             proc->procW = funcW;
162             TRACE( "allocated %p for builtin %p/%p (%d/%d used)\n",
163                    proc_to_handle(proc), funcA, funcW, builtin_used, BUILTIN_WINPROCS );
164         }
165         else if (winproc_used < MAX_WINPROCS)
166         {
167             proc = &winproc_array[winproc_used++];
168             proc->procA = funcA;
169             proc->procW = funcW;
170             TRACE( "allocated %p for %c %p (%d/%d used)\n",
171                    proc_to_handle(proc), funcA ? 'A' : 'W', funcA ? funcA : funcW,
172                    winproc_used, MAX_WINPROCS );
173         }
174         else FIXME( "too many winprocs, cannot allocate one for %p/%p\n", funcA, funcW );
175     }
176     else TRACE( "reusing %p for %p/%p\n", proc_to_handle(proc), funcA, funcW );
177
178     LeaveCriticalSection( &winproc_cs );
179     return proc;
180 }
181
182 #ifdef __i386__
183 /* Some window procedures modify register they shouldn't, or are not
184  * properly declared stdcall; so we need a small assembly wrapper to
185  * call them. */
186 extern LRESULT WINPROC_wrapper( WNDPROC proc, HWND hwnd, UINT msg,
187                                 WPARAM wParam, LPARAM lParam );
188 __ASM_GLOBAL_FUNC( WINPROC_wrapper,
189                    "pushl %ebp\n\t"
190                    __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
191                    __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
192                    "movl %esp,%ebp\n\t"
193                    __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
194                    "pushl %edi\n\t"
195                    __ASM_CFI(".cfi_rel_offset %edi,-4\n\t")
196                    "pushl %esi\n\t"
197                    __ASM_CFI(".cfi_rel_offset %esi,-8\n\t")
198                    "pushl %ebx\n\t"
199                    __ASM_CFI(".cfi_rel_offset %ebx,-12\n\t")
200                    "subl $12,%esp\n\t"
201                    "pushl 24(%ebp)\n\t"
202                    "pushl 20(%ebp)\n\t"
203                    "pushl 16(%ebp)\n\t"
204                    "pushl 12(%ebp)\n\t"
205                    "movl 8(%ebp),%eax\n\t"
206                    "call *%eax\n\t"
207                    "leal -12(%ebp),%esp\n\t"
208                    "popl %ebx\n\t"
209                    __ASM_CFI(".cfi_same_value %ebx\n\t")
210                    "popl %esi\n\t"
211                    __ASM_CFI(".cfi_same_value %esi\n\t")
212                    "popl %edi\n\t"
213                    __ASM_CFI(".cfi_same_value %edi\n\t")
214                    "leave\n\t"
215                    __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
216                    __ASM_CFI(".cfi_same_value %ebp\n\t")
217                    "ret" )
218 #else
219 static inline LRESULT WINPROC_wrapper( WNDPROC proc, HWND hwnd, UINT msg,
220                                        WPARAM wParam, LPARAM lParam )
221 {
222     return proc( hwnd, msg, wParam, lParam );
223 }
224 #endif  /* __i386__ */
225
226 static WPARAM map_wparam_char_WtoA( WPARAM wParam, DWORD len )
227 {
228     WCHAR wch = wParam;
229     BYTE ch[2];
230
231     RtlUnicodeToMultiByteN( (LPSTR)ch, len, &len, &wch, sizeof(wch) );
232     if (len == 2)
233         return MAKEWPARAM( (ch[0] << 8) | ch[1], HIWORD(wParam) );
234     else
235         return MAKEWPARAM( ch[0], HIWORD(wParam) );
236 }
237
238 /* call a 32-bit window procedure */
239 static LRESULT call_window_proc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp, LRESULT *result, void *arg )
240 {
241     WNDPROC proc = arg;
242
243     USER_CheckNotLock();
244
245     hwnd = WIN_GetFullHandle( hwnd );
246     if (TRACE_ON(relay))
247         DPRINTF( "%04x:Call window proc %p (hwnd=%p,msg=%s,wp=%08lx,lp=%08lx)\n",
248                  GetCurrentThreadId(), proc, hwnd, SPY_GetMsgName(msg, hwnd), wp, lp );
249
250     *result = WINPROC_wrapper( proc, hwnd, msg, wp, lp );
251
252     if (TRACE_ON(relay))
253         DPRINTF( "%04x:Ret  window proc %p (hwnd=%p,msg=%s,wp=%08lx,lp=%08lx) retval=%08lx\n",
254                  GetCurrentThreadId(), proc, hwnd, SPY_GetMsgName(msg, hwnd), wp, lp, *result );
255     return *result;
256 }
257
258 /* call a 32-bit dialog procedure */
259 static LRESULT call_dialog_proc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp, LRESULT *result, void *arg )
260 {
261     WNDPROC proc = arg;
262     LRESULT ret;
263
264     USER_CheckNotLock();
265
266     hwnd = WIN_GetFullHandle( hwnd );
267     if (TRACE_ON(relay))
268         DPRINTF( "%04x:Call dialog proc %p (hwnd=%p,msg=%s,wp=%08lx,lp=%08lx)\n",
269                  GetCurrentThreadId(), proc, hwnd, SPY_GetMsgName(msg, hwnd), wp, lp );
270
271     ret = WINPROC_wrapper( proc, hwnd, msg, wp, lp );
272     *result = GetWindowLongPtrW( hwnd, DWLP_MSGRESULT );
273
274     if (TRACE_ON(relay))
275         DPRINTF( "%04x:Ret  dialog proc %p (hwnd=%p,msg=%s,wp=%08lx,lp=%08lx) retval=%08lx result=%08lx\n",
276                  GetCurrentThreadId(), proc, hwnd, SPY_GetMsgName(msg, hwnd), wp, lp, ret, *result );
277     return ret;
278 }
279
280
281 /**********************************************************************
282  *           WINPROC_GetProc
283  *
284  * Get a window procedure pointer that can be passed to the Windows program.
285  */
286 WNDPROC WINPROC_GetProc( WNDPROC proc, BOOL unicode )
287 {
288     WINDOWPROC *ptr = handle_to_proc( proc );
289
290     if (!ptr || ptr == WINPROC_PROC16) return proc;
291     if (unicode)
292     {
293         if (ptr->procW) return ptr->procW;
294         return proc;
295     }
296     else
297     {
298         if (ptr->procA) return ptr->procA;
299         return proc;
300     }
301 }
302
303
304 /**********************************************************************
305  *           WINPROC_AllocProc
306  *
307  * Allocate a window procedure for a window or class.
308  *
309  * Note that allocated winprocs are never freed; the idea is that even if an app creates a
310  * lot of windows, it will usually only have a limited number of window procedures, so the
311  * array won't grow too large, and this way we avoid the need to track allocations per window.
312  */
313 WNDPROC WINPROC_AllocProc( WNDPROC funcA, WNDPROC funcW )
314 {
315     WINDOWPROC *proc;
316
317     if (!(proc = alloc_winproc( funcA, funcW ))) return NULL;
318     if (proc == WINPROC_PROC16) return funcA ? funcA : funcW;
319     return proc_to_handle( proc );
320 }
321
322
323 /**********************************************************************
324  *           WINPROC_IsUnicode
325  *
326  * Return the window procedure type, or the default value if not a winproc handle.
327  */
328 BOOL WINPROC_IsUnicode( WNDPROC proc, BOOL def_val )
329 {
330     WINDOWPROC *ptr = handle_to_proc( proc );
331
332     if (!ptr) return def_val;
333     if (ptr == WINPROC_PROC16) return FALSE;  /* 16-bit is always A */
334     if (ptr->procA && ptr->procW) return def_val;  /* can be both */
335     return (ptr->procW != NULL);
336 }
337
338
339 /**********************************************************************
340  *           WINPROC_TestLBForStr
341  *
342  * Return TRUE if the lparam is a string
343  */
344 static inline BOOL WINPROC_TestLBForStr( HWND hwnd, UINT msg )
345 {
346     DWORD style = GetWindowLongA( hwnd, GWL_STYLE );
347     if (msg <= CB_MSGMAX)
348         return (!(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) || (style & CBS_HASSTRINGS));
349     else
350         return (!(style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE)) || (style & LBS_HASSTRINGS));
351
352 }
353
354
355 /**********************************************************************
356  *           WINPROC_CallProcAtoW
357  *
358  * Call a window procedure, translating args from Ansi to Unicode.
359  */
360 LRESULT WINPROC_CallProcAtoW( winproc_callback_t callback, HWND hwnd, UINT msg, WPARAM wParam,
361                               LPARAM lParam, LRESULT *result, void *arg, enum wm_char_mapping mapping )
362 {
363     LRESULT ret = 0;
364
365     TRACE_(msg)("(hwnd=%p,msg=%s,wp=%08lx,lp=%08lx)\n",
366                 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam);
367
368     switch(msg)
369     {
370     case WM_NCCREATE:
371     case WM_CREATE:
372         {
373             WCHAR *ptr, buffer[512];
374             CREATESTRUCTA *csA = (CREATESTRUCTA *)lParam;
375             CREATESTRUCTW csW = *(CREATESTRUCTW *)csA;
376             MDICREATESTRUCTW mdi_cs;
377             DWORD name_lenA = 0, name_lenW = 0, class_lenA = 0, class_lenW = 0;
378
379             if (!IS_INTRESOURCE(csA->lpszClass))
380             {
381                 class_lenA = strlen(csA->lpszClass) + 1;
382                 RtlMultiByteToUnicodeSize( &class_lenW, csA->lpszClass, class_lenA );
383             }
384             if (!IS_INTRESOURCE(csA->lpszName))
385             {
386                 name_lenA = strlen(csA->lpszName) + 1;
387                 RtlMultiByteToUnicodeSize( &name_lenW, csA->lpszName, name_lenA );
388             }
389
390             if (!(ptr = get_buffer( buffer, sizeof(buffer), class_lenW + name_lenW ))) break;
391
392             if (class_lenW)
393             {
394                 csW.lpszClass = ptr;
395                 RtlMultiByteToUnicodeN( ptr, class_lenW, NULL, csA->lpszClass, class_lenA );
396             }
397             if (name_lenW)
398             {
399                 csW.lpszName = ptr + class_lenW/sizeof(WCHAR);
400                 RtlMultiByteToUnicodeN( ptr + class_lenW/sizeof(WCHAR), name_lenW, NULL,
401                                         csA->lpszName, name_lenA );
402             }
403
404             if (GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
405             {
406                 mdi_cs = *(MDICREATESTRUCTW *)csA->lpCreateParams;
407                 mdi_cs.szTitle = csW.lpszName;
408                 mdi_cs.szClass = csW.lpszClass;
409                 csW.lpCreateParams = &mdi_cs;
410             }
411
412             ret = callback( hwnd, msg, wParam, (LPARAM)&csW, result, arg );
413             free_buffer( buffer, ptr );
414         }
415         break;
416
417     case WM_MDICREATE:
418         {
419             WCHAR *ptr, buffer[512];
420             DWORD title_lenA = 0, title_lenW = 0, class_lenA = 0, class_lenW = 0;
421             MDICREATESTRUCTA *csA = (MDICREATESTRUCTA *)lParam;
422             MDICREATESTRUCTW csW;
423
424             memcpy( &csW, csA, sizeof(csW) );
425
426             if (!IS_INTRESOURCE(csA->szTitle))
427             {
428                 title_lenA = strlen(csA->szTitle) + 1;
429                 RtlMultiByteToUnicodeSize( &title_lenW, csA->szTitle, title_lenA );
430             }
431             if (!IS_INTRESOURCE(csA->szClass))
432             {
433                 class_lenA = strlen(csA->szClass) + 1;
434                 RtlMultiByteToUnicodeSize( &class_lenW, csA->szClass, class_lenA );
435             }
436
437             if (!(ptr = get_buffer( buffer, sizeof(buffer), title_lenW + class_lenW ))) break;
438
439             if (title_lenW)
440             {
441                 csW.szTitle = ptr;
442                 RtlMultiByteToUnicodeN( ptr, title_lenW, NULL, csA->szTitle, title_lenA );
443             }
444             if (class_lenW)
445             {
446                 csW.szClass = ptr + title_lenW/sizeof(WCHAR);
447                 RtlMultiByteToUnicodeN( ptr + title_lenW/sizeof(WCHAR), class_lenW, NULL,
448                                         csA->szClass, class_lenA );
449             }
450             ret = callback( hwnd, msg, wParam, (LPARAM)&csW, result, arg );
451             free_buffer( buffer, ptr );
452         }
453         break;
454
455     case WM_GETTEXT:
456     case WM_ASKCBFORMATNAME:
457         {
458             WCHAR *ptr, buffer[512];
459             LPSTR str = (LPSTR)lParam;
460             DWORD len = wParam * sizeof(WCHAR);
461
462             if (!(ptr = get_buffer( buffer, sizeof(buffer), len ))) break;
463             ret = callback( hwnd, msg, wParam, (LPARAM)ptr, result, arg );
464             if (wParam)
465             {
466                 len = 0;
467                 if (*result)
468                     RtlUnicodeToMultiByteN( str, wParam - 1, &len, ptr, strlenW(ptr) * sizeof(WCHAR) );
469                 str[len] = 0;
470                 *result = len;
471             }
472             free_buffer( buffer, ptr );
473         }
474         break;
475
476     case LB_ADDSTRING:
477     case LB_INSERTSTRING:
478     case LB_FINDSTRING:
479     case LB_FINDSTRINGEXACT:
480     case LB_SELECTSTRING:
481     case CB_ADDSTRING:
482     case CB_INSERTSTRING:
483     case CB_FINDSTRING:
484     case CB_FINDSTRINGEXACT:
485     case CB_SELECTSTRING:
486         if (!lParam || !WINPROC_TestLBForStr( hwnd, msg ))
487         {
488             ret = callback( hwnd, msg, wParam, lParam, result, arg );
489             break;
490         }
491         /* fall through */
492     case WM_SETTEXT:
493     case WM_WININICHANGE:
494     case WM_DEVMODECHANGE:
495     case CB_DIR:
496     case LB_DIR:
497     case LB_ADDFILE:
498     case EM_REPLACESEL:
499         if (!lParam) ret = callback( hwnd, msg, wParam, lParam, result, arg );
500         else
501         {
502             WCHAR *ptr, buffer[512];
503             LPCSTR strA = (LPCSTR)lParam;
504             DWORD lenW, lenA = strlen(strA) + 1;
505
506             RtlMultiByteToUnicodeSize( &lenW, strA, lenA );
507             if ((ptr = get_buffer( buffer, sizeof(buffer), lenW )))
508             {
509                 RtlMultiByteToUnicodeN( ptr, lenW, NULL, strA, lenA );
510                 ret = callback( hwnd, msg, wParam, (LPARAM)ptr, result, arg );
511                 free_buffer( buffer, ptr );
512             }
513         }
514         break;
515
516     case LB_GETTEXT:
517     case CB_GETLBTEXT:
518         if (lParam && WINPROC_TestLBForStr( hwnd, msg ))
519         {
520             WCHAR buffer[512];  /* FIXME: fixed sized buffer */
521
522             ret = callback( hwnd, msg, wParam, (LPARAM)buffer, result, arg );
523             if (*result >= 0)
524             {
525                 DWORD len;
526                 RtlUnicodeToMultiByteN( (LPSTR)lParam, ~0u, &len,
527                                         buffer, (strlenW(buffer) + 1) * sizeof(WCHAR) );
528                 *result = len - 1;
529             }
530         }
531         else ret = callback( hwnd, msg, wParam, lParam, result, arg );
532         break;
533
534     case EM_GETLINE:
535         {
536             WCHAR *ptr, buffer[512];
537             WORD len = *(WORD *)lParam;
538
539             if (!(ptr = get_buffer( buffer, sizeof(buffer), len * sizeof(WCHAR) ))) break;
540             *((WORD *)ptr) = len;   /* store the length */
541             ret = callback( hwnd, msg, wParam, (LPARAM)ptr, result, arg );
542             if (*result)
543             {
544                 DWORD reslen;
545                 RtlUnicodeToMultiByteN( (LPSTR)lParam, len, &reslen, ptr, *result * sizeof(WCHAR) );
546                 if (reslen < len) ((LPSTR)lParam)[reslen] = 0;
547                 *result = reslen;
548             }
549             free_buffer( buffer, ptr );
550         }
551         break;
552
553     case WM_GETDLGCODE:
554         if (lParam)
555         {
556             MSG newmsg = *(MSG *)lParam;
557             if (map_wparam_AtoW( newmsg.message, &newmsg.wParam, WMCHAR_MAP_NOMAPPING ))
558                 ret = callback( hwnd, msg, wParam, (LPARAM)&newmsg, result, arg );
559         }
560         else ret = callback( hwnd, msg, wParam, lParam, result, arg );
561         break;
562
563     case WM_CHARTOITEM:
564     case WM_MENUCHAR:
565     case WM_CHAR:
566     case WM_DEADCHAR:
567     case WM_SYSCHAR:
568     case WM_SYSDEADCHAR:
569     case EM_SETPASSWORDCHAR:
570     case WM_IME_CHAR:
571         if (map_wparam_AtoW( msg, &wParam, mapping ))
572             ret = callback( hwnd, msg, wParam, lParam, result, arg );
573         break;
574
575     case WM_GETTEXTLENGTH:
576     case CB_GETLBTEXTLEN:
577     case LB_GETTEXTLEN:
578         ret = callback( hwnd, msg, wParam, lParam, result, arg );
579         if (*result >= 0)
580         {
581             WCHAR *ptr, buffer[512];
582             LRESULT tmp;
583             DWORD len = *result + 1;
584             /* Determine respective GETTEXT message */
585             UINT msgGetText = (msg == WM_GETTEXTLENGTH) ? WM_GETTEXT :
586                               ((msg == CB_GETLBTEXTLEN) ? CB_GETLBTEXT : LB_GETTEXT);
587             /* wParam differs between the messages */
588             WPARAM wp = (msg == WM_GETTEXTLENGTH) ? len : wParam;
589
590             if (!(ptr = get_buffer( buffer, sizeof(buffer), len * sizeof(WCHAR) ))) break;
591
592             if (callback == call_window_proc)  /* FIXME: hack */
593                 callback( hwnd, msgGetText, wp, (LPARAM)ptr, &tmp, arg );
594             else
595                 tmp = SendMessageW( hwnd, msgGetText, wp, (LPARAM)ptr );
596             RtlUnicodeToMultiByteSize( &len, ptr, tmp * sizeof(WCHAR) );
597             *result = len;
598             free_buffer( buffer, ptr );
599         }
600         break;
601
602     case WM_PAINTCLIPBOARD:
603     case WM_SIZECLIPBOARD:
604         FIXME_(msg)( "message %s (0x%x) needs translation, please report\n",
605                      SPY_GetMsgName(msg, hwnd), msg );
606         break;
607
608     default:
609         ret = callback( hwnd, msg, wParam, lParam, result, arg );
610         break;
611     }
612     return ret;
613 }
614
615
616 /**********************************************************************
617  *           WINPROC_CallProcWtoA
618  *
619  * Call a window procedure, translating args from Unicode to Ansi.
620  */
621 static LRESULT WINPROC_CallProcWtoA( winproc_callback_t callback, HWND hwnd, UINT msg, WPARAM wParam,
622                                      LPARAM lParam, LRESULT *result, void *arg )
623 {
624     LRESULT ret = 0;
625
626     TRACE_(msg)("(hwnd=%p,msg=%s,wp=%08lx,lp=%08lx)\n",
627                 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam);
628
629     switch(msg)
630     {
631     case WM_NCCREATE:
632     case WM_CREATE:
633         {
634             char buffer[1024], *cls;
635             CREATESTRUCTW *csW = (CREATESTRUCTW *)lParam;
636             CREATESTRUCTA csA = *(CREATESTRUCTA *)csW;
637             MDICREATESTRUCTA mdi_cs;
638             DWORD name_lenA = 0, name_lenW = 0, class_lenA = 0, class_lenW = 0;
639
640             if (!IS_INTRESOURCE(csW->lpszClass))
641             {
642                 class_lenW = (strlenW(csW->lpszClass) + 1) * sizeof(WCHAR);
643                 RtlUnicodeToMultiByteSize(&class_lenA, csW->lpszClass, class_lenW);
644             }
645             if (!IS_INTRESOURCE(csW->lpszName))
646             {
647                 name_lenW = (strlenW(csW->lpszName) + 1) * sizeof(WCHAR);
648                 RtlUnicodeToMultiByteSize(&name_lenA, csW->lpszName, name_lenW);
649             }
650
651             if (!(cls = get_buffer( buffer, sizeof(buffer), class_lenA + name_lenA ))) break;
652
653             if (class_lenA)
654             {
655                 RtlUnicodeToMultiByteN(cls, class_lenA, NULL, csW->lpszClass, class_lenW);
656                 csA.lpszClass = cls;
657             }
658             if (name_lenA)
659             {
660                 char *name = cls + class_lenA;
661                 RtlUnicodeToMultiByteN(name, name_lenA, NULL, csW->lpszName, name_lenW);
662                 csA.lpszName = name;
663             }
664
665             if (GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
666             {
667                 mdi_cs = *(MDICREATESTRUCTA *)csW->lpCreateParams;
668                 mdi_cs.szTitle = csA.lpszName;
669                 mdi_cs.szClass = csA.lpszClass;
670                 csA.lpCreateParams = &mdi_cs;
671             }
672
673             ret = callback( hwnd, msg, wParam, (LPARAM)&csA, result, arg );
674             free_buffer( buffer, cls );
675         }
676         break;
677
678     case WM_GETTEXT:
679     case WM_ASKCBFORMATNAME:
680         {
681             char *ptr, buffer[512];
682             DWORD len = wParam * 2;
683
684             if (!(ptr = get_buffer( buffer, sizeof(buffer), len ))) break;
685             ret = callback( hwnd, msg, wParam, (LPARAM)ptr, result, arg );
686             if (len)
687             {
688                 if (*result)
689                 {
690                     RtlMultiByteToUnicodeN( (LPWSTR)lParam, wParam*sizeof(WCHAR), &len, ptr, strlen(ptr)+1 );
691                     *result = len/sizeof(WCHAR) - 1;  /* do not count terminating null */
692                 }
693                 ((LPWSTR)lParam)[*result] = 0;
694             }
695             free_buffer( buffer, ptr );
696         }
697         break;
698
699     case LB_ADDSTRING:
700     case LB_INSERTSTRING:
701     case LB_FINDSTRING:
702     case LB_FINDSTRINGEXACT:
703     case LB_SELECTSTRING:
704     case CB_ADDSTRING:
705     case CB_INSERTSTRING:
706     case CB_FINDSTRING:
707     case CB_FINDSTRINGEXACT:
708     case CB_SELECTSTRING:
709         if (!lParam || !WINPROC_TestLBForStr( hwnd, msg ))
710         {
711             ret = callback( hwnd, msg, wParam, lParam, result, arg );
712             break;
713         }
714         /* fall through */
715     case WM_SETTEXT:
716     case WM_WININICHANGE:
717     case WM_DEVMODECHANGE:
718     case CB_DIR:
719     case LB_DIR:
720     case LB_ADDFILE:
721     case EM_REPLACESEL:
722         if (!lParam) ret = callback( hwnd, msg, wParam, lParam, result, arg );
723         else
724         {
725             char *ptr, buffer[512];
726             LPCWSTR strW = (LPCWSTR)lParam;
727             DWORD lenA, lenW = (strlenW(strW) + 1) * sizeof(WCHAR);
728
729             RtlUnicodeToMultiByteSize( &lenA, strW, lenW );
730             if ((ptr = get_buffer( buffer, sizeof(buffer), lenA )))
731             {
732                 RtlUnicodeToMultiByteN( ptr, lenA, NULL, strW, lenW );
733                 ret = callback( hwnd, msg, wParam, (LPARAM)ptr, result, arg );
734                 free_buffer( buffer, ptr );
735             }
736         }
737         break;
738
739     case WM_MDICREATE:
740         {
741             char *ptr, buffer[1024];
742             DWORD title_lenA = 0, title_lenW = 0, class_lenA = 0, class_lenW = 0;
743             MDICREATESTRUCTW *csW = (MDICREATESTRUCTW *)lParam;
744             MDICREATESTRUCTA csA;
745
746             memcpy( &csA, csW, sizeof(csA) );
747
748             if (!IS_INTRESOURCE(csW->szTitle))
749             {
750                 title_lenW = (strlenW(csW->szTitle) + 1) * sizeof(WCHAR);
751                 RtlUnicodeToMultiByteSize( &title_lenA, csW->szTitle, title_lenW );
752             }
753             if (!IS_INTRESOURCE(csW->szClass))
754             {
755                 class_lenW = (strlenW(csW->szClass) + 1) * sizeof(WCHAR);
756                 RtlUnicodeToMultiByteSize( &class_lenA, csW->szClass, class_lenW );
757             }
758
759             if (!(ptr = get_buffer( buffer, sizeof(buffer), title_lenA + class_lenA ))) break;
760
761             if (title_lenA)
762             {
763                 RtlUnicodeToMultiByteN( ptr, title_lenA, NULL, csW->szTitle, title_lenW );
764                 csA.szTitle = ptr;
765             }
766             if (class_lenA)
767             {
768                 RtlUnicodeToMultiByteN( ptr + title_lenA, class_lenA, NULL, csW->szClass, class_lenW );
769                 csA.szClass = ptr + title_lenA;
770             }
771             ret = callback( hwnd, msg, wParam, (LPARAM)&csA, result, arg );
772             free_buffer( buffer, ptr );
773         }
774         break;
775
776     case LB_GETTEXT:
777     case CB_GETLBTEXT:
778         if (lParam && WINPROC_TestLBForStr( hwnd, msg ))
779         {
780             char buffer[512];  /* FIXME: fixed sized buffer */
781
782             ret = callback( hwnd, msg, wParam, (LPARAM)buffer, result, arg );
783             if (*result >= 0)
784             {
785                 DWORD len;
786                 RtlMultiByteToUnicodeN( (LPWSTR)lParam, ~0u, &len, buffer, strlen(buffer) + 1 );
787                 *result = len / sizeof(WCHAR) - 1;
788             }
789         }
790         else ret = callback( hwnd, msg, wParam, lParam, result, arg );
791         break;
792
793     case EM_GETLINE:
794         {
795             char *ptr, buffer[512];
796             WORD len = *(WORD *)lParam;
797
798             if (!(ptr = get_buffer( buffer, sizeof(buffer), len * 2 ))) break;
799             *((WORD *)ptr) = len * 2;   /* store the length */
800             ret = callback( hwnd, msg, wParam, (LPARAM)ptr, result, arg );
801             if (*result)
802             {
803                 DWORD reslen;
804                 RtlMultiByteToUnicodeN( (LPWSTR)lParam, len*sizeof(WCHAR), &reslen, ptr, *result );
805                 *result = reslen / sizeof(WCHAR);
806                 if (*result < len) ((LPWSTR)lParam)[*result] = 0;
807             }
808             free_buffer( buffer, ptr );
809         }
810         break;
811
812     case WM_GETDLGCODE:
813         if (lParam)
814         {
815             MSG newmsg = *(MSG *)lParam;
816             switch(newmsg.message)
817             {
818             case WM_CHAR:
819             case WM_DEADCHAR:
820             case WM_SYSCHAR:
821             case WM_SYSDEADCHAR:
822                 newmsg.wParam = map_wparam_char_WtoA( newmsg.wParam, 1 );
823                 break;
824             case WM_IME_CHAR:
825                 newmsg.wParam = map_wparam_char_WtoA( newmsg.wParam, 2 );
826                 break;
827             }
828             ret = callback( hwnd, msg, wParam, (LPARAM)&newmsg, result, arg );
829         }
830         else ret = callback( hwnd, msg, wParam, lParam, result, arg );
831         break;
832
833     case WM_CHAR:
834         {
835             WCHAR wch = wParam;
836             char ch[2];
837             DWORD len;
838
839             RtlUnicodeToMultiByteN( ch, 2, &len, &wch, sizeof(wch) );
840             ret = callback( hwnd, msg, (BYTE)ch[0], lParam, result, arg );
841             if (len == 2) ret = callback( hwnd, msg, (BYTE)ch[1], lParam, result, arg );
842         }
843         break;
844
845     case WM_CHARTOITEM:
846     case WM_MENUCHAR:
847     case WM_DEADCHAR:
848     case WM_SYSCHAR:
849     case WM_SYSDEADCHAR:
850     case EM_SETPASSWORDCHAR:
851         ret = callback( hwnd, msg, map_wparam_char_WtoA(wParam,1), lParam, result, arg );
852         break;
853
854     case WM_IME_CHAR:
855         ret = callback( hwnd, msg, map_wparam_char_WtoA(wParam,2), lParam, result, arg );
856         break;
857
858     case WM_PAINTCLIPBOARD:
859     case WM_SIZECLIPBOARD:
860         FIXME_(msg)( "message %s (%04x) needs translation, please report\n",
861                      SPY_GetMsgName(msg, hwnd), msg );
862         break;
863
864     default:
865         ret = callback( hwnd, msg, wParam, lParam, result, arg );
866         break;
867     }
868
869     return ret;
870 }
871
872
873 /**********************************************************************
874  *              WINPROC_call_window
875  *
876  * Call the window procedure of the specified window.
877  */
878 BOOL WINPROC_call_window( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam,
879                           LRESULT *result, BOOL unicode, enum wm_char_mapping mapping )
880 {
881     struct user_thread_info *thread_info = get_user_thread_info();
882     WND *wndPtr;
883     WNDPROC func;
884     WINDOWPROC *proc;
885
886     if (!(wndPtr = WIN_GetPtr( hwnd ))) return FALSE;
887     if (wndPtr == WND_OTHER_PROCESS || wndPtr == WND_DESKTOP) return FALSE;
888     if (wndPtr->tid != GetCurrentThreadId())
889     {
890         WIN_ReleasePtr( wndPtr );
891         return FALSE;
892     }
893     func = wndPtr->winproc;
894     proc = handle_to_proc( wndPtr->winproc );
895     WIN_ReleasePtr( wndPtr );
896
897     if (!proc) return TRUE;
898
899     if (thread_info->recursion_count > MAX_WINPROC_RECURSION) return FALSE;
900     thread_info->recursion_count++;
901
902     if (unicode)
903     {
904         if (proc == WINPROC_PROC16)
905             WINPROC_CallProcWtoA( wow_handlers.call_window_proc, hwnd, msg, wParam, lParam, result, func );
906         else if (proc->procW)
907             call_window_proc( hwnd, msg, wParam, lParam, result, proc->procW );
908         else
909             WINPROC_CallProcWtoA( call_window_proc, hwnd, msg, wParam, lParam, result, proc->procA );
910     }
911     else
912     {
913         if (proc == WINPROC_PROC16)
914             wow_handlers.call_window_proc( hwnd, msg, wParam, lParam, result, func );
915         else if (proc->procA)
916             call_window_proc( hwnd, msg, wParam, lParam, result, proc->procA );
917         else
918             WINPROC_CallProcAtoW( call_window_proc, hwnd, msg, wParam, lParam, result, proc->procW, mapping );
919     }
920     thread_info->recursion_count--;
921     return TRUE;
922 }
923
924
925 /**********************************************************************
926  *              CallWindowProcA (USER32.@)
927  *
928  * The CallWindowProc() function invokes the windows procedure _func_,
929  * with _hwnd_ as the target window, the message specified by _msg_, and
930  * the message parameters _wParam_ and _lParam_.
931  *
932  * Some kinds of argument conversion may be done, I'm not sure what.
933  *
934  * CallWindowProc() may be used for windows subclassing. Use
935  * SetWindowLong() to set a new windows procedure for windows of the
936  * subclass, and handle subclassed messages in the new windows
937  * procedure. The new windows procedure may then use CallWindowProc()
938  * with _func_ set to the parent class's windows procedure to dispatch
939  * the message to the superclass.
940  *
941  * RETURNS
942  *
943  *    The return value is message dependent.
944  *
945  * CONFORMANCE
946  *
947  *   ECMA-234, Win32
948  */
949 LRESULT WINAPI CallWindowProcA(
950     WNDPROC func,  /* [in] window procedure */
951     HWND hwnd,     /* [in] target window */
952     UINT msg,      /* [in] message */
953     WPARAM wParam, /* [in] message dependent parameter */
954     LPARAM lParam  /* [in] message dependent parameter */
955 ) {
956     WINDOWPROC *proc;
957     LRESULT result;
958
959     if (!func) return 0;
960
961     if (!(proc = handle_to_proc( func )))
962         call_window_proc( hwnd, msg, wParam, lParam, &result, func );
963     else if (proc == WINPROC_PROC16)
964         wow_handlers.call_window_proc( hwnd, msg, wParam, lParam, &result, func );
965     else if (proc->procA)
966         call_window_proc( hwnd, msg, wParam, lParam, &result, proc->procA );
967     else
968         WINPROC_CallProcAtoW( call_window_proc, hwnd, msg, wParam, lParam, &result,
969                               proc->procW, WMCHAR_MAP_CALLWINDOWPROC );
970     return result;
971 }
972
973
974 /**********************************************************************
975  *              CallWindowProcW (USER32.@)
976  *
977  * See CallWindowProcA.
978  */
979 LRESULT WINAPI CallWindowProcW( WNDPROC func, HWND hwnd, UINT msg,
980                                   WPARAM wParam, LPARAM lParam )
981 {
982     WINDOWPROC *proc;
983     LRESULT result;
984
985     if (!func) return 0;
986
987     if (!(proc = handle_to_proc( func )))
988         call_window_proc( hwnd, msg, wParam, lParam, &result, func );
989     else if (proc == WINPROC_PROC16)
990         WINPROC_CallProcWtoA( wow_handlers.call_window_proc, hwnd, msg, wParam, lParam, &result, func );
991     else if (proc->procW)
992         call_window_proc( hwnd, msg, wParam, lParam, &result, proc->procW );
993     else
994         WINPROC_CallProcWtoA( call_window_proc, hwnd, msg, wParam, lParam, &result, proc->procA );
995     return result;
996 }
997
998
999 /**********************************************************************
1000  *              WINPROC_CallDlgProcA
1001  */
1002 INT_PTR WINPROC_CallDlgProcA( DLGPROC func, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1003 {
1004     WINDOWPROC *proc;
1005     LRESULT result;
1006     INT_PTR ret;
1007
1008     if (!func) return 0;
1009
1010     if (!(proc = handle_to_proc( func )))
1011         ret = call_dialog_proc( hwnd, msg, wParam, lParam, &result, func );
1012     else if (proc == WINPROC_PROC16)
1013     {
1014         ret = wow_handlers.call_dialog_proc( hwnd, msg, wParam, lParam, &result, func );
1015         SetWindowLongPtrW( hwnd, DWLP_MSGRESULT, result );
1016     }
1017     else if (proc->procW)
1018     {
1019         ret = WINPROC_CallProcAtoW( call_dialog_proc, hwnd, msg, wParam, lParam, &result,
1020                                     proc->procW, WMCHAR_MAP_CALLWINDOWPROC );
1021         SetWindowLongPtrW( hwnd, DWLP_MSGRESULT, result );
1022     }
1023     else
1024         ret = call_dialog_proc( hwnd, msg, wParam, lParam, &result, proc->procA );
1025     return ret;
1026 }
1027
1028
1029 /**********************************************************************
1030  *              WINPROC_CallDlgProcW
1031  */
1032 INT_PTR WINPROC_CallDlgProcW( DLGPROC func, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1033 {
1034     WINDOWPROC *proc;
1035     LRESULT result;
1036     INT_PTR ret;
1037
1038     if (!func) return 0;
1039
1040     if (!(proc = handle_to_proc( func )))
1041         ret = call_dialog_proc( hwnd, msg, wParam, lParam, &result, func );
1042     else if (proc == WINPROC_PROC16)
1043     {
1044         ret = WINPROC_CallProcWtoA( wow_handlers.call_dialog_proc, hwnd, msg, wParam, lParam, &result, func );
1045         SetWindowLongPtrW( hwnd, DWLP_MSGRESULT, result );
1046     }
1047     else if (proc->procA)
1048     {
1049         ret = WINPROC_CallProcWtoA( call_dialog_proc, hwnd, msg, wParam, lParam, &result, proc->procA );
1050         SetWindowLongPtrW( hwnd, DWLP_MSGRESULT, result );
1051     }
1052     else
1053         ret = call_dialog_proc( hwnd, msg, wParam, lParam, &result, proc->procW );
1054     return ret;
1055 }
1056
1057
1058 /***********************************************************************
1059  * Window procedures for builtin classes
1060  */
1061
1062 static LRESULT WINAPI ButtonWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1063 {
1064     return wow_handlers.button_proc( hwnd, msg, wParam, lParam, FALSE );
1065 }
1066
1067 static LRESULT WINAPI ButtonWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1068 {
1069     return wow_handlers.button_proc( hwnd, msg, wParam, lParam, TRUE );
1070 }
1071
1072 static LRESULT WINAPI ComboWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1073 {
1074     return wow_handlers.combo_proc( hwnd, message, wParam, lParam, FALSE );
1075 }
1076
1077 static LRESULT WINAPI ComboWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1078 {
1079     return wow_handlers.combo_proc( hwnd, message, wParam, lParam, TRUE );
1080 }
1081
1082 LRESULT WINAPI EditWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1083 {
1084     return wow_handlers.edit_proc( hwnd, msg, wParam, lParam, FALSE );
1085 }
1086
1087 static LRESULT WINAPI EditWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1088 {
1089     return wow_handlers.edit_proc( hwnd, msg, wParam, lParam, TRUE );
1090 }
1091
1092 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1093 {
1094     return wow_handlers.listbox_proc( hwnd, msg, wParam, lParam, FALSE );
1095 }
1096
1097 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1098 {
1099     return wow_handlers.listbox_proc( hwnd, msg, wParam, lParam, TRUE );
1100 }
1101
1102 static LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1103 {
1104     return wow_handlers.mdiclient_proc( hwnd, msg, wParam, lParam, FALSE );
1105 }
1106
1107 static LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1108 {
1109     return wow_handlers.mdiclient_proc( hwnd, msg, wParam, lParam, TRUE );
1110 }
1111
1112 static LRESULT WINAPI ScrollBarWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1113 {
1114     return wow_handlers.scrollbar_proc( hwnd, msg, wParam, lParam, FALSE );
1115 }
1116
1117 static LRESULT WINAPI ScrollBarWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1118 {
1119     return wow_handlers.scrollbar_proc( hwnd, msg, wParam, lParam, TRUE );
1120 }
1121
1122 static LRESULT WINAPI StaticWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1123 {
1124     return wow_handlers.static_proc( hwnd, msg, wParam, lParam, FALSE );
1125 }
1126
1127 static LRESULT WINAPI StaticWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1128 {
1129     return wow_handlers.static_proc( hwnd, msg, wParam, lParam, TRUE );
1130 }
1131
1132
1133 /**********************************************************************
1134  *              UserRegisterWowHandlers (USER32.@)
1135  *
1136  * NOTE: no attempt has been made to be compatible here,
1137  * the Windows function is most likely completely different.
1138  */
1139 void WINAPI UserRegisterWowHandlers( const struct wow_handlers16 *new, struct wow_handlers32 *orig )
1140 {
1141     orig->button_proc    = ButtonWndProc_common;
1142     orig->combo_proc     = ComboWndProc_common;
1143     orig->edit_proc      = EditWndProc_common;
1144     orig->listbox_proc   = ListBoxWndProc_common;
1145     orig->mdiclient_proc = MDIClientWndProc_common;
1146     orig->scrollbar_proc = ScrollBarWndProc_common;
1147     orig->static_proc    = StaticWndProc_common;
1148     orig->alloc_winproc  = WINPROC_AllocProc;
1149
1150     wow_handlers = *new;
1151 }
1152
1153 struct wow_handlers16 wow_handlers =
1154 {
1155     ButtonWndProc_common,
1156     ComboWndProc_common,
1157     EditWndProc_common,
1158     ListBoxWndProc_common,
1159     MDIClientWndProc_common,
1160     ScrollBarWndProc_common,
1161     StaticWndProc_common,
1162     NULL,  /* call_window_proc */
1163     NULL   /* call_dialog_proc */
1164 };