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 ();
150 private static VisualStyleState visual_style_state = VisualStyleState.ClientAndNonClientAreasEnabled;
153 private Application () {
156 #region Private Methods
157 private static void CloseForms(Thread thread) {
163 Console.WriteLine(" CloseForms({0}) called", thread);
165 if (thread == null) {
172 control = forms.GetEnumerator();
174 while (control.MoveNext()) {
175 f = (Form)control.Current;
177 if (all || (thread == f.creator_thread)) {
178 if (f.IsHandleCreated) {
179 XplatUI.PostMessage(f.Handle, Msg.WM_CLOSE_INTERNAL, IntPtr.Zero, IntPtr.Zero);
182 Console.WriteLine(" Closing form {0}", c);
188 #endregion // Private methods
190 #region Public Static Properties
191 public static bool AllowQuit {
193 return !browser_embedded;
197 public static string CommonAppDataPath {
199 return Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
203 public static RegistryKey CommonAppDataRegistry {
207 key = Registry.LocalMachine.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
213 public static string CompanyName {
215 AssemblyCompanyAttribute[] attrs = (AssemblyCompanyAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), true);
217 if ((attrs != null) && attrs.Length>0) {
218 return attrs[0].Company;
221 return Assembly.GetEntryAssembly().GetName().Name;
225 public static CultureInfo CurrentCulture {
227 return Thread.CurrentThread.CurrentUICulture;
232 Thread.CurrentThread.CurrentUICulture=value;
236 public static InputLanguage CurrentInputLanguage {
238 return input_language;
242 input_language=value;
246 public static string ExecutablePath {
248 return Assembly.GetEntryAssembly().Location;
252 public static string LocalUserAppDataPath {
254 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), CompanyName), ProductName), ProductVersion);
258 public static bool MessageLoop {
260 return MWFThread.Current.MessageLoop;
264 public static string ProductName {
266 AssemblyProductAttribute[] attrs = (AssemblyProductAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), true);
268 if ((attrs != null) && attrs.Length>0) {
269 return attrs[0].Product;
272 return Assembly.GetEntryAssembly().GetName().Name;
276 public static string ProductVersion {
280 version = Assembly.GetEntryAssembly().GetName().Version.ToString();
286 public static string SafeTopLevelCaptionFormat {
288 return safe_caption_format;
292 safe_caption_format=value;
296 public static string StartupPath {
298 return Path.GetDirectoryName(Application.ExecutablePath);
302 public static string UserAppDataPath {
304 return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), CompanyName), ProductName), ProductVersion);
308 public static RegistryKey UserAppDataRegistry {
312 key = Registry.CurrentUser.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
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 public static void Restart ()
382 //FIXME: ClickOnce stuff using the Update or UpdateAsync methods.
383 //FIXME: SecurityPermission: Restart () requires IsUnrestricted permission.
385 if (Assembly.GetEntryAssembly () == null)
386 throw new NotSupportedException ("The method 'Restart' is not supported by this application type.");
388 string mono_path = null;
391 PropertyInfo gac = typeof (Environment).GetProperty ("GacPath", BindingFlags.Static | BindingFlags.NonPublic);
392 MethodInfo get_gac = null;
394 get_gac = gac.GetGetMethod (true);
396 if (get_gac != null) {
397 string gac_path = Path.GetDirectoryName ((string)get_gac.Invoke (null, null));
398 string mono_prefix = Path.GetDirectoryName (Path.GetDirectoryName (gac_path));
400 if (Environment.OSVersion.Platform == PlatformID.Unix) {
401 mono_path = Path.Combine (mono_prefix, "bin/mono");
402 if (!File.Exists (mono_path))
406 mono_path = Path.Combine (mono_prefix, "bin\\mono.bat");
408 if (!File.Exists (mono_path))
409 mono_path = Path.Combine (mono_prefix, "bin\\mono.exe");
411 if (!File.Exists (mono_path))
412 mono_path = Path.Combine (mono_prefix, "mono\\mono\\mini\\mono.exe");
414 if (!File.Exists (mono_path))
415 throw new FileNotFoundException (string.Format ("Windows mono path not found: '{0}'", mono_path));
419 //Get command line arguments
420 StringBuilder argsBuilder = new StringBuilder ();
421 string[] args = Environment.GetCommandLineArgs ();
422 for (int i = 0; i < args.Length; i++)
424 argsBuilder.Append (string.Format ("\"{0}\" ", args[i]));
426 string arguments = argsBuilder.ToString ();
427 ProcessStartInfo procInfo = Process.GetCurrentProcess ().StartInfo;
429 if (mono_path == null) { //it is .NET on Windows
430 procInfo.FileName = args[0];
431 procInfo.Arguments = arguments.Remove (0, args[0].Length + 3); //1 space and 2 quotes
434 procInfo.Arguments = arguments;
435 procInfo.FileName = mono_path;
438 procInfo.WorkingDirectory = Environment.CurrentDirectory;
441 Process.Start (procInfo);
445 public static void Exit() {
448 // FIXME - this needs to be fired when they're all closed
449 // But CloseForms uses PostMessage, so it gets fired before
450 // We need to wait on something...
451 if (ApplicationExit != null) {
452 ApplicationExit(null, EventArgs.Empty);
456 public static void ExitThread() {
457 CloseForms(Thread.CurrentThread);
458 MWFThread.Current.Exit();
461 public static ApartmentState OleRequired() {
462 //throw new NotImplementedException("OLE Not supported by this System.Windows.Forms implementation");
463 return ApartmentState.Unknown;
466 public static void OnThreadException(Exception t) {
467 if (MWFThread.Current.HandlingException) {
468 /* we're already handling an exception and we got
469 another one? print it out and exit, this means
470 we've got a runtime/SWF bug. */
471 Console.WriteLine (t);
475 MWFThread.Current.HandlingException = true;
477 if (Application.ThreadException != null) {
478 Application.ThreadException(null, new ThreadExceptionEventArgs(t));
482 if (SystemInformation.UserInteractive) {
483 Form form = new ThreadExceptionDialog (t);
486 Console.WriteLine (t.ToString ());
490 MWFThread.Current.HandlingException = false;
493 public static void RemoveMessageFilter(IMessageFilter filter) {
494 lock (message_filters) {
495 message_filters.Remove(filter);
499 public static void Run() {
500 RunLoop(false, new ApplicationContext());
503 public static void Run(Form mainForm) {
504 RunLoop(false, new ApplicationContext(mainForm));
507 public static void Run(ApplicationContext context) {
508 RunLoop(false, context);
511 internal static void RunLoop(bool Modal, ApplicationContext context) {
519 thread = MWFThread.Current;
523 if (context == null) {
524 context = new ApplicationContext();
527 thread.Context = context;
529 if (context.MainForm != null) {
530 context.MainForm.is_modal = Modal;
531 context.MainForm.context = context;
532 context.MainForm.closing = false;
533 context.MainForm.Visible = true; // Cannot use Show() or scaling gets confused by menus
534 // FIXME - do we need this?
535 //context.MainForm.PerformLayout();
536 context.MainForm.Activate();
540 Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
546 toplevels = new Queue();
549 control = forms.GetEnumerator();
551 while (control.MoveNext()) {
552 f = (Form)control.Current;
554 if (f != context.MainForm) {
555 if (f.IsHandleCreated && XplatUI.IsEnabled(f.Handle)) {
557 Console.WriteLine(" Disabling form {0}", c);
559 XplatUI.EnableWindow(f.Handle, false);
560 toplevels.Enqueue(f);
566 // FIXME - need activate?
567 /* make sure the MainForm is enabled */
568 if (context.MainForm != null) {
569 XplatUI.EnableWindow (context.MainForm.Handle, true);
570 XplatUI.SetModal(context.MainForm.Handle, true);
576 queue_id = XplatUI.StartLoop(Thread.CurrentThread);
577 thread.MessageLoop = true;
579 while (XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) {
580 lock (message_filters) {
581 if (message_filters.Count > 0) {
586 m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
587 for (int i = 0; i < message_filters.Count; i++) {
588 if (((IMessageFilter)message_filters[i]).PreFilterMessage(ref m)) {
589 // we're dropping the message
600 switch((Msg)msg.message) {
602 case Msg.WM_SYSKEYDOWN:
606 case Msg.WM_SYSKEYUP:
610 m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
611 c = Control.FromHandle(msg.hwnd);
612 if ((c != null) && !c.PreProcessMessage(ref m)) {
617 XplatUI.TranslateMessage(ref msg);
618 XplatUI.DispatchMessage(ref msg);
622 // Handle exit, Form might have received WM_CLOSE and set 'closing' in response
623 if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) {
625 XplatUI.PostQuitMessage(0);
632 Console.WriteLine(" RunLoop loop left");
635 thread.MessageLoop = false;
636 XplatUI.EndLoop(Thread.CurrentThread);
641 Form old = context.MainForm;
643 context.MainForm = null;
645 while (toplevels.Count>0) {
647 Console.WriteLine(" Re-Enabling form form {0}", toplevels.Peek());
649 c = (Form)toplevels.Dequeue();
650 if (c.IsHandleCreated) {
651 XplatUI.EnableWindow(c.window.Handle, true);
652 context.MainForm = c;
656 Console.WriteLine(" Done with the re-enable");
658 if (context.MainForm != null && context.MainForm.IsHandleCreated) {
659 XplatUI.SetModal(context.MainForm.Handle, false);
662 Console.WriteLine(" Done with the SetModal");
665 old.is_modal = false;
669 Console.WriteLine("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
671 if (context.MainForm != null) {
672 context.MainForm.context = null;
673 context.MainForm = null;
681 #endregion // Public Static Methods
684 public static event EventHandler ApplicationExit;
686 public static event EventHandler Idle {
688 XplatUI.Idle += value;
691 XplatUI.Idle -= value;
695 public static event EventHandler ThreadExit;
696 public static event ThreadExceptionEventHandler ThreadException;
699 [EditorBrowsable (EditorBrowsableState.Advanced)]
700 public static event EventHandler EnterThreadModal;
702 [EditorBrowsable (EditorBrowsableState.Advanced)]
703 public static event EventHandler LeaveThreadModal;
707 #region Internal Methods
708 internal static void AddForm (Form f)
714 internal static void RemoveForm (Form f)