Inter-thread SendMessage() bugfixes:
[wine] / libtest / guitest.c
1 /* Windows GUI Behaviour Tester */
2 /* by Ove Kåven */
3
4 #include <stdio.h>
5 #include <string.h>
6 #include <stdarg.h>
7 #include <windows.h>
8
9 #include "guitest.rh"
10
11 /* checks to include */
12 #define LOGGING /* can be undefined under Wine and use -debugmsg +message instead */
13 #define MAIN_STYLE WS_OVERLAPPEDWINDOW
14 #undef TEST_DESTROY_MAIN
15 #undef SHOW_SUB
16 #define TEST_DIALOG
17 #define RESIZE_DIALOG
18 #undef TEST_SUBDIALOG
19 #undef TEST_COMMCTL
20
21 /************************/
22 /*** GLOBAL VARIABLES ***/
23 /************************/
24
25 HINSTANCE hInst;
26 DWORD StartTime;
27 HWND hListBox,hMainWnd,hSubWnd;
28 HWND hButton[4]={0,0,0,0};
29 HWND hDialog=0,hGroup=0,hSubDlg=0;
30 WNDPROC wndButton[4],wndDialog,wndGroup,wndSubDlg;
31 BOOL Clicked=0,Ready=0;
32 int State=0,Rec=0;
33 #define STATE_CREATE 0
34 #define STATE_DESTROY 1
35 #define STATE_SHOW 2
36 #define STATE_UPDATE 3
37 #define STATE_DIALOG 4
38 #define STATE_TEST 5
39 #define STATE_DIRECT 6
40 #define STATE_DISPATCH 7
41 #define STATE_RECURS 8
42 char*StateName[]={
43  "Creat",
44  "Destr",
45  "Show ",
46  "Updat",
47  "Dialg",
48  "Test ",
49  "Call ",
50  "Disp ",
51  "RCall"
52 };
53
54 static char wclassname[] = "GUITestClass";
55 static char wcclassname[] = "GUITestChildClass";
56 static char winname[] = "GUITest";
57
58 /**************************/
59 /*** LOGGING FACILITIES ***/
60 /**************************/
61
62 struct MSGNAMES {
63  int msg;
64  char*name;
65 } MsgNames[]={
66 #define MSG(x) {x,#x},
67 #define MSG2(x,y) {y,#x},
68 #define ENDMSG {0}
69
70 /* we get these in CreateWindow */
71 MSG(WM_GETMINMAXINFO)
72 MSG(WM_NCCREATE)
73 MSG(WM_NCCALCSIZE)
74 MSG(WM_CREATE)
75 MSG(WM_PARENTNOTIFY)
76
77 /* we get these in ShowWindow */
78 MSG(WM_SHOWWINDOW)
79 MSG(WM_WINDOWPOSCHANGING)
80 MSG(WM_QUERYNEWPALETTE)
81 MSG(WM_ACTIVATEAPP)
82 MSG(WM_NCACTIVATE)
83 MSG(WM_GETTEXT)
84 MSG(WM_ACTIVATE)
85 MSG(WM_SETFOCUS)
86 MSG(WM_NCPAINT)
87 MSG(WM_ERASEBKGND)
88 MSG(WM_WINDOWPOSCHANGED)
89 MSG(WM_SIZE)
90 MSG(WM_MOVE)
91
92 /* we get these in DestroyWindow */
93 MSG(WM_KILLFOCUS)
94 MSG(WM_DESTROY)
95 MSG(WM_NCDESTROY)
96
97 /* we get these directly sent */
98 MSG(WM_NCHITTEST)
99 MSG(WM_SETCURSOR)
100 MSG(WM_MOUSEACTIVATE)
101 MSG(WM_CHILDACTIVATE)
102 MSG(WM_COMMAND)
103 MSG(WM_SYSCOMMAND)
104
105 /* posted events */
106 MSG(WM_MOUSEMOVE)
107 MSG(WM_NCMOUSEMOVE)
108 MSG(WM_PAINT)
109 MSG(WM_LBUTTONDOWN)
110 MSG(WM_LBUTTONUP)
111 MSG(WM_NCLBUTTONDOWN)
112 MSG(WM_NCLBUTTONUP)
113
114 MSG(WM_KEYDOWN)
115 MSG(WM_KEYUP)
116 MSG(WM_CHAR)
117
118 #ifdef WIN32
119 MSG(WM_CTLCOLORBTN)
120 MSG(WM_CTLCOLORDLG)
121 MSG(WM_CTLCOLORSTATIC)
122 #else
123 MSG(WM_CTLCOLOR)
124 #endif
125
126 /* moving and sizing */
127 MSG2(WM_ENTERSIZEMOVE,0x0231)
128 MSG2(WM_EXITSIZEMOVE,0x0232)
129 #ifdef WIN32
130 MSG(WM_SIZING)
131 #endif
132
133 /* menus/dialog boxes */
134 MSG(WM_CANCELMODE)
135 MSG(WM_ENABLE)
136 MSG(WM_SETFONT)
137 MSG(WM_INITDIALOG)
138 MSG(WM_GETDLGCODE)
139 MSG(WM_ENTERIDLE)
140
141 /* getting these from Wine but not from Windows */
142 MSG2(WM_SETVISIBLE,0x0009) /* unheard of in BC++ 4.52 */
143 #ifdef WIN32
144 MSG(WM_CAPTURECHANGED)
145 #endif
146
147 ENDMSG};
148
149 struct MSGNAMES ButMsgs[]={
150 MSG(BM_SETSTATE)
151 MSG(BM_SETSTYLE)
152
153 ENDMSG};
154
155 char*MsgName(UINT msg,HWND hWnd)
156 {
157  int i;
158  static char buffer[64],wclass[64];
159  GetClassName(hWnd,wclass,sizeof(wclass));
160
161 #define MSGSEARCH(msgs) { \
162   for (i=0; msgs[i].name&&msgs[i].msg!=msg; i++); \
163   if (msgs[i].name) return msgs[i].name; \
164  }
165
166  if (!stricmp(wclass,"Button")) MSGSEARCH(ButMsgs);
167  MSGSEARCH(MsgNames);
168  /* WM_USER */
169  if (msg>=WM_USER) {
170   sprintf(buffer,"WM_USER+%04x{%s}",msg-WM_USER,wclass);
171   return buffer;
172  }
173  /* message not found */
174  sprintf(buffer,"%04x{%s}",msg,wclass);
175  return buffer;
176 }
177
178 char*WndName(HWND hWnd,int state=State)
179 {
180  static char buffer[16];
181  if (!hWnd) return "0000";
182  if (hWnd==hMainWnd || (state==STATE_CREATE && !hMainWnd)) return "main";
183  if (hWnd==hSubWnd || (state==STATE_CREATE && !hSubWnd)) return "chld";
184  if (hWnd==hDialog || (state==STATE_DIALOG && !hDialog)) return "tdlg";
185  if (hWnd==hGroup) return "tgrp";
186  if (hWnd==hButton[0]) return "but1";
187  if (hWnd==hButton[1]) return "but2";
188  if (hWnd==hButton[2]) return "but3";
189  if (hWnd==hButton[3]) return "but4";
190  if (hWnd==hSubDlg || (state==STATE_CREATE && !hSubDlg)) return "sdlg";
191  if (hDialog) {
192   int id=GetDlgCtrlID(hWnd);
193   if (id) {
194    sprintf(buffer,"dlgitem(%d)",id);
195    return buffer;
196   }
197  }
198  sprintf(buffer,"%04x",hWnd);
199  return buffer;
200 }
201
202 void Log(const char*fmt)
203 {
204 #ifdef LOGGING
205  if (!Clicked) SendMessage(hListBox,LB_ADDSTRING,0,(LPARAM)fmt);
206 #endif
207 }
208
209 void Logf(const char*fmt,...)
210 {
211  va_list par;
212  static char buffer[256];
213
214  va_start(par,fmt);
215  vsprintf(buffer,fmt,par);
216  va_end(par);
217  Log(buffer);
218 }
219
220 void LogChildOrder(HWND hWnd)
221 {
222  HWND hWndChild = GetWindow(hWnd,GW_CHILD);
223  char*name;
224  static char buffer[256];
225
226  strcpy(buffer,"child list:");
227  while (hWndChild) {
228   strcat(strcat(buffer," "),WndName(hWndChild));
229   hWndChild=GetWindow(hWndChild,GW_HWNDNEXT);
230  }
231  Log(buffer);
232 }
233
234 void LogMessage(int state,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam,char*name=NULL)
235 {
236  static char buffer[256];
237  DWORD tick=GetTickCount()-StartTime;
238  char*msgname=MsgName(msg,hWnd);
239  if (!name) name=WndName(hWnd,state);
240  switch (msg) {
241   case WM_SETFOCUS:
242   case WM_KILLFOCUS:
243   case WM_SETCURSOR:
244    Logf("%04d[%s(%d):%s]%s(%s,%08x)",tick,StateName[state],Rec,
245         name,msgname,WndName((HWND)wParam),lParam);
246    break;
247 #ifdef WIN32
248   case WM_ENTERIDLE:
249   case WM_CTLCOLORBTN:
250   case WM_CTLCOLORDLG:
251    Logf("%04d[%s(%d):%s]%s(%08x,%s)",tick,StateName[state],Rec,
252         name,msgname,wParam,WndName((HWND)lParam));
253    break;
254 #else
255   case WM_ENTERIDLE:
256   case WM_CTLCOLOR:
257    Logf("%04d[%s(%d):%s]%s(%08x,%04x:%s)",tick,StateName[state],Rec,
258         name,msgname,wParam,HIWORD(lParam),WndName((HWND)LOWORD(lParam)));
259    break;
260 #endif
261   case WM_WINDOWPOSCHANGING:
262   case WM_WINDOWPOSCHANGED:
263    {
264     WINDOWPOS*pos=(WINDOWPOS*)lParam;
265 #ifdef WIN32
266     Logf("%04d[%s(%d):%s]%s(%08x,%p)",tick,StateName[state],Rec,
267          name,msgname,wParam,pos);
268 #else
269     Logf("%04d[%s(%d):%s]%s(%04x,%p)",tick,StateName[state],Rec,
270          name,msgname,wParam,pos);
271 #endif
272     strcpy(buffer,"FLAGS:");
273     if (pos->flags&SWP_DRAWFRAME) strcat(buffer," DRAWFRAME");
274     if (pos->flags&SWP_HIDEWINDOW) strcat(buffer," HIDEWINDOW");
275     if (pos->flags&SWP_NOACTIVATE) strcat(buffer," NOACTIVATE");
276     if (pos->flags&SWP_NOCOPYBITS) strcat(buffer," NOCOPYBITS");
277     if (pos->flags&SWP_NOMOVE) strcat(buffer," NOMOVE");
278     if (pos->flags&SWP_NOOWNERZORDER) strcat(buffer," NOOWNERZORDER");
279     if (pos->flags&SWP_NOSIZE) strcat(buffer," NOSIZE");
280     if (pos->flags&SWP_NOREDRAW) strcat(buffer," NOREDRAW");
281     if (pos->flags&SWP_NOZORDER) strcat(buffer," NOZORDER");
282     if (pos->flags&SWP_SHOWWINDOW) strcat(buffer," SHOWWINDOW");
283     Log(buffer);
284    }
285    break;
286   default:
287 #ifdef WIN32
288    Logf("%04d[%s(%d):%s]%s(%08x,%08x)",tick,StateName[state],Rec,
289         name,msgname,wParam,lParam);
290 #else
291    Logf("%04d[%s(%d):%s]%s(%04x,%08x)",tick,StateName[state],Rec,
292         name,msgname,wParam,lParam);
293 #endif
294  }
295 }
296
297 /***************************/
298 /*** GRAPHICS FACILITIES ***/
299 /***************************/
300
301 void Paint(HWND hWnd)
302 {
303  HDC dc;
304  PAINTSTRUCT ps;
305  dc=BeginPaint(hWnd,&ps);
306  EndPaint(hWnd,&ps);
307 }
308
309 void FillPattern(HWND hWnd,HDC pdc=0)
310 {
311  HDC dc=pdc?pdc:GetDC(hWnd);
312  HBRUSH oldbrush;
313  RECT rect;
314  if (!dc) {
315   Logf("failed to acquire DC for window %s",WndName(hWnd));
316   return;
317  } else {
318   Logf("acquired DC for %s window %s, painting",
319        IsWindowVisible(hWnd)?"visible":"invisible",WndName(hWnd));
320  }
321  GetClientRect(hWnd,&rect);
322  oldbrush=SelectObject(dc,GetStockObject(LTGRAY_BRUSH));
323  PatBlt(dc,0,0,rect.right,rect.bottom,PATCOPY);
324  SelectObject(dc,oldbrush);
325  if (!pdc) ReleaseDC(hWnd,dc);
326 }
327
328 void PaintPattern(HWND hWnd)
329 {
330  HDC dc;
331  PAINTSTRUCT ps;
332  dc=BeginPaint(hWnd,&ps);
333  FillPattern(hWnd,dc);
334  EndPaint(hWnd,&ps);
335 }
336
337 /*************************/
338 /*** WINDOW PROCEDURES ***/
339 /*************************/
340
341 /* MAIN WINDOW */
342 LRESULT FAR CALLBACK _export MainWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
343 {
344  LRESULT lResult=0;
345  RECT rect;
346  int OldState=State;
347
348  State=STATE_RECURS; Rec++;
349  if (!Clicked) LogMessage(OldState,hWnd,msg,wParam,lParam);
350  switch (msg) {
351   case WM_NCHITTEST:
352    lResult=DefWindowProc(hWnd,msg,wParam,lParam);
353    break;
354   case WM_LBUTTONDOWN:
355   case WM_CHAR:
356    if (!Clicked) {
357     SetParent(hListBox,hWnd);
358     GetClientRect(hWnd,&rect);
359     MoveWindow(hListBox,0,0,rect.right,rect.bottom,TRUE);
360     ShowWindow(hListBox,SW_SHOW);
361     SetFocus(hListBox);
362     Clicked=TRUE;
363    }
364    break;
365   case WM_SIZE:
366    GetClientRect(hWnd,&rect);
367    if (Clicked) {
368     MoveWindow(hListBox,0,0,rect.right,rect.bottom,TRUE);
369    }
370    MoveWindow(hSubWnd,0,rect.bottom/2,rect.right,rect.bottom-(rect.bottom/2),TRUE);
371    break;
372   case WM_PAINT:
373    Paint(hWnd);
374    break;
375   case WM_DESTROY:
376    PostQuitMessage(0);
377    break;
378   default:
379    lResult=DefWindowProc(hWnd,msg,wParam,lParam);
380  }
381  State=OldState; Rec--;
382  return lResult;
383 }
384
385 /* CHILD WINDOW */
386 LRESULT FAR CALLBACK _export SubWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
387 {
388  LRESULT lResult=0;
389  RECT rect;
390  int OldState=State;
391
392  State=STATE_RECURS; Rec++;
393  if (!Clicked) LogMessage(OldState,hWnd,msg,wParam,lParam);
394  switch (msg) {
395   case WM_PAINT:
396    Paint(hWnd);
397    break;
398   default:
399    lResult=DefWindowProc(hWnd,msg,wParam,lParam);
400  }
401  State=OldState; Rec--;
402  return lResult;
403 }
404
405 BOOL FAR CALLBACK _export SubDialogProc(HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
406
407 /* SUBCLASSED CONTROLS */
408 LRESULT FAR CALLBACK _export SubClassWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
409 {
410  LRESULT lResult=0;
411  RECT rect;
412  int OldState=State;
413  int But=-1;
414
415  if (hWnd==hButton[0]) But=0; else
416  if (hWnd==hButton[1]) But=1; else
417  if (hWnd==hButton[2]) But=2; else
418  if (hWnd==hButton[3]) But=3;
419
420  State=STATE_RECURS; Rec++;
421  if (!Clicked) {
422   LogMessage(OldState,hWnd,msg,wParam,lParam);
423   if (But!=-1) {
424    lResult=CallWindowProc((FARPROC)wndButton[But],hWnd,msg,wParam,lParam);
425    if (msg==WM_LBUTTONUP) {
426     LogChildOrder(GetParent(hWnd));
427    }
428   }
429   else if (hWnd==hDialog) {
430    lResult=CallWindowProc((FARPROC)wndDialog,hWnd,msg,wParam,lParam);
431   }
432   else if (hWnd==hSubDlg) {
433    lResult=CallWindowProc((FARPROC)wndSubDlg,hWnd,msg,wParam,lParam);
434   }
435   else if (hWnd==hGroup) {
436    lResult=CallWindowProc((FARPROC)wndGroup,hWnd,msg,wParam,lParam);
437    if (msg==WM_SETFOCUS) {
438     /* create subdialog */
439     if (hSubDlg) {
440 #if 0
441      SetRect(&rect,0,0,1,1);
442      InvalidateRect(hWnd,&rect,FALSE);
443 #endif
444     } else {
445 #ifdef TEST_SUBDIALOG
446      State=STATE_CREATE;
447      hSubDlg=CreateDialog(hInst,MAKEINTRESOURCE(2),hWnd,(FARPROC)SubDialogProc);
448      State=STATE_RECURS;
449 #else
450 #ifdef RESIZE_DIALOG
451      GetWindowRect(GetParent(hWnd),&rect);
452      rect.right++;
453      SetWindowPos(GetParent(hWnd),0,0,0,
454                   rect.right-rect.left,rect.bottom-rect.top,
455                   SWP_NOMOVE|SWP_NOZORDER);
456 #endif
457 #endif
458     }
459    }
460   }
461  }
462  State=OldState; Rec--;
463  return lResult;
464 }
465
466 /* MAIN DIALOG PROCEDURE */
467 BOOL FAR CALLBACK _export TestDialogProc(HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
468 {
469  BOOL bResult=0;
470  RECT rect;
471  int OldState=State;
472  int But=-1;
473
474  State=STATE_RECURS; Rec++;
475  if (!Clicked) LogMessage(OldState,hWndDlg,msg,wParam,lParam,"dlgp");
476  switch (msg) {
477   case WM_INITDIALOG:
478    hDialog = hWndDlg;
479    /* subclass dialog window proc */
480    wndDialog = (WNDPROC)SetWindowLong(hDialog,GWL_WNDPROC,(LONG)SubClassWindowProc);
481    Logf("dialog visible=%s",IsWindowVisible(hWndDlg)?"TRUE":"FALSE");
482    /* subclass OK button */
483    hButton[3] = GetDlgItem(hWndDlg,IDOK);
484    wndButton[3] = (WNDPROC)SetWindowLong(hButton[3],GWL_WNDPROC,(LONG)SubClassWindowProc);
485    /* subclass group box */
486    hGroup = GetDlgItem(hWndDlg,IDC_GROUPBOX1);
487    wndGroup = (WNDPROC)SetWindowLong(hGroup,GWL_WNDPROC,(LONG)SubClassWindowProc);
488
489 #ifdef RESIZE_DIALOG
490    GetWindowRect(hWndDlg,&rect);
491    rect.right--;
492    SetWindowPos(hWndDlg,0,0,0,
493                 rect.right-rect.left,rect.bottom-rect.top,
494                 SWP_NOMOVE|SWP_NOZORDER);
495 //   ShowWindow(GetDlgItem(hWndDlg,IDCANCEL),SW_HIDE);
496 #endif
497
498    bResult=TRUE; /* we don't do SetFocus */
499    break;
500   case WM_PAINT:
501    PaintPattern(hWndDlg);
502    bResult=TRUE;
503    break;
504   case WM_COMMAND:
505    EndDialog(hWndDlg,LOWORD(wParam));
506    bResult=TRUE;
507    break;
508   case WM_CLOSE:
509    EndDialog(hWndDlg,IDCANCEL);
510    bResult=TRUE;
511    break;
512   case WM_NCDESTROY:
513    hDialog = 0;
514    break;
515  }
516  State=OldState; Rec--;
517  return bResult;
518 }
519
520 /* SUBDIALOG PROCEDURE */
521 BOOL FAR CALLBACK _export SubDialogProc(HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
522 {
523  BOOL bResult=0;
524  RECT rect;
525  int OldState=State;
526  int But=-1;
527
528  State=STATE_RECURS; Rec++;
529  if (!Clicked) LogMessage(OldState,hWndDlg,msg,wParam,lParam);
530  switch (msg) {
531   case WM_INITDIALOG:
532    hSubDlg = hWndDlg;
533    /* subclass dialog window proc */
534    wndSubDlg = (WNDPROC)SetWindowLong(hDialog,GWL_WNDPROC,(LONG)SubClassWindowProc);
535
536    bResult=TRUE; /* we don't do SetFocus */
537    break;
538   case WM_NCDESTROY:
539    hSubDlg = 0;
540    break;
541  }
542  State=OldState; Rec--;
543  return bResult;
544 }
545
546 /********************/
547 /*** MAIN PROGRAM ***/
548 /********************/
549
550 BOOL AppInit(void)
551 {
552  WNDCLASS wclass;
553
554  wclass.style = CS_HREDRAW|CS_VREDRAW;
555  wclass.lpfnWndProc = MainWindowProc;
556  wclass.cbClsExtra = 0;
557  wclass.cbWndExtra = 0;
558  wclass.hInstance = hInst;
559  wclass.hIcon = LoadIcon(hInst,MAKEINTRESOURCE(1));
560  wclass.hCursor = LoadCursor(0,IDC_ARROW);
561  wclass.hbrBackground = GetStockObject(WHITE_BRUSH);
562  wclass.lpszMenuName = NULL;
563  wclass.lpszClassName = wclassname;
564  if (!RegisterClass(&wclass)) return FALSE;
565  wclass.lpfnWndProc = SubWindowProc;
566  wclass.lpszClassName = wcclassname;
567  if (!RegisterClass(&wclass)) return FALSE;
568  return TRUE;
569 }
570
571 int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
572                    LPSTR lpszCmdLine, int nCmdShow)
573 {
574  MSG msg;
575  RECT rect;
576
577  hInst = hInstance;
578  if (!hPrevInstance)
579   if (!AppInit())
580    return 0;
581
582  StartTime=GetTickCount();
583  hListBox = CreateWindow("LISTBOX","Messages",WS_BORDER|WS_VSCROLL|WS_CHILD|
584                                               LBS_HASSTRINGS|LBS_NOTIFY|LBS_WANTKEYBOARDINPUT,
585                          0,0,0,0,GetDesktopWindow(),0,hInst,0);
586  if (!hListBox) {
587   MessageBox(0,"Could not create list box","Error",MB_OK);
588  }
589
590  State=STATE_CREATE;
591  hMainWnd = CreateWindow(wclassname,winname,MAIN_STYLE,
592                          CW_USEDEFAULT,0,400,300,0,0,hInst,0);
593  if (!hMainWnd) return 0;
594  State=STATE_SHOW;
595  ShowWindow(hMainWnd,nCmdShow);
596 #ifdef TEST_DESTROY_MAIN
597  State=STATE_DESTROY;
598  DestroyWindow(hMainWnd);
599  State=STATE_DIRECT;
600  while (GetMessage(&msg,0,0,0)) {
601   TranslateMessage(&msg);
602   State=STATE_DISPATCH;
603   DispatchMessage(&msg);
604   State=STATE_DIRECT;
605  }
606  State=STATE_CREATE;
607  hMainWnd = CreateWindow(wclassname,winname,MAIN_STYLE,
608                          CW_USEDEFAULT,0,400,300,0,0,hInst,0);
609  if (!hMainWnd) return 0;
610  State=STATE_SHOW;
611  ShowWindow(hMainWnd,nCmdShow);
612 #endif
613 /* update, so no WM_PAINTs are pending */
614  State=STATE_UPDATE;
615 // UpdateWindow(hMainWnd);
616  Ready=TRUE;
617 /* fill client area with a pattern */
618  FillPattern(hMainWnd);
619 /* create subwindow */
620  State=STATE_CREATE;
621  GetClientRect(hMainWnd,&rect);
622  hSubWnd = CreateWindow(wcclassname,winname,WS_CHILD|WS_BORDER|WS_CLIPSIBLINGS,
623                         0,rect.bottom/2,rect.right,rect.bottom-(rect.bottom/2),hMainWnd,0,hInst,0);
624  if (!hSubWnd) return 0;
625 /* create buttons */
626  hButton[0] = CreateWindow("BUTTON","1",WS_CHILD|WS_CLIPSIBLINGS|WS_VISIBLE,
627                            8,8,48,20,hMainWnd,0,hInst,0);
628  hButton[1] = CreateWindow("BUTTON","2",WS_CHILD|WS_CLIPSIBLINGS|WS_VISIBLE,
629                            32,12,48,20,hMainWnd,0,hInst,0);
630  hButton[2] = CreateWindow("BUTTON","3",WS_CHILD|WS_CLIPSIBLINGS|WS_VISIBLE,
631                            56,16,48,20,hMainWnd,0,hInst,0);
632 /* subclass them */
633  wndButton[0] = (WNDPROC)SetWindowLong(hButton[0],GWL_WNDPROC,(LONG)SubClassWindowProc);
634  wndButton[1] = (WNDPROC)SetWindowLong(hButton[1],GWL_WNDPROC,(LONG)SubClassWindowProc);
635  wndButton[2] = (WNDPROC)SetWindowLong(hButton[2],GWL_WNDPROC,(LONG)SubClassWindowProc);
636 /* show them */
637  State=STATE_UPDATE;
638  UpdateWindow(hButton[0]);
639  LogChildOrder(hMainWnd);
640  Logf("but1 visible=%d",IsWindowVisible(hButton[0]));
641
642 /* now reparent the button to our (invisible) subwindow */
643  State=STATE_TEST;
644  /* in different order, seeing who gets topmost */
645  SetParent(hButton[0],hSubWnd);
646  SetParent(hButton[2],hSubWnd);
647  SetParent(hButton[1],hSubWnd);
648  LogChildOrder(hSubWnd);
649 /* the button should now be invisible */
650  Logf("but1 visible=%d",IsWindowVisible(hButton[0]));
651 /* see if we can draw on them */
652  FillPattern(hButton[0]);
653
654 #ifdef SHOW_SUB
655  State=STATE_SHOW;
656  ShowWindow(hSubWnd,SW_SHOWNORMAL);
657  State=STATE_UPDATE;
658  UpdateWindow(hSubWnd);
659  FillPattern(hSubWnd);
660 // InvalidateRect(hMainWnd,NULL,TRUE);
661  Logf("but1 visible=%d",IsWindowVisible(hButton[0]));
662 #endif
663
664 #ifdef TEST_DIALOG
665  State=STATE_DIALOG;
666  DialogBox(hInst,MAKEINTRESOURCE(1),hMainWnd,(FARPROC)TestDialogProc);
667 #endif
668 #ifdef TEST_COMMCTL
669  {
670   DWORD arr[16];
671   CHOOSECOLOR cc={sizeof(cc),0,hInst,0,arr,0};
672   ChooseColor(&cc);
673  }
674 #endif
675
676  State=STATE_DIRECT;
677  while (GetMessage(&msg,0,0,0)) {
678   TranslateMessage(&msg);
679   State=STATE_DISPATCH;
680   DispatchMessage(&msg);
681   State=STATE_DIRECT;
682  }
683  return 0;
684 }
685
686