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