1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004 - 2006 Novell, Inc.
23 // Peter Bartok pbartok@novell.com
24 // Daniel Nauck (dna(at)mono-project(dot)de)
31 using Microsoft.Win32;
34 using System.ComponentModel;
35 using System.Collections;
36 using System.Diagnostics;
37 using System.Globalization;
39 using System.Reflection;
40 using System.Runtime.InteropServices;
41 using System.Threading;
44 using System.Windows.Forms.VisualStyles;
47 namespace System.Windows.Forms {
48 public sealed class Application {
49 internal class MWFThread {
51 private ApplicationContext context;
52 private bool messageloop_started;
53 private bool handling_exception;
54 private int thread_id;
56 private static Hashtable threads = new Hashtable();
62 #endregion // Constructors
65 public ApplicationContext Context {
66 get { return context; }
67 set { context = value; }
70 public bool MessageLoop {
71 get { return messageloop_started; }
72 set { messageloop_started = value; }
75 public bool HandlingException {
76 get { return handling_exception; }
77 set { handling_exception = value; }
81 public static int LoopCount {
86 foreach (MWFThread thread in threads.Values) {
87 if (thread.messageloop_started)
96 public static MWFThread Current {
102 thread = (MWFThread)threads[Thread.CurrentThread.GetHashCode()];
103 if (thread == null) {
104 thread = new MWFThread();
105 thread.thread_id = Thread.CurrentThread.GetHashCode();
106 threads[thread.thread_id] = thread;
113 #endregion // Properties
117 if (context != null) {
118 context.ExitThread();
122 if (Application.ThreadExit != null) {
123 Application.ThreadExit(null, EventArgs.Empty);
126 if (LoopCount == 0) {
127 if (Application.ApplicationExit != null) {
128 Application.ApplicationExit(null, EventArgs.Empty);
132 ((MWFThread)threads[thread_id]).MessageLoop = false;
134 #endregion // Methods
137 private static bool browser_embedded = false;
138 private static InputLanguage input_language = InputLanguage.CurrentInputLanguage;
139 private static string safe_caption_format = "{1} - {0} - {2}";
140 private static ArrayList message_filters = new ArrayList();
141 private static FormCollection forms = new FormCollection ();
144 private static bool use_wait_cursor = false;
145 private static ToolStrip keyboard_capture;
146 private static VisualStyleState visual_style_state = VisualStyleState.ClientAndNonClientAreasEnabled;
149 private Application () {
152 #region Private Methods
153 internal static void CloseForms(Thread thread) {
155 Console.WriteLine(" CloseForms({0}) called", thread);
158 ArrayList forms_to_close = new ArrayList ();
161 foreach (Form f in forms) {
162 if (thread == null || thread == f.creator_thread)
163 forms_to_close.Add (f);
166 foreach (Form f in forms_to_close) {
168 Console.WriteLine(" Closing form {0}", f);
174 #endregion // Private methods
176 #region Public Static Properties
177 public static bool AllowQuit {
179 return !browser_embedded;
183 public static string CommonAppDataPath {
185 return Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
189 public static RegistryKey CommonAppDataRegistry {
193 key = Registry.LocalMachine.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
199 public static string CompanyName {
201 Assembly assembly = Assembly.GetEntryAssembly ();
202 if (assembly == null)
203 assembly = Assembly.GetCallingAssembly ();
204 if (assembly == null)
207 AssemblyCompanyAttribute[] attrs = (AssemblyCompanyAttribute[]) assembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), true);
209 if ((attrs != null) && attrs.Length>0) {
210 return attrs[0].Company;
213 return assembly.GetName().Name;
217 public static CultureInfo CurrentCulture {
219 return Thread.CurrentThread.CurrentUICulture;
224 Thread.CurrentThread.CurrentUICulture=value;
228 public static InputLanguage CurrentInputLanguage {
230 return input_language;
234 input_language=value;
238 public static string ExecutablePath {
240 return Assembly.GetEntryAssembly().Location;
244 public static string LocalUserAppDataPath {
246 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), CompanyName), ProductName), ProductVersion);
250 public static bool MessageLoop {
252 return MWFThread.Current.MessageLoop;
256 public static string ProductName {
258 Assembly assembly = Assembly.GetEntryAssembly ();
259 if (assembly == null)
260 assembly = Assembly.GetCallingAssembly ();
261 if (assembly == null)
264 AssemblyProductAttribute[] attrs = (AssemblyProductAttribute[]) assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), true);
266 if ((attrs != null) && attrs.Length>0) {
267 return attrs[0].Product;
270 return assembly.GetName ().Name;
274 public static string ProductVersion {
278 Assembly assembly = Assembly.GetEntryAssembly ();
279 if (assembly == null)
280 assembly = Assembly.GetCallingAssembly ();
281 if (assembly == null)
284 version = assembly.GetName ().Version.ToString ();
290 public static string SafeTopLevelCaptionFormat {
292 return safe_caption_format;
296 safe_caption_format=value;
300 public static string StartupPath {
302 return Path.GetDirectoryName(Application.ExecutablePath);
306 public static string UserAppDataPath {
308 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), CompanyName), ProductName), ProductVersion);
312 public static RegistryKey UserAppDataRegistry {
316 key = Registry.CurrentUser.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
324 public static bool UseWaitCursor {
326 return use_wait_cursor;
329 use_wait_cursor = value;
330 if (use_wait_cursor) {
331 foreach (Form form in OpenForms) {
332 form.Cursor = Cursors.WaitCursor;
338 public static bool RenderWithVisualStyles {
340 if (VisualStyleInformation.IsSupportedByOS)
342 if (!VisualStyleInformation.IsEnabledByUser)
345 if (!XplatUI.ThemesEnabled)
348 if (Application.VisualStyleState == VisualStyleState.ClientAndNonClientAreasEnabled)
351 if (Application.VisualStyleState == VisualStyleState.ClientAreaEnabled)
359 public static VisualStyleState VisualStyleState {
360 get { return Application.visual_style_state; }
361 set { Application.visual_style_state = value; }
367 #region Public Static Methods
368 public static void AddMessageFilter(IMessageFilter value) {
369 lock (message_filters) {
370 message_filters.Add(value);
374 public static void DoEvents() {
378 public static void EnableVisualStyles() {
379 XplatUI.EnableThemes();
383 [EditorBrowsable (EditorBrowsableState.Advanced)]
386 static bool FilterMessage (ref Message message)
388 lock (message_filters)
389 foreach (IMessageFilter filter in message_filters)
390 if (filter.PreFilterMessage (ref message))
398 // If true, it uses GDI+, performance reasons were quoted
400 static internal bool use_compatible_text_rendering = true;
402 public static void SetCompatibleTextRenderingDefault (bool defaultValue)
404 use_compatible_text_rendering = defaultValue;
407 public static FormCollection OpenForms {
413 [MonoNotSupported ("Only applies when Winforms is being hosted by an unmanaged app.")]
414 [EditorBrowsable (EditorBrowsableState.Advanced)]
415 public static void RegisterMessageLoop (MessageLoopCallback callback)
419 [MonoNotSupported ("Empty stub.")]
420 public static bool SetSuspendState (PowerState state, bool force, bool disableWakeEvent)
425 [MonoNotSupported ("Empty stub.")]
426 public static void SetUnhandledExceptionMode (UnhandledExceptionMode mode)
428 //FIXME: a stub to fill
431 [MonoNotSupported ("Empty stub.")]
432 public static void SetUnhandledExceptionMode (UnhandledExceptionMode mode, bool threadScope)
434 //FIXME: a stub to fill
437 [MonoNotSupported ("Only applies when Winforms is being hosted by an unmanaged app.")]
438 [EditorBrowsable (EditorBrowsableState.Advanced)]
439 public static void UnregisterMessageLoop ()
443 [EditorBrowsable (EditorBrowsableState.Advanced)]
444 public static void RaiseIdle (EventArgs e)
446 XplatUI.RaiseIdle (e);
449 public static void Restart ()
451 //FIXME: ClickOnce stuff using the Update or UpdateAsync methods.
452 //FIXME: SecurityPermission: Restart () requires IsUnrestricted permission.
454 if (Assembly.GetEntryAssembly () == null)
455 throw new NotSupportedException ("The method 'Restart' is not supported by this application type.");
457 string mono_path = null;
460 PropertyInfo gac = typeof (Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
461 MethodInfo get_gac = null;
463 get_gac = gac.GetGetMethod (true);
465 if (get_gac != null) {
466 string gac_path = Path.GetDirectoryName ((string)get_gac.Invoke (null, null));
467 string mono_prefix = Path.GetDirectoryName (Path.GetDirectoryName (gac_path));
469 if (XplatUI.RunningOnUnix) {
470 mono_path = Path.Combine (mono_prefix, "bin/mono");
471 if (!File.Exists (mono_path))
474 mono_path = Path.Combine (mono_prefix, "bin\\mono.bat");
476 if (!File.Exists (mono_path))
477 mono_path = Path.Combine (mono_prefix, "bin\\mono.exe");
479 if (!File.Exists (mono_path))
480 mono_path = Path.Combine (mono_prefix, "mono\\mono\\mini\\mono.exe");
482 if (!File.Exists (mono_path))
483 throw new FileNotFoundException (string.Format ("Windows mono path not found: '{0}'", mono_path));
487 //Get command line arguments
488 StringBuilder argsBuilder = new StringBuilder ();
489 string[] args = Environment.GetCommandLineArgs ();
490 for (int i = 0; i < args.Length; i++)
492 argsBuilder.Append (string.Format ("\"{0}\" ", args[i]));
494 string arguments = argsBuilder.ToString ();
495 ProcessStartInfo procInfo = Process.GetCurrentProcess ().StartInfo;
497 if (mono_path == null) { //it is .NET on Windows
498 procInfo.FileName = args[0];
499 procInfo.Arguments = arguments.Remove (0, args[0].Length + 3); //1 space and 2 quotes
502 procInfo.Arguments = arguments;
503 procInfo.FileName = mono_path;
506 procInfo.WorkingDirectory = Environment.CurrentDirectory;
509 Process.Start (procInfo);
513 public static void Exit() {
514 XplatUI.PostQuitMessage(0);
519 [EditorBrowsable (EditorBrowsableState.Advanced)]
520 public static void Exit (CancelEventArgs e)
522 ArrayList forms_to_close;
525 forms_to_close = new ArrayList (forms);
527 foreach (Form f in forms_to_close) {
528 // Give each form a chance to cancel the Application.Exit
529 e.Cancel = f.FireClosingEvents (CloseReason.ApplicationExitCall);
539 XplatUI.PostQuitMessage (0);
543 public static void ExitThread() {
544 CloseForms(Thread.CurrentThread);
545 // this might not be right - need to investigate (somehow) if a WM_QUIT message is generated here
546 XplatUI.PostQuitMessage(0);
549 public static ApartmentState OleRequired() {
550 //throw new NotImplementedException("OLE Not supported by this System.Windows.Forms implementation");
551 return ApartmentState.Unknown;
554 public static void OnThreadException(Exception t) {
555 if (MWFThread.Current.HandlingException) {
556 /* we're already handling an exception and we got
557 another one? print it out and exit, this means
558 we've got a runtime/SWF bug. */
559 Console.WriteLine (t);
560 // Don't use Application.Exit here, since it may cause a stack overflow
561 // in certain cases. It's however hard to reproduce since it seems to
562 // be depending on when the GC kicks in.
567 MWFThread.Current.HandlingException = true;
569 if (Application.ThreadException != null) {
570 Application.ThreadException(null, new ThreadExceptionEventArgs(t));
574 if (SystemInformation.UserInteractive) {
575 Form form = new ThreadExceptionDialog (t);
578 Console.WriteLine (t.ToString ());
582 MWFThread.Current.HandlingException = false;
586 public static void RemoveMessageFilter(IMessageFilter filter) {
587 lock (message_filters) {
588 message_filters.Remove(filter);
592 public static void Run() {
593 RunLoop(false, new ApplicationContext());
596 public static void Run(Form mainForm) {
597 RunLoop(false, new ApplicationContext(mainForm));
600 public static void Run(ApplicationContext context) {
601 RunLoop(false, context);
604 private static void DisableFormsForModalLoop (Queue toplevels, ApplicationContext context)
609 IEnumerator control = forms.GetEnumerator ();
611 while (control.MoveNext ()) {
612 f = (Form)control.Current;
614 // Don't disable the main form.
615 if (f == context.MainForm) {
619 // Don't disable any children of the main form.
620 // These do not have to be MDI children.
622 bool is_child_of_main = false; ;
625 if (current.Parent == context.MainForm) {
626 is_child_of_main = true;
629 current = current.Parent;
630 } while (current != null);
632 if (is_child_of_main)
636 if (f.IsHandleCreated && XplatUI.IsEnabled (f.Handle)) {
638 Console.WriteLine(" Disabling form {0}", f);
640 XplatUI.EnableWindow (f.Handle, false);
641 toplevels.Enqueue (f);
649 private static void EnableFormsForModalLoop (Queue toplevels, ApplicationContext context)
651 while (toplevels.Count > 0) {
653 Console.WriteLine(" Re-Enabling form form {0}", toplevels.Peek());
655 Form c = (Form)toplevels.Dequeue ();
656 if (c.IsHandleCreated) {
657 XplatUI.EnableWindow (c.window.Handle, true);
658 context.MainForm = c;
662 Console.WriteLine(" Done with the re-enable");
666 internal static void RunLoop(bool Modal, ApplicationContext context) {
671 ApplicationContext previous_thread_context;
673 thread = MWFThread.Current;
676 * There is a NotWorking test for this, but since we are using this method both for Form.ShowDialog as for ApplicationContexts we'll
677 * fail on nested ShowDialogs, so disable the check for the moment.
679 //if (thread.MessageLoop) {
680 // throw new InvalidOperationException ("Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead.");
685 if (context == null) {
686 context = new ApplicationContext();
689 previous_thread_context = thread.Context;
690 thread.Context = context;
692 if (context.MainForm != null) {
693 context.MainForm.is_modal = Modal;
694 context.MainForm.context = context;
695 context.MainForm.closing = false;
696 context.MainForm.Visible = true; // Cannot use Show() or scaling gets confused by menus
697 // XXX the above line can be used to close the form. another problem with our handling of Show/Activate.
698 if (context.MainForm != null)
699 context.MainForm.Activate();
703 Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
707 toplevels = new Queue ();
708 DisableFormsForModalLoop (toplevels, context);
710 // FIXME - need activate?
711 /* make sure the MainForm is enabled */
712 if (context.MainForm != null) {
713 XplatUI.EnableWindow (context.MainForm.Handle, true);
714 XplatUI.SetModal(context.MainForm.Handle, true);
720 queue_id = XplatUI.StartLoop(Thread.CurrentThread);
721 thread.MessageLoop = true;
725 while (!quit && XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) {
726 Message m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
728 if (Application.FilterMessage (ref m))
731 switch((Msg)msg.message) {
733 case Msg.WM_SYSKEYDOWN:
737 case Msg.WM_SYSKEYUP:
739 c = Control.FromHandle(msg.hwnd);
742 // If we have a control with keyboard capture (usually a *Strip)
743 // give it the message, and then drop the message
744 if (keyboard_capture != null) {
745 // WM_SYSKEYUP does not make it into ProcessCmdKey, so do it here
746 if ((Msg)m.Msg == Msg.WM_SYSKEYUP)
747 if (m.WParam.ToInt32() == (int)Keys.Menu) {
748 keyboard_capture.GetTopLevelToolStrip ().Dismiss (ToolStripDropDownCloseReason.Keyboard);
752 m.HWnd = keyboard_capture.Handle;
754 if (keyboard_capture.PreProcessControlMessageInternal (ref m) != PreProcessControlState.MessageProcessed && (m.Msg == (int)Msg.WM_KEYDOWN && !keyboard_capture.ProcessControlMnemonic ((char)m.WParam))) {
755 if (c == null || c.Parent == null || !(c.Parent is ToolStrip))
764 if ((c != null) && c.PreProcessControlMessageInternal (ref m) != PreProcessControlState.MessageProcessed) {
769 case Msg.WM_LBUTTONDOWN:
770 case Msg.WM_MBUTTONDOWN:
771 case Msg.WM_RBUTTONDOWN:
772 if (keyboard_capture != null) {
773 Control c2 = Control.FromHandle (msg.hwnd);
775 // If we clicked a ToolStrip, we have to make sure it isn't
776 // the one we are on, or any of its parents or children
777 // If we clicked off the dropped down menu, release everything
778 if (c2 is ToolStrip) {
779 if ((c2 as ToolStrip).GetTopLevelToolStrip () != keyboard_capture.GetTopLevelToolStrip ())
780 ToolStripManager.FireAppClicked ();
782 ToolStripManager.FireAppClicked ();
788 quit = true; // make sure we exit
791 XplatUI.TranslateMessage(ref msg);
792 XplatUI.DispatchMessage(ref msg);
796 // Handle exit, Form might have received WM_CLOSE and set 'closing' in response
797 if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) {
799 XplatUI.PostQuitMessage(0);
806 Console.WriteLine(" RunLoop loop left");
809 thread.MessageLoop = false;
810 XplatUI.EndLoop(Thread.CurrentThread);
813 Form old = context.MainForm;
815 context.MainForm = null;
817 EnableFormsForModalLoop (toplevels, context);
819 if (context.MainForm != null && context.MainForm.IsHandleCreated) {
820 XplatUI.SetModal(context.MainForm.Handle, false);
823 Console.WriteLine(" Done with the SetModal");
825 old.RaiseCloseEvents (true);
826 old.is_modal = false;
830 Console.WriteLine("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
832 if (context.MainForm != null) {
833 context.MainForm.context = null;
834 context.MainForm = null;
837 thread.Context = previous_thread_context;
844 #endregion // Public Static Methods
847 public static event EventHandler ApplicationExit;
849 public static event EventHandler Idle {
851 XplatUI.Idle += value;
854 XplatUI.Idle -= value;
858 public static event EventHandler ThreadExit;
859 public static event ThreadExceptionEventHandler ThreadException;
862 [EditorBrowsable (EditorBrowsableState.Advanced)]
863 public static event EventHandler EnterThreadModal;
865 [EditorBrowsable (EditorBrowsableState.Advanced)]
866 public static event EventHandler LeaveThreadModal;
870 #region Public Delegates
872 [EditorBrowsable (EditorBrowsableState.Advanced)]
873 public delegate bool MessageLoopCallback ();
877 #region Internal Properties
879 internal static ToolStrip KeyboardCapture {
880 get { return keyboard_capture; }
881 set { keyboard_capture = value; }
886 #region Internal Methods
887 internal static void AddForm (Form f)
893 internal static void RemoveForm (Form f)