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