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 {
87 e = threads.Values.GetEnumerator();
90 while (e.MoveNext()) {
91 thread = (MWFThread)e.Current;
92 if (thread != null && thread.messageloop_started) {
101 public static MWFThread Current {
107 thread = (MWFThread)threads[Thread.CurrentThread.GetHashCode()];
108 if (thread == null) {
109 thread = new MWFThread();
110 thread.thread_id = Thread.CurrentThread.GetHashCode();
111 threads[thread.thread_id] = thread;
118 #endregion // Properties
122 if (context != null) {
123 context.ExitThread();
127 if (Application.ThreadExit != null) {
128 Application.ThreadExit(null, EventArgs.Empty);
131 if (LoopCount == 0) {
132 if (Application.ApplicationExit != null) {
133 Application.ApplicationExit(null, EventArgs.Empty);
137 threads[thread_id] = null;
140 #endregion // Methods
143 private static bool browser_embedded = false;
144 private static InputLanguage input_language = InputLanguage.CurrentInputLanguage;
145 private static string safe_caption_format = "{1} - {0} - {2}";
146 private static ArrayList message_filters = new ArrayList();
147 private static FormCollection forms = new FormCollection ();
148 private static bool use_wait_cursor = false;
151 private static VisualStyleState visual_style_state = VisualStyleState.ClientAndNonClientAreasEnabled;
154 private Application () {
157 #region Private Methods
158 private static void CloseForms(Thread thread) {
164 Console.WriteLine(" CloseForms({0}) called", thread);
166 if (thread == null) {
173 control = forms.GetEnumerator();
175 while (control.MoveNext()) {
176 f = (Form)control.Current;
178 if (all || (thread == f.creator_thread)) {
179 if (f.IsHandleCreated) {
180 XplatUI.PostMessage(f.Handle, Msg.WM_CLOSE_INTERNAL, IntPtr.Zero, IntPtr.Zero);
183 Console.WriteLine(" Closing form {0}", f);
189 #endregion // Private methods
191 #region Public Static Properties
192 public static bool AllowQuit {
194 return !browser_embedded;
198 public static string CommonAppDataPath {
200 return Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
204 public static RegistryKey CommonAppDataRegistry {
208 key = Registry.LocalMachine.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
214 public static string CompanyName {
216 AssemblyCompanyAttribute[] attrs = (AssemblyCompanyAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), true);
218 if ((attrs != null) && attrs.Length>0) {
219 return attrs[0].Company;
222 return Assembly.GetEntryAssembly().GetName().Name;
226 public static CultureInfo CurrentCulture {
228 return Thread.CurrentThread.CurrentUICulture;
233 Thread.CurrentThread.CurrentUICulture=value;
237 public static InputLanguage CurrentInputLanguage {
239 return input_language;
243 input_language=value;
247 public static string ExecutablePath {
249 return Assembly.GetEntryAssembly().Location;
253 public static string LocalUserAppDataPath {
255 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), CompanyName), ProductName), ProductVersion);
259 public static bool MessageLoop {
261 return MWFThread.Current.MessageLoop;
265 public static string ProductName {
267 AssemblyProductAttribute[] attrs = (AssemblyProductAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), true);
269 if ((attrs != null) && attrs.Length>0) {
270 return attrs[0].Product;
273 return Assembly.GetEntryAssembly().GetName().Name;
277 public static string ProductVersion {
281 version = Assembly.GetEntryAssembly().GetName().Version.ToString();
287 public static string SafeTopLevelCaptionFormat {
289 return safe_caption_format;
293 safe_caption_format=value;
297 public static string StartupPath {
299 return Path.GetDirectoryName(Application.ExecutablePath);
303 public static string UserAppDataPath {
305 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), CompanyName), ProductName), ProductVersion);
309 public static RegistryKey UserAppDataRegistry {
313 key = Registry.CurrentUser.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
321 public static bool UseWaitCursor {
323 return use_wait_cursor;
326 use_wait_cursor = value;
327 if (use_wait_cursor) {
328 foreach (Form form in OpenForms) {
329 form.Cursor = Cursors.WaitCursor;
335 public static bool RenderWithVisualStyles {
337 if (VisualStyleInformation.IsSupportedByOS)
339 if (!VisualStyleInformation.IsEnabledByUser)
342 if (!XplatUI.ThemesEnabled)
345 if (Application.VisualStyleState == VisualStyleState.ClientAndNonClientAreasEnabled)
348 if (Application.VisualStyleState == VisualStyleState.ClientAreaEnabled)
356 public static VisualStyleState VisualStyleState {
357 get { return Application.visual_style_state; }
358 set { Application.visual_style_state = value; }
364 #region Public Static Methods
365 public static void AddMessageFilter(IMessageFilter value) {
366 lock (message_filters) {
367 message_filters.Add(value);
371 public static void DoEvents() {
375 public static void EnableVisualStyles() {
376 XplatUI.EnableThemes();
381 // If true, it uses GDI+, performance reasons were quoted
383 static internal bool use_compatible_text_rendering = true;
385 public static void SetCompatibleTextRenderingDefault (bool defaultValue)
387 use_compatible_text_rendering = defaultValue;
390 public static FormCollection OpenForms {
396 public static void Restart ()
398 //FIXME: ClickOnce stuff using the Update or UpdateAsync methods.
399 //FIXME: SecurityPermission: Restart () requires IsUnrestricted permission.
401 if (Assembly.GetEntryAssembly () == null)
402 throw new NotSupportedException ("The method 'Restart' is not supported by this application type.");
404 string mono_path = null;
407 PropertyInfo gac = typeof (Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
408 MethodInfo get_gac = null;
410 get_gac = gac.GetGetMethod (true);
412 if (get_gac != null) {
413 string gac_path = Path.GetDirectoryName ((string)get_gac.Invoke (null, null));
414 string mono_prefix = Path.GetDirectoryName (Path.GetDirectoryName (gac_path));
416 if (Environment.OSVersion.Platform == PlatformID.Unix) {
417 mono_path = Path.Combine (mono_prefix, "bin/mono");
418 if (!File.Exists (mono_path))
422 mono_path = Path.Combine (mono_prefix, "bin\\mono.bat");
424 if (!File.Exists (mono_path))
425 mono_path = Path.Combine (mono_prefix, "bin\\mono.exe");
427 if (!File.Exists (mono_path))
428 mono_path = Path.Combine (mono_prefix, "mono\\mono\\mini\\mono.exe");
430 if (!File.Exists (mono_path))
431 throw new FileNotFoundException (string.Format ("Windows mono path not found: '{0}'", mono_path));
435 //Get command line arguments
436 StringBuilder argsBuilder = new StringBuilder ();
437 string[] args = Environment.GetCommandLineArgs ();
438 for (int i = 0; i < args.Length; i++)
440 argsBuilder.Append (string.Format ("\"{0}\" ", args[i]));
442 string arguments = argsBuilder.ToString ();
443 ProcessStartInfo procInfo = Process.GetCurrentProcess ().StartInfo;
445 if (mono_path == null) { //it is .NET on Windows
446 procInfo.FileName = args[0];
447 procInfo.Arguments = arguments.Remove (0, args[0].Length + 3); //1 space and 2 quotes
450 procInfo.Arguments = arguments;
451 procInfo.FileName = mono_path;
454 procInfo.WorkingDirectory = Environment.CurrentDirectory;
457 Process.Start (procInfo);
461 public static void Exit() {
464 // FIXME - this needs to be fired when they're all closed
465 // But CloseForms uses PostMessage, so it gets fired before
466 // We need to wait on something...
467 if (ApplicationExit != null) {
468 ApplicationExit(null, EventArgs.Empty);
472 public static void ExitThread() {
473 CloseForms(Thread.CurrentThread);
474 MWFThread.Current.Exit();
477 public static ApartmentState OleRequired() {
478 //throw new NotImplementedException("OLE Not supported by this System.Windows.Forms implementation");
479 return ApartmentState.Unknown;
482 public static void OnThreadException(Exception t) {
483 if (MWFThread.Current.HandlingException) {
484 /* we're already handling an exception and we got
485 another one? print it out and exit, this means
486 we've got a runtime/SWF bug. */
487 Console.WriteLine (t);
491 MWFThread.Current.HandlingException = true;
493 if (Application.ThreadException != null) {
494 Application.ThreadException(null, new ThreadExceptionEventArgs(t));
498 if (SystemInformation.UserInteractive) {
499 Form form = new ThreadExceptionDialog (t);
502 Console.WriteLine (t.ToString ());
506 MWFThread.Current.HandlingException = false;
509 public static void RemoveMessageFilter(IMessageFilter filter) {
510 lock (message_filters) {
511 message_filters.Remove(filter);
515 public static void Run() {
516 RunLoop(false, new ApplicationContext());
519 public static void Run(Form mainForm) {
520 RunLoop(false, new ApplicationContext(mainForm));
523 public static void Run(ApplicationContext context) {
524 RunLoop(false, context);
527 internal static void RunLoop(bool Modal, ApplicationContext context) {
535 thread = MWFThread.Current;
539 if (context == null) {
540 context = new ApplicationContext();
543 thread.Context = context;
545 if (context.MainForm != null) {
546 context.MainForm.is_modal = Modal;
547 context.MainForm.context = context;
548 context.MainForm.closing = false;
549 context.MainForm.Visible = true; // Cannot use Show() or scaling gets confused by menus
550 // FIXME - do we need this?
551 //context.MainForm.PerformLayout();
552 context.MainForm.Activate();
556 Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
562 toplevels = new Queue();
565 control = forms.GetEnumerator();
567 while (control.MoveNext()) {
568 f = (Form)control.Current;
570 if (f != context.MainForm) {
571 if (f.IsHandleCreated && XplatUI.IsEnabled(f.Handle)) {
573 Console.WriteLine(" Disabling form {0}", f);
575 XplatUI.EnableWindow(f.Handle, false);
576 toplevels.Enqueue(f);
582 // FIXME - need activate?
583 /* make sure the MainForm is enabled */
584 if (context.MainForm != null) {
585 XplatUI.EnableWindow (context.MainForm.Handle, true);
586 XplatUI.SetModal(context.MainForm.Handle, true);
592 queue_id = XplatUI.StartLoop(Thread.CurrentThread);
593 thread.MessageLoop = true;
595 while (XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) {
596 lock (message_filters) {
597 if (message_filters.Count > 0) {
602 m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
603 for (int i = 0; i < message_filters.Count; i++) {
604 if (((IMessageFilter)message_filters[i]).PreFilterMessage(ref m)) {
605 // we're dropping the message
616 switch((Msg)msg.message) {
618 case Msg.WM_SYSKEYDOWN:
622 case Msg.WM_SYSKEYUP:
626 m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
627 c = Control.FromHandle(msg.hwnd);
628 if ((c != null) && !c.PreProcessMessage(ref m)) {
633 XplatUI.TranslateMessage(ref msg);
634 XplatUI.DispatchMessage(ref msg);
638 // Handle exit, Form might have received WM_CLOSE and set 'closing' in response
639 if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) {
641 XplatUI.PostQuitMessage(0);
648 Console.WriteLine(" RunLoop loop left");
651 thread.MessageLoop = false;
652 XplatUI.EndLoop(Thread.CurrentThread);
657 Form old = context.MainForm;
659 context.MainForm = null;
661 while (toplevels.Count>0) {
663 Console.WriteLine(" Re-Enabling form form {0}", toplevels.Peek());
665 c = (Form)toplevels.Dequeue();
666 if (c.IsHandleCreated) {
667 XplatUI.EnableWindow(c.window.Handle, true);
668 context.MainForm = c;
672 Console.WriteLine(" Done with the re-enable");
674 if (context.MainForm != null && context.MainForm.IsHandleCreated) {
675 XplatUI.SetModal(context.MainForm.Handle, false);
678 Console.WriteLine(" Done with the SetModal");
681 old.is_modal = false;
685 Console.WriteLine("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
687 if (context.MainForm != null) {
688 context.MainForm.context = null;
689 context.MainForm = null;
697 #endregion // Public Static Methods
700 public static event EventHandler ApplicationExit;
702 public static event EventHandler Idle {
704 XplatUI.Idle += value;
707 XplatUI.Idle -= value;
711 public static event EventHandler ThreadExit;
712 public static event ThreadExceptionEventHandler ThreadException;
715 [EditorBrowsable (EditorBrowsableState.Advanced)]
716 public static event EventHandler EnterThreadModal;
718 [EditorBrowsable (EditorBrowsableState.Advanced)]
719 public static event EventHandler LeaveThreadModal;
723 #region Internal Methods
724 internal static void AddForm (Form f)
730 internal static void RemoveForm (Form f)