1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 module dfl.application;
5 
6 import core.sys.windows.windows;
7 import core.sys.windows.commctrl;
8 import core.stdc.stdlib;
9 
10 import dfl.base;
11 import dfl.button;
12 import dfl.control;
13 import dfl.drawing;
14 import dfl.environment;
15 import dfl.event;
16 import dfl.exception;
17 import dfl.form;
18 import dfl.internal.clib;
19 import dfl.internal.dlib;
20 import dfl.internal.utf;
21 import dfl.internal.visualstyles;
22 import dfl.label;
23 import dfl.textbox;
24 
25 
26 version = DFL_NO_RESOURCES;
27 version (DFL_NO_RESOURCES) {
28 } else {
29    import dfl.resources;
30 }
31 
32 version (DFL_NO_MENUS) {
33 } else {
34    import dfl.menu;
35 }
36 
37 version = DFL_NO_ZOMBIE_FORM;
38 
39 //debug = APP_PRINT;
40 //debug = SHOW_MESSAGE_INFO; // Slow.
41 
42 debug (APP_PRINT) {
43    pragma(msg, "DFL: debug app print");
44 
45    version (DFL_LIB) static assert(0);
46 }
47 
48 private extern (C) void abort();
49 
50 class ApplicationContext {
51 
52    this() {
53    }
54 
55    // If onMainFormClose isn't overridden, the message
56    // loop terminates when the main form is destroyed.
57    this(Form mainForm) {
58       mform = mainForm;
59       mainForm.closed ~= &onMainFormClosed;
60    }
61 
62    final @property void mainForm(Form mainForm) {
63       if (mform) {
64          mform.closed.removeHandler(&onMainFormClosed);
65       }
66 
67       mform = mainForm;
68 
69       if (mainForm) {
70          mainForm.closed ~= &onMainFormClosed;
71       }
72    }
73 
74    final @property Form mainForm() nothrow {
75       return mform;
76    }
77 
78    Event!(Object, EventArgs) threadExit;
79 
80    final void exitThread() {
81       exitThreadCore();
82    }
83 
84 protected:
85 
86    void exitThreadCore() {
87       threadExit(this, EventArgs.empty);
88       //ExitThread(0);
89    }
90 
91    void onMainFormClosed(Object sender, EventArgs args) {
92       exitThreadCore();
93    }
94 
95 private:
96    Form mform; // The context form.
97 }
98 
99 private extern (Windows) nothrow {
100    alias GetTempFileNameWProc = UINT function(LPCWSTR lpPathName,
101       LPCWSTR lpPrefixString, UINT uUnique, LPWSTR lpTempFileName);
102    alias GetTempPathWProc = DWORD function(DWORD nBufferLength, LPWSTR lpBuffer);
103    alias CreateActCtxWProc = HANDLE function(PACTCTXW pActCtx);
104    alias ActivateActCtxProc = BOOL function(HANDLE hActCtx, ULONG_PTR* lpCookie);
105 }
106 
107 version (NO_WINDOWS_HUNG_WORKAROUND) {
108 } else {
109    version = WINDOWS_HUNG_WORKAROUND;
110 }
111 
112 // Compatibility with previous DFL versions.
113 // Set version=DFL_NO_COMPAT to disable.
114 enum DflCompat {
115    NONE = 0,
116 
117    // Adding to menus is the old way.
118    MENU_092 = 0x1,
119 
120    // Controls don't recreate automatically when necessary.
121    CONTROL_RECREATE_095 = 0x2,
122 
123    // Nothing.
124    CONTROL_KEYEVENT_096 = 0x4,
125 
126    // When a Form is in showDialog, changing the dialogResult from NONE doesn't close the form.
127    FORM_DIALOGRESULT_096 = 0x8,
128 
129    // Call onLoad/load and focus a control at old time.
130    FORM_LOAD_096 = 0x10,
131 
132    // Parent controls now need to be container-controls; this removes that limit.
133    CONTROL_PARENT_096 = 0x20,
134 }
135 
136 final class Application {
137    private this() {
138    }
139 
140 static:
141 
142    // Should be called before creating any controls.
143    // This is typically the first function called in main().
144    // Does nothing if not supported.
145    void enableVisualStyles() {
146       enum MANIFEST = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` ~ "\r\n" ~ `<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">` ~ "\r\n" ~ `<description>DFL manifest</description>` ~ "\r\n" ~ `<dependency>` ~ "\r\n" ~ `<dependentAssembly>` ~ "\r\n" ~ `<assemblyIdentity ` ~ `type="win32" ` ~ `name="Microsoft.Windows.Common-Controls" ` ~ `version="6.0.0.0" ` ~ `processorArchitecture="X86" ` ~ `publicKeyToken="6595b64144ccf1df" ` ~ `language="*" ` ~ `/>` ~ "\r\n" ~ `</dependentAssembly>` ~ "\r\n" ~ `</dependency>` ~ "\r\n" ~ `</assembly>` ~ "\r\n";
147 
148       HMODULE kernel32;
149       kernel32 = GetModuleHandleA("kernel32.dll");
150       //if(kernel32)
151       assert(kernel32);
152       {
153          CreateActCtxWProc createActCtxW;
154          createActCtxW = cast(CreateActCtxWProc) GetProcAddress(kernel32, "CreateActCtxW");
155          if (createActCtxW) {
156             GetTempPathWProc getTempPathW;
157             GetTempFileNameWProc getTempFileNameW;
158             ActivateActCtxProc activateActCtx;
159 
160             getTempPathW = cast(GetTempPathWProc) GetProcAddress(kernel32, "GetTempPathW");
161             assert(getTempPathW !is null);
162             getTempFileNameW = cast(GetTempFileNameWProc) GetProcAddress(kernel32,
163                "GetTempFileNameW");
164             assert(getTempFileNameW !is null);
165             activateActCtx = cast(ActivateActCtxProc) GetProcAddress(kernel32, "ActivateActCtx");
166             assert(activateActCtx !is null);
167 
168             DWORD pathlen;
169             wchar[MAX_PATH] pathbuf = void;
170             //if(pathbuf)
171             {
172                pathlen = getTempPathW(pathbuf.length, pathbuf.ptr);
173                if (pathlen) {
174                   DWORD manifestlen;
175                   wchar[MAX_PATH] manifestbuf = void;
176                   //if(manifestbuf)
177                   {
178                      manifestlen = getTempFileNameW(pathbuf.ptr, "dmf", 0, manifestbuf.ptr);
179                      if (manifestlen) {
180                         HANDLE hf;
181                         hf = CreateFileW(manifestbuf.ptr, GENERIC_WRITE, 0,
182                            null, CREATE_ALWAYS,
183                            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, HANDLE.init);
184                         if (hf != INVALID_HANDLE_VALUE) {
185                            DWORD written;
186                            if (WriteFile(hf, MANIFEST.ptr, MANIFEST.length, &written,
187                                  null)) {
188                               CloseHandle(hf);
189 
190                               ACTCTXW ac;
191                               HANDLE hac;
192 
193                               ac.cbSize = ACTCTXW.sizeof;
194                               //ac.dwFlags = 4; // ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID
195                               ac.dwFlags = 0;
196                               ac.lpSource = manifestbuf.ptr;
197                               //ac.lpAssemblyDirectory = pathbuf; // ?
198 
199                               hac = createActCtxW(&ac);
200                               if (hac != INVALID_HANDLE_VALUE) {
201                                  ULONG_PTR ul;
202                                  activateActCtx(hac, &ul);
203 
204                                  _initCommonControls(ICC_STANDARD_CLASSES); // Yes.
205                                  //InitCommonControls(); // No. Doesn't work with common controls version 6!
206 
207                                  // Ensure the actctx is actually associated with the message queue...
208                                  PostMessageA(null, wmDfl, 0, 0);
209                                  {
210                                     MSG msg;
211                                     PeekMessageA(&msg, null, wmDfl, wmDfl, PM_REMOVE);
212                                  }
213                               } else {
214                                  debug (APP_PRINT)
215                                     cprintf("CreateActCtxW failed.\n");
216                               }
217                            } else {
218                               CloseHandle(hf);
219                            }
220                         }
221 
222                         DeleteFileW(manifestbuf.ptr);
223                      }
224                   }
225                }
226             }
227          }
228       }
229    }
230 
231    /+
232    //
233    @property bool visualStyles() nothrow {
234       // IsAppThemed:
235       // "Do not call this function during DllMain or global objects contructors.
236       // This may cause invalid return values in Microsoft Windows Vista and may cause Windows XP to become unstable."
237    }
238    +/
239 
240    /// Path of the executable including its file name.
241    @property Dstring executablePath() {
242       return dfl.internal.utf.getModuleFileName(HMODULE.init);
243    }
244 
245    /// Directory containing the executable.
246    @property Dstring startupPath() {
247       return pathGetDirName(dfl.internal.utf.getModuleFileName(HMODULE.init));
248    }
249 
250    // Used internally.
251    Dstring getSpecialPath(Dstring name) { // package
252       HKEY hk;
253       if (ERROR_SUCCESS != RegOpenKeyA(HKEY_CURRENT_USER,
254             r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders".ptr,
255             &hk)) {
256       bad_path:
257          throw new DflException("Unable to obtain " ~ name ~ " directory information");
258       }
259       scope (exit)
260          RegCloseKey(hk);
261       Dstring result;
262       result = regQueryValueString(hk, name);
263       if (!result.length) {
264          goto bad_path;
265       }
266       return result;
267    }
268 
269    /// Application data base directory path, usually `C:\Documents and Settings\<user>\Application Data`; this directory might not exist yet.
270    @property Dstring userAppDataBasePath() {
271       return getSpecialPath("AppData");
272    }
273 
274    @property bool messageLoop() nothrow {
275       return (threadFlags & TF.RUNNING) != 0;
276    }
277 
278    void addMessageFilter(IMessageFilter mf) {
279       //filters ~= mf;
280 
281       IMessageFilter[] fs = filters;
282       fs ~= mf;
283       filters = fs;
284    }
285 
286    void removeMessageFilter(IMessageFilter mf) {
287       uint i;
288       for (i = 0; i != filters.length; i++) {
289          if (mf is filters[i]) {
290             if (!i) {
291                filters = filters[1 .. filters.length];
292             } else if (i == filters.length - 1) {
293                filters = filters[0 .. i];
294             } else {
295                filters = filters[0 .. i] ~ filters[i + 1 .. filters.length];
296             }
297             break;
298          }
299       }
300    }
301 
302    package bool _doEvents(bool* keep) {
303       if (threadFlags & (TF.STOP_RUNNING | TF.QUIT)) {
304          return false;
305       }
306 
307       try {
308          Message msg;
309 
310          //while(PeekMessageA(&msg._winMsg, HWND.init, 0, 0, PM_REMOVE))
311          while (dfl.internal.utf.peekMessage(&msg._winMsg, HWND.init, 0, 0, PM_REMOVE)) {
312             gotMessage(msg);
313 
314             if (msg.msg == WM_QUIT) {
315                threadFlags = threadFlags | TF.QUIT;
316                return false;
317             }
318             if (threadFlags & TF.STOP_RUNNING) {
319                return false;
320             }
321             if (!*keep) {
322                break;
323             }
324          }
325 
326          // Execution continues after this so it's not idle.
327       }
328       catch (DThrowable e) {
329          onThreadException(e);
330       }
331 
332       return (threadFlags & TF.QUIT) == 0;
333    }
334 
335    /// Process all messages in the message queue. Returns false if the application should exit.
336    bool doEvents() {
337       bool keep = true;
338       return _doEvents(&keep);
339    }
340 
341    bool doEvents(uint msDelay) {
342       if (msDelay <= 3) {
343          return doEvents();
344       }
345       struct TMR {
346          public import dfl.timer;
347       }
348 
349       scope tmr = new TMR.Timer();
350       bool keep = true;
351       tmr.interval = msDelay;
352       tmr.tick ~= (TMR.Timer sender, EventArgs ea) { sender.stop(); keep = false; };
353       tmr.start();
354       while (keep) {
355          Application.waitForEvent();
356          if (!_doEvents(&keep)) {
357             return false;
358          }
359       }
360       return true;
361    }
362 
363    /// Run the application.
364    void run() {
365       run(new ApplicationContext);
366    }
367 
368    void run(void delegate() whileIdle) {
369       run(new ApplicationContext, whileIdle);
370    }
371 
372    void run(ApplicationContext appcon) {
373       void whileIdle() {
374          waitForEvent();
375       }
376 
377       run(appcon, &whileIdle);
378    }
379 
380    // -whileIdle- is called repeatedly while there are no messages in the queue.
381    // Application.idle events are suppressed; however, the -whileIdle- handler
382    // may manually fire the Application.idle event.
383    void run(ApplicationContext appcon, void delegate() whileIdle) {
384       if (threadFlags & TF.RUNNING) {
385          //throw new DflException("Cannot have more than one message loop per thread");
386          assert(0, "Cannot have more than one message loop per thread");
387       }
388 
389       if (threadFlags & TF.QUIT) {
390          assert(0, "The application is shutting down");
391       }
392 
393       version (CUSTOM_MSG_HOOK) {
394          HHOOK _msghook = SetWindowsHookExA(WH_CALLWNDPROCRET, &globalMsgHook,
395             null, GetCurrentThreadId());
396          if (!_msghook) {
397             throw new DflException("Unable to get window messages");
398          }
399          msghook = _msghook;
400       }
401 
402       void threadJustExited(Object sender, EventArgs ea) {
403          exitThread();
404       }
405 
406       ctx = appcon;
407       ctx.threadExit ~= &threadJustExited;
408       try {
409          threadFlags = threadFlags | TF.RUNNING;
410 
411          if (ctx.mainForm) {
412             //ctx.mainForm.createControl();
413             ctx.mainForm.show();
414          }
415 
416          for (;;) {
417             try {
418                still_running: while (!(threadFlags & (TF.QUIT | TF.STOP_RUNNING))) {
419                   Message msg;
420 
421                   //while(PeekMessageA(&msg._winMsg, HWND.init, 0, 0, PM_REMOVE))
422                   while (dfl.internal.utf.peekMessage(&msg._winMsg, HWND.init, 0, 0,
423                         PM_REMOVE)) {
424                      gotMessage(msg);
425 
426                      if (msg.msg == WM_QUIT) {
427                         threadFlags = threadFlags | TF.QUIT;
428                         break still_running;
429                      }
430 
431                      if (threadFlags & (TF.QUIT | TF.STOP_RUNNING)) {
432                         break still_running;
433                      }
434                   }
435 
436                   whileIdle();
437                }
438 
439                // Stopped running.
440                threadExit(typeid(Application), EventArgs.empty);
441                threadFlags = threadFlags & ~(TF.RUNNING | TF.STOP_RUNNING);
442                return;
443             }
444             catch (DThrowable e) {
445                onThreadException(e);
446             }
447          }
448       }
449       finally {
450          threadFlags = threadFlags & ~(TF.RUNNING | TF.STOP_RUNNING);
451 
452          ApplicationContext tctx;
453          tctx = ctx;
454          ctx = null;
455 
456          version (CUSTOM_MSG_HOOK)
457             UnhookWindowsHookEx(msghook);
458 
459          tctx.threadExit.removeHandler(&threadJustExited);
460       }
461    }
462 
463    // Makes the form -mainForm- visible.
464    void run(Form mainForm, void delegate() whileIdle) {
465       ApplicationContext appcon = new ApplicationContext(mainForm);
466       //mainForm.show(); // Interferes with -running-.
467       run(appcon, whileIdle);
468    }
469 
470    void run(Form mainForm) {
471       ApplicationContext appcon = new ApplicationContext(mainForm);
472       //mainForm.show(); // Interferes with -running-.
473       run(appcon);
474    }
475 
476    void exit() {
477       PostQuitMessage(0);
478    }
479 
480    /// Exit the thread's message loop and return from run.
481    // Actually only stops the current run() loop.
482    void exitThread() {
483       threadFlags = threadFlags | TF.STOP_RUNNING;
484    }
485 
486    // Will be null if not in a successful Application.run.
487    package @property ApplicationContext context() nothrow {
488       return ctx;
489    }
490 
491    HINSTANCE getInstance() {
492       if (!hinst) {
493          _initInstance();
494       }
495       return hinst;
496    }
497 
498    void setInstance(HINSTANCE inst) {
499       if (hinst) {
500          if (inst != hinst) {
501             throw new DflException("Instance is already set");
502          }
503          return;
504       }
505 
506       if (inst) {
507          _initInstance(inst);
508       } else {
509          _initInstance(); // ?
510       }
511    }
512 
513    // ApartmentState oleRequired() ...
514 
515    private static class ErrForm : Form {
516       protected override void onLoad(EventArgs ea) {
517          okBtn.focus();
518       }
519 
520       protected override void onClosing(CancelEventArgs cea) {
521          cea.cancel = !errdone;
522       }
523 
524       enum PADDING = 10;
525 
526       void onOkClick(Object sender, EventArgs ea) {
527          errdone = true;
528          ctnu = true;
529          //close();
530          dispose();
531       }
532 
533       void onCancelClick(Object sender, EventArgs ea) {
534          errdone = true;
535          ctnu = false;
536          //close();
537          dispose();
538       }
539 
540       this(Dstring errmsg) {
541          text = "Error";
542          clientSize = Size(340, 150);
543          startPosition = FormStartPosition.CENTER_SCREEN;
544          formBorderStyle = FormBorderStyle.FIXED_DIALOG;
545          minimizeBox = false;
546          maximizeBox = false;
547          controlBox = false;
548 
549          Label label;
550          with (label = new Label) {
551             bounds = Rect(PADDING, PADDING, this.clientSize.width - PADDING * 2, 40);
552             label.text
553                = "An application exception has occured. Click Continue to allow " ~ "the application to ignore this error and attempt to continue.";
554             parent = this;
555          }
556 
557          with (errBox = new TextBox) {
558             text = errmsg;
559             bounds = Rect(PADDING, 40 + PADDING, this.clientSize.width - PADDING * 2,
560                50);
561             errBox.backColor = this.backColor;
562             readOnly = true;
563             multiline = true;
564             parent = this;
565          }
566 
567          with (okBtn = new Button) {
568             width = 100;
569             location = Point(this.clientSize.width - width - PADDING - width - PADDING,
570                this.clientSize.height - height - PADDING);
571             text = "&Continue";
572             parent = this;
573             click ~= &onOkClick;
574          }
575          acceptButton = okBtn;
576 
577          with (new Button) {
578             width = 100;
579             location = Point(this.clientSize.width - width - PADDING,
580                this.clientSize.height - height - PADDING);
581             text = "&Quit";
582             parent = this;
583             click ~= &onCancelClick;
584          }
585 
586          autoScale = true;
587       }
588 
589       /+
590       private int inThread2() {
591          try {
592             // Create in this thread so that it owns the handle.
593             assert(!isHandleCreated);
594             show();
595             SetForegroundWindow(handle);
596 
597             MSG msg;
598             assert(isHandleCreated);
599             // Using the unicode stuf here messes up the redrawing for some reason.
600             while(GetMessageA(&msg, HWND.init, 0, 0)) // TODO: unicode ?
601                //while(dfl.internal.utf.getMessage(&msg, HWND.init, 0, 0))
602             {
603                if(!IsDialogMessageA(handle, &msg))
604                   //if(!dfl.internal.utf.isDialogMessage(handle, &msg))
605                {
606                   TranslateMessage(&msg);
607                   DispatchMessageA(&msg);
608                   //dfl.internal.utf.dispatchMessage(&msg);
609                }
610 
611                if(!isHandleCreated) {
612                   break;
613                }
614             }
615          }
616          finally {
617             dispose();
618             assert(!isHandleCreated);
619 
620             thread1 = null;
621          }
622 
623          return 0;
624       }
625 
626       private void tinThread2() {
627          inThread2();
628       }
629 
630 
631       private Thread thread1;
632 
633       bool doContinue() {
634          assert(!isHandleCreated);
635 
636          // Need to use a separate thread so that all the main thread's messages
637          // will be there still when the exception is recovered from.
638          // This is very important for some messages, such as socket events.
639          thread1 = Thread.getThis(); // Problems with DMD 2.x
640          Thread thd;
641          thd = new Thread(&inThread2);
642          thd.start();
643          do {
644             Sleep(200);
645          } while(thread1);
646 
647          return ctnu;
648       }
649       +/
650 
651       bool doContinue() {
652          assert(!isHandleCreated);
653 
654          show();
655 
656          Message msg;
657          for (;;) {
658             WaitMessage();
659             if (PeekMessageA(&msg._winMsg, handle, 0, 0, PM_REMOVE | PM_NOYIELD)) {
660                /+
661                //if(!IsDialogMessageA(handle, &msg._winMsg)) // Back to the old problems.
662                {
663                   TranslateMessage(&msg._winMsg);
664                   DispatchMessageA(&msg._winMsg);
665                }
666                +/
667                gotMessage(msg);
668             }
669 
670             if (!isHandleCreated) {
671                break;
672             }
673          }
674 
675          return ctnu;
676       }
677 
678       override Dstring toString() {
679          return errBox.text;
680       }
681 
682    private:
683       bool errdone = false;
684       bool ctnu = false;
685       Button okBtn;
686       TextBox errBox;
687    }
688 
689    bool showDefaultExceptionDialog(Object e) {
690       /+
691       if(IDYES == MessageBoxA(null,
692                               "An application exception has occured. Click Yes to allow\r\n"
693                               "the application to ignore this error and attempt to continue.\r\n"
694                               "Click No to quit the application.\r\n\r\n"~
695                               e.toString(),
696                               null, MB_ICONWARNING | MB_TASKMODAL | MB_YESNO)) {
697          except = false;
698          return;
699       }
700       +/
701 
702       //try
703       {
704          if ((new ErrForm(getObjectString(e))).doContinue()) {
705             return true;
706          }
707       }
708       /+
709       catch {
710          MessageBoxA(null, "Error displaying error message", "DFL", MB_ICONERROR | MB_TASKMODAL);
711       }
712       +/
713 
714       return false;
715    }
716 
717    void onThreadException(DThrowable e) nothrow {
718       try {
719          static bool except = false;
720 
721          version (WINDOWS_HUNG_WORKAROUND) {
722             version (WINDOWS_HUNG_WORKAROUND_NO_IGNORE) {
723             } else {
724                if (cast(WindowsHungDflException) e) {
725                   return;
726                }
727             }
728          }
729 
730          if (except) {
731             cprintf("Error: %.*s\n", cast(int) getObjectString(e).length, getObjectString(e).ptr);
732 
733             abort();
734             return;
735          }
736 
737          except = true;
738          //if(threadException.handlers.length)
739          if (threadException.hasHandlers) {
740             threadException(typeid(Application), new ThreadExceptionEventArgs(e));
741             except = false;
742             return;
743          } else {
744             // No thread exception handlers, display a dialog.
745             if (showDefaultExceptionDialog(e)) {
746                except = false;
747                return;
748             }
749          }
750          //except = false;
751 
752          //throw e;
753          cprintf("Error: %.*s\n", cast(int) getObjectString(e).length, getObjectString(e).ptr);
754          //exitThread();
755          Environment.exit(EXIT_FAILURE);
756       }
757       catch (DThrowable e) {
758       }
759    }
760 
761    Event!(Object, EventArgs) idle; // Finished processing and is now idle.
762 
763    Event!(Object, ThreadExceptionEventArgs) threadException;
764 
765    Event!(Object, EventArgs) threadExit;
766 
767    void addHotkey(Keys k, void delegate(Object sender, KeyEventArgs ea) dg) {
768       if (auto pkid = k in hotkeyId) {
769          immutable kid = *pkid;
770          hotkeyHandler[kid] ~= dg;
771       } else {
772          int kid = 0;
773          foreach (aak, aav; hotkeyHandler) {
774             if (!aav.hasHandlers) {
775                kid = aak;
776                break;
777             }
778             ++kid;
779          }
780          immutable mod = (k & Keys.MODIFIERS) >> 16, keycode = k & Keys.KEY_CODE;
781          if (RegisterHotKey(null, kid, mod, keycode)) {
782             hotkeyId[k] = kid;
783             if (auto h = kid in hotkeyHandler) {
784                *h ~= dg;
785             } else {
786                typeof(hotkeyHandler[kid]) e;
787                e ~= dg;
788                hotkeyHandler[kid] = e;
789             }
790          } else {
791             throw new DflException("Hotkey cannot resistered.");
792          }
793       }
794    }
795 
796    void removeHotkey(Keys k, void delegate(Object sender, KeyEventArgs ea) dg) {
797       if (auto pkid = k in hotkeyId) {
798          immutable kid = *pkid;
799          hotkeyHandler[kid].removeHandler(dg);
800          if (!hotkeyHandler[kid].hasHandlers) {
801             if (UnregisterHotKey(null, kid) == 0) {
802                throw new DflException("Hotkey cannot unresistered.");
803             }
804             hotkeyHandler.remove(kid);
805             hotkeyId.remove(k);
806          }
807       }
808    }
809 
810    void removeHotkey(Keys k) {
811       if (auto pkid = k in hotkeyId) {
812          immutable kid = *pkid;
813          foreach (hnd; hotkeyHandler[kid]) {
814             hotkeyHandler[kid].removeHandler(hnd);
815          }
816          assert(!hotkeyHandler[kid].hasHandlers);
817          if (UnregisterHotKey(null, kid) == 0) {
818             throw new DflException("Hotkey cannot unresistered.");
819          }
820          hotkeyHandler.remove(kid);
821          hotkeyId.remove(k);
822       }
823    }
824 
825    struct HotkeyRegister {
826    static:
827 
828       alias Handler = void delegate(Object c, KeyEventArgs e);
829 
830       void addHandler(Keys k, Handler dg) {
831          addHotkey(k, dg);
832       }
833 
834       struct IndexedCatAssigner {
835          Keys k;
836 
837          void opCatAssign(Handler dg) {
838             addHandler(k, dg);
839          }
840       }
841 
842       IndexedCatAssigner opIndex(Keys k) {
843          return IndexedCatAssigner(k);
844       }
845 
846       void removeHandler(Keys k, Handler dg) {
847          removeHotkey(k, dg);
848       }
849 
850       void removeHandler(Keys k) {
851          removeHotkey(k);
852       }
853    }
854 
855    /// helper
856    HotkeyRegister hotkeys;
857 
858    static ~this() {
859       foreach (key; hotkeyId.keys) {
860          removeHotkey(key);
861       }
862       hotkeyId = null;
863    }
864 
865    // Returns null if not found.
866    package Control lookupHwnd(HWND hwnd) nothrow {
867       //if(hwnd in controls)
868       // return controls[hwnd];
869       auto pc = hwnd in controls;
870       if (pc) {
871          return *pc;
872       }
873       return null;
874    }
875 
876    // Also makes a great zombie.
877    package void removeHwnd(HWND hwnd) {
878       //delete controls[hwnd];
879       controls.remove(hwnd);
880    }
881 
882    version (DFL_NO_ZOMBIE_FORM) {
883    } else {
884       package enum ZOMBIE_PROP = "DFL_Zombie";
885 
886       // Doesn't do any good since the child controls still reference this control.
887       package void zombieHwnd(Control c)
888       in {
889          assert(c !is null);
890          assert(c.isHandleCreated);
891          assert(lookupHwnd(c.handle));
892       }
893       body {
894          SetPropA(c.handle, ZOMBIE_PROP.ptr, cast(HANDLE) cast(void*) c);
895          removeHwnd(c.handle);
896       }
897 
898       package void unzombieHwnd(Control c)
899       in {
900          assert(c !is null);
901          assert(c.isHandleCreated);
902          assert(!lookupHwnd(c.handle));
903       }
904       body {
905          RemovePropA(c.handle, ZOMBIE_PROP.ptr);
906          controls[c.handle] = c;
907       }
908 
909       // Doesn't need to be a zombie.
910       package void zombieKill(Control c)
911       in {
912          assert(c !is null);
913       }
914       body {
915          if (c.isHandleCreated) {
916             RemovePropA(c.handle, ZOMBIE_PROP.ptr);
917          }
918       }
919    }
920 
921    version (DFL_NO_MENUS) {
922    } else {
923       // Returns its new unique menu ID.
924       package int addMenuItem(MenuItem menu) {
925          if (nmenus == END_MENU_ID - FIRST_MENU_ID) {
926             throw new DflException("Out of menus");
927          }
928 
929          typeof(menus) tempmenus;
930 
931          // TODO: sort menu IDs in 'menus' so that looking for free ID is much faster.
932 
933          prevMenuID++;
934          if (prevMenuID >= END_MENU_ID || prevMenuID <= FIRST_MENU_ID) {
935             prevMenuID = FIRST_MENU_ID;
936             previdloop: for (;;) {
937                for (size_t iw; iw != nmenus; iw++) {
938                   MenuItem mi;
939                   mi = cast(MenuItem) menus[iw];
940                   if (mi) {
941                      if (prevMenuID == mi._menuID) {
942                         prevMenuID++;
943                         continue previdloop;
944                      }
945                   }
946                }
947                break;
948             }
949          }
950          tempmenus = cast(Menu*) realloc(menus, Menu.sizeof * (nmenus + 1));
951          if (!tempmenus) {
952             //throw new OutOfMemory;
953             throw new DflException("Out of memory");
954          }
955          menus = tempmenus;
956 
957          menus[nmenus++] = menu;
958 
959          return prevMenuID;
960       }
961 
962       package void addContextMenu(ContextMenu menu) {
963          if (nmenus == END_MENU_ID - FIRST_MENU_ID) {
964             throw new DflException("Out of menus");
965          }
966 
967          typeof(menus) tempmenus;
968          int idx;
969 
970          idx = nmenus;
971          nmenus++;
972          tempmenus = cast(Menu*) realloc(menus, Menu.sizeof * nmenus);
973          if (!tempmenus) {
974             nmenus--;
975             //throw new OutOfMemory;
976             throw new DflException("Out of memory");
977          }
978          menus = tempmenus;
979 
980          menus[idx] = menu;
981       }
982 
983       package void removeMenu(Menu menu) {
984          uint idx;
985 
986          for (idx = 0; idx != nmenus; idx++) {
987             if (menus[idx] is menu) {
988                goto found;
989             }
990          }
991          return;
992 
993       found:
994          if (nmenus == 1) {
995             free(menus);
996             menus = null;
997             nmenus--;
998          } else {
999             if (idx != nmenus - 1) {
1000                menus[idx] = menus[nmenus - 1]; // Move last one in its place
1001             }
1002 
1003             nmenus--;
1004             menus = cast(Menu*) realloc(menus, Menu.sizeof * nmenus);
1005             assert(menus != null); // Memory shrink shouldn't be a problem.
1006          }
1007       }
1008 
1009       package MenuItem lookupMenuID(int menuID) {
1010          uint idx;
1011          MenuItem mi;
1012 
1013          for (idx = 0; idx != nmenus; idx++) {
1014             mi = cast(MenuItem) menus[idx];
1015             if (mi && mi._menuID == menuID) {
1016                return mi;
1017             }
1018          }
1019          return null;
1020       }
1021 
1022       package Menu lookupMenu(HMENU hmenu) {
1023          uint idx;
1024 
1025          for (idx = 0; idx != nmenus; idx++) {
1026             if (menus[idx].handle == hmenu) {
1027                return menus[idx];
1028             }
1029          }
1030          return null;
1031       }
1032    }
1033 
1034    package void creatingControl(Control ctrl) nothrow {
1035       TlsSetValue(tlsControl, cast(Control*) ctrl);
1036    }
1037 
1038    version (DFL_NO_RESOURCES) {
1039    } else {
1040 
1041       @property Resources resources() {
1042          static Resources rc = null;
1043 
1044          if (!rc) {
1045             synchronized {
1046                if (!rc) {
1047                   rc = new Resources(getInstance());
1048                }
1049             }
1050          }
1051          return rc;
1052       }
1053    }
1054 
1055    private UINT gctimer = 0;
1056    private DWORD gcinfo = 1;
1057 
1058    @property void autoCollect(bool byes) {
1059       if (byes) {
1060          if (!autoCollect) {
1061             gcinfo = 1;
1062          }
1063       } else {
1064          if (autoCollect) {
1065             gcinfo = 0;
1066             KillTimer(HWND.init, gctimer);
1067             gctimer = 0;
1068          }
1069       }
1070    }
1071 
1072    @property bool autoCollect() nothrow {
1073       return gcinfo > 0;
1074    }
1075 
1076    package void _waitMsg() {
1077       if (threadFlags & (TF.STOP_RUNNING | TF.QUIT)) {
1078          return;
1079       }
1080 
1081       idle(typeid(Application), EventArgs.empty);
1082       WaitMessage();
1083    }
1084 
1085    package deprecated alias waitMsg = _waitMsg;
1086 
1087    // Because waiting for an event enters an idle state,
1088    // this function fires the -idle- event.
1089    void waitForEvent() {
1090       if (!autoCollect) {
1091          _waitMsg();
1092          return;
1093       }
1094 
1095       if (1 == gcinfo) {
1096          gcinfo = gcinfo.max;
1097          assert(!gctimer);
1098          gctimer = SetTimer(HWND.init, 0, 200, &_gcTimeout);
1099       }
1100 
1101       _waitMsg();
1102 
1103       if (GetTickCount() > gcinfo) {
1104          gcinfo = 1;
1105       }
1106    }
1107 
1108    version (DFL_NO_COMPAT)
1109       package enum _compat = DflCompat.NONE;
1110    else {
1111       package DflCompat _compat = DflCompat.NONE;
1112    }
1113 
1114    deprecated void setCompat(DflCompat dflcompat) {
1115       version (DFL_NO_COMPAT) {
1116          assert(0, "Compatibility disabled"); // version=DFL_NO_COMPAT
1117       } else {
1118          if (messageLoop) {
1119             assert(0, "setCompat"); // Called too late, must enable compatibility sooner.
1120          }
1121 
1122          _compat |= dflcompat;
1123       }
1124    }
1125 
1126    private static size_t _doref(void* p, int by) {
1127       assert(1 == by || -1 == by);
1128 
1129       size_t result;
1130 
1131       synchronized {
1132          auto pref = p in _refs;
1133          if (pref) {
1134             size_t count;
1135             count = *pref;
1136 
1137             assert(count || -1 != by);
1138 
1139             if (-1 == by) {
1140                count--;
1141             } else {
1142                count++;
1143             }
1144 
1145             if (!count) {
1146                result = 0;
1147                _refs.remove(p);
1148             } else {
1149                result = count;
1150                _refs[p] = count;
1151             }
1152          } else if (1 == by) {
1153             _refs[p] = 1;
1154             result = 1;
1155          }
1156       }
1157 
1158       return result;
1159    }
1160 
1161    package size_t refCountInc(void* p) {
1162       return _doref(p, 1);
1163    }
1164 
1165    // Returns the new ref count.
1166    package size_t refCountDec(void* p) {
1167       return _doref(p, -1);
1168    }
1169 
1170    package void ppin(void* p) {
1171       dfl.internal.dlib.gcPin(p);
1172    }
1173 
1174    package void punpin(void* p) {
1175       dfl.internal.dlib.gcUnpin(p);
1176    }
1177 
1178 private:
1179 static:
1180    size_t[void* ] _refs;
1181    IMessageFilter[] filters;
1182    DWORD tlsThreadFlags;
1183    DWORD tlsControl;
1184    DWORD tlsFilter; // IMessageFilter[]*.
1185    version (CUSTOM_MSG_HOOK) DWORD tlsHook; // HHOOK.
1186    Control[HWND] controls;
1187    HINSTANCE hinst;
1188    ApplicationContext ctx = null;
1189    int[Keys] hotkeyId;
1190    Event!(Object, KeyEventArgs)[int] hotkeyHandler;
1191 
1192    version (DFL_NO_MENUS) {
1193    } else {
1194       // Menus.
1195       enum short FIRST_MENU_ID = 200;
1196       enum short END_MENU_ID = 10000;
1197 
1198       // Controls.
1199       enum ushort FIRST_CTRL_ID = END_MENU_ID + 1;
1200       enum ushort LAST_CTRL_ID = 65500;
1201 
1202       ushort prevMenuID = FIRST_MENU_ID;
1203       // malloc() is needed so the menus can be garbage collected.
1204       uint nmenus = 0; // Number of -menus-.
1205       Menu* menus = null; // WARNING: malloc()'d memory!
1206 
1207       // Destroy all menu handles at program exit because Windows will not
1208       // unless it is assigned to a window.
1209       // Note that this is probably just a 16bit issue, but it still appeared in the 32bit docs.
1210       private void sdtorFreeAllMenus() {
1211          foreach (Menu m; menus[0 .. nmenus]) {
1212             DestroyMenu(m.handle);
1213          }
1214          nmenus = 0;
1215          free(menus);
1216          menus = null;
1217       }
1218    }
1219 
1220    private struct TlsFilterValue {
1221       IMessageFilter[] filters;
1222    }
1223 
1224    /+
1225    @property void filters(IMessageFilter[] filters) {
1226       // The TlsFilterValue is being garbage collected!
1227 
1228       TlsFilterValue* val = cast(TlsFilterValue*)TlsGetValue(tlsFilter);
1229       if(!val) {
1230          val = new TlsFilterValue;
1231       }
1232       val.filters = filters;
1233       TlsSetValue(tlsFilter, cast(LPVOID)val);
1234    }
1235 
1236 
1237    @property IMessageFilter[] filters() nothrow {
1238       TlsFilterValue* val = cast(TlsFilterValue*)TlsGetValue(tlsFilter);
1239       if(!val) {
1240          return null;
1241       }
1242       return val.filters;
1243    }
1244    +/
1245 
1246    version (CUSTOM_MSG_HOOK) {
1247       @property void msghook(HHOOK hhook) {
1248          TlsSetValue(tlsHook, cast(LPVOID) hhook);
1249       }
1250 
1251       @property HHOOK msghook() nothrow {
1252          return cast(HHOOK) TlsGetValue(tlsHook);
1253       }
1254    }
1255 
1256    Control getCreatingControl() nothrow {
1257       return cast(Control) cast(Control*) TlsGetValue(tlsControl);
1258    }
1259 
1260    // Thread flags.
1261    enum TF : DWORD {
1262       RUNNING = 1, // Application.run is in affect.
1263       STOP_RUNNING = 2,
1264       QUIT = 4, // Received WM_QUIT.
1265    }
1266 
1267    @property TF threadFlags() nothrow {
1268       return cast(TF) cast(DWORD) TlsGetValue(tlsThreadFlags);
1269    }
1270 
1271    @property void threadFlags(TF flags) {
1272       if (!TlsSetValue(tlsThreadFlags, cast(LPVOID) cast(DWORD) flags)) {
1273          assert(0);
1274       }
1275    }
1276 
1277    void gotMessage(ref Message msg) {
1278       //debug(SHOW_MESSAGE_INFO)
1279       // showMessageInfo(msg);
1280       void handleHotkey() {
1281          immutable kid = cast(int) msg.wParam,
1282             mod = cast(uint)(msg.lParam & 0x0000ffff),
1283             keycode = cast(uint)((msg.lParam & 0xffff0000) >> 16);
1284          assert(kid < hotkeyHandler.length);
1285          hotkeyHandler[kid](typeid(Application), new KeyEventArgs(cast(Keys)((mod << 16) | keycode)));
1286       }
1287       // Don't bother with this extra stuff if there aren't any filters.
1288       if (filters.length) {
1289          try {
1290             // Keep a local reference so that handlers
1291             // may be added and removed during filtering.
1292             IMessageFilter[] local = filters;
1293 
1294             foreach (IMessageFilter mf; local) {
1295                // Returning true prevents dispatching.
1296                if (mf.preFilterMessage(msg)) {
1297                   Control ctrl;
1298                   ctrl = lookupHwnd(msg.hWnd);
1299                   if (ctrl) {
1300                      ctrl.mustWndProc(msg);
1301                   } else if (msg.msg == WM_HOTKEY) {
1302                      handleHotkey();
1303                   }
1304                   return;
1305                }
1306             }
1307          }
1308          catch (DThrowable o) {
1309             Control ctrl;
1310             ctrl = lookupHwnd(msg.hWnd);
1311             if (ctrl) {
1312                ctrl.mustWndProc(msg);
1313             }
1314             throw o;
1315          }
1316       }
1317       if (msg.msg == WM_HOTKEY) {
1318          handleHotkey();
1319       }
1320       TranslateMessage(&msg._winMsg);
1321       //DispatchMessageA(&msg._winMsg);
1322       dfl.internal.utf.dispatchMessage(&msg._winMsg);
1323    }
1324 }
1325 
1326 package:
1327 
1328 extern (Windows) void _gcTimeout(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) nothrow {
1329    KillTimer(hwnd, Application.gctimer);
1330    Application.gctimer = 0;
1331 
1332    //cprintf("Auto-collecting\n");
1333    dfl.internal.dlib.gcFullCollect();
1334 
1335    Application.gcinfo = GetTickCount() + 4000;
1336 }
1337 
1338 // Note: phobos-only.
1339 debug (SHOW_MESSAGE_INFO) {
1340    private import std.stdio, std..string;
1341 
1342    void showMessageInfo(ref Message m) {
1343       void writeWm(Dstring wmName) {
1344          writef("Message %s=%d(0x%X)\n", wmName, m.msg, m.msg);
1345       }
1346 
1347       switch (m.msg) {
1348       case WM_NULL:
1349          writeWm("WM_NULL");
1350          break;
1351       case WM_CREATE:
1352          writeWm("WM_CREATE");
1353          break;
1354       case WM_DESTROY:
1355          writeWm("WM_DESTROY");
1356          break;
1357       case WM_MOVE:
1358          writeWm("WM_MOVE");
1359          break;
1360       case WM_SIZE:
1361          writeWm("WM_SIZE");
1362          break;
1363       case WM_ACTIVATE:
1364          writeWm("WM_ACTIVATE");
1365          break;
1366       case WM_SETFOCUS:
1367          writeWm("WM_SETFOCUS");
1368          break;
1369       case WM_KILLFOCUS:
1370          writeWm("WM_KILLFOCUS");
1371          break;
1372       case WM_ENABLE:
1373          writeWm("WM_ENABLE");
1374          break;
1375       case WM_SETREDRAW:
1376          writeWm("WM_SETREDRAW");
1377          break;
1378       case WM_SETTEXT:
1379          writeWm("WM_SETTEXT");
1380          break;
1381       case WM_GETTEXT:
1382          writeWm("WM_GETTEXT");
1383          break;
1384       case WM_GETTEXTLENGTH:
1385          writeWm("WM_GETTEXTLENGTH");
1386          break;
1387       case WM_PAINT:
1388          writeWm("WM_PAINT");
1389          break;
1390       case WM_CLOSE:
1391          writeWm("WM_CLOSE");
1392          break;
1393       case WM_QUERYENDSESSION:
1394          writeWm("WM_QUERYENDSESSION");
1395          break;
1396       case WM_QUIT:
1397          writeWm("WM_QUIT");
1398          break;
1399       case WM_QUERYOPEN:
1400          writeWm("WM_QUERYOPEN");
1401          break;
1402       case WM_ERASEBKGND:
1403          writeWm("WM_ERASEBKGND");
1404          break;
1405       case WM_SYSCOLORCHANGE:
1406          writeWm("WM_SYSCOLORCHANGE");
1407          break;
1408       case WM_ENDSESSION:
1409          writeWm("WM_ENDSESSION");
1410          break;
1411       case WM_SHOWWINDOW:
1412          writeWm("WM_SHOWWINDOW");
1413          break;
1414          //case WM_WININICHANGE: writeWm("WM_WININICHANGE"); break;
1415       case WM_SETTINGCHANGE:
1416          writeWm("WM_SETTINGCHANGE");
1417          break;
1418       case WM_DEVMODECHANGE:
1419          writeWm("WM_DEVMODECHANGE");
1420          break;
1421       case WM_ACTIVATEAPP:
1422          writeWm("WM_ACTIVATEAPP");
1423          break;
1424       case WM_FONTCHANGE:
1425          writeWm("WM_FONTCHANGE");
1426          break;
1427       case WM_TIMECHANGE:
1428          writeWm("WM_TIMECHANGE");
1429          break;
1430       case WM_CANCELMODE:
1431          writeWm("WM_CANCELMODE");
1432          break;
1433       case WM_SETCURSOR:
1434          writeWm("WM_SETCURSOR");
1435          break;
1436       case WM_MOUSEACTIVATE:
1437          writeWm("WM_MOUSEACTIVATE");
1438          break;
1439       case WM_CHILDACTIVATE:
1440          writeWm("WM_CHILDACTIVATE");
1441          break;
1442       case WM_QUEUESYNC:
1443          writeWm("WM_QUEUESYNC");
1444          break;
1445       case WM_GETMINMAXINFO:
1446          writeWm("WM_GETMINMAXINFO");
1447          break;
1448       case WM_NOTIFY:
1449          writeWm("WM_NOTIFY");
1450          break;
1451       case WM_INPUTLANGCHANGEREQUEST:
1452          writeWm("WM_INPUTLANGCHANGEREQUEST");
1453          break;
1454       case WM_INPUTLANGCHANGE:
1455          writeWm("WM_INPUTLANGCHANGE");
1456          break;
1457       case WM_TCARD:
1458          writeWm("WM_TCARD");
1459          break;
1460       case WM_HELP:
1461          writeWm("WM_HELP");
1462          break;
1463       case WM_USERCHANGED:
1464          writeWm("WM_USERCHANGED");
1465          break;
1466       case WM_NOTIFYFORMAT:
1467          writeWm("WM_NOTIFYFORMAT");
1468          break;
1469       case WM_CONTEXTMENU:
1470          writeWm("WM_CONTEXTMENU");
1471          break;
1472       case WM_STYLECHANGING:
1473          writeWm("WM_STYLECHANGING");
1474          break;
1475       case WM_STYLECHANGED:
1476          writeWm("WM_STYLECHANGED");
1477          break;
1478       case WM_DISPLAYCHANGE:
1479          writeWm("WM_DISPLAYCHANGE");
1480          break;
1481       case WM_GETICON:
1482          writeWm("WM_GETICON");
1483          break;
1484       case WM_SETICON:
1485          writeWm("WM_SETICON");
1486          break;
1487       case WM_NCCREATE:
1488          writeWm("WM_NCCREATE");
1489          break;
1490       case WM_NCDESTROY:
1491          writeWm("WM_NCDESTROY");
1492          break;
1493       case WM_NCCALCSIZE:
1494          writeWm("WM_NCCALCSIZE");
1495          break;
1496       case WM_NCHITTEST:
1497          writeWm("WM_NCHITTEST");
1498          break;
1499       case WM_NCPAINT:
1500          writeWm("WM_NCPAINT");
1501          break;
1502       case WM_NCACTIVATE:
1503          writeWm("WM_NCACTIVATE");
1504          break;
1505       case WM_GETDLGCODE:
1506          writeWm("WM_GETDLGCODE");
1507          break;
1508       case WM_NCMOUSEMOVE:
1509          writeWm("WM_NCMOUSEMOVE");
1510          break;
1511       case WM_NCLBUTTONDOWN:
1512          writeWm("WM_NCLBUTTONDOWN");
1513          break;
1514       case WM_NCLBUTTONUP:
1515          writeWm("WM_NCLBUTTONUP");
1516          break;
1517       case WM_NCLBUTTONDBLCLK:
1518          writeWm("WM_NCLBUTTONDBLCLK");
1519          break;
1520       case WM_NCRBUTTONDOWN:
1521          writeWm("WM_NCRBUTTONDOWN");
1522          break;
1523       case WM_NCRBUTTONUP:
1524          writeWm("WM_NCRBUTTONUP");
1525          break;
1526       case WM_NCRBUTTONDBLCLK:
1527          writeWm("WM_NCRBUTTONDBLCLK");
1528          break;
1529       case WM_NCMBUTTONDOWN:
1530          writeWm("WM_NCMBUTTONDOWN");
1531          break;
1532       case WM_NCMBUTTONUP:
1533          writeWm("WM_NCMBUTTONUP");
1534          break;
1535       case WM_NCMBUTTONDBLCLK:
1536          writeWm("WM_NCMBUTTONDBLCLK");
1537          break;
1538       case WM_KEYDOWN:
1539          writeWm("WM_KEYDOWN");
1540          break;
1541       case WM_KEYUP:
1542          writeWm("WM_KEYUP");
1543          break;
1544       case WM_CHAR:
1545          writeWm("WM_CHAR");
1546          break;
1547       case WM_DEADCHAR:
1548          writeWm("WM_DEADCHAR");
1549          break;
1550       case WM_SYSKEYDOWN:
1551          writeWm("WM_SYSKEYDOWN");
1552          break;
1553       case WM_SYSKEYUP:
1554          writeWm("WM_SYSKEYUP");
1555          break;
1556       case WM_SYSCHAR:
1557          writeWm("WM_SYSCHAR");
1558          break;
1559       case WM_SYSDEADCHAR:
1560          writeWm("WM_SYSDEADCHAR");
1561          break;
1562       case WM_IME_STARTCOMPOSITION:
1563          writeWm("WM_IME_STARTCOMPOSITION");
1564          break;
1565       case WM_IME_ENDCOMPOSITION:
1566          writeWm("WM_IME_ENDCOMPOSITION");
1567          break;
1568       case WM_IME_COMPOSITION:
1569          writeWm("WM_IME_COMPOSITION");
1570          break;
1571       case WM_INITDIALOG:
1572          writeWm("WM_INITDIALOG");
1573          break;
1574       case WM_COMMAND:
1575          writeWm("WM_COMMAND");
1576          break;
1577       case WM_SYSCOMMAND:
1578          writeWm("WM_SYSCOMMAND");
1579          break;
1580       case WM_TIMER:
1581          writeWm("WM_TIMER");
1582          break;
1583       case WM_HSCROLL:
1584          writeWm("WM_HSCROLL");
1585          break;
1586       case WM_VSCROLL:
1587          writeWm("WM_VSCROLL");
1588          break;
1589       case WM_INITMENU:
1590          writeWm("WM_INITMENU");
1591          break;
1592       case WM_INITMENUPOPUP:
1593          writeWm("WM_INITMENUPOPUP");
1594          break;
1595       case WM_MENUSELECT:
1596          writeWm("WM_MENUSELECT");
1597          break;
1598       case WM_MENUCHAR:
1599          writeWm("WM_MENUCHAR");
1600          break;
1601       case WM_ENTERIDLE:
1602          writeWm("WM_ENTERIDLE");
1603          break;
1604       case WM_CTLCOLORMSGBOX:
1605          writeWm("WM_CTLCOLORMSGBOX");
1606          break;
1607       case WM_CTLCOLOREDIT:
1608          writeWm("WM_CTLCOLOREDIT");
1609          break;
1610       case WM_CTLCOLORLISTBOX:
1611          writeWm("WM_CTLCOLORLISTBOX");
1612          break;
1613       case WM_CTLCOLORBTN:
1614          writeWm("WM_CTLCOLORBTN");
1615          break;
1616       case WM_CTLCOLORDLG:
1617          writeWm("WM_CTLCOLORDLG");
1618          break;
1619       case WM_CTLCOLORSCROLLBAR:
1620          writeWm("WM_CTLCOLORSCROLLBAR");
1621          break;
1622       case WM_CTLCOLORSTATIC:
1623          writeWm("WM_CTLCOLORSTATIC");
1624          break;
1625       case WM_MOUSEMOVE:
1626          writeWm("WM_MOUSEMOVE");
1627          break;
1628       case WM_LBUTTONDOWN:
1629          writeWm("WM_LBUTTONDOWN");
1630          break;
1631       case WM_LBUTTONUP:
1632          writeWm("WM_LBUTTONUP");
1633          break;
1634       case WM_LBUTTONDBLCLK:
1635          writeWm("WM_LBUTTONDBLCLK");
1636          break;
1637       case WM_RBUTTONDOWN:
1638          writeWm("WM_RBUTTONDOWN");
1639          break;
1640       case WM_RBUTTONUP:
1641          writeWm("WM_RBUTTONUP");
1642          break;
1643       case WM_RBUTTONDBLCLK:
1644          writeWm("WM_RBUTTONDBLCLK");
1645          break;
1646       case WM_MBUTTONDOWN:
1647          writeWm("WM_MBUTTONDOWN");
1648          break;
1649       case WM_MBUTTONUP:
1650          writeWm("WM_MBUTTONUP");
1651          break;
1652       case WM_MBUTTONDBLCLK:
1653          writeWm("WM_MBUTTONDBLCLK");
1654          break;
1655       case WM_PARENTNOTIFY:
1656          writeWm("WM_PARENTNOTIFY");
1657          break;
1658       case WM_ENTERMENULOOP:
1659          writeWm("WM_ENTERMENULOOP");
1660          break;
1661       case WM_EXITMENULOOP:
1662          writeWm("WM_EXITMENULOOP");
1663          break;
1664       case WM_NEXTMENU:
1665          writeWm("WM_NEXTMENU");
1666          break;
1667       case WM_SETFONT:
1668          writeWm("WM_SETFONT");
1669          break;
1670       case WM_GETFONT:
1671          writeWm("WM_GETFONT");
1672          break;
1673       case WM_USER:
1674          writeWm("WM_USER");
1675          break;
1676       case WM_NEXTDLGCTL:
1677          writeWm("WM_NEXTDLGCTL");
1678          break;
1679       case WM_CAPTURECHANGED:
1680          writeWm("WM_CAPTURECHANGED");
1681          break;
1682       case WM_WINDOWPOSCHANGING:
1683          writeWm("WM_WINDOWPOSCHANGING");
1684          break;
1685       case WM_WINDOWPOSCHANGED:
1686          writeWm("WM_WINDOWPOSCHANGED");
1687          break;
1688       case WM_DRAWITEM:
1689          writeWm("WM_DRAWITEM");
1690          break;
1691       case WM_CLEAR:
1692          writeWm("WM_CLEAR");
1693          break;
1694       case WM_CUT:
1695          writeWm("WM_CUT");
1696          break;
1697       case WM_COPY:
1698          writeWm("WM_COPY");
1699          break;
1700       case WM_PASTE:
1701          writeWm("WM_PASTE");
1702          break;
1703       case WM_MDITILE:
1704          writeWm("WM_MDITILE");
1705          break;
1706       case WM_MDICASCADE:
1707          writeWm("WM_MDICASCADE");
1708          break;
1709       case WM_MDIICONARRANGE:
1710          writeWm("WM_MDIICONARRANGE");
1711          break;
1712       case WM_MDIGETACTIVE:
1713          writeWm("WM_MDIGETACTIVE");
1714          break;
1715       case WM_MOUSEWHEEL:
1716          writeWm("WM_MOUSEWHEEL");
1717          break;
1718       case WM_MOUSEHOVER:
1719          writeWm("WM_MOUSEHOVER");
1720          break;
1721       case WM_MOUSELEAVE:
1722          writeWm("WM_MOUSELEAVE");
1723          break;
1724       case WM_PRINT:
1725          writeWm("WM_PRINT");
1726          break;
1727       case WM_PRINTCLIENT:
1728          writeWm("WM_PRINTCLIENT");
1729          break;
1730       case WM_MEASUREITEM:
1731          writeWm("WM_MEASUREITEM");
1732          break;
1733 
1734       default:
1735          if (m.msg >= WM_USER && m.msg <= 0x7FFF) {
1736             writeWm("WM_USER+" ~ std..string.toString(m.msg - WM_USER));
1737          } else if (m.msg >= 0xC000 && m.msg <= 0xFFFF) {
1738             writeWm("RegisterWindowMessage");
1739          } else {
1740             writeWm("?");
1741          }
1742       }
1743 
1744       Control ctrl;
1745       ctrl = Application.lookupHwnd(m.hWnd);
1746       writef("HWND=%d(0x%X) %s WPARAM=%d(0x%X) LPARAM=%d(0x%X)\n\n",
1747          cast(size_t) m.hWnd, cast(size_t) m.hWnd,
1748          ctrl ? ("DFLname='" ~ ctrl.name ~ "'") : "<nonDFL>", m.wParam,
1749          m.wParam, m.lParam, m.lParam);
1750 
1751       debug (MESSAGE_PAUSE) {
1752          Sleep(50);
1753       }
1754    }
1755 }
1756 
1757 extern (Windows) LRESULT dflWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) nothrow {
1758    //cprintf("HWND %p; WM %d(0x%X); WPARAM %d(0x%X); LPARAM %d(0x%X);\n", hwnd, msg, msg, wparam, wparam, lparam, lparam);
1759 
1760    if (msg == wmDfl) {
1761       switch (wparam) {
1762       case WPARAM_DFL_INVOKE: {
1763             InvokeData* pinv;
1764             pinv = cast(InvokeData*) lparam;
1765             try {
1766                pinv.result = pinv.dg(pinv.args);
1767             }
1768             catch (DThrowable e) {
1769                //Application.onThreadException(e);
1770                try {
1771                   pinv.exception = e;
1772                }
1773                catch (DThrowable e2) {
1774                   Application.onThreadException(e2);
1775                }
1776             }
1777          }
1778          return LRESULT_DFL_INVOKE;
1779 
1780       case WPARAM_DFL_INVOKE_SIMPLE: {
1781             InvokeSimpleData* pinv;
1782             pinv = cast(InvokeSimpleData*) lparam;
1783             try {
1784                pinv.dg();
1785             }
1786             catch (DThrowable e) {
1787                //Application.onThreadException(e);
1788                try {
1789                   pinv.exception = e;
1790                }
1791                catch (DThrowable e2) {
1792                   Application.onThreadException(e2);
1793                }
1794             }
1795          }
1796          return LRESULT_DFL_INVOKE;
1797 
1798       case WPARAM_DFL_DELAY_INVOKE:
1799          try {
1800             (cast(void function()) lparam)();
1801          }
1802          catch (DThrowable e) {
1803             Application.onThreadException(e);
1804          }
1805          break;
1806 
1807       case WPARAM_DFL_DELAY_INVOKE_PARAMS: {
1808             DflInvokeParam* p;
1809             p = cast(DflInvokeParam*) lparam;
1810             try {
1811                p.fp(Application.lookupHwnd(hwnd), p.params.ptr[0 .. p.nparams]);
1812             }
1813             catch (DThrowable e) {
1814                Application.onThreadException(e);
1815             }
1816             free(p);
1817          }
1818          break;
1819 
1820       default:
1821       }
1822    }
1823 
1824    Message dm = Message(hwnd, msg, wparam, lparam);
1825    Control ctrl;
1826 
1827    debug (SHOW_MESSAGE_INFO)
1828       showMessageInfo(dm);
1829 
1830    if (msg == WM_NCCREATE) {
1831       ctrl = Application.getCreatingControl();
1832       if (!ctrl) {
1833          debug (APP_PRINT)
1834             cprintf("Unable to add window 0x%X.\n", hwnd);
1835          return dm.result;
1836       }
1837       Application.creatingControl(null); // Reset.
1838 
1839       Application.controls[hwnd] = ctrl;
1840       ctrl.hwnd = hwnd;
1841       debug (APP_PRINT)
1842          cprintf("Added window 0x%X.\n", hwnd);
1843 
1844       //ctrl.finishCreating(hwnd);
1845       goto do_msg;
1846    }
1847 
1848    ctrl = Application.lookupHwnd(hwnd);
1849 
1850    if (!ctrl) {
1851       // Zombie...
1852       //return 1; // Returns correctly for most messages. e.g. WM_QUERYENDSESSION, WM_NCACTIVATE.
1853       dm.result = 1;
1854       version (DFL_NO_ZOMBIE_FORM) {
1855       } else {
1856          ctrl = cast(Control) cast(void*) GetPropA(hwnd, Application.ZOMBIE_PROP.ptr);
1857          if (ctrl) {
1858             ctrl.mustWndProc(dm);
1859          }
1860       }
1861       return dm.result;
1862    }
1863 
1864    if (ctrl) {
1865    do_msg:
1866       try {
1867          ctrl.mustWndProc(dm);
1868          if (!ctrl.preProcessMessage(dm)) {
1869             ctrl._wndProc(dm);
1870          }
1871       }
1872       catch (DThrowable e) {
1873          Application.onThreadException(e);
1874       }
1875    }
1876    return dm.result;
1877 }
1878 
1879 version (CUSTOM_MSG_HOOK) {
1880    alias CustomMsg = CWPRETSTRUCT;
1881 
1882    // Needs to be re-entrant.
1883    extern (Windows) LRESULT globalMsgHook(int code, WPARAM wparam, LPARAM lparam) {
1884       if (code == HC_ACTION) {
1885          CustomMsg* msg = cast(CustomMsg*) lparam;
1886          Control ctrl;
1887 
1888          switch (msg.message) {
1889             // ...
1890          }
1891       }
1892 
1893       return CallNextHookEx(Application.msghook, code, wparam, lparam);
1894    }
1895 } else {
1896    /+
1897    struct CustomMsg {
1898       HWND hwnd;
1899       UINT message;
1900       WPARAM wParam;
1901       LPARAM lParam;
1902    }
1903    +/
1904 }
1905 
1906 enum LRESULT LRESULT_DFL_INVOKE = 0x95FADF; // Magic number.
1907 
1908 struct InvokeData {
1909    Object delegate(Object[]) dg;
1910    Object[] args;
1911    Object result;
1912    DThrowable exception = null;
1913 }
1914 
1915 struct InvokeSimpleData {
1916    void delegate() dg;
1917    DThrowable exception = null;
1918 }
1919 
1920 UINT wmDfl;
1921 
1922 enum : WPARAM {
1923    WPARAM_DFL_INVOKE = 78,
1924    WPARAM_DFL_DELAY_INVOKE = 79,
1925    WPARAM_DFL_DELAY_INVOKE_PARAMS = 80,
1926    WPARAM_DFL_INVOKE_SIMPLE = 81,
1927 }
1928 
1929 struct DflInvokeParam {
1930    void function(Control, size_t[]) fp;
1931    size_t nparams;
1932    size_t[1] params;
1933 }
1934 
1935 version (DFL_NO_WM_GETCONTROLNAME) {
1936 } else {
1937    UINT wmGetControlName;
1938 }
1939 
1940 extern (Windows) {
1941    alias TrackMouseEventProc = BOOL function(LPTRACKMOUSEEVENT lpEventTrack);
1942    alias SetLayeredWindowAttributesProc = BOOL function(HWND, COLORREF, BYTE, DWORD);
1943 
1944    alias GetWindowThemeProc = HTHEME function(HWND);
1945    alias IsThemeBackgroundPartiallyTransparentProc = BOOL function(HTHEME hTheme,
1946       int iPartId, int iStateId);
1947    alias DrawThemeParentBackgroundProc = HRESULT function(HWND hwnd, HDC hdc, RECT* prc);
1948    alias SetThemeAppPropertiesProc = void function(DWORD dwFlags);
1949 }
1950 
1951 // Set version = SUPPORTS_MOUSE_TRACKING if it is guaranteed to be supported.
1952 TrackMouseEventProc trackMouseEvent;
1953 
1954 // Set version = SUPPORTS_OPACITY if it is guaranteed to be supported.
1955 SetLayeredWindowAttributesProc setLayeredWindowAttributes;
1956 
1957 /+
1958 GetWindowThemeProc getWindowTheme;
1959 IsThemeBackgroundPartiallyTransparentProc isThemeBackgroundPartiallyTransparent;
1960 DrawThemeParentBackgroundProc drawThemeParentBackground;
1961 SetThemeAppPropertiesProc setThemeAppProperties;
1962 +/
1963 
1964 enum CONTROL_CLASSNAME = "DFL_Control";
1965 enum FORM_CLASSNAME = "DFL_Form";
1966 enum TEXTBOX_CLASSNAME = "DFL_TextBox";
1967 enum LISTBOX_CLASSNAME = "DFL_ListBox";
1968 //enum LABEL_CLASSNAME = "DFL_Label";
1969 enum BUTTON_CLASSNAME = "DFL_Button";
1970 enum MDICLIENT_CLASSNAME = "DFL_MdiClient";
1971 enum RICHTEXTBOX_CLASSNAME = "DFL_RichTextBox";
1972 enum COMBOBOX_CLASSNAME = "DFL_ComboBox";
1973 enum TREEVIEW_CLASSNAME = "DFL_TreeView";
1974 enum TABCONTROL_CLASSNAME = "DFL_TabControl";
1975 enum LISTVIEW_CLASSNAME = "DFL_ListView";
1976 enum STATUSBAR_CLASSNAME = "DFL_StatusBar";
1977 enum PROGRESSBAR_CLASSNAME = "DFL_ProgressBar";
1978 
1979 WNDPROC textBoxPrevWndProc;
1980 WNDPROC listboxPrevWndProc;
1981 //WNDPROC labelPrevWndProc;
1982 WNDPROC buttonPrevWndProc;
1983 WNDPROC mdiclientPrevWndProc;
1984 WNDPROC richtextboxPrevWndProc;
1985 WNDPROC comboboxPrevWndProc;
1986 WNDPROC treeviewPrevWndProc;
1987 WNDPROC tabcontrolPrevWndProc;
1988 WNDPROC listviewPrevWndProc;
1989 WNDPROC statusbarPrevWndProc;
1990 WNDPROC progressbarPrevWndProc;
1991 
1992 LONG textBoxClassStyle;
1993 LONG listboxClassStyle;
1994 //LONG labelClassStyle;
1995 LONG buttonClassStyle;
1996 LONG mdiclientClassStyle;
1997 LONG richtextboxClassStyle;
1998 LONG comboboxClassStyle;
1999 LONG treeviewClassStyle;
2000 LONG tabcontrolClassStyle;
2001 LONG listviewClassStyle;
2002 LONG statusbarClassStyle;
2003 LONG progressbarClassStyle;
2004 
2005 HMODULE hmodRichtextbox;
2006 
2007 // DMD 0.93: CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS is not an expression
2008 //enum UINT WNDCLASS_STYLE = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
2009 //enum UINT WNDCLASS_STYLE = 11;
2010 
2011 //enum UINT WNDCLASS_STYLE = CS_DBLCLKS;
2012 // DMD 0.106: CS_DBLCLKS is not an expression
2013 enum UINT WNDCLASS_STYLE = 0x0008;
2014 
2015 extern (Windows) {
2016    alias InitCommonControlsExProc = BOOL function(LPINITCOMMONCONTROLSEX lpInitCtrls);
2017 }
2018 
2019 // For this to work properly on Windows 95, Internet Explorer 4.0 must be installed.
2020 void _initCommonControls(DWORD dwControls) {
2021    version (SUPPORTS_COMMON_CONTROLS_EX) {
2022       pragma(msg, "DFL: extended common controls supported at compile time");
2023 
2024       alias initProc = InitCommonControlsEx;
2025    } else {
2026       // Make sure InitCommonControlsEx() is in comctl32.dll,
2027       // otherwise use the old InitCommonControls().
2028 
2029       HMODULE hmodCommonControls;
2030       InitCommonControlsExProc initProc;
2031 
2032       hmodCommonControls = LoadLibraryA("comctl32.dll");
2033       if (!hmodCommonControls) // throw new DflException("Unable to load 'comctl32.dll'");
2034       {
2035          goto no_comctl32;
2036       }
2037 
2038       initProc = cast(InitCommonControlsExProc) GetProcAddress(hmodCommonControls,
2039          "InitCommonControlsEx");
2040       if (!initProc) {
2041          //FreeLibrary(hmodCommonControls);
2042       no_comctl32:
2043          InitCommonControls();
2044          return;
2045       }
2046    }
2047 
2048    INITCOMMONCONTROLSEX icce;
2049    icce.dwSize = INITCOMMONCONTROLSEX.sizeof;
2050    icce.dwICC = dwControls;
2051    initProc(&icce);
2052 }
2053 
2054 extern (C) {
2055    size_t C_refCountInc(void* p) {
2056       return Application._doref(p, 1);
2057    }
2058 
2059    // Returns the new ref count.
2060    size_t C_refCountDec(void* p) {
2061       return Application._doref(p, -1);
2062    }
2063 }
2064 
2065 static this() {
2066    dfl.internal.utf._utfinit();
2067 
2068    Application.tlsThreadFlags = TlsAlloc();
2069    Application.tlsControl = TlsAlloc();
2070    Application.tlsFilter = TlsAlloc();
2071    version (CUSTOM_MSG_HOOK)
2072       Application.tlsHook = TlsAlloc();
2073 
2074    wmDfl = RegisterWindowMessageA("WM_DFL");
2075    if (!wmDfl) {
2076       wmDfl = WM_USER + 0x7CD;
2077    }
2078 
2079    version (DFL_NO_WM_GETCONTROLNAME) {
2080    } else {
2081       wmGetControlName = RegisterWindowMessageA("WM_GETCONTROLNAME");
2082    }
2083 
2084    //InitCommonControls(); // Done later. Needs to be linked with comctl32.lib.
2085    OleInitialize(null); // Needs to be linked with ole32.lib.
2086 
2087    HMODULE user32 = GetModuleHandleA("user32.dll");
2088 
2089    version (SUPPORTS_MOUSE_TRACKING) {
2090       pragma(msg, "DFL: mouse tracking supported at compile time");
2091 
2092       trackMouseEvent = &TrackMouseEvent;
2093    } else {
2094       trackMouseEvent = cast(TrackMouseEventProc) GetProcAddress(user32, "TrackMouseEvent");
2095       if (!trackMouseEvent) { // Must be Windows 95; check if common controls has it (IE 5.5).
2096          trackMouseEvent = cast(TrackMouseEventProc) GetProcAddress(
2097             GetModuleHandleA("comctl32.dll"), "_TrackMouseEvent");
2098       }
2099    }
2100 
2101    version (SUPPORTS_OPACITY) {
2102       pragma(msg, "DFL: opacity supported at compile time");
2103 
2104       setLayeredWindowAttributes = &SetLayeredWindowAttributes;
2105    } else {
2106       setLayeredWindowAttributes = cast(SetLayeredWindowAttributesProc) GetProcAddress(user32,
2107          "SetLayeredWindowAttributes");
2108    }
2109 }
2110 
2111 static ~this() {
2112    version (DFL_NO_MENUS) {
2113    } else {
2114       Application.sdtorFreeAllMenus();
2115    }
2116 
2117    if (hmodRichtextbox) {
2118       FreeLibrary(hmodRichtextbox);
2119    }
2120 }
2121 
2122 void _unableToInit(Dstring what) {
2123    /+if(what.length > 4
2124          && what[0] == 'D' && what[1] == 'F'
2125          && what[2] == 'L' && what[3] == '_')+/
2126    what = what[4 .. what.length];
2127    throw new DflException("Unable to initialize " ~ what);
2128 }
2129 
2130 void _initInstance() {
2131    return _initInstance(GetModuleHandleA(null));
2132 }
2133 
2134 void _initInstance(HINSTANCE inst)
2135 in {
2136    assert(!Application.hinst);
2137    assert(inst);
2138 }
2139 body {
2140    Application.hinst = inst;
2141 
2142    dfl.internal.utf.WndClass wc;
2143    wc.wc.style = WNDCLASS_STYLE;
2144    wc.wc.hInstance = inst;
2145    wc.wc.lpfnWndProc = &dflWndProc;
2146 
2147    // Control wndclass.
2148    wc.className = CONTROL_CLASSNAME;
2149    if (!dfl.internal.utf.registerClass(wc)) {
2150       _unableToInit(CONTROL_CLASSNAME);
2151    }
2152 
2153    // Form wndclass.
2154    wc.wc.cbWndExtra = DLGWINDOWEXTRA;
2155    wc.className = FORM_CLASSNAME;
2156    if (!dfl.internal.utf.registerClass(wc)) {
2157       _unableToInit(FORM_CLASSNAME);
2158    }
2159 }
2160 
2161 extern (Windows) {
2162    void _initTextBox() {
2163       if (!textBoxPrevWndProc) {
2164          dfl.internal.utf.WndClass info;
2165          textBoxPrevWndProc = superClass(HINSTANCE.init, "EDIT", TEXTBOX_CLASSNAME,
2166             info);
2167          if (!textBoxPrevWndProc) {
2168             _unableToInit(TEXTBOX_CLASSNAME);
2169          }
2170          textBoxClassStyle = info.wc.style;
2171       }
2172    }
2173 
2174    void _initListbox() {
2175       if (!listboxPrevWndProc) {
2176          dfl.internal.utf.WndClass info;
2177          listboxPrevWndProc = superClass(HINSTANCE.init, "LISTBOX", LISTBOX_CLASSNAME,
2178             info);
2179          if (!listboxPrevWndProc) {
2180             _unableToInit(LISTBOX_CLASSNAME);
2181          }
2182          listboxClassStyle = info.wc.style;
2183       }
2184    }
2185 
2186    /+
2187    void _initLabel() {
2188       if(!labelPrevWndProc) {
2189          dfl.internal.utf.WndClass info;
2190          labelPrevWndProc = superClass(HINSTANCE.init, "STATIC", LABEL_CLASSNAME, info);
2191          if(!labelPrevWndProc) {
2192             _unableToInit(LABEL_CLASSNAME);
2193          }
2194          labelClassStyle = info.wc.style;
2195       }
2196    }
2197    +/
2198 
2199    void _initButton() {
2200       if (!buttonPrevWndProc) {
2201          dfl.internal.utf.WndClass info;
2202          buttonPrevWndProc = superClass(HINSTANCE.init, "BUTTON", BUTTON_CLASSNAME,
2203             info);
2204          if (!buttonPrevWndProc) {
2205             _unableToInit(BUTTON_CLASSNAME);
2206          }
2207          buttonClassStyle = info.wc.style;
2208       }
2209    }
2210 
2211    void _initMdiclient() {
2212       if (!mdiclientPrevWndProc) {
2213          dfl.internal.utf.WndClass info;
2214          mdiclientPrevWndProc = superClass(HINSTANCE.init, "MDICLIENT", MDICLIENT_CLASSNAME,
2215             info);
2216          if (!mdiclientPrevWndProc) {
2217             _unableToInit(MDICLIENT_CLASSNAME);
2218          }
2219          mdiclientClassStyle = info.wc.style;
2220       }
2221    }
2222 
2223    void _initRichtextbox() {
2224       if (!richtextboxPrevWndProc) {
2225          if (!hmodRichtextbox) {
2226             hmodRichtextbox = LoadLibraryA("riched20.dll");
2227             if (!hmodRichtextbox) {
2228                throw new DflException("Unable to load 'riched20.dll'");
2229             }
2230          }
2231 
2232          Dstring classname;
2233          if (dfl.internal.utf.useUnicode) {
2234             classname = "RichEdit20W";
2235          } else {
2236             classname = "RichEdit20A";
2237          }
2238 
2239          dfl.internal.utf.WndClass info;
2240          richtextboxPrevWndProc = superClass(HINSTANCE.init, classname,
2241             RICHTEXTBOX_CLASSNAME, info);
2242          if (!richtextboxPrevWndProc) {
2243             _unableToInit(RICHTEXTBOX_CLASSNAME);
2244          }
2245          richtextboxClassStyle = info.wc.style;
2246       }
2247    }
2248 
2249    void _initCombobox() {
2250       if (!comboboxPrevWndProc) {
2251          dfl.internal.utf.WndClass info;
2252          comboboxPrevWndProc = superClass(HINSTANCE.init, "COMBOBOX", COMBOBOX_CLASSNAME,
2253             info);
2254          if (!comboboxPrevWndProc) {
2255             _unableToInit(COMBOBOX_CLASSNAME);
2256          }
2257          comboboxClassStyle = info.wc.style;
2258       }
2259    }
2260 
2261    void _initTreeview() {
2262       if (!treeviewPrevWndProc) {
2263          _initCommonControls(ICC_TREEVIEW_CLASSES);
2264 
2265          dfl.internal.utf.WndClass info;
2266          treeviewPrevWndProc = superClass(HINSTANCE.init, "SysTreeView32",
2267             TREEVIEW_CLASSNAME, info);
2268          if (!treeviewPrevWndProc) {
2269             _unableToInit(TREEVIEW_CLASSNAME);
2270          }
2271          treeviewClassStyle = info.wc.style;
2272       }
2273    }
2274 
2275    void _initTabcontrol() {
2276       if (!tabcontrolPrevWndProc) {
2277          _initCommonControls(ICC_TAB_CLASSES);
2278 
2279          dfl.internal.utf.WndClass info;
2280          tabcontrolPrevWndProc = superClass(HINSTANCE.init, "SysTabControl32",
2281             TABCONTROL_CLASSNAME, info);
2282          if (!tabcontrolPrevWndProc) {
2283             _unableToInit(TABCONTROL_CLASSNAME);
2284          }
2285          tabcontrolClassStyle = info.wc.style;
2286       }
2287    }
2288 
2289    void _initListview() {
2290       if (!listviewPrevWndProc) {
2291          _initCommonControls(ICC_LISTVIEW_CLASSES);
2292 
2293          dfl.internal.utf.WndClass info;
2294          listviewPrevWndProc = superClass(HINSTANCE.init, "SysListView32",
2295             LISTVIEW_CLASSNAME, info);
2296          if (!listviewPrevWndProc) {
2297             _unableToInit(LISTVIEW_CLASSNAME);
2298          }
2299          listviewClassStyle = info.wc.style;
2300       }
2301    }
2302 
2303    void _initStatusbar() {
2304       if (!statusbarPrevWndProc) {
2305          _initCommonControls(ICC_WIN95_CLASSES);
2306 
2307          dfl.internal.utf.WndClass info;
2308          statusbarPrevWndProc = superClass(HINSTANCE.init,
2309             "msctls_statusbar32", STATUSBAR_CLASSNAME, info);
2310          if (!statusbarPrevWndProc) {
2311             _unableToInit(STATUSBAR_CLASSNAME);
2312          }
2313          statusbarClassStyle = info.wc.style;
2314       }
2315    }
2316 
2317    void _initProgressbar() {
2318       if (!progressbarPrevWndProc) {
2319          _initCommonControls(ICC_PROGRESS_CLASS);
2320 
2321          dfl.internal.utf.WndClass info;
2322          progressbarPrevWndProc = superClass(HINSTANCE.init,
2323             "msctls_progress32", PROGRESSBAR_CLASSNAME, info);
2324          if (!progressbarPrevWndProc) {
2325             _unableToInit(PROGRESSBAR_CLASSNAME);
2326          }
2327          progressbarClassStyle = info.wc.style;
2328       }
2329    }
2330 }
2331 
2332 WNDPROC _superClass(HINSTANCE hinst, Dstring className, Dstring newClassName, out WNDCLASSA getInfo) { // deprecated
2333    WNDPROC wndProc;
2334 
2335    if (!GetClassInfoA(hinst, unsafeStringz(className), &getInfo)) { // TODO: unicode.
2336       throw new DflException("Unable to obtain information for window class '" ~ className ~ "'");
2337    }
2338 
2339    wndProc = getInfo.lpfnWndProc;
2340    getInfo.lpfnWndProc = &dflWndProc;
2341 
2342    getInfo.style &= ~CS_GLOBALCLASS;
2343    getInfo.hCursor = HCURSOR.init;
2344    getInfo.lpszClassName = unsafeStringz(newClassName);
2345    getInfo.hInstance = Application.getInstance();
2346 
2347    if (!RegisterClassA(&getInfo)) // TODO: unicode.
2348       //throw new DflException("Unable to register window class '" ~ newClassName ~ "'");
2349       {
2350       return null;
2351    }
2352    return wndProc;
2353 }
2354 
2355 public:
2356 
2357 // Returns the old wndProc.
2358 // This is the old, unsafe, unicode-unfriendly function for superclassing.
2359 deprecated WNDPROC superClass(HINSTANCE hinst, Dstring className,
2360    Dstring newClassName, out WNDCLASSA getInfo) { // package
2361    return _superClass(hinst, className, newClassName, getInfo);
2362 }
2363 
2364 deprecated WNDPROC superClass(HINSTANCE hinst, Dstring className, Dstring newClassName) { // package
2365    WNDCLASSA info;
2366    return _superClass(hinst, className, newClassName, info);
2367 }
2368 
2369 // Returns the old wndProc.
2370 WNDPROC superClass(HINSTANCE hinst, Dstring className, Dstring newClassName,
2371    out dfl.internal.utf.WndClass getInfo) { // package
2372    WNDPROC wndProc;
2373 
2374    if (!dfl.internal.utf.getClassInfo(hinst, className, getInfo)) {
2375       throw new DflException("Unable to obtain information for window class '" ~ className ~ "'");
2376    }
2377 
2378    wndProc = getInfo.wc.lpfnWndProc;
2379    getInfo.wc.lpfnWndProc = &dflWndProc;
2380 
2381    getInfo.wc.style &= ~CS_GLOBALCLASS;
2382    getInfo.wc.hCursor = HCURSOR.init;
2383    getInfo.className = newClassName;
2384    getInfo.wc.hInstance = Application.getInstance();
2385 
2386    if (!dfl.internal.utf.registerClass(getInfo)) //throw new DflException("Unable to register window class '" ~ newClassName ~ "'");
2387    {
2388       return null;
2389    }
2390    return wndProc;
2391 }