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