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