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 ();
142 private static bool use_wait_cursor = false;
145 private static VisualStyleState visual_style_state = VisualStyleState.ClientAndNonClientAreasEnabled;
148 private Application () {
151 #region Private Methods
152 internal static void CloseForms(Thread thread) {
154 Console.WriteLine(" CloseForms({0}) called", thread);
157 ArrayList forms_to_close = new ArrayList ();
160 foreach (Form f in forms) {
161 if (thread == null || thread == f.creator_thread)
162 forms_to_close.Add (f);
165 foreach (Form f in forms_to_close) {
167 Console.WriteLine(" Closing form {0}", f);
173 #endregion // Private methods
175 #region Public Static Properties
176 public static bool AllowQuit {
178 return !browser_embedded;
182 public static string CommonAppDataPath {
184 return Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
188 public static RegistryKey CommonAppDataRegistry {
192 key = Registry.LocalMachine.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
198 public static string CompanyName {
200 AssemblyCompanyAttribute[] attrs = (AssemblyCompanyAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), true);
202 if ((attrs != null) && attrs.Length>0) {
203 return attrs[0].Company;
206 return Assembly.GetEntryAssembly().GetName().Name;
210 public static CultureInfo CurrentCulture {
212 return Thread.CurrentThread.CurrentUICulture;
217 Thread.CurrentThread.CurrentUICulture=value;
221 public static InputLanguage CurrentInputLanguage {
223 return input_language;
227 input_language=value;
231 public static string ExecutablePath {
233 return Assembly.GetEntryAssembly().Location;
237 public static string LocalUserAppDataPath {
239 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), CompanyName), ProductName), ProductVersion);
243 public static bool MessageLoop {
245 return MWFThread.Current.MessageLoop;
249 public static string ProductName {
251 AssemblyProductAttribute[] attrs = (AssemblyProductAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), true);
253 if ((attrs != null) && attrs.Length>0) {
254 return attrs[0].Product;
257 return Assembly.GetEntryAssembly().GetName().Name;
261 public static string ProductVersion {
265 version = Assembly.GetEntryAssembly().GetName().Version.ToString();
271 public static string SafeTopLevelCaptionFormat {
273 return safe_caption_format;
277 safe_caption_format=value;
281 public static string StartupPath {
283 return Path.GetDirectoryName(Application.ExecutablePath);
287 public static string UserAppDataPath {
289 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), CompanyName), ProductName), ProductVersion);
293 public static RegistryKey UserAppDataRegistry {
297 key = Registry.CurrentUser.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
305 public static bool UseWaitCursor {
307 return use_wait_cursor;
310 use_wait_cursor = value;
311 if (use_wait_cursor) {
312 foreach (Form form in OpenForms) {
313 form.Cursor = Cursors.WaitCursor;
319 public static bool RenderWithVisualStyles {
321 if (VisualStyleInformation.IsSupportedByOS)
323 if (!VisualStyleInformation.IsEnabledByUser)
326 if (!XplatUI.ThemesEnabled)
329 if (Application.VisualStyleState == VisualStyleState.ClientAndNonClientAreasEnabled)
332 if (Application.VisualStyleState == VisualStyleState.ClientAreaEnabled)
340 public static VisualStyleState VisualStyleState {
341 get { return Application.visual_style_state; }
342 set { Application.visual_style_state = value; }
348 #region Public Static Methods
349 public static void AddMessageFilter(IMessageFilter value) {
350 lock (message_filters) {
351 message_filters.Add(value);
355 public static void DoEvents() {
359 public static void EnableVisualStyles() {
360 XplatUI.EnableThemes();
365 // If true, it uses GDI+, performance reasons were quoted
367 static internal bool use_compatible_text_rendering = true;
369 public static void SetCompatibleTextRenderingDefault (bool defaultValue)
371 use_compatible_text_rendering = defaultValue;
374 public static FormCollection OpenForms {
380 [MonoNotSupported ("Empty stub.")]
381 public static void SetUnhandledExceptionMode (UnhandledExceptionMode mode)
383 //FIXME: a stub to fill
386 public static void Restart ()
388 //FIXME: ClickOnce stuff using the Update or UpdateAsync methods.
389 //FIXME: SecurityPermission: Restart () requires IsUnrestricted permission.
391 if (Assembly.GetEntryAssembly () == null)
392 throw new NotSupportedException ("The method 'Restart' is not supported by this application type.");
394 string mono_path = null;
397 PropertyInfo gac = typeof (Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
398 MethodInfo get_gac = null;
400 get_gac = gac.GetGetMethod (true);
402 if (get_gac != null) {
403 string gac_path = Path.GetDirectoryName ((string)get_gac.Invoke (null, null));
404 string mono_prefix = Path.GetDirectoryName (Path.GetDirectoryName (gac_path));
406 if (Environment.OSVersion.Platform == PlatformID.Unix) {
407 mono_path = Path.Combine (mono_prefix, "bin/mono");
408 if (!File.Exists (mono_path))
412 mono_path = Path.Combine (mono_prefix, "bin\\mono.bat");
414 if (!File.Exists (mono_path))
415 mono_path = Path.Combine (mono_prefix, "bin\\mono.exe");
417 if (!File.Exists (mono_path))
418 mono_path = Path.Combine (mono_prefix, "mono\\mono\\mini\\mono.exe");
420 if (!File.Exists (mono_path))
421 throw new FileNotFoundException (string.Format ("Windows mono path not found: '{0}'", mono_path));
425 //Get command line arguments
426 StringBuilder argsBuilder = new StringBuilder ();
427 string[] args = Environment.GetCommandLineArgs ();
428 for (int i = 0; i < args.Length; i++)
430 argsBuilder.Append (string.Format ("\"{0}\" ", args[i]));
432 string arguments = argsBuilder.ToString ();
433 ProcessStartInfo procInfo = Process.GetCurrentProcess ().StartInfo;
435 if (mono_path == null) { //it is .NET on Windows
436 procInfo.FileName = args[0];
437 procInfo.Arguments = arguments.Remove (0, args[0].Length + 3); //1 space and 2 quotes
440 procInfo.Arguments = arguments;
441 procInfo.FileName = mono_path;
444 procInfo.WorkingDirectory = Environment.CurrentDirectory;
447 Process.Start (procInfo);
451 public static void Exit() {
452 XplatUI.PostQuitMessage(0);
456 public static void ExitThread() {
457 CloseForms(Thread.CurrentThread);
458 // this might not be right - need to investigate (somehow) if a WM_QUIT message is generated here
459 XplatUI.PostQuitMessage(0);
462 public static ApartmentState OleRequired() {
463 //throw new NotImplementedException("OLE Not supported by this System.Windows.Forms implementation");
464 return ApartmentState.Unknown;
467 public static void OnThreadException(Exception t) {
468 if (MWFThread.Current.HandlingException) {
469 /* we're already handling an exception and we got
470 another one? print it out and exit, this means
471 we've got a runtime/SWF bug. */
472 Console.WriteLine (t);
476 MWFThread.Current.HandlingException = true;
478 if (Application.ThreadException != null) {
479 Application.ThreadException(null, new ThreadExceptionEventArgs(t));
483 if (SystemInformation.UserInteractive) {
484 Form form = new ThreadExceptionDialog (t);
487 Console.WriteLine (t.ToString ());
491 MWFThread.Current.HandlingException = false;
494 public static void RemoveMessageFilter(IMessageFilter filter) {
495 lock (message_filters) {
496 message_filters.Remove(filter);
500 public static void Run() {
501 RunLoop(false, new ApplicationContext());
504 public static void Run(Form mainForm) {
505 RunLoop(false, new ApplicationContext(mainForm));
508 public static void Run(ApplicationContext context) {
509 RunLoop(false, context);
512 internal static void RunLoop(bool Modal, ApplicationContext context) {
519 thread = MWFThread.Current;
522 * There is a NotWorking test for this, but since we are using this method both for Form.ShowDialog as for ApplicationContexts we'll
523 * fail on nested ShowDialogs, so disable the check for the moment.
525 //if (thread.MessageLoop) {
526 // throw new InvalidOperationException ("Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead.");
531 if (context == null) {
532 context = new ApplicationContext();
535 thread.Context = context;
537 if (context.MainForm != null) {
538 context.MainForm.is_modal = Modal;
539 context.MainForm.context = context;
540 context.MainForm.closing = false;
541 context.MainForm.Visible = true; // Cannot use Show() or scaling gets confused by menus
542 // XXX the above line can be used to close the form. another problem with our handling of Show/Activate.
543 if (context.MainForm != null)
544 context.MainForm.Activate();
548 Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
554 toplevels = new Queue();
557 control = forms.GetEnumerator();
559 while (control.MoveNext()) {
560 f = (Form)control.Current;
562 // Don't disable the main form.
563 if (f == context.MainForm) {
567 // Don't disable any children of the main form.
568 // These do not have to be MDI children.
570 bool is_child_of_main = false;;
573 if (current.Parent == context.MainForm) {
574 is_child_of_main = true;
577 current = current.Parent;
578 } while (current != null);
580 if (is_child_of_main)
584 if (f.IsHandleCreated && XplatUI.IsEnabled(f.Handle)) {
586 Console.WriteLine(" Disabling form {0}", f);
588 XplatUI.EnableWindow(f.Handle, false);
589 toplevels.Enqueue(f);
594 // FIXME - need activate?
595 /* make sure the MainForm is enabled */
596 if (context.MainForm != null) {
597 XplatUI.EnableWindow (context.MainForm.Handle, true);
598 XplatUI.SetModal(context.MainForm.Handle, true);
604 queue_id = XplatUI.StartLoop(Thread.CurrentThread);
605 thread.MessageLoop = true;
609 while (!quit && XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) {
610 lock (message_filters) {
611 if (message_filters.Count > 0) {
616 m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
617 for (int i = 0; i < message_filters.Count; i++) {
618 if (((IMessageFilter)message_filters[i]).PreFilterMessage(ref m)) {
619 // we're dropping the message
630 switch((Msg)msg.message) {
632 case Msg.WM_SYSKEYDOWN:
636 case Msg.WM_SYSKEYUP:
640 m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
641 c = Control.FromHandle(msg.hwnd);
642 if ((c != null) && !c.PreProcessMessage(ref m)) {
647 quit = true; // make sure we exit
650 XplatUI.TranslateMessage(ref msg);
651 XplatUI.DispatchMessage(ref msg);
655 // Handle exit, Form might have received WM_CLOSE and set 'closing' in response
656 if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) {
658 XplatUI.PostQuitMessage(0);
665 Console.WriteLine(" RunLoop loop left");
668 thread.MessageLoop = false;
669 XplatUI.EndLoop(Thread.CurrentThread);
674 Form old = context.MainForm;
676 context.MainForm = null;
678 while (toplevels.Count>0) {
680 Console.WriteLine(" Re-Enabling form form {0}", toplevels.Peek());
682 c = (Form)toplevels.Dequeue();
683 if (c.IsHandleCreated) {
684 XplatUI.EnableWindow(c.window.Handle, true);
685 context.MainForm = c;
689 Console.WriteLine(" Done with the re-enable");
691 if (context.MainForm != null && context.MainForm.IsHandleCreated) {
692 XplatUI.SetModal(context.MainForm.Handle, false);
695 Console.WriteLine(" Done with the SetModal");
698 old.is_modal = false;
702 Console.WriteLine("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
704 if (context.MainForm != null) {
705 context.MainForm.context = null;
706 context.MainForm = null;
714 #endregion // Public Static Methods
717 public static event EventHandler ApplicationExit;
719 public static event EventHandler Idle {
721 XplatUI.Idle += value;
724 XplatUI.Idle -= value;
728 public static event EventHandler ThreadExit;
729 public static event ThreadExceptionEventHandler ThreadException;
732 [EditorBrowsable (EditorBrowsableState.Advanced)]
733 public static event EventHandler EnterThreadModal;
735 [EditorBrowsable (EditorBrowsableState.Advanced)]
736 public static event EventHandler LeaveThreadModal;
740 #region Internal Methods
741 internal static void AddForm (Form f)
747 internal static void RemoveForm (Form f)