/*
* Application.cs - Implementation of the
* "System.Windows.Forms.Application" class.
*
* Copyright (C) 2003 Southern Storm Software, Pty Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
namespace System.Windows.Forms
{
using Microsoft.Win32;
using System.Globalization;
using System.Threading;
using System.Reflection;
using System.IO;
using System.Drawing.Toolkit;
public sealed class Application
{
// Internal state.
private static Request requests;
private static Request lastRequest;
// Cannot instantiate this class.
private Application() {}
#if !CONFIG_COMPACT_FORMS
// Internal state.
[ThreadStatic] private static InputLanguage inputLanguage;
private static String safeTopLevelCaptionFormat = "{0}";
// Determine if it is possible to quit this application.
public static bool AllowQuit
{
get
{
// Returns false for applet usage, which we don't have yet.
return true;
}
}
#if CONFIG_WIN32_SPECIFICS
// Get a registry key for a data path.
private static RegistryKey GetAppDataRegistry(RegistryKey hive)
{
String key = "Software\\" + CompanyName + "\\" +
ProductName + "\\" + ProductVersion;
return hive.CreateSubKey(key);
}
// Get the common registry key for data shared between all users.
public static RegistryKey CommonAppDataRegistry
{
get
{
return GetAppDataRegistry(Registry.LocalMachine);
}
}
#endif
#if !ECMA_COMPAT
// Get a data path based on a special folder name.
private static String GetAppDataPath(Environment.SpecialFolder folder)
{
String path = Environment.GetFolderPath(folder);
path += Path.DirectorySeparatorChar.ToString() +
CompanyName +
Path.DirectorySeparatorChar.ToString() +
ProductName +
Path.DirectorySeparatorChar.ToString() +
ProductVersion;
if(!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
return path;
}
// Get the common data path for data shared between all users.
public static String CommonAppDataPath
{
get
{
return GetAppDataPath
(Environment.SpecialFolder.CommonApplicationData);
}
}
// Get the local user application data path.
public static String LocalUserAppDataPath
{
get
{
return GetAppDataPath
(Environment.SpecialFolder.LocalApplicationData);
}
}
// Get the user application data path.
public static String UserAppDataPath
{
get
{
return GetAppDataPath
(Environment.SpecialFolder.ApplicationData);
}
}
// Get the company name for this application
public static String CompanyName
{
get
{
Assembly assembly = Assembly.GetEntryAssembly();
Object[] attrs = assembly.GetCustomAttributes
(typeof(AssemblyCompanyAttribute), false);
if(attrs != null && attrs.Length > 0)
{
return ((AssemblyCompanyAttribute)(attrs[0])).Company;
}
return assembly.GetName().Name;
}
}
// Get the product name associated with this application.
public static String ProductName
{
get
{
Assembly assembly = Assembly.GetEntryAssembly();
Object[] attrs = assembly.GetCustomAttributes
(typeof(AssemblyProductAttribute), false);
if(attrs != null && attrs.Length > 0)
{
return ((AssemblyProductAttribute)(attrs[0])).Product;
}
return assembly.GetName().Name;
}
}
// Get the product version associated with this application.
public static String ProductVersion
{
get
{
Assembly assembly = Assembly.GetEntryAssembly();
Object[] attrs = assembly.GetCustomAttributes
(typeof(AssemblyInformationalVersionAttribute), false);
if(attrs != null && attrs.Length > 0)
{
return ((AssemblyInformationalVersionAttribute)
(attrs[0])).InformationalVersion;
}
return assembly.GetName().Version.ToString();
}
}
#endif // !ECMA_COMPAT
// Get or set the culture for the current thread.
public static CultureInfo CurrentCulture
{
get
{
return CultureInfo.CurrentCulture;
}
set
{
#if !ECMA_COMPAT
Thread.CurrentThread.CurrentCulture = value;
#endif
}
}
// Get or set the input language for the current thread.
public static InputLanguage CurrentInputLanguage
{
get
{
return inputLanguage;
}
set
{
inputLanguage = value;
}
}
// Get the executable path for this application.
public static String ExecutablePath
{
get
{
return (Environment.GetCommandLineArgs())[0];
}
}
// Determine if a message loop exists on this thread.
public static bool MessageLoop
{
get
{
return true;
}
}
// Get or set the top-level warning caption format.
public static String SafeTopLevelCaptionFormat
{
get
{
return safeTopLevelCaptionFormat;
}
set
{
safeTopLevelCaptionFormat = value;
}
}
// Get the startup path for the executable.
public static String StartupPath
{
get
{
return Path.GetDirectoryName(ExecutablePath);
}
}
#if CONFIG_WIN32_SPECIFICS
// Get the registry key for user-specific data.
public static RegistryKey UserAppDataRegistry
{
get
{
return GetAppDataRegistry(Registry.CurrentUser);
}
}
#endif
// Add a message filter.
public static void AddMessageFilter(IMessageFilter value)
{
// We don't use message filters in this implementation.
}
// Enable Windows XP visual styles.
public static void EnableVisualStyles()
{
// Not used in this implementation.
}
// Exit the message loop on the current thread and close all windows.
public static void ExitThread()
{
// We only allow one message loop in this implementation,
// so "ExitThread" is the same as "Exit".
Exit();
}
#if !ECMA_COMPAT
// Initialize OLE on the current thread.
public static ApartmentState OleRequired()
{
// Not used in this implementation.
return ApartmentState.Unknown;
}
#endif
// Raise the thread exception event.
public static void OnThreadException(Exception t)
{
if(ThreadException != null)
{
ThreadException(null, new ThreadExceptionEventArgs(t));
}
}
// Remove a message filter.
public static void RemoveMessageFilter(IMessageFilter value)
{
// We don't use message filters in this implementation.
}
// Event that is raised when the application is about to exit.
public static event EventHandler ApplicationExit;
// Event that is raised when the message loop is entering the idle state.
public static event EventHandler Idle;
// Event that is raised for an untrapped thread exception.
public static event ThreadExceptionEventHandler ThreadException;
// Event that is raised when the current thread is about to exit.
public static event EventHandler ThreadExit;
#endif // !CONFIG_COMPACT_FORMS
// The thread that is running the main message loop.
private static Thread mainThread;
// Process all events that are currently in the message queue.
public static void DoEvents()
{
bool isMainThread;
Thread thread = Thread.CurrentThread;
Request request;
// Determine if we are the main thread.
lock(typeof(Application))
{
isMainThread = (mainThread == thread);
}
// Process pending events.
if(isMainThread)
{
ToolkitManager.Toolkit.ProcessEvents(false);
}
// Process requests that were sent via "SendRequest".
while((request = NextRequest(thread, false)) != null)
{
request.Execute();
}
}
// Tell the application to exit.
public static void Exit()
{
lock(typeof(Application))
{
if(mainThread != null ||
Thread.CurrentThread == null)
{
ToolkitManager.Toolkit.Quit();
}
}
}
// Exit from the current thread when the main form closes.
private static void ContextExit(Object sender, EventArgs e)
{
#if !CONFIG_COMPACT_FORMS
ExitThread();
#else
Exit();
#endif
}
// Inner version of "Run". In this implementation we only allow a
// message loop to be running on one of the threads.
private static void RunMessageLoop(ApplicationContext context)
{
Form mainForm = context.MainForm;
EventHandler handler;
Request request;
Thread thread = Thread.CurrentThread;
bool isMainThread;
// Connect the context's ThreadExit event to our "ExitThread".
handler = new EventHandler(ContextExit);
context.ThreadExit += handler;
// Show the main form on-screen.
if(mainForm != null)
{
mainForm.Show();
mainForm.SelectNextControl (null, true, true, true, false);
Form.activeForm = mainForm;
}
// Make sure that we are the only message loop.
lock(typeof(Application))
{
if(mainThread != null)
{
isMainThread = false;
}
else
{
mainThread = thread;
isMainThread = true;
}
}
// Run the main message processing loop.
if(isMainThread)
{
IToolkit toolkit = ToolkitManager.Toolkit;
try
{
for(;;)
{
try {
// Process events in the queue.
if(!toolkit.ProcessEvents(false))
{
#if !CONFIG_COMPACT_FORMS
// There were no events, so raise "Idle".
if(Idle != null)
{
Idle(null, EventArgs.Empty);
}
#endif
// Block until an event, or quit, arrives.
if(!toolkit.ProcessEvents(true))
{
break;
}
}
// Process requests sent via "SendRequest".
while((request = NextRequest(thread, false))
!= null)
{
request.Execute();
}
}
catch( Exception e ) {
Application.OnThreadException( e );
}
}
}
finally
{
// Reset the "mainThread" variable because there
// is no message loop any more.
lock(typeof(Application))
{
mainThread = null;
}
}
}
else
{
// This is not the main thread, so only process
// requests that were sent via "SendRequest".
while((request = NextRequest(thread, true)) != null)
{
request.Execute();
}
}
// Disconnect from the context's "ThreadExit" event.
context.ThreadExit -= handler;
Form.activeForm = null;
#if !CONFIG_COMPACT_FORMS
// Raise the "ThreadExit" event.
if(ThreadExit != null)
{
ThreadExit(null, EventArgs.Empty);
}
// Raise the "ApplicationExit" event.
if(ApplicationExit != null)
{
ApplicationExit(null, EventArgs.Empty);
}
#endif
}
// Run an inner message loop until the dialog result is set on a form.
internal static void InnerMessageLoop(Form form)
{
Request request;
Thread thread = Thread.CurrentThread;
bool isMainThread;
bool resetMainThread;
// Determine if we are running on the main thread or not.
lock(typeof(Application))
{
if(mainThread == null)
{
// The main message loop hasn't started yet.
// This might happen with MessageBox dialogs.
mainThread = thread;
isMainThread = true;
resetMainThread = true;
}
else
{
isMainThread = (mainThread == thread);
resetMainThread = false;
}
}
// Run the main message processing loop.
if(isMainThread)
{
IToolkit toolkit = ToolkitManager.Toolkit;
try
{
while(!(form.dialogResultIsSet) && form.Visible)
{
// Process events in the queue.
if(!toolkit.ProcessEvents(false))
{
#if !CONFIG_COMPACT_FORMS
// There were no events, so raise "Idle".
if(Idle != null)
{
Idle(null, EventArgs.Empty);
}
#endif
// Block until an event, or quit, arrives.
if(!toolkit.ProcessEvents(true))
{
break;
}
}
// Process requests sent via "SendRequest".
while((request = NextRequest(thread, false))
!= null)
{
request.Execute();
}
}
}
finally
{
// Reset the "mainThread" variable because there
// is no message loop any more.
lock(typeof(Application))
{
if(resetMainThread)
{
mainThread = null;
}
}
}
}
else
{
// This is not the main thread, so only process
// requests that were sent via "SendRequest".
while(!(form.dialogResultIsSet) && form.Visible)
{
if((request = NextRequest(thread, true)) != null)
{
request.Execute();
}
else
{
break;
}
}
}
}
// Make the specified form visible and run the main loop.
// The loop will exit when "Exit" is called.
public static void Run(Form mainForm)
{
RunMessageLoop(new ApplicationContext(mainForm));
}
#if !CONFIG_COMPACT_FORMS
// Run the main message loop on this thread.
public static void Run()
{
RunMessageLoop(new ApplicationContext());
}
// Run the main message loop for an application context.
public static void Run(ApplicationContext context)
{
if(context == null)
{
context = new ApplicationContext();
}
RunMessageLoop(context);
}
#endif // !CONFIG_COMPACT_FORMS
// Information about a request to be executed in a specific thread.
// This is used to help implement the "Control.Invoke" method.
internal abstract class Request
{
public Request next;
public Thread thread;
// Execute the request.
public abstract void Execute();
}; // class Request
// Send a request to a particular thread's message queue.
internal static void SendRequest(Request request, Thread thread)
{
Object obj = typeof(Application);
request.thread = thread;
lock(obj)
{
// Add the request to the queue.
request.next = null;
if(requests != null)
{
lastRequest.next = request;
}
else
{
requests = request;
}
lastRequest = request;
// Wake up all threads that are blocking in "NextRequest".
Monitor.PulseAll(obj);
// Wake up the thread that will receive the request,
// as it may be blocking inside "ProcessEvents".
ToolkitManager.Toolkit.Wakeup(thread);
}
}
// Get the next pending request for a particular thread.
private static Request NextRequest(Thread thread, bool block)
{
Object obj = typeof(Application);
Request request, prev;
lock(obj)
{
for(;;)
{
// See if there is a request on the queue for us.
prev = null;
request = requests;
while(request != null)
{
if(request.thread == thread)
{
if(prev != null)
{
prev.next = request.next;
}
else
{
requests = request.next;
}
if(request.next == null)
{
lastRequest = prev;
}
else
{
request.next = null;
}
break;
}
prev = request;
request = request.next;
}
// Bail out if we got something or we aren't blocking.
if(request != null || !block)
{
break;
}
// Wait to be signalled by "SendRequest".
Monitor.Wait(obj);
}
}
return request;
}
}; // class Application
}; // namespace System.Windows.Forms