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