commit 857eda29e3432bc3f46335ab35ef8b9cae60654f Author: Donald Duck Date: Mon Feb 15 12:32:26 2016 -0500 initial checkin of yaulw (locally) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95471e1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +!.gitignore + +# iOS Ignores +.DS_Store +*.swp +*~.nib + +build/ +Target/ + +*.pbxuser +*.perspective +*.perspectivev3 + +*.mode1v3 +*mode2v3 + +xcuserdata + +#MonoTouch Ignores +*.userprefs +bin/ +obj/ + +*/bin/* +*/obj/* +*/*/bin/* +*/*/obj/* +*/_ReSharper.*/* +*.user +*.suo +*.pidb +*.userprefs +#*.Designer.cs +#*.designer.cs +*.DS_Store +*.db diff --git a/@integrate/AlternateDataStreamWrapper.cs b/@integrate/AlternateDataStreamWrapper.cs new file mode 100644 index 0000000..830465f --- /dev/null +++ b/@integrate/AlternateDataStreamWrapper.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using PhoneHome.Lib.Assembly; + +// Downloaded from +// http://www.codeproject.com/Articles/2670/Accessing-alternative-data-streams-of-files-on-an +using Trinet.Core.IO.Ntfs; +using System.IO; + +namespace PhoneHome +{ + /// + /// Wrapper class arround Alternate Data Streams + /// * IMP * Registrate 'Later' Medisoft Demo Registration Work-arround + /// Medisoft has a 30 day trial where they don't enter a serial number. However, this can + /// be bypassed, since registration is terrible at what it does. So we must make sure that + /// the 30 days haven't passed, and we deal with it here with PhoneHome via AlternateStreams + /// + internal static class AlternateDataStreamWrapper + { + /// + /// Read the Timestamp found in the Alternative Stream + /// + /// Dt found or DT.Min (if none found) + internal static DateTime ReadDateTimeStamp() + { + try + { + string s_curDir = Path.GetDirectoryName(AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameNPath(AssemblyW.AssemblyST.Executing)); + SafeNativeMethods.Win32StreamInfo s_streamInfo = new SafeNativeMethods.Win32StreamInfo() { StreamAttributes = FileStreamAttributes.None, StreamName = "phdt", StreamSize = 20, StreamType = FileStreamType.Data }; + + AlternateDataStreamInfo adataStream = new AlternateDataStreamInfo(s_curDir, s_streamInfo); + using (FileStream fs = adataStream.OpenRead()) + using (StreamReader sr = new StreamReader(fs)) + { + string strLine = sr.ReadLine(); + DateTime dtFound = DateTime.Parse(strLine); + return dtFound; + } + } + catch (Exception e) { string Message = e.Message; } + return DateTime.MinValue; + } + + /// + /// Write the passed in Timestamp to the Alternative Stream + /// + /// Timestamp to write + internal static void WriteDateTimeStamp(DateTime dtStamp) + { + try + { + string s_curDir = Path.GetDirectoryName(AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameNPath(AssemblyW.AssemblyST.Executing)); + SafeNativeMethods.Win32StreamInfo s_streamInfo = new SafeNativeMethods.Win32StreamInfo() { StreamAttributes = FileStreamAttributes.None, StreamName = "phdt", StreamSize = 20, StreamType = FileStreamType.Data }; + + AlternateDataStreamInfo adataStream = new AlternateDataStreamInfo(s_curDir, s_streamInfo); + using (FileStream fs = adataStream.OpenWrite()) + using (StreamWriter sw = new StreamWriter(fs)) + { + sw.WriteLine(dtStamp.ToShortDateString()); + } + } + catch (Exception e) { string Message = e.Message; } + } + + } + +} diff --git a/@integrate/App.xaml.cs b/@integrate/App.xaml.cs new file mode 100644 index 0000000..4f2eb5a --- /dev/null +++ b/@integrate/App.xaml.cs @@ -0,0 +1,839 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Windows; +using Watchdog.WatchdogLib.File; +using WatchdogLib.Tools; +using Watchdog.WatchdogLib.Process; +using Watchdog.WatchdogLib.WinForms; +using Watchdog.WatchdogLib.Assembly; +using WatchdogLib.File; +using System.Reflection; +using Watchdog.WatchdogLib.Other; +using Watchdog.WatchdogLib.Monitor; +using System.Diagnostics; +using Forms = System.Windows.Forms; +using Watchdog.WatchdogLib.Net; +using System.IO; +using System.Net; + +namespace Watchdog +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + /// + /// Main Application Object + /// + public App() + { + AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); + AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); + } + + #region Application Constants + + // Application Constants + internal static readonly string APPLICATION_NAME_SHORT = AppResx.GetString("APPLICATION_NAME_SHORT"); + internal static readonly string APPLICATION_NAME_LONG = AppResx.GetString("APPLICATION_NAME_LONG"); + internal static readonly string SUPPORT_PHONENUMBER = AppResx.GetString("SUPPORT_PHONENUMBER"); + internal static readonly int APPLICATION_VERSION_MAJOR = AssemblyW.GetAssemblyVersion(AssemblyW.AssemblyST.Executing).Major; + internal static readonly int APPLICATION_VERSION_MINOR = AssemblyW.GetAssemblyVersion(AssemblyW.AssemblyST.Executing).Minor; + internal static readonly int APPLICATION_VERSION_BUILDNUMBER = AssemblyW.GetAssemblyVersion(AssemblyW.AssemblyST.Executing).Build; + internal static readonly int APPLICATION_VERSION_REVISION = AssemblyW.GetAssemblyVersion(AssemblyW.AssemblyST.Executing).Revision; + internal static string APPLICATION_VERSION { get { return (APPLICATION_VERSION_MAJOR.ToString() + "." + APPLICATION_VERSION_MINOR.ToString() + "." + APPLICATION_VERSION_BUILDNUMBER.ToString() + "." + APPLICATION_VERSION_REVISION.ToString()); } } + + /// + /// This is the Product ID Generated for our Product via ClickOnce and our Certificate. + /// As long as the Certificate Stays the same This ID stays the same. + /// ~we are using this to overide the uninstall icon in Add/Remove * Branding * + /// + internal static readonly string APPLICATION_PRODUCT_CLICKONCE_ID = "6f02138d8632343a"; + internal static readonly string APPLICATION_PRODUCT_CLICKONCE_URL = "http://www.medisoft.com/Watchdog/Publish/Watchdog.application"; + internal static string APPLICATION_CLICKONCE_PUBLISHER { get { return AssemblyW.GetAssemblyCompany(AssemblyW.AssemblyST.Executing); } } + internal static string APPLICATION_CLICKONCE_PRODUCT { get { return AssemblyW.GetAssemblyProductName(AssemblyW.AssemblyST.Executing); } } + internal static string APPLICATION_CLICKONCE_STARTMENU_LINK + { + get { return string.Concat(Environment.GetFolderPath(Environment.SpecialFolder.Programs), "\\", APPLICATION_CLICKONCE_PUBLISHER, "\\", APPLICATION_CLICKONCE_PRODUCT , ".appref-ms"); } + } + + // Default Log File Settings + internal const int LOG_FILE_FILE_SIZE_IN_MB = 2; + internal const int LOG_FILE_NUM_OF_BACKUPS = 4; + + // Log File Constants + internal static readonly string FILE_EXTENSION_LOG_DEFAULT = AppResx.GetString("FILE_EXTENSION_LOG_DEFAULT"); + internal static readonly string LOG_NAME_APPMAIN = AppResx.GetString("APPLICATION_NAME_SHORT"); + + // Application Files / Dependencies + internal static string APP_LOG_FILENAME { get { return (LOG_NAME_APPMAIN + "." + FILE_EXTENSION_LOG_DEFAULT); } } + internal static string APP_LOG_FILENAMEANDPATH + { + get + { + // Make sure Subpath begin|end with a slash, as needed + string commondir = PathNaming.PathEndsWithSlash(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)); + string subpath = PathNaming.PathBeginsWithNoSlash(PathNaming.PathEndsWithSlash(AppResx.GetString("LOG_FILE_SUBPATH"))); + string filename = LOG_NAME_APPMAIN + "." + FILE_EXTENSION_LOG_DEFAULT; + return (commondir + subpath + filename); + } + } + internal static string APP_LOG_PATH + { + get + { + // Make sure Subpath begin|end with a slash, as needed + string commondir = PathNaming.PathEndsWithSlash(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)); + string subpath = PathNaming.PathBeginsWithNoSlash(PathNaming.PathEndsWithNoSlash(AppResx.GetString("LOG_FILE_SUBPATH"))); + return (commondir + subpath); + } + } + internal static string APP_XML_DS_FILENAME { get { return AppResx.GetString("XMLCONFIG_FILENAME"); } } + internal static string APP_XML_DS_FILENAMEANDPATH + { + get + { + // Make sure Subpath begin|end with a slash, as needed + string commondir = PathNaming.PathEndsWithSlash(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)); + string subpath = PathNaming.PathBeginsWithNoSlash(PathNaming.PathEndsWithSlash(AppResx.GetString("XMLCONFIG_FILE_SUBPATH"))); + string filename = AppResx.GetString("XMLCONFIG_FILENAME"); + return (commondir + subpath + filename); + } + } + internal static string APP_XML_DS_PATH + { + get + { + // Make sure Subpath begin|end with a slash, as needed + string commondir = PathNaming.PathEndsWithSlash(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)); + string subpath = PathNaming.PathBeginsWithNoSlash(PathNaming.PathEndsWithNoSlash(AppResx.GetString("XMLCONFIG_FILE_SUBPATH"))); + return (commondir + subpath); + } + } + internal static string APP_INI_SETTINGS_FILENAME { get { return AppResx.GetString("INISETTINGS_FILENAME"); } } + internal static string APP_INI_SETTINGS_FILENAMEANDPATH + { + get + { + // Make sure Subpath begin|end with a slash, as needed + string commondir = PathNaming.PathEndsWithSlash(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)); + string subpath = PathNaming.PathBeginsWithNoSlash(PathNaming.PathEndsWithSlash(AppResx.GetString("INISETTINGS_SUBPATH"))); + string filename = AppResx.GetString("INISETTINGS_FILENAME"); + return (commondir + subpath + filename); + } + } + internal static string APP_INI_SETTINGS_PATH + { + get + { + // Make sure Subpath begin|end with a slash, as needed + string commondir = PathNaming.PathEndsWithSlash(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData)); + string subpath = PathNaming.PathBeginsWithNoSlash(PathNaming.PathEndsWithNoSlash(AppResx.GetString("INISETTINGS_SUBPATH"))); + return (commondir + subpath); + } + } + + #endregion + + #region Application Help + + // Application's Main Help Object + internal static CHMFile chmhelp = null; + + #endregion + + #region Xml Configuration + + // Application's Main Xml Object + internal static MonitorDataStore xmlfile = null; + + #endregion + + #region Ini Configuration + + // Application's Main Ini Object + internal static INIFile inifile = new INIFile(APP_INI_SETTINGS_FILENAMEANDPATH, typeof(Ini_Setting)); + + internal enum Ini_Setting + { + MonitorSettings__Start_With_Windows, + MonitorSettings__Monitor_On_Start, + MonitorSettings__Max_Fail_Count, + MonitorSettings__Max_Fail_Count_Per_Hour, + MonitorSettings__Default_Exe_Running_Filter, + MonitorSettings__Default_Services_Names_Running_Filter, + MonitorSettings__Sheduler_StartDateTime, + MonitorSettings__Sheduler_RepeatEveryNHoursOrMinutes, + MonitorSettings__Sheduler_RepeatIsHour, + MonitorSettings__Sheduler_LastRun_DateTime, + MonitorSettings__Scheduler_Restarts_Services, + MonitorSettings__ShedulerBackup_StartDateTime, + MonitorSettings__ShedulerBackup_ForDurationNHoursOrMinutes, + MonitorSettings__ShedulerBackup_DurationIsHour, + MonitorSettings__ShedulerBackup_LastRun_DateTime, + MonitorSettings__ShedulerBackup_StopInsteadOfPause, + MonitorSettings__ShedulerBackup_StopServices, + WindowSettings__AboutWindow_TopLeft, + WindowSettings__SettingsWindow_TopLeft, + WindowSettings__LogViewerWindow_TopLeft, + WindowsSettings__LockWorkstation_With_Windows, + WindowsSettings__AutoLogin_With_Windows, + WindowsSettings__CreateExeHardLink_In_RootPath, + EmailSettings__EnableEmailNotifications, + EmailSettings__SmtpServer, + EmailSettings__SenderEmail, + EmailSettings__ReveiverEmail, + EmailSettings__SmtpPort, + EmailSettings__SmtpRequiresAuth, + EmailSettings__SmtpRequiresSSL, + EmailSettings__SmtpUsername, + EmailSettings__SmtpPassword, + EmailSettings__Custom_Message_Text, + } + + // Wrapper class arround Ini Settings + internal static Settings setting = new Settings(); + + #endregion + + #region Command Line Parameters + + // Application's CmdLine Parser + internal static CMDline cmdline = new CMDline(typeof(App.CommandLine_Option), typeof(App.CommandLine_Flag)); + + /// + /// CommandLine Flags for Application + /// + public enum CommandLine_Flag + { + START, + PAUSE, + RESTART, + RESTART_ALL, + STOP, + STOP_ALL, + SHOW_PROCESSES, + SHOW_SERVICES, + LOCK, + RUNASSERVICE, + UNINSTALL, + } + + /// + /// CommandLine Options for Application + /// + public enum CommandLine_Option + { + ADD_SERVICE, + REMOVE_SERVICE, + ADD_PROCESS, + REMOVE_PROCESS, + } + + #endregion + + #region Logging Configuration + + // Application's Main Log Object + internal static Logging log = null; + + /// + /// Create a Logging_Configuration Object with Default Application Settings + /// + /// Specify the LogFile and Path to Log to + /// Specify Logging Details Setting + /// true, to only allow exclusive (one process) access to file + /// a Logging_Configuration object to use Utilities.GenericUtilities.File.Logging + //private static Logging_Configuration CreateDefaultLoggingConfiguration(string LogFileNameNPath, Logging_Detail Detail = Logging_Detail.ERROR, bool UseExclusiveFileLock = false) + private static Logging_Configuration CreateDefaultLoggingConfiguration(string LogFileNameNPath, Logging_Detail Detail, bool UseExclusiveFileLock) + { + Logging_Configuration config; + config.LogFileNameNPath = LogFileNameNPath; + config.maxFileSizeInMB = LOG_FILE_FILE_SIZE_IN_MB; + config.numOfBackupLogFiles = LOG_FILE_NUM_OF_BACKUPS; + config.Detail = Detail; + config.UseExclusiveFileLock = UseExclusiveFileLock; + return config; + } + + /// + /// Responsible for creating the AppMain Logging Object + /// + public static Logging CreateAppMainLogging() + { + // Log FileNameNPath * Debug mode set logging level to Debug, Release to Info * +#if DEBUG + return Logging.AddGlobalLoggerConfiguration(LOG_NAME_APPMAIN, CreateDefaultLoggingConfiguration(APP_LOG_FILENAMEANDPATH, Logging_Detail.DEBUG, false)); +#else + return Logging.AddGlobalLoggerConfiguration(LOG_NAME_APPMAIN, CreateDefaultLoggingConfiguration(APP_LOG_FILENAMEANDPATH, Logging_Detail.INFO, false)); +#endif + } + + /// + /// Retrieve the AppMain Logging Object, It must have been created by calling CreateAppMainLogging() + /// + /// + public static Logging GetAppMainLogging() + { + return Logging.GetLogger(LOG_NAME_APPMAIN); + } + + #endregion + + #region Message Box Configuration + + /// + /// We also want to initialize the MsgBox Class here + /// + public static void ConfigureAppMainMsgBox() + { + // Fatal Errors + MsgBox.MsgBox_FatalErrorTitleHeader = AppResx.GetStringFormated("APPLICATION_FATAL_ERROR", APPLICATION_NAME_SHORT); + MsgBox.MsgBox_FatalErrorHeader = AppResx.GetString("FATAL_ERROR_HEADER"); + MsgBox.MsgBox_FatalErrorFooter = AppResx.GetStringFormated("FATAL_ERROR_APPLICATION_WILL_EXIT", APPLICATION_NAME_LONG) + + AppResx.GetStringFormated("CONTACT_SUPPORT_URGENTLY", SUPPORT_PHONENUMBER) + + AppResx.GetStringFormated("ERROR_LOG_FILE_LOCATION", APP_LOG_FILENAME, APP_LOG_PATH); + + // Errors + MsgBox.MsgBox_ErrorTitleHeader = AppResx.GetStringFormated("APPLICATION_ERROR", APPLICATION_NAME_SHORT); + MsgBox.MsgBox_ErrorHeader = AppResx.GetString("ERROR_HEADER"); + MsgBox.MsgBox_ErrorFooter = AppResx.GetStringFormated("CONTACT_SUPPORT_NICELY", SUPPORT_PHONENUMBER) + + AppResx.GetStringFormated("ERROR_LOG_FILE_LOCATION", APP_LOG_FILENAME, APP_LOG_PATH); + + // Info + MsgBox.MsgBox_InfoTitleHeader = AppResx.GetStringFormated("APPLICATION_INFO", APPLICATION_NAME_SHORT); + + // * For Debugging * + //MsgBox.ShowError("01234567890123456789012345678901234567890123456789012345678901234"); + //MsgBox.ShowFatalError("01234567890123456789012345678901234567890123456789012345678901234"); + } + + #endregion + + #region Application State + + /// + /// Various Keys that we can use to save/get the GUI State + /// + internal enum StateKey + { + Monitor_Started_bool, + Monitor_ErrorsOccured_bool, + Scheduler_Started_bool, + SchedulerBackup_Started_bool, + SpecialMode_Paused_Mode_bool, + SpecialMode_CommandLine_Mode_bool, + SpecialMode_RunAsService_Mode_bool, + SpecialCircum_CommandLine_ConsoleWindowIsAttached_bool, + SpecialCircum_CommandLine_MonitorInstanceExists_bool, + SpecialCircum_DontCloseApplicationsOnExit_bool, + Help_Is_Available_bool, + } + + /// + /// State of the System + /// + public enum StateSystem + { + Running, + Paused, + Stopped, + Error + } + + // Initialize BkgdState + internal static readonly StateM State = new StateM(typeof(StateKey)); + + #endregion + + #region Application State - Monitor State + + /// + /// Returns the state of the System (icon to display) + /// + /// Application State for System + internal static StateSystem State_GetSystemState() + { + if (State.GetStateValue(App.StateKey.Monitor_Started_bool, false)) + { + // Error and Pause are Special 'Running' States + if (State.GetStateValue(App.StateKey.SpecialMode_Paused_Mode_bool, false)) + return StateSystem.Paused; + if (State.GetStateValue(App.StateKey.Monitor_ErrorsOccured_bool, false)) + return StateSystem.Error; + else + return StateSystem.Running; + } + else + { + return StateSystem.Stopped; + } + } + + /// + /// Is the Monitor Currently in a 'Running' State + /// + /// true if yes, false otherwise + internal static bool State_MonitorIsInRunningState() { return State.GetStateValue(App.StateKey.Monitor_Started_bool, false); } + + /// + /// Sets the System into an Error State + /// + /// Error Message to Notify to User + internal static void State_Error_SetErrorState(string strErrorMessageToNotify) + { + App.State.SetStateValue(App.StateKey.Monitor_ErrorsOccured_bool, true); + if (!String.IsNullOrEmpty(strErrorMessageToNotify)) + { + //// + // Application entered an Error State * Better Notify User, if specified * + //// + if (App.setting.EmailNotificationEnabled && App.setting.EmailSettingsValid) + { + bool bIsSend = Emailer.SendEmail(App.setting.EmailStmpServer, App.setting.EmailSenderEmail, App.APPLICATION_NAME_SHORT, + App.setting.EmailReceiverEmail, AppResx.GetStringFormated("ERRORSTATE_EMAIL_SUBJECT", App.APPLICATION_NAME_SHORT), + AppResx.GetStringFormated("ERRORSTATE_EMAIL_BODY", App.APPLICATION_NAME_SHORT, strErrorMessageToNotify ,App.setting.EmailCustomMessageText), + App.setting.EmailSmtpPort, String.Empty, App.setting.EmailStmpServerRequiresAuth, App.setting.EmailSmtpUsername, App.setting.EmailSmtpPassword, + App.setting.EmailStmpServerRequiresSSL, 30); + if (!bIsSend) + App.log.Error("Failed to Send ERRORSTATE_EMAIL"); + else + App.log.Info(String.Format("Email (ERRORSTATE_EMAIL) Notification send to {0}", App.setting.EmailReceiverEmail)); + } + } + } + + /// + /// Error State Reset + /// + internal static void State_Error_ResetErrorState() { App.State.SetStateValue(App.StateKey.Monitor_ErrorsOccured_bool, false); } + + /// + /// Set Main Monitor to Started + /// + internal static void State_MonitorStarted() { App.State.SetStateValue(App.StateKey.Monitor_Started_bool, true); App.State.SetStateValue(App.StateKey.SpecialMode_Paused_Mode_bool, false); } + + /// + /// Set Main Monitor to Stopped + /// + internal static void State_MonitorStopped() { App.State.SetStateValue(App.StateKey.Monitor_Started_bool, false); App.State.SetStateValue(App.StateKey.SpecialMode_Paused_Mode_bool, false); } + + /// + /// Set Main Monitor to Paused + /// + internal static void State_MonitorPaused() { App.State.SetStateValue(App.StateKey.SpecialMode_Paused_Mode_bool, true); } + + /// + /// Is the System in a Paused State? + /// + /// true if yes, false if no + internal static bool State_IsMonitorPaused() { return App.State.GetStateValue(App.StateKey.SpecialMode_Paused_Mode_bool, false); } + + #endregion + + #region Application State - Scheduler State + + /// + /// Is the Scheduler Executing? + /// + /// true if yes, false if no + internal static bool State_IsSchedulerExecuting() { return App.State.GetStateValue(App.StateKey.Scheduler_Started_bool, false); } + + /// + /// Scheduler Execution Started + /// + internal static void State_SchedulerExecution_Started() { App.State.SetStateValue(App.StateKey.Scheduler_Started_bool, true); } + + /// + /// Scheduler Execution Stopped + /// + internal static void State_SchedulerExecution_Stopped() { App.State.SetStateValue(App.StateKey.Scheduler_Started_bool, false); } + + /// + /// Is the Scheduler Executing? + /// + /// true if yes, false if no + internal static bool State_IsSchedulerBackupExecuting() { return App.State.GetStateValue(App.StateKey.SchedulerBackup_Started_bool, false); } + + /// + /// Scheduler Execution Started + /// + internal static void State_SchedulerBackupExecution_Started() { App.State.SetStateValue(App.StateKey.SchedulerBackup_Started_bool, true); } + + /// + /// Scheduler Execution Stopped + /// + internal static void State_SchedulerBackupExecution_Stopped() { App.State.SetStateValue(App.StateKey.SchedulerBackup_Started_bool, false); } + + #endregion + + #region Application State - CommandLine State + + /// + /// Set Application into CommandLine Mode + /// + internal static void State_SpecialMode_CommandLineSet() { App.State.SetStateValue(App.StateKey.SpecialMode_CommandLine_Mode_bool, true); } + + /// + /// Is Application in CommandLine Mode? + /// + /// true if yes, false if no + internal static bool State_SpecialMode_IsCommandLineSet() { return App.State.GetStateValue(App.StateKey.SpecialMode_CommandLine_Mode_bool, false); } + + /// + /// Set Application into RunAsService Mode + /// + internal static void State_SpecialMode_RunAsServiceSet() { App.State.SetStateValue(App.StateKey.SpecialMode_RunAsService_Mode_bool, true); } + + /// + /// Is Application in RunAsService Mode? + /// + /// true if yes, false if no + internal static bool State_SpecialMode_IsRunAsServiceSet() { return App.State.GetStateValue(App.StateKey.SpecialMode_RunAsService_Mode_bool, false); } + + /// + /// Special Circumstance - Set Console as Attached + /// + internal static void State_SpecialCircum_ConsoleWindowIsAttached() { App.State.SetStateValue(App.StateKey.SpecialCircum_CommandLine_ConsoleWindowIsAttached_bool, true); } + + /// + /// Special Circumstance - Was Console Window Attached? + /// + internal static bool State_SpecialCircum_IsConsoleWindowIsAttached() { return App.State.GetStateValue(App.StateKey.SpecialCircum_CommandLine_ConsoleWindowIsAttached_bool, false); } + + /// + /// Special Circumstance - Set Communication with Main Instance via WCF as Succeeded + /// + internal static void State_SpecialCircum_MainMonitorInstance_CommSuccees() { App.State.SetStateValue(App.StateKey.SpecialCircum_CommandLine_MonitorInstanceExists_bool, true); } + + /// + /// Special Circumstance - Did Communication with Mai Instance via WCF Succeed? + /// + /// true if yes, false if no + internal static bool State_SpecialCircum_DidMainMonitorInstanceCommSucceed() { return App.State.GetStateValue(App.StateKey.SpecialCircum_CommandLine_MonitorInstanceExists_bool, false); } + + #endregion + + #region Applicaton State - Special States + + /// + /// Special Circumstance - Set that the Application won't close Applications on Exit * Useful for Auto-Updating the Software * + /// + internal static void State_SpecialCircum_DontCloseApplicationsOnExit() { App.State.SetStateValue(App.StateKey.SpecialCircum_DontCloseApplicationsOnExit_bool, true); } + + /// + /// Special Circumstance - Check to see if we should Close Applications when Exiting + /// + /// true if yes, don't close, false if no + internal static bool State_SpecialCircum_ShouldWeNotCloseApplicationsOnExit() { return App.State.GetStateValue(App.StateKey.SpecialCircum_DontCloseApplicationsOnExit_bool, false); } + + /// + /// Check to see if html is available + /// + /// true, if available, false otherwise + internal static bool State_HtmlHelpIsAvailable() { return App.State.GetStateValue(App.StateKey.Help_Is_Available_bool, false); } + + /// + /// Set Html Help File as available + /// + internal static void State_HtmlHelpAvailable() { App.State.SetStateValue(App.StateKey.Help_Is_Available_bool, true); } + + #endregion + + #region Unhandled Expections! IMP - Show WinForm and Log + + /// + /// * Generic Unhandled Exception Handler * + /// Handles all unhandled Exceptions for the Entire AppDomain. + /// First Show a Window Message Box, so that we can for sure capture the message + /// Second Log it + /// + private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + Exception ex = (Exception)e.ExceptionObject; + + // Exeption to capture error information + string exceptionMessage = AppResx.GetString("FATAL_ERROR_HEADER"); + exceptionMessage = AppResx.GetStringFormated("FATAL_ERROR_APPLICATION_WILL_EXIT", APPLICATION_NAME_LONG); + exceptionMessage += ex.Message + "\n\n"; + if (!String.IsNullOrEmpty(ex.StackTrace)) + exceptionMessage += ex.StackTrace.Substring(0, 880) + "\n\n"; + if(!String.IsNullOrEmpty(ex.InnerException.Message)) + exceptionMessage += ex.InnerException.Message + "\n\n"; + if (!String.IsNullOrEmpty(ex.Source)) + exceptionMessage += ex.Source + "\n\n"; + + // Polite Message to Show in Message Box + string PoliteExceptionMessage = exceptionMessage + + AppResx.GetStringFormated("CONTACT_SUPPORT_URGENTLY", SUPPORT_PHONENUMBER) + + AppResx.GetStringFormated("ERROR_LOG_FILE_LOCATION", APP_LOG_FILENAME, APP_LOG_PATH); + + // Show Message Box First - Guaranteed to work (Polite Exception Message) + MessageBox.Show(PoliteExceptionMessage, AppResx.GetStringFormated("APPLICATION_FATAL_ERROR", APPLICATION_NAME_SHORT), MessageBoxButton.OK, MessageBoxImage.Error); + + // Log the Error to the Main Log File + CreateAppMainLogging().Fatal(exceptionMessage, ex); + } + + #endregion + + #region Fatal Exception! IMP - Show WinForm, Log, and Exit the Application + + /// + /// Some Events continue execution, even though a fatal exception occured (.net!) + /// This flag allows those functions to check for this and stop processing + /// + public static bool s_FatalErrorOccured = false; + + /// + /// Custom User Specified Fatal Exception Occured * Stops Application Execution * + /// + /// Message to show/log + //public static void FatalExceptionStopExecution(string Message, bool bShowMessageBox = true) + public static void FatalExceptionStopExecution(string Message, bool bShowMessageBox) + { + s_FatalErrorOccured = true; + log.Fatal(Message); + if(bShowMessageBox && !App.State_SpecialMode_IsRunAsServiceSet()) + MsgBox.ShowFatalError(Message, "", Forms.MessageBoxButtons.OK); + App.Current.Shutdown(); + + // To make 100% sure, that we are exiting... (not needed) + //System.Diagnostics.Process.GetCurrentProcess().Kill(); + } + + #endregion + + #region Application Multi-File Assembly Handling + + /// + /// A way to embed multiple dlls into one exe: + /// http://blogs.msdn.com/b/microsoft_press/archive/2010/02/03/jeffrey-richter-excerpt-2-from-clr-via-c-third-edition.aspx + /// + /// a loaded assembly if found, null otherwise + System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + string curAssemblyName = AssemblyW.GetAssemblyName(AssemblyW.AssemblyST.Executing); + String resourceName = curAssemblyName + ".Components." + new AssemblyName(args.Name).Name + ".dll"; + + //string[] resources = AssemblyW.SpecializedAssemblyInfo.GetAssemblyResourceNames(AssemblyW.AssemblyST.Entry); + var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); + if(stream != null) + { + using (stream) + { + Byte[] assemblyData = new Byte[stream.Length]; + stream.Read(assemblyData, 0, assemblyData.Length); + return Assembly.Load(assemblyData); + } + } + return null; + } + + #endregion + + #region Application Startup N' Exit + + /// + /// Handles the Application StartUp event + /// + private void Application_Startup(object sender, StartupEventArgs e) + { + // We need to make sure that the Permissions are set correctly for + // the ProgramData/AllUser folder for ALL our configuration files + Installer.GrantFullPermissionToFolderForUserOrGroup(APP_XML_DS_PATH, "Everyone"); + + // Create the First Main Log Instance + log = CreateAppMainLogging(); + + // Parse the Command Line + cmdline.Parse(e.Args); + + // Delay Start * Imp. for Auto-Update Feature + string[] activationData = null; + if(AppDomain.CurrentDomain.SetupInformation.ActivationArguments != null) + activationData = AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData; + if (activationData != null && activationData.Length > 0) + { + uint uDelayStart = 0; + if (uint.TryParse(activationData[0], out uDelayStart) && (uDelayStart > 0)) + { + App.log.Info(String.Format("Auto Update Delay Start Called with {0} Seconds", uDelayStart)); + System.Threading.Thread.Sleep(TimeSpan.FromSeconds(uDelayStart)); + } + } + + // If Command-Line has params, we are being launched in Command-Line Mode * Hence, we put + // the app into a different State * + if (cmdline.HasParams && !cmdline.GetFlagValue(CommandLine_Flag.RUNASSERVICE)) // RunAsService is NOT Command-line Mode + { + App.State_SpecialMode_CommandLineSet(); + log.Info(AppResx.GetStringFormated("APPLICATION_STARTED_COMMANDLINE_MODE", APPLICATION_NAME_SHORT, cmdline.ParsedArgs(), APPLICATION_VERSION)); + } + else if (cmdline.HasParams && cmdline.GetFlagValue(CommandLine_Flag.RUNASSERVICE)) // RunAsService is it's own Mode + { + State_SpecialMode_RunAsServiceSet(); + log.Info(AppResx.GetStringFormated("APPLICATION_STARTED_SERVICE_MODE", APPLICATION_NAME_SHORT, cmdline.ParsedArgs(), APPLICATION_VERSION)); + } + else + { + log.Info(AppResx.GetStringFormated("APPLICATION_STARTED", APPLICATION_NAME_SHORT, APPLICATION_VERSION)); + } + + // Configure our Message Boxes + ConfigureAppMainMsgBox(); + + // Create the Xml File Instance * To Read/Write DataStore * + // ~Also, don't allow the configuration to add this Application to the Configuration + DelegateCollection.Void_Param1_Exception_Func XmlFileExceptionHandler = delegate(Exception ex) + { + App.log.Error("Saving MonitorDataStore XMLFile Error Thrown", ex); + if(!App.State_SpecialMode_IsRunAsServiceSet()) + MsgBox.ShowError("Saving XMLDataStore XMLFile Error " + ex.Message, "", Forms.MessageBoxButtons.OK); + }; + List excludedProcessNames = new List(); + excludedProcessNames.Add(AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileName(AssemblyW.AssemblyST.Entry)); + xmlfile = new MonitorDataStore(App.APP_XML_DS_FILENAMEANDPATH, excludedProcessNames, XmlFileExceptionHandler); + + // If this is a Debug build, enable * Performance Tracing * + #if DEBUG + TraceM.EnableTracing = true; + #endif + + // Make sure that the File Name of this Assembly matches the Assembly Name, + // this allows us to enforce for sure that only One Instance of this program is running + // i.e. someone could run this program otherwise by just changing the filename + string entryAssemblyName = AssemblyW.GetAssemblyName(AssemblyW.AssemblyST.Entry); + string entryAssemblyFileName = AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileName(AssemblyW.AssemblyST.Entry); + if (String.Compare(entryAssemblyName, entryAssemblyFileName, true) != 0) + FatalExceptionStopExecution(AppResx.GetStringFormated("FATAL_ERROR_ASSEMBLY_FILENAME_MISMATCH", entryAssemblyName, entryAssemblyFileName), true); + + // Check that this is the ONLY Instance running on this Machine + // ~We only allow one instance of this app to be running... (Only Do this if NOT in Command-Line Mode) + if (!s_FatalErrorOccured && !App.State_SpecialMode_IsCommandLineSet()) + { + // Check if there are other instances. Start/Stop/Etc won't work if other instances are not running + bool bAnotherInstanceIsRunning = true; +#if DEBUG + bAnotherInstanceIsRunning = !ProcessW.IsTheOnlyProcessRunning(AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileName(AssemblyW.AssemblyST.Entry).ToLower(), false); +#else + bAnotherInstanceIsRunning = !ProcessW.IsTheOnlyProcessRunning(AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileName(AssemblyW.AssemblyST.Entry).ToLower(), true); +#endif + if (bAnotherInstanceIsRunning) + { + // Alert the User and ask the User, if they would like to close that process, + // * Could be that The Application Errored out, yet still remains running, so they are trying to restart it * + Process[] ps = null; +#if DEBUG + ps = ProcessW.AllRunningProcessesOf(AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileName(AssemblyW.AssemblyST.Entry).ToLower(), false, true); +#else + ps = ProcessW.AllRunningProcessesOf(AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileName(AssemblyW.AssemblyST.Entry).ToLower(), true, true); +#endif + Forms.DialogResult dr = System.Windows.Forms.DialogResult.Ignore; + if (!App.State_SpecialMode_IsRunAsServiceSet()) + { + string msg = AppResx.GetStringFormated("FATAL_ERROR_OTHER_INSTANCES", App.APPLICATION_NAME_SHORT); + msg += "\nWould you like to try to force close the other instance(s)?\n"; + msg += "\nClicking 'Yes' will force all other instances to close."; + msg += "\nClicking 'No' will close this instance."; + dr = MsgBox.ShowInfo(msg, "Other Instance Found", Forms.MessageBoxButtons.YesNo); + } + + // Check Dialog Result (If in GUI MODE) + if (dr == Forms.DialogResult.Yes) + { + // try to kill all other instances + bool bSuccess = true; + foreach (Process p in ps) + { + if(Process.GetCurrentProcess().Id != p.Id) + bSuccess = PStarter.KillProcess((uint)p.Id, false, 1, true, 2); + if (!bSuccess) + break; + } + if (!bSuccess) + { + App.log.Error("Error occured closing other instances"); + FatalExceptionStopExecution(AppResx.GetStringFormated("FATAL_ERROR_OTHER_INSTANCES", App.APPLICATION_NAME_SHORT), false); + } + else + { + // * Opportunity To Start the WCF Host * + WCFHost.StartHost(); + + // Refresh the taskbar, after killing any instances + Watchdog.WatchdogLib.Win32.Functions.RefreshTaskbarNotificationArea(); + } + } + else + { + FatalExceptionStopExecution(AppResx.GetStringFormated("FATAL_ERROR_OTHER_INSTANCES", App.APPLICATION_NAME_SHORT), false); + } + } + else + { + // * Opportunity To Start the WCF Host * + WCFHost.StartHost(); + + // * Opportunity to load the chm help file * + // ~Force a new chm file write, if an upgrade occured + try + { + #if DEBUG + bool bForceCreationOfNewCHMFile = true; + #else + bool bForceCreationOfNewCHMFile = (App.APPLICATION_VERSION != App.setting.LastProgramVersion); + #endif + string curAssemblyName = AssemblyW.GetAssemblyName(AssemblyW.AssemblyST.Executing); + App.chmhelp = new CHMFile(Assembly.GetExecutingAssembly().GetManifestResourceStream(curAssemblyName + "." + "Watchdog.chm"), App.APPLICATION_NAME_SHORT, bForceCreationOfNewCHMFile); + if (App.chmhelp != null) + App.State_HtmlHelpAvailable(); // Html Help loaded successfully + if (bForceCreationOfNewCHMFile && (App.chmhelp != null)) + App.setting.LastProgramVersion = App.APPLICATION_VERSION; + } + catch (Exception ex) + { + App.log.Error("Failed to create chm Helf File", ex); + } + } + } + else if (!s_FatalErrorOccured && App.State_SpecialMode_IsCommandLineSet()) + { + // We are called with CmdLine Parameters * Try Attaching to the console, in case we got called from a Command Window * + bool bAttachSuccess = Watchdog.WatchdogLib.Win32.Kernel32.AttachConsole(-1); + App.log.Info(String.Format("CommandLineMode - Attached to Console is {0}", bAttachSuccess)); + if (bAttachSuccess) + App.State_SpecialCircum_ConsoleWindowIsAttached(); + } + } + + /// + /// Handles the Application Exit event. + /// + private void Application_Exit(object sender, ExitEventArgs e) + { + if (!App.State_SpecialMode_IsCommandLineSet()) + { + log.Info(AppResx.GetStringFormated("APPLICATION_ENDED", APPLICATION_NAME_SHORT, APPLICATION_VERSION)); + + // * Opportunity To Stop the WCF Host * + WCFHost.StopHost(); + } + else if(App.State_SpecialMode_IsCommandLineSet()) + { + if(App.State_SpecialCircum_IsConsoleWindowIsAttached()) + { + bool bFreeSuccess = Watchdog.WatchdogLib.Win32.Kernel32.FreeConsole(); + App.log.Info(String.Format("CommandLineMode - Free from Console is {0}", bFreeSuccess)); + } + log.Info(AppResx.GetStringFormated("APPLICATION_ENDED_COMMANDLINE_MODE", APPLICATION_NAME_SHORT, APPLICATION_VERSION)); + } + } + + #endregion + } +} diff --git a/@integrate/AppState.cs b/@integrate/AppState.cs new file mode 100644 index 0000000..055defd --- /dev/null +++ b/@integrate/AppState.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Watchdog.WatchdogLib.Other; + +namespace Watchdog +{ + /// + /// Keeps Track of RunTime State Variables/Objects + /// + public static class AppState + { + private static object s_LockObject = new Object(); + + #region Internal State Check Functions + + /// true if the Process Monitor is running, false otherwise + internal static bool IsMonitorRunning() + { + lock (s_LockObject) + { + return AppState.State.GetStateValue(AppState.StateKey.ProcessMonitorStarted_bool, false); + } + } + + /// true if the Scheduler is running, false otherwise + internal static bool IsSchedulerRunning() + { + lock (s_LockObject) + { + return AppState.State.GetStateValue(AppState.StateKey.SchedulerIsRunning_bool, false); + } + } + + /// true if the Process Monitor encountered errors, false otherwise + internal static bool DidMonitorEncounterErrors() + { + lock (s_LockObject) + { + return AppState.State.GetStateValue(AppState.StateKey.ProcessMonitorEncounteredErrors_bool, false); + } + } + + /// true if the Application is called with Command-Line Parameters, false otherwise + internal static bool IsInCommandLinePrmsMode() + { + lock (s_LockObject) + { + return AppState.State.GetStateValue(AppState.StateKey.IsInCommandLineMode_bool, false); + } + } + + /// true if the Application is attached to a Console Window For Output, false otherwise + internal static bool IsAttachedToConsoleWindow() + { + lock (s_LockObject) + { + return AppState.State.GetStateValue(AppState.StateKey.IsAttachedToConsoleWindow_bool, false); + } + } + + /// When the application is in commandline mode, we want to make sure that a 'real' monitor instance is running, + /// in order to communicate with it, this keeps track if that other instance is available + internal static bool InCommandLineMode_IsMonitorInstanceAvailable() + { + lock (s_LockObject) + { + return AppState.State.GetStateValue(AppState.StateKey.InCommandLineMode_IsMonitorInstanceAvailable_bool, false); + } + } + + #endregion + + #region Internal State Set Functions + + /// + /// Use this to set the Monitor is Running state to true/false + /// + internal static bool MonitorIsRunning(bool bIsRunning) + { + lock (s_LockObject) + { + return AppState.State.SetStateValue(AppState.StateKey.ProcessMonitorStarted_bool, bIsRunning); + } + } + + /// + /// Use this to set the Scheduler is Running state to true/false + /// + internal static bool SchedulerIsRunning(bool bIsRunning) + { + lock (s_LockObject) + { + return AppState.State.SetStateValue(AppState.StateKey.SchedulerIsRunning_bool, bIsRunning); + } + } + + /// + /// Use this to set the Monitor's Error State + /// + internal static bool MonitorEncounterErrors(bool bErrorsOccured) + { + lock (s_LockObject) + { + return AppState.State.SetStateValue(AppState.StateKey.ProcessMonitorEncounteredErrors_bool, bErrorsOccured); + } + } + + /// + /// Use this to set that the Application is called with Command-Line Parameters + /// + internal static bool CommandLinePrmsMode(bool bIsInCommandLinePrmsMode) + { + lock (s_LockObject) + { + return AppState.State.SetStateValue(AppState.StateKey.IsInCommandLineMode_bool, bIsInCommandLinePrmsMode); + } + } + + /// + /// Use this to set that the Application is attached to a Console Window for Output + /// + internal static bool AttachedToConsoleWindow(bool bItIsAttached) + { + lock (s_LockObject) + { + return AppState.State.SetStateValue(AppState.StateKey.IsAttachedToConsoleWindow_bool, bItIsAttached); + } + } + + /// + /// Use this to set that the Application can communicate with the 'real' monitor instance that is running. + /// + internal static bool CommandLineMode_IsMonitorInstanceAvailable(bool bIsAvailable) + { + lock (s_LockObject) + { + return AppState.State.GetStateValue(AppState.StateKey.InCommandLineMode_IsMonitorInstanceAvailable_bool, bIsAvailable); + } + } + + #endregion + + /// + /// Various Keys that we can use to save/get the GUI State + /// + internal enum StateKey + { + ProcessMonitorStarted_bool, + ProcessMonitorEncounteredErrors_bool, + SchedulerIsRunning_bool, + IsInCommandLineMode_bool, + IsAttachedToConsoleWindow_bool, + InCommandLineMode_IsMonitorInstanceAvailable_bool, + } + + // Initialize BkgdState + internal static readonly StateM State = new StateM(typeof(StateKey)); + } +} diff --git a/@integrate/CMDlineHandler.cs b/@integrate/CMDlineHandler.cs new file mode 100644 index 0000000..a2dd276 --- /dev/null +++ b/@integrate/CMDlineHandler.cs @@ -0,0 +1,311 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Diagnostics; +using WatchdogLib; +using Watchdog.WatchdogLib.WinForms; +using Watchdog.WatchdogLib.Win32; + +namespace Watchdog +{ + /// + /// Handles CommandLine Logic + /// + public static class CMDlineHandler + { + // Our WCF Client object stub + private static IWatchdog _proxyObj = null; + + /// + /// Shows the Command Line Help either as a Windows Form or + /// to the Console * Depending if a Console Window is Attached * + /// + private static void ShowCommandLineHelp() + { + bool bToConsole = App.State_SpecialCircum_IsConsoleWindowIsAttached(); + string cmdHelpText = ""; + + cmdHelpText += "\n"; + cmdHelpText += "**************************************************************\n"; + cmdHelpText += " Make changes to the .xml Configuration.\n"; + cmdHelpText += "**************************************************************\n"; + cmdHelpText += "-Add_Process \"Application Exe;Prms;WorkingDir\"\n"; + cmdHelpText += "-Remove_Process \"Application Exe;Prms\"\n"; + cmdHelpText += "-Add_Service \"Service Name\"\n"; + cmdHelpText += "-Remove_Service \"Service Name\"\n"; + cmdHelpText += "**************************************************************\n"; + cmdHelpText += " View the .xml Configuration.\n"; + cmdHelpText += "**************************************************************\n"; + cmdHelpText += "-Show_Processes\n"; + cmdHelpText += "-Show_Services\n"; + cmdHelpText += "**************************************************************\n"; + cmdHelpText += " Manipulate a Running Monitor Instance.\n"; + cmdHelpText += "**************************************************************\n"; + cmdHelpText += "-Start\n"; + cmdHelpText += "-Pause\n"; + cmdHelpText += "-Restart\n"; + cmdHelpText += "-Restart_All\n"; + cmdHelpText += "-Stop\n"; + cmdHelpText += "-Stop_All\n"; + cmdHelpText += "**************************************************************\n"; + cmdHelpText += "-? Show Help.\n\n"; + + // Show Help to the User (Either to the console or a Message Window) + if (bToConsole) + Console.Write(cmdHelpText); + else + MsgBox.ShowInfo(cmdHelpText, (App.APPLICATION_NAME_SHORT + " Command Line Help"), System.Windows.Forms.MessageBoxButtons.OK); + } + + /// + /// Either writes out to the console or shows a message box, if we want to alert the user + /// + /// string to write out + private static void WriteOut(string str) + { + bool bToConsole = App.State_SpecialCircum_IsConsoleWindowIsAttached(); + if (bToConsole) + Console.WriteLine("\n" + str); + else + MsgBox.ShowInfo(str, (App.APPLICATION_NAME_SHORT + " Command Line Alert"), System.Windows.Forms.MessageBoxButtons.OK); + } + + /// + /// Main Entry Point for CommandLine Mode * When Application is being called with + /// CommandLine Parameters * + /// + static public void EnterCommandLineMode() + { + // Show Help? + if (App.cmdline.ShowHelp || !App.cmdline.ParamsValid) + { + ShowCommandLineHelp(); + return; + } + + // Check now using WCF... + // Now we need to check communication with the other instance, + // if there isn't another process, then start/stop/restart/etc,.. won't work + _proxyObj = WCFHost.GetWatchDogClientInterface(); + if (_proxyObj != null) + App.State_SpecialCircum_MainMonitorInstance_CommSuccees(); + else + App.log.Info("No other Instances found, Command-line parameter functionallity limited to making configuration changes only"); + + // Now let's go thru all the CommandLine_Flags * WCF * + DealWithApplicationStateWCFCalls_CommandLineFlags(); + + // Now let's go thru all the CommandLine_Flags * Non-WCF * + DealWithDisplayConfigurationNonWCFCalls_CommandLineFlags(); + + // Now let's go thru all the CommandLine_Options * Non-WCF * + DealWithConfigurationChanges_CommandLineOptions(); + + // Done Here + _proxyObj = null; + } + + /// + /// Deals with all the State Changes for the Main Application + /// * Via WCF * calls into main app and makes those changes + /// + private static void DealWithApplicationStateWCFCalls_CommandLineFlags() + { + // Now let's go thru all the CommandLine_Flags * WCF * + if (App.State_SpecialCircum_DidMainMonitorInstanceCommSucceed()) + { + try + { + if (App.cmdline.GetFlagValue(App.CommandLine_Flag.START)) + { + _proxyObj.StartMonitoring(); + } + else if (App.cmdline.GetFlagValue(App.CommandLine_Flag.STOP)) + { + _proxyObj.StopMonitoring(); + } + else if (App.cmdline.GetFlagValue(App.CommandLine_Flag.STOP_ALL)) + { + _proxyObj.StopAllMonitoring(); + } + else if (App.cmdline.GetFlagValue(App.CommandLine_Flag.RESTART)) + { + _proxyObj.RestartMonitoring(); + } + else if (App.cmdline.GetFlagValue(App.CommandLine_Flag.RESTART_ALL)) + { + _proxyObj.RestartAllMonitoring(); + } + else if (App.cmdline.GetFlagValue(App.CommandLine_Flag.PAUSE)) + { + _proxyObj.PauseMonitoring(); + } + } + catch (Exception ex) + { + App.log.Error("Error Occured processing CommandLine_Flag via WCF", ex); + + // Alert the user + WriteOut(ex.Message); + } + } + } + + /// + /// Deals with all the Flags that deal with displaying configuration. does this locally * in this process * + /// View Configuration. Also handles LockWorkstation (Feature). + /// + private static void DealWithDisplayConfigurationNonWCFCalls_CommandLineFlags() + { + if (App.cmdline.GetFlagValue(App.CommandLine_Flag.LOCK)) + { + // Hidden Feature, allows us to specify in windows start-up to lock the workstation + if (!User32.LockWorkStation()) + { + App.log.Error("LockWorkstation() Failed"); + WriteOut("LockWorkstation Failed"); + } + } + else if (App.cmdline.GetFlagValue(App.CommandLine_Flag.SHOW_PROCESSES)) + { + // write out each process + string[] processes = WCFHost.ShowProcesses(); + StringBuilder sb = new StringBuilder(); + if (processes != null && processes.Length != 0) + { + foreach (string p in processes) + { + sb.Append(p); + sb.Append("\n"); + } + } + if (sb.Length > 0) + WriteOut(sb.ToString()); + } + else if (App.cmdline.GetFlagValue(App.CommandLine_Flag.SHOW_SERVICES)) + { + // write out each service + string[] services = WCFHost.ShowServices(); + StringBuilder sb = new StringBuilder(); + if (services != null && services.Length != 0) + { + foreach (string s in services) + { + sb.Append(s); + sb.Append("\n"); + } + } + if (sb.Length > 0) + WriteOut(sb.ToString()); + } + } + + /// + /// Deals with making configuration changes. does this locally * in this process * + /// Add/Remove Configuration + /// + private static void DealWithConfigurationChanges_CommandLineOptions() + { + string ProcessAdd = App.cmdline.GetOptionValue(App.CommandLine_Option.ADD_PROCESS, String.Empty); + if (String.IsNullOrEmpty(ProcessAdd)) + { + string ProcessRemove = App.cmdline.GetOptionValue(App.CommandLine_Option.REMOVE_PROCESS, String.Empty); + if (String.IsNullOrEmpty(ProcessRemove)) + { + string ServiceAdd = App.cmdline.GetOptionValue(App.CommandLine_Option.ADD_SERVICE, String.Empty); + if (String.IsNullOrEmpty(ServiceAdd)) + { + string ServiceRemove = App.cmdline.GetOptionValue(App.CommandLine_Option.REMOVE_SERVICE, String.Empty); + if (!String.IsNullOrEmpty(ServiceRemove)) + { + bool bRemoved = WCFHost.RemoveService(ServiceRemove); + if (bRemoved) + { + WriteOut(String.Format("Service '{0}' removed successfully from Configuration", ServiceRemove)); + App.log.Info(String.Format("Service '{0}' removed successfully from Configuration", ServiceRemove)); + + // * Reload on Main Instance, if possible * + if (App.State_SpecialCircum_DidMainMonitorInstanceCommSucceed()) _proxyObj.ReloadConfigurationNextInterval(); + App.xmlfile.ForceRefreshOnNext_ReadData = true; + } + else + { + WriteOut(String.Format("Service '{0}' failed to be removed from Configuration", ServiceRemove)); + App.log.Error(String.Format("Service '{0}' failed to be removed from Configuration", ServiceRemove)); + } + } + } + else + { + bool bAdded = WCFHost.AddService(ServiceAdd); + if (bAdded) + { + WriteOut(String.Format("Service '{0}' added successfully to Configuration", ServiceAdd)); + App.log.Info(String.Format("Service '{0}' added successfully to Configuration", ServiceAdd)); + + // * Reload on Main Instance, if possible * + if (App.State_SpecialCircum_DidMainMonitorInstanceCommSucceed()) _proxyObj.ReloadConfigurationNextInterval(); + App.xmlfile.ForceRefreshOnNext_ReadData = true; + } + else + { + WriteOut(String.Format("Service '{0}' failed to be added to Configuration", ServiceAdd)); + App.log.Error(String.Format("Service '{0}' failed to be added to Configuration", ServiceAdd)); + } + } + } + else + { + string[] pNc = ProcessRemove.Split(';'); + bool bRemoved = false; + if (pNc.Length == 1) + bRemoved = WCFHost.RemoveProcess(pNc[0], String.Empty); + else if (pNc.Length == 2) + bRemoved = WCFHost.RemoveProcess(pNc[0], pNc[1]); + if (bRemoved) + { + WriteOut(String.Format("Application '{0}' with CommandLinePrms '{1}' removed successfully from Configuration", pNc[0], (pNc.Length > 1) ? pNc[1] : "")); + App.log.Info(String.Format("Application '{0}' with CommandLinePrms '{1}' removed successfully from Configuration", pNc[0], (pNc.Length > 1) ? pNc[1] : "")); + + // * Reload on Main Instance, if possible * + if (App.State_SpecialCircum_DidMainMonitorInstanceCommSucceed()) _proxyObj.ReloadConfigurationNextInterval(); + App.xmlfile.ForceRefreshOnNext_ReadData = true; + } + else + { + WriteOut(String.Format("Application '{0}' with CommandLinePrms '{1}' failed to be removed from Configuration", pNc[0], (pNc.Length > 1) ? pNc[1] : "")); + App.log.Error(String.Format("Application '{0}' with CommandLinePrms '{1}' failed to be removed from Configuration", pNc[0], (pNc.Length > 1) ? pNc[1] : "")); + } + } + } + else + { + string[] pNc = ProcessAdd.Split(';'); + bool bAdded = false; + if(pNc.Length == 1) + bAdded = WCFHost.AddProcess(pNc[0], String.Empty, String.Empty); + else if(pNc.Length == 2) + bAdded = WCFHost.AddProcess(pNc[0], pNc[1], String.Empty); + else if (pNc.Length == 3) + bAdded = WCFHost.AddProcess(pNc[0], pNc[1], pNc[2]); + if (bAdded) + { + WriteOut(String.Format("Application '{0}' with CommandLinePrms '{1}' added successfully to Configuration", pNc[0], (pNc.Length > 1) ? pNc[1] : "")); + App.log.Info(String.Format("Application '{0}' with CommandLinePrms '{1}' added successfully to Configuration", pNc[0], (pNc.Length > 1) ? pNc[1] : "")); + + // * Reload on Main Instance, if possible * + if (App.State_SpecialCircum_DidMainMonitorInstanceCommSucceed()) _proxyObj.ReloadConfigurationNextInterval(); + App.xmlfile.ForceRefreshOnNext_ReadData = true; + } + else + { + WriteOut(String.Format("Application '{0}' with CommandLinePrms '{1}' failed to be added to Configuration", pNc[0], (pNc.Length > 1) ? pNc[1] : "")); + App.log.Error(String.Format("Application '{0}' with CommandLinePrms '{1}' failed to be added to Configuration", pNc[0], (pNc.Length > 1) ? pNc[1] : "")); + } + } + } + + } +} diff --git a/@integrate/CheckActiveTwo.cs b/@integrate/CheckActiveTwo.cs new file mode 100644 index 0000000..f5c746c --- /dev/null +++ b/@integrate/CheckActiveTwo.cs @@ -0,0 +1,482 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Text; +using System.Diagnostics; + +namespace PhoneHome +{ + internal class CheckActiveTwo + { + internal string ProductName = String.Empty; + internal string ProductVersion = String.Empty; + internal string SerialNumber = String.Empty; + private string _LastGeneratedKey = String.Empty; + + /// + /// + /// + /// + /// + /// + internal CheckActiveTwo(string ProductName, string ProductVersion, string SerialNumber) + { + if (!String.IsNullOrEmpty(ProductName) && !String.IsNullOrEmpty(ProductVersion) && !String.IsNullOrEmpty(SerialNumber)) + { + this.ProductName = ProductName; + this.ProductVersion = ProductVersion; + this.SerialNumber = SerialNumber; + } + else + { + throw new ArgumentException("ProductName, ProductVersion, and SerialNumber can't be blank"); + } + } + + /// + /// + /// + /// + internal CheckActiveTwo(string strEncGeneratedString) + { + if (IsValidEncKey(strEncGeneratedString)) + { + _LastGeneratedKey = strEncGeneratedString; + } + else + { + throw new ArgumentException("Not a valid Enc String"); + } + } + + #region Static Internal Utilities + + /// + /// Perform checksum on string + /// + /// + /// Checksum + internal static int PerformChecksum(string strAboutToBeChecksummed) + { + if (!String.IsNullOrEmpty(strAboutToBeChecksummed)) + { + int nChecksum = 0; + for (int i = 0; i < strAboutToBeChecksummed.Length; ++i) + { + if (Char.IsDigit(strAboutToBeChecksummed[i])) + nChecksum = nChecksum + int.Parse(strAboutToBeChecksummed[i].ToString()); + } + return nChecksum; + } + return 0; + } + + /// + /// Dash a String + /// + /// + internal static string MakeIntoDashSeperatedString(string strAboutToBeDashed, int DashEveryNthCharacter) + { + if (!String.IsNullOrEmpty(strAboutToBeDashed)) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < strAboutToBeDashed.Length; i++) + { + if ((i != 0) && ((i % DashEveryNthCharacter) == 0)) + sb.Append("-"); + sb.Append(strAboutToBeDashed[i]); + } + return sb.ToString(); + } + return String.Empty; + } + + /// + /// Undash a String + /// + /// + internal static string MakeIntoDashUnseperatedString(string strAboutToBeUndashed) + { + if (!String.IsNullOrEmpty(strAboutToBeUndashed)) + return strAboutToBeUndashed.Replace("-", ""); + return String.Empty; + } + + #endregion + + #region Internal Methods + + /// + /// Generate a new Key to use - the key can be used to verify the serial number + /// + /// a new Key + internal string GenerateNewKey() + { + // GenerateString For User to send + string KeyValue = MakeKey(); + string EncrKey = EncodeShuffle(KeyValue); + _LastGeneratedKey = MakeIntoDashSeperatedString(EncrKey, 4); + + // For Debugging + string UnEncoded = DecodeShuffle(EncrKey); + if (KeyValue != UnEncoded) + { + // something is terribly wrong with the Encryption + Debug.Assert(false); + } + return _LastGeneratedKey; + } + + /// + /// The function should really be called 'GeneratedPIN' but for reverse engineering + /// purpose, keeping them guessin * security by obscurity * + /// + /// + internal string GeneraledPiM() + { + if (!String.IsNullOrEmpty(_LastGeneratedKey)) + { + int KeyChecksum = PerformChecksum(MakeKey()); + int ShuffledChecksum = PerformChecksum(_LastGeneratedKey); + string Result = KeyChecksum.ToString() + ShuffledChecksum.ToString(); + if (Result.Length < 4) + { + StringBuilder sb = new StringBuilder(Result); + int nRemainder = 4 - Result.Length; + while (nRemainder > 0) { sb.Append("7"); nRemainder--; } + Result = sb.ToString(); + } + Result = Result.Substring(0, 4); + int nHour = DateTime.Now.ToUniversalTime().Hour; + nHour = (nHour <= 6) ? (nHour + 3) : (nHour - 2); + string strHour = String.Format("{0}", (nHour < 10) ? ("8" + nHour.ToString()) : (nHour.ToString())); + string fourdigitPin = (strHour[1] + Result[1].ToString() + strHour[0] + Result[3].ToString()); + int nChecksumPin = PerformChecksum(fourdigitPin); + string strChecksumLastDigit = nChecksumPin.ToString()[nChecksumPin.ToString().Length - 1].ToString(); + return fourdigitPin + strChecksumLastDigit; + } + else + { + GenerateNewKey(); + return GeneraledPiM(); + } + } + + /// + /// + /// + /// + /// + /// + /// + /// true, if successful, false otherwise + internal bool RetrieveValues(out DateTime dtStamp, out string ProductName, out string ProductVersion, out string SerialNumber) + { + dtStamp = DateTime.MinValue; + ProductName = String.Empty; + ProductVersion = String.Empty; + SerialNumber = String.Empty; + if (!String.IsNullOrEmpty(_LastGeneratedKey)) + { + string Undashed = MakeIntoDashUnseperatedString(_LastGeneratedKey); + if (UnmakeKey(DecodeShuffle(Undashed), out dtStamp, out ProductName, out ProductVersion, out SerialNumber)) + { + this.ProductName = ProductName; + this.ProductVersion = ProductVersion; + this.SerialNumber = SerialNumber; + return true; + } + } + return false; + } + + #endregion + + #region Private Key Generation Functions + + private bool ContainsOnlyDigits(string strToCheckForDigits) + { + if (!String.IsNullOrEmpty(strToCheckForDigits)) + { + for (int i = 0; i < strToCheckForDigits.Length; ++i) + { + if (!Char.IsDigit(strToCheckForDigits[i])) + return false; + } + return true; + } + return false; + } + + /// + /// Check to see if the Generated string being passed in is a valid generated string + /// + /// + /// true if valid, false otherwise + private bool IsValidEncKey(string strEncGeneratedString) + { + string Undashed = MakeIntoDashUnseperatedString(strEncGeneratedString); + DateTime dt; + string pName, pVersion, pSerial; + if (UnmakeKey(DecodeShuffle(Undashed), out dt, out pName, out pVersion, out pSerial)) + return true; + return false; + } + + /// + /// Make a Key to send across (all the info the auth needs) + /// + /// Key with needed Info + private string MakeKey() + { + //string dtShortTest = DateTime.Now.ToUniversalTime().ToShortDateString().Replace("/", ""); + DateTime dtUniversal = DateTime.Now.ToUniversalTime(); + string dtMonth = (dtUniversal.Month > 9) ? String.Format("{0}", dtUniversal.Month) : String.Format("0{0}", dtUniversal.Month); + string dtDay = (dtUniversal.Day > 9) ? String.Format("{0}", dtUniversal.Day) : String.Format("0{0}", dtUniversal.Day); + string dtYear = dtUniversal.Year.ToString(); + string dtShort = String.Format("{0}{1}{2}", dtMonth, dtDay, dtYear); + string ProductId = ProductName.Substring(0, 1); // should always be 'M' or 'L' // so we use "LMXYZ" + string strKey = dtShort + "Z" + ProductId + "Y" + ProductVersion.Split('.')[0] + "X" + SerialNumber; + return strKey; + } + + /// + /// Unmake a key * Don't even know why i wrote this, prob. won't end up using this * + /// + /// true if successful, false otheriwise + private bool UnmakeKey(string strAboutToBeUnkeyed, out DateTime dtStamp, out string ProductName, out string ProductVersion, out string SerialNumber) + { + dtStamp = DateTime.MinValue; + ProductName = ""; + ProductVersion = ""; + SerialNumber = ""; + + //string strKey = dtShort + "Z" + ProductId + "Y" + ProductVersion.Split('.')[0] + "X" + SerialNumber; + //0123456Z + try + { + if (!String.IsNullOrEmpty(strAboutToBeUnkeyed)) + { + int nZIndex = strAboutToBeUnkeyed.IndexOf("Z"); + int nYIndex = strAboutToBeUnkeyed.IndexOf("Y"); + int nXIndex = strAboutToBeUnkeyed.IndexOf("X"); + if (nZIndex == -1 || nYIndex == -1 || nXIndex == -1) + return false; + + // dtShort + string strDT = strAboutToBeUnkeyed.Substring(0, nZIndex); + strDT = String.Format("{0}/{1}/{2}", strDT.Substring(0, 2), strDT.Substring(2, 2), strDT.Substring(4)); + dtStamp = DateTime.Parse(strDT); + + // ProductId + string ProductId = strAboutToBeUnkeyed.Substring(nZIndex + 1, 1); + if (ProductId == "L") + ProductName = "Lytec"; + else if (ProductId == "M") + ProductName = "Medisoft"; + else + return false; + + // ProductVersion + string strProductVersion = strAboutToBeUnkeyed.Substring(nYIndex + 1, (nXIndex - nYIndex - 1)); + if (!String.IsNullOrEmpty(strProductVersion) && ContainsOnlyDigits(strProductVersion)) + ProductVersion = strProductVersion; + else + return false; + + // Serial Number + SerialNumber = strAboutToBeUnkeyed.Substring(nXIndex + 1); + return !String.IsNullOrEmpty(SerialNumber) && ContainsOnlyDigits(SerialNumber); + } + } + catch (Exception) { /* ignore */ } + return false; + } + + /// + /// * Simple Encoder * + /// + /// + /// + private string EncodeShuffle(string strAboutToBeEncodeShuffled) + { + const string Char_CodeLib = "ABCDEFGHIJKNOPQRSTUVW"; //20 - W is never used + const string Char_CodeLibExcluded = "LMXYZ"; //5 + + if (!String.IsNullOrEmpty(strAboutToBeEncodeShuffled)) + { + List ResultStr = new List(strAboutToBeEncodeShuffled); + int nCount = ResultStr.Count; + + // Every N'th Digit do something + for (int i = 0; i < nCount; i = i + 3) + { + char c = ResultStr[i]; + if (char.IsDigit(c)) + { + int nChar = int.Parse(c.ToString()); + ResultStr[i] = Char_CodeLib[nChar]; // 0..9 + } + } + + // Every N'th Digit do something + for (int i = 0; i < nCount; i = i + 4) + { + char c = ResultStr[i]; + if (char.IsDigit(c)) + { + int nChar = int.Parse(c.ToString()); + ResultStr[i] = Char_CodeLib[nChar + 10]; // 10..19 + } + } + + // Add Randomness to the end of the string + Random random = new Random(); + int nRand = random.Next(1, 9); + + // Perform a Random Shift * So that the code ALWAYS looks different from use to use * + for (int i = 0; i < nCount; i = i + 2) + { + char c = ResultStr[i]; + if (char.IsLetter(c) && !Char_CodeLibExcluded.Contains(c.ToString())) + { + int nIndexShifted = Char_CodeLib.IndexOf(c) + nRand; + int nIndexShiftedAdj = nIndexShifted % 21; + ResultStr[i] = Char_CodeLib[nIndexShiftedAdj]; // 0..20 + } + } + + // Perform another Random Swap * So that the code ALWAYS looks different from use to use * + for (int i = 0; i < nCount; i = i + nRand) + { + char c = ResultStr[i]; + int nOpposite = nCount - i - 1; + char o = ResultStr[nOpposite]; + if (char.IsLetter(c) && !Char_CodeLibExcluded.Contains(c.ToString()) && + char.IsLetter(o) && !Char_CodeLibExcluded.Contains(o.ToString())) + { + // swap + ResultStr[i] = o; + ResultStr[nOpposite] = c; + } + } + + // Perform a Reversal + for (int i = 0; i < (nCount / 2); ++i) + { + char N1 = ResultStr[i]; + char N2 = ResultStr[nCount - 1 - i]; + // swap + ResultStr[i] = N2; + ResultStr[nCount - 1 - i] = N1; + } + + // Add the Randomness to the string for proper decoding to occur + ResultStr.Add(Char.Parse(nRand.ToString())); + + // And Return + return new String(ResultStr.ToArray()); + } + return String.Empty; + } + + /// + /// * Simple Decoder * + /// + /// + /// + private string DecodeShuffle(string strAboutToBeDecodeShuffled) + { + const string Char_CodeLib = "ABCDEFGHIJKNOPQRSTUVW"; //20 + const string Char_CodeLibExcluded = "LMXYZ"; //5 + try + { + if (!String.IsNullOrEmpty(strAboutToBeDecodeShuffled)) + { + List ResultStr = new List(strAboutToBeDecodeShuffled); + + // retrieve Randomness Factor + char cLast = ResultStr[ResultStr.Count - 1]; + ResultStr.RemoveAt(ResultStr.Count - 1); + int nCount = ResultStr.Count; + int nRand = int.Parse(cLast.ToString()); + + // Perform a Reversal + for (int i = 0; i < (nCount / 2); ++i) + { + char N1 = ResultStr[i]; + char N2 = ResultStr[nCount - 1 - i]; + // swap + ResultStr[i] = N2; + ResultStr[nCount - 1 - i] = N1; + } + + // Perform another Random Swap * So that the code ALWAYS looks different from use to use * + for (int i = 0; i < nCount; i = i + nRand) + { + char c = ResultStr[i]; + int nOpposite = nCount - i - 1; + char o = ResultStr[nOpposite]; + if (char.IsLetter(c) && !Char_CodeLibExcluded.Contains(c.ToString()) && + char.IsLetter(o) && !Char_CodeLibExcluded.Contains(o.ToString())) + { + // swap + ResultStr[i] = o; + ResultStr[nOpposite] = c; + } + } + + // Perform a Random Shift * So that the code ALWAYS looks different from use to use * + for (int i = 0; i < nCount; i = i + 2) + { + char c = ResultStr[i]; + if (char.IsLetter(c) && !Char_CodeLibExcluded.Contains(c.ToString())) + { + int nIndexShifted = Char_CodeLib.IndexOf(c) - nRand; + int nIndexShiftedAdj = (nIndexShifted < 0)? 21 + nIndexShifted : nIndexShifted; + + ResultStr[i] = Char_CodeLib[nIndexShiftedAdj]; // 0..20 + } + } + + // Every N'th Digit do something + for (int i = 0; i < nCount; i = i + 4) + { + char c = ResultStr[i]; + if (char.IsLetter(c) && !Char_CodeLibExcluded.Contains(c.ToString())) + { + int nIndex = Char_CodeLib.IndexOf(c); + if (nIndex >= 10) + { + nIndex = nIndex - 10; + if (nIndex >= 0 && nIndex <= 9) + ResultStr[i] = Char.Parse(nIndex.ToString()); // 11..19 + } + } + } + + // Every N'th Digit do something + for (int i = 0; i < nCount; i = i + 3) + { + char c = ResultStr[i]; + if (char.IsLetter(c)) + { + int nIndex = Char_CodeLib.IndexOf(c); + if (nIndex >= 0 && nIndex <= 9) + ResultStr[i] = Char.Parse(nIndex.ToString()); // 1..9 + } + } + + // And Return + return new String(ResultStr.ToArray()); + } + } + catch (Exception) { /* ignore */ } + return String.Empty; + } + + #endregion + } + +} diff --git a/@integrate/ClickOnceUpdater.cs b/@integrate/ClickOnceUpdater.cs new file mode 100644 index 0000000..faa0c43 --- /dev/null +++ b/@integrate/ClickOnceUpdater.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Deployment.Application; +using Forms = System.Windows.Forms; +using System.Windows; +using System.Diagnostics; +using Watchdog.WatchdogLib.WinForms; +using System.IO; + +namespace Watchdog +{ + /// + /// Responsible for Dynamically Checking if there is an update available via ClickOnce + /// + public static class ClickOnceUpdater + { + /// + /// Construction + /// + static ClickOnceUpdater(){} + + /// + /// Installs the Update and Restarts the Current Instance + /// + /// false if an error occured, if succesful * Will Restart the App * + internal static bool InstallUpdateAndRestartIfSuccessful() + { + try + { + UpdateCheckInfo info = null; + ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment; + if (ad != null) + { + // Log this to make sure we are sane + App.log.Info("Success in retrieving Application Deployment Manifest. This is a ClickOnce App"); + + try + { + info = ad.CheckForDetailedUpdate(); + } + catch (DeploymentDownloadException dde) + { + MsgBox.ShowInfo("The new version of the application cannot be downloaded at this time. \n\nPlease check your network connection, or try again later. Error: " + dde.Message, "Unable to Download", System.Windows.Forms.MessageBoxButtons.OK); + App.log.Info("The new version of the application cannot be downloaded at this time. \n\nPlease check your network connection, or try again later. Error: " + dde.Message); + return false; + } + catch (InvalidDeploymentException ide) + { + MsgBox.ShowError("Cannot check for a new version of the application. The ClickOnce deployment is corrupt. Please redeploy the application and try again. Error: " + ide.Message, "Invalid Deployment", System.Windows.Forms.MessageBoxButtons.OK); + App.log.Error("Cannot check for a new version of the application. The ClickOnce deployment is corrupt. Please redeploy the application and try again. Error: " + ide.Message); + return false; + } + catch (InvalidOperationException ioe) + { + MsgBox.ShowError("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message, "Invalid Operation", System.Windows.Forms.MessageBoxButtons.OK); + App.log.Error("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message); + return false; + } + + if (info.UpdateAvailable) + { + Boolean doUpdate = true; + + if (!info.IsUpdateRequired) + { + Forms.DialogResult dr = MsgBox.ShowInfo("An update is available. Would you like to update\nthe application now?", "Update Available", Forms.MessageBoxButtons.OKCancel); + if (!(Forms.DialogResult.OK == dr)) + doUpdate = false; + } + else + { + // Display a message that the app MUST reboot. Display the minimum required version. + MsgBox.ShowInfo("This application has detected a mandatory update from your\ncurrent " + + "version to version " + info.MinimumRequiredVersion.ToString() + + ".\nThe application will now install\nthe update and restart.", + "Update Available", System.Windows.Forms.MessageBoxButtons.OK); + App.log.Info("This application has detected a mandatory update from your current " + + "version to version " + info.MinimumRequiredVersion.ToString() + + ". The application will now install the update and restart."); + } + + if (doUpdate) + { + try + { + ad.Update(); + App.log.Info("The application has been upgraded,and will now restart."); + + // Restart the Application * Imp! Auto-Delay the Start of the new Watchdog Instance * + if(File.Exists(App.APPLICATION_CLICKONCE_STARTMENU_LINK)) + Process.Start(App.APPLICATION_CLICKONCE_STARTMENU_LINK, "15"); + + // Auto-Update * No Need to Close Applications * + App.State_SpecialCircum_DontCloseApplicationsOnExit(); + Application.Current.Shutdown(); + return true; + } + catch (DeploymentDownloadException dde) + { + MsgBox.ShowError("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde, "Update Error", System.Windows.Forms.MessageBoxButtons.OK); + App.log.Error("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde); + return false; + } + } + } + else + { + App.log.Info("Newer Version not available at this time."); + MsgBox.ShowInfo("A newer version of the application\nis not available at this time. \n\nPlease try again later.", "Newer Version not available", System.Windows.Forms.MessageBoxButtons.OK); + } + } + + } + catch (Exception) { /* ignore */ } + return false; + } + + } +} diff --git a/@integrate/Component.All/Common.cs b/@integrate/Component.All/Common.cs new file mode 100644 index 0000000..7cb381f --- /dev/null +++ b/@integrate/Component.All/Common.cs @@ -0,0 +1,276 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BridgeConnector.Lib.Process; +using BridgeConnector.Lib.Tools; +using System.IO; +using BridgeConnector.Lib.File; +using BridgeConnector.Lib.XML; +using System.Reflection; + +namespace Component.All +{ + /// + /// Common Functions and Helpers Useful for all Installing activities. + /// + public static class Common + { + #region Public Definitions + + public const string INSTALLED_COMPONENT_CONFIG_XML_FILENAME = "InstalledComponentConfig.xml"; + public const string EMBEDDED_COMPONENT_CONFIG_XML_FILENAME = "EmbeddedComponentConfig.xml"; + + /// + /// Allow Setting of Log by external Assembly + /// + public static Logging Log + { + get + { + return s_Log; + } + set + { + if (value != null) + s_Log = value; + } + } + + #endregion + + #region Private Statics + + private static ISReadWrite s_isrw = null; + private static XSerializer s_serializer = null; + private static ComponentConfig s_EmbeddedComponentConfig = null; + private static ComponentConfig s_InstalledComponentConfig = null; + private static Logging s_Log = null; + + #endregion + + #region Construction + + /// + /// Responsible for reading in embedded and installed configuration + /// + static Common() + { + s_isrw = new ISReadWrite(INSTALLED_COMPONENT_CONFIG_XML_FILENAME); + s_serializer = new XSerializer(); + + //# Read in from Resource (Embedded Components) + s_EmbeddedComponentConfig = s_serializer.ReadFromResource(Assembly.GetExecutingAssembly().GetManifestResourceStream("Component.All." + EMBEDDED_COMPONENT_CONFIG_XML_FILENAME)); + if (s_EmbeddedComponentConfig == null) + throw new Exception("Could not read in EmbeddedComponentConfig"); // should never happen + + //# Read in from IS (Currently Installed Components) + s_InstalledComponentConfig = s_isrw.ReadFromIS(); + if (s_InstalledComponentConfig == null) + s_InstalledComponentConfig = new ComponentConfig(); + } + + #endregion + + #region Public Statics + + /// + /// Retrieve the EmbeddedComponentConfig + /// + /// the EmbeddedComponentConfig + public static ComponentConfig EmbeddedConfig + { + get + { + return s_EmbeddedComponentConfig; + } + } + + /// + /// Retrieve the InstalledComponentConfig + /// + /// the InstalledComponentConfig or null, if not existent + public static ComponentConfig InstalledConfig + { + get + { + return s_InstalledComponentConfig; + } + } + + /// + /// Allows Caller to write out any changes to InstalledConfig back to the File + /// + public static void WriteOutChangesToInstalledConfig() + { + s_isrw.WriteToIS(s_InstalledComponentConfig); + } + + /// + /// Checks to see if any Components are installed. If this returns false, then this is a Fresh Install + /// + /// true, if any components are installed, false otherwise + public static bool AreAnyComponentsInstalled() + { + bool bIsInstalled = (InstalledConfig != null) && (InstalledConfig.BinaryComponents.Components.Length > 0 || InstalledConfig.SetupComponents.Components.Length > 0); + return bIsInstalled; + } + + /// + /// Retrieves the Index for the Component that matches the specified Unique Label + /// + /// label to search components for + /// a component array + /// a value >=0 or -1, if not found + public static int GetIndexForComponentUniqueLabel(string UniqueLabel, ComponentConfig.Component[] components) + { + if (String.IsNullOrEmpty(UniqueLabel) || components == null || components.Length <= 0) + return -1; + + for (int i = 0; i < components.Length; ++i) + { + if (String.Compare(components[i].UniqueLabel, UniqueLabel, true) == 0) + return i; + } + return -1; + } + + /// + /// Spawn a Setup Process (Setup.exe for example) + /// + public static void PSetupSpwan(string SetupFileNameNPath, string param_s) + { + PStarter.StartProcess(PStartInfo.CreateProcess(SetupFileNameNPath, param_s, "", true, System.Diagnostics.ProcessWindowStyle.Hidden, false), true, false); + } + + /// + /// Spawn a MSI Setup Process (*.msi) + /// + public static void PSetupMSIEXEC(string param_s) + { + string msiexec = System.Environment.GetFolderPath(Environment.SpecialFolder.System) + "\\msiexec.exe"; + PStarter.StartProcess(PStartInfo.CreateProcess(msiexec, param_s, "", true, System.Diagnostics.ProcessWindowStyle.Hidden, false), true, false); + } + + /// + /// Run a command on the commandline * Hidden * + /// + /// cmd to run + public static string RunCmdLine(string cmdline) + { + string result = PStarter.RunDosCommand(cmdline); + return result; + } + + /// + /// Use this to get the complete path to a .net framework utility like gacutil.exe or + /// installutil.exe.. any .net framework utility. Will fall back to look in the %temp% folder, + /// if not found, giving the opportunity to deploy the util directly with the components + /// + /// the utility in .net you are looking for like gacutil.exe + /// the full filenameNpath or "", if no existing file found + public static string GetNetFrameworkUtilFileNameNPathFile(string UtilFileName) + { + string windir = System.Environment.GetEnvironmentVariable("windir", EnvironmentVariableTarget.Machine); + string NetFramework1_0 = windir + "\\Microsoft.Net\\Framework\\v1.0.3705"; + string NetFramework1_1 = windir + "\\Microsoft.Net\\Framework\\v1.1.4322"; + string NetFramework2_0 = windir + "\\Microsoft.Net\\Framework\\v2.0.50727"; + string NetFramework3_0 = windir + "\\Microsoft.Net\\Framework\\v3.0"; + string NetFramework3_5 = windir + "\\Microsoft.Net\\Framework\\v3.5"; + string NetFramework4_0 = windir + "\\Microsoft.Net\\Framework\\v4.0.30319"; + string TempPath = PathNaming.PathEndsWithNoSlash(Path.GetTempPath()); // We use this as a Fallback, in case file doesn't exist in the framework + string[] Frameworks = new string[] { NetFramework2_0, NetFramework4_0, NetFramework1_0, NetFramework1_1, NetFramework3_0, NetFramework3_5, TempPath }; + + string NetUtilFileNameNPath = ""; + foreach (string framework in Frameworks) + { + if (File.Exists(framework + "\\" + UtilFileName)) + { + NetUtilFileNameNPath = framework + "\\" + UtilFileName; + return NetUtilFileNameNPath; + } + }; + return NetUtilFileNameNPath; + } + + /// + /// Quick Helper to get the Program Files Path for the specific system + /// + /// Program Files path with a '/' at the end + public static string GetProgramFilesPathOnSystemWithEndSlash() + { + string ProgramFiles = System.Environment.GetEnvironmentVariable("ProgramFiles(x86)"); + if (String.IsNullOrEmpty(ProgramFiles)) + ProgramFiles = System.Environment.GetEnvironmentVariable("ProgramFiles"); + return PathNaming.PathEndsWithSlash(ProgramFiles); + } + + /// + /// + /// + /// + public static string SetOwnership() + { + // in order to do any of this, sadly, we must first install the windows resource kit + //subinacl /subdirectories *.* /setowner=domainname\user + return String.Empty; + } + + /// + /// To grant the specified User or group Full Control permissions to the folder and its contents + /// + /// full path to folder/directory + /// domainname\administrator, any windows user or group + /// + public static bool GrantFullPermissionToFolderForUserOrGroup(string FolderNPath, string UserOrGroup) + { + if (Directory.Exists(FolderNPath)) + { + string command = String.Format("cacls \"{0}\" /t /e /g {1}:f", FolderNPath, UserOrGroup); + if (command.Contains("Invalid arguments.")) + return false; + else + return true; + } + return false; + } + + /// + /// Stop a service + /// + /// name of service to stop + /// true if successful, false otherwise + public static bool StopService(string ServiceName) + { + bool bSuccess = true; + if (ServiceW.DoesServiceExist(ServiceName)) + bSuccess = ServiceW.StopService(ServiceName, true, 120); + return bSuccess; + } + + /// + /// start a service + /// + /// name of a service to start + /// true if successful, false otherwise + public static bool StartService(string ServiceName) + { + bool bSuccess = true; + if (ServiceW.DoesServiceExist(ServiceName)) + bSuccess = ServiceW.StartService(ServiceName, 120); + return bSuccess; + } + + /// + /// Does Service Exist + /// + /// Name of service to check + /// true if successful, false otherwise + public static bool ServiceExists(string ServiceName) + { + return ServiceW.DoesServiceExist(ServiceName); + } + + #endregion + } +} diff --git a/@integrate/Component.All/Common_MediLytec.cs b/@integrate/Component.All/Common_MediLytec.cs new file mode 100644 index 0000000..7e0353b --- /dev/null +++ b/@integrate/Component.All/Common_MediLytec.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Win32; + +namespace Component.All +{ + /// + /// Install Configuration + /// + public enum InstallConfig + { + LytecMD, + MedisoftClinical, + } + + /// + /// Common Functions and Helpers Useful for Lytec, Medisoft Installing activities + /// + public static class Common_MediLytec + { + /// + /// Common/Ofted used #Defs/Definitions associated with Lytec/Medisoft + /// + public static class MediLytecPoundDef + { + public const string BRIDGE_SERVICE_TITLE = "BridgeService"; + public const string BRIDGE_SERVICE_ASSEMBLY = "BridgeService.exe"; + public const string BRIDGE_SERVICE_NAME = "McKesson Bridge Service"; + public const string MIRTH_SERVICE_NAME = "Mirth Connect Service"; + public const string POSTGRE_SERVICE_NAME = "Mirth_Connect_PostgreSQL_Server"; + public const string POSTGRE_SERVER_PORT = "5432"; + } + + /// + /// Retrieve the Configuration (Lytec/Medisoft) from the Registry + /// + /// Lytec/Medisoft + public static InstallConfig RetrieveInstallConfigFromRegistry() + { + bool bIsLytecInstalled = false; + try + { + RegistryKey reg = Registry.LocalMachine.OpenSubKey("Software\\Lytec", false); + bIsLytecInstalled = (reg != null); + if (!bIsLytecInstalled) // also check Wow64 + { + reg = Registry.LocalMachine.OpenSubKey("Software\\Wow6432Node\\Lytec", false); + bIsLytecInstalled = (reg != null); + } + + } + catch (Exception) { /* ignore */ } + + if (bIsLytecInstalled) + return InstallConfig.LytecMD; + else + return InstallConfig.MedisoftClinical; + } + } +} diff --git a/@integrate/Component.All/Component.All.csproj b/@integrate/Component.All/Component.All.csproj new file mode 100644 index 0000000..1b98bcc --- /dev/null +++ b/@integrate/Component.All/Component.All.csproj @@ -0,0 +1,79 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {94CB1BBA-5CF1-4155-827E-1527032D3921} + Library + Properties + Component.All + Component.All + v3.5 + 512 + + + true + full + false + ..\..\Output\Debug\Components\ + DEBUG;TRACE + prompt + 4 + x86 + + + pdbonly + true + ..\..\Output\Release\Components\ + TRACE + prompt + 4 + x86 + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + {9F60FBD1-0DA0-4558-971F-5BA9EE44493A} + BridgeConnectorLib + False + + + + + + + + \ No newline at end of file diff --git a/@integrate/Component.All/ComponentConfig.cs b/@integrate/Component.All/ComponentConfig.cs new file mode 100644 index 0000000..7eb5d1e --- /dev/null +++ b/@integrate/Component.All/ComponentConfig.cs @@ -0,0 +1,223 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Serialization; +using System.Collections; +using BridgeConnector.Lib.Tools; +using System.IO; + +namespace Component.All +{ + /// + /// Serializable Xml Object used to store all Configuration data + /// + [XmlRoot("ComponentConfig", Namespace = "BridgeConnect", IsNullable = false)] + public class ComponentConfig + { + public ComponentW BinaryComponents = null; + public ComponentW SetupComponents = null; + + /// + /// XML Embedded Component Configuration + /// + public ComponentConfig() + { + BinaryComponents = new ComponentW(); + SetupComponents = new ComponentW(); + } + + /// + /// Wrapper class for multiple Components + /// + public class ComponentW + { + private ArrayList m_ArrayList; + public ComponentW() + { + m_ArrayList = new ArrayList(); + } + + [XmlElement("Component")] + public Component[] Components + { + get + { + Component[] components = new Component[m_ArrayList.Count]; + m_ArrayList.CopyTo(components); + return components; + } + set + { + if (value == null) return; + Component[] components = (Component[])value; + m_ArrayList.Clear(); + foreach (Component component in components) + AddUpdateComponent(component.UniqueLabel, component.Version, component.FileName); + } + } + + #region Public Helpers + + /// + /// Call this function to Add/Update a component + /// + /// unique label used to identify a component + /// Version of the component + /// FileName of the component + public void AddUpdateComponent(string UniqueLabel, string Version, string FileName) + { + int nIndex = GetIndexForComponent(UniqueLabel); + if (nIndex != -1) + { + Component component = ((Component)m_ArrayList[nIndex]); + component.UniqueLabel = UniqueLabel; + component.Version = Version; + component.FileName = FileName; + } + else + { + m_ArrayList.Add(new Component(UniqueLabel, Version, FileName)); + } + } + + /// + /// Call this function to remove a component from the list + /// + /// unique label used to identify a component + public void RemoveComponent(string UniqueLabel) + { + int nIndex = GetIndexForComponent(UniqueLabel); + if (nIndex != -1) + m_ArrayList.RemoveAt(nIndex); + } + + /// + /// Checks to see if a component already exists + /// + /// unique name identifying the component + /// true for yes, no otherwise + public bool ComponentExists(string UniqueLabel) + { + return (GetIndexForComponent(UniqueLabel) != -1); + } + + /// + /// Retrieves the component for the specified UniqueLabel + /// + /// unique name identifying the component + /// the Component for the Label, or null if not found + public Component GetComponent(string UniqueLabel) + { + int nIndex = GetIndexForComponent(UniqueLabel); + if (nIndex != -1) + return (Component) m_ArrayList[nIndex]; + else + return null; + } + + #endregion + + #region Internal & Private Helpers + + /// + /// gets the index in the array list for the specified component + /// + /// unique name identifying the component + /// index >= 0 or -1 if not found + private int GetIndexForComponent(string UniqueLabel) + { + for (int i = 0; i < m_ArrayList.Count; ++i) + { + Component component = (Component)m_ArrayList[i]; + if (String.Compare(component.UniqueLabel, UniqueLabel, true) == 0) + return i; + } + return -1; + } + + #endregion + } + + /// + /// specify the Component + /// + public class Component : IComparable + { + public Component() { } + public Component(string UniqueLabel, string Version, string FileName) { this.UniqueLabel = UniqueLabel; this.Version = Version; this.FileName = FileName; } + + [XmlText] + public string FileName = ""; + + /// + /// In case a component has multiple files, seperated by a ';', internally we should always call this + /// + public string[] FileNames + { + get + { + if (!String.IsNullOrEmpty(FileName)) + { + if (FileName.Contains(';')) + return FileName.Split(';'); + else + return new string[] { FileName }; + } + return new string[] { }; + } + } + + [XmlAttribute("UniqueLabel")] + public string UniqueLabel = ""; + + [XmlAttribute("Version")] + public string Version = ""; + + /// + /// In case a component has multiple files, seperated by a ';', internally we should always call this + /// + public string[] TempFileNamesNPath + { + get + { + string[] files = FileNames; + List tFiles = new List(); + if (files != null) + { + string strPath = PathNaming.PathEndsWithSlash(Path.GetTempPath()); + foreach (string file in files) + tFiles.Add(strPath + file); + return tFiles.ToArray(); + } + return new string[] { }; + } + } + + #region IComparable Members + + /// + /// Compares the Components Unique Label and Version + /// + /// + /// + public int CompareTo(object obj) + { + if (obj is Component) + { + Component c = (Component)obj; + int nCompare = String.Compare(this.UniqueLabel, c.UniqueLabel, true); + if (nCompare == 0) + nCompare = String.Compare(this.Version, c.Version, true); + return nCompare; + } + else + { + throw new ArgumentException("object is not a Component"); + } + } + + #endregion + } + } +} diff --git a/@integrate/Component.All/Component_Binary_Manager.cs b/@integrate/Component.All/Component_Binary_Manager.cs new file mode 100644 index 0000000..23b6ac6 --- /dev/null +++ b/@integrate/Component.All/Component_Binary_Manager.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BridgeConnector.Lib.File; +using System.Xml.Serialization; +using System.Collections; +using BridgeConnector.Lib.XML; +using System.Resources; +using System.Reflection; +using System.IO; +using BridgeConnector.Lib.Tools; +using BridgeConnector.Lib.Assembly; + +namespace Component.All +{ + /// + /// Responsible for extracting the Components that are embedded in this dll + /// into the temporary directory for the installer to consume + /// + public class Component_Binary_Manager : IDisposable, IManageComponents + { + #region Private Members + + private bool _disposed = false; + + private Dictionary _componentsLoaded = new Dictionary(); + + #endregion + + #region Construction + + /// + /// Constructor + /// + public Component_Binary_Manager() + { + } + + /// + /// Finalizer + /// + ~Component_Binary_Manager() + { + Dispose(true); + } + + #endregion + + #region IManageComponents Members + + /// + /// Checks if there are Newer Components embedded then that were installed on the system + /// + /// true, if newer components were found, false otherwise + public bool AreNewerComponentsAvailable() + { + //# If nothing is installed, no need to continue + if (GetAllInstalledComponents() == null) + return true; + + // if the lengths don't match, something must get installed + if (GetAllInstalledComponents().Length != GetAllEmbeddedComponents().Length) + return true; + + // # Otherwise, let's determine 1 by 1 + foreach (ComponentConfig.Component component in GetAllEmbeddedComponents()) + { + int nIndex = Common.GetIndexForComponentUniqueLabel(component.UniqueLabel, GetAllInstalledComponents()); + if (nIndex == -1) + return true; + else if(GetAllInstalledComponents()[nIndex].CompareTo(component) != 0) + return true; + } + return false; + } + + /// + /// Returns the Component Definition containing the temporary file (extracted component) + /// for all Newer components found on the system + /// + /// Setup Event Object is passed, for component manager to pass down to it's components + /// a list of newer binary components, or empty list for none + public IInstallComponent[] GetNewerComponents(ref SetupEvents setupEventObj) + { + // # Extract all, or let's determine 1 by 1 and extract + bool bInstalledCompsFound = (GetAllInstalledComponents() != null); + foreach (ComponentConfig.Component component in GetAllEmbeddedComponents()) + { + bool bExtract = true; + if (bInstalledCompsFound) + { + int nIndex = Common.GetIndexForComponentUniqueLabel(component.UniqueLabel, GetAllInstalledComponents()); + if (nIndex != -1) + bExtract = (GetAllInstalledComponents()[nIndex].CompareTo(component) != 0); + } + + // mark component for Installation * Extract to File System * + if (bExtract) + { + if (!ExtractComponentFromResourceToTempFileLocation(component)) + Common.Log.Error(String.Format("Failed to Extract Component {0}", component.UniqueLabel)); + } + } + + List ComponentsToInstall = new List(); + if (_componentsLoaded.Count > 0) + { + foreach(Assembly asm in _componentsLoaded.Values) + { + IInstallComponent installComp = null; + Type[] types = asm.GetTypes(); + foreach (Type t in types) + { + if (t.GetInterface(typeof(IInstallComponent).Name) != null) + { + installComp = (IInstallComponent) asm.CreateInstance(t.FullName); + break; + } + } + + // Check IInstallComponent was found + if (installComp == null) + { + Common.Log.Error(String.Format("Component {0} contains no IInstallComponent Definition.", AssemblyW.GetAssemblyName(asm))); + continue; + } + + // Check if class also implements ISetup + if(installComp is ISetup) + ((ISetup) installComp).ComponentLoaded(ref setupEventObj); + + // Add to Install Components + ComponentsToInstall.Add(installComp); + } + } + return ComponentsToInstall.ToArray(); + } + + /// + /// Retrieves the list of all installed components, in order to uninstall + /// + /// a list of all installed components, null otherwise + public IInstallComponent[] GetAllInstalledComponents(string ComponentsSeperatedbySemiColonOrBlankForAll, ref SetupEvents setupEventObj) + { + // TO DO: + return null; + } + + /// + /// Retrieves the list of all installed components + /// + /// a list of all installed components, null otherwise + public ComponentConfig.Component[] GetAllInstalledComponents() + { + if (Common.InstalledConfig != null && Common.InstalledConfig.BinaryComponents.Components.Length > 0) + return Common.InstalledConfig.BinaryComponents.Components; + return null; + } + + /// + /// Retrieves the list of all installed componentsW + /// + /// a list of all installed componentsW, null otherwise + public ComponentConfig.ComponentW GetAllInstalledComponentsW() + { + return Common.InstalledConfig.BinaryComponents; + } + + /// + /// Retrieves the list of all Embedded components + /// + /// a list of all embedded components, null otherwise + public ComponentConfig.Component[] GetAllEmbeddedComponents() + { + if (Common.EmbeddedConfig != null && Common.EmbeddedConfig.BinaryComponents.Components.Length > 0) + return Common.EmbeddedConfig.BinaryComponents.Components; + return null; + } + + #endregion + + #region Private Helpers + + /// + /// Private Helper to physically extract the bits from the resource and write them to a temporary + /// file location + /// + /// Component, whose files are to be extracted + /// true, if successful, false otherwise + private bool ExtractComponentFromResourceToTempFileLocation(ComponentConfig.Component component) + { + if (component != null) + { + // Extract the component to the Temp Directory, + // if it is not already there... + for (int i = 0; i < component.FileNames.Length; ++i) + { + // First try loading the assembly + string asmName = "Component.Binary." + component.UniqueLabel; + Assembly asm = Assembly.Load(asmName); + if (asm == null) + return false; + else + _componentsLoaded[component.UniqueLabel] = asm; // <- imp + string FileName = component.FileNames[i]; + string TempFileNameNPath = component.TempFileNamesNPath[i]; + if (!File.Exists(TempFileNameNPath)) + { + using (BinaryReader br = new BinaryReader(asm.GetManifestResourceStream(asmName + "." + FileName))) + using (BinaryWriter bw = new BinaryWriter(new FileStream(TempFileNameNPath, FileMode.Create))) + { + byte[] buffer = new byte[64 * 1024]; + int numread = br.Read(buffer, 0, buffer.Length); + while (numread > 0) + { + bw.Write(buffer, 0, numread); + numread = br.Read(buffer, 0, buffer.Length); + } + bw.Flush(); + } + } + } + return true; + } + return false; + } + + #endregion + + #region IDisposable Members + + /// + /// Dispose of all extracted files + /// + public void Dispose() + { + Dispose(true); + + // Use SupressFinalize in case a subclass + // of this type implements a finalizer + GC.SuppressFinalize(this); + } + + /// + /// Make sure to conserve disk space, to delete all files that were extracted + /// by this program + /// + /// if true, delete all files extracted by this dll + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + // When Disposing, Delete all Temporary Files on the System that may exist + foreach (ComponentConfig.Component component in GetAllEmbeddedComponents()) + { + foreach (string tFileNameNPath in component.TempFileNamesNPath) + { + if (File.Exists(tFileNameNPath)) + File.Delete(tFileNameNPath); + } + } + + _componentsLoaded.Clear(); + _componentsLoaded = null; + } + + // Indicate that the instance has been disposed. + _disposed = true; + } + } + + #endregion + } +} diff --git a/@integrate/Component.All/Component_Setup_Manager.cs b/@integrate/Component.All/Component_Setup_Manager.cs new file mode 100644 index 0000000..3ae9ab6 --- /dev/null +++ b/@integrate/Component.All/Component_Setup_Manager.cs @@ -0,0 +1,213 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using BridgeConnector.Lib.Assembly; + +namespace Component.All +{ + /// + /// Responsible for working with setup components which don't require any special treatment + /// + public class Component_Setup_Manager : IDisposable, IManageComponents + { + #region Private Members + + private bool _disposed = false; + + private Dictionary _componentsLoaded = new Dictionary(); + + #endregion + + #region Construction + + /// + /// Constructor + /// + public Component_Setup_Manager() + { + } + + /// + /// Finalizer + /// + ~Component_Setup_Manager() + { + Dispose(true); + } + + #endregion + + #region IManageComponents Members + + /// + /// Checks if there are Newer Components embedded then that were installed on the system + /// + /// true, if newer components were found, false otherwise + public bool AreNewerComponentsAvailable() + { + //# If nothing is installed, no need to continue + if (GetAllInstalledComponents() == null) + return true; + + // if the lengths don't match, something must get installed + if (GetAllInstalledComponents().Length != GetAllEmbeddedComponents().Length) + return true; + + // # Otherwise, let's determine 1 by 1 + foreach (ComponentConfig.Component component in GetAllEmbeddedComponents()) + { + int nIndex = Common.GetIndexForComponentUniqueLabel(component.UniqueLabel, GetAllInstalledComponents()); + if (nIndex == -1) + return true; + else if (GetAllInstalledComponents()[nIndex].CompareTo(component) != 0) + return true; + } + return false; + } + + /// + /// Returns the Component Definitions for all Newer components found on the system + /// + /// a list of newer setup components, or empty list for none + public IInstallComponent[] GetNewerComponents(ref SetupEvents setupEventObj) + { + // # Extract all, or let's determine 1 by 1 and extract + bool bInstalledCompsFound = (GetAllInstalledComponents() != null); + foreach (ComponentConfig.Component component in GetAllEmbeddedComponents()) + { + bool bInstall = true; + if (bInstalledCompsFound) + { + int nIndex = Common.GetIndexForComponentUniqueLabel(component.UniqueLabel, GetAllInstalledComponents()); + if (nIndex != -1) + bInstall = (GetAllInstalledComponents()[nIndex].CompareTo(component) != 0); + } + + // mark component for Installation + if (bInstall) + { + string asmName = "Component.Setup." + component.UniqueLabel; + _componentsLoaded[component.UniqueLabel] = Assembly.Load(asmName); + } + } + + List ComponentsToInstall = new List(); + if (_componentsLoaded.Count > 0) + { + foreach (Assembly asm in _componentsLoaded.Values) + { + IInstallComponent installComp = null; + Type[] types = asm.GetTypes(); + foreach (Type t in types) + { + if (t.GetInterface(typeof(IInstallComponent).Name) != null) + { + installComp = (IInstallComponent)asm.CreateInstance(t.FullName); + break; + } + } + + // Check IInstallComponent was found + if (installComp == null) + { + Common.Log.Error(String.Format("Component {0} contains no IInstallComponent Definition.", AssemblyW.GetAssemblyName(asm))); + continue; + } + + // Check if class also implements ISetup + if (installComp is ISetup) + ((ISetup)installComp).ComponentLoaded(ref setupEventObj); + + // Add to Install Components + ComponentsToInstall.Add(installComp); + } + } + return ComponentsToInstall.ToArray(); + } + + /// + /// Retrieves the list of all installed components, in order to uninstall + /// + /// a list of all installed components, null otherwise + public IInstallComponent[] GetAllInstalledComponents(string ComponentsSeperatedbySemiColonOrBlankForAll, ref SetupEvents setupEventObj) + { + // TO DO: + + //string[] ComponentsUniqueLabels = null; + //if (!String.IsNullOrEmpty(ComponentsSeperatedbySemiColonOrBlankForAll)) + // ComponentsUniqueLabels = ComponentsSeperatedbySemiColonOrBlankForAll.Split(';'); + return null; + } + + /// + /// Retrieves the list of all installed components + /// + /// a list of all installed components, null otherwise + public ComponentConfig.Component[] GetAllInstalledComponents() + { + if (Common.InstalledConfig != null && Common.InstalledConfig.SetupComponents.Components.Length > 0) + return Common.InstalledConfig.SetupComponents.Components; + return null; + } + + /// + /// Retrieves the list of all installed componentsW + /// + /// a list of all installed componentsW, null otherwise + public ComponentConfig.ComponentW GetAllInstalledComponentsW() + { + return Common.InstalledConfig.SetupComponents; + } + + /// + /// Retrieves the list of all Embedded components + /// + /// a list of all embedded components, null otherwise + public ComponentConfig.Component[] GetAllEmbeddedComponents() + { + if (Common.EmbeddedConfig != null && Common.EmbeddedConfig.SetupComponents.Components.Length > 0) + return Common.EmbeddedConfig.SetupComponents.Components; + return null; + } + + #endregion + + #region IDisposable Members + + /// + /// Dispose of all extracted files + /// + public void Dispose() + { + Dispose(true); + + // Use SupressFinalize in case a subclass + // of this type implements a finalizer + GC.SuppressFinalize(this); + } + + /// + /// Make sure to conserve disk space, to delete all files that were extracted + /// by this program + /// + /// if true, delete all files extracted by this dll + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _componentsLoaded.Clear(); + _componentsLoaded = null; + } + + // Indicate that the instance has been disposed. + _disposed = true; + } + } + + #endregion + } +} diff --git a/@integrate/Component.All/EmbeddedComponentConfig.xml b/@integrate/Component.All/EmbeddedComponentConfig.xml new file mode 100644 index 0000000..2dceded --- /dev/null +++ b/@integrate/Component.All/EmbeddedComponentConfig.xml @@ -0,0 +1,13 @@ + + + + jre-6u27-windows-i586-s.exe + postgresql-9.1.1-1-windows.exe + pgadmin3-1.14.0.msi + Devart.Data.dll;Devart.Data.PostgreSql.dll;Devart.Data.PostgreSql.xml;gacutil.exe;gacutil.exe.config + mirthconnect-2.1.1.5490.b781-windows.exe + + + BridgeService.exe + + \ No newline at end of file diff --git a/@integrate/Component.All/GenericInstaller.cs b/@integrate/Component.All/GenericInstaller.cs new file mode 100644 index 0000000..65ec7ad --- /dev/null +++ b/@integrate/Component.All/GenericInstaller.cs @@ -0,0 +1,328 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using BridgeConnector.Lib.Thread; + +namespace Component.All +{ + /// + /// Calls GenericInstaller in order to perform an install/uninstall + /// * Does so in a threaded fashion * + /// + public class GenericInstall + { + // Let the Caller know that a) the thread Completed b) it did so successfully + public static bool s_PerformInstallCompleted = false; + public static bool s_PerformInstallCompletedSuccessfully = false; + + // Let the Caller know that a) the thread Completed b) it did so successfully + public static bool s_PerformUninstallCompleted = false; + public static bool s_PerformUninstallCompletedSuccessfully = false; + + // Private ComponentMgmtObjects + private static GenericInstaller s_binaryInstaller = null; + private static GenericInstaller s_setupInstaller = null; + private static SetupEvents s_SetupEvents = new SetupEvents(); + + /// + /// Perform a Threaded Install + /// + public static void PerformInstall() + { + TStarter.ThreadMethod tMethod = delegate() + { + s_PerformInstallCompleted = false; + s_PerformInstallCompletedSuccessfully = false; + bool bBinaryInstallNeeded = false; + bool bSetupInstallNeeded = false; + + // # Let's see if we need to update/install Binary Components + if(s_binaryInstaller == null) + s_binaryInstaller = new GenericInstaller(); + bBinaryInstallNeeded = s_binaryInstaller.IsInstallNeeded(ref s_SetupEvents); + + // # Let's see if we need to update/install Setup Components + if(s_setupInstaller == null) + s_setupInstaller = new GenericInstaller(); + bSetupInstallNeeded = s_setupInstaller.IsInstallNeeded(ref s_SetupEvents); + + // # Let's Install, Baby... + if (bBinaryInstallNeeded || bSetupInstallNeeded) + { + // # Trigger Start Install Event + s_SetupEvents.Raise_Before_Install_Event(); + + bool bBinarySuccess = true; + bool bSetupbSuccess = true; + + // update/install Binary Components + if (bBinaryInstallNeeded) + bBinarySuccess = s_binaryInstaller.Install(); + + // update/install Setup Components + if (bSetupInstallNeeded) + bSetupbSuccess = s_setupInstaller.Install(); + + // # Trigger End Install Event + if (bBinaryInstallNeeded || bSetupInstallNeeded) + s_SetupEvents.Raise_After_Install_Event(); + + // # We fail if one of them fails + s_PerformInstallCompletedSuccessfully = bBinarySuccess && bSetupbSuccess; + } + + // # Let External Callers know that this Thread Completed + s_PerformInstallCompleted = true; + }; + TStarter.StartThread(tMethod, "PerformInstall", System.Threading.ApartmentState.MTA, false, System.Threading.ThreadPriority.Normal); + } + + /// + /// Perform a Threaded Uninstall + /// + public static void PerformUninstall(string ComponentsSeperatedbySemiColonOrBlankForAll) + { + s_PerformUninstallCompleted = false; + s_PerformUninstallCompletedSuccessfully = false; + } + + /// + /// Call this function to let every object know that either PerformInstall or PerformUninstall was called, + /// the Gui then may have made some other modifications, and we are now completed + /// + public static void SetupMainCompleted() + { + // Notify all components that Setup is now complete + s_SetupEvents.Raise_Ending_Setup_Event(); + + // # Imp! - We must call dispose on our Setup Objects + if (s_binaryInstaller != null) + { + s_binaryInstaller.Dispose(); + s_binaryInstaller = null; + } + if (s_setupInstaller != null) + { + s_setupInstaller.Dispose(); + s_setupInstaller = null; + } + } + } + + /// + /// Generic Install Class used for IManageComponents + /// + /// IManageComponents responsible for Installing + public class GenericInstaller : IDisposable where T : IManageComponents, IDisposable, new() + { + #region Private Members + + private T _ComponentMGR = default(T); + private IInstallComponent[] _ComponentsToPerformWorkOn = null; + private bool _disposed = false; + + #endregion + + #region Construction + + /// + /// Construct the T Object + /// + public GenericInstaller() + { + _ComponentMGR = new T(); + } + + /// + /// Finalizer + /// + ~GenericInstaller() + { + Dispose(true); + } + + #endregion + + #region Public Methods + + /// + /// + /// + /// + /// + public bool IsInstallNeeded(ref SetupEvents setupEventObj) + { + if (_ComponentMGR.AreNewerComponentsAvailable()) + { + Common.Log.Info(String.Format("Newer Components available for Type {0}.", _ComponentMGR.GetType().Name)); + _ComponentsToPerformWorkOn = _ComponentMGR.GetNewerComponents(ref setupEventObj); + if (_ComponentsToPerformWorkOn != null && _ComponentsToPerformWorkOn.Length > 0) + { + Common.Log.Info(String.Format("Found {0} Newer Components.", _ComponentsToPerformWorkOn.Length)); + return true; + } + } + + Common.Log.Info(String.Format("No Newer Components available for Type {0}.", _ComponentMGR.GetType().Name)); + return false; + } + + /// + /// Perform Install on the IManageComponents specified when Constructing the GenericInstaller Object + /// + /// true if no error occurs, false otherwise + public bool Install() + { + bool bErrorsOccured = false; + List successfullyInstalledComponents = new List(); + if (_ComponentsToPerformWorkOn != null && _ComponentsToPerformWorkOn.Length > 0) + { + // # Start Install + foreach (IInstallComponent installComp in _ComponentsToPerformWorkOn) + { + ComponentConfig.Component component = installComp.GetComponent(); + if (installComp.BEFORE_INSTALLING_COMPONENT()) + { + Common.Log.Info(String.Format("Installing Component {0} with Version {1}.", component.UniqueLabel, component.Version)); + bool bInstallSuccess = installComp.INSTALL_COMPONENT(); + bInstallSuccess = bInstallSuccess && installComp.AFTER_INSTALLING_COMPONENT(); + if (bInstallSuccess) + { + Common.Log.Info(String.Format("Component {0} with version {1} was correctly installed.", component.UniqueLabel, component.Version)); + successfullyInstalledComponents.Add(component); + } + else + { + bErrorsOccured = true; + string msg = String.Format("Component {0} with version {1} was not correctly installed.", component.UniqueLabel, component.Version); + Common.Log.Error(msg); + } + } + } + // # End Install + + // Add any installed components to the Installed Configuration + foreach (ComponentConfig.Component component in successfullyInstalledComponents) + _ComponentMGR.GetAllInstalledComponentsW().AddUpdateComponent(component.UniqueLabel, component.Version, component.FileName); + + // Write out the installed Configuration + Common.WriteOutChangesToInstalledConfig(); + } + + Common.Log.Info(String.Format("Exiting Install() for Type {0} with bErrorsOccured set to = {1}", _ComponentMGR.GetType().Name, bErrorsOccured)); + return !bErrorsOccured; + } + + /// + /// + /// + /// + /// + public bool IsUninstallNeeded(ref SetupEvents setupEventObj, string ComponentsSeperatedbySemiColonOrBlankForAll) + { + if(_ComponentMGR.GetAllInstalledComponents() != null) + { + Common.Log.Info(String.Format("Installed Components available for Type {0}.", _ComponentMGR.GetType().Name)); + _ComponentsToPerformWorkOn = _ComponentMGR.GetAllInstalledComponents(ComponentsSeperatedbySemiColonOrBlankForAll, ref setupEventObj); + if (_ComponentsToPerformWorkOn != null && _ComponentsToPerformWorkOn.Length > 0) + { + Common.Log.Info(String.Format("Found {0} Components to Uninstall.", _ComponentsToPerformWorkOn.Length)); + return true; + } + } + + Common.Log.Info(String.Format("No Installed Components to Uninstall for Type {0}.", _ComponentMGR.GetType().Name)); + return false; + } + + /// + /// Perform Uninstall on the IManageComponents specified when Constructing the GenericInstaller Object + /// + /// coma-seperated Unique Labels of components to uninstall, blank for all + /// true if no error occurs, false otherwise + public bool Uninstall(ref SetupEvents setupEventObj, string ComponentsSeperatedbySemiColonOrBlankForAll) + { + bool bErrorsOccured = false; + List successfullyUninstalledComponents = new List(); + if (_ComponentsToPerformWorkOn != null && _ComponentsToPerformWorkOn.Length > 0) + { + // # Start Uninstall + foreach (IInstallComponent uninstallComp in _ComponentsToPerformWorkOn) + { + if (!uninstallComp.SUPPORTS_UNINSTALL()) // Not all components support uninstall * although they should, we should allow for this contigency + continue; + + ComponentConfig.Component component = uninstallComp.GetComponent(); + if (uninstallComp.BEFORE_UNINSTALLING_COMPONENT()) + { + Common.Log.Info(String.Format("About to Uninstall Component {0} with Version {1}.", component.UniqueLabel, component.Version)); + bool bUninstallSuccess = uninstallComp.UNINSTALL_COMPONENT(); + if (!bUninstallSuccess) + bErrorsOccured = true; + + bUninstallSuccess = bUninstallSuccess && uninstallComp.AFTER_UNINSTALLING_COMPONENT(); + if (bUninstallSuccess) + { + Common.Log.Info(String.Format("Component {0} with version {1} was uninstalled.", component.UniqueLabel, component.Version)); + successfullyUninstalledComponents.Add(component); + } + else + { + string msg = String.Format("Component {0} with version {1} was not uninstalled.", component.UniqueLabel, component.Version); + Common.Log.Error(msg); + } + } + } + // # End Uninstall + + // Remove any uninstalled components from the Installed Configuration + foreach (ComponentConfig.Component component in successfullyUninstalledComponents) + _ComponentMGR.GetAllInstalledComponentsW().RemoveComponent(component.UniqueLabel); + + // Write out the installed Configuration + Common.WriteOutChangesToInstalledConfig(); + } + + Common.Log.Info(String.Format("Exiting Uninstall() for Type {0} with bErrorsOccured set to = {1}", _ComponentMGR.GetType().Name, bErrorsOccured)); + return !bErrorsOccured; + } + + #endregion + + #region IDisposable Members + + /// + /// Dispose of all extracted files + /// + public void Dispose() + { + Dispose(true); + + // Use SupressFinalize in case a subclass + // of this type implements a finalizer + GC.SuppressFinalize(this); + } + + /// + /// Make sure to call Dispose on the ComponentMGR for it to perform any cleanup + /// + /// if true, dispose ComponentMGR + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _ComponentMGR.Dispose(); + _ComponentMGR = default(T); + } + + // Indicate that the instance has been disposed. + _disposed = true; + } + } + + #endregion + } +} diff --git a/@integrate/Component.All/IInstallComponent.cs b/@integrate/Component.All/IInstallComponent.cs new file mode 100644 index 0000000..689aa24 --- /dev/null +++ b/@integrate/Component.All/IInstallComponent.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Component.All +{ + /// + /// All Components that install themselves must support this interface + /// + public interface IInstallComponent + { + /// + /// Get the Corresponding Component for this IInstallComponent + /// + /// the component for this IInstallCompoent + ComponentConfig.Component GetComponent(); + + /// + /// Used to determine if the component is needed to install + /// + /// true to continue to Install, false otherwise + bool BEFORE_INSTALLING_COMPONENT(); + + /// + /// Used to install a component + /// + /// true, if install was successful, false otherwise + bool INSTALL_COMPONENT(); + + /// + /// Used to do any validation after install occured + /// + /// true, if validation was successful, false otherwise + bool AFTER_INSTALLING_COMPONENT(); + + /// + /// Used to determine if the component supports uninstalling + /// + /// true, if component supports uninstalling, false otherwise + bool SUPPORTS_UNINSTALL(); + + /// + /// Used to determine if the component is needed to uninstall + /// + /// true to continue to uninstall, false otherwise + bool BEFORE_UNINSTALLING_COMPONENT(); + + /// + /// Used to uninstall a component + /// + /// true, if uninstall was successful, false otherwise + bool UNINSTALL_COMPONENT(); + + /// + /// Used to do any validation after uninstall occured + /// + /// true, if validation was successful, false otherwise + bool AFTER_UNINSTALLING_COMPONENT(); + } +} diff --git a/@integrate/Component.All/IManageComponents.cs b/@integrate/Component.All/IManageComponents.cs new file mode 100644 index 0000000..e8b57dc --- /dev/null +++ b/@integrate/Component.All/IManageComponents.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Component.All +{ + /// + /// Used by the Binary and Setup Component Manager, + /// could possible be reused later for a new Component Manager + /// + public interface IManageComponents + { + /// + /// Quick Check to see if newer Components are available + /// + /// true, if newer components are available, false otherwise + bool AreNewerComponentsAvailable(); + + /// + /// Retrieves the list of newer components that need to be installed + /// + /// Setup Event Object is passed, for component manager to pass down to it's components + /// a list of newer components to install, null otherwise + IInstallComponent[] GetNewerComponents(ref SetupEvents setupEventObj); + + /// + /// Retrieves the list of all installed components, in order to uninstall + /// + /// Specify Component Filter + /// Setup Event Object is passed, for component manager to pass down to it's components + /// a list of all installed components, null otherwise + IInstallComponent[] GetAllInstalledComponents(string ComponentsSeperatedbySemiColonOrBlankForAll, ref SetupEvents setupEventObj); + + /// + /// Retrieves the list of all installed components + /// + /// a list of all installed components, null otherwise + ComponentConfig.Component[] GetAllInstalledComponents(); + + /// + /// Retrieves the list of all installed componentsW + /// + /// a list of all installed componentsW, null otherwise + ComponentConfig.ComponentW GetAllInstalledComponentsW(); + + /// + /// Retrieves the list of all Embedded components + /// + /// a list of all embedded components, null otherwise + ComponentConfig.Component[] GetAllEmbeddedComponents(); + } +} diff --git a/@integrate/Component.All/ISetup.cs b/@integrate/Component.All/ISetup.cs new file mode 100644 index 0000000..e9c86bc --- /dev/null +++ b/@integrate/Component.All/ISetup.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Component.All +{ + /// + /// Class is passed into ISetup ComponentLoaded() giving each component + /// a chance to subscribe to it + /// + public class SetupEvents + { + #region Events + + /// + /// Delegate used to handle Setup Events + /// + public delegate void SetupEvent(); + + /// + /// Called after the entire Setup Application ends + /// + public event SetupEvent Ending_Setup; + + /// + /// Called before the entire Install Process Begins + /// + public event SetupEvent Before_Install; + + /// + /// Called after the entire Install Process ends + /// + public event SetupEvent After_Install; + + /// + /// Called before the entire Uninstall Process Begins + /// + public event SetupEvent Before_Uninstall; + + /// + /// Called after the entire Uninstall Process ends + /// + public event SetupEvent After_Uninstall; + + #endregion + + # region Event Raisers + + /// + /// Raise the Ending Setup Event + /// + public void Raise_Ending_Setup_Event() + { + if (Ending_Setup != null) + Ending_Setup(); + } + + /// + /// Raise the Before Install Event + /// + public void Raise_Before_Install_Event() + { + if (Before_Install != null) + Before_Install(); + } + + /// + /// Raise the After Install Event + /// + public void Raise_After_Install_Event() + { + if (After_Install != null) + After_Install(); + } + + /// + /// Raise the Before Uninstall Event + /// + public void Raise_Before_Uninstall_Event() + { + if (Before_Uninstall != null) + Before_Uninstall(); + } + + /// + /// Raise the After Uninstall Event + /// + public void Raise_After_Uninstall_Event() + { + if (After_Uninstall != null) + After_Uninstall(); + } + + #endregion + } + + /// + /// All Components that install themselves will implement this interface, if they are interested to + /// listen to / Handle Setup Events + /// + public interface ISetup + { + /// + /// Called when the Component is loaded. Components are only loaded + /// when an Install on them is iminent. The component has a chance to + /// listen to/handle events in the setup process. + /// + void ComponentLoaded(ref SetupEvents subscribeToDesiredEvents); + } + +} diff --git a/@integrate/Component.All/Properties/AssemblyInfo.cs b/@integrate/Component.All/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..580d301 --- /dev/null +++ b/@integrate/Component.All/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Components")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Components")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ae8a648a-90cf-48ce-9b71-6e6b4cc417aa")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/@integrate/HiddenMainWindow.xaml b/@integrate/HiddenMainWindow.xaml new file mode 100644 index 0000000..270af8b --- /dev/null +++ b/@integrate/HiddenMainWindow.xaml @@ -0,0 +1,7 @@ + + + + diff --git a/@integrate/HiddenMainWindow.xaml.cs b/@integrate/HiddenMainWindow.xaml.cs new file mode 100644 index 0000000..b099576 --- /dev/null +++ b/@integrate/HiddenMainWindow.xaml.cs @@ -0,0 +1,1456 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using WatchdogLib.WPF; +using WatchdogLib.Thread; +using System.Timers; +using Watchdog.WatchdogLib.Process; +using System.Diagnostics; +using WatchdogLib.File; +using WatchdogLib.Tools; +using Watchdog.WatchdogLib.File; +using Watchdog.WatchdogLib.Registry; +using Watchdog.WatchdogLib.Assembly; +using System.Windows.Threading; +using Watchdog.WatchdogLib.Other; +using Watchdog.WPFWindows; +using Watchdog.WatchdogLib.Monitor; +using Watchdog.WatchdogLib.Net; +using Watchdog.WatchdogLib.Win32; + +namespace Watchdog +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class HiddenMainWindow : Window + { + #region Private Statics + + // Core Timers * And HiddenWindow Var * + private static KeepHidden _HiddenWindow = null; + private static TTimer _timer = new TTimer(TTimer_ElapsedEventHandler, 1523, false, false); // Kicks in every ~1.5 Seconds + private static TTimer _timerScheduler = new TTimer(TTimer_ElapsedEventHandlerScheduler, 61491, false, false); // Kicks in every ~1 Minute + private static TTimer _timerSchedulerBackup = new TTimer(TTimer_ElapsedEventHandlerSchedulerBackup, 99986, false, false); // Kicks in every 1.665 Minutes (uneven so timers have the highest chance of not overlapping) + + // In case an unexpected process shutdown occurs, let's try to do more clean-up + private static bool s_WindowCloseGotCalled = false; + + // to makes sure we have a clean shut down * Prev State Tracking * + private static bool s_bIsTimerRunning = false; + private static bool s_bIsSchedulerTimerRunning = false; + private static bool s_bIsSchedulerBackupTimerRunning = false; + + // Start/Stop Timers *While shuting down _timer can be null* calling These functions and ignore any errors, + // Functions return the * previous state before calling * so if the previous state was 'Off' then it returns false, previous state was 'On' it returns true; + private static bool StartTimer() { lock (_HiddenWindow) { bool bPrevState = s_bIsTimerRunning; try { _timer.Start(); s_bIsTimerRunning = true; } catch (Exception) { /* ignore */ } return bPrevState; } } + private static bool StopTimer() { lock (_HiddenWindow) { bool bPrevState = s_bIsTimerRunning; try { _timer.Stop(); s_bIsTimerRunning = false; } catch (Exception) { /* ignore */ } return bPrevState; } } + private static bool StartSchedulerTimer() { lock (_HiddenWindow) { bool bPrevState = s_bIsSchedulerTimerRunning; try { _timerScheduler.Start(); s_bIsSchedulerTimerRunning = true; } catch (Exception) { /* ignore */ } return bPrevState; } } + private static bool StopSchedulerTimer() { lock (_HiddenWindow) { bool bPrevState = s_bIsSchedulerTimerRunning; try { _timerScheduler.Stop(); s_bIsSchedulerTimerRunning = false; } catch (Exception) { /* ignore */ } return bPrevState; } } + //private static bool StartSchedulerBackupTimer(uint IntervalInMiliseconds = 99986) { lock (_HiddenWindow) { bool bPrevState = s_bIsSchedulerBackupTimerRunning; try { _timerSchedulerBackup.Start(IntervalInMiliseconds); s_bIsSchedulerBackupTimerRunning = true; } catch (Exception) { /* ignore */ } return bPrevState; } } + private static bool StartSchedulerBackupTimer(uint IntervalInMiliseconds) { lock (_HiddenWindow) { bool bPrevState = s_bIsSchedulerBackupTimerRunning; try { _timerSchedulerBackup.Start(IntervalInMiliseconds); s_bIsSchedulerBackupTimerRunning = true; } catch (Exception) { /* ignore */ } return bPrevState; } } + private static bool StopSchedulerBackupTimer() { lock (_HiddenWindow) { bool bPrevState = s_bIsSchedulerBackupTimerRunning; try { _timerSchedulerBackup.Stop(); s_bIsSchedulerBackupTimerRunning = false; } catch (Exception) { /* ignore */ } return bPrevState; } } + + #endregion + + #region Internal Statics * (Monitor API) * + + /// + /// Use this to Start the Process Monitor * Also Called by Scheduler and Scheduler Backup * + /// + /// true to force a one-time refresh of XML DS Data, false otherwise + /// true to interact with desktop, false otherwise + //internal static void StartMonitoring(bool bReloadConfigurationData = false, bool bShowUserPopup = true) + internal static void StartMonitoring(bool bReloadConfigurationData, bool bShowUserPopup) + { + // If the Monitor was Paused Prior? - restart the Timers + if (App.State_IsMonitorPaused()) + { + lock (_HiddenWindow) + { + // Set to Running + App.State_MonitorStarted(); + + // Re-start Timers + StartTimer(); + StartScheduler(); + StartSchedulerBackup(99986); + + // Log and Inform the User + App.log.Info(AppResx.GetStringFormated("MESSAGE_MONITOR_STARTED_FROM_PAUSE", App.APPLICATION_NAME_SHORT, false)); + if(bShowUserPopup && !App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_START_SUCCESS", App.APPLICATION_NAME_SHORT, App.APP_XML_DS_FILENAME)); + + // * Set the System Tray Icon According to Running State * + if(!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.SetSysTrayIconAccordingToRunningState(App.State_GetSystemState()); + return; + } + } + + // Is the Monitor already running? + if (App.State_MonitorIsInRunningState()) + return; + + // Set to Running + App.State_MonitorStarted(); + + // Let's get Started + lock (_HiddenWindow) + { + // Reset the Last Scheduler Run Time + if (!App.State_IsSchedulerExecuting()) + App.setting.LastSchedulerRun = DateTime.MinValue; + + // Reset the Last Scheduler Backup Run Time + if (!App.State_IsSchedulerBackupExecuting()) + App.setting.LastSchedulerBackupRun = DateTime.MinValue; + + // Reset Error State * But Only if we are NOT being Called by the Scheduler * + if (!App.State_IsSchedulerExecuting()) + App.State_Error_ResetErrorState(); + + // Log Start of monitoring + App.log.Info(AppResx.GetStringFormated("MESSAGE_MONITOR_STARTED", App.APPLICATION_NAME_SHORT, bReloadConfigurationData ? "True" : "False")); + + // * Set the System Tray Icon According to Running State * + if(!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.SetSysTrayIconAccordingToRunningState(App.State_GetSystemState()); + + // Popup Std Start Monitoring Message + if (bShowUserPopup && !App.State_SpecialMode_IsRunAsServiceSet()) + { + if (App.State_IsSchedulerExecuting()) + WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_STARTSTOP", App.APPLICATION_NAME_SHORT, App.APP_XML_DS_FILENAME)); + else if (App.State_IsSchedulerBackupExecuting()) + WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_BACKUP_STARTSTOP_DISPLAY2", App.APPLICATION_NAME_SHORT)); + else + WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_START_SUCCESS", App.APPLICATION_NAME_SHORT, App.APP_XML_DS_FILENAME)); + } + + // Reload Configuration, if neccessary + App.xmlfile.ForceRefreshOnNext_ReadData = bReloadConfigurationData; + if (!bReloadConfigurationData) + { + // make sure that at least all the process/service info is cleared + App.xmlfile.ReadData(false).ClearAllStartNFailTimes(); + // also clear PID information + App.xmlfile.ReadData(false).MonitoredProcesses.ClearPidInformation(); + } + + // Let the Schedulers Handle the starting of the Timers, + // * don't start anything if it is already executing - concurrency * + if (!App.State_IsSchedulerExecuting() && !App.State_IsSchedulerBackupExecuting()) + { + if(App.setting.SchedulerSettingsExist || App.setting.SchedulerBackupSettingsExist) + { + // When we StartMonitoring, we should delay start the Scheduler, + // to give the timer a change to start all processes. 10 Seconds should do. + ElapsedEventHandler timedStart = delegate(object sender, ElapsedEventArgs e) + { + Timer timer = (Timer)sender; + timer.Stop(); + StartScheduler(); // * Start the Scheduler, if it is set * + StartSchedulerBackup(99986); // * Start the Scheduler Backup, if it is set * + timer.Dispose(); + timer = null; + }; + TTimer delayStartScheduler = new TTimer(timedStart, 5000, true, true); + } + } + + // Allow Scheduler to manage this timer + if(!App.State_IsSchedulerExecuting()) + StartTimer(); + } + } + + /// + /// Pause the Monitor * Also Called by Scheduler Backup * + /// + /// true to interact with desktop, false otherwise + //internal static void PauseMonitoring(bool bShowUserPopup = true) + internal static void PauseMonitoring(bool bShowUserPopup) + { + if (App.State_IsMonitorPaused() || !App.State_MonitorIsInRunningState()) + return; + + App.log.Info("PauseMonitoring Called"); + + lock (_HiddenWindow) + { + // Stop Both Timers, inside a Lock, + // should do the trick * Neither should execute * + StopTimer(); + StopScheduler(); + if (!App.State_IsSchedulerBackupExecuting()) + StopSchedulerBackup(); + + // Mark the System as a Paused State + App.State_MonitorPaused(); + + // Log Pause of monitoring + App.log.Info(AppResx.GetStringFormated("MESSAGE_MONITOR_PAUSED", App.APPLICATION_NAME_SHORT)); + if (bShowUserPopup && !App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_MONITOR_PAUSED", App.APPLICATION_NAME_SHORT)); + + // * Set the System Tray Icon According to Current State * + if(!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.SetSysTrayIconAccordingToRunningState(App.State_GetSystemState()); + } + } + + /// + /// Restarts the Monitor * Also Called by Scheduler * + /// + /// Time in seconds to wait before starting (after stopping) + /// true to interact with desktop, false otherwise + //internal static void RestartMonitoring(uint nWaitBeforeStartSeconds = 4, bool bShowUserPopup = true) + internal static void RestartMonitoring(uint nWaitBeforeStartSeconds, bool bShowUserPopup) + { + App.log.Info("RestartMonitoring Called"); + + // Stop the Monitor + StopMonitoring(bShowUserPopup, false); + + // Sleep, for a little while,... + System.Threading.Thread.Sleep(TimeSpan.FromSeconds(nWaitBeforeStartSeconds)); + + // Now Start again... + StartMonitoring(!App.State_IsSchedulerExecuting(), bShowUserPopup); + } + + /// + /// Restarts the Monitor, including all services + /// + /// Time in seconds to wait before starting (after stopping) + /// true to interact with desktop, false otherwise + //internal static void RestartAllMonitoring(uint nWaitBeforeStartSeconds = 4, bool bShowUserPopup = true) + internal static void RestartAllMonitoring(uint nWaitBeforeStartSeconds, bool bShowUserPopup) + { + App.log.Info("RestartAllMonitoring Called"); + + // Stop the Monitor + StopMonitoring(bShowUserPopup, true); + + // Sleep, for a little while,... + System.Threading.Thread.Sleep(TimeSpan.FromSeconds(nWaitBeforeStartSeconds)); + + // Now Start again... + StartMonitoring(!App.State_IsSchedulerExecuting(), bShowUserPopup); + } + + /// + /// Stops Monitoring, including all Services + /// + /// true to interact with desktop, false otherwise + //internal static void StopAllMonitoring(bool bShowUserPopup = true) + internal static void StopAllMonitoring(bool bShowUserPopup) + { + App.log.Info("StopAllMonitoring Called"); + StopMonitoring(bShowUserPopup, true); + } + + /// + /// Use this to Stop the Process Monitor * Also Called by Scheduler and Scheduler Backup * + /// + //internal static void StopMonitoring(bool bShowUserPopup = true, bool bForceShutdownOfServices = false) + internal static void StopMonitoring(bool bShowUserPopup, bool bForceShutdownOfServices) + { + // Is the Monitor even running? + if (!App.State_MonitorIsInRunningState()) + return; + + // Set to Stopped + App.State_MonitorStopped(); + + lock (_HiddenWindow) + { + // Reset Error State * But Only if we are NOT being Called by the Scheduler * + if (!App.State_IsSchedulerExecuting() && !App.State_IsSchedulerBackupExecuting()) + App.State_Error_ResetErrorState(); + + // Log Stop of monitoring + App.log.Info(AppResx.GetStringFormated("MESSAGE_MONITOR_STOPPED", App.APPLICATION_NAME_SHORT)); + + // Stop the Internal Timers * Always do this * just in case + StopTimer(); + StopSchedulerTimer(); + if (!App.State_IsSchedulerBackupExecuting()) + StopSchedulerBackupTimer(); + + // Read the config + WatchdogConfiguration config = App.xmlfile.ReadData(false); + + // * Set the System Tray Icon According to Running State * + if(!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.SetSysTrayIconAccordingToRunningState(App.State_GetSystemState()); + + // Popup Std Stop Monitoring Message * But Only if Scheduler && Scheduler Backup is NOT running * + if (bShowUserPopup && !App.State_SpecialMode_IsRunAsServiceSet()) + { + if(App.State_IsSchedulerExecuting()) + { + WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_STARTSTOP_DISPLAY1", App.APPLICATION_NAME_SHORT)); + App.log.Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_STARTSTOP_DISPLAY1", App.APPLICATION_NAME_SHORT)); + } + else if (App.State_IsSchedulerBackupExecuting()) + { + WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_BACKUP_STARTSTOP_DISPLAY1", App.APPLICATION_NAME_SHORT)); + App.log.Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_BACKUP_STARTSTOP_DISPLAY1", App.APPLICATION_NAME_SHORT)); + } + else + { + if (config.ConfiguredHasAny) + WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_STOP_SUCCESS", App.APPLICATION_NAME_SHORT, App.APP_XML_DS_FILENAME, bForceShutdownOfServices)); + else + WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_NO_CONFIGURATION_FOUND", App.APP_XML_DS_FILENAME)); + } + + if (config.ConfiguredHasAny) + App.log.Info(AppResx.GetStringFormated("MESSAGE_STOP_SUCCESS", App.APPLICATION_NAME_SHORT, App.APP_XML_DS_FILENAME, bForceShutdownOfServices)); + else + App.log.Error(AppResx.GetStringFormated("MESSAGE_NO_CONFIGURATION_FOUND", App.APP_XML_DS_FILENAME)); + } + + // Shutdown any of our Running Processes + TraceM.TraceBegin(); + bool bKillApplications = !(App.State_SpecialCircum_ShouldWeNotCloseApplicationsOnExit() && s_WindowCloseGotCalled); + if(bKillApplications) + ProcessKillAllMonitoredProcessesIfAny(config); + TraceM.TraceEnd("Killing Running Applications"); + + // Shutdown any of our Running Services + TraceM.TraceBegin(); + ServicesKillAllMonitoredServicesIfAny(config, bForceShutdownOfServices); + TraceM.TraceEnd("Killing Running Services"); + + // Try Reclaim memory by running the Garbage Collector + // * Really appears to not be doing much * + if (!App.State_IsSchedulerExecuting() && !App.State_IsSchedulerBackupExecuting()) + { + TraceM.TraceBegin(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + TraceM.TraceEnd("Garbage Collector Called"); + } + } + } + + /// + /// Use this to Start the Scheduler + /// + internal static void StartScheduler() + { + lock (_HiddenWindow) + { + // Start the Scheduler Only if there is any Scheduler Information (and the Settings Window + // isn't currently being shown - Settings window disables/enables Scheduler on it's own * + if (!App.State_IsSchedulerExecuting() && App.setting.SchedulerSettingsExist && + !WPFWindowMaker.IsWindowOfTypeWPFWindowShowing_WithinLock(WPFWindow.Settings_Window)) + StartSchedulerTimer(); + } + } + + /// + /// Use this to Stop the Scheduler + /// + internal static void StopScheduler() + { + lock (_HiddenWindow) + { + StopSchedulerTimer(); + } + } + + /// + /// Use this to Start the Scheduler Backup + /// + //internal static void StartSchedulerBackup(uint IntervalInMiliseconds = 99986) + internal static void StartSchedulerBackup(uint IntervalInMiliseconds) + { + lock (_HiddenWindow) + { + // Start the Scheduler Backup Only if there is any Scheduler Backup Information (and the Settings Window + // isn't currently being shown - Settings window disables/enables Scheduler Backup on it's own * + if (!App.State_IsSchedulerBackupExecuting() && App.setting.SchedulerBackupSettingsExist && + !WPFWindowMaker.IsWindowOfTypeWPFWindowShowing_WithinLock(WPFWindow.Settings_Window)) + StartSchedulerBackupTimer(IntervalInMiliseconds); + } + } + + /// + /// Use this to Stop the Scheduler Backup + /// + internal static void StopSchedulerBackup() + { + lock (_HiddenWindow) + { + StopSchedulerBackupTimer(); + } + } + + #endregion + + #region Construction + + /// + /// Constructor + /// + public HiddenMainWindow() + { + InitializeComponent(); + + // Only continue, if no fatal application error occured + if (!App.s_FatalErrorOccured) + { + // * Force this window to remain hidden indefinitly* + _HiddenWindow = new KeepHidden(this); + + // Assign ProcessExit Event to Handle more specialized Cleanup + AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit); + + // We DON'T NEED TO SHOW THE HIDDEN WINDOW * Triggering OnLoad(), if we are + // being launched via the Command-Line. + if (App.State_SpecialMode_IsCommandLineSet()) + { + // If we are in Command-Line Mode * Then Hidden Window should never be shown * + // in fact we'll enter into a whole new entry point * CommandLine MODE + ElapsedEventHandler EnterCommandLineMode = delegate(object sender, ElapsedEventArgs e) + { + Timer timer = (Timer)sender; + timer.Stop(); + + // * Enter CommandLine Mode * + CMDlineHandler.EnterCommandLineMode(); + + timer.Dispose(); + timer = null; + + // Done Here, shut down the app + App.Current.Shutdown(); + }; + TTimer timerEnterCommandLineMode = new TTimer(EnterCommandLineMode, 500, true, true); + } + else + { + // Calling Show() directly in the Constructor causes a .Net Run-time Crash + // upon first launch only. Calling it .5 seconds later does the trick of avoiding that error, + // because by then the object is fully constructed + ElapsedEventHandler DelayShowHiddenWindow = delegate(object sender, ElapsedEventArgs e) + { + Timer timer = (Timer)sender; + timer.Stop(); + + // * Show the Hidden Window * + _HiddenWindow.Show(); + + timer.Dispose(); + timer = null; + }; + TTimer timerDelayShowHiddenWindow = new TTimer(DelayShowHiddenWindow, 500, true, true); + } + } + } + + #endregion + + #region Process Monitor / Service Monitor * CORE LOGIC * + + /// + /// Called every Nth Interval, does the grunt of the work for this whole + /// Application. It checks the processes, and if they are running, etc... + /// * Core Business Logic * + /// + private static void TTimer_ElapsedEventHandler(object sender, ElapsedEventArgs e) + { + lock (_HiddenWindow) + { + // Stop the timer to prevent multiple call-Ins, while work is still being done + StopTimer(); + + // Everything is Good, Monitor the Processes / Services Start any processes / Services that aren't available + if (App.State_MonitorIsInRunningState() && !App.State_IsMonitorPaused()) + { + // Get the Configuration + WatchdogConfiguration config = App.xmlfile.ReadData(false); + + // * NO CONFIGURATION * Stop! - Nothing to monitor + if (!config.ConfiguredHasAny) + { + StopMonitoring(true, false); + return; + } + + // * Read the Ini Configuration Settings * + TraceM.TraceBegin(); + uint nMaxFailureCount = App.setting.MaxFailureCount; + uint nMaxFailureCountHour = App.setting.MaxFailureCountHour; + TraceM.TraceEnd("Monitor - Reading in INI Configuration"); + + // * SERVICE MONITORING * + TraceM.TraceBegin(); + bool bErrorThrownInServicesMonitor = !ServiceMonitor(config, nMaxFailureCount, nMaxFailureCountHour); + TraceM.TraceEnd("* Service Monitoring *"); + + // * PROCESS MONITORING * + TraceM.TraceBegin(); + bool bErrorThrownInProcessMonitor = !ProcessMonitor(config, nMaxFailureCount, nMaxFailureCountHour); + TraceM.TraceEnd("* Process Monitoring *"); + + // If Any Unknown Errors were Thrown! Stop the Monitor + if (bErrorThrownInServicesMonitor || bErrorThrownInProcessMonitor) + { + StopMonitoring(true, false); + + //// + // An Error Occured with the Application * Better Notify User, if specified * + //// + if (App.setting.EmailNotificationEnabled && App.setting.EmailSettingsValid) + { + bool bIsSend = Emailer.SendEmail(App.setting.EmailStmpServer, App.setting.EmailSenderEmail, App.APPLICATION_NAME_SHORT, + App.setting.EmailReceiverEmail, AppResx.GetStringFormated("ERROR_EMAIL_SUBJECT", App.APPLICATION_NAME_SHORT), + AppResx.GetStringFormated("ERROR_EMAIL_BODY", App.APPLICATION_NAME_SHORT, App.setting.EmailCustomMessageText), + App.setting.EmailSmtpPort, String.Empty, App.setting.EmailStmpServerRequiresAuth, App.setting.EmailSmtpUsername, App.setting.EmailSmtpPassword, + App.setting.EmailStmpServerRequiresSSL, 30); + if (!bIsSend) + App.log.Error("Failed to Send ERROR_EMAIL"); + else + App.log.Info(String.Format("Email (ERROR_EMAIL) Notification send to {0}", App.setting.EmailReceiverEmail)); + } + return; + } + + if (!App.State_IsMonitorPaused()) // If the System Is Paused, no need to start the Timer again + { + // Re-Start the timer, if all went well + // * At the end of each Cycle, Set the System Tray Icon Accordingly * Keep track when we were last run + if(!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.SetSysTrayIconAccordingToRunningState(App.State_GetSystemState()); + StartTimer(); // Everything went well, continue... + } + } + } + } + + /// + /// Used by the Scheduler Timer to determine if a restart is to occur + /// + /// now datetime stamp + /// scheduler start datetime stamp + /// datetime when last run occured + /// numeric value indicating hours or minutes + /// true if hours, false if minutes + /// true if we want the Scheduler to Restart, false otherwise + private static bool PerformRestartTimeSchedulerTriggered(DateTime now, DateTime start, DateTime LastRun, uint nHoursOrMinutes, bool bIsHours) + { + // * OLD CALCULATION * OLD SCHOOL * Make sure enough time has passed since last run, otherwise no need to continue + //TimeSpan tsLastRun = now - LastRun; + //bool bEnoughTimeSinceLastRunHasPassed = bIsHours ? (((double)(tsLastRun.TotalMinutes / 60)) >= ((double)nHoursOrMinutes)) : (tsLastRun.TotalMinutes >= ((double)nHoursOrMinutes)); + //return bEnoughTimeSinceLastRunHasPassed; + + // If the current time is within 2 minutes of last run, there is no need to continue + TimeSpan tsDiffBetweenNowAndLastRun = (now - LastRun); + bool bIsWithin2MinutesOfLastRun = (tsDiffBetweenNowAndLastRun.TotalMinutes >= 0) && (tsDiffBetweenNowAndLastRun.TotalMinutes <= 2.9997); + if (bIsWithin2MinutesOfLastRun) + return false; + + // Check the start time * if we are near the start time and a last run hasn't occured, then yes, run this * + TimeSpan tsDiffBetweenNowAndStart = (now - start); + bool bIsWithin2MinutesOfScheduledStartTime = (tsDiffBetweenNowAndStart.TotalMinutes >= 0) && (tsDiffBetweenNowAndStart.TotalMinutes <= 2.9997); + if (bIsWithin2MinutesOfScheduledStartTime) + return true; + + // Perform Cycle Calculation + double durationInMinutes = bIsHours ? nHoursOrMinutes * 60 : nHoursOrMinutes; + double remCycle = tsDiffBetweenNowAndStart.TotalMinutes % durationInMinutes; + bool bIsWithin2MinutesOfRemCycle = (remCycle >= 0) && (remCycle <= 2.9997); + return bIsWithin2MinutesOfRemCycle; + } + + /// + /// Called every Nth Interval, does the grunt of the work for the Scheduler + /// * Core Business Logic * + /// + private static void TTimer_ElapsedEventHandlerScheduler(object sender, ElapsedEventArgs e) + { + lock (_HiddenWindow) + { + // *imp * stop the process/services monitor (so that while + // this timer occurs it won't conflict with what happens here + bool bPrevStateTimer = StopTimer(); + + // Stop the Scheduler timer to prevent multiple call-Ins, while work is still being done + StopSchedulerTimer(); + + // Everything is Good, + if (App.State_MonitorIsInRunningState() && !App.State_IsSchedulerExecuting() && !App.State_IsMonitorPaused()) + { + // Set the Scheduler State + App.State_SchedulerExecution_Started(); + + // Fetch scheduler settings + bool bFoundSchedulerSettings = false; + DateTime dtStart = DateTime.MinValue; + DateTime dtLastRun = DateTime.MinValue; + uint nHoursOrMinutes = 0; + bool bIsHours = true; + + // Only Do something if there are any settings + bFoundSchedulerSettings = App.setting.GetSchedulerSettings(out dtStart, out dtLastRun, out nHoursOrMinutes, out bIsHours); + if (!bFoundSchedulerSettings) + return; // No Scheduler Settings, no need to continue + + // Get the Now Time + DateTime dtNow = DateTime.Now; + + // Make sure we've reached the Start Time, else there is no need to continue + if (dtNow >= dtStart && PerformRestartTimeSchedulerTriggered(dtNow, dtStart, dtLastRun, nHoursOrMinutes, bIsHours)) + { + // Log the fact that Scheduler is about to stop / start + App.log.Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_STARTSTOP", App.APPLICATION_NAME_SHORT, App.APP_XML_DS_FILENAME)); + + // * Perform Restart * + RestartMonitoring(4, true); + + // Set Last Run + App.setting.LastSchedulerRun = dtNow; + } + + // Reset the Scheduler State + App.State_SchedulerExecution_Stopped(); + } + + if (!App.State_IsMonitorPaused()) // If the System Is Paused, no need to start the Timers again + { + // Re-Start the Scheduler timer * Everything went well s * + StartScheduler(); + + // Now it should be save to start the Main Monitor Timer + if (bPrevStateTimer) + StartTimer(); + } + } + } + + /// + /// Integrity - When we fire a start Trigger with SchedulerBackupTriggerOccured(), in case another call in occurs, + /// we know to ignore it + /// + private static DateTime _LastStartTriggerFired = DateTime.MinValue; + + /// + /// Integrity - When we fire a end Trigger with SchedulerBackupTriggerOccured(), in case another call in occurs, + /// we know to ignore it + /// + private static DateTime _LastEndTriggerFired = DateTime.MinValue; + + /// + /// Deterimes the State of the Scheduler Backup and what action to take + /// + /// The Now DateTime + /// Scheduled Start DateTime + /// Last Run, should be set when the first start occured and passed into here for checking + /// the duration from start to end + /// we want to perform the following action * Trigger Occured *, 0 = None, 1 = Start, 2 = End + private static uint SchedulerBackupTriggerOccured (DateTime dtNow, DateTime dtScheduledStartDT, DateTime dtLastRun, TimeSpan tsDuration) + { + // Time Of Day Vars + TimeSpan TimeOfDayNow = dtNow.TimeOfDay; + TimeSpan TimeOfDayScheduled = dtScheduledStartDT.TimeOfDay; + + // Debug + //dtNow = DateTime.Parse("8/13/2011 01:57:29 AM"); + //TimeOfDayNow = new TimeSpan(1, 57, 29); + //TimeOfDayScheduled = new TimeSpan(1, 56, 11); + //tsDuration = new TimeSpan(4, 0, 0); + //dtLastRun = DateTime.Parse("1/1/0001 12:00:00 AM"); + + // Time of Day Within Time Calc; + TimeSpan tsDiffBetweenNowAndScheduled = (TimeOfDayNow - TimeOfDayScheduled); + bool bIsWithin2MinutesOfScheduledTime = (tsDiffBetweenNowAndScheduled.TotalMinutes > 0) && (tsDiffBetweenNowAndScheduled.TotalMinutes <= 2.9997); + TimeSpan tsDiffBetweenNowAndScheduledDurationEnd = TimeOfDayNow - (TimeOfDayScheduled + tsDuration); + if(tsDiffBetweenNowAndScheduledDurationEnd.TotalMinutes < 0) // A Day must have passed + tsDiffBetweenNowAndScheduledDurationEnd = tsDiffBetweenNowAndScheduledDurationEnd + new TimeSpan(24, 00, 00); + bool bIsWithin2MinutesOfScheduledTimeDurationEnd = (tsDiffBetweenNowAndScheduledDurationEnd.TotalMinutes <= 2.9997); + + // Last Run Calculations * make sure to see if it was started prior + // ~Last Run allows us to determine if there exists a start for an end, + // we only check the end event to see if a start occured + if (bIsWithin2MinutesOfScheduledTimeDurationEnd) + { + TimeSpan tsDiffBetweenNowAndLastRun = dtNow - dtLastRun; + TimeSpan tsDiffInDuration = tsDiffBetweenNowAndLastRun - tsDuration; + + // In the event the Monitor App got started somewhere in between times, + // and we trigger the end event (because time was reached), we + // are checking last run, to see if it coincides with this end event, + // if it doesn't (as in it is way too long ago ~here anything above 10 minutes out + // of bounds from the duration), we won't trigger the end event, assuming + // that it never started in this cycle to begin with, waiting for the next cycle + bool bIsLastRunOutOfBounce = (tsDiffInDuration.TotalMinutes > 0) && (tsDiffInDuration.TotalMinutes >= 10); + if (bIsLastRunOutOfBounce) + return 0; + } + + // Integrity Check * prevent multiple callins within time boundary 2.9997, * or actually + // within the same day * may as well, we only allow 1 start/end trigger per day + if (bIsWithin2MinutesOfScheduledTime) + { + TimeSpan ts = dtNow - _LastStartTriggerFired; + if ((ts.TotalMinutes > 0) && (ts.TotalMinutes > 1000)) // we should only be firing on start trigger per day aka 1440, but 1000 is a good check (even) + _LastStartTriggerFired = dtNow; + else + bIsWithin2MinutesOfScheduledTime = false; + } + else if (bIsWithin2MinutesOfScheduledTimeDurationEnd) + { + TimeSpan ts = dtNow - _LastEndTriggerFired; + if ((ts.TotalMinutes > 0) && (ts.TotalMinutes > 1000)) // we should only be firing on end trigger per day aka 1440, but 1000 is a good check (even) + _LastStartTriggerFired = dtNow; + else + bIsWithin2MinutesOfScheduledTime = false; + } + + // Trigger Start / Stop / Or Nothing + if (bIsWithin2MinutesOfScheduledTime) + return 1; + else if (bIsWithin2MinutesOfScheduledTimeDurationEnd) + return 2; + else + return 0; + } + + /// + /// Called every Nth Interval, does the grunt of the work for the Scheduler Backup + /// * Core Business Logic * + /// + private static void TTimer_ElapsedEventHandlerSchedulerBackup(object sender, ElapsedEventArgs e) + { + lock (_HiddenWindow) + { + // Stop the Scheduler Backup timer to prevent multiple call-Ins, while work is still being done + StopSchedulerBackupTimer(); + + // Everything is Good, + //if (App.State_MonitorIsInRunningState() && !App.State_IsSchedulerBackupExecuting()) // BUG + if (!App.State_IsSchedulerBackupExecuting()) + { + // Set the Scheduler State + App.State_SchedulerBackupExecution_Started(); + + // Fetch scheduler Backup settings + bool bFoundSchedulerBackupSettings = false; + DateTime dtStart = DateTime.MinValue; + DateTime dtLastRun = DateTime.MinValue; + uint nHoursOrMinutes = 0; + bool bIsHours = true; + + // Only Do something if there are any settings + bFoundSchedulerBackupSettings = App.setting.GetSchedulerBackupSettings(out dtStart, out dtLastRun, out nHoursOrMinutes, out bIsHours); + if (!bFoundSchedulerBackupSettings) + return; // No Scheduler Backup Settings, no need to continue + + // Get the Now Time + DateTime dtNow = DateTime.Now; + + // Make sure we've reached the Start Time, else there is no need to continue + if (dtNow >= dtStart) + { + // Get the Duration + TimeSpan tsDuration = bIsHours ? TimeSpan.FromHours(nHoursOrMinutes) : TimeSpan.FromMinutes(nHoursOrMinutes); + + // Determine the State of the Scheduler Backup an the Action To Take + uint nTriggerResult = SchedulerBackupTriggerOccured(dtNow, dtStart, dtLastRun, tsDuration); + + bool bPerformAnAction = (nTriggerResult == 1) || (nTriggerResult == 2); + if (bPerformAnAction) + { + // Get Current State + App.StateSystem state = App.State_GetSystemState(); + bool bStopInsteadOfPause = App.setting.SchedulerBackupStopInsteadOfPause; + bool bStopServices = App.setting.SchedulerBackupStopServices; + + // Log the fact that Scheduler Backup is about to pause / stop / start + App.log.Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_BACKUP_STARTSTOP", App.APPLICATION_NAME_SHORT, (state == App.StateSystem.Paused))); + + // * Perform System Pause * or + // * Perform System Start *, or * System Stop *, depending on system state and Settings + switch (nTriggerResult) + { + case 1: // Duration Start + { + // We are in a wrong state to perform a start + if (state != App.StateSystem.Error && + state != App.StateSystem.Running) + { + App.log.Error(String.Format("SchedulerBackup Wrong State for Action 'Duration Start' for State {0}. SchedulerBackup Trigger Ignored", Enum.GetName(typeof(App.StateSystem), state))); + } + else + { + // Perform Action + if (bStopInsteadOfPause) + StopMonitoring(true, bStopServices); + else + PauseMonitoring(true); + + // Mark Scheduler Backup as Started + App.setting.LastSchedulerBackupRun = dtNow; + } + break; + } + case 2: // Duration End + { + // We are in a wrong state to perform a start + if (state != App.StateSystem.Paused && + state != App.StateSystem.Stopped) + { + App.log.Error(String.Format("SchedulerBackup Wrong State for Action 'Duration End' for State {0}. SchedulerBackup Trigger Ignored", Enum.GetName(typeof(App.StateSystem), state))); + } + else + { + // Perform Action + StartMonitoring(false, true); + } + break; + } + } + } + } + + // Reset the Scheduler Backup State + App.State_SchedulerBackupExecution_Stopped(); + } + + // Re-Start the Scheduler Backup timer * Waiting for the next iteration * + StartSchedulerBackup(99986); + } + } + + /// + /// * Core Logic * Monitor Processes from the Configuration. Start Any Processes that are Ok to + /// be started (ProcessOkToBeStarted_Check) that are currently not running + /// + /// Monitor Configuration + /// Max Failure Count + /// Max Failure Count in given Hour Timespan + /// true if successfull, false if ProcessMonitor Threw an Error + private static bool ProcessMonitor(WatchdogConfiguration config, uint nMaxFailureCount, uint nMaxFailureCountHour) + { + // * PROCESS MONITORING * + if (config.ConfiguredHasAnyProcesses) + { + try + { + // Start Any Processes that are NOT Running + uint uProcessStartSuccessCount = 0; + uint uProcessStarFailCount = 0; + List nonRunningProcesses = Processes_ThatNeedToBeStarted_Check(config); + if (nonRunningProcesses.Count > 0) + { + // Iterate each non-running Process and try to Start it + foreach (WatchdogConfiguration.ProcessExe mProcess in nonRunningProcesses) + { + // Check that the Process can be started + if (!ProcessOkToBeStarted_Check(mProcess, nMaxFailureCount, nMaxFailureCountHour)) + continue; + + App.log.Info(AppResx.GetStringFormated("MESSAGE_ABOUT_TO_START_PROCESS", mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms), -1); + uint PID = PStarter.StartProcess(PStartInfo.CreateProcess(mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms, mProcess.WorkingDirectory, false, ProcessWindowStyle.Normal, true), false, false); + if (PID > 0) + { + // Add to Start Attempt, Set the PID, log, and incr. Counter + mProcess.AddStartTime(DateTime.Now, (int)(nMaxFailureCount + 2)); // *Always* Keep the List 2 bigger than nMaxFailureCount, this way we can track up to nMaxFailure + mProcess.PID = PID; + App.log.Info(AppResx.GetStringFormated("MESSAGE_START_PROCESS_SUCCESS", mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms), -1); + App.log.Info(AppResx.GetStringFormated("MESSAGE_START_PROCESS_SUCCESS2", mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms, mProcess.GetStartTimesInGivenTimeSpan(TimeSpan.FromHours(nMaxFailureCountHour)), nMaxFailureCountHour), -1); + uProcessStartSuccessCount++; + } + else + { + // Add to Failure, Reset the PID, log, and incr. Counter + mProcess.AddFailTime(DateTime.Now, (int)(nMaxFailureCount + 2)); // *Always* Keep the List 2 bigger than nMaxFailureCount, this way we can track up to nMaxFailure + mProcess.PID = 0; + App.log.Error(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED", mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms), -1); + App.log.Error(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED2", mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms, mProcess.GetFailTimesInGivenTimeSpan(TimeSpan.FromHours(nMaxFailureCountHour)), nMaxFailureCountHour), -1); + uProcessStarFailCount++; + } + } + + // Imp! - Let the User know Visually that Processes were started or failed to start + if (uProcessStartSuccessCount > 0 && uProcessStarFailCount == 0) + { + App.log.Info(AppResx.GetStringFormated("MESSAGE_PROCESS_N_SUCCESS_COUNT", uProcessStartSuccessCount)); + if(!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_PROCESS_N_SUCCESS_COUNT", (int)uProcessStartSuccessCount)); + } + else if (uProcessStartSuccessCount == 0 && uProcessStarFailCount > 0) + { + App.log.Error(AppResx.GetStringFormated("MESSAGE_PROCESS_N_FAIL_COUNT", uProcessStarFailCount)); + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_PROCESS_N_FAIL_COUNT", (int)uProcessStarFailCount)); + } + else if (uProcessStartSuccessCount > 0 && uProcessStarFailCount > 0) + { + App.log.Error(AppResx.GetStringFormated("MESSAGE_PROCESS_N_SUCCESS_AND_N_FAIL_COUNT", uProcessStartSuccessCount, uProcessStarFailCount)); + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_PROCESS_N_SUCCESS_AND_N_FAIL_COUNT", (int)uProcessStartSuccessCount, (int)uProcessStarFailCount)); + } + + // If any Process Changes occured * Refresh the System Tray Notification Area * + if (uProcessStartSuccessCount > 0 || uProcessStarFailCount > 0) + { + if (!App.State_SpecialMode_IsRunAsServiceSet()) + Watchdog.WatchdogLib.Win32.Functions.RefreshTaskbarNotificationArea(); + } + } + else + { + // We are in a * good State * No Configuration was found that isn't running, + // use this time to make sure that our StartedPID Table is in sync * Perform general internal DS Cleanup * + PStarter.PerformGeneralCleanupOfAllStartedProcessIDs(config.MonitoredProcesses.QueryAllPidInfo(false, true)); + } + } + catch (Exception ex) { App.log.Error("Process Monitor Running Error Thrown", ex); return false; } + } + + return true; + } + + /// + /// Checks to see if the process is Ok to be Started. Checks error state as well as timeout values + /// + /// a valid process exe, that is going to be started + /// Max Failure Count + /// Max Failure Count in given Hour Timespan + /// true if it is ok to start, false otherwise + private static bool ProcessOkToBeStarted_Check(WatchdogConfiguration.ProcessExe processExe, uint nMaxFailureCount, uint nMaxFailureCountHour) + { + if (processExe == null) + return false; + + // * Ignore any process marked as errored * + if (processExe.InErrorState) + return false; + + // If there was a failure starting the process before,... check + // the last failed time flag, we don't want to try over and over again, every second, + // but we'll give it a try every minute + if (!processExe.LastFailTimeTimeoutExceeded(TimeSpan.FromMinutes(1))) + { + return false; + } + else if (processExe.GetFailTimesInGivenTimeSpanMaxTryExceeded(new TimeSpan((int)nMaxFailureCountHour, 0, 0), nMaxFailureCount)) + { + // We are now officially in an error state + App.State_Error_SetErrorState(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED_ERROR_STATE", processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms)); + processExe.InErrorState = true; + App.log.Info(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED_ERROR_STATE", processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms)); + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED_ERROR_STATE", processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms)); + return false; // skip Starting of this Process; Start Failure Count too High + } + + // If the process started correctly, but then failed shortly thereafter, + // then we want to keep track of it and make sure that don't try to continue starting it + if (!processExe.LastStartTimeTimeoutExceeded(TimeSpan.FromMinutes(1))) + { + return false; + } + else if (processExe.GetStartTimesInGivenTimeSpanMaxTryExceeded(new TimeSpan((int)nMaxFailureCountHour, 0, 0), nMaxFailureCount)) + { + // We are now officially in an error state + App.State_Error_SetErrorState(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED_ERROR_STATE", processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms)); + processExe.InErrorState = true; + App.log.Info(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED_ERROR_STATE", processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms)); + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED_ERROR_STATE", processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms)); + return false; // skip Starting of this Process; Start Count too High + } + + return true; + } + + /// + /// Static variable * For Performance * used by Processes_ThatNeedToBeStarted_Check() + /// + private static List s_nonRunningProcesses = new List(); + + /// + /// Goes thru the configuration and returns back a list of all processes that need to be started + /// + /// Monitor Configuration + /// List of all processes currently not running that require a new start, empty list if none found + private static List Processes_ThatNeedToBeStarted_Check(WatchdogConfiguration config) + { + // Clear Non-Running List + s_nonRunningProcesses.Clear(); + + // We don't want to run thru all command-line parameters all the time * Time-Hog * + // ~instead we want to check our run-time configuration to see if we have any start/fail information, + // if we don't, then we MUST run thru everything, because this means our PID info will be out of date + // ~If we are in an Error state, don't perform in-depth search, otherwise once the error state is reached + // we'll always fail here and always perform the in-depth search pointlessly since we'll never launch that process + bool bPerformInDepthProcessSearch = config.MonitoredProcesses.HasIncompletePidInformation() && (App.State_GetSystemState() != App.StateSystem.Error); + if (bPerformInDepthProcessSearch) + { + // Cache Process Lookups * for performance * + ProcessW.ClearProcessObjsGetterCache(); + foreach (WatchdogConfiguration.ProcessExe mProcess in config.MonitoredProcesses.ProcessExes) + { + // Immediatly Cache the Process Getter Calls * Better Performance * + Process[] RunningProcessesOfExe = ProcessW.AllRunningProcessesOf(mProcess.ProcessName, true, true); + + // Perform ExactProcessExeNCommandLinePrms Matching + Process rProcess = null; + int nProcessIndex = -1; + TraceM.TraceBegin(); + bool bFoundMatch = ProcessW.FoundExactProcessExeNCommandLinePrmsInRunningProcesses(RunningProcessesOfExe, mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms, (int)mProcess.PID, out rProcess, out nProcessIndex); + TraceM.TraceEnd("Calling FoundExactProcessExeNCommandLinePrmsInRunningProcessess"); + + // If no Match was found in the Running Processes, add to the list + if (!bFoundMatch) + { + s_nonRunningProcesses.Add(mProcess); + } + else if(rProcess != null) // we found a match * update the configuration * + { + // Update the Pid in our configuration + mProcess.PID = (uint)rProcess.Id; + rProcess = null; + } + } + } + else // Quick Search * Better Performances * + { + // Fastest way to check for PID changes is to retrieve + // all Pids on the system once and then do everything in memory + TraceM.TraceBegin(); + List AllComputerPIDs = ProcessW.AllRunningPids(); + foreach (uint pid in config.MonitoredProcesses.QueryAllPidInfo(false, true)) + { + if (!AllComputerPIDs.Contains(pid)) + { + WatchdogConfiguration.ProcessExe p = config.MonitoredProcesses.GetProcessExeForPid(pid); + if(p != null) + s_nonRunningProcesses.Add(p); + } + } + TraceM.TraceEnd("Going Thru Applications Pid-by-Pid"); + } + + // Return all Processes that aren't running + return s_nonRunningProcesses; + } + + /// + /// Iterates thru the Configured Processes and Try's to Kill Any Processes that + /// are running. + /// + private static void ProcessKillAllMonitoredProcessesIfAny(WatchdogConfiguration config) + { + // Kill all Processes that exist in our configuration + uint nAssumedRunningProcesses = 0; + uint nErroredProcesses = 0; + uint nProcessesKilled = 0; + + // Allow the User to not kill certain processes via the Restart Scheduler + bool bIncludeNonRestartProcesses = !App.State_IsSchedulerExecuting(); + + // Check Configuration + if (config.ConfiguredHasAnyProcesses) + { + nErroredProcesses = config.MonitoredProcesses.NumberOfPidsInErrorState(); + if (nErroredProcesses > 0) + App.log.Info(AppResx.GetStringFormated("MESSAGE_NPROCESSES_IN_AN_ERROR_STATE", App.APPLICATION_NAME_SHORT, nErroredProcesses)); + + nAssumedRunningProcesses = (uint)config.MonitoredProcesses.QueryAllPidInfo(false, bIncludeNonRestartProcesses).Length; + if (nAssumedRunningProcesses > 0) + App.log.Info(AppResx.GetStringFormated("MESSAGE_ABOUT_TO_CLOSE_N_PROCESSES", App.APPLICATION_NAME_SHORT, nAssumedRunningProcesses)); + + if (nAssumedRunningProcesses <= 0) // nothing to do here + return; + } + else + { + return; // nothing to do here + } + + // Iterate the Configuration * Use the PIDs whe have stored * + foreach (uint pid in config.MonitoredProcesses.QueryAllPidInfo(false, bIncludeNonRestartProcesses)) + { + WatchdogConfiguration.ProcessExe p = config.MonitoredProcesses.GetProcessExeForPid(pid); + bool bSuccess = PStarter.KillProcess(pid, false, 1, true, 2); + if (bSuccess) + { + App.log.Info(AppResx.GetStringFormated("MESSAGE_CLOSE_PID_SUCCESS", pid, p.ProcessExeFileNameNPath, p.CommandLinePrms), -1); + nProcessesKilled++; + } + else + { + App.log.Error(AppResx.GetStringFormated("MESSAGE_CLOSE_PID_FAILURE", pid, p.ProcessExeFileNameNPath, p.CommandLinePrms), -1); + } + } + + // Refresh the taskbar, after killing all the processes + if(nProcessesKilled > 0 && !App.State_SpecialMode_IsRunAsServiceSet()) + Watchdog.WatchdogLib.Win32.Functions.RefreshTaskbarNotificationArea(); + + // Let the User know that errors occured + if (App.State_GetSystemState() != App.StateSystem.Error) /* if We already are in an error state, no need to pop-up a message again */ + { + if (nProcessesKilled != nAssumedRunningProcesses) + { + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_ABOUT_CLOSE_N_PROCESSES_FAILURE", (int)(nAssumedRunningProcesses - nProcessesKilled))); + App.log.Error(AppResx.GetStringFormated("MESSAGE_ABOUT_CLOSE_N_PROCESSES_FAILURE", (config.MonitoredProcesses.ProcessExes.Length - nProcessesKilled))); + } + else + { + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_ABOUT_CLOSE_N_PROCESSES_SUCCESS", nProcessesKilled)); + App.log.Info(AppResx.GetStringFormated("MESSAGE_ABOUT_CLOSE_N_PROCESSES_SUCCESS", nProcessesKilled)); + } + } + } + + /// + /// * Core Logic * Monitor Services from the Configuration. Start Any Services that are Ok to + /// be started (ServiceOkToBeStarted_Check) that are currently not running + /// + /// Monitor Configuration + /// Max Failure Count + /// Max Failure Count in given Hour Timespan + /// true if successfull, false if ServiceMonitor Threw an Error + private static bool ServiceMonitor(WatchdogConfiguration config, uint nMaxFailureCount, uint nMaxFailureCountHour) + { + // * SERVICE MONITORING * + if (config.ConfiguredHasAnyServices) + { + try + { + // Check To see if the Service is up and running, if it isn't, restart it * simple stuff * + uint uServiceStartSuccessCount = 0; + uint uServiceStartFailCount = 0; + foreach (WatchdogConfiguration.ServiceExe s in config.MonitoredServices.ServiceExes) + { + // Start the Service up to Five Times * Simplified way of doing this * + TraceM.TraceBegin(); + bool bIsServiceRunning = ServiceW.IsServiceRunning(s.Name); + TraceM.TraceEnd("Checking Service is Running"); + + if (!bIsServiceRunning) + { + // Check that the Service can be started + if (!ServiceOkToBeStarted_Check(s, nMaxFailureCount, nMaxFailureCountHour)) + continue; + + // * Try to Restart the Service * + TraceM.TraceBegin(); + App.log.Info(AppResx.GetStringFormated("MESSAGE_ABOUT_TO_RESTART_SERVICE", s.Name), -1); + bool bRestartOk = ServiceW.RestartService(s.Name, true, 240 , 2); + TraceM.TraceEnd("Restarting Service"); + + if (bRestartOk) + { + // Add to Start Attempt, Log the Successful Launch, incr. Start Service Counter + s.AddStartTime(DateTime.Now, (int)(nMaxFailureCount + 2)); // *Always* Keep the List 2 bigger than nMaxFailureCount, this way we can track up to nMaxFailure + App.log.Info(AppResx.GetStringFormated("MESSAGE_START_SERVICE_SUCCESS", s.Name, s.GetStartTimesInGivenTimeSpan(TimeSpan.FromHours(nMaxFailureCountHour)), nMaxFailureCountHour), -1); + uServiceStartSuccessCount++; + } + else + { + // Add to Failure, Log the Errored Launch, incr. Failed Service Counter + s.AddFailTime(DateTime.Now, (int)(nMaxFailureCount + 2)); // *Always* Keep the List 2 bigger than nMaxFailureCount, this way we can track up to nMaxFailure + App.log.Error(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED", s.Name, s.GetFailTimesInGivenTimeSpan(TimeSpan.FromHours(nMaxFailureCountHour)), nMaxFailureCountHour), -1); + uServiceStartFailCount++; + } + } + } + + // Imp! - Let the User know Visually that Processes were started or failed to start + if (uServiceStartSuccessCount > 0 && uServiceStartFailCount == 0) + { + App.log.Info(AppResx.GetStringFormated("MESSAGE_SERVICE_N_SUCCESS_COUNT", uServiceStartSuccessCount)); + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_SERVICE_N_SUCCESS_COUNT", (int)uServiceStartSuccessCount)); + } + else if (uServiceStartSuccessCount == 0 && uServiceStartFailCount > 0) + { + App.log.Error(AppResx.GetStringFormated("MESSAGE_SERVICE_N_FAIL_COUNT", uServiceStartFailCount)); + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_SERVICE_N_FAIL_COUNT", (int)uServiceStartFailCount)); + } + else if (uServiceStartSuccessCount > 0 && uServiceStartFailCount > 0) + { + App.log.Error(AppResx.GetStringFormated("MESSAGE_SERVICE_N_SUCCESS_AND_N_FAIL_COUNT", uServiceStartSuccessCount, uServiceStartFailCount)); + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_SERVICE_N_SUCCESS_AND_N_FAIL_COUNT", (int)uServiceStartSuccessCount, (int)uServiceStartFailCount)); + } + } + catch (Exception ex) { App.log.Error("Error Thrown in Service Monitor", ex); return false; }; + } + return true; + } + + /// + /// Checks to see if the service is Ok to be Started. Checks error state as well as timeout values + /// + /// a valid service exe, that is going to be started + /// Max Failure Count + /// Max Failure Count in given Hour Timespan + /// true if it is ok to start, false otherwise + private static bool ServiceOkToBeStarted_Check(WatchdogConfiguration.ServiceExe serviceExe, uint nMaxFailureCount, uint nMaxFailureCountHour) + { + if (serviceExe == null) + return false; + + // * Ignore any services marked as errored * + if (serviceExe.InErrorState) + return false; + + // If there was a failure starting the Service before,... check + // the last failed time flag, we don't want to try over and over again, every second, + // but we'll give it a try every minute + if (!serviceExe.LastFailTimeTimeoutExceeded(TimeSpan.FromMinutes(1))) + { + return false; + } + else if (serviceExe.GetFailTimesInGivenTimeSpanMaxTryExceeded(new TimeSpan((int)nMaxFailureCountHour, 0, 0), nMaxFailureCount)) // Default 1 Hour + { + // We are now officially in an error state + App.State_Error_SetErrorState(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED_ERROR_STATE", serviceExe.Name)); + serviceExe.InErrorState = true; + App.log.Error(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED_ERROR_STATE", serviceExe.Name)); + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED_ERROR_STATE", serviceExe.Name)); + return false; // skip Starting of this Service; Start Failure Count too High + } + + // If the service started correctly, but then failed shortly thereafter, + // then we want to keep track of it and make sure that don't try to continue starting it + if (!serviceExe.LastStartTimeTimeoutExceeded(TimeSpan.FromMinutes(1))) + { + return false; + } + else if (serviceExe.GetStartTimesInGivenTimeSpanMaxTryExceeded(new TimeSpan((int)nMaxFailureCountHour, 0, 0), nMaxFailureCount)) // Default 1 Hour + { + // We are now officially in an error state + App.State_Error_SetErrorState(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED_ERROR_STATE", serviceExe.Name)); + serviceExe.InErrorState = true; + App.log.Error(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED_ERROR_STATE", serviceExe.Name)); + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED_ERROR_STATE", serviceExe.Name)); + return false; // skip Starting of this Service; Start Count too High + } + + return true; + } + + /// + /// Iterates thru the Configured Processes and Try's to Kill Any Services that + /// are running and configured to be Stopped. + /// + /// Set to true to force shutting down of services, false otherwise + //private static void ServicesKillAllMonitoredServicesIfAny(WatchdogConfiguration config, bool bForceShutdownOfServices = false) + private static void ServicesKillAllMonitoredServicesIfAny(WatchdogConfiguration config, bool bForceShutdownOfServices) + { + // Should we shutdown Services??? + bool bRestartServices = bForceShutdownOfServices || (App.State_IsSchedulerExecuting() && App.setting.SchedulerRestartsServices); + if (bRestartServices) + { + int nServicesClosed = 0; + int nServicesNotClosed = 0; + + // Allow the User to not kill certain services via the Restart Scheduler + bool bIncludeNonRestartServices = !App.State_IsSchedulerExecuting(); + if (config.ConfiguredHasAnyServices) + App.log.Info(AppResx.GetStringFormated("MESSAGE_ABOUT_TO_CLOSE_N_SERVICES", App.APPLICATION_NAME_SHORT, config.MonitoredServices.QueryAllServiceInfo(false, bIncludeNonRestartServices).Length)); + + foreach (string ServiceName in config.MonitoredServices.QueryAllServiceInfo(false, bIncludeNonRestartServices)) + { + // This is only getting called when the scheduler is executing... + // the scheduler will be responsible for starting the monitor which in turn will start the services and keep track of all + // service starts, hence all we have to do here is shut down the services * they will automatically be restarted * + App.log.Info(AppResx.GetStringFormated("MESSAGE_ABOUT_TO_CLOSE_SERVICE", ServiceName), -1); + bool bCloseServiceSuccess = ServiceW.StopService(ServiceName, true, 240); + if (bCloseServiceSuccess) + { + App.log.Info(AppResx.GetStringFormated("MESSAGE_CLOSE_SERVICE_SUCCESS", ServiceName), -1); + nServicesClosed++; + } + else + { + App.log.Error(AppResx.GetStringFormated("MESSAGE_CLOSE_SERVICE_FAILURE", ServiceName), -1); + nServicesNotClosed++; + } + } + + // Imp! - Let the User know Visually that Services were Stopped + if (nServicesClosed > 0 && nServicesNotClosed == 0) + { + App.log.Info(AppResx.GetStringFormated("MESSAGE_STOPSERVICE_N_SUCCESS_COUNT", nServicesClosed)); + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_STOPSERVICE_N_SUCCESS_COUNT", nServicesClosed)); + } + else if (nServicesClosed == 0 && nServicesNotClosed > 0) + { + App.log.Error(AppResx.GetStringFormated("MESSAGE_STOPSERVICE_N_FAIL_COUNT", nServicesNotClosed)); + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_STOPSERVICE_N_FAIL_COUNT", nServicesNotClosed)); + } + else if (nServicesClosed > 0 && nServicesNotClosed > 0) + { + App.log.Error(AppResx.GetStringFormated("MESSAGE_STOPSERVICE_N_SUCCESS_AND_N_FAIL_COUNT", nServicesClosed, nServicesNotClosed)); + if (!App.State_SpecialMode_IsRunAsServiceSet()) + WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_STOPSERVICE_N_SUCCESS_AND_N_FAIL_COUNT", nServicesClosed, nServicesNotClosed)); + } + } + } + + #endregion + + #region Loaded/Unloaded Event Handlers + + /// + /// Handles the Main Window StartUp event. + /// + private void Window_Loaded(object sender, RoutedEventArgs e) + { + // * To make sure, we are not fatally closing * + if (App.s_FatalErrorOccured) + return; + + // * To make sure, do nothing in command-line mode * + if (App.State_SpecialMode_IsCommandLineSet()) + return; + + if (!App.State_SpecialMode_IsRunAsServiceSet()) + { + // Performance Load WPF Windows + WPFWindows.WPFWindowMaker.PerformanceCache(); + + // Set Registry key to Start w. Windows, if specified + bool bStartWithWindows = App.setting.StartWithWindows; + App.log.Info(String.Format("Starting with Windows is set to: '{0}'", bStartWithWindows)); + } + + // Ensure proper existence of HardLink, if specified + bool bCreateHardLink = App.setting.CreateExeHardLinkInRootPath; + App.log.Info(String.Format("Creating .Exe HardLink in Root Path is set to: '{0}'", bCreateHardLink)); + + // If we are deployed by ClickOnce, let's modify the uninstall icon (Add/Remove) Programs + // ~there appears to be no better way than modifying the registry manually * Now Add/Remove will have a nice + // little App icon instead of the default ClickOnce Icon * + string strKeyFetched = RegKey.GetKey("Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + App.APPLICATION_PRODUCT_CLICKONCE_ID, "DisplayIcon", ""); + if(!String.IsNullOrEmpty(strKeyFetched)) + RegKey.SetKey("Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + App.APPLICATION_PRODUCT_CLICKONCE_ID, "DisplayIcon", AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameNPath(AssemblyW.AssemblyST.Entry)); + + // Sleep a little before showing the System Tray + System.Threading.Thread.Sleep(250); + + if (!App.State_SpecialMode_IsRunAsServiceSet()) + { + // Refresh the taskbar, when launching (maybe there are trailing instances) + Watchdog.WatchdogLib.Win32.Functions.RefreshTaskbarNotificationArea(); + + // Show the System Tray Icon/Menu + WatchdogSysTray.WatchdogSysTray_Show(); + } + + // Start Process Monitoring upon start? * Default is Yes * + if (App.setting.StartMonitorUponStart) + { + // We should only start the monitor, if there is any configuration + if (App.xmlfile.ReadData(false).ConfiguredHasAny) + { + StartMonitoring(false, true); + } + else if (!App.State_SpecialMode_IsRunAsServiceSet()) + { + WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_NO_CONFIGURATION_FOUND", App.APP_XML_DS_FILENAME)); + WPFWindows.WPFWindowMaker.CreateNShowWindow(WPFWindows.WPFWindow.Settings_Window); + } + else + { + // Nothing to do Here (we must be in Service Mode), without a configuration + App.FatalExceptionStopExecution("No Configuration Found in ServiceMode", false); + } + } + } + + /// + /// Handles the Main Window Exit event. + /// + private void Window_Closed(object sender, EventArgs e) + { + // * To make sure, we are not fatally closing * + if (App.s_FatalErrorOccured) + return; + + // * To make sure, do nothing in command-line mode * + if (App.State_SpecialMode_IsCommandLineSet()) + return; + + // for unexpected cleanup + s_WindowCloseGotCalled = true; + + // Stop the Process Monitor + StopMonitoring(false, false); + + // Make sure the latest DS is saved to file + App.xmlfile.SaveData(); + + if (!App.State_SpecialMode_IsRunAsServiceSet()) + { + // Close the System Tray Icon/Menu + WatchdogSysTray.WatchdogSysTray_Hide(); + + // Unload All Windows (incl. Performance) * Close Log Tail if exists Prior * + LogViewerWindow.CloseFileTailBeforeExitIfExists(); + WPFWindows.WPFWindowMaker.CloseAll(); + } + } + + /// + /// Handle Specialized Cleanup + /// + void CurrentDomain_ProcessExit(object sender, EventArgs e) + { + try + { + // Stop Internal Timers + StopTimer(); + StopSchedulerTimer(); + StopSchedulerBackupTimer(); + + if (_timer != null) + _timer.Dispose(); + + if (_timerScheduler != null) + _timerScheduler.Dispose(); + + if (_timerSchedulerBackup != null) + _timerSchedulerBackup.Dispose(); + + // Make sure that the close get's called * Unexpected cleanup * + if (!s_WindowCloseGotCalled) + Window_Closed(null, null); + } + catch (Exception) { /* ignore */ } + } + + #endregion + } +} diff --git a/@integrate/InstallComponent.cs b/@integrate/InstallComponent.cs new file mode 100644 index 0000000..a27f00d --- /dev/null +++ b/@integrate/InstallComponent.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Component.All; +using BridgeConnector.Lib.File; + +namespace Component.Binary.Mirth +{ + public class InstallComponent : IInstallComponent , ISetup + { + public const string MIRTH_SERVICE_NAME = Common_MediLytec.MediLytecPoundDef.MIRTH_SERVICE_NAME; + private const string POSTGRE_SERVICE_NAME = Common_MediLytec.MediLytecPoundDef.POSTGRE_SERVICE_NAME; + + #region IInstallComponent Members + + public ComponentConfig.Component GetComponent() + { + return Common.EmbeddedConfig.BinaryComponents.GetComponent("Mirth"); + } + + public bool BEFORE_INSTALLING_COMPONENT() + { + bool bSuccess = Common.StopService(MIRTH_SERVICE_NAME); + if (!bSuccess) + { + Common.Log.Error(String.Format("Failed to stop {0}", MIRTH_SERVICE_NAME)); + return false; + } + + string result = Common.RunCmdLine("netsh firewall set portopening tcp 8080 MirthConnectAdministrator ENABLE ALL"); + bSuccess = result.Contains("successfully") || result.Contains("Ok.") || result.Contains("The service has not been started"); + if (bSuccess) + { + result = Common.RunCmdLine("netsh firewall set portopening tcp 8443 MirthConnectServer ENABLE ALL"); + bSuccess = result.Contains("successfully") || result.Contains("Ok.") || result.Contains("The service has not been started"); + } + if (bSuccess) + { + result = Common.RunCmdLine("netsh firewall set portopening tcp 1099 MirthConnectJMX ENABLE ALL"); + bSuccess = result.Contains("successfully") || result.Contains("Ok.") || result.Contains("The service has not been started"); + } + if (!bSuccess) + { + Common.Log.Error(String.Format("Errors occured opening up ports for '{0}'", MIRTH_SERVICE_NAME)); + } + return bSuccess; + } + + public bool INSTALL_COMPONENT() + { + Common.PSetupSpwan(GetComponent().TempFileNamesNPath[0], "-q -overwrite"); + return true; + } + + public bool AFTER_INSTALLING_COMPONENT() + { + if (!Common.ServiceExists(MIRTH_SERVICE_NAME)) + { + Common.Log.Error(String.Format("Service '{0}' does not exist. Something went wrong with Setup", MIRTH_SERVICE_NAME)); + return false; + } + // Make sure Service is stopped + Common.StopService(MIRTH_SERVICE_NAME); + + // # Configure Mirth to work with PostgreSQL, if possible + if (Common.ServiceExists(POSTGRE_SERVICE_NAME)) + { + //SetupMirthToUsePostgreSQL("mirthdb", "postgres", "Clinical$1"); + } + return true; + } + + public bool SUPPORTS_UNINSTALL() + { + return false; + } + + public bool BEFORE_UNINSTALLING_COMPONENT() + { + return false; + } + + public bool UNINSTALL_COMPONENT() + { + return false; + } + + public bool AFTER_UNINSTALLING_COMPONENT() + { + return false; + } + + #endregion + + #region ISetup Members + + public void ComponentLoaded(ref SetupEvents subscribeToDesiredEvents) + { + subscribeToDesiredEvents.Before_Install += new SetupEvents.SetupEvent(subscribeToDesiredEvents_Before_Install); + subscribeToDesiredEvents.After_Install += new SetupEvents.SetupEvent(subscribeToDesiredEvents_After_Install); + subscribeToDesiredEvents.Ending_Setup += new SetupEvents.SetupEvent(subscribeToDesiredEvents_Ending_Setup); + } + + void subscribeToDesiredEvents_Ending_Setup() + { + // Make Sure at the end that both Mirth and Postgresql are started + Common.StartService(POSTGRE_SERVICE_NAME); + Common.StartService(MIRTH_SERVICE_NAME); + } + + void subscribeToDesiredEvents_Before_Install() + { + } + + void subscribeToDesiredEvents_After_Install() + { + } + + #endregion + + #region Private Helpers + + /// + /// Function is responsible fore writing Postgresql Information to the Mirth Configuration + /// * The Mirth Service needs to be stopped/started for this configuration to take affect, + /// ideally this should only be called with the mirth service stopped * + /// + private void SetupMirthToUsePostgreSQL(string dbname, string user, string password) + { + // Open Mirth Configuration + LineReplacer replacer = new LineReplacer((Common.GetProgramFilesPathOnSystemWithEndSlash() + "Mirth Connect\\conf\\mirth.properties"), Encoding.ASCII); + + LineReplace_Rule DBurl = new LineReplace_Rule(); + DBurl.StartsWith = "database.url ="; + DBurl.ReplaceLineWith = String.Format("database.url = jdbc:postgresql://localhost:5432/{0}", dbname); + DBurl.Comparer = LineReplace_ComparerModifier.None; + replacer.AddUpdateRule("DBurl", DBurl); + + LineReplace_Rule DBuser = new LineReplace_Rule(); + DBuser.StartsWith = "database.username ="; + DBuser.ReplaceLineWith = String.Format("database.username = {0}", user); + DBuser.Comparer = LineReplace_ComparerModifier.None; + replacer.AddUpdateRule("DBuser", DBuser); + + LineReplace_Rule DBpass = new LineReplace_Rule(); + DBpass.StartsWith = "database.password ="; + DBpass.ReplaceLineWith = String.Format("database.password = {0}", password); + DBpass.Comparer = LineReplace_ComparerModifier.None; + replacer.AddUpdateRule("DBpass", DBpass); + + // Replace Lines in Mirth Configuration + replacer.ReplaceLines(); + } + + #endregion + } +} diff --git a/@integrate/InstallComponent2.cs b/@integrate/InstallComponent2.cs new file mode 100644 index 0000000..33b229d --- /dev/null +++ b/@integrate/InstallComponent2.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Component.All; +using Microsoft.Win32; + +namespace Component.Binary.Java +{ + public class InstallComponent : IInstallComponent , ISetup + { + #region IInstallComponent Members + + /// + /// Get the Component bound to this IInstallComponent + /// + /// + public ComponentConfig.Component GetComponent() + { + return Common.EmbeddedConfig.BinaryComponents.GetComponent("Java"); + } + + /// + /// We only want to install Java if an earlier version is installed + /// + /// true, if an earlier version or none is installed, false otherwise + public bool BEFORE_INSTALLING_COMPONENT() + { + // If we get here, just in case always just install the java version + // to make sure everything works as expected + + //string Version = GetLatestJavaVersionFromTheRegistry(); + //if (!String.IsNullOrEmpty(Version)) + //{ + // int nCompare = String.Compare(Version, GetComponent().Version); + // if (nCompare < 0) + // return true; + // else + // return false; + //} + return true; + } + + /// + /// Installs Java on the system + /// + /// Returns true if the Component installs, false otherwise + public bool INSTALL_COMPONENT() + { + Common.PSetupSpwan(GetComponent().TempFileNamesNPath[0], "/s /v \"/qn ADDLOCAL=ALL IEXPLORER=1 REBOOT=Supress\""); + return true; + } + + /// + /// Make sure that the version in the registry matches the component + /// + /// true, if the version in the registry matches what we installed, false otherwise + public bool AFTER_INSTALLING_COMPONENT() + { + string Version = GetLatestJavaVersionFromTheRegistry(); + if (!String.IsNullOrEmpty(Version)) + { + int nCompare = String.Compare(GetComponent().Version, Version); + if (nCompare != 0) + return false; + else + return true; + } + return false; + } + + /// + /// Check to see, if this component supports uninstalling + /// + /// true, if uninstall is supported, false otherwise + public bool SUPPORTS_UNINSTALL() + { + return true; + } + + /// + /// Check if we can uninstall the java version + /// + /// true, if version is installed, false otherwise + public bool BEFORE_UNINSTALLING_COMPONENT() + { + string[] Versions = GetAllJavaVersionFromTheRegistry(); + if (Versions != null && Versions.Length > 0) + { + if (Versions.Contains(GetComponent().Version)) + return true; + } + return false; + } + + /// + /// Uninstalls Java on the system + /// + /// Returns true if the Component installs, false otherwise + public bool UNINSTALL_COMPONENT() + { + Common.PSetupSpwan(GetComponent().TempFileNamesNPath[0], "/s /v \"/qn REBOOT=Supress\" /x"); + return true; + } + + /// + /// Check to make sure that the version we uninstalled doesn't exist anymore + /// + /// true if version was removed, false otherwise + public bool AFTER_UNINSTALLING_COMPONENT() + { + string[] Versions = GetAllJavaVersionFromTheRegistry(); + if (Versions != null && Versions.Length > 0) + { + if (Versions.Contains(GetComponent().Version)) + return false; + } + return true; + } + + #endregion + + #region ISetup Members + + public void ComponentLoaded(ref SetupEvents subscribeToDesiredEvents) + { + subscribeToDesiredEvents.Before_Install += new SetupEvents.SetupEvent(subscribeToDesiredEvents_Before_Install); + subscribeToDesiredEvents.After_Install += new SetupEvents.SetupEvent(subscribeToDesiredEvents_After_Install); + } + + void subscribeToDesiredEvents_Before_Install() + { + } + + void subscribeToDesiredEvents_After_Install() + { + } + + #endregion + + #region Private Helpers + + /// + /// Use this function to check the java version in the registry + /// + /// The Java Version found in the registry or String.Empty, if not found + string GetLatestJavaVersionFromTheRegistry() + { + string[] Versions = GetAllJavaVersionFromTheRegistry(); + if (Versions != null && Versions.Length > 0) + { + string strVersion = Versions[Versions.Length - 1]; + if (!String.IsNullOrEmpty(strVersion)) + return strVersion; + } + return String.Empty; + } + + /// + /// Use this function to check all the java versions in the registry + /// + /// The Java Versions found in the registry or null, if none found + string[] GetAllJavaVersionFromTheRegistry() + { + bool bIsJavaInstalled = false; + try + { + RegistryKey reg = Registry.LocalMachine.OpenSubKey("Software\\JavaSoft\\Java Plug-in", false); + bIsJavaInstalled = (reg != null); + if(reg == null) + reg = Registry.LocalMachine.OpenSubKey("Software\\Wow6432Node\\JavaSoft\\Java Plug-in", false); + bIsJavaInstalled = (reg != null); + if (bIsJavaInstalled) + { + string[] SubKeys = reg.GetSubKeyNames(); + if (SubKeys != null && SubKeys.Length > 0) + return SubKeys; + } + } + catch (Exception) { /* ignore */ } + return null; + } + + #endregion + } +} diff --git a/@integrate/InstallComponent4.cs b/@integrate/InstallComponent4.cs new file mode 100644 index 0000000..1e55c09 --- /dev/null +++ b/@integrate/InstallComponent4.cs @@ -0,0 +1,254 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Component.All; +using Microsoft.Win32; +using BridgeConnector.Lib.Assembly; +using System.IO; +using BridgeConnector.Lib.XML; +using BridgeConnector; + +namespace Component.Setup.BridgeService +{ + public class InstallComponent : IInstallComponent , ISetup + { + #region Private Helpers + + public const string BRIDGE_SERVICE_TITLE = Common_MediLytec.MediLytecPoundDef.BRIDGE_SERVICE_TITLE; + public const string BRIDGE_SERVICE_ASSEMBLY = Common_MediLytec.MediLytecPoundDef.BRIDGE_SERVICE_ASSEMBLY; + public const string BRIDGE_SERVICE_NAME = Common_MediLytec.MediLytecPoundDef.BRIDGE_SERVICE_NAME; + + /// + /// Write Service Configuration to the ImagePath for the Service + /// + /// + public static bool WriteServiceConfigurationToServiceInRegistry(InstallConfig config) + { + try + { + using (RegistryKey service = Registry.LocalMachine.OpenSubKey("System\\CurrentControlSet\\services\\" + BRIDGE_SERVICE_TITLE, true)) + { + // # Determine Image Path Parameter + string ImagePathParameter = ""; + switch (config) + { + case InstallConfig.LytecMD: + ImagePathParameter = " /Lytec"; + break; + + case InstallConfig.MedisoftClinical: + ImagePathParameter = " /Medisoft"; + break; + } + + // # Set Image Path Parameter + string serviceAssembly = "\"" + AssemblyW.SpecializedAssemblyInfo.GetAssemblyPath(AssemblyW.AssemblyST.Executing) + "\\" + BRIDGE_SERVICE_ASSEMBLY + "\""; + service.SetValue("ImagePath", serviceAssembly + ImagePathParameter); + return true; + } + } + catch (Exception) { /* ignore */ } + return false; + } + + #endregion + + #region IInstallComponent Members + + public ComponentConfig.Component GetComponent() + { + return Common.EmbeddedConfig.SetupComponents.GetComponent("BridgeService"); + } + + public bool BEFORE_INSTALLING_COMPONENT() + { + // Stop Service BRIDGE_SERVICE_NAME + if (!Common.StopService(BRIDGE_SERVICE_NAME)) + { + Common.Log.Error(String.Format("Couldn't stop the {0} Service.", BRIDGE_SERVICE_NAME)); + return false; + } + + // Read the Service Configuration from the Registry + InstallConfig config = Common_MediLytec.RetrieveInstallConfigFromRegistry(); + if (config == InstallConfig.LytecMD) + { + bool bSuccess = false; + string result = Common.RunCmdLine("netsh firewall set portopening tcp 5000 BridgeService_LytecBridgeIn ENABLE ALL"); + bSuccess = result.Contains("successfully") || result.Contains("Ok.") || result.Contains("The service has not been started"); + if (bSuccess) + { + result = Common.RunCmdLine("netsh firewall set portopening tcp 5001 BridgeService_LytecMirthOut ENABLE ALL"); + bSuccess = result.Contains("successfully") || result.Contains("Ok.") || result.Contains("The service has not been started"); + } + if (bSuccess) + { + return true; + } + else + { + Common.Log.Error(String.Format("Opening up ports for Lytec '{0}' failed", BRIDGE_SERVICE_NAME)); + return false; + } + } + else if (config == InstallConfig.MedisoftClinical) + { + bool bSuccess = false; + string result = Common.RunCmdLine("netsh firewall set portopening tcp 7000 BridgeService_MedisoftBridgeIn ENABLE ALL"); + bSuccess = result.Contains("successfully") || result.Contains("Ok.") || result.Contains("The service has not been started"); + if (bSuccess) + { + result = Common.RunCmdLine("netsh firewall set portopening tcp 7001 BridgeService_MedisoftMirthOut ENABLE ALL"); + bSuccess = result.Contains("successfully") || result.Contains("Ok.") || result.Contains("The service has not been started"); + } + if (bSuccess) + { + return true; + } + else + { + Common.Log.Error(String.Format("Opening up ports for Medisoft '{0}' failed", BRIDGE_SERVICE_NAME)); + return false; + } + } + return true; + } + + /// + /// + /// + /// + public bool INSTALL_COMPONENT() + { + // Uninstall Bridge Service, if it exists, and re-install + if (Common.ServiceExists(BRIDGE_SERVICE_NAME)) + UNINSTALL_COMPONENT(); + + string installUtil = Common.GetNetFrameworkUtilFileNameNPathFile("installutil.exe"); + string serviceAssembly = AssemblyW.SpecializedAssemblyInfo.GetAssemblyPath(AssemblyW.AssemblyST.Entry) + "\\" + BRIDGE_SERVICE_ASSEMBLY; + if (!String.IsNullOrEmpty(installUtil)) + { + Common.StopService(BRIDGE_SERVICE_NAME); + string result = Common.RunCmdLine(installUtil + " \"" + serviceAssembly + "\""); + bool bSuccess = !result.Contains("failed"); + if (bSuccess) + { + // Write the Service Configuration to the Registry + InstallConfig config = Common_MediLytec.RetrieveInstallConfigFromRegistry(); + bSuccess = WriteServiceConfigurationToServiceInRegistry(config); + } + if (!bSuccess) + { + Common.Log.Error(String.Format("Errors Occured installing {0}.", BRIDGE_SERVICE_NAME)); + } + return bSuccess; + } + return false; + } + + public bool AFTER_INSTALLING_COMPONENT() + { + // Make sure service exists + if (!Common.ServiceExists(BRIDGE_SERVICE_NAME)) + { + Common.Log.Error(String.Format("Service {0} does Not Exist. Install Failed", BRIDGE_SERVICE_NAME)); + return false; + } + + // Make sure Service is stopped + Common.StopService(BRIDGE_SERVICE_NAME); + return true; + } + + public bool SUPPORTS_UNINSTALL() + { + return false; + } + + public bool BEFORE_UNINSTALLING_COMPONENT() + { + // Stop Service BRIDGE_SERVICE_NAME + if (!Common.StopService(BRIDGE_SERVICE_NAME)) + { + Common.Log.Error(String.Format("Couldn't stop the {0} Service.", BRIDGE_SERVICE_NAME)); + return false; + } + return true; + } + + public bool UNINSTALL_COMPONENT() + { + if (Common.ServiceExists(BRIDGE_SERVICE_NAME)) + { + Common.StopService(BRIDGE_SERVICE_NAME); + string installUtil = Common.GetNetFrameworkUtilFileNameNPathFile("installutil.exe"); + string serviceAssembly = AssemblyW.SpecializedAssemblyInfo.GetAssemblyPath(AssemblyW.AssemblyST.Entry) + "\\" + BRIDGE_SERVICE_ASSEMBLY; + if (!String.IsNullOrEmpty(installUtil)) + { + string result = Common.RunCmdLine(installUtil + " /u \"" + serviceAssembly + "\""); + bool bSuccess = !result.Contains("failed"); + if(!bSuccess) + Common.Log.Error(String.Format("Errors Occured uninstalling {0}.", BRIDGE_SERVICE_NAME)); + return bSuccess; + } + } + return false; + } + + public bool AFTER_UNINSTALLING_COMPONENT() + { + if (Common.ServiceExists(BRIDGE_SERVICE_NAME)) + { + Common.Log.Error(String.Format("Service {0} Still Exists. Uninstall Failed", BRIDGE_SERVICE_NAME)); + return false; + } + return true; + } + + #endregion + + #region ISetup Members + + public void ComponentLoaded(ref SetupEvents subscribeToDesiredEvents) + { + subscribeToDesiredEvents.Before_Install += new SetupEvents.SetupEvent(subscribeToDesiredEvents_Before_Install); + subscribeToDesiredEvents.After_Install += new SetupEvents.SetupEvent(subscribeToDesiredEvents_After_Install); + subscribeToDesiredEvents.Ending_Setup += new SetupEvents.SetupEvent(subscribeToDesiredEvents_Ending_Setup); + } + + void subscribeToDesiredEvents_Ending_Setup() + { + if (Common.ServiceExists(BRIDGE_SERVICE_NAME)) + { + string path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\McKesson\\Bridge\\BridgeConfig.xml"; + if (File.Exists(path)) + { + XSerializer serialize = new XSerializer(); + XMLConfig config = serialize.ReadFromFile(path); + if (config != null) + { + // Read the Service Configuration from the Registry + InstallConfig sconfig = Common_MediLytec.RetrieveInstallConfigFromRegistry(); + if (sconfig == InstallConfig.LytecMD && !String.IsNullOrEmpty(config.DefaultMapping.Lytec)) + Common.StartService(BRIDGE_SERVICE_NAME); + else if(sconfig == InstallConfig.MedisoftClinical && !String.IsNullOrEmpty(config.DefaultMapping.Medisoft)) + Common.StartService(BRIDGE_SERVICE_NAME); + } + } + } + } + + void subscribeToDesiredEvents_Before_Install() + { + + } + + void subscribeToDesiredEvents_After_Install() + { + + } + + #endregion + } +} diff --git a/@integrate/Logging.cs b/@integrate/Logging.cs new file mode 100644 index 0000000..f8e7722 --- /dev/null +++ b/@integrate/Logging.cs @@ -0,0 +1,240 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +// Log4Net Declarations +using log4net.Appender; +using log4net.Core; +using log4net.Layout; +using log4net.Config; +using log4net; +using Diag = System.Diagnostics; +using BridgeConnector.Lib.Other; + +namespace BridgeConnector.Lib.File +{ + /// + /// Logging Detail + /// + public enum Logging_Detail + { + NONE, + ERROR, + INFO, + DEBUG + } + + /// + /// Used to Configure a Logger Instance in the Logging Class via AddGlobalLoggerConfiguration() + /// + public struct Logging_Configuration + { + public string LogFileNameNPath; + public Logging_Detail Detail; + public bool UseExclusiveFileLock; + public int maxFileSizeInMB; + public int numOfBackupLogFiles; + public string PatternLayout; + } + + /// + /// Is a Wrapper Object around Log4Net's Rolling File Appender. + /// Use it by calling AddGlobalLoggerConfiguration() with a valid Logging Configuration. + /// You can configure multipe Logger Instances distinguished by Name. + /// subsequent calls can call GetLogger() with the named Logger instance to receive a valid logger object + /// + public class Logging + { + #region Private Static Members + + private static Dictionary _loggerConfigurationMap = new Dictionary(); + private static Dictionary _loggerObjects = new Dictionary(); + private static bool s_IsVSHosted = false; + + #endregion + + #region Private Construction + + private Logging() { s_IsVSHosted = Diag.Process.GetCurrentProcess().ProcessName.Contains("vshost"); } + + #endregion + + #region Internal Members + + internal ILog _Log4NetLog = null; // Initialized by GetLogger() + + #endregion + + #region Public Log Methods + + public void Info(object message) { if (_Log4NetLog != null) _Log4NetLog.Info(MessageHeader() + message); } + public void Info(object message, int nPlusMinus) { if (_Log4NetLog != null) _Log4NetLog.Info(MessageHeader(nPlusMinus) + message); } + public void Info(object message, Exception exception) { if (_Log4NetLog != null) _Log4NetLog.Info(MessageHeader() + message, exception); } + + public void Debug(object message) { if (_Log4NetLog != null) _Log4NetLog.Debug(MessageHeader() + message); } + public void Debug(object message, int nPlusMinus) { if (_Log4NetLog != null) _Log4NetLog.Debug(MessageHeader(nPlusMinus) + message); } + public void Debug(object message, Exception exception) { if (_Log4NetLog != null) _Log4NetLog.Debug(MessageHeader() + message, exception); } + + public void Error(object message) { if (_Log4NetLog != null) _Log4NetLog.Error(MessageHeader() + message); } + public void Error(object message, int nPlusMinus) { if (_Log4NetLog != null) _Log4NetLog.Error(MessageHeader(nPlusMinus) + message); } + public void Error(object message, Exception exception) { if (_Log4NetLog != null) _Log4NetLog.Error(MessageHeader() + message, exception); } + + public void Fatal(object message) { if (_Log4NetLog != null) _Log4NetLog.Fatal(MessageHeader() + message); } + public void Fatal(object message, int nPlusMinus) { if (_Log4NetLog != null) _Log4NetLog.Fatal(MessageHeader(nPlusMinus) + message); } + public void Fatal(object message, Exception exception) { if (_Log4NetLog != null) _Log4NetLog.Fatal(MessageHeader() + message, exception); } + + /// + /// Message Header to be shown on every log message + /// + private string MessageHeader() + { + if (s_IsVSHosted) + return MessageHeader(0); + else + return StackWalker.GetMethodNameFromStack(-2) + "()- "; + } + + /// + /// Message Header to be shown on every log message + /// + /// Use this to add/substract from the base stack level you want to retrieve + private string MessageHeader(int nPlusMinus) + { + // When Running this from via VS it behaves differently then when + // running it outside of it, when running it regularly, each foreach loop + // is it's own stackframe! ~weird but true. Only use the nPlusMinus when not running + // inside VS + if(s_IsVSHosted) + return StackWalker.GetMethodNameFromStack(nPlusMinus) + "()- "; + else + return StackWalker.GetMethodNameFromStack(nPlusMinus - 1) + "()- "; + } + + #endregion + + #region Public Static Configuration and Logger Creation Methods + + /// + /// Used to add a new Configuration and Logger Instance onto a Static Map. + /// Will create one logger instance per unique name. + /// + /// a name for the logger instance + /// a valid configuration to use on the instance + /// a logging object that can be used to log + public static Logging AddGlobalLoggerConfiguration(string Name, Logging_Configuration Configuration) + { + // Must have a Valid Input + if (string.IsNullOrEmpty(Name)) + throw new ArgumentException("Name Is Invalid"); + + if (!_loggerObjects.Keys.Contains(Name.ToLower())) + { + // Create the Repository + log4net.Repository.ILoggerRepository repository = LogManager.CreateRepository(Name.ToLower()); + + // Create FileAppender Configuration + RollingFileAppender appender = RollingFileAppenderCreator(Configuration); + + // Run the Configuration against the Repository + BasicConfigurator.Configure(repository, appender); + + // Add Configuration to our Static Map + _loggerConfigurationMap[Name.ToLower()] = Configuration; + + // Last, but not least, Create the new Logging Instance Object and Store it + _loggerObjects[Name.ToLower()] = LogManager.GetLogger(Name.ToLower(), Name.ToLower()); + } + + // Let the Caller get the Logging Object + return GetLogger(Name); + } + + /// + /// Used to retrieve a named logging instance that has already been created via a previous call + /// to AddGlobalLoggerConfiguration(). + /// + /// a name for a previously created Logging instance + /// a Logging object that can be used to log + public static Logging GetLogger(string Name) + { + if (_loggerObjects.Keys.Contains(Name.ToLower())) + { + Logging logger = new Logging(); + logger._Log4NetLog = _loggerObjects[Name.ToLower()]; + return logger; + } + else + throw new ArgumentException("Must call AddGlobalLoggerConfiguration() with a Configuration Before calling this Function"); + } + + #endregion + + #region Private Static Helper Methods + + /// + /// Creates a Log4Net RollingFileAppender with the specified configuration + /// + /// a valid configuration + /// a Log4Net RollingFileAppender Object + private static RollingFileAppender RollingFileAppenderCreator(Logging_Configuration config) + { + #region Input Validation + + if (config.maxFileSizeInMB <= 0) + throw new ArgumentException("Logging_Configuration - Invalid maxFileSizeInMB"); + + if (config.numOfBackupLogFiles < 0) + throw new ArgumentException("Logging_Configuration - Invalid numOfBackupLogFiles"); + + if (String.IsNullOrEmpty(config.LogFileNameNPath)) + throw new Exception("Logging_Configuration - Invalid LogFileNameNPath"); + + if (!Directory.Exists(Path.GetDirectoryName(config.LogFileNameNPath))) + Directory.CreateDirectory(Path.GetDirectoryName(config.LogFileNameNPath)); + + #endregion + + // Create and Set Layout for FileAppender + RollingFileAppender rfAppender = new RollingFileAppender(); + if (!String.IsNullOrEmpty(config.PatternLayout)) + rfAppender.Layout = new PatternLayout(config.PatternLayout); + else + rfAppender.Layout = new PatternLayout("%date{dd MMM HH:mm:ss,fff} [%thread] %level - %message%newline"); + //rfAppender.Layout = new PatternLayout("%date{dd MMM yyyy HH:mm:ss,fff} [%thread] %level %logger - %message%newline"); + + // Locking Minimal allows us to run Log4Net from multiple processes + if (config.UseExclusiveFileLock) + rfAppender.LockingModel = new FileAppender.ExclusiveLock(); + else + rfAppender.LockingModel = new FileAppender.MinimalLock(); + + // Configure FileName and always set Appent to true + rfAppender.File = config.LogFileNameNPath; + rfAppender.AppendToFile = true; + + // According to the listings on the blog site + // http://blog.aggregatedintelligence.com/2009/08/log4net-logging-levels-available.html + // Error, will log Error, Fatal + // Info, will log Info, Error, and Fatal + // Debug, will log Info, Error, Fatal and Debug + if (config.Detail == Logging_Detail.NONE) + rfAppender.Threshold = Level.Off; + else if (config.Detail == Logging_Detail.ERROR) + rfAppender.Threshold = Level.Error; + else if (config.Detail == Logging_Detail.INFO) + rfAppender.Threshold = Level.Info; + else if (config.Detail == Logging_Detail.DEBUG) + rfAppender.Threshold = Level.Debug; + + rfAppender.MaximumFileSize = String.Format("{0}MB", config.maxFileSizeInMB); + rfAppender.MaxSizeRollBackups = config.numOfBackupLogFiles; + rfAppender.RollingStyle = RollingFileAppender.RollingMode.Size; // Setting to RollingMode.Size will make MaxSizeRollBackups work + rfAppender.ActivateOptions(); + return rfAppender; + } + + #endregion + } +} diff --git a/@integrate/MsgBox.cs b/@integrate/MsgBox.cs new file mode 100644 index 0000000..de00ef5 --- /dev/null +++ b/@integrate/MsgBox.cs @@ -0,0 +1,482 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Watchdog.WatchdogLib.Win32; +using System.Windows.Forms; +using System.Drawing; +using System.ComponentModel; +using WPFWin = System.Windows; +using WPFWinInterop = System.Windows.Interop; + +namespace Watchdog.WatchdogLib.WinForms +{ + /// + /// MsgBox is a WindowsForms MessageBox that can be centered to the Parent + /// + public static class MsgBox + { + #region Private Static Members + + private static User32.WindowsHookProc _hookProcDelegate = null; + private static int _hHook = 0; + private static string _title = null; + private static string _msg = null; + private static IntPtr _hIcon = IntPtr.Zero; + private static bool _IsDesktopOwner = false; + private static bool _ShowFatal = false; + + // Delegate to make All Message Boxes Thread-Safe + private delegate DialogResult ShowMsgBoxDelegate(IWin32Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon, Icon TitleBarIcon); + + #endregion + + #region Public Properties + + public static Icon DefaultTitleBarIcon { get; set; } + public static uint MaxNumberOfCharactersPerLine { get; set; } + public const uint DefaultNumberOfCharactersPerLine = 62; + + #endregion + + #region Public Header Properties + + public static bool ShowHeader { get; set; } + public static string MsgBox_ErrorHeader { get; set; } + public static string MsgBox_FatalErrorHeader { get; set; } + public static string MsgBox_WarningHeader { get; set; } + public static string MsgBox_InfoHeader { get; set; } + + #endregion + + #region Public Footer Properties + + public static bool ShowFooter { get; set; } + public static string MsgBox_ErrorFooter { get; set; } + public static string MsgBox_FatalErrorFooter { get; set; } + public static string MsgBox_WarningFooter { get; set; } + public static string MsgBox_InfoFooter { get; set; } + + #endregion + + #region Public Title Header Properties + + public static bool ShowTitleHeader { get; set; } + public static string MsgBox_ErrorTitleHeader { get; set; } + public static string MsgBox_FatalErrorTitleHeader { get; set; } + public static string MsgBox_WarningTitleHeader { get; set; } + public static string MsgBox_InfoTitleHeader { get; set; } + + #endregion + + #region Construction + + static MsgBox() + { + DefaultTitleBarIcon = null; + MaxNumberOfCharactersPerLine = DefaultNumberOfCharactersPerLine; + + // Header Init + MsgBox_FatalErrorHeader = String.Empty; + MsgBox_ErrorHeader = String.Empty; + MsgBox_WarningHeader = String.Empty; + MsgBox_InfoHeader = String.Empty; + ShowHeader = true; + + // Footer Init + MsgBox_FatalErrorFooter = String.Empty; + MsgBox_ErrorFooter = String.Empty; + MsgBox_WarningFooter = String.Empty; + MsgBox_InfoFooter = String.Empty; + ShowFooter = true; + + // Title Header Init + MsgBox_FatalErrorTitleHeader = String.Empty; + MsgBox_ErrorTitleHeader = String.Empty; + MsgBox_WarningTitleHeader = String.Empty; + MsgBox_InfoTitleHeader = String.Empty; + ShowTitleHeader = true; + } + + #endregion + + #region Public Static Methods + + /// + /// Shows a custom Message Box (centered to parent), with the DefaultTitleBarIcon + /// + public static DialogResult Show(WPFWin.Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon) + { + if (parent != null) + { + WPFWinInterop.WindowInteropHelper interop = new WPFWinInterop.WindowInteropHelper(parent); + if(interop.Handle != IntPtr.Zero) + return Show(Watchdog.WatchdogLib.Win32.Convert.ConverthWndToIWin32Window(interop.Handle), Title, Text, MsgBoxButtons, MsgBoxIcon, DefaultTitleBarIcon); + } + return Show(Functions.GetDestopWindow(), Title, Text, MsgBoxButtons, MsgBoxIcon, DefaultTitleBarIcon); + } + + /// + /// Shows a custom Message Box (centered to parent), with the DefaultTitleBarIcon + /// + public static DialogResult Show(IWin32Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon) + { + return Show(parent, Title, Text, MsgBoxButtons, MsgBoxIcon, DefaultTitleBarIcon); + } + + /// + /// Shows a custom Message Box (centered to Desktop), with the DefaultTitleBarIcon + /// + public static DialogResult Show(String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon) + { + return Show(Functions.GetDestopWindow(), Title, Text, MsgBoxButtons, MsgBoxIcon, DefaultTitleBarIcon); + } + + /// + /// *Main MessageBox Show Function* allows you to center the Message Box to the Parent + /// + /// Result of Dialog + public static DialogResult Show(IWin32Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon, Icon TitleBarIcon) + { + ISynchronizeInvoke InvokeObject = null; + if (parent != null && parent is ISynchronizeInvoke) + InvokeObject = (ISynchronizeInvoke)parent; + + // Invoke if we need to * Make MessageBoxes generally Thread-safe * + if ((InvokeObject != null) && InvokeObject.InvokeRequired) + { + DialogResult result = (DialogResult)InvokeObject.Invoke(new ShowMsgBoxDelegate(MsgBox.Show), new object[] { parent, Title, Text, MsgBoxButtons, MsgBoxIcon, TitleBarIcon }); + return result; + } + else + { + return MsgBox.ShowInternal(parent, Text, Title, MsgBoxButtons, MsgBoxIcon, TitleBarIcon); + } + } + + #endregion + + #region Public Static Methods Extended + + /// + /// Shows Warning MessageBox + /// + //public static DialogResult ShowWarning(IWin32Window parent, String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + public static DialogResult ShowWarning(IWin32Window parent, String Text, String Title, MessageBoxButtons MsgBoxButtons) + { + return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Exclamation); + } + + /// + /// Shows Warning MessageBox + /// + //public static DialogResult ShowWarning(String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + public static DialogResult ShowWarning(String Text, String Title, MessageBoxButtons MsgBoxButtons) + { + return Show(Title, Text, MsgBoxButtons, MessageBoxIcon.Exclamation); + } + + /// + /// Shows Fatal Error MessageBox + /// + //public static DialogResult ShowFatalError(IWin32Window parent, String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + public static DialogResult ShowFatalError(IWin32Window parent, String Text, String Title, MessageBoxButtons MsgBoxButtons) + { + _ShowFatal = true; + DialogResult dr = Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Error); + _ShowFatal = false; + return dr; + } + + /// + /// Shows Fatal Error MessageBox + /// + //public static DialogResult ShowFatalError(String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + public static DialogResult ShowFatalError(String Text, String Title, MessageBoxButtons MsgBoxButtons) + { + _ShowFatal = true; + DialogResult dr = Show(Title, Text, MsgBoxButtons, MessageBoxIcon.Error); + _ShowFatal = false; + return dr; + } + + /// + /// Shows Error MessageBox + /// + //public static DialogResult ShowError(IWin32Window parent, String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + public static DialogResult ShowError(IWin32Window parent, String Text, String Title, MessageBoxButtons MsgBoxButtons) + { + return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Error); + } + + /// + /// Shows Error MessageBox + /// + //public static DialogResult ShowError(String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + public static DialogResult ShowError(String Text, String Title, MessageBoxButtons MsgBoxButtons) + { + return Show(Title, Text, MsgBoxButtons, MessageBoxIcon.Error); + } + + /// + /// Shows Information MessageBox + /// + //public static DialogResult ShowInfo(IWin32Window parent, String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + public static DialogResult ShowInfo(IWin32Window parent, String Text, String Title, MessageBoxButtons MsgBoxButtons) + { + return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Information); + } + + /// + /// Shows Information MessageBox + /// + //public static DialogResult ShowInfo(String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + public static DialogResult ShowInfo(String Text, String Title, MessageBoxButtons MsgBoxButtons) + { + return Show(Title, Text, MsgBoxButtons, MessageBoxIcon.Information); + } + + /// + /// Shows Default MessageBox + /// + //public static DialogResult ShowDefault(IWin32Window parent, String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + public static DialogResult ShowDefault(IWin32Window parent, String Text, String Title, MessageBoxButtons MsgBoxButtons) + { + return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.None); + } + + /// + /// Shows Default MessageBox + /// + //public static DialogResult ShowDefault(String Text, String Title = "", MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + public static DialogResult ShowDefault(String Text, String Title, MessageBoxButtons MsgBoxButtons) + { + return Show(Title, Text, MsgBoxButtons, MessageBoxIcon.None); + } + + #endregion + + #region Private Methods + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + private static DialogResult ShowInternal(IWin32Window owner, string msg, string title, MessageBoxButtons btns, MessageBoxIcon icon, Icon TitleBarIcon) + { + // Create a callback delegate + _hookProcDelegate = new User32.WindowsHookProc(HookCallback); + + #region Header Action + if (ShowHeader) + { + if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_FatalErrorHeader) && _ShowFatal) + msg = MsgBox_FatalErrorHeader + msg; + else if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_ErrorHeader) && !_ShowFatal) + msg = MsgBox_ErrorHeader + msg; + else if (icon == MessageBoxIcon.Exclamation && !String.IsNullOrEmpty(MsgBox_WarningHeader)) + msg = MsgBox_WarningHeader + msg; + else if (icon == MessageBoxIcon.Information && !String.IsNullOrEmpty(MsgBox_InfoHeader)) + msg = MsgBox_InfoHeader + msg; + } + #endregion + + #region Footer Action + if (ShowFooter) + { + if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_FatalErrorFooter) && _ShowFatal) + msg = msg + MsgBox_FatalErrorFooter; + else if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_ErrorFooter) && !_ShowFatal) + msg = msg + MsgBox_ErrorFooter; + else if (icon == MessageBoxIcon.Exclamation && !String.IsNullOrEmpty(MsgBox_WarningFooter)) + msg = msg + MsgBox_WarningFooter; + else if (icon == MessageBoxIcon.Information && !String.IsNullOrEmpty(MsgBox_InfoFooter)) + msg = msg + MsgBox_InfoFooter; + } + #endregion + + #region Title Header + if (ShowTitleHeader) + { + if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_FatalErrorTitleHeader) && _ShowFatal) + title = MsgBox_FatalErrorTitleHeader + ((!String.IsNullOrEmpty(title)) ? (" (" + title + ")") : ""); + else if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_ErrorTitleHeader) && !_ShowFatal) + title = MsgBox_ErrorTitleHeader + ((!String.IsNullOrEmpty(title)) ? (" (" + title + ")") : ""); + else if (icon == MessageBoxIcon.Exclamation && !String.IsNullOrEmpty(MsgBox_WarningTitleHeader)) + title = MsgBox_WarningTitleHeader + ((!String.IsNullOrEmpty(title)) ? (" (" + title + ")") : ""); + else if (icon == MessageBoxIcon.Information && !String.IsNullOrEmpty(MsgBox_InfoTitleHeader)) + title = MsgBox_InfoTitleHeader + ((!String.IsNullOrEmpty(title)) ? (" (" + title + ")") : ""); + } + #endregion + + #region Text Padding + + // Stripe last \n, if exists + if (!String.IsNullOrEmpty(msg) && (msg.Length > 0) && (msg[msg.Length - 1] == '\n')) + msg = msg.Remove(msg.Length - 1); + + // Make sure the text looks good, by using padding + if (!String.IsNullOrEmpty(msg) && (msg.Length > 0) && (msg.Length < MaxNumberOfCharactersPerLine)) + { + string[] lines = msg.Split('\n'); + StringBuilder sb = new StringBuilder(); + foreach (string line in lines) + { + sb.Append(line.PadRight((int)MaxNumberOfCharactersPerLine)); + sb.Append("\n"); + } + msg = sb.ToString(); + } + else if (!String.IsNullOrEmpty(msg) && (msg.Length > 0) && (msg.Length > MaxNumberOfCharactersPerLine)) + { + // Incredible and amazing Padding code + string[] lines = msg.Split('\n'); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < lines.Length; ++i) + { + if (lines[i].Length < MaxNumberOfCharactersPerLine) + { + sb.Append(lines[i].PadRight((int)MaxNumberOfCharactersPerLine)); + sb.Append("\n"); + } + else if (lines[i].Length > MaxNumberOfCharactersPerLine) + { + // truncate + for (int j = 0; j < lines[i].Length; j = j + (int)MaxNumberOfCharactersPerLine) + { + string strSub = lines[i].Substring(j, ((lines[i].Length - j) > (int)MaxNumberOfCharactersPerLine) ? (int)MaxNumberOfCharactersPerLine : (lines[i].Length - j)); + if (strSub.Length == (int)MaxNumberOfCharactersPerLine) + { + sb.Append(strSub); + sb.Append("\n"); + } + else + { + sb.Append(strSub.PadRight((int)MaxNumberOfCharactersPerLine)); + sb.Append("\n"); + } + } + } + else + { + sb.Append(lines[i]); + sb.Append("\n"); + } + } + + // Write nicely formatted Message out + msg = sb.ToString(); + } + else + { + // do nothing, string is miracioulsy exactly correct + } + #endregion + + // Remember the title & message that we'll look for. + // The hook sees *all* windows, so we need to make sure we operate on the right one. + _msg = msg; + _title = title; + + // if Owner is the Desktop Window + _IsDesktopOwner = (owner.Handle == Functions.GetDestopWindow().Handle); + + // Icon could not be present + if (TitleBarIcon != null) + _hIcon = TitleBarIcon.ToBitmap().GetHicon(); + + // Set the hook. + // Suppress "GetCurrentThreadId() is deprecated" warning. + // It's documented that Thread.ManagedThreadId doesn't work with SetWindowsHookEx() +#pragma warning disable 0618 + _hHook = User32.SetWindowsHookEx(Definitions.WH_CBT, _hookProcDelegate, IntPtr.Zero, AppDomain.GetCurrentThreadId()); +#pragma warning restore 0618 + + // Pop a standard MessageBox. The hook will center it. + DialogResult rslt = DialogResult.None; + if (_IsDesktopOwner) + rslt = MessageBox.Show(_msg, _title, btns, icon); + else + rslt = MessageBox.Show(owner, _msg, _title, btns, icon); + + // Release hook, clean up (may have already occurred) + Unhook(); + + return rslt; + } + + private static void Unhook() + { + User32.UnhookWindowsHookEx(_hHook); + _hHook = 0; + _hookProcDelegate = null; + _msg = null; + _title = null; + } + + private static int HookCallback(int code, IntPtr wParam, IntPtr lParam) + { + int hHook = _hHook; // Local copy for CallNextHookEx() JIC we release _hHook + + // Look for HCBT_ACTIVATE, *not* HCBT_CREATEWND: + // child controls haven't yet been created upon HCBT_CREATEWND. + if (code == Definitions.HCBT_ACTIVATE) + { + string cls = Functions.GetWindowClassName(wParam); + if (cls == "#32770") // MessageBoxes are Dialog boxes + { + string title = Functions.GetWindowText(wParam); + string msg = Functions.GetDlgItemText(wParam, 0xFFFF); // -1 aka IDC_STATIC + if ((title == _title) && (msg == _msg)) + { + // Only Center the Window, if the Owner is NOT the Desktop + if (!_IsDesktopOwner) + CenterWindowOnParent(wParam); + + Unhook(); // Release hook - we've done what we needed + + // Now we also want to set the Icon on the Dialog + if (_hIcon != IntPtr.Zero) + { + User32.SendMessage(wParam, (int)Definitions.WM.WM_SETICON, (IntPtr)1, _hIcon); + User32.SendMessage(wParam, (int)Definitions.WM.WM_SETICON, (IntPtr)0, _hIcon); + } + } + } + } + return User32.CallNextHookEx(hHook, code, wParam, lParam); + } + + // Boilerplate window-centering code. + // Split out of HookCallback() for clarity. + private static void CenterWindowOnParent(IntPtr hChildWnd) + { + // Get child (MessageBox) size + Structures.RECT rcChild = new Structures.RECT(); + User32.GetWindowRect(hChildWnd, out rcChild); + int cxChild = rcChild.right - rcChild.left; + int cyChild = rcChild.bottom - rcChild.top; + + // Get parent (Form) size & location + IntPtr hParent = User32.GetParent(hChildWnd); + Structures.RECT rcParent = new Structures.RECT(); + User32.GetWindowRect(hParent, out rcParent); + int cxParent = rcParent.right - rcParent.left; + int cyParent = rcParent.bottom - rcParent.top; + + // Center the MessageBox on the Form + int x = rcParent.left + (cxParent - cxChild) / 2; + int y = rcParent.top + (cyParent - cyChild) / 2; + uint uFlags = 0x15; // SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE; + User32.SetWindowPos(hChildWnd, IntPtr.Zero, x, y, 0, 0, uFlags); + } + + #endregion + } +} diff --git a/@integrate/README.txt b/@integrate/README.txt new file mode 100644 index 0000000..9f9479d --- /dev/null +++ b/@integrate/README.txt @@ -0,0 +1,12 @@ +Things to go over + +File->Logging.cs +Process->PStarter.cs +Thread->SingleThreadTimer.cs (new) +Watchdog->SysTray.cs +Watchdog->MsgBox.cs +Watchdog->Timer.cs +Watchdog->Installer.cs (new) +Watchdog->CHMFile.cs (new) + + diff --git a/@integrate/ResxHelper.cs b/@integrate/ResxHelper.cs new file mode 100644 index 0000000..3c31973 --- /dev/null +++ b/@integrate/ResxHelper.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Resources; +using System.Reflection; +using System.IO; +using Watchdog.WatchdogLib.Assembly; + +namespace Watchdog +{ + // ResxTypes + public enum Resx + { + AppResx, + } + + #region ResxHelper Wrapper Classes + + /// + /// AppResx Wrapper String Resource Value Functions + /// + public static class AppResx + { + public static string GetString(string name) + { + return ResxHelper.GetStringValue(Resx.AppResx, name); + } + public static string GetStringFormated(string name, params object[] args) + { + return ResxHelper.GetStringValueFormated(Resx.AppResx, name, args); + } + } + + #endregion + + /// + /// Allows External Callers To Quickly gain access to the Resources Contained in this Assembly + /// + public static class ResxHelper + { + /// + /// Private static Dictionary Map of Resource Managers + /// + private static Dictionary _ResourceMap = new Dictionary(); + + /// + /// Static Constructor, iterates through the enumerations and loads resourceManagers internally + /// + static ResxHelper() + { + //string[] resources = Assembly.GetExecutingAssembly().GetManifestResourceNames(); + //~we shouldn't be doing this on filename (filename could change) + //string curAsmName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().ManifestModule.Name); + string curAsmName = AssemblyW.GetAssemblyName(AssemblyW.AssemblyST.Entry); + + // Create a ResourceManager for each Resource Type in this Assembly + foreach (string ResxName in Enum.GetNames(typeof(Resx))) + _ResourceMap.Add((Resx)Enum.Parse(typeof(Resx), ResxName), new ResourceManager((curAsmName + "." + ResxName), Assembly.GetExecutingAssembly())); + } + + #region String Resource Value Functions + + /// + /// Returns the Value of a String Resource via GetString() + /// + /// Reource Type to Access + /// Name of String Resource to get + /// the value of the resource, or "" if not found/Error Occured + public static string GetStringValue(Resx resx, string Name) + { + try + { + if (!String.IsNullOrEmpty(Name)) + { + string Value = PreserverFormating(_ResourceMap[resx].GetString(Name)); + return Value; + } + } + catch (Exception) { /* ignore */ } + return String.Empty; + } + + /// + /// Returns a Formated String Value of a String Resource via GetString() + /// + /// Reource Type to Access + /// Name of String Resource to get + /// Arguments to pass into String.Format() + /// the value of the resource, or "" if not found/Error Occured + public static string GetStringValueFormated(Resx resx, string Name, params object[] args) + { + String retVal = GetStringValue(resx, Name); + if (!String.IsNullOrEmpty(retVal)) + return String.Format(retVal, args); + else + return String.Empty; + } + + /// + /// we want to preserver formating using '\' characters that are in the resource + /// + /// a string value retrieved from the resource + /// a string that preserves formatting + private static string PreserverFormating(string Value) + { + if (!String.IsNullOrEmpty(Value)) + { + Value = Value.Replace("\\N", "\n"); + Value = Value.Replace("\\n", "\n"); + Value = Value.Replace("\\T", "\t"); + Value = Value.Replace("\\t", "\t"); + return Value; + } + return String.Empty; + } + + #endregion + } +} diff --git a/@integrate/Settings.cs b/@integrate/Settings.cs new file mode 100644 index 0000000..f0e3135 --- /dev/null +++ b/@integrate/Settings.cs @@ -0,0 +1,1057 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Watchdog.WatchdogLib.Registry; +using Watchdog.WatchdogLib.Assembly; +using Watchdog.WatchdogLib.Monitor; +using Watchdog.WatchdogLib.Process; +using System.Diagnostics; +using WatchdogLib.Tools; + +namespace Watchdog +{ + internal class Settings + { + /// + /// Critical Section + /// + private object _lock = new object(); + + #region Construction + + /// + /// Construction + /// + internal Settings() + { + } + + #endregion + + #region Scheduler N' Scheduler Backup Settings + + /// + /// Minimum Minute Interval for Schedulers + /// + public const uint MINIMUM_MINUTE_INTERVAL_SCHEDULERS = 10; + + /// + /// True if there are Settings specified for the Scheduler + /// + internal bool SchedulerSettingsExist + { + get + { + DateTime dtStart = DateTime.MinValue; + DateTime dtLastRun = DateTime.MinValue; + uint nHoursOrMinutes = 0; + bool bIsHours = true; + return GetSchedulerSettings(out dtStart, out dtLastRun, out nHoursOrMinutes, out bIsHours); + } + } + + /// + /// True if there are Settings specified for the Scheduler Backup + /// + internal bool SchedulerBackupSettingsExist + { + get + { + DateTime dtStart = DateTime.MinValue; + DateTime dtLastRun = DateTime.MinValue; + uint nHoursOrMinutes = 0; + bool bIsHours = true; + return GetSchedulerBackupSettings(out dtStart, out dtLastRun, out nHoursOrMinutes, out bIsHours); + } + } + + /// + /// Use this to retrieve the Scheduler Settings + /// + /// Start Time + /// Last Run Time + /// number of Hours or Minutes + /// is hours + /// Returns true if there is Scheduler Information in the Settings, also returns the settings, false otherwise + internal bool GetSchedulerSettings(out DateTime dtStart, out DateTime dtLastRun, out uint nHoursOrMinutes, out bool bIsHours) + { + lock (_lock) + { + dtStart = DateTime.MinValue; + dtLastRun = DateTime.MinValue; + nHoursOrMinutes = 0; + bIsHours = true; + + if (uint.TryParse(App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_RepeatEveryNHoursOrMinutes, "0"), out nHoursOrMinutes) && + DateTime.TryParse(App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_StartDateTime, DateTime.MinValue.ToString()), out dtStart) && + nHoursOrMinutes > 0 && + dtStart != DateTime.MinValue + ) + { + // Get the Last time this scheduler was run + dtLastRun = DateTime.MinValue; + DateTime.TryParse(App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_LastRun_DateTime, DateTime.MinValue.ToString()), out dtLastRun); + + // Are we on an hourly or minute schedule? + bIsHours = App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_RepeatIsHour, true); + + // Minimum Interval for Minutes + if (!bIsHours && nHoursOrMinutes < MINIMUM_MINUTE_INTERVAL_SCHEDULERS) + nHoursOrMinutes = MINIMUM_MINUTE_INTERVAL_SCHEDULERS; + + // return only true if nHoursOrMinutes is bigger 0 and dtStart is NOT equal to MinVal + return true; + } + + return false; + } + } + + /// + /// Use this to retrieve the Scheduler Backup Settings + /// + /// Start Time + /// Last Run Time + /// number of Hours or Minutes + /// is hours + /// Returns true if there is Scheduler Backup Information in the Settings, also returns the settings, false otherwise + internal bool GetSchedulerBackupSettings(out DateTime dtStart, out DateTime dtLastRun, out uint nHoursOrMinutes, out bool bIsHours) + { + lock (_lock) + { + dtStart = DateTime.MinValue; + dtLastRun = DateTime.MinValue; + nHoursOrMinutes = 0; + bIsHours = true; + + if (uint.TryParse(App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_ForDurationNHoursOrMinutes, "0"), out nHoursOrMinutes) && + DateTime.TryParse(App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_StartDateTime, DateTime.MinValue.ToString()), out dtStart) && + nHoursOrMinutes > 0 && + dtStart != DateTime.MinValue + ) + { + // Get the Last time this scheduler was run + dtLastRun = DateTime.MinValue; + DateTime.TryParse(App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_LastRun_DateTime, DateTime.MinValue.ToString()), out dtLastRun); + + // Are we on an hourly or minute schedule? + bIsHours = App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_DurationIsHour, true); + + // Minimum Interval for Minutes + if (!bIsHours && nHoursOrMinutes < MINIMUM_MINUTE_INTERVAL_SCHEDULERS) + nHoursOrMinutes = MINIMUM_MINUTE_INTERVAL_SCHEDULERS; + + // return only true if nHoursOrMinutes is bigger 0 and dtStart is NOT equal to MinVal + return true; + } + + return false; + } + } + + /// + /// Set/Get the Scheduler Start DT + /// + internal DateTime SchedulerStartDateTime + { + get + { + string strDateTime = App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_StartDateTime, DateTime.MinValue.ToString()); + DateTime dt; + if (DateTime.TryParse(strDateTime, out dt) && dt != DateTime.MinValue) + return dt; + else + return DateTime.Now; + } + set + { + if (value != DateTime.MinValue) + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_StartDateTime, value.ToString()); + } + } + + /// + /// Set/Get the Scheduler Backup Start DT + /// + internal DateTime SchedulerBackupStartDateTime + { + get + { + string strDateTime = App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_StartDateTime, DateTime.MinValue.ToString()); + DateTime dt; + if (DateTime.TryParse(strDateTime, out dt) && dt != DateTime.MinValue) + return dt; + else + return DateTime.Now; + } + set + { + if (value != DateTime.MinValue) + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_StartDateTime, value.ToString()); + } + } + + /// + /// Set/Get the Scheduler Repeat Hours or Minutes + /// + internal uint SchedulerRepeatEveryHoursOrMinutes + { + get + { + uint nRepeatEveryNHoursOrMinutes = 0; + if (uint.TryParse(App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_RepeatEveryNHoursOrMinutes, "0"), out nRepeatEveryNHoursOrMinutes) && (nRepeatEveryNHoursOrMinutes >= 1 && nRepeatEveryNHoursOrMinutes <= 8760)) + { + if ((nRepeatEveryNHoursOrMinutes > 0) && (nRepeatEveryNHoursOrMinutes < MINIMUM_MINUTE_INTERVAL_SCHEDULERS) && !SchedulerRepeatIsHour) + return MINIMUM_MINUTE_INTERVAL_SCHEDULERS; + else + return nRepeatEveryNHoursOrMinutes; + } + else + { + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_RepeatEveryNHoursOrMinutes, "0"); + return 0; + } + } + set + { + if (value >= 0 && value <= 8760) + { + if ((value > 0) && (value < MINIMUM_MINUTE_INTERVAL_SCHEDULERS) && !SchedulerRepeatIsHour) + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_RepeatEveryNHoursOrMinutes, MINIMUM_MINUTE_INTERVAL_SCHEDULERS.ToString()); + else + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_RepeatEveryNHoursOrMinutes, value.ToString()); + } + } + } + + /// + /// Set/Get the Scheduler Backup Duration Hours or Minutes + /// + internal uint SchedulerBackupForDurationHoursOrMinutes + { + get + { + uint nForDurationNHoursOrMinutes = 0; + if (uint.TryParse(App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_ForDurationNHoursOrMinutes, "0"), out nForDurationNHoursOrMinutes) && (nForDurationNHoursOrMinutes >= 1 && nForDurationNHoursOrMinutes <= 8760)) + { + if ((nForDurationNHoursOrMinutes > 0) && (nForDurationNHoursOrMinutes < MINIMUM_MINUTE_INTERVAL_SCHEDULERS) && !SchedulerBackupDurationIsHour) + return MINIMUM_MINUTE_INTERVAL_SCHEDULERS; + else + return nForDurationNHoursOrMinutes; + } + else + { + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_ForDurationNHoursOrMinutes, "0"); + return 0; + } + } + set + { + if (value >= 0 && value <= 8760) + { + if ((value > 0) && (value < MINIMUM_MINUTE_INTERVAL_SCHEDULERS) && !SchedulerBackupDurationIsHour) + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_ForDurationNHoursOrMinutes, MINIMUM_MINUTE_INTERVAL_SCHEDULERS.ToString()); + else + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_ForDurationNHoursOrMinutes, value.ToString()); + } + } + } + + /// + /// Set/Get the Scheduler Repeat Is Hour Flag + /// + internal bool SchedulerRepeatIsHour + { + get + { + return App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_RepeatIsHour, true); + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_RepeatIsHour, value); + } + } + + /// + /// Set/Get the Scheduler Backup Duration Is Hour Flag + /// + internal bool SchedulerBackupDurationIsHour + { + get + { + return App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_DurationIsHour, true); + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_DurationIsHour, value); + } + } + + /// + /// Boolean Flag indicating whether the Scheduler Restarts Services + /// + internal bool SchedulerRestartsServices + { + get + { + return App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Scheduler_Restarts_Services, false); + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__Scheduler_Restarts_Services, value); + } + } + + /// + /// DT stamp when the Scheduler was last run + /// + internal DateTime LastSchedulerRun + { + get + { + // Get the Last time this scheduler was run + DateTime dtLastRun = DateTime.MinValue; + DateTime.TryParse(App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_LastRun_DateTime, DateTime.MinValue.ToString()), out dtLastRun); + return dtLastRun; + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__Sheduler_LastRun_DateTime, value.ToString()); + } + + } + + /// + /// DT stamp when the Scheduler Backup was last run + /// + internal DateTime LastSchedulerBackupRun + { + get + { + // Get the Last time this scheduler was run + DateTime dtLastRun = DateTime.MinValue; + DateTime.TryParse(App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_LastRun_DateTime, DateTime.MinValue.ToString()), out dtLastRun); + return dtLastRun; + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_LastRun_DateTime, value.ToString()); + } + + } + + /// + /// Boolean Flag indicating whether the Scheduler Backup Stops Instead of Pauses + /// + internal bool SchedulerBackupStopInsteadOfPause + { + get + { + return App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_StopInsteadOfPause, true); + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_StopInsteadOfPause, value); + } + } + + /// + /// Boolean Flag indicating whether the Scheduler Backup Stops Servies + /// + internal bool SchedulerBackupStopServices + { + get + { + return App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_StopServices, true); + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__ShedulerBackup_StopServices, value); + } + } + + #endregion + + #region Program Settings + + /// + /// Boolean flag indicates whether upon program start it should start monitoring right away + /// + internal bool StartMonitorUponStart + { + get + { + return App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Monitor_On_Start, true); + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__Monitor_On_Start, value); + } + } + + /// + /// Indicates the max failure count that can occur per process/service within a given MaxFailureCountHour Timespan + /// * Important Setting for Process Monitor * Value can be between 1 and 99 + /// + internal uint MaxFailureCount + { + get + { + // Re-read MaxFailureCount Setting * Default is specified in AppResx * + uint nMaxFailureCount = uint.Parse(AppResx.GetString("DEFAULT_VALUE_MAX_PROCESS_START_FAILURE_COUNT")); + bool bCanParseIni = uint.TryParse(App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Max_Fail_Count, AppResx.GetString("DEFAULT_VALUE_MAX_PROCESS_START_FAILURE_COUNT")), out nMaxFailureCount); + if (!bCanParseIni) + App.log.Error(AppResx.GetStringFormated("ERROR_INI_SETTINGS_PARSE", App.Ini_Setting.MonitorSettings__Max_Fail_Count.ToString())); + if (nMaxFailureCount < 1) + nMaxFailureCount = 1; + else if (nMaxFailureCount > 99) + nMaxFailureCount = 99; + return nMaxFailureCount; + } + set + { + if (value >= 1 && value <= 99) + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__Max_Fail_Count, value.ToString()); + } + } + + /// + /// Indicates the Hour Timespan in which MaxFailureCount can occur + /// * Important Setting for Process Monitor * Value can be between 1 and 8760 + /// + internal uint MaxFailureCountHour + { + get + { + // Re-read MaxFailureCountHour Setting * Default is specified in AppResx + uint nMaxFailureCountHour = uint.Parse(AppResx.GetString("DEFAULT_VALUE_MAX_PROCESS_START_FAILURE_COUNT_HOUR")); + bool bCanParseIni = uint.TryParse(App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Max_Fail_Count_Per_Hour, AppResx.GetString("DEFAULT_VALUE_MAX_PROCESS_START_FAILURE_COUNT_HOUR")), out nMaxFailureCountHour); + if (!bCanParseIni) + App.log.Error(AppResx.GetStringFormated("ERROR_INI_SETTINGS_PARSE", App.Ini_Setting.MonitorSettings__Max_Fail_Count_Per_Hour.ToString())); + if (nMaxFailureCountHour < 1) + nMaxFailureCountHour = 1; + else if (nMaxFailureCountHour > 8760) + nMaxFailureCountHour = 8760; + return nMaxFailureCountHour; + } + set + { + if (value >= 1 && value <= 8760) + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__Max_Fail_Count_Per_Hour, value.ToString()); + } + } + + /// + /// Get/Set the Last Program Version in the Registry * Used to check if an upgrade occured * + /// + internal string LastProgramVersion + { + get + { + string LastVersion = RegKey.GetKey(App.APPLICATION_NAME_SHORT, "LastProgramVersion", String.Empty); + return LastVersion; + } + set + { + RegKey.SetKey(App.APPLICATION_NAME_SHORT, "LastProgramVersion", value); + } + } + + #endregion + + #region Email Settings + + /// + /// Boolean flag indicates whether Email Notifications are enabled + /// + internal bool EmailNotificationEnabled + { + get + { + return App.inifile.GetKeyValue(App.Ini_Setting.EmailSettings__EnableEmailNotifications, false); + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.EmailSettings__EnableEmailNotifications, value); + } + } + + /// + /// True if the settings for Email are valid + /// + internal bool EmailSettingsValid + { + get + { + // Check reg + bool NotValid = String.IsNullOrEmpty(EmailStmpServer) || String.IsNullOrEmpty(EmailSenderEmail) || + String.IsNullOrEmpty(EmailReceiverEmail); + if (NotValid) + return false; + + // Check auth + if (EmailStmpServerRequiresAuth) + NotValid = String.IsNullOrEmpty(EmailSmtpUsername) || String.IsNullOrEmpty(EmailSmtpPassword); + return !NotValid; + } + } + + /// + /// Boolean flag indicates whether Email Smtp Server Requires Authentication + /// + internal bool EmailStmpServerRequiresAuth + { + get + { + return App.inifile.GetKeyValue(App.Ini_Setting.EmailSettings__SmtpRequiresAuth, false); + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.EmailSettings__SmtpRequiresAuth, value); + } + } + + /// + /// Boolean flag indicates whether Email Smtp Server Requires SSL + /// + internal bool EmailStmpServerRequiresSSL + { + get + { + return App.inifile.GetKeyValue(App.Ini_Setting.EmailSettings__SmtpRequiresSSL, false); + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.EmailSettings__SmtpRequiresSSL, value); + } + } + + /// + /// Email Smtp Server + /// + internal string EmailStmpServer + { + get + { + string strFound = App.inifile.GetKeyValue(App.Ini_Setting.EmailSettings__SmtpServer, ""); + if (!String.IsNullOrEmpty(strFound) && strFound.Contains('.')) + return strFound; + else + return String.Empty; + } + set + { + if(!String.IsNullOrEmpty(value) && value.Contains('.')) + App.inifile.SetKeyValue(App.Ini_Setting.EmailSettings__SmtpServer, value); + } + } + + /// + /// Email Sender Email + /// + internal string EmailSenderEmail + { + get + { + string strFound = App.inifile.GetKeyValue(App.Ini_Setting.EmailSettings__SenderEmail, ""); + if (!String.IsNullOrEmpty(strFound) && strFound.Contains('.') && strFound.Contains('@')) + return strFound; + else + return String.Empty; + } + set + { + if (!String.IsNullOrEmpty(value) && value.Contains('.') && value.Contains('@')) + App.inifile.SetKeyValue(App.Ini_Setting.EmailSettings__SenderEmail, value); + } + } + + /// + /// Email Receiver Email + /// + internal string EmailReceiverEmail + { + get + { + string strFound = App.inifile.GetKeyValue(App.Ini_Setting.EmailSettings__ReveiverEmail, ""); + if (!String.IsNullOrEmpty(strFound) && strFound.Contains('.') && strFound.Contains('@')) + return strFound; + else + return String.Empty; + } + set + { + if (!String.IsNullOrEmpty(value) && value.Contains('.') && value.Contains('@')) + App.inifile.SetKeyValue(App.Ini_Setting.EmailSettings__ReveiverEmail, value); + } + } + + /// + /// Email Smtp Port * Default is 25 * + /// + internal uint EmailSmtpPort + { + get + { + uint nPort = 25; + bool bCanParseIni = uint.TryParse(App.inifile.GetKeyValue(App.Ini_Setting.EmailSettings__SmtpPort, "25"), out nPort); + if (!bCanParseIni) + App.log.Error(AppResx.GetStringFormated("ERROR_INI_SETTINGS_PARSE", App.Ini_Setting.EmailSettings__SmtpPort.ToString())); + if (nPort < 1) + nPort = 1; + else if (nPort > 65536) + nPort = 65536; + return nPort; + } + set + { + if (value >= 1 && value <= 65536) + App.inifile.SetKeyValue(App.Ini_Setting.EmailSettings__SmtpPort, value.ToString()); + } + } + + /// + /// Email Smtp Username + /// + internal string EmailSmtpUsername + { + get + { + string strFound = App.inifile.GetKeyValue(App.Ini_Setting.EmailSettings__SmtpUsername, ""); + if (!String.IsNullOrEmpty(strFound)) + return strFound; + else + return String.Empty; + } + set + { + if (!String.IsNullOrEmpty(value)) + App.inifile.SetKeyValue(App.Ini_Setting.EmailSettings__SmtpUsername, value); + else + App.inifile.SetKeyValue(App.Ini_Setting.EmailSettings__SmtpUsername, ""); + } + } + + /// + /// Email Smtp Password + /// + internal string EmailSmtpPassword + { + get + { + string strFound = App.inifile.GetKeyValue(App.Ini_Setting.EmailSettings__SmtpPassword, ""); + if (!String.IsNullOrEmpty(strFound)) + return strFound; + else + return String.Empty; + } + set + { + if (!String.IsNullOrEmpty(value)) + App.inifile.SetKeyValue(App.Ini_Setting.EmailSettings__SmtpPassword, value); + else + App.inifile.SetKeyValue(App.Ini_Setting.EmailSettings__SmtpPassword, ""); + } + } + + /// + /// Email Custom Message Text + /// + internal string EmailCustomMessageText + { + get + { + string strFound = App.inifile.GetKeyValue(App.Ini_Setting.EmailSettings__Custom_Message_Text, ""); + if (!String.IsNullOrEmpty(strFound)) + return strFound; + else + return String.Empty; + } + set + { + if (!String.IsNullOrEmpty(value)) + App.inifile.SetKeyValue(App.Ini_Setting.EmailSettings__Custom_Message_Text, value); + else + App.inifile.SetKeyValue(App.Ini_Setting.EmailSettings__Custom_Message_Text, ""); + } + } + + #endregion + + #region Windows Settings + + /// + /// Boolean Flag indicates whether this app should start with windows + /// + internal bool StartWithWindows + { + get + { + bool bStartWithWindows = App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Start_With_Windows, true); + bool bSetOk = true; + if (bStartWithWindows) + bSetOk = RegKey.SetKey("Microsoft\\Windows\\CurrentVersion\\Run", App.APPLICATION_NAME_SHORT, AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameNPath(AssemblyW.AssemblyST.Entry)); + else + bSetOk = RegKey.DeleteKey("Microsoft\\Windows\\CurrentVersion\\Run", App.APPLICATION_NAME_SHORT); + if (!bSetOk) + App.log.Error("Failed to write to Microsoft\\Windows\\CurrentVersion\\Run. Check Permissions."); + + // * Special Circumstance * + if (bStartWithWindows) + DeleteOffendingHL7KeyIfApplicable(); + + return bStartWithWindows; + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__Start_With_Windows, value); + bool bSetOk = true; + if (value) + bSetOk = RegKey.SetKey("Microsoft\\Windows\\CurrentVersion\\Run", App.APPLICATION_NAME_SHORT, AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameNPath(AssemblyW.AssemblyST.Entry)); + else + bSetOk = RegKey.DeleteKey("Microsoft\\Windows\\CurrentVersion\\Run", App.APPLICATION_NAME_SHORT); + + // * Special Circumstance * + if (value) + DeleteOffendingHL7KeyIfApplicable(); + + if (!bSetOk) + App.log.Error("Failed to write to HKCU\\Microsoft\\Windows\\CurrentVersion\\Run. Check Permissions."); + } + } + + /// + /// Boolean Flag indicates whether LockWorkstation should be called upon CU login + /// + internal bool LockWorkstationWithWindows + { + get + { + bool bLockWorkstationWithWindows = App.inifile.GetKeyValue(App.Ini_Setting.WindowsSettings__LockWorkstation_With_Windows, false); + bool bSetOk = true; + if (bLockWorkstationWithWindows) + bSetOk = RegKey.SetKey("Microsoft\\Windows\\CurrentVersion\\Run", "ALockWorkstation", "rundll32.exe user32.dll LockWorkStation"); + else + bSetOk = RegKey.DeleteKey("Microsoft\\Windows\\CurrentVersion\\Run", "ALockWorkstation"); + if (!bSetOk) + App.log.Error("Failed to write to HKCU\\Microsoft\\Windows\\CurrentVersion\\Run. Check Permissions."); + return bLockWorkstationWithWindows; + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.WindowsSettings__LockWorkstation_With_Windows, value); + bool bSetOk = true; + if (value) + bSetOk = RegKey.SetKey("Microsoft\\Windows\\CurrentVersion\\Run", "ALockWorkstation", "rundll32.exe user32.dll LockWorkStation"); + else + bSetOk = RegKey.DeleteKey("Microsoft\\Windows\\CurrentVersion\\Run", "ALockWorkstation"); + if (!bSetOk) + App.log.Error("Failed to write to HKCU\\Microsoft\\Windows\\CurrentVersion\\Run. Check Permissions."); + } + } + + /// + /// Boolean Flag indicates whether Windows should automatically Login + /// + internal bool EnableAutomaticLoginWithWindows + { + get + { + bool bAutoLogin = App.inifile.GetKeyValue(App.Ini_Setting.WindowsSettings__AutoLogin_With_Windows, false); + bool bSetOk = true; + if (bAutoLogin) + bSetOk = RegKey.SetKey(HKEYRoot.LocalMachine, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "AutoAdminLogon", "1"); + else + bSetOk = RegKey.SetKey(HKEYRoot.LocalMachine, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "AutoAdminLogon", "0"); + if (!bSetOk) + App.log.Error("Failed to write to HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon. Check Permissions."); + return bAutoLogin; + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.WindowsSettings__AutoLogin_With_Windows, value); + bool bSetOk = true; + if (value) + bSetOk = RegKey.SetKey(HKEYRoot.LocalMachine, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "AutoAdminLogon", "1"); + else + bSetOk = RegKey.SetKey(HKEYRoot.LocalMachine, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "AutoAdminLogon", "0"); + if (!bSetOk) + App.log.Error("Failed to write to HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon. Check Permissions."); + } + } + + /// + /// AutoLogin Default Domain + /// + internal string AutomaticLoginDefaultDomain + { + get + { + return RegKey.GetKey(HKEYRoot.LocalMachine, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultDomainName", Environment.UserDomainName); + } + set + { + bool bSetOk = true; + if (!String.IsNullOrEmpty(value)) + bSetOk = RegKey.SetKey(HKEYRoot.LocalMachine, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultDomainName", value); + else + bSetOk = RegKey.SetKey(HKEYRoot.LocalMachine, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultDomainName", ""); + if (!bSetOk) + App.log.Error("Failed to write to HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon. Check Permissions."); + } + } + + /// + /// AutoLogin Default Username + /// + internal string AutomaticLoginDefaultUsername + { + get + { + return RegKey.GetKey(HKEYRoot.LocalMachine, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultUserName", Environment.UserName); + } + set + { + bool bSetOk = true; + if (!String.IsNullOrEmpty(value)) + bSetOk = RegKey.SetKey(HKEYRoot.LocalMachine, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultUserName", value); + else + bSetOk = RegKey.SetKey(HKEYRoot.LocalMachine, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultUserName", ""); + if (!bSetOk) + App.log.Error("Failed to write to HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon. Check Permissions."); + } + } + + /// + /// AutoLogin Default Password + /// + internal string AutomaticLoginDefaultPassword + { + get + { + return RegKey.GetKey(HKEYRoot.LocalMachine, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultPassword", ""); + } + set + { + bool bSetOk = true; + if (!String.IsNullOrEmpty(value)) + bSetOk = RegKey.SetKey(HKEYRoot.LocalMachine, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultPassword", value); + else + bSetOk = RegKey.SetKey(HKEYRoot.LocalMachine, "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", "DefaultPassword", ""); + if (!bSetOk) + App.log.Error("Failed to write to HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon. Check Permissions."); + } + } + + /// + /// Creates an Exe HardLink to the Application .exe at the Rooth Path + /// + internal bool CreateExeHardLinkInRootPath + { + get + { + //fsutil hardlink create c:\foo.txt c:\bar.txt + bool bCreateHardLinkInRootPath = App.inifile.GetKeyValue(App.Ini_Setting.WindowsSettings__CreateExeHardLink_In_RootPath, true); + String FileNameWithExtension = AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameWithExtension(AssemblyW.AssemblyST.Entry); + String RootPath = PathNaming.GetPathRoot(AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameNPath(AssemblyW.AssemblyST.Entry)); + String FileNameFullPath = AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameNPath(AssemblyW.AssemblyST.Entry); + if (bCreateHardLinkInRootPath && (String.Compare(FileNameFullPath, String.Format("{0}{1}", RootPath, FileNameWithExtension), true) != 0)) + { + Process.Start(PStartInfo.CreateCMDDosCommandProcess(String.Format("del {0}{1}", RootPath, FileNameWithExtension), false)); + Process.Start(PStartInfo.CreateCMDDosCommandProcess(String.Format("fsutil hardlink create {0}{1} \"{2}\"", RootPath, FileNameWithExtension, FileNameFullPath), false)); + } + else if (String.Compare(FileNameFullPath, String.Format("{0}{1}", RootPath, FileNameWithExtension), true) != 0) + { + Process.Start(PStartInfo.CreateCMDDosCommandProcess(String.Format("del {0}{1}", "", ""), false)); + } + return bCreateHardLinkInRootPath; + } + set + { + App.inifile.SetKeyValue(App.Ini_Setting.WindowsSettings__CreateExeHardLink_In_RootPath, value); + String FileNameWithExtension = AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameWithExtension(AssemblyW.AssemblyST.Entry); + String RootPath = PathNaming.GetPathRoot(AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameNPath(AssemblyW.AssemblyST.Entry)); + String FileNameFullPath = AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameNPath(AssemblyW.AssemblyST.Entry); + if (value && (String.Compare(FileNameFullPath, String.Format("{0}{1}", RootPath, FileNameWithExtension), true) != 0)) + { + Process.Start(PStartInfo.CreateCMDDosCommandProcess(String.Format("del {0}\\{1}", RootPath, FileNameWithExtension), false)); + Process.Start(PStartInfo.CreateCMDDosCommandProcess(String.Format("fsutil hardlink create {0}\\{1} \"{2}\"", RootPath, FileNameWithExtension, FileNameFullPath), false)); + } + else if (String.Compare(FileNameFullPath, String.Format("{0}{1}", RootPath, FileNameWithExtension), true) != 0) + { + Process.Start(PStartInfo.CreateCMDDosCommandProcess(String.Format("del {0}\\{1}", "", ""), false)); + } + } + } + + #endregion + + #region Prefill Settings + + /// + /// Prefill's the default exes for the Process Monitor (GUI Prefill Feature) + /// + internal string DefaultExeRunningFilter + { + get + { + return App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Default_Exe_Running_Filter, AppResx.GetString("DEFAULT_EXE_RUNNING_FILTER")); + } + set + { + if(value != null) + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__Default_Exe_Running_Filter, value); + } + } + + /// + /// Prefill's the default services for the Service Monitor (GUI Prefill Feature) + /// + internal string DefaultServicesNamesRunningFilter + { + get + { + return App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Default_Services_Names_Running_Filter, AppResx.GetString("DEFAULT_SERVICE_NAMES_RUNNING_FILTER")); + } + set + { + if(value != null) + App.inifile.SetKeyValue(App.Ini_Setting.MonitorSettings__Default_Services_Names_Running_Filter, value); + } + } + + #endregion + + #region Window Settings + + /// + /// Retrieve the Top N' Left for the Settings Window + /// + /// Top Double + /// Left Double + internal void GetWindowSetting_TopLeft_SettingsWindow(ref double Top, ref double Left) + { + // Load the Window at the correct position + try + { + string TopLeft = App.inifile.GetKeyValue(App.Ini_Setting.WindowSettings__SettingsWindow_TopLeft, String.Empty); + if (!String.IsNullOrEmpty(TopLeft)) + { + string[] tl = TopLeft.Split(';'); + Top = double.Parse(tl[0]); + Left = double.Parse(tl[1]); + + // Check Screen ! * To Do * Check Screen Dimensions * + } + } + catch (Exception ex) { App.log.Error("Error Loading Settings Window Settings", ex); } + } + + /// + /// Set the Top N' Left for the Settings Window + /// + /// Top Double + /// Left Double + internal void SetWindowSetting_TopLeft_SettingsWindow(double Top, double Left) + { + if (!Double.IsNaN(Top) && !Double.IsNaN(Left)) + App.inifile.SetKeyValue(App.Ini_Setting.WindowSettings__SettingsWindow_TopLeft, (Top.ToString() + ";" + Left.ToString())); + } + + /// + /// Retrieve the Top N' Left for the Settings Window + /// + /// Top Double + /// Left Double + internal void GetWindowSetting_TopLeft_LogViewerWindow(ref double Top, ref double Left) + { + // Load the Window at the correct position + try + { + string TopLeft = App.inifile.GetKeyValue(App.Ini_Setting.WindowSettings__LogViewerWindow_TopLeft, String.Empty); + if (!String.IsNullOrEmpty(TopLeft)) + { + string[] tl = TopLeft.Split(';'); + Top = double.Parse(tl[0]); + Left = double.Parse(tl[1]); + + // Check Screen ! * To Do * Check Screen Dimensions * + } + } + catch (Exception ex) { App.log.Error("Error Loading Settings Window Settings", ex); } + } + + /// + /// Set the Top N' Left for the Settings Window + /// + /// Top Double + /// Left Double + internal void SetWindowSetting_TopLeft_LogViewerWindow(double Top, double Left) + { + if (!Double.IsNaN(Top) && !Double.IsNaN(Left)) + App.inifile.SetKeyValue(App.Ini_Setting.WindowSettings__LogViewerWindow_TopLeft, (Top.ToString() + ";" + Left.ToString())); + } + + /// + /// Retrieve the Top N' Left for the Settings Window + /// + /// Top Double + /// Left Double + internal void GetWindowSetting_TopLeft_AboutWindow(ref double Top, ref double Left) + { + // Load the Window at the correct position + try + { + string TopLeft = App.inifile.GetKeyValue(App.Ini_Setting.WindowSettings__AboutWindow_TopLeft, String.Empty); + if (!String.IsNullOrEmpty(TopLeft)) + { + string[] tl = TopLeft.Split(';'); + Top = double.Parse(tl[0]); + Left = double.Parse(tl[1]); + + // Check Screen ! * To Do * Check Screen Dimensions * + } + } + catch (Exception ex) { App.log.Error("Error Loading Settings Window Settings", ex); } + } + + /// + /// Set the Top N' Left for the Settings Window + /// + /// Top Double + /// Left Double + internal void SetWindowSetting_TopLeft_AboutWindow(double Top, double Left) + { + if (!Double.IsNaN(Top) && !Double.IsNaN(Left)) + App.inifile.SetKeyValue(App.Ini_Setting.WindowSettings__AboutWindow_TopLeft, (Top.ToString() + ";" + Left.ToString())); + } + + #endregion + + #region Special Circumstances + + /// + /// HL7Messaging puts itself in the Windows\Run to start-up automatically, + /// If we monitor HL7Messaging then we should remove that key * Always * if we are to + /// start with windows. ~This function get's called in those instances where we should + /// check exactly that, and deal with exactly that issue. + /// + internal void DeleteOffendingHL7KeyIfApplicable() + { + bool bStartWithWindowsIsSet = App.inifile.GetKeyValue(App.Ini_Setting.MonitorSettings__Start_With_Windows, true); + if (bStartWithWindowsIsSet) + { + WatchdogConfiguration config = App.xmlfile.ReadData(false); + bool bHasHL7MessagingConfigured = (config.MonitoredProcesses.GetProcessExesByProcessExeFileNameWithoutExtension("HL7Messaging").Length > 0); + if (bHasHL7MessagingConfigured) // Make sure the Key is Deleted + { + if (!RegKey.DeleteKey("Microsoft\\Windows\\CurrentVersion\\Run", "HL7Messenger")) + App.log.Error("Failed to write to Microsoft\\Windows\\CurrentVersion\\Run. Check Permissions."); + } + } + } + + #endregion + } +} diff --git a/@integrate/Snippets.txt b/@integrate/Snippets.txt new file mode 100644 index 0000000..ad14c72 --- /dev/null +++ b/@integrate/Snippets.txt @@ -0,0 +1,24 @@ + private string GetDefaultBrowserPath() + { + try + { + if (ApplicationSettings.appSettings().LogEvents) + { + CrossProductLogging.WriteEventLogFile("Begin GetDefaultBrowserPath"); + } + + string key = @"htmlfile\shell\open\command"; + RegistryKey registryKey = + Registry.ClassesRoot.OpenSubKey(key, false); + // get default browser path + if (ApplicationSettings.appSettings().LogEvents) + { + CrossProductLogging.WriteEventLogFile("End GetDefaultBrowserPath"); + } + return ((string)registryKey.GetValue(null, null)).Split('"')[1]; + } + catch + { + return ""; + } + } \ No newline at end of file diff --git a/@integrate/SysTray.cs b/@integrate/SysTray.cs new file mode 100644 index 0000000..6dfacdd --- /dev/null +++ b/@integrate/SysTray.cs @@ -0,0 +1,238 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Drawing; +using System.Windows.Forms; +using WatchdogLib.Thread; +using System.Timers; + +namespace WatchdogLib.WinForms +{ + /// + /// Wrapper Class around .Net NotifyIcon, to make it easier to work with an System Tray Icon. + /// Instantiate the object and set ContextMenu or ContextMenuStrip in order to have a right click menu. + /// Subscripe to the MouseLeftClick, and MouseLeftDoubleClick event to get accurate events to handle, + /// Call Show()/Hide() to show/hide the System Tray Icon, respectively. + /// + public class SysTray + { + #region Public Properties + + public NotifyIcon trayNotify { get; private set; } + public Icon Icon { get { return trayNotify.Icon; } set { trayNotify.Icon = value; } } + public ContextMenu trayMenu { get { return trayNotify.ContextMenu; } set { trayNotify.ContextMenu = value; } } + public ContextMenuStrip trayMenuStrip { get { return trayNotify.ContextMenuStrip; } set { trayNotify.ContextMenuStrip = value; } } + public string toolTip { get { return trayNotify.Text; } set { trayNotify.Text = value; } } + public bool Visible { get { return trayNotify.Visible; } } + + #endregion + + #region Public Events + + public delegate void SingleLeftMouseClick(MouseEventArgs e); + /// + /// Subscribe to get the Left Mouse Single Click Event + /// + public event SingleLeftMouseClick LeftMouseClick; + + public delegate void DoubleLeftMouseClick(MouseEventArgs e); + /// + /// Subscribe to get the Left Mouse Double Click Event + /// + public event DoubleLeftMouseClick LeftMouseDoubleClick; + + public delegate void RightMouseClick(MouseEventArgs e); + /// + /// Subscribe to get any Right Mouse Fired Event, use to redo context menu, if needed + /// + public event RightMouseClick RightMouseFired; + + #endregion + + #region Private Members + + private TTimer SingleClickDetectTimer = null; + private TimeSpan _LastFiredEvent = new TimeSpan(DateTime.Now.Ticks); + private const int _MILISECONDS_FOR_SINGLEMOUSE_CLICKEVENT_TOCOUNT = 500; + private const int _N_SECONDS_TOIGNORE_NEXT_SIGNLEMOUSE_CLICKEVENT = 2; // to avoid tripple clicks, etc... (only sends one double click) + + /// + /// Returns true if enough time since _LastFiredEvent has passed + /// + private bool EnoughTimeSinceLastEventHasElapsed + { + get + { + // 1 second is 10 Million ticks, one Milisecond is 10K + return (DateTime.Now.Ticks - _LastFiredEvent.Ticks) >= (_N_SECONDS_TOIGNORE_NEXT_SIGNLEMOUSE_CLICKEVENT * 1000 * 10000); + } + } + + #endregion + + #region Construction + + /// + /// Construct a System Tray Icon (use public properties like ContextMenu,Icon,toolTip to customize further) + /// + /// pass in an initial ToolTip to display, defaults to "" + /// if null, defaults to systemIcons.Application + //public SysTray(string toolTip = "", Icon icon = null) + public SysTray(string toolTip, Icon icon) + { + // Create internal objects + this.trayNotify = new NotifyIcon(); + this.SingleClickDetectTimer = new TTimer(new ElapsedEventHandler(RealSingleClickDetectTimer_ElapsedEventHandler), _MILISECONDS_FOR_SINGLEMOUSE_CLICKEVENT_TOCOUNT, false, true); + + // Add Single / Double-Click Event Handlers + trayNotify.Click += new EventHandler(trayNotify_Click); + trayNotify.DoubleClick += new EventHandler(trayNotify_DoubleClick); + trayNotify.MouseDown += new MouseEventHandler(trayNotify_MouseDown); + + // Set ToolTip + if (!String.IsNullOrEmpty(toolTip)) + this.toolTip = toolTip; + + // Set Icon + if (icon == null) + this.Icon = new Icon(SystemIcons.Application, 40, 40); + else + this.Icon = icon; + } + + #endregion + + #region Click Event Handlers + + /// + /// Called by NotifyIcon DoubleClick Event, We filter for only the left mouse double-click, + /// event and fire event when neccessary + /// + /// + /// + private void trayNotify_DoubleClick(object sender, EventArgs e) + { + MouseEventArgs args = (MouseEventArgs)e; + if (args.Button == MouseButtons.Left) + { + SingleClickDetectTimer.Stop(); + if (LeftMouseDoubleClick != null && EnoughTimeSinceLastEventHasElapsed) + { + _LastFiredEvent = new TimeSpan(DateTime.Now.Ticks); + LeftMouseDoubleClick(new MouseEventArgs(MouseButtons.Left, 2, 0, 0, 0)); + } + } + } + + /// + // Called by NotifyIcon Click Event, We filter for only the left mouse click, + /// event and fire event when neccessary + /// + /// + /// + private void trayNotify_Click(object sender, EventArgs e) + { + MouseEventArgs args = (MouseEventArgs) e; + if (args.Button == MouseButtons.Left && EnoughTimeSinceLastEventHasElapsed) + SingleClickDetectTimer.Start(); // Start Single Click Detect Timer + } + + /// + /// In order to accurately re-do a context menu, we handle MouseDown for the + /// Right-Mouse click. Mouse Down comes in before the click event, which gives + /// the caller an opportunity to handle/recreate the context menu dynamically, if needed + /// + /// + /// + void trayNotify_MouseDown(object sender, MouseEventArgs e) + { + MouseEventArgs args = (MouseEventArgs)e; + if (args.Button == MouseButtons.Right && (RightMouseFired != null)) + RightMouseFired(args); + } + + /// + /// Used to detect ONLY Single Clicks, since a single-click and then a double-click fires, + /// we want to ignore the first click,and first see if a double-click comes in, if so, ignore + /// the single click, otherwise send it. (this is done by trayNotify_Click & transNotify_DoubleClick) + /// + /// + /// + private void RealSingleClickDetectTimer_ElapsedEventHandler(object sender, ElapsedEventArgs e) + { + SingleClickDetectTimer.Stop(); + if (LeftMouseClick != null) + { + _LastFiredEvent = new TimeSpan(DateTime.Now.Ticks); + LeftMouseClick(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0)); + } + } + + #endregion + + #region Show N' Hide + + /// + /// Show the System Tray Icon + /// + public void Show() + { + trayNotify.Visible = true; + } + + /// + /// Hide the System Tray Icon + /// + public void Hide() + { + trayNotify.Visible = false; + } + + #endregion + + #region Public ShowBallon + + /// + /// Pops up a Ballon over the System Tray Icon + /// + /// Specify the Timeout in Seconds + //public void ShowBallon_Default(string BallonTipTitle, string BallonTipText, int nTimeoutInSeconds = 10) + public void ShowBallon_Default(string BallonTipTitle, string BallonTipText, int nTimeoutInSeconds) + { + trayNotify.ShowBalloonTip((nTimeoutInSeconds * 1000), BallonTipTitle, BallonTipText, ToolTipIcon.None); + } + + /// + /// Pops up a Error Ballon over the System Tray Icon + /// + /// Specify the Timeout in Seconds + //public void ShowBallon_Error(string BallonTipTitle, string BallonTipText, int nTimeoutInSeconds = 10) + public void ShowBallon_Error(string BallonTipTitle, string BallonTipText, int nTimeoutInSeconds) + { + trayNotify.ShowBalloonTip((nTimeoutInSeconds * 1000), BallonTipTitle, BallonTipText, ToolTipIcon.Error); + } + + /// + /// Pops up a Warning Ballon over the System Tray Icon + /// + /// Specify the Timeout in Seconds + //public void ShowBallon_Warning(string BallonTipTitle, string BallonTipText, int nTimeoutInSeconds = 10) + public void ShowBallon_Warning(string BallonTipTitle, string BallonTipText, int nTimeoutInSeconds) + { + trayNotify.ShowBalloonTip((nTimeoutInSeconds * 1000), BallonTipTitle, BallonTipText, ToolTipIcon.Warning); + } + + /// + /// Pops up a Info Ballon over the System Tray Icon + /// + /// Specify the Timeout in Seconds + //public void ShowBallon_Info(string BallonTipTitle, string BallonTipText, int nTimeoutInSeconds = 10) + public void ShowBallon_Info(string BallonTipTitle, string BallonTipText, int nTimeoutInSeconds) + { + trayNotify.ShowBalloonTip((nTimeoutInSeconds * 1000), BallonTipTitle, BallonTipText, ToolTipIcon.Info); + } + + #endregion + } +} diff --git a/@integrate/TTimer.cs b/@integrate/TTimer.cs new file mode 100644 index 0000000..17a24d9 --- /dev/null +++ b/@integrate/TTimer.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; +using System.Windows.Threading; +using WinThread = System.Threading; + +namespace WatchdogLib.Thread +{ + /// + /// Wrapper class around Timer Objects + /// + public class TTimer : IDisposable + { + // private Members + private Timer _Timer = new Timer(); + private bool _disposed = false; + private Dispatcher _Dispatcher = null; + private ElapsedEventHandler _DispatchedElapsedEvent = null; + + /// + /// Creates a new Multi-threaded System.Timer + /// + /// Event Handler for Timer + /// Interval in Miliseconds + /// True to start the timer upon creation, false otherwise + /// A Timer Object, which should be Disposed by Caller + //public TTimer(ElapsedEventHandler ElapsedHandler, int IntervalMiliseconds = 1000, bool StartEnabled = false, bool bUseDispatcher = true) + public TTimer(ElapsedEventHandler ElapsedHandler, int IntervalMiliseconds, bool StartEnabled, bool bUseDispatcher) + { + if (ElapsedHandler != null) + { + _Timer = new System.Timers.Timer(); + + // The Primary Dispatcher thread is the thread that called us + if (bUseDispatcher) + { + _Dispatcher = Dispatcher.CurrentDispatcher; + _DispatchedElapsedEvent = ElapsedHandler; + _Timer.Elapsed += new ElapsedEventHandler(_Timer_Elapsed); + } + else + { + _Timer.Elapsed += ElapsedHandler; + } + + // Set the Interval / start + _Timer.Interval = IntervalMiliseconds; + _Timer.Enabled = StartEnabled; + if (StartEnabled) + _Timer.Start(); + + // Keep the timer alive + GC.KeepAlive(_Timer); + } + } + + /// + /// For Dispatching the Event to the Primary Dispatcher Thread + /// + void _Timer_Elapsed(object sender, ElapsedEventArgs e) + { + object[] param_s = new object[] { sender, e }; + _Dispatcher.Invoke((ElapsedEventHandler)_DispatchedElapsedEvent, param_s); + } + + /// + /// Manually Start the Timer + /// + public void Start() + { + Stop(); // First Stop(), an existing Timer + _Timer.Enabled = true; + _Timer.Start(); + } + + /// + /// Manually Start the Timer at a new Interval + /// + /// Interval in Miliseconds + public void Start(uint IntervalMiliseconds) + { + Stop(); // First Stop(), an existing Timer + _Timer.Interval = IntervalMiliseconds; + _Timer.Enabled = true; + _Timer.Start(); + } + + /// + /// Manually Start the Timer at a new Interval + /// + /// Interval as a TimeSpan + public void Start(TimeSpan tsInterval) + { + Stop(); // First Stop(), an existing Timer + _Timer.Interval = tsInterval.TotalMilliseconds; + _Timer.Enabled = true; + _Timer.Start(); + } + + /// + /// Manually Stop the Timer + /// + public void Stop() + { + _Timer.Enabled = false; + _Timer.Stop(); + } + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + + // Use SupressFinalize in case a subclass + // of this type implements a finalizer + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + if (_Timer != null) + _Timer.Dispose(); + } + + // Indicate that the instance has been disposed. + _Timer = null; + _disposed = true; + } + } + + #endregion + } +} diff --git a/@integrate/Trinet.Core.IO.Ntfs/AlternateDataStreamInfo.cs b/@integrate/Trinet.Core.IO.Ntfs/AlternateDataStreamInfo.cs new file mode 100644 index 0000000..ef7f713 --- /dev/null +++ b/@integrate/Trinet.Core.IO.Ntfs/AlternateDataStreamInfo.cs @@ -0,0 +1,654 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Security; +using System.Security.Permissions; + +namespace Trinet.Core.IO.Ntfs +{ + /// + /// Represents the details of an alternative data stream. + /// + [DebuggerDisplay("{FullPath}")] + public sealed class AlternateDataStreamInfo : IEquatable + { + #region Private Data + + private readonly string _fullPath; + private readonly string _filePath; + private readonly string _streamName; + private readonly FileStreamType _streamType; + private readonly FileStreamAttributes _attributes; + private readonly long _size; + private readonly bool _exists; + + #endregion + + #region Constructor + + /// + /// Initializes a new instance of the class. + /// + /// + /// The full path of the file. + /// This argument must not be . + /// + /// + /// The containing the stream information. + /// + internal AlternateDataStreamInfo(string filePath, SafeNativeMethods.Win32StreamInfo info) + { + _filePath = filePath; + _streamName = info.StreamName; + _streamType = info.StreamType; + _attributes = info.StreamAttributes; + _size = info.StreamSize; + _exists = true; + + _fullPath = SafeNativeMethods.BuildStreamPath(_filePath, _streamName); + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The full path of the file. + /// This argument must not be . + /// + /// + /// The name of the stream + /// This argument must not be . + /// + /// + /// The full path of the stream. + /// If this argument is , it will be generated from the + /// and arguments. + /// + /// + /// if the stream exists; + /// otherwise, . + /// + internal AlternateDataStreamInfo(string filePath, string streamName, string fullPath, bool exists) + { + if (string.IsNullOrEmpty(fullPath)) fullPath = SafeNativeMethods.BuildStreamPath(filePath, streamName); + _streamType = FileStreamType.AlternateDataStream; + + _filePath = filePath; + _streamName = streamName; + _fullPath = fullPath; + _exists = exists; + + if (_exists) + { + _size = SafeNativeMethods.GetFileSize(_fullPath); + } + } + + #endregion + + #region Properties + + /// + /// Returns the full path of this stream. + /// + /// + /// The full path of this stream. + /// + public string FullPath + { + get { return _fullPath; } + } + + /// + /// Returns the full path of the file which contains the stream. + /// + /// + /// The full file-system path of the file which contains the stream. + /// + public string FilePath + { + get { return _filePath; } + } + + /// + /// Returns the name of the stream. + /// + /// + /// The name of the stream. + /// + public string Name + { + get { return _streamName; } + } + + /// + /// Returns a flag indicating whether the specified stream exists. + /// + /// + /// if the stream exists; + /// otherwise, . + /// + public bool Exists + { + get { return _exists; } + } + + /// + /// Returns the size of the stream, in bytes. + /// + /// + /// The size of the stream, in bytes. + /// + public long Size + { + get { return _size; } + } + + /// + /// Returns the type of data. + /// + /// + /// One of the values. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public FileStreamType StreamType + { + get { return _streamType; } + } + + /// + /// Returns attributes of the data stream. + /// + /// + /// A combination of values. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public FileStreamAttributes Attributes + { + get { return _attributes; } + } + + #endregion + + #region Methods + + #region -IEquatable + + /// + /// Returns a that represents the current instance. + /// + /// + /// A that represents the current instance. + /// + public override string ToString() + { + return this.FullPath; + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + var comparer = StringComparer.OrdinalIgnoreCase; + return comparer.GetHashCode(_filePath ?? string.Empty) + ^ comparer.GetHashCode(_streamName ?? string.Empty); + } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// + /// An object to compare with this object. + /// + /// + /// if the current object is equal to the parameter; + /// otherwise, . + /// + public override bool Equals(object obj) + { + if (object.ReferenceEquals(null, obj)) return false; + if (object.ReferenceEquals(this, obj)) return true; + + AlternateDataStreamInfo other = obj as AlternateDataStreamInfo; + if (!object.ReferenceEquals(null, other)) return this.Equals(other); + + return false; + } + + /// + /// Returns a value indicating whether + /// this instance is equal to another instance. + /// + /// + /// The instance to compare to. + /// + /// + /// if the current object is equal to the parameter; + /// otherwise, . + /// + public bool Equals(AlternateDataStreamInfo other) + { + if (object.ReferenceEquals(null, other)) return false; + if (object.ReferenceEquals(this, other)) return true; + + var comparer = StringComparer.OrdinalIgnoreCase; + return comparer.Equals(this._filePath ?? string.Empty, other._filePath ?? string.Empty) + && comparer.Equals(this._streamName ?? string.Empty, other._streamName ?? string.Empty); + } + + /// + /// The equality operator. + /// + /// + /// The first object. + /// + /// + /// The second object. + /// + /// + /// if the two objects are equal; + /// otherwise, . + /// + public static bool operator ==(AlternateDataStreamInfo first, AlternateDataStreamInfo second) + { + if (object.ReferenceEquals(first, second)) return true; + if (object.ReferenceEquals(null, first)) return false; + if (object.ReferenceEquals(null, second)) return false; + return first.Equals(second); + } + + /// + /// The inequality operator. + /// + /// + /// The first object. + /// + /// + /// The second object. + /// + /// + /// if the two objects are not equal; + /// otherwise, . + /// + public static bool operator !=(AlternateDataStreamInfo first, AlternateDataStreamInfo second) + { + if (object.ReferenceEquals(first, second)) return false; + if (object.ReferenceEquals(null, first)) return true; + if (object.ReferenceEquals(null, second)) return true; + return !first.Equals(second); + } + + #endregion + + #region -Delete + + /// + /// Deletes this stream from the parent file. + /// + /// + /// if the stream was deleted; + /// otherwise, . + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + /// + /// The specified file is in use. + /// + /// + /// The path of the stream is invalid. + /// + public bool Delete() + { + const FileIOPermissionAccess permAccess = FileIOPermissionAccess.Write; + new FileIOPermission(permAccess, _filePath).Demand(); + return SafeNativeMethods.SafeDeleteFile(this.FullPath); + } + + #endregion + + #region -Open + + /// + /// Calculates the access to demand. + /// + /// + /// The . + /// + /// + /// The . + /// + /// + /// The . + /// + private static FileIOPermissionAccess CalculateAccess(FileMode mode, FileAccess access) + { + FileIOPermissionAccess permAccess = FileIOPermissionAccess.NoAccess; + switch (mode) + { + case FileMode.Append: + permAccess = FileIOPermissionAccess.Append; + break; + + case FileMode.Create: + case FileMode.CreateNew: + case FileMode.OpenOrCreate: + case FileMode.Truncate: + permAccess = FileIOPermissionAccess.Write; + break; + + case FileMode.Open: + permAccess = FileIOPermissionAccess.Read; + break; + } + switch (access) + { + case FileAccess.ReadWrite: + permAccess |= FileIOPermissionAccess.Write; + permAccess |= FileIOPermissionAccess.Read; + break; + + case FileAccess.Write: + permAccess |= FileIOPermissionAccess.Write; + break; + + case FileAccess.Read: + permAccess |= FileIOPermissionAccess.Read; + break; + } + + return permAccess; + } + + /// + /// Opens this alternate data stream. + /// + /// + /// A value that specifies whether a stream is created if one does not exist, + /// and determines whether the contents of existing streams are retained or overwritten. + /// + /// + /// A value that specifies the operations that can be performed on the stream. + /// + /// + /// A value specifying the type of access other threads have to the file. + /// + /// + /// The size of the buffer to use. + /// + /// + /// to enable async-IO; + /// otherwise, . + /// + /// + /// A for this alternate data stream. + /// + /// + /// is less than or equal to zero. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + /// + /// The specified file is in use. + /// + /// + /// The path of the stream is invalid. + /// + /// + /// There was an error opening the stream. + /// + public FileStream Open(FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) + { + if (0 >= bufferSize) throw new ArgumentOutOfRangeException("bufferSize", bufferSize, null); + + FileIOPermissionAccess permAccess = CalculateAccess(mode, access); + new FileIOPermission(permAccess, _filePath).Demand(); + + SafeNativeMethods.NativeFileFlags flags = useAsync ? SafeNativeMethods.NativeFileFlags.Overlapped : 0; + var handle = SafeNativeMethods.SafeCreateFile(this.FullPath, access.ToNative(), share, IntPtr.Zero, mode, flags, IntPtr.Zero); + if (handle.IsInvalid) SafeNativeMethods.ThrowLastIOError(this.FullPath); + return new FileStream(handle, access, bufferSize, useAsync); + } + + /// + /// Opens this alternate data stream. + /// + /// + /// A value that specifies whether a stream is created if one does not exist, + /// and determines whether the contents of existing streams are retained or overwritten. + /// + /// + /// A value that specifies the operations that can be performed on the stream. + /// + /// + /// A value specifying the type of access other threads have to the file. + /// + /// + /// The size of the buffer to use. + /// + /// + /// A for this alternate data stream. + /// + /// + /// is less than or equal to zero. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + /// + /// The specified file is in use. + /// + /// + /// The path of the stream is invalid. + /// + /// + /// There was an error opening the stream. + /// + public FileStream Open(FileMode mode, FileAccess access, FileShare share, int bufferSize) + { + return this.Open(mode, access, share, bufferSize, false); + } + + /// + /// Opens this alternate data stream. + /// + /// + /// A value that specifies whether a stream is created if one does not exist, + /// and determines whether the contents of existing streams are retained or overwritten. + /// + /// + /// A value that specifies the operations that can be performed on the stream. + /// + /// + /// A value specifying the type of access other threads have to the file. + /// + /// + /// A for this alternate data stream. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + /// + /// The specified file is in use. + /// + /// + /// The path of the stream is invalid. + /// + /// + /// There was an error opening the stream. + /// + public FileStream Open(FileMode mode, FileAccess access, FileShare share) + { + return this.Open(mode, access, share, SafeNativeMethods.DefaultBufferSize, false); + } + + /// + /// Opens this alternate data stream. + /// + /// + /// A value that specifies whether a stream is created if one does not exist, + /// and determines whether the contents of existing streams are retained or overwritten. + /// + /// + /// A value that specifies the operations that can be performed on the stream. + /// + /// + /// A for this alternate data stream. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + /// + /// The specified file is in use. + /// + /// + /// The path of the stream is invalid. + /// + /// + /// There was an error opening the stream. + /// + public FileStream Open(FileMode mode, FileAccess access) + { + return this.Open(mode, access, FileShare.None, SafeNativeMethods.DefaultBufferSize, false); + } + + /// + /// Opens this alternate data stream. + /// + /// + /// A value that specifies whether a stream is created if one does not exist, + /// and determines whether the contents of existing streams are retained or overwritten. + /// + /// + /// A for this alternate data stream. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + /// + /// The specified file is in use. + /// + /// + /// The path of the stream is invalid. + /// + /// + /// There was an error opening the stream. + /// + public FileStream Open(FileMode mode) + { + FileAccess access = (FileMode.Append == mode) ? FileAccess.Write : FileAccess.ReadWrite; + return this.Open(mode, access, FileShare.None, SafeNativeMethods.DefaultBufferSize, false); + } + + #endregion + + #region -OpenRead / OpenWrite / OpenText + + /// + /// Opens this stream for reading. + /// + /// + /// A read-only for this stream. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + /// + /// The specified file is in use. + /// + /// + /// The path of the stream is invalid. + /// + /// + /// There was an error opening the stream. + /// + public FileStream OpenRead() + { + return this.Open(FileMode.Open, FileAccess.Read, FileShare.Read); + } + + /// + /// Opens this stream for writing. + /// + /// + /// A write-only for this stream. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + /// + /// The specified file is in use. + /// + /// + /// The path of the stream is invalid. + /// + /// + /// There was an error opening the stream. + /// + public FileStream OpenWrite() + { + return this.Open(FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); + } + + /// + /// Opens this stream as a text file. + /// + /// + /// A which can be used to read the contents of this stream. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + /// + /// The specified file is in use. + /// + /// + /// The path of the stream is invalid. + /// + /// + /// There was an error opening the stream. + /// + public StreamReader OpenText() + { + Stream fileStream = this.Open(FileMode.Open, FileAccess.Read, FileShare.Read); + return new StreamReader(fileStream); + } + + #endregion + + #endregion + } +} diff --git a/@integrate/Trinet.Core.IO.Ntfs/FileStreamAttributes.cs b/@integrate/Trinet.Core.IO.Ntfs/FileStreamAttributes.cs new file mode 100644 index 0000000..dde135a --- /dev/null +++ b/@integrate/Trinet.Core.IO.Ntfs/FileStreamAttributes.cs @@ -0,0 +1,32 @@ +using System; + +namespace Trinet.Core.IO.Ntfs +{ + /// + /// Represents the attributes of a file stream. + /// + [Flags] + public enum FileStreamAttributes + { + /// + /// No attributes. + /// + None = 0, + /// + /// Set if the stream contains data that is modified when read. + /// + ModifiedWhenRead = 1, + /// + /// Set if the stream contains security data. + /// + ContainsSecurity = 2, + /// + /// Set if the stream contains properties. + /// + ContainsProperties = 4, + /// + /// Set if the stream is sparse. + /// + Sparse = 8, + } +} diff --git a/@integrate/Trinet.Core.IO.Ntfs/FileStreamType.cs b/@integrate/Trinet.Core.IO.Ntfs/FileStreamType.cs new file mode 100644 index 0000000..a0201a4 --- /dev/null +++ b/@integrate/Trinet.Core.IO.Ntfs/FileStreamType.cs @@ -0,0 +1,56 @@ +using System; + +namespace Trinet.Core.IO.Ntfs +{ + /// + /// Represents the type of data in a stream. + /// + public enum FileStreamType + { + /// + /// Unknown stream type. + /// + Unknown = 0, + /// + /// Standard data. + /// + Data = 1, + /// + /// Extended attribute data. + /// + ExtendedAttributes = 2, + /// + /// Security data. + /// + SecurityData = 3, + /// + /// Alternate data stream. + /// + AlternateDataStream = 4, + /// + /// Hard link information. + /// + Link = 5, + /// + /// Property data. + /// + PropertyData = 6, + /// + /// Object identifiers. + /// + ObjectId = 7, + /// + /// Reparse points. + /// + ReparseData = 8, + /// + /// Sparse file. + /// + SparseBlock = 9, + /// + /// Transactional data. + /// (Undocumented - BACKUP_TXFS_DATA) + /// + TransactionData = 10, + } +} diff --git a/@integrate/Trinet.Core.IO.Ntfs/FileSystem.cs b/@integrate/Trinet.Core.IO.Ntfs/FileSystem.cs new file mode 100644 index 0000000..c20a92c --- /dev/null +++ b/@integrate/Trinet.Core.IO.Ntfs/FileSystem.cs @@ -0,0 +1,452 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security; +using System.Security.Permissions; + +namespace Trinet.Core.IO.Ntfs +{ + using Resources = Properties.Resources; + + /// + /// File-system utilities. + /// + public static class FileSystem + { + #region Create FileSystemInfo + + /// + /// Creates a for the specified path. + /// + /// + /// The path of the file or directory. + /// + /// + /// The representing the file or directory. + /// + /// + /// is or empty. + /// + private static FileSystemInfo CreateInfo(string path) + { + if (string.IsNullOrEmpty(path)) throw new ArgumentNullException("path"); + + path = Path.GetFullPath(path); + if (!File.Exists(path) && Directory.Exists(path)) return new DirectoryInfo(path); + return new FileInfo(path); + } + + #endregion + + #region List Streams + + /// + /// (Extension Method)
+ /// Returns a read-only list of alternate data streams for the specified file. + ///
+ /// + /// The to inspect. + /// + /// + /// A read-only list of objects + /// representing the alternate data streams for the specified file, if any. + /// If no streams are found, returns an empty list. + /// + /// + /// is . + /// + /// + /// The specified does not exist. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission. + /// + public static IList ListAlternateDataStreams(this FileSystemInfo file) + { + if (null == file) throw new ArgumentNullException("file"); + if (!file.Exists) throw new FileNotFoundException(null, file.FullName); + + string path = file.FullName; + new FileIOPermission(FileIOPermissionAccess.Read, path).Demand(); + + return SafeNativeMethods.ListStreams(path) + .Select(s => new AlternateDataStreamInfo(path, s)) + .ToList().AsReadOnly(); + } + + /// + /// Returns a read-only list of alternate data streams for the specified file. + /// + /// + /// The full path of the file to inspect. + /// + /// + /// A read-only list of objects + /// representing the alternate data streams for the specified file, if any. + /// + /// + /// is or an empty string. + /// + /// + /// is not a valid file path. + /// + /// + /// The specified does not exist. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission. + /// + public static IList ListAlternateDataStreams(string filePath) + { + if (string.IsNullOrEmpty(filePath)) throw new ArgumentNullException("filePath"); + return CreateInfo(filePath).ListAlternateDataStreams(); + } + + #endregion + + #region Stream Exists + + /// + /// (Extension Method)
+ /// Returns a flag indicating whether the specified alternate data stream exists. + ///
+ /// + /// The to inspect. + /// + /// + /// The name of the stream to find. + /// + /// + /// if the specified stream exists; + /// otherwise, . + /// + /// + /// is . + /// + /// + /// contains invalid characters. + /// + public static bool AlternateDataStreamExists(this FileSystemInfo file, string streamName) + { + if (null == file) throw new ArgumentNullException("file"); + SafeNativeMethods.ValidateStreamName(streamName); + + string path = SafeNativeMethods.BuildStreamPath(file.FullName, streamName); + return -1 != SafeNativeMethods.SafeGetFileAttributes(path); + } + + /// + /// Returns a flag indicating whether the specified alternate data stream exists. + /// + /// + /// The path of the file to inspect. + /// + /// + /// The name of the stream to find. + /// + /// + /// if the specified stream exists; + /// otherwise, . + /// + /// + /// is or an empty string. + /// + /// + /// is not a valid file path. + /// -or- + /// contains invalid characters. + /// + public static bool AlternateDataStreamExists(string filePath, string streamName) + { + if (string.IsNullOrEmpty(filePath)) throw new ArgumentNullException("filePath"); + return CreateInfo(filePath).AlternateDataStreamExists(streamName); + } + + #endregion + + #region Open Stream + + /// + /// (Extension Method)
+ /// Opens an alternate data stream. + ///
+ /// + /// The which contains the stream. + /// + /// + /// The name of the stream to open. + /// + /// + /// One of the values, indicating how the stream is to be opened. + /// + /// + /// An representing the stream. + /// + /// + /// is . + /// + /// + /// The specified was not found. + /// + /// + /// contains invalid characters. + /// + /// + /// is either or . + /// + /// + /// is , and the stream doesn't exist. + /// -or- + /// is , and the stream already exists. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + public static AlternateDataStreamInfo GetAlternateDataStream(this FileSystemInfo file, string streamName, FileMode mode) + { + if (null == file) throw new ArgumentNullException("file"); + if (!file.Exists) throw new FileNotFoundException(null, file.FullName); + SafeNativeMethods.ValidateStreamName(streamName); + + if (FileMode.Truncate == mode || FileMode.Append == mode) + { + throw new NotSupportedException(string.Format(Resources.Culture, + Resources.Error_InvalidMode, mode)); + } + + FileIOPermissionAccess permAccess = (FileMode.Open == mode) ? FileIOPermissionAccess.Read : FileIOPermissionAccess.Read | FileIOPermissionAccess.Write; + new FileIOPermission(permAccess, file.FullName).Demand(); + + string path = SafeNativeMethods.BuildStreamPath(file.FullName, streamName); + bool exists = -1 != SafeNativeMethods.SafeGetFileAttributes(path); + + if (!exists && FileMode.Open == mode) + { + throw new IOException(string.Format(Resources.Culture, + Resources.Error_StreamNotFound, streamName, file.Name)); + } + if (exists && FileMode.CreateNew == mode) + { + throw new IOException(string.Format(Resources.Culture, + Resources.Error_StreamExists, streamName, file.Name)); + } + + return new AlternateDataStreamInfo(file.FullName, streamName, path, exists); + } + + /// + /// (Extension Method)
+ /// Opens an alternate data stream. + ///
+ /// + /// The which contains the stream. + /// + /// + /// The name of the stream to open. + /// + /// + /// An representing the stream. + /// + /// + /// is . + /// + /// + /// The specified was not found. + /// + /// + /// contains invalid characters. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + public static AlternateDataStreamInfo GetAlternateDataStream(this FileSystemInfo file, string streamName) + { + return file.GetAlternateDataStream(streamName, FileMode.OpenOrCreate); + } + + /// + /// Opens an alternate data stream. + /// + /// + /// The path of the file which contains the stream. + /// + /// + /// The name of the stream to open. + /// + /// + /// One of the values, indicating how the stream is to be opened. + /// + /// + /// An representing the stream. + /// + /// + /// is or an empty string. + /// + /// + /// The specified was not found. + /// + /// + /// is not a valid file path. + /// -or- + /// contains invalid characters. + /// + /// + /// is either or . + /// + /// + /// is , and the stream doesn't exist. + /// -or- + /// is , and the stream already exists. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + public static AlternateDataStreamInfo GetAlternateDataStream(string filePath, string streamName, FileMode mode) + { + if (string.IsNullOrEmpty(filePath)) throw new ArgumentNullException("filePath"); + return CreateInfo(filePath).GetAlternateDataStream(streamName, mode); + } + + /// + /// Opens an alternate data stream. + /// + /// + /// The path of the file which contains the stream. + /// + /// + /// The name of the stream to open. + /// + /// + /// An representing the stream. + /// + /// + /// is or an empty string. + /// + /// + /// The specified was not found. + /// + /// + /// is not a valid file path. + /// -or- + /// contains invalid characters. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + public static AlternateDataStreamInfo GetAlternateDataStream(string filePath, string streamName) + { + return GetAlternateDataStream(filePath, streamName, FileMode.OpenOrCreate); + } + + #endregion + + #region Delete Stream + + /// + /// (Extension Method)
+ /// Deletes the specified alternate data stream if it exists. + ///
+ /// + /// The to inspect. + /// + /// + /// The name of the stream to delete. + /// + /// + /// if the specified stream is deleted; + /// otherwise, . + /// + /// + /// is . + /// + /// + /// contains invalid characters. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + /// + /// The specified file is in use. + /// + public static bool DeleteAlternateDataStream(this FileSystemInfo file, string streamName) + { + if (null == file) throw new ArgumentNullException("file"); + SafeNativeMethods.ValidateStreamName(streamName); + + const FileIOPermissionAccess permAccess = FileIOPermissionAccess.Write; + new FileIOPermission(permAccess, file.FullName).Demand(); + + var result = false; + if (file.Exists) + { + string path = SafeNativeMethods.BuildStreamPath(file.FullName, streamName); + if (-1 != SafeNativeMethods.SafeGetFileAttributes(path)) + { + result = SafeNativeMethods.SafeDeleteFile(path); + } + } + + return result; + } + + /// + /// Deletes the specified alternate data stream if it exists. + /// + /// + /// The path of the file to inspect. + /// + /// + /// The name of the stream to find. + /// + /// + /// if the specified stream is deleted; + /// otherwise, . + /// + /// + /// is or an empty string. + /// + /// + /// is not a valid file path. + /// -or- + /// contains invalid characters. + /// + /// + /// The caller does not have the required permission. + /// + /// + /// The caller does not have the required permission, or the file is read-only. + /// + /// + /// The specified file is in use. + /// + public static bool DeleteAlternateDataStream(string filePath, string streamName) + { + if (string.IsNullOrEmpty(filePath)) throw new ArgumentNullException("filePath"); + return CreateInfo(filePath).DeleteAlternateDataStream(streamName); + } + + #endregion + } +} diff --git a/@integrate/Trinet.Core.IO.Ntfs/SafeHGlobalHandle.cs b/@integrate/Trinet.Core.IO.Ntfs/SafeHGlobalHandle.cs new file mode 100644 index 0000000..71e19db --- /dev/null +++ b/@integrate/Trinet.Core.IO.Ntfs/SafeHGlobalHandle.cs @@ -0,0 +1,118 @@ +using System; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; + +namespace Trinet.Core.IO.Ntfs +{ + /// + /// A for a global memory allocation. + /// + internal sealed class SafeHGlobalHandle : SafeHandle + { + #region Private Data + + private readonly int _size; + + #endregion + + #region Constructor + + /// + /// Initializes a new instance of the class. + /// + /// + /// The initial handle value. + /// + /// + /// The size of this memory block, in bytes. + /// + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + private SafeHGlobalHandle(IntPtr toManage, int size) : base(IntPtr.Zero, true) + { + _size = size; + base.SetHandle(toManage); + } + + /// + /// Initializes a new instance of the class. + /// + private SafeHGlobalHandle() : base(IntPtr.Zero, true) + { + } + + #endregion + + #region Properties + + /// + /// Gets a value indicating whether the handle value is invalid. + /// + /// + /// if the handle value is invalid; + /// otherwise, . + /// + public override bool IsInvalid + { + get { return IntPtr.Zero == base.handle; } + } + + /// + /// Returns the size of this memory block. + /// + /// + /// The size of this memory block, in bytes. + /// + public int Size + { + get { return _size; } + } + + #endregion + + #region Methods + + /// + /// Allocates memory from the unmanaged memory of the process using GlobalAlloc. + /// + /// + /// The number of bytes in memory required. + /// + /// + /// A representing the memory. + /// + /// + /// There is insufficient memory to satisfy the request. + /// + public static SafeHGlobalHandle Allocate(int bytes) + { + return new SafeHGlobalHandle(Marshal.AllocHGlobal(bytes), bytes); + } + + /// + /// Returns an invalid handle. + /// + /// + /// An invalid . + /// + public static SafeHGlobalHandle Invalid() + { + return new SafeHGlobalHandle(); + } + + /// + /// Executes the code required to free the handle. + /// + /// + /// if the handle is released successfully; + /// otherwise, in the event of a catastrophic failure, . + /// In this case, it generates a releaseHandleFailed MDA Managed Debugging Assistant. + /// + protected override bool ReleaseHandle() + { + Marshal.FreeHGlobal(base.handle); + return true; + } + + #endregion + } +} diff --git a/@integrate/Trinet.Core.IO.Ntfs/SafeNativeMethods.cs b/@integrate/Trinet.Core.IO.Ntfs/SafeNativeMethods.cs new file mode 100644 index 0000000..33028a4 --- /dev/null +++ b/@integrate/Trinet.Core.IO.Ntfs/SafeNativeMethods.cs @@ -0,0 +1,478 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Win32.SafeHandles; + +namespace Trinet.Core.IO.Ntfs +{ + using Resources = Properties.Resources; + + /// + /// Safe native methods. + /// + internal static class SafeNativeMethods + { + #region Constants and flags + + public const int MaxPath = 256; + private const string LongPathPrefix = @"\\?\"; + public const char StreamSeparator = ':'; + public const int DefaultBufferSize = 0x1000; + + private const int ErrorFileNotFound = 2; + + // "Characters whose integer representations are in the range from 1 through 31, + // except for alternate streams where these characters are allowed" + // http://msdn.microsoft.com/en-us/library/aa365247(v=VS.85).aspx + private static readonly char[] InvalidStreamNameChars = Path.GetInvalidFileNameChars().Where(c => c < 1 || c > 31).ToArray(); + + [Flags] + public enum NativeFileFlags : uint + { + WriteThrough = 0x80000000, + Overlapped = 0x40000000, + NoBuffering = 0x20000000, + RandomAccess = 0x10000000, + SequentialScan = 0x8000000, + DeleteOnClose = 0x4000000, + BackupSemantics = 0x2000000, + PosixSemantics = 0x1000000, + OpenReparsePoint = 0x200000, + OpenNoRecall = 0x100000 + } + + [Flags] + public enum NativeFileAccess : uint + { + GenericRead = 0x80000000, + GenericWrite = 0x40000000 + } + + #endregion + + #region P/Invoke Structures + + [StructLayout(LayoutKind.Sequential)] + private struct LargeInteger + { + public readonly int Low; + public readonly int High; + + public long ToInt64() + { + return (this.High * 0x100000000) + this.Low; + } + + /* + public static LargeInteger FromInt64(long value) + { + return new LargeInteger + { + Low = (int)(value & 0x11111111), + High = (int)((value / 0x100000000) & 0x11111111) + }; + } + */ + } + + [StructLayout(LayoutKind.Sequential)] + private struct Win32StreamId + { + public readonly int StreamId; + public readonly int StreamAttributes; + public LargeInteger Size; + public readonly int StreamNameSize; + } + +/* + [StructLayout(LayoutKind.Sequential)] + private struct FileInformationByHandle + { + public int dwFileAttributes; + public LargeInteger ftCreationTime; + public LargeInteger ftLastAccessTime; + public LargeInteger ftLastWriteTime; + public int dwVolumeSerialNumber; + public LargeInteger FileSize; + public int nNumberOfLinks; + public LargeInteger FileIndex; + } +*/ + + #endregion + + #region P/Invoke Methods + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, BestFitMapping = false, ThrowOnUnmappableChar = true)] + private static extern int FormatMessage( + int dwFlags, + IntPtr lpSource, + int dwMessageId, + int dwLanguageId, + StringBuilder lpBuffer, + int nSize, + IntPtr vaListArguments); + + [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern int GetFileAttributes(string fileName); + + [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GetFileSizeEx(SafeFileHandle handle, out LargeInteger size); + + [DllImport("kernel32.dll")] + private static extern int GetFileType(SafeFileHandle handle); + + [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern SafeFileHandle CreateFile( + string name, + NativeFileAccess access, + FileShare share, + IntPtr security, + FileMode mode, + NativeFileFlags flags, + IntPtr template); + + [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool DeleteFile(string name); + + [DllImport("kernel32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool BackupRead( + SafeFileHandle hFile, + ref Win32StreamId pBuffer, + int numberOfBytesToRead, + out int numberOfBytesRead, + [MarshalAs(UnmanagedType.Bool)] bool abort, + [MarshalAs(UnmanagedType.Bool)] bool processSecurity, + ref IntPtr context); + + [DllImport("kernel32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool BackupRead( + SafeFileHandle hFile, + SafeHGlobalHandle pBuffer, + int numberOfBytesToRead, + out int numberOfBytesRead, + [MarshalAs(UnmanagedType.Bool)] bool abort, + [MarshalAs(UnmanagedType.Bool)] bool processSecurity, + ref IntPtr context); + + [DllImport("kernel32", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool BackupSeek( + SafeFileHandle hFile, + int bytesToSeekLow, + int bytesToSeekHigh, + out int bytesSeekedLow, + out int bytesSeekedHigh, + ref IntPtr context); + + #endregion + + #region Utility Structures + + public struct Win32StreamInfo + { + public FileStreamType StreamType; + public FileStreamAttributes StreamAttributes; + public long StreamSize; + public string StreamName; + } + + #endregion + + #region Utility Methods + + private static int MakeHRFromErrorCode(int errorCode) + { + return (-2147024896 | errorCode); + } + + private static string GetErrorMessage(int errorCode) + { + var lpBuffer = new StringBuilder(0x200); + if (0 != FormatMessage(0x3200, IntPtr.Zero, errorCode, 0, lpBuffer, lpBuffer.Capacity, IntPtr.Zero)) + { + return lpBuffer.ToString(); + } + + return string.Format(Resources.Culture, Resources.Error_UnknownError, errorCode); + } + + private static void ThrowIOError(int errorCode, string path) + { + switch (errorCode) + { + case 0: + { + break; + } + case 2: // File not found + { + if (string.IsNullOrEmpty(path)) throw new FileNotFoundException(); + throw new FileNotFoundException(null, path); + } + case 3: // Directory not found + { + if (string.IsNullOrEmpty(path)) throw new DirectoryNotFoundException(); + throw new DirectoryNotFoundException(string.Format(Resources.Culture, Resources.Error_DirectoryNotFound, path)); + } + case 5: // Access denied + { + if (string.IsNullOrEmpty(path)) throw new UnauthorizedAccessException(); + throw new UnauthorizedAccessException(string.Format(Resources.Culture, Resources.Error_AccessDenied_Path, path)); + } + case 15: // Drive not found + { + if (string.IsNullOrEmpty(path)) throw new DriveNotFoundException(); + throw new DriveNotFoundException(string.Format(Resources.Culture, Resources.Error_DriveNotFound, path)); + } + case 32: // Sharing violation + { + if (string.IsNullOrEmpty(path)) throw new IOException(GetErrorMessage(errorCode), MakeHRFromErrorCode(errorCode)); + throw new IOException(string.Format(Resources.Culture, Resources.Error_SharingViolation, path), MakeHRFromErrorCode(errorCode)); + } + case 80: // File already exists + { + if (!string.IsNullOrEmpty(path)) + { + throw new IOException(string.Format(Resources.Culture, Resources.Error_FileAlreadyExists, path), MakeHRFromErrorCode(errorCode)); + } + break; + } + case 87: // Invalid parameter + { + throw new IOException(GetErrorMessage(errorCode), MakeHRFromErrorCode(errorCode)); + } + case 183: // File or directory already exists + { + if (!string.IsNullOrEmpty(path)) + { + throw new IOException(string.Format(Resources.Culture, Resources.Error_AlreadyExists, path), MakeHRFromErrorCode(errorCode)); + } + break; + } + case 206: // Path too long + { + throw new PathTooLongException(); + } + case 995: // Operation cancelled + { + throw new OperationCanceledException(); + } + default: + { + Marshal.ThrowExceptionForHR(MakeHRFromErrorCode(errorCode)); + break; + } + } + } + + public static void ThrowLastIOError(string path) + { + int errorCode = Marshal.GetLastWin32Error(); + if (0 != errorCode) + { + int hr = Marshal.GetHRForLastWin32Error(); + if (0 <= hr) throw new Win32Exception(errorCode); + ThrowIOError(errorCode, path); + } + } + + public static NativeFileAccess ToNative(this FileAccess access) + { + NativeFileAccess result = 0; + if (FileAccess.Read == (FileAccess.Read & access)) result |= NativeFileAccess.GenericRead; + if (FileAccess.Write == (FileAccess.Write & access)) result |= NativeFileAccess.GenericWrite; + return result; + } + + public static string BuildStreamPath(string filePath, string streamName) + { + string result = filePath; + if (!string.IsNullOrEmpty(filePath)) + { + if (1 == result.Length) result = ".\\" + result; + result += StreamSeparator + streamName + StreamSeparator + "$DATA"; + if (MaxPath <= result.Length) result = LongPathPrefix + result; + } + return result; + } + + public static void ValidateStreamName(string streamName) + { + if (!string.IsNullOrEmpty(streamName) && -1 != streamName.IndexOfAny(InvalidStreamNameChars)) + { + throw new ArgumentException(Resources.Error_InvalidFileChars); + } + } + + public static int SafeGetFileAttributes(string name) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); + + int result = GetFileAttributes(name); + if (-1 == result) + { + int errorCode = Marshal.GetLastWin32Error(); + if (ErrorFileNotFound != errorCode) ThrowLastIOError(name); + } + + return result; + } + + public static bool SafeDeleteFile(string name) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); + + bool result = DeleteFile(name); + if (!result) + { + int errorCode = Marshal.GetLastWin32Error(); + if (ErrorFileNotFound != errorCode) ThrowLastIOError(name); + } + + return result; + } + + public static SafeFileHandle SafeCreateFile(string path, NativeFileAccess access, FileShare share, IntPtr security, FileMode mode, NativeFileFlags flags, IntPtr template) + { + SafeFileHandle result = CreateFile(path, access, share, security, mode, flags, template); + if (!result.IsInvalid && 1 != GetFileType(result)) + { + result.Dispose(); + throw new NotSupportedException(string.Format(Resources.Culture, + Resources.Error_NonFile, path)); + } + + return result; + } + + private static long GetFileSize(string path, SafeFileHandle handle) + { + long result = 0L; + if (null != handle && !handle.IsInvalid) + { + LargeInteger value; + if (GetFileSizeEx(handle, out value)) + { + result = value.ToInt64(); + } + else + { + ThrowLastIOError(path); + } + } + + return result; + } + + public static long GetFileSize(string path) + { + long result = 0L; + if (!string.IsNullOrEmpty(path)) + { + using (SafeFileHandle handle = SafeCreateFile(path, NativeFileAccess.GenericRead, FileShare.Read, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero)) + { + result = GetFileSize(path, handle); + } + } + + return result; + } + + public static IList ListStreams(string filePath) + { + if (string.IsNullOrEmpty(filePath)) throw new ArgumentNullException("filePath"); + if (-1 != filePath.IndexOfAny(Path.GetInvalidPathChars())) throw new ArgumentException(Resources.Error_InvalidFileChars, "filePath"); + + var result = new List(); + + using (SafeFileHandle hFile = SafeCreateFile(filePath, NativeFileAccess.GenericRead, FileShare.Read, IntPtr.Zero, FileMode.Open, NativeFileFlags.BackupSemantics, IntPtr.Zero)) + using (var hName = new StreamName()) + { + if (!hFile.IsInvalid) + { + var streamId = new Win32StreamId(); + int dwStreamHeaderSize = Marshal.SizeOf(streamId); + bool finished = false; + IntPtr context = IntPtr.Zero; + int bytesRead; + string name; + + try + { + while (!finished) + { + // Read the next stream header: + if (!BackupRead(hFile, ref streamId, dwStreamHeaderSize, out bytesRead, false, false, ref context)) + { + finished = true; + } + else if (dwStreamHeaderSize != bytesRead) + { + finished = true; + } + else + { + // Read the stream name: + if (0 >= streamId.StreamNameSize) + { + name = null; + } + else + { + hName.EnsureCapacity(streamId.StreamNameSize); + if (!BackupRead(hFile, hName.MemoryBlock, streamId.StreamNameSize, out bytesRead, false, false, ref context)) + { + name = null; + finished = true; + } + else + { + // Unicode chars are 2 bytes: + name = hName.ReadStreamName(bytesRead >> 1); + } + } + + // Add the stream info to the result: + if (!string.IsNullOrEmpty(name)) + { + result.Add(new Win32StreamInfo + { + StreamType = (FileStreamType)streamId.StreamId, + StreamAttributes = (FileStreamAttributes)streamId.StreamAttributes, + StreamSize = streamId.Size.ToInt64(), + StreamName = name + }); + } + + // Skip the contents of the stream: + int bytesSeekedLow, bytesSeekedHigh; + if (!finished && !BackupSeek(hFile, streamId.Size.Low, streamId.Size.High, out bytesSeekedLow, out bytesSeekedHigh, ref context)) + { + finished = true; + } + } + } + } + finally + { + // Abort the backup: + BackupRead(hFile, hName.MemoryBlock, 0, out bytesRead, true, false, ref context); + } + } + } + + return result; + } + + #endregion + } +} diff --git a/@integrate/Trinet.Core.IO.Ntfs/StreamName.cs b/@integrate/Trinet.Core.IO.Ntfs/StreamName.cs new file mode 100644 index 0000000..fdbe7ff --- /dev/null +++ b/@integrate/Trinet.Core.IO.Ntfs/StreamName.cs @@ -0,0 +1,134 @@ +using System; +using System.Runtime.InteropServices; + +namespace Trinet.Core.IO.Ntfs +{ + internal sealed class StreamName : IDisposable + { + #region Private Data + + private static readonly SafeHGlobalHandle _invalidBlock = SafeHGlobalHandle.Invalid(); + private SafeHGlobalHandle _memoryBlock = _invalidBlock; + + #endregion + + #region Constructor + + /// + /// Initializes a new instance of the class. + /// + public StreamName() + { + } + + #endregion + + #region Properties + + /// + /// Returns the handle to the block of memory. + /// + /// + /// The representing the block of memory. + /// + public SafeHGlobalHandle MemoryBlock + { + get { return _memoryBlock; } + } + + #endregion + + #region Methods + + /// + /// Performs application-defined tasks associated with freeing, + /// releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + if (!_memoryBlock.IsInvalid) + { + _memoryBlock.Dispose(); + _memoryBlock = _invalidBlock; + } + } + + /// + /// Ensures that there is sufficient memory allocated. + /// + /// + /// The required capacity of the block, in bytes. + /// + /// + /// There is insufficient memory to satisfy the request. + /// + public void EnsureCapacity(int capacity) + { + int currentSize = _memoryBlock.IsInvalid ? 0 : _memoryBlock.Size; + if (capacity > currentSize) + { + if (0 != currentSize) currentSize <<= 1; + if (capacity > currentSize) currentSize = capacity; + + if (!_memoryBlock.IsInvalid) _memoryBlock.Dispose(); + _memoryBlock = SafeHGlobalHandle.Allocate(currentSize); + } + } + + /// + /// Reads the Unicode string from the memory block. + /// + /// + /// The length of the string to read, in characters. + /// + /// + /// The string read from the memory block. + /// + public string ReadString(int length) + { + if (0 >= length || _memoryBlock.IsInvalid) return null; + if (length > _memoryBlock.Size) length = _memoryBlock.Size; + return Marshal.PtrToStringUni(_memoryBlock.DangerousGetHandle(), length); + } + + /// + /// Reads the string, and extracts the stream name. + /// + /// + /// The length of the string to read, in characters. + /// + /// + /// The stream name. + /// + public string ReadStreamName(int length) + { + string name = this.ReadString(length); + if (!string.IsNullOrEmpty(name)) + { + // Name is of the format ":NAME:$DATA\0" + int separatorIndex = name.IndexOf(SafeNativeMethods.StreamSeparator, 1); + if (-1 != separatorIndex) + { + name = name.Substring(1, separatorIndex - 1); + } + else + { + // Should never happen! + separatorIndex = name.IndexOf('\0'); + if (1 < separatorIndex) + { + name = name.Substring(1, separatorIndex - 1); + } + else + { + name = null; + } + } + } + + return name; + } + + #endregion + } +} diff --git a/@integrate/WCFHost.cs b/@integrate/WCFHost.cs new file mode 100644 index 0000000..51ad48d --- /dev/null +++ b/@integrate/WCFHost.cs @@ -0,0 +1,322 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ServiceModel; +using Watchdog.WatchdogLib.Monitor; +using Watchdog.WatchdogLib.WinForms; + +namespace Watchdog +{ + /// + /// WCF Operations Contract in order to allow the command-line process + /// to communicate with the running Application's instance + /// + [ServiceContract] + public interface IWatchdog + { + #region Application State Changes + + /// + /// Starts Monitoring + /// + [OperationContract] + void StartMonitoring(); + + /// + /// Pauses Monitoring + /// + [OperationContract] + void PauseMonitoring(); + + /// + /// Restarts Monitoring + /// + [OperationContract] + void RestartMonitoring(); + + /// + /// Restarts All Monitoring + /// + [OperationContract] + void RestartAllMonitoring(); + + /// + /// Stops Monitoring + /// + [OperationContract] + void StopMonitoring(); + + /// + /// Stops All Monitoring + /// + [OperationContract] + void StopAllMonitoring(); + + /// + /// Tell the Running Application to Reload the Configuration + /// + [OperationContract] + void ReloadConfigurationNextInterval(); + + #endregion + + #region Ping + + /// + /// To Check if we can communicate with the other side + /// + /// PING_RETURN_VALUE + [OperationContract] + int Ping(); + + #endregion + } + + /// + /// Responsible for the WCF Callbacks + /// + public class WCFHost : IWatchdog + { + #region IWatchdog Members + + /// + /// Starts Monitoring + /// + public void StartMonitoring() + { + HiddenMainWindow.StartMonitoring(true, true); + } + + /// + /// Pauses Monitoring + /// + public void PauseMonitoring() + { + HiddenMainWindow.PauseMonitoring(true); + } + + /// + /// Restarts Monitoring + /// + public void RestartMonitoring() + { + HiddenMainWindow.RestartMonitoring(4, true); + } + + /// + /// Restarts All Monitoring + /// + public void RestartAllMonitoring() + { + HiddenMainWindow.RestartAllMonitoring(4, true); + } + + /// + /// Stops Monitoring + /// + public void StopMonitoring() + { + HiddenMainWindow.StopMonitoring(true, false); + } + + /// + /// Stops All Monitoring + /// + public void StopAllMonitoring() + { + HiddenMainWindow.StopAllMonitoring(true); + } + + /// + /// Tell the Running Application to Reload the Configuration + /// + public void ReloadConfigurationNextInterval() + { + App.xmlfile.ForceRefreshOnNext_ReadData = true; + } + + /// + /// Const Value to return on Ping + /// + public const int PING_RETURN_VALUE = 10; + /// + /// To Check if we can communicate with the other side + /// + /// PING_RETURN_VALUE + public int Ping() + { + return PING_RETURN_VALUE; + } + + #endregion + + #region Configuration Changes / Display Configuration Statics + + /// + /// Adds a Process from the Configuration and saves the xml + /// + /// Process Exe File name + /// Command Line Prms for Process + /// Working Directory + /// true if successfull, false otherwise + public static bool AddProcess(string ProcessExeFileNameNPath, string CommandlinePrms, string WorkingDir) + { + WatchdogConfiguration config = App.xmlfile.ReadData(false); + bool bSuccess = config.MonitoredProcesses.AddProcessExe(ProcessExeFileNameNPath, CommandlinePrms, WorkingDir); + if (bSuccess) + App.xmlfile.SaveData(); + return bSuccess; + } + + /// + /// Removes a Process from the Configuration and saves the xml + /// + /// Process Exe File name + /// Command Line Prms for Process + /// true if successfully deleted a process with matching ProcessExeFileNameNPath and CommandlinePrms + public static bool RemoveProcess(string ProcessExeFileNameNPath, string CommandlinePrms) + { + WatchdogConfiguration config = App.xmlfile.ReadData(false); + bool bSuccess = config.MonitoredProcesses.RemoveProcessExe(ProcessExeFileNameNPath, CommandlinePrms); + if (bSuccess) + App.xmlfile.SaveData(); + return bSuccess; + } + + /// + /// Adds a Service from the configuration and saves the xml + /// + /// Name of Service + /// true if sucessfull, false otherwise + public static bool AddService(string ServiceName) + { + WatchdogConfiguration config = App.xmlfile.ReadData(false); + bool bSuccess = config.MonitoredServices.AddServiceExe(ServiceName); + if (bSuccess) + App.xmlfile.SaveData(); + return bSuccess; + } + + /// + /// Removes a Service from the configuration and saves the xml + /// + /// Name of Service + /// true if sucessfull, false otherwise + public static bool RemoveService(string ServiceName) + { + WatchdogConfiguration config = App.xmlfile.ReadData(false); + bool bSuccess = config.MonitoredServices.RemoveServiceExe(ServiceName); + if (bSuccess) + App.xmlfile.SaveData(); + return bSuccess; + } + + /// + /// Returns an array of all Processes and their configurations (in a ';' seperated string) + /// + /// Process List with configuration + public static string[] ShowProcesses() + { + List ProcessList = new List(); + WatchdogConfiguration config = App.xmlfile.ReadData(false); + string s = String.Empty; + foreach (WatchdogConfiguration.ProcessExe p in config.MonitoredProcesses.ProcessExes) + { + if (String.IsNullOrEmpty(p.CommandLinePrms) && String.IsNullOrEmpty(p.WorkingDirectory)) + s = p.ProcessExeFileNameNPath; + else if (!String.IsNullOrEmpty(p.CommandLinePrms) && String.IsNullOrEmpty(p.WorkingDirectory)) + s = p.ProcessExeFileNameNPath + ";" + p.CommandLinePrms; + else if (!String.IsNullOrEmpty(p.CommandLinePrms) && !String.IsNullOrEmpty(p.WorkingDirectory)) + s = p.ProcessExeFileNameNPath + ";" + p.CommandLinePrms + ";" + p.WorkingDirectory; + else + continue; + ProcessList.Add(s); + } + return ProcessList.ToArray(); + } + + /// + /// Returns an array of all Services + /// + /// Services List + public static string[] ShowServices() + { + List ProcessList = new List(); + WatchdogConfiguration config = App.xmlfile.ReadData(false); + foreach (WatchdogConfiguration.ServiceExe s in config.MonitoredServices.ServiceExes) + ProcessList.Add(s.Name); + return ProcessList.ToArray(); + } + + #endregion + + #region Internal Statics + + private static ServiceHost _host = null; + + /// + /// Start the WCF Pipe Host (Watchdog) + /// + internal static void StartHost() + { + // Make sure it is closed + StopHost(); + _host = new ServiceHost(typeof(WCFHost), new Uri[] { new Uri("net.pipe://localhost") }); + _host.AddServiceEndpoint(typeof(IWatchdog), new NetNamedPipeBinding(), AppResx.GetString("APPLICATION_NAME_SHORT")); + + try + { + _host.Open(); + } + catch (Exception e) + { + App.log.Fatal("Unable to start WCF Pipe Host", e); + MsgBox.ShowFatalError("Unable to start WCF Pipe Host", "Pipe Host Failure", System.Windows.Forms.MessageBoxButtons.OK); + App.Current.Shutdown(); + } + } + + /// + /// Stop the WCF Pipe Host + /// + internal static void StopHost() + { + if (_host != null) + { + _host.Close(); + _host = null; + } + } + + /// + /// Called by Client to communicate with the server + /// + /// a valid IWatchDog Object or Null if error occured + internal static IWatchdog GetWatchDogClientInterface() + { + // We only should be calling this in CommandLine Mode. + if (App.State_SpecialMode_IsCommandLineSet()) + { + try + { + IWatchdog watchdoginterface = null; + ChannelFactory iwatchdogFactory = new ChannelFactory(new NetNamedPipeBinding(), new EndpointAddress(("net.pipe://localhost/" + AppResx.GetString("APPLICATION_NAME_SHORT")))); + watchdoginterface = iwatchdogFactory.CreateChannel(); + + // Try to ping the object! ~if it fails, target object doesn't exist + if (watchdoginterface.Ping() == WCFHost.PING_RETURN_VALUE) + return watchdoginterface; + } + catch (Exception) + { + // * Ignore this error we don't want to log this* + //App.log.Error("Failed to obtain WatchDogClientInterface", e); + } + } + return null; + } + + #endregion + } +} diff --git a/Assembly/AssemblyW.cs b/Assembly/AssemblyW.cs new file mode 100644 index 0000000..55d6836 --- /dev/null +++ b/Assembly/AssemblyW.cs @@ -0,0 +1,491 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Refl = System.Reflection; +using System.Diagnostics; +using System.Reflection; + +namespace Yaulw.Assembly +{ + /// + /// Useful Functions for Retrieving General Assembly Information + /// + public static class AssemblyW + { + #region Assembly State/Type + + /// + /// Assembly State/Type + /// + public enum AssemblyST + { + Calling, + Entry, + Executing + } + + /// + /// Internal Helper Function to Get the Appropriate Assembly State/Type + /// + /// State/Type of Assembly to Retrieve + /// Thrown if invalist AssemblyST is passed in + /// Returns the Specified State/Type Assembly + private static Refl.Assembly GetAssembly(AssemblyST st) + { + switch (st) + { + case AssemblyST.Calling: + return Refl.Assembly.GetCallingAssembly(); + case AssemblyST.Entry: + return Refl.Assembly.GetEntryAssembly(); + case AssemblyST.Executing: + return Refl.Assembly.GetExecutingAssembly(); + } + throw (new ArgumentException("Invalid AssemblyST")); + } + + #endregion + + #region Common Assembly Properties + + /// + /// Returns the Name of the Assembly + /// + /// State/Type of Assembly to Retrieve Info for + /// Thrown if invalid AssemblyST is passed in + /// Returns 'just the Name' of the Assembly + public static string GetAssemblyName(AssemblyST st) + { + string[] curAsmName = GetAssembly(st).FullName.Split(','); + return curAsmName[0]; + } + + /// + /// Returns the Name of the Assembly + /// + /// Assembly to Retrieve Info for + /// Thrown if invalid Assembly is passed in + /// Returns 'just the Name' of the Assembly + public static string GetAssemblyName(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + string[] curAsmName = asm.FullName.Split(','); + return curAsmName[0]; + } + + /// + /// Returns the Assembly Title + /// + /// State/Type of Assembly to Retrieve Info for + /// Thrown if invalid AssemblyST is passed in + /// Returns the Assembly Title, as specified in AssemblyInfo + public static string GetAssemblyTitle(AssemblyST st) + { + object[] attributes = GetAssembly(st).GetCustomAttributes(typeof(AssemblyTitleAttribute), false); + if (attributes.Length > 0) + { + AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0]; + return titleAttribute.Title; + } + return String.Empty; + } + + /// + /// Returns the Assembly Title + /// + /// Assembly to Retrieve Info for + /// Thrown if invalid Assembly is passed in + /// Returns the Assembly Title, as specified in AssemblyInfo + public static string GetAssemblyTitle(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + object[] attributes = asm.GetCustomAttributes(typeof(AssemblyTitleAttribute), false); + if (attributes.Length > 0) + { + AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0]; + return titleAttribute.Title; + } + return String.Empty; + } + + /// + /// Returns the Description of the Assembly + /// + /// State/Type of Assembly to Retrieve Info for + /// Thrown if invalid AssemblyST is passed in + /// Returns the Assembly Description, as specified in AssemblyInfo + public static string GetAssemblyDescription(AssemblyST st) + { + object[] attributes = GetAssembly(st).GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false); + if (attributes.Length > 0) + { + AssemblyDescriptionAttribute descriptionAttribute = (AssemblyDescriptionAttribute)attributes[0]; + return descriptionAttribute.Description; + } + return String.Empty; + } + + /// + /// Returns the Description of the Assembly + /// + /// Assembly to Retrieve Info for + /// Thrown if invalid Assembly is passed in + /// Returns the Assembly Description, as specified in AssemblyInfo + public static string GetAssemblyDescription(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + object[] attributes = asm.GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false); + if (attributes.Length > 0) + { + AssemblyDescriptionAttribute descriptionAttribute = (AssemblyDescriptionAttribute)attributes[0]; + return descriptionAttribute.Description; + } + return String.Empty; + } + + /// + /// Returns the Company Name of the Assembly + /// + /// State/Type of Assembly to Retrieve Info for + /// Thrown if invalid AssemblyST is passed in + /// Returns the Assembly Company, as specified in AssemblyInfo + public static string GetAssemblyCompany(AssemblyST st) + { + object[] attributes = GetAssembly(st).GetCustomAttributes(typeof(AssemblyCompanyAttribute), false); + if (attributes.Length > 0) + { + AssemblyCompanyAttribute companyAttribute = (AssemblyCompanyAttribute)attributes[0]; + return companyAttribute.Company; + } + return String.Empty; + } + + /// + /// Returns the Company Name of the Assembly + /// + /// Assembly to Retrieve Info for + /// Thrown if invalid Assembly is passed in + /// Returns the Assembly Company, as specified in AssemblyInfo + public static string GetAssemblyCompany(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + object[] attributes = asm.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false); + if (attributes.Length > 0) + { + AssemblyCompanyAttribute companyAttribute = (AssemblyCompanyAttribute)attributes[0]; + return companyAttribute.Company; + } + return String.Empty; + } + + /// + /// Returns the Product Name of the Assembly + /// + /// State/Type of Assembly to Retrieve Info for + /// Thrown if invalid AssemblyST is passed in + /// Returns the Assembly Product, as specified in AssemblyInfo + public static string GetAssemblyProductName(AssemblyST st) + { + object[] attributes = GetAssembly(st).GetCustomAttributes(typeof(AssemblyProductAttribute), false); + if (attributes.Length > 0) + { + AssemblyProductAttribute productAttribute = (AssemblyProductAttribute)attributes[0]; + return productAttribute.Product; + } + return String.Empty; + } + + /// + /// Returns the Product Name of the Assembly + /// + /// Assembly to Retrieve Info for + /// Thrown if invalid Assembly is passed in + /// Returns the Assembly Product, as specified in AssemblyInfo + public static string GetAssemblyProductName(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + object[] attributes = asm.GetCustomAttributes(typeof(AssemblyProductAttribute), false); + if (attributes.Length > 0) + { + AssemblyProductAttribute productAttribute = (AssemblyProductAttribute)attributes[0]; + return productAttribute.Product; + } + return String.Empty; + } + + /// + /// Returns the Copyright Information of the Assembly + /// + /// State/Type of Assembly to Retrieve Info for + /// Thrown if invalid AssemblyST is passed in + /// Returns the Assembly Copyright, as specified in AssemblyInfo + public static string GetAssemblyCopyright(AssemblyST st) + { + object[] attributes = GetAssembly(st).GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false); + if (attributes.Length > 0) + { + AssemblyCopyrightAttribute copyrightAttribute = (AssemblyCopyrightAttribute)attributes[0]; + return copyrightAttribute.Copyright; + } + return String.Empty; + } + + /// + /// Returns the Copyright Information of the Assembly + /// + /// Assembly to Retrieve Info for + /// Thrown if invalid Assembly is passed in + /// Returns the Assembly Copyright, as specified in AssemblyInfo + public static string GetAssemblyCopyright(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + object[] attributes = asm.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false); + if (attributes.Length > 0) + { + AssemblyCopyrightAttribute copyrightAttribute = (AssemblyCopyrightAttribute)attributes[0]; + return copyrightAttribute.Copyright; + } + return String.Empty; + } + + /// + /// Returns the Assembly Version + /// + /// State/Type of Assembly to Retrieve + /// Thrown if invalist AssemblyST is passed in + /// Returns the Assembly Version, as specified in AssemblyInfo + public static Version GetAssemblyVersion(AssemblyST st) + { + return SpecializedAssemblyInfo.GetAssemblyNameObj(st).Version; + } + + /// + /// Returns the Assembly Version + /// + /// Assembly to Retrieve Info for + /// Thrown if invalist Assembly is passed in + /// Returns the Assembly Version, as specified in AssemblyInfo + public static Version GetAssemblyVersion(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + return SpecializedAssemblyInfo.GetAssemblyNameObj(asm).Version; + } + + /// + /// Returns the Assembly File Version + /// + /// State/Type of Assembly to Retrieve + /// Thrown if invalist AssemblyST is passed in + /// Returns the File Version, as specified by the File + public static FileVersionInfo GetAssemblyFileVersion(AssemblyST st) + { + FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(SpecializedAssemblyInfo.GetAssemblyFileNameNPath(st)); + return fvi; + } + + /// + /// Returns the Assembly File Version + /// + /// Assembly to Retrieve Info for + /// Thrown if invalist Assembly is passed in + /// Returns the File Version, as specified by the File + public static FileVersionInfo GetAssemblyFileVersion(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(SpecializedAssemblyInfo.GetAssemblyFileNameNPath(asm)); + return fvi; + } + + #endregion + + #region Specialized Assembly Information + + /// + /// Useful Functions for Retrieving Specialized Assembly Information + /// + public class SpecializedAssemblyInfo + { + /// + /// Returns the Assembly Name Object from the Assembly + /// + /// State/Type of Assembly to Retrieve Info for + /// Thrown if invalid AssemblyST is passed in + /// AssemblyName object for the specified assembly + public static Refl.AssemblyName GetAssemblyNameObj(AssemblyST st) + { + return GetAssembly(st).GetName(); + } + + /// + /// Returns the Assembly Name Object from the Assembly + /// + /// Assembly to Retrieve Info for + /// Thrown if invalid Assembly is passed in + /// AssemblyName object for the specified assembly + public static Refl.AssemblyName GetAssemblyNameObj(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + return asm.GetName(); + } + + /// + /// Returns the Exact File Name and Path of the Assembly + /// + /// State/Type of Assembly to Retrieve Info for + /// Thrown if invalid AssemblyST is passed in + /// Full File Name and Path + public static string GetAssemblyFileNameNPath(AssemblyST st) + { + Refl.Assembly asm = GetAssembly(st); + string curAsmName = asm.Location; + return curAsmName; + } + + /// + /// Returns the Exact File Name and Path of the Assembly + /// + /// Assembly to Retrieve Info for + /// Thrown if invalid Assembly is passed in + /// Full File Name and Path + public static string GetAssemblyFileNameNPath(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + string curAsmName = asm.Location; + return curAsmName; + } + + /// + /// Returns the File Name (without Extension) of the Assembly + /// + /// State/Type of Assembly to Retrieve Info for + /// Thrown if invalid AssemblyST is passed in + /// File Name without extension + public static string GetAssemblyFileName(AssemblyST st) + { + string curAsmName = Path.GetFileNameWithoutExtension(GetAssembly(st).ManifestModule.Name); + return curAsmName; + } + + /// + /// Returns the File Name (without Extension) of the Assembly + /// + /// Assembly to Retrieve Info for + /// Thrown if invalid Assembly is passed in + /// File Name without extension + public static string GetAssemblyFileName(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + string curAsmName = Path.GetFileNameWithoutExtension(asm.ManifestModule.Name); + return curAsmName; + } + + /// + /// Returns the Path of the Assembly + /// + /// State/Type of Assembly to Retrieve Info for + /// Thrown if invalid AssemblyST is passed in + /// Path + public static string GetAssemblyPath(AssemblyST st) + { + Refl.Assembly asm = GetAssembly(st); + string curAsmName = Path.GetDirectoryName(asm.Location); + return curAsmName; + } + + /// + /// Returns the Path of the Assembly + /// + /// Assembly to Retrieve Info for + /// Thrown if invalid Assembly is passed in + /// Path + public static string GetAssemblyPath(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + string curAsmName = Path.GetDirectoryName(asm.Location); + return curAsmName; + } + + /// + /// Returns the File Name (with Extension) of the Assembly + /// + /// State/Type of Assembly to Retrieve Info for + /// Thrown if invalid AssemblyST is passed in + /// File Name with extension + public static string GetAssemblyFileNameWithExtension(AssemblyST st) + { + string curAsmName = Path.GetFileName(GetAssembly(st).ManifestModule.Name); + return curAsmName; + } + + /// + /// Returns the File Name (with Extension) of the Assembly + /// + /// Assembly to Retrieve Info for + /// Thrown if invalid Assembly is passed in + /// File Name with extension + public static string GetAssemblyFileNameWithExtension(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + string curAsmName = Path.GetFileName(asm.ManifestModule.Name); + return curAsmName; + } + + /// + /// Returns all the Resource names inside the assembly + /// + /// State/Type of Assembly to Retrieve Info for + /// Thrown if invalid AssemblyST is passed in + /// Returns all resource names defined in the assembly + public static string[] GetAssemblyResourceNames(AssemblyST st) + { + string[] resources = GetAssembly(st).GetManifestResourceNames(); + return resources; + } + + /// + /// Returns all the Resource names inside the assembly + /// + /// Assembly to Retrieve Info for + /// Thrown if invalid Assembly is passed in + /// Returns all resource names defined in the assembly + public static string[] GetAssemblyResourceNames(Refl.Assembly asm) + { + if (asm == null) + throw new ArgumentException("Invalid Assembly"); + + string[] resources = asm.GetManifestResourceNames(); + return resources; + } + } + + #endregion + } +} diff --git a/Components/ICSharpCode.SharpZipLib.dll b/Components/ICSharpCode.SharpZipLib.dll new file mode 100644 index 0000000..fe643eb Binary files /dev/null and b/Components/ICSharpCode.SharpZipLib.dll differ diff --git a/Components/log4net.dll b/Components/log4net.dll new file mode 100644 index 0000000..ffc57e1 Binary files /dev/null and b/Components/log4net.dll differ diff --git a/Encryption/TextEncryptHash.cs b/Encryption/TextEncryptHash.cs new file mode 100644 index 0000000..22ced8c --- /dev/null +++ b/Encryption/TextEncryptHash.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using System.Security.Cryptography; + +namespace Yaulw.Encryption +{ + /// + /// + /// + public static class TextEncryptHash + { + private static byte[] _byteky = new byte[] {173,61,94,8,43,151,9,42,113,122,47,208,230,63,122,252,134,18,73,248,232,72,122,240,145,72,249,199,16,152,236,174,111,76,119,52,161,81,161,14,11,164,65,49,127,118,100,223,177,104,145,216,39,213,239,81,143,20,111,239,35,75,167,117}; + + public static string EncryptText(string text) + { + + using (HMACSHA1 provider = new System.Security.Cryptography.HMACSHA1(_byteky, true)) + { + //provider.En + } + + return ""; + } + + } +} diff --git a/File/CHMFile.cs b/File/CHMFile.cs new file mode 100644 index 0000000..18f3660 --- /dev/null +++ b/File/CHMFile.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Resources; +using IO = System.IO; +using System.Windows.Forms; +using System.Windows; +using System.Windows.Interop; +using Yaulw.Process; +using Diag = System.Diagnostics; + +namespace Yaulw.File +{ + /// + /// CHM Help File Wrapper + /// + public class CHMFile + { + #region Private Members + + /// + /// The Full File Name and Path to the CHM File + /// + private string _FileNameNPath = ""; + + /// + /// The File Stream that created the CHM File (if used) + /// + private IO.Stream _FileStream = null; + + /// + /// Internally we keep track of each Form to Help Context ID + /// + private Dictionary s_FormContextMap = new Dictionary(); + + #endregion + + #region Construction + + /// + /// Construct a CHMFile object with the specified chm file + /// + /// a full path to a valid chm file + public CHMFile(string FileNameNPath) + { + if (!String.IsNullOrEmpty(FileNameNPath) && IO.File.Exists(FileNameNPath) && (IO.Path.GetExtension(FileNameNPath).ToLower() == "chm")) + _FileNameNPath = FileNameNPath; + else + throw new ArgumentException("Invalid chm File or File does not exist"); + } + + /// + /// Construct a CHMFile object from the specified stream (will construct a chm file in + /// a temporary location from the specified stream) + /// + /// stream to construct the chm file from + /// The name of the application (will be used when creating the .chm file) + /// if true, will force a new file to be written out, false will only write file if not exists + public CHMFile(IO.Stream stream, string ApplicationName, bool ForceNewFile) + { + if (stream != null && !String.IsNullOrEmpty(ApplicationName)) + { + _FileStream = stream; + _FileNameNPath = IO.Path.GetTempPath() + ApplicationName + ".chm"; + EnsureCHMFileExistance(ForceNewFile); + } + else + throw new ArgumentNullException("Resource can not be null. ApplicationName can not be empty"); + } + + #endregion + + #region Public Consts + + /// + /// Legacy MFC View Context offset + /// + public const int MNU = 0x10000; + + /// + /// Legacy MFC Dialog Context offset + /// + public const int DLG = 0x20000; + + #endregion + + #region Public Methods + + /// + /// Attaches the HelpRequest Handler as well as shows the HelpButton on the CaptionBar + /// + /// WinForm to Attach Help Button to + /// the Help Topic Context Id to attach to the specified WinForm + public void AttachHelpCaptionButtonToWinForm(Form form, int dwContextId) + { + // Set the HelpButton to be displayed + form.MinimizeBox = false; + form.MaximizeBox = false; + form.HelpButton = true; + + // add the form / contextID to the map + s_FormContextMap[form] = (int)dwContextId; + + // Subscribe to the Help Requested Handler + form.HelpRequested += new HelpEventHandler(form_HelpRequested); + + // Subscribe to the Destroyed Event Handler *to remove the form from the list* + form.HandleDestroyed += new EventHandler(form_HandleDestroyed); + } + + /// + /// Launch CHM Help + /// + public void LaunchHelp() + { + int bHelpIsAlreadyRunning_Pid = 0; + Diag.Process[] ps = ProcessW.AllRunningProcessesOf("hh", false, true); + if (ps != null && ps.Length >= 1) + { + foreach (Diag.Process p in ps) + { + string ProcessName = ""; + string CommandLine = ""; + ProcessW.GetCommandLineArgumentsForProcessPID(p.Id, out ProcessName, out CommandLine); + if (CommandLine.Contains(_FileNameNPath)) + { + bHelpIsAlreadyRunning_Pid = p.Id; + break; + } + } + } + + // No need to launch a second time + if (bHelpIsAlreadyRunning_Pid != 0) + { + // Set Focus + IntPtr hWnd = Yaulw.Win32.Functions.GetFirstTopLevelWindowForProcess(bHelpIsAlreadyRunning_Pid); + if (hWnd != IntPtr.Zero) + Yaulw.Win32.User32.SetActiveWindow(hWnd); + } + else + { + // Launch + EnsureCHMFileExistance(false); + PStarter.StartProcess(PStartInfo.CreateProcess(_FileNameNPath, "", "", false, System.Diagnostics.ProcessWindowStyle.Normal, true), false, false); + } + } + + /// + /// Launch CHM Help + /// + /// form which will be the parent of the CHM File + public void LaunchHelp(Form form) + { + if (form != null) + { + Control control = Control.FromHandle(form.Handle); + if (control != null) + { + EnsureCHMFileExistance(false); + Help.ShowHelp(control, _FileNameNPath); + } + } + } + + /// + /// aunch CHM Help + /// + /// Window which will be the parent for the CHM file + public void LaunchHelp(Window window) + { + if (window != null) + { + Control control = Control.FromHandle(GetHandleForWPFWindow(window)); + if (control != null) + { + EnsureCHMFileExistance(false); + Help.ShowHelp(control, _FileNameNPath); + } + } + } + + /// + /// Launch CHM Help for the specified Context + /// + /// form which will be the parent of the CHM File + /// Context Id to launch chm file with + public void LaunchHelp(Form form, int dwContextId) + { + if (form != null) + { + Control control = Control.FromHandle(form.Handle); + if (control != null) + { + EnsureCHMFileExistance(false); + Help.ShowHelp(control, _FileNameNPath, HelpNavigator.TopicId, dwContextId.ToString()); + } + } + } + + /// + /// aunch CHM Help for the specified Context + /// + /// Window which will be the parent for the CHM file + /// Context Id to launch chm file with + public void LaunchHelp(Window window, int dwContextId) + { + if (window != null) + { + EnsureCHMFileExistance(false); + Help.ShowHelp(null, _FileNameNPath, HelpNavigator.TopicId, dwContextId.ToString()); + } + } + + #endregion + + #region Private WinForm Help Event Handlers + + /// + /// Handle HelpRequests + /// + private void form_HelpRequested(object sender, HelpEventArgs hlpevent) + { + Form form = sender as Form; + if (form != null) + { + int dwContextId = s_FormContextMap[form]; + Help.ShowHelp(form, _FileNameNPath, HelpNavigator.TopicId, dwContextId.ToString()); + } + } + + /// + /// Handle Form Destroys + /// + private void form_HandleDestroyed(object sender, EventArgs e) + { + Form form = sender as Form; + if (form != null) + s_FormContextMap.Remove(form); + } + + #endregion + + #region Private Helpers + + /// + /// Creates the CHMFile from the stream (if a stream was used) + /// + /// true to force re-creation of the file, false otherwise + private void EnsureCHMFileExistance(bool ForceCreationOfFile) + { + if (_FileStream != null) + { + if (ForceCreationOfFile || !IO.File.Exists(_FileNameNPath)) + { + using (IO.FileStream fs = new IO.FileStream(_FileNameNPath, IO.FileMode.Create)) + { + using (IO.BinaryWriter writer = new IO.BinaryWriter(fs)) + { + // Read from the Resource + Byte[] streamData = new Byte[_FileStream.Length]; + _FileStream.Read(streamData, 0, streamData.Length); + + // Write to File + writer.Write(streamData); + writer.Close(); + } + } + } + } + } + + /// + /// Uses the InteropHelper to get the Handle of a WPF Window + /// + /// a WPF Window + /// an hWnd for the specified WPF Window + private IntPtr GetHandleForWPFWindow(Window window) + { + WindowInteropHelper InteropHelper = new WindowInteropHelper(window); + return InteropHelper.Handle; + } + + #endregion + } +} diff --git a/File/FileWriter.cs b/File/FileWriter.cs new file mode 100644 index 0000000..784b161 --- /dev/null +++ b/File/FileWriter.cs @@ -0,0 +1,193 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace Yaulw.File +{ + /// + /// Simple Line-By-Line UTF/Ascii File Writer Object + /// + public class FileWriter + { + #region Private Members + + private string _FileName = ""; + private string _FileType = ""; + private string _dirPath = ""; + private bool _OverideExisting = false; + private bool _FileIsCreated = false; + + #endregion + + #region Construction + + /// + /// Create a Simple UTF/Ascii Line FileWriter Object + /// + /// The Name of the File (If blank will generate a random file name) + /// The Type of File to write to (Default = "log") + /// The path where to write the file (If Blank will use Temp Path) + /// true to overide an existing file, false will try to append by default + public FileWriter(string FileName = "", string FileType = "log", string dirPath = "", bool OverideExisting = true) + { + _FileName = FileName; + _FileType = FileType; + _dirPath = dirPath; + _OverideExisting = OverideExisting; + + // Generate File Name and Path, if not exist + if(String.IsNullOrEmpty(_FileName)) + _FileName = System.IO.Path.GetFileNameWithoutExtension(System.IO.Path.GetRandomFileName()); + if(String.IsNullOrEmpty(_dirPath)) + _dirPath = System.IO.Path.GetTempPath(); + + // Make Sure Path Exists + if (!Directory.Exists(_dirPath)) + Directory.CreateDirectory(_dirPath); + } + + #endregion + + #region Write Ascii + + /// + /// Write a Line in Ascii to File + /// + /// ascii line to write + public void WriteLineA(string line) + { + using (FileStream fs = CreateFileStream()) + using(StreamWriter sw = new StreamWriter(fs, Encoding.ASCII)) + { + sw.WriteLine(line); + sw.Flush(); + } + } + + /// + /// Write Lines in Ascii to file + /// + /// ascii lines to write + public void WriteLineA(string[] lines) + { + using (FileStream fs = CreateFileStream()) + using (StreamWriter sw = new StreamWriter(fs, Encoding.ASCII)) + { + foreach (string line in lines) + sw.WriteLine(line); + sw.Flush(); + } + } + + #endregion + + #region Write UTF + + /// + /// Write a Line in UTF to File + /// + /// utf line to write + public void WriteLineUTF8(string line) + { + using (FileStream fs = CreateFileStream()) + using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8)) + { + sw.WriteLine(line); + sw.Flush(); + } + } + + /// + /// Write Lines in UTF to File + /// + /// utf lines to write + public void WriteLineUTF8(string[] lines) + { + using (FileStream fs = CreateFileStream()) + using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8)) + { + foreach (string line in lines) + sw.WriteLine(line); + sw.Flush(); + } + } + + #endregion + + #region Public Properties + + /// + /// Returns the File Name + /// + public string FileName { get { return (this._FileName + "." + this._FileType); } } + + /// + /// Returns the Path where the File is located + /// + public string Path { get { return (this._dirPath); } } + + /// + /// Returns both Path and FileName + /// + public string FileNameNPath { get { return (this.Path + "\\" + this.FileName); } } + + #endregion + + #region Public Methods + + /// + /// Deletes the File, if it exists + /// + /// true if successful, false otherwise + public bool DeleteFile() + { + try + { + if (System.IO.File.Exists(this.FileNameNPath)) + { + System.IO.File.Delete(this.FileNameNPath); + _FileIsCreated = false; + return true; + } + } + catch (Exception) { /* ignore */ } + return false; + } + + #endregion + + #region Private Methods + + /// + /// Creates the File Stream, either in Create or Append Mode + /// + /// a File Stream to Write to * Must be Closed by Caller * + private FileStream CreateFileStream() + { + try + { + bool bFileExists = System.IO.File.Exists(this.FileNameNPath); + if (!_FileIsCreated && _OverideExisting && bFileExists) + { + _FileIsCreated = true; + return (new FileStream(this.FileNameNPath, FileMode.Create)); + } + else if (bFileExists) + { + return (new FileStream(this.FileNameNPath, FileMode.Append)); + } + else + { + _FileIsCreated = true; + return (new FileStream(this.FileNameNPath, FileMode.Create)); + } + } + catch (Exception) { /* ignore */ } + return null; + } + + #endregion + } +} diff --git a/File/INIFile.cs b/File/INIFile.cs new file mode 100644 index 0000000..a994a73 --- /dev/null +++ b/File/INIFile.cs @@ -0,0 +1,213 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Yaulw.Tools; +using Yaulw.Win32; +using System.Diagnostics; + +namespace Yaulw.File +{ + /// + /// Responsible for Reading and Writing an INI File. + /// Simply construct an INIFile Object passing in a File name and Path, + /// as well as an enum that lists all the Categories and Keys. + /// + /// An example *You can mix and match Categories and Keys as needed *: + /// public enum INICatsNKeys + /// { + /// App_Settings__Load_XML_At_StartUp, + /// App_Settings__Allow_Command_Line_to_override_INI, + /// App_Settings__Default_User, + /// App_Settings__Default_Server, + /// Runtime_Debug__Error_Debug_Level, + /// Data_Access__Read_Only, + /// Settings__Program_Folder, + /// Settings__User1_Default, + /// Recent_File_List__File1, + /// Recent_File_List__File2, + /// Recent_File_List__File3, + /// Recent_File_List__File4, + /// } + /// + /// The '__' Character signifies a category to Key change + /// the '_' will be replaced by ' ', when actually writing to the Ini File. + /// + public class INIFile + { + #region Private Members + + private string _IniPathNFileName = String.Empty; + private Type _IniKeysNCategories = null; + + #endregion + + #region Construction + + /// + /// Initialize an INIFile object with a valid path and FileName to write/read from. + /// Also pass in an Enum Type that contains Category and Key Information. This Enum will be used + /// to read and write to the INIFile (to determine which Section/Key to read/write from/to) + /// + /// Pass in the full file name and path to use * Creates Directory, if it does not exits * + /// Pass in an Enum that specifies the Categories and Keys + /// Thrown if an invalid Enum is passed in via IniKeysNCategories + public INIFile(string IniPathNFileName, Type IniKeysNCategories) + { + // Check and ensure Enum Accuracy + if (IniKeysNCategories.IsEnum && EnumTool.EnumType_HasCategoryNKey(IniKeysNCategories)) + { + _IniKeysNCategories = IniKeysNCategories; + } + else + throw new ArgumentException("IniKeysNCategories is not an Enum or not all Enums contain Key and Category Information"); + + // Ensure Directory Existence + if (!Directory.Exists(Path.GetDirectoryName(IniPathNFileName))) + Directory.CreateDirectory(Path.GetDirectoryName(IniPathNFileName)); + _IniPathNFileName = IniPathNFileName; + } + + #endregion + + #region Public Properties + + /// + /// Ini File Used by IniFile + /// + public string IniFile { get { return _IniPathNFileName; } } + + #endregion + + #region Public Ini Key Reader / Writer Functions + + /// + /// Use this to directly set a key to any string value + /// + /// Specify Key and category set Directly + /// value to set + /// If cKey is not a valid Category/Key Pair Enum + public void SetKeyValue(Enum cKey, T Value) + { + if (EnumTool.Enum_HasCategoryNKey(cKey)) + { + string Category; + string Key; + EnumTool.Enum_ToCategoryNKey(cKey, out Category, out Key); + string ValueStr = ObjTool.ConvertObjToString(Value); + SetKeyValueString(Category, Key, ValueStr); + } + else + { + throw new ArgumentException("cKey must have both Category and Key Information"); + } + } + + /// + /// Use this to directly retrieve a key + /// + /// Specify Key and category set Directly + /// default value to return, if not found + /// value retrieved or default value + /// If cKey is not a valid Category/Key Pair Enum + public T GetKeyValue(Enum cKey, T defaultValue) + { + try + { + if (EnumTool.Enum_HasCategoryNKey(cKey)) + { + string Category; + string Key; + EnumTool.Enum_ToCategoryNKey(cKey, out Category, out Key); + + // Get Key and verify that something was found + string defaultValueStr = ObjTool.ConvertObjToString(defaultValue); + string KeyValue = GetKeyValueString(Category, Key, defaultValueStr); + bool bHasAValue = (KeyValue.Trim().Length != 0); + + // always write the value back out to ini + SetKeyValue(cKey, bHasAValue ? KeyValue : defaultValueStr); + + // Return value + if (bHasAValue) + { + return ObjTool.ConvertStringToObj(KeyValue); + } + else + { + return defaultValue; + } + } + else + { + throw new ArgumentException("cKey must have both Category and Key Information"); + } + } + catch (Exception) { /* ignore */ } + return defaultValue; + } + + #endregion + + #region Private Generic Ini Key Reader / Writer Functions + + /// + /// Set a value for the specified Key and for the specified category + /// + /// specify category + /// specify key + /// specify a value to set + private void SetKeyValueString(string category, string key, string value) + { + bool bSuccess = Kernel32.WritePrivateProfileString(category, key, value, IniFile); + Debug.Assert(bSuccess); + } + + /// + /// Retrieve the value for a specified Key and for a specified category + /// + /// specify category + /// specify key + /// specify default value, returned when no value found + /// the value for the Key, or default value if no value found + private string GetKeyValueString(string category, string key, string defaultValue) + { + StringBuilder returnString = new StringBuilder(1024); + Kernel32.GetPrivateProfileString(category, key, defaultValue, returnString, 1024, IniFile); + return returnString.ToString().Split('\0')[0]; + } + + #endregion + + #region Private Generic Ini File Readers (Not Used) + + /// + /// Use this to return all the categories in an INI File + /// + /// a list with Gategory Items, or 0 length list is none found + private List GetAllCategories() + { + StringBuilder returnString = new StringBuilder(65536); + uint returnValue = Kernel32.GetPrivateProfileString(null, null, null, returnString, 65536, IniFile); + List result = new List(returnString.ToString().Split('\0')); + result.RemoveRange(result.Count - 2, 2); + return result; + } + + /// + /// Use this to return the Keys from a category from an INI File + /// + /// a list with Key Items, or 0 length list is none found + private List GetAllKeysInCategory(string category) + { + StringBuilder returnString = new StringBuilder(32768); + Kernel32.GetPrivateProfileString(category, null, null, returnString, 32768, IniFile); + List result = new List(returnString.ToString().Split('\0')); + result.RemoveRange(result.Count - 2, 2); + return result; + } + + #endregion + } +} diff --git a/File/ISReadWrite.cs b/File/ISReadWrite.cs new file mode 100644 index 0000000..4896bbd --- /dev/null +++ b/File/ISReadWrite.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO.IsolatedStorage; +using System.IO; +using System.Reflection; + +namespace Yaulw.File +{ + /// + /// + /// + public enum ISScope + { + User, + Machine, + } + + /// + /// Class allows reading and writing to isolated Storage + /// + public class ISReadWrite + { + #region Private Members + + private string _ISOLATED_STORAGE_FILENAME = ""; + private bool _bFileInIsoStoreExists = false; + private Xml.XSerializer _XSerializer = null; + private IsolatedStorageScope _scope = IsolatedStorageScope.None; + private IsolatedStorageFile _isoStore = null; + + #endregion + + #region Construction + + /// + /// + /// + /// + public ISReadWrite(string ISFileName) : this(ISFileName, ISScope.Machine) { } + + /// + /// + /// + /// + /// + public ISReadWrite(string ISFileName, ISScope scope) + { + _ISOLATED_STORAGE_FILENAME = ISFileName; + + if (String.IsNullOrEmpty(_ISOLATED_STORAGE_FILENAME)) + throw new ArgumentNullException("ISFileName can not be empty."); +#if NET4 + if (!IsolatedStorageFile.IsEnabled) + throw new Exception("Isolated Storage not enabled"); +#endif + _scope = ConvertISScopeToIsolatedStorageScope(scope); + _isoStore = IsolatedStorageFile.GetStore(_scope, typeof(System.Security.Policy.Url), typeof(System.Security.Policy.Url)); + _bFileInIsoStoreExists = (_isoStore.GetFileNames(ISFileName).Length > 0); + _XSerializer = new Xml.XSerializer(); + } + + #endregion + + #region Public Read / Write Methods + + public string ReadFromIS() + { + using (IsolatedStorageFileStream fs = new IsolatedStorageFileStream(_ISOLATED_STORAGE_FILENAME, FileMode.OpenOrCreate, _isoStore)) + using (StreamReader reader = new StreamReader(fs)) + { + _bFileInIsoStoreExists = true; + string strFileContents = reader.ReadToEnd(); + if (!String.IsNullOrEmpty(strFileContents)) + return strFileContents; + } + return String.Empty; + } + + /// + /// + /// + /// + /// + public T ReadFromIS() + { + using (IsolatedStorageFileStream fs = new IsolatedStorageFileStream(_ISOLATED_STORAGE_FILENAME, FileMode.OpenOrCreate, _isoStore)) + using (StreamReader reader = new StreamReader(fs)) + { + _bFileInIsoStoreExists = true; + string strFileContents = reader.ReadToEnd(); + if (!String.IsNullOrEmpty(strFileContents)) + { + T t = _XSerializer.ReadFromString(strFileContents); + return t; + } + } + + return default(T); + } + + /// + /// + /// + /// + /// + public void WriteToIS(T XMLSerializableObject) where T : new() + { + using (IsolatedStorageFileStream fs = new IsolatedStorageFileStream(_ISOLATED_STORAGE_FILENAME, FileMode.Create, _isoStore)) + using (StreamWriter writer = new StreamWriter(fs)) + { + _bFileInIsoStoreExists = true; + string strFileContentsToWrite = _XSerializer.WriteToString(XMLSerializableObject); + if(!String.IsNullOrEmpty(strFileContentsToWrite)) + writer.Write(strFileContentsToWrite); + } + } + + /// + /// + /// + public void WriteToIS(string Content) + { + using (IsolatedStorageFileStream fs = new IsolatedStorageFileStream(_ISOLATED_STORAGE_FILENAME, FileMode.Create, _isoStore)) + using (StreamWriter writer = new StreamWriter(fs)) + { + _bFileInIsoStoreExists = true; + string strFileContentsToWrite = Content; + if (!String.IsNullOrEmpty(strFileContentsToWrite)) + writer.Write(strFileContentsToWrite); + } + } + + #endregion + + #region Public Methods + + /// + /// + /// + public void Delete() + { + if (_bFileInIsoStoreExists) + { + _isoStore.DeleteFile(_ISOLATED_STORAGE_FILENAME); + _bFileInIsoStoreExists = false; + } + } + + #endregion + + #region Public Properties + + public bool Exists + { + get { return _bFileInIsoStoreExists; } + } + + #endregion + + #region Private Helpers + + /// + /// + /// + /// + /// + private IsolatedStorageScope ConvertISScopeToIsolatedStorageScope(ISScope scope) + { + switch (scope) + { + case ISScope.User: + return IsolatedStorageScope.User | IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly; + + case ISScope.Machine: + return IsolatedStorageScope.Machine | IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly; + } + return IsolatedStorageScope.Application; + } + + #endregion + } + +} diff --git a/File/LineReplacer.cs b/File/LineReplacer.cs new file mode 100644 index 0000000..9f11c19 --- /dev/null +++ b/File/LineReplacer.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using IO = System.IO; + +namespace Yaulw.File +{ + #region Rule Definitions + + /// + /// Comparer Modifier used when looking at the line + /// + public enum LineReplace_ComparerModifier + { + None, + toLower, + toUpper, + } + + /// + /// Rule Used to determine which line to replace + /// ~Replace Line with contains the new line to put instead + /// + public struct LineReplace_Rule + { + public string Contains; + public string StartsWith; + public string EndsWith; + public LineReplace_ComparerModifier Comparer; + public string ReplaceLineWith; + } + + #endregion + + /// + /// Useful for replacing lines in a text file. + /// It reads in a text file line by line and compares the lines. + /// Using the rules you can replace a line or multiple lines and write out to the text file. + /// + public class LineReplacer + { + #region Private Members + + private Dictionary _RulesDictionary = new Dictionary(); + private string _fileNameNPath = ""; + private Encoding _encoding = Encoding.UTF8; + + #endregion + + #region Construction + + /// + /// Allows for easy line replacing of a text file + /// + /// Full File Name and Path to a file that exists + /// Thrown when File does not exist + public LineReplacer(string FileNameNPath, Encoding encoding) + { + if (String.IsNullOrEmpty(FileNameNPath) || !IO.File.Exists(FileNameNPath)) + throw new ArgumentException("Line Replace makes only sense on Files that exist already"); + + _fileNameNPath = FileNameNPath; + _encoding = encoding; + } + + #endregion + + #region Public Methods + + /// + /// Add/Update Line Replacing Rules into here BEFORE calling ReplaceLines() + /// + /// unique name of the rule + /// Rule Definition + /// true if successfully added, false otherwise + public bool AddUpdateRule(string RuleName, LineReplace_Rule rule) + { + if (!String.IsNullOrEmpty(RuleName)) + { + // We must be able to match on something + if (String.IsNullOrEmpty(rule.StartsWith) && String.IsNullOrEmpty(rule.EndsWith) && + String.IsNullOrEmpty(rule.Contains)) + return false; + + // We must be able to replace with something + if (String.IsNullOrEmpty(rule.ReplaceLineWith)) + return false; + + // Rule is good, add it + _RulesDictionary[RuleName] = rule; + } + return false; + } + + /// + /// Call this Function to Replace all the Lines of the Text Files that + /// match a specific Rule + /// + public void ReplaceLines() + { + if (_RulesDictionary.Count <= 0) + return; + + // # Readin and Process all Lines + List ReadInLines = new List(); + using (IO.FileStream fs = new IO.FileStream(_fileNameNPath, IO.FileMode.Open)) + using (IO.StreamReader reader = new IO.StreamReader(fs, _encoding)) + { + // # Iterate thru all the lines in the File + for (string Line = reader.ReadLine(); Line != null; Line = reader.ReadLine()) + { + FoundRuleMathAndPerformedAction(ref Line); + ReadInLines.Add(Line); + } + } + + // # Write out all the Lines + using (IO.FileStream fs = new IO.FileStream(_fileNameNPath, IO.FileMode.Create)) + using (IO.StreamWriter writer = new IO.StreamWriter(fs, _encoding)) + { + foreach (string Line in ReadInLines) + writer.WriteLine(Line); + } + } + + #endregion + + #region Private Helpers + + /// + /// Responsible for finding a match between a line and a rule and + /// performing the action + /// + /// Line to find a match for + /// true, if match was found/action was done, false otherwise + private bool FoundRuleMathAndPerformedAction(ref string Line) + { + if (String.IsNullOrEmpty(Line)) + return false; + + bool bFoundMatch = false; + // Iterate foreach rule for a line + foreach (LineReplace_Rule rule in _RulesDictionary.Values) + { + // Make sure to use the proper comparer Modifier for the rule + string line = Line; + if (rule.Comparer == LineReplace_ComparerModifier.toLower) + line = Line.ToLower(); + else if (rule.Comparer == LineReplace_ComparerModifier.toUpper) + line = Line.ToUpper(); + + // Now let's match on the actual Rule + if (!bFoundMatch && !String.IsNullOrEmpty(rule.StartsWith)) + bFoundMatch = line.StartsWith(rule.StartsWith); + if (!bFoundMatch && !String.IsNullOrEmpty(rule.EndsWith)) + bFoundMatch = line.StartsWith(rule.EndsWith); + if (!bFoundMatch && !String.IsNullOrEmpty(rule.Contains)) + bFoundMatch = line.StartsWith(rule.Contains); + if (bFoundMatch) + { + Line = rule.ReplaceLineWith; + break; + } + } + return bFoundMatch; + } + + #endregion + + } +} diff --git a/File/LoggerQuick.cs b/File/LoggerQuick.cs new file mode 100644 index 0000000..91a4639 --- /dev/null +++ b/File/LoggerQuick.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Yaulw.Assembly; + +namespace Yaulw.File +{ + /// + /// Easy Class for quick and dirty logging *that can automatically on a daily basis* + /// + public class LoggerQuick + { + #region Private Members + + /// + /// FileWriter Object + /// + private Yaulw.File.FileWriter _fileWriter = null; + + /// + /// Current Directory + /// + private string _curPath = ""; + + /// + /// Log Debug Messages + /// + private bool _LogDebug = false; + + /// + /// Name of the Log (also serves as RegKey) + /// + private string LogName = ""; + + #endregion + + #region Construction + + /// + /// Quick Logger (which can clear itself daily and has no dependencies) + /// quick n' dirty logs in the current directory from which it is called + /// + /// Name of the Log file + /// true to spit out DebugMessages, false otherwise + public LoggerQuick(string LogName, bool bLogDebugMessages) + { + if(String.IsNullOrEmpty(LogName)) + throw new ArgumentException("LogName can't be empty"); + + // Log Name, which will also server as the registry key name + this.LogName = Path.GetFileName(LogName); + + // Log Debug Messages? + _LogDebug = bLogDebugMessages; + + // Get the current running directory + if (String.IsNullOrEmpty(_curPath)) + _curPath = Path.GetDirectoryName(AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameNPath(AssemblyW.AssemblyST.Executing)); + + // Set up FileWriter + if (_fileWriter == null) + _fileWriter = new Yaulw.File.FileWriter(LogName, "log", _curPath, false); + } + + /// + /// Quick Logger (which can clear itself daily and has no dependencies) + /// quick n' dirty logs in the current directory from which it is called + /// + /// Name of the Log file + /// true to spit out DebugMessages, false otherwise + /// path to log to + public LoggerQuick(string LogName, bool bLogDebugMessages, string path) + { + if (String.IsNullOrEmpty(LogName)) + throw new ArgumentException("LogName can't be empty"); + + // Log Name, which will also server as the registry key name + this.LogName = Path.GetFileName(LogName); + + // Log Debug Messages? + _LogDebug = bLogDebugMessages; + + // make sure path is valid + try + { + if (!String.IsNullOrEmpty(path)) + { + path = Path.GetDirectoryName(path); + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + } + } + catch (Exception) { /* ignore */ } + + // Get the current running directory + if (String.IsNullOrEmpty(_curPath) && String.IsNullOrEmpty(path)) + _curPath = Path.GetDirectoryName(AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameNPath(AssemblyW.AssemblyST.Executing)); + else + _curPath = path; + + // Set up FileWriter + if (_fileWriter == null) + _fileWriter = new Yaulw.File.FileWriter(LogName, "log", _curPath, false); + } + + #endregion + + #region Public Log Methods + + /// + /// Debug Logging only logs when _LogDebug is set + /// + /// + /// + public void Debug(string line, params object[] args) + { + if (_LogDebug) + Log("Debug: ", line, args); + } + + /// + /// Info Logging + /// + /// + /// + public void Info(string line, params object[] args) + { + Log("Info: ", line, args); + } + + /// + /// Error Logging + /// + /// + /// + public void Error(string line, params object[] args) + { + Log("Error: ", line, args); + } + + #endregion + + #region Private Log Methods + + /// + /// Used for logging * Medisoft people * crack me up + /// + /// + /// + private void Log(string prePrend, string line, params object[] args) + { + if (_fileWriter != null) + { + // Clear the log once a day automatically - yeah! + DateTime LastLogged = Yaulw.Registry.RegKey.GetKey("LoggerQuick", LogName, DateTime.MinValue); + if (LastLogged != DateTime.MinValue && LastLogged.Day != DateTime.Now.Day) + _fileWriter.DeleteFile(); + + // Always set the DT Stamp, for every log + Yaulw.Registry.RegKey.SetKey("LoggerQuick", LogName, DateTime.Now); + + // Now let's start Logging + line = prePrend + line; + if (args != null) + _fileWriter.WriteLineA(String.Format(line, args)); + else + _fileWriter.WriteLineA(line); + } + } + + #endregion + + } +} diff --git a/File/Logging.cs b/File/Logging.cs new file mode 100644 index 0000000..1a35c6a --- /dev/null +++ b/File/Logging.cs @@ -0,0 +1,391 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Diag = System.Diagnostics; + +// Log4Net Declarations +using log4net.Appender; +using log4net.Core; +using log4net.Layout; +using log4net.Config; +using log4net; +using Yaulw.Other; + +namespace Yaulw.File +{ + /// + /// Logging Detail + /// + public enum Logging_Detail + { + NONE, + ERROR, + INFO, + DEBUG + } + + /// + /// Used to Configure a Logger Instance in the Logging Class via AddGlobalLoggerConfiguration() + /// + /// + /// Logging_Configuration config; + /// config.LogFileNameNPath = "c:\\LogFile.log"; + /// config.Detail = Logging_Detail.Error; + /// config.UseExclusiveFileLock = false; + /// config.maxFileSizeInMB = 4; + /// config.numOfBackupLogFiles = 3; + /// config.Log4NetDetailPatternLayout = "%date{dd MMM HH:mm:ss,fff} [%thread] %level - %message%newline"; (Default) + /// "%date{dd MMM yyyy HH:mm:ss,fff} [%thread] %level %logger - %message%newline"; + /// config.LogCallingTypeAndCallingFunction = true; + /// + /// + /// + public struct Logging_Configuration + { + public string LogFileNameNPath; + public Logging_Detail Detail; + public bool UseExclusiveFileLock; + public int maxFileSizeInMB; + public int numOfBackupLogFiles; + public string Log4NetDetailPatternLayout; + public bool LogCallingType; + public bool LogCallingFunction; + } + + /// + /// Is a Wrapper Object around Log4Net's Rolling File Appender. + /// Use it by calling AddGlobalLoggerConfiguration() with a valid Logging Configuration. + /// You can configure multipe Logger Instances distinguished by Name. + /// subsequent calls can call GetLogger() with the named Logger instance to receive a valid logger object + /// + public class Logging + { + #region Private Static Members + + private static Dictionary _loggerConfigurationMap = new Dictionary(); + private static Dictionary _loggerObjects = new Dictionary(); + + // If this process is hosted in VS then it changes the stack frame + private static bool s_IsVSHosted = false; + + #endregion + + #region Private Construction + + /// + /// Private Constructor should only be called by static GetLogger() Function + /// + /// if true, will log calling type and Calling Function + private Logging(bool bLogCallingType, bool bLogCallingFunction) + { + s_IsVSHosted = Diag.Process.GetCurrentProcess().ProcessName.Contains("vshost"); + _LogCallingType = bLogCallingType; + _LogCallingFunction = bLogCallingFunction; + } + + #endregion + + #region Internal Members + + // Initialized by GetLogger() + internal ILog _Log4NetLog = null; + + // Initialized by GetLogger() + internal bool _LogCallingType = false; + + // Initialized by GetLogger() + internal bool _LogCallingFunction = false; + + #endregion + + #region Public Log Methods + + /// + /// Log Information + /// + /// Message to write + /// arguments to pass to String.Format + public void Info(string message, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Info(MessageHeader() + String.Format(message, args)); } } + + /// + /// Log Information + /// + /// Message to write + /// Use this to add/substract from the base stack level you want to retrieve + /// arguments to pass to String.Format + public void Info(string message, int nPlusMinus, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Info(MessageHeader(nPlusMinus) + String.Format(message, args)); } } + + /// + /// Log Information + /// + /// Message to write + /// Exception to Log + /// arguments to pass to String.Format + public void Info(string message, Exception exception, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Info(MessageHeader() + String.Format(message, args), exception); } } + + /// + /// Log Information + /// + /// Message to write + /// Exception to Log + /// Use this to add/substract from the base stack level you want to retrieve + /// arguments to pass to String.Format + public void Info(string message, Exception exception, int nPlusMinus, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Info(MessageHeader(nPlusMinus) + String.Format(message, args), exception); } } + + /// + /// Log Debug Information + /// + /// Message to write + /// arguments to pass to String.Format + public void Debug(string message, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Debug(MessageHeader() + String.Format(message, args)); } } + + /// + /// Log Debug Information + /// + /// Message to write + /// Use this to add/substract from the base stack level you want to retrieve + /// arguments to pass to String.Format + public void Debug(string message, int nPlusMinus, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Debug(MessageHeader(nPlusMinus) + String.Format(message, args)); } } + + /// + /// Log Debug Information + /// + /// Message to write + /// Exception to Log + /// arguments to pass to String.Format + public void Debug(string message, Exception exception, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Debug(MessageHeader() + String.Format(message, args), exception); } } + + /// + /// Log Debug Information + /// + /// Message to write + /// Exception to Log + /// Use this to add/substract from the base stack level you want to retrieve + /// arguments to pass to String.Format + public void Debug(string message, Exception exception, int nPlusMinus, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Debug(MessageHeader(nPlusMinus) + String.Format(message, args), exception); } } + + /// + /// Log Error Information + /// + /// Message to write + /// arguments to pass to String.Format + public void Error(string message, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Error(MessageHeader() + String.Format(message, args)); } } + + /// + /// Log Error Information + /// + /// Message to write + /// Use this to add/substract from the base stack level you want to retrieve + /// arguments to pass to String.Format + public void Error(string message, int nPlusMinus, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Error(MessageHeader(nPlusMinus) + String.Format(message, args)); } } + + /// + /// Log Error Information + /// + /// Message to write + /// Exception to Log + /// arguments to pass to String.Format + public void Error(string message, Exception exception, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Error(MessageHeader() + String.Format(message, args), exception); } } + + /// + /// Log Error Information + /// + /// Message to write + /// Exception to Log + /// Use this to add/substract from the base stack level you want to retrieve + /// arguments to pass to String.Format + public void Error(string message, Exception exception, int nPlusMinus, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Error(MessageHeader(nPlusMinus) + String.Format(message, args), exception); } } + + /// + /// Log Fatal Error Information + /// + /// Message to write + /// arguments to pass to String.Format + public void Fatal(string message, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Fatal(MessageHeader() + String.Format(message, args)); } } + + /// + /// Log Fatal Error Information + /// + /// Message to write + /// Use this to add/substract from the base stack level you want to retrieve + /// arguments to pass to String.Format + public void Fatal(string message, int nPlusMinus, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Fatal(MessageHeader(nPlusMinus) + String.Format(message, args)); } } + + /// + /// Log Fatal Error Information + /// + /// Message to write + /// Exception to Log + /// arguments to pass to String.Format + public void Fatal(string message, Exception exception, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Fatal(MessageHeader() + String.Format(message, args), exception); } } + + /// + /// Log Fatal Error Information + /// + /// Message to write + /// Exception to Log + /// Use this to add/substract from the base stack level you want to retrieve + /// arguments to pass to String.Format + public void Fatal(string message, Exception exception, int nPlusMinus, params object[] args) { if (_Log4NetLog != null) { _Log4NetLog.Fatal(MessageHeader(nPlusMinus) + String.Format(message, args), exception); } } + + /// + /// Message Header to be shown on every log message + /// + /// Use this to add/substract from the base stack level you want to retrieve + private string MessageHeader(int nPlusMinus = 0) + { + // When Running this from via VS it behaves differently then when + // running it outside of it, when running it regularly, each foreach loop + if (s_IsVSHosted) + { + if (_LogCallingType && _LogCallingFunction) + return StackWalker.GetTypeFromStack(nPlusMinus + 1) + " - " + StackWalker.GetMethodNameFromStack(nPlusMinus + 1) + "()- "; + else if (_LogCallingFunction) + return StackWalker.GetMethodNameFromStack(nPlusMinus + 1) + "()- "; + else + return ""; + } + else + { + if (_LogCallingType && _LogCallingFunction) + return StackWalker.GetTypeFromStack(nPlusMinus) + " - " + StackWalker.GetMethodNameFromStack(nPlusMinus) + "()- "; + else if (_LogCallingFunction) + return StackWalker.GetMethodNameFromStack(nPlusMinus) + "()- "; + else + return ""; + } + } + + #endregion + + #region Public Static Configuration and Logger Creation Methods + + /// + /// Used to add a new Configuration and Logger Instance onto a Static Map. + /// Will create one logger instance per unique name. + /// + /// a name for the logger instance + /// a valid configuration to use on the instance + /// a logging object that can be used to log + public static Logging AddGlobalLoggerConfiguration(string Name, Logging_Configuration Configuration) + { + // Must have a Valid Input + if (string.IsNullOrEmpty(Name)) + throw new ArgumentException("Name Is Invalid"); + + if (!_loggerObjects.Keys.Contains(Name.ToLower())) + { + // Create the Repository + log4net.Repository.ILoggerRepository repository = LogManager.CreateRepository(Name.ToLower()); + + // Create FileAppender Configuration + RollingFileAppender appender = RollingFileAppenderCreator(Configuration); + + // Run the Configuration against the Repository + BasicConfigurator.Configure(repository, appender); + + // Add Configuration to our Static Map + _loggerConfigurationMap[Name.ToLower()] = Configuration; + + // Last, but not least, Create the new Logging Instance Object and Store it + _loggerObjects[Name.ToLower()] = LogManager.GetLogger(Name.ToLower(), Name.ToLower()); + } + + // Let the Caller get the Logging Object + return GetLogger(Name); + } + + /// + /// Used to retrieve a named logging instance that has already been created via a previous call + /// to AddGlobalLoggerConfiguration(). + /// + /// a name for a previously created Logging instance + /// a Logging object that can be used to log + public static Logging GetLogger(string Name) + { + if (_loggerObjects.Keys.Contains(Name.ToLower()) && _loggerConfigurationMap.Keys.Contains(Name.ToLower())) + { + bool bLogCallingType = _loggerConfigurationMap[Name.ToLower()].LogCallingType; + bool bLogCallingFunction = _loggerConfigurationMap[Name.ToLower()].LogCallingFunction; + Logging logger = new Logging(bLogCallingType, bLogCallingFunction); + logger._Log4NetLog = _loggerObjects[Name.ToLower()]; + return logger; + } + else + throw new ArgumentException("Must call AddGlobalLoggerConfiguration() with a Configuration Before calling this Function"); + } + + #endregion + + #region Private Static Helper Methods + + /// + /// Creates a Log4Net RollingFileAppender with the specified configuration + /// + /// a valid configuration + /// a Log4Net RollingFileAppender Object + private static RollingFileAppender RollingFileAppenderCreator(Logging_Configuration config) + { + #region Input Validation + + if (config.maxFileSizeInMB <= 0) + throw new ArgumentException("Logging_Configuration - Invalid maxFileSizeInMB"); + + if (config.numOfBackupLogFiles < 0) + throw new ArgumentException("Logging_Configuration - Invalid numOfBackupLogFiles"); + + if (String.IsNullOrEmpty(config.LogFileNameNPath)) + throw new Exception("Logging_Configuration - Invalid LogFileNameNPath"); + + if (!Directory.Exists(Path.GetDirectoryName(config.LogFileNameNPath))) + Directory.CreateDirectory(Path.GetDirectoryName(config.LogFileNameNPath)); + + #endregion + + // Create and Set Layout for FileAppender + RollingFileAppender rfAppender = new RollingFileAppender(); + + // Set Layout + if (!String.IsNullOrEmpty(config.Log4NetDetailPatternLayout)) + rfAppender.Layout = new PatternLayout(config.Log4NetDetailPatternLayout); + else + rfAppender.Layout = new PatternLayout("%date{dd MMM HH:mm:ss,fff} [%thread] %level - %message%newline"); + + // Locking Minimal allows us to run Log4Net from multiple processes + if (config.UseExclusiveFileLock) + rfAppender.LockingModel = new FileAppender.ExclusiveLock(); + else + rfAppender.LockingModel = new FileAppender.MinimalLock(); + + // Configure FileName and always set Appent to true + rfAppender.File = config.LogFileNameNPath; + rfAppender.AppendToFile = true; + + // According to the listings on the blog site + // http://blog.aggregatedintelligence.com/2009/08/log4net-logging-levels-available.html + // Error, will log Error, Fatal + // Info, will log Info, Error, and Fatal + // Debug, will log Info, Error, Fatal and Debug + if (config.Detail == Logging_Detail.NONE) + rfAppender.Threshold = Level.Off; + else if (config.Detail == Logging_Detail.ERROR) + rfAppender.Threshold = Level.Error; + else if (config.Detail == Logging_Detail.INFO) + rfAppender.Threshold = Level.Info; + else if (config.Detail == Logging_Detail.DEBUG) + rfAppender.Threshold = Level.Debug; + + rfAppender.MaximumFileSize = String.Format("{0}MB", config.maxFileSizeInMB); + rfAppender.MaxSizeRollBackups = config.numOfBackupLogFiles; + + // Setting to RollingMode.Size will make MaxSizeRollBackups work + rfAppender.RollingStyle = RollingFileAppender.RollingMode.Size; + rfAppender.ActivateOptions(); + return rfAppender; + } + + #endregion + } +} diff --git a/File/Tail.cs b/File/Tail.cs new file mode 100644 index 0000000..bd20d8c --- /dev/null +++ b/File/Tail.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace Yaulw.File +{ + /// + /// A File SystemWatcher utility that functions like the infamous unix tail utility, + /// with a callback when the file get's created or updated. + /// + public class Tail + { + #region Public Events + + /// + /// Event Delegate for the Incoming Data Event + /// + /// object reference to the sending Tail Object + /// change data / or full data if bIsNewFile is true + /// true if a new file was created + public delegate void IncomingDataHandler(object sender, string newData, bool bIsNewFile); + + /// + /// Caller must subscribe to the Incoming Data Event + /// + public event IncomingDataHandler IncomingData = null; + + #endregion + + #region Private Members + + private string _FileNameNPath = ""; + private FileSystemWatcher _fileSystemWatcher = null; + private long _previousSeekPosition = 0; + private object _lockingObj = new Object(); + private const int MAX_BYTE_TO_READ_IN_ONE_TIME = 1024 * 16; + + #endregion + + #region Construction + + /// + /// Construct a new Tail Object to monitor the specified file + /// + /// File to Monitor with Tail + public Tail(string FileNameNPath) + { + _previousSeekPosition = 0; + _FileNameNPath = FileNameNPath; + } + + #endregion + + #region Public Methods + + /// + /// Start Monitoring the File + /// + public void StartMonitoring() + { + // Get File Information * Setup System Watcher * + FileInfo targetFile = new FileInfo(_FileNameNPath); + _fileSystemWatcher = new FileSystemWatcher(); + _fileSystemWatcher.IncludeSubdirectories = false; + _fileSystemWatcher.Path = targetFile.DirectoryName; + _fileSystemWatcher.Filter = targetFile.Name; + _fileSystemWatcher.Created += new FileSystemEventHandler(TargetFile_Created); + _fileSystemWatcher.Changed += new FileSystemEventHandler(TargetFile_Changed); + + // Perform an Initial read, if the file exists + if (targetFile.Exists) + TargetFile_Created(null, null); + + // Start up File System Watcher + _fileSystemWatcher.EnableRaisingEvents = true; + } + + /// + /// Stop Monitoring the File + /// + /// set to false, if you are running into trouble during a shutdown + public void StopMonitoring(bool bDispose = true) + { + _fileSystemWatcher.EnableRaisingEvents = false; + if(bDispose) + _fileSystemWatcher.Dispose(); + _fileSystemWatcher = null; + } + + /// + /// To read a file from Beginning to End. Call this function, + /// if you want to reset the position counter and start reading again from the beginning. + /// + /// the Contents of the File + public StringBuilder ReadFromBeginningToEndOfFile() + { + _previousSeekPosition = 0; + return ReadFileContentsTillEndStartingFromPreviousSeekPosition(); + } + + #endregion + + #region Private Helper Methods + + /// + /// Handled File System Watcher's Created Callback + /// + /// SystemWatcher object + /// SystemWatcher event args + private void TargetFile_Created(object source, FileSystemEventArgs e) + { + // Read the File Contents + StringBuilder sb = ReadFromBeginningToEndOfFile(); + + //call delegate with the string + if (IncomingData != null && sb.Length > 0) + IncomingData(this, sb.ToString(), true); + } + + /// + /// Handled File System Watcher's Changed Callback + /// + /// SystemWatcher object + /// SystemWatcher event args + private void TargetFile_Changed(object source, FileSystemEventArgs e) + { + // File size size changed to less! ~someone must have + // changed content inside of it + FileInfo targetFile = new FileInfo(_FileNameNPath); + bool bIsNew = false; + if (targetFile.Length < _previousSeekPosition) + { + _previousSeekPosition = 0; + bIsNew = true; + } + + // Read the File Contents + StringBuilder sb = ReadFileContentsTillEndStartingFromPreviousSeekPosition(); + + //call delegate with the string + if(IncomingData != null && sb.Length > 0) + IncomingData(this, sb.ToString(), bIsNew); + } + + /// + /// Helper Function to read a file from _previousSeekPosition to End. + /// + /// contents of file starting from _previousSeekPosition + private StringBuilder ReadFileContentsTillEndStartingFromPreviousSeekPosition() + { + lock (_lockingObj) + { + // Open the file + FileStream fs = new FileStream(_FileNameNPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + + // Read File Contents into a StringBuilder + StringBuilder sb = new StringBuilder(); + bool bReadOrContinueReadingFile = true; + while (bReadOrContinueReadingFile) + { + // Position the file + _previousSeekPosition = (int)fs.Seek(_previousSeekPosition, SeekOrigin.Begin); + + //read from current seek position to end of file + byte[] bytesRead = new byte[MAX_BYTE_TO_READ_IN_ONE_TIME]; + int numBytes = fs.Read(bytesRead, 0, MAX_BYTE_TO_READ_IN_ONE_TIME); + _previousSeekPosition += numBytes; + + // Append Data to the String Builder + for (int i = 0; i < numBytes; i++) + sb.Append((char)bytesRead[i]); + + // If we have read up to MAX_BYTE_TO_READ_IN_ONE_TIME, then there could be more data... + bReadOrContinueReadingFile = (numBytes == MAX_BYTE_TO_READ_IN_ONE_TIME); + } + + // Close File + fs.Dispose(); + fs = null; + + // Return SB object + return sb; + } + } + + #endregion + } +} diff --git a/Installer/Common.cs b/Installer/Common.cs new file mode 100644 index 0000000..57684ed --- /dev/null +++ b/Installer/Common.cs @@ -0,0 +1,344 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Yaulw.Process; +using System.IO; +using Yaulw.Tools; +using System.Diagnostics; +using Yaulw.Other; + +namespace Yaulw.Installer +{ + public static class Common + { + + #region Setup / Spawners + + /// + /// Spawn a Setup Process (Setup.exe for example) + /// + public static void PSetupSpwan(string SetupFileNameNPath, string param_s, bool bWait) + { + try + { + if (!String.IsNullOrEmpty(SetupFileNameNPath) && Tools.PathNaming.FileNameIsValid(System.IO.Path.GetFileName(SetupFileNameNPath))) + { + //string dir = Tools.PathNaming.MakeDirectoryPathValid(System.IO.Path.GetDirectoryName(SetupFileNameNPath)); + //SetupFileNameNPath = dir + System.IO.Path.DirectorySeparatorChar + System.IO.Path.GetFileName(SetupFileNameNPath); + + //if (!String.IsNullOrEmpty(param_s)) + //{ + // CMDline cmd = new CMDline(null, null); + // bool bIsValidParse = cmd.Parse(param_s.Split(' '), false); + // if (bIsValidParse) + // param_s = cmd.MakeValidCmdStrFromNewlyParsedCmdLine(); + // else + // return; // Invalid Parameters passed in that could cause OS Command Injection + //} + PStarter.StartProcess(PStartInfo.CreateProcess(SetupFileNameNPath, param_s, "", true, System.Diagnostics.ProcessWindowStyle.Hidden, false), bWait, false); + } + } + catch (Exception e) { throw e; } + } + + /// + /// Spawn a Setup Process (Setup.exe for example) using shell execute + /// + public static void PSetupSpwanShell(string SetupFileNameNPath, string param_s, bool bWait) + { + try + { + if (!String.IsNullOrEmpty(SetupFileNameNPath) && Tools.PathNaming.FileNameIsValid(System.IO.Path.GetFileName(SetupFileNameNPath))) + { + //string dir = Tools.PathNaming.MakeDirectoryPathValid(System.IO.Path.GetDirectoryName(SetupFileNameNPath)); + //SetupFileNameNPath = dir + System.IO.Path.DirectorySeparatorChar + System.IO.Path.GetFileName(SetupFileNameNPath); + + //if (!String.IsNullOrEmpty(param_s)) + //{ + // CMDline cmd = new CMDline(null, null); + // bool bIsValidParse = cmd.Parse(param_s.Split(' '), false); + // if (bIsValidParse) + // param_s = cmd.MakeValidCmdStrFromNewlyParsedCmdLine(); + // else + // return; // Invalid Parameters passed in that could cause OS Command Injection + //} + PStarter.StartProcess(PStartInfo.CreateProcess(SetupFileNameNPath, param_s, "", true, System.Diagnostics.ProcessWindowStyle.Hidden, true), bWait, false); + } + } + catch (Exception e) { throw e; } + } + + /// + /// Spawn a MSI Setup Process (*.msi) + /// + public static void PSetupMSIEXEC(string param_s) + { + try + { + string msiexec = System.Environment.GetFolderPath(Environment.SpecialFolder.System) + "\\msiexec.exe"; + if (System.IO.File.Exists(msiexec)) + { + //if (!String.IsNullOrEmpty(param_s)) + //{ + // CMDline cmd = new CMDline(null, null); + // bool bIsValidParse = cmd.Parse(param_s.Split(' '), false); + // if (bIsValidParse) + // param_s = cmd.MakeValidCmdStrFromNewlyParsedCmdLine(); + // else + // return; // Invalid Parameters passed in that could cause OS Command Injection + //} + PStarter.StartProcess(PStartInfo.CreateProcess(msiexec, param_s, "", true, System.Diagnostics.ProcessWindowStyle.Hidden, false), true, false); + } + } + catch (Exception e) { throw e; } + } + + /// + /// Run a command on the commandline * Hidden * + /// + /// cmd to run + public static string RunCmdLine(string cmdline) + { + string result = PStarter.RunDosCommand(cmdline); + return result; + } + + #endregion + + #region Service (ServiceW Repeat with Check Existence) + + /// + /// Stop a service + /// + /// name of service to stop + /// true if successful, false otherwise + public static bool StopService(string ServiceName) + { + bool bSuccess = true; + if (ServiceW.DoesServiceExist(ServiceName)) + bSuccess = ServiceW.StopService(ServiceName, true, 120); + return bSuccess; + } + + /// + /// Stop a service + /// + /// name of service to stop + /// true if successful, false otherwise + public static bool StopService(string ServiceName, int nTimeoutBeforeKill) + { + bool bSuccess = true; + if (ServiceW.DoesServiceExist(ServiceName)) + bSuccess = ServiceW.StopService(ServiceName, true, nTimeoutBeforeKill); + return bSuccess; + } + + /// + /// start a service + /// + /// name of a service to start + /// true if successful, false otherwise + public static bool StartService(string ServiceName) + { + bool bSuccess = true; + if (ServiceW.DoesServiceExist(ServiceName)) + bSuccess = ServiceW.StartService(ServiceName, 120); + return bSuccess; + } + + /// + /// Does Service Exist + /// + /// Name of service to check + /// true if successful, false otherwise + public static bool ServiceExists(string ServiceName) + { + return ServiceW.DoesServiceExist(ServiceName); + } + + #endregion + + #region Windows Utils + + /// + /// Use this to get the complete path to a .net framework utility like gacutil.exe or + /// installutil.exe.. any .net framework utility. Will fall back to look in the %temp% folder, + /// if not found, giving the opportunity to deploy the util directly with the components + /// + /// the utility in .net you are looking for like gacutil.exe + /// the full filenameNpath or "", if no existing file found + public static string GetNetFrameworkUtilFileNameNPathFile(string UtilFileName) + { + string windir = System.Environment.GetEnvironmentVariable("windir", EnvironmentVariableTarget.Machine); + string NetFramework1_0 = windir + "\\Microsoft.Net\\Framework\\v1.0.3705"; + string NetFramework1_1 = windir + "\\Microsoft.Net\\Framework\\v1.1.4322"; + string NetFramework2_0 = windir + "\\Microsoft.Net\\Framework\\v2.0.50727"; + string NetFramework3_0 = windir + "\\Microsoft.Net\\Framework\\v3.0"; + string NetFramework3_5 = windir + "\\Microsoft.Net\\Framework\\v3.5"; + string NetFramework4_0 = windir + "\\Microsoft.Net\\Framework\\v4.0.30319"; + string TempPath = PathNaming.PathEndsWithNoSlash(Path.GetTempPath()); // We use this as a Fallback, in case file doesn't exist in the framework + string[] Frameworks = new string[] { NetFramework2_0, NetFramework4_0, NetFramework1_0, NetFramework1_1, NetFramework3_0, NetFramework3_5, TempPath }; + + string NetUtilFileNameNPath = ""; + foreach (string framework in Frameworks) + { + if (System.IO.File.Exists(framework + "\\" + UtilFileName)) + { + NetUtilFileNameNPath = framework + "\\" + UtilFileName; + return NetUtilFileNameNPath; + } + }; + return NetUtilFileNameNPath; + } + + /// + /// Quick Helper to get the Program Files Path for the specific system + /// + /// Program Files path with a '/' at the end + public static string GetProgramFilesPathOnSystemWithEndSlash() + { + string ProgramFiles = System.Environment.GetEnvironmentVariable("ProgramFiles(x86)"); + if (String.IsNullOrEmpty(ProgramFiles)) + ProgramFiles = System.Environment.GetEnvironmentVariable("ProgramFiles"); + return PathNaming.PathEndsWithSlash(ProgramFiles); + } + + /// + /// + /// + /// + public static string SetOwnership() + { + // in order to do any of this, sadly, we must first install the windows resource kit + //subinacl /subdirectories *.* /setowner=domainname\user + return String.Empty; + } + + /// + /// To grant the specified User or group Full Control permissions to the folder and its contents + /// + /// full path to folder/directory + /// domainname\administrator, any windows user or group + /// + public static bool GrantFullPermissionToFolderForUserOrGroup(string FolderNPath, string UserOrGroup) + { + if (Directory.Exists(FolderNPath)) + { + string command = String.Format("cacls \"{0}\" /t /e /g {1}:f", FolderNPath, UserOrGroup); + if (command.Contains("Invalid arguments.")) + return false; + else + return true; + } + return false; + } + + #endregion + + #region Windows Commands + + /// + /// Get The System up Time, important because we need SQL Server and Advantage up and running before + /// doing anything + /// + /// + public static TimeSpan GetSystemUpTime() + { + string Result = Yaulw.Installer.Common.RunCmdLine("net statistics server"); + int n = Result.IndexOf("Sessions accepted"); + if (n != -1) + { + Result = Result.Substring(0, n); + n = Result.IndexOf("Statistics since "); + if (n != -1) + { + Result = Result.Substring(n + "Statistics since ".Length); + Result = Result.Replace("\n", ""); + Result = Result.Replace("\r", ""); + + DateTime BootTime; + if (DateTime.TryParse(Result, out BootTime)) + return (DateTime.Now - BootTime); + } + } + + // Something went wrong with the first approach, + // let's try another... + using (var uptime = new PerformanceCounter("System", "System Up Time")) + { + uptime.NextValue(); //Call this an extra time before reading its value + return TimeSpan.FromSeconds(uptime.NextValue()); + } + } + + /// + /// Use this to create a quick File Shortcut on the system using ShellLink (File Only) + /// + /// A valid File on the system to point to (not a url) i believe url's are created differently + /// a valid .lnk file name and location where to put the shortcut + public static void CreateFileShortcut(string TargetFileNameNPath, string LnkFileNameNPath) + { + if (String.IsNullOrEmpty(TargetFileNameNPath) || String.IsNullOrEmpty(LnkFileNameNPath)) + return; + + if (!System.IO.File.Exists(TargetFileNameNPath)) + return; + + if (System.IO.File.Exists(LnkFileNameNPath)) + System.IO.File.Delete(LnkFileNameNPath); + + using (ShellLink shortcut = new ShellLink()) + { + shortcut.Target = TargetFileNameNPath; + shortcut.WorkingDirectory = Path.GetDirectoryName(TargetFileNameNPath); + shortcut.DisplayMode = ShellLink.LinkDisplayMode.edmNormal; + shortcut.Description = ""; // doesn't appear to do anything + shortcut.Save(LnkFileNameNPath); + } + } + + #endregion + + #region Resource Extraction + + /// + /// Extract a Binary Resource from a stream and write it to a file + /// + /// + /// + /// + /// + public static bool ExtractResourceStreamToFile(Stream resourceStream, string FileNameNPath, bool bForceWrite) + { + if (resourceStream != null && !String.IsNullOrEmpty(FileNameNPath)) + { + if (bForceWrite || !System.IO.File.Exists(FileNameNPath)) + { + + try + { + using (resourceStream) + using (BinaryReader br = new BinaryReader(resourceStream)) + using (BinaryWriter bw = new BinaryWriter(new FileStream(FileNameNPath, FileMode.Create))) + { + byte[] buffer = new byte[64 * 1024]; + int numread = br.Read(buffer, 0, buffer.Length); + while (numread > 0) + { + bw.Write(buffer, 0, numread); + numread = br.Read(buffer, 0, buffer.Length); + } + bw.Flush(); + } + return true; + } + catch (Exception) { /* ignore */ } + } + } + return false; + } + + #endregion + } +} diff --git a/Installer/FileIcon.cs b/Installer/FileIcon.cs new file mode 100644 index 0000000..a32bd2d --- /dev/null +++ b/Installer/FileIcon.cs @@ -0,0 +1,249 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; +using System.Drawing; + +namespace Yaulw.Installer +{ + /// + /// Enables extraction of icons for any file type from + /// the Shell. + /// + /// + public class FileIcon + { + #region UnmanagedCode + private const int MAX_PATH = 260; + + [StructLayout(LayoutKind.Sequential)] + private struct SHFILEINFO + { + public IntPtr hIcon; + public int iIcon; + public int dwAttributes; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)] + public string szDisplayName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] + public string szTypeName; + } + + [DllImport("shell32")] + private static extern int SHGetFileInfo( + string pszPath, + int dwFileAttributes, + ref SHFILEINFO psfi, + uint cbFileInfo, + uint uFlags); + + [DllImport("user32.dll")] + private static extern int DestroyIcon(IntPtr hIcon); + + private const int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x100; + private const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x2000; + private const int FORMAT_MESSAGE_FROM_HMODULE = 0x800; + private const int FORMAT_MESSAGE_FROM_STRING = 0x400; + private const int FORMAT_MESSAGE_FROM_SYSTEM = 0x1000; + private const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x200; + private const int FORMAT_MESSAGE_MAX_WIDTH_MASK = 0xFF; + [DllImport("kernel32")] + private extern static int FormatMessage( + int dwFlags, + IntPtr lpSource, + int dwMessageId, + int dwLanguageId, + string lpBuffer, + uint nSize, + int argumentsLong); + + [DllImport("kernel32")] + private extern static int GetLastError(); + #endregion + + #region Member Variables + private string fileName; + private string displayName; + private string typeName; + private SHGetFileInfoConstants flags; + private Icon fileIcon; + #endregion + + #region Enumerations + [Flags] + public enum SHGetFileInfoConstants : int + { + SHGFI_ICON = 0x100, // get icon + SHGFI_DISPLAYNAME = 0x200, // get display name + SHGFI_TYPENAME = 0x400, // get type name + SHGFI_ATTRIBUTES = 0x800, // get attributes + SHGFI_ICONLOCATION = 0x1000, // get icon location + SHGFI_EXETYPE = 0x2000, // return exe type + SHGFI_SYSICONINDEX = 0x4000, // get system icon index + SHGFI_LINKOVERLAY = 0x8000, // put a link overlay on icon + SHGFI_SELECTED = 0x10000, // show icon in selected state + SHGFI_ATTR_SPECIFIED = 0x20000, // get only specified attributes + SHGFI_LARGEICON = 0x0, // get large icon + SHGFI_SMALLICON = 0x1, // get small icon + SHGFI_OPENICON = 0x2, // get open icon + SHGFI_SHELLICONSIZE = 0x4, // get shell size icon + //SHGFI_PIDL = 0x8, // pszPath is a pidl + SHGFI_USEFILEATTRIBUTES = 0x10, // use passed dwFileAttribute + SHGFI_ADDOVERLAYS = 0x000000020, // apply the appropriate overlays + SHGFI_OVERLAYINDEX = 0x000000040 // Get the index of the overlay + } + #endregion + + #region Implementation + /// + /// Gets/sets the flags used to extract the icon + /// + public FileIcon.SHGetFileInfoConstants Flags + { + get + { + return flags; + } + set + { + flags = value; + } + } + + /// + /// Gets/sets the filename to get the icon for + /// + public string FileName + { + get + { + return fileName; + } + set + { + fileName = value; + } + } + + /// + /// Gets the icon for the chosen file + /// + public Icon ShellIcon + { + get + { + return fileIcon; + } + } + + /// + /// Gets the display name for the selected file + /// if the SHGFI_DISPLAYNAME flag was set. + /// + public string DisplayName + { + get + { + return displayName; + } + } + + /// + /// Gets the type name for the selected file + /// if the SHGFI_TYPENAME flag was set. + /// + public string TypeName + { + get + { + return typeName; + } + } + + /// + /// Gets the information for the specified + /// file name and flags. + /// + public void GetInfo() + { + fileIcon = null; + typeName = ""; + displayName = ""; + + SHFILEINFO shfi = new SHFILEINFO(); + uint shfiSize = (uint)Marshal.SizeOf(shfi.GetType()); + + int ret = SHGetFileInfo( + fileName, 0, ref shfi, shfiSize, (uint)(flags)); + if (ret != 0) + { + if (shfi.hIcon != IntPtr.Zero) + { + fileIcon = System.Drawing.Icon.FromHandle(shfi.hIcon); + // Now owned by the GDI+ object + //DestroyIcon(shfi.hIcon); + } + typeName = shfi.szTypeName; + displayName = shfi.szDisplayName; + } + else + { + + int err = GetLastError(); + Console.WriteLine("Error {0}", err); + string txtS = new string('\0', 256); + int len = FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + IntPtr.Zero, err, 0, txtS, 256, 0); + Console.WriteLine("Len {0} text {1}", len, txtS); + + // throw exception + + } + } + + /// + /// Constructs a new, default instance of the FileIcon + /// class. Specify the filename and call GetInfo() + /// to retrieve an icon. + /// + public FileIcon() + { + flags = SHGetFileInfoConstants.SHGFI_ICON | + SHGetFileInfoConstants.SHGFI_DISPLAYNAME | + SHGetFileInfoConstants.SHGFI_TYPENAME | + SHGetFileInfoConstants.SHGFI_ATTRIBUTES | + SHGetFileInfoConstants.SHGFI_EXETYPE; + } + /// + /// Constructs a new instance of the FileIcon class + /// and retrieves the icon, display name and type name + /// for the specified file. + /// + /// The filename to get the icon, + /// display name and type name for + public FileIcon(string fileName) + : this() + { + this.fileName = fileName; + GetInfo(); + } + /// + /// Constructs a new instance of the FileIcon class + /// and retrieves the information specified in the + /// flags. + /// + /// The filename to get information + /// for + /// The flags to use when extracting the + /// icon and other shell information. + public FileIcon(string fileName, FileIcon.SHGetFileInfoConstants flags) + { + this.fileName = fileName; + this.flags = flags; + GetInfo(); + } + + #endregion + } +} diff --git a/Installer/ShellLink.cs b/Installer/ShellLink.cs new file mode 100644 index 0000000..82c4b73 --- /dev/null +++ b/Installer/ShellLink.cs @@ -0,0 +1,953 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace Yaulw.Installer +{ + #region ShellLink Object + /// + /// Summary description for ShellLink. + /// + /// + public class ShellLink : IDisposable + { + #region ComInterop for IShellLink + + #region IPersist Interface + [ComImportAttribute()] + [GuidAttribute("0000010C-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + private interface IPersist + { + [PreserveSig] + //[helpstring("Returns the class identifier for the component object")] + void GetClassID(out Guid pClassID); + } + #endregion + + #region IPersistFile Interface + [ComImportAttribute()] + [GuidAttribute("0000010B-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + private interface IPersistFile + { + // can't get this to go if I extend IPersist, so put it here: + [PreserveSig] + void GetClassID(out Guid pClassID); + + //[helpstring("Checks for changes since last file write")] + void IsDirty(); + + //[helpstring("Opens the specified file and initializes the object from its contents")] + void Load( + [MarshalAs(UnmanagedType.LPWStr)] string pszFileName, + uint dwMode); + + //[helpstring("Saves the object into the specified file")] + void Save( + [MarshalAs(UnmanagedType.LPWStr)] string pszFileName, + [MarshalAs(UnmanagedType.Bool)] bool fRemember); + + //[helpstring("Notifies the object that save is completed")] + void SaveCompleted( + [MarshalAs(UnmanagedType.LPWStr)] string pszFileName); + + //[helpstring("Gets the current name of the file associated with the object")] + void GetCurFile( + [MarshalAs(UnmanagedType.LPWStr)] out string ppszFileName); + } + #endregion + + #region IShellLink Interface + [ComImportAttribute()] + [GuidAttribute("000214EE-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + private interface IShellLinkA + { + //[helpstring("Retrieves the path and filename of a shell link object")] + void GetPath( + [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszFile, + int cchMaxPath, + ref _WIN32_FIND_DATAA pfd, + uint fFlags); + + //[helpstring("Retrieves the list of shell link item identifiers")] + void GetIDList(out IntPtr ppidl); + + //[helpstring("Sets the list of shell link item identifiers")] + void SetIDList(IntPtr pidl); + + //[helpstring("Retrieves the shell link description string")] + void GetDescription( + [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszFile, + int cchMaxName); + + //[helpstring("Sets the shell link description string")] + void SetDescription( + [MarshalAs(UnmanagedType.LPStr)] string pszName); + + //[helpstring("Retrieves the name of the shell link working directory")] + void GetWorkingDirectory( + [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszDir, + int cchMaxPath); + + //[helpstring("Sets the name of the shell link working directory")] + void SetWorkingDirectory( + [MarshalAs(UnmanagedType.LPStr)] string pszDir); + + //[helpstring("Retrieves the shell link command-line arguments")] + void GetArguments( + [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszArgs, + int cchMaxPath); + + //[helpstring("Sets the shell link command-line arguments")] + void SetArguments( + [MarshalAs(UnmanagedType.LPStr)] string pszArgs); + + //[propget, helpstring("Retrieves or sets the shell link hot key")] + void GetHotkey(out short pwHotkey); + //[propput, helpstring("Retrieves or sets the shell link hot key")] + void SetHotkey(short pwHotkey); + + //[propget, helpstring("Retrieves or sets the shell link show command")] + void GetShowCmd(out uint piShowCmd); + //[propput, helpstring("Retrieves or sets the shell link show command")] + void SetShowCmd(uint piShowCmd); + + //[helpstring("Retrieves the location (path and index) of the shell link icon")] + void GetIconLocation( + [Out(), MarshalAs(UnmanagedType.LPStr)] StringBuilder pszIconPath, + int cchIconPath, + out int piIcon); + + //[helpstring("Sets the location (path and index) of the shell link icon")] + void SetIconLocation( + [MarshalAs(UnmanagedType.LPStr)] string pszIconPath, + int iIcon); + + //[helpstring("Sets the shell link relative path")] + void SetRelativePath( + [MarshalAs(UnmanagedType.LPStr)] string pszPathRel, + uint dwReserved); + + //[helpstring("Resolves a shell link. The system searches for the shell link object and updates the shell link path and its list of identifiers (if necessary)")] + void Resolve( + IntPtr hWnd, + uint fFlags); + + //[helpstring("Sets the shell link path and filename")] + void SetPath( + [MarshalAs(UnmanagedType.LPStr)] string pszFile); + } + + + [ComImportAttribute()] + [GuidAttribute("000214F9-0000-0000-C000-000000000046")] + [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + private interface IShellLinkW + { + //[helpstring("Retrieves the path and filename of a shell link object")] + void GetPath( + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxPath, + ref _WIN32_FIND_DATAW pfd, + uint fFlags); + + //[helpstring("Retrieves the list of shell link item identifiers")] + void GetIDList(out IntPtr ppidl); + + //[helpstring("Sets the list of shell link item identifiers")] + void SetIDList(IntPtr pidl); + + //[helpstring("Retrieves the shell link description string")] + void GetDescription( + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, + int cchMaxName); + + //[helpstring("Sets the shell link description string")] + void SetDescription( + [MarshalAs(UnmanagedType.LPWStr)] string pszName); + + //[helpstring("Retrieves the name of the shell link working directory")] + void GetWorkingDirectory( + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, + int cchMaxPath); + + //[helpstring("Sets the name of the shell link working directory")] + void SetWorkingDirectory( + [MarshalAs(UnmanagedType.LPWStr)] string pszDir); + + //[helpstring("Retrieves the shell link command-line arguments")] + void GetArguments( + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, + int cchMaxPath); + + //[helpstring("Sets the shell link command-line arguments")] + void SetArguments( + [MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + + //[propget, helpstring("Retrieves or sets the shell link hot key")] + void GetHotkey(out short pwHotkey); + //[propput, helpstring("Retrieves or sets the shell link hot key")] + void SetHotkey(short pwHotkey); + + //[propget, helpstring("Retrieves or sets the shell link show command")] + void GetShowCmd(out uint piShowCmd); + //[propput, helpstring("Retrieves or sets the shell link show command")] + void SetShowCmd(uint piShowCmd); + + //[helpstring("Retrieves the location (path and index) of the shell link icon")] + void GetIconLocation( + [Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, + out int piIcon); + + //[helpstring("Sets the location (path and index) of the shell link icon")] + void SetIconLocation( + [MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, + int iIcon); + + //[helpstring("Sets the shell link relative path")] + void SetRelativePath( + [MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, + uint dwReserved); + + //[helpstring("Resolves a shell link. The system searches for the shell link object and updates the shell link path and its list of identifiers (if necessary)")] + void Resolve( + IntPtr hWnd, + uint fFlags); + + //[helpstring("Sets the shell link path and filename")] + void SetPath( + [MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + #endregion + + #region ShellLinkCoClass + [GuidAttribute("00021401-0000-0000-C000-000000000046")] + [ClassInterfaceAttribute(ClassInterfaceType.None)] + [ComImportAttribute()] + private class CShellLink{} + + #endregion + + #region Private IShellLink enumerations + private enum EShellLinkGP : uint + { + SLGP_SHORTPATH = 1, + SLGP_UNCPRIORITY = 2 + } + + [Flags] + private enum EShowWindowFlags : uint + { + SW_HIDE = 0, + SW_SHOWNORMAL = 1, + SW_NORMAL = 1, + SW_SHOWMINIMIZED = 2, + SW_SHOWMAXIMIZED = 3, + SW_MAXIMIZE = 3, + SW_SHOWNOACTIVATE = 4, + SW_SHOW = 5, + SW_MINIMIZE = 6, + SW_SHOWMINNOACTIVE = 7, + SW_SHOWNA = 8, + SW_RESTORE = 9, + SW_SHOWDEFAULT = 10, + SW_MAX = 10 + } + #endregion + + #region IShellLink Private structs + + [StructLayoutAttribute(LayoutKind.Sequential, Pack=4, Size=0, + CharSet=CharSet.Unicode)] + private struct _WIN32_FIND_DATAW + { + public uint dwFileAttributes; + public _FILETIME ftCreationTime; + public _FILETIME ftLastAccessTime; + public _FILETIME ftLastWriteTime; + public uint nFileSizeHigh; + public uint nFileSizeLow; + public uint dwReserved0; + public uint dwReserved1; + [MarshalAs(UnmanagedType.ByValTStr , SizeConst = 260)] // MAX_PATH + public string cFileName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] + public string cAlternateFileName; + } + + [StructLayoutAttribute(LayoutKind.Sequential, Pack=4, Size=0, + CharSet=CharSet.Ansi)] + private struct _WIN32_FIND_DATAA + { + public uint dwFileAttributes; + public _FILETIME ftCreationTime; + public _FILETIME ftLastAccessTime; + public _FILETIME ftLastWriteTime; + public uint nFileSizeHigh; + public uint nFileSizeLow; + public uint dwReserved0; + public uint dwReserved1; + [MarshalAs(UnmanagedType.ByValTStr , SizeConst = 260)] // MAX_PATH + public string cFileName; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] + public string cAlternateFileName; + } + + [StructLayoutAttribute(LayoutKind.Sequential, Pack=4, Size=0)] + private struct _FILETIME + { + public uint dwLowDateTime; + public uint dwHighDateTime; + } + #endregion + + #region UnManaged Methods + private class UnManagedMethods + { + [DllImport("Shell32", CharSet=CharSet.Auto)] + internal extern static int ExtractIconEx ( + [MarshalAs(UnmanagedType.LPTStr)] + string lpszFile, + int nIconIndex, + IntPtr[] phIconLarge, + IntPtr[] phIconSmall, + int nIcons); + + [DllImport("user32")] + internal static extern int DestroyIcon(IntPtr hIcon); + } + #endregion + + #endregion + + #region Enumerations + /// + /// Flags determining how the links with missing + /// targets are resolved. + /// + [Flags] + public enum EShellLinkResolveFlags : uint + { + /// + /// Allow any match during resolution. Has no effect + /// on ME/2000 or above, use the other flags instead. + /// + SLR_ANY_MATCH = 0x2, + /// + /// Call the Microsoft Windows Installer. + /// + SLR_INVOKE_MSI = 0x80, + /// + /// Disable distributed link tracking. By default, + /// distributed link tracking tracks removable media + /// across multiple devices based on the volume name. + /// It also uses the UNC path to track remote file + /// systems whose drive letter has changed. Setting + /// SLR_NOLINKINFO disables both types of tracking. + /// + SLR_NOLINKINFO = 0x40, + /// + /// Do not display a dialog box if the link cannot be resolved. + /// When SLR_NO_UI is set, a time-out value that specifies the + /// maximum amount of time to be spent resolving the link can + /// be specified in milliseconds. The function returns if the + /// link cannot be resolved within the time-out duration. + /// If the timeout is not set, the time-out duration will be + /// set to the default value of 3,000 milliseconds (3 seconds). + /// + SLR_NO_UI = 0x1, + /// + /// Not documented in SDK. Assume same as SLR_NO_UI but + /// intended for applications without a hWnd. + /// + SLR_NO_UI_WITH_MSG_PUMP = 0x101, + /// + /// Do not update the link information. + /// + SLR_NOUPDATE = 0x8, + /// + /// Do not execute the search heuristics. + /// + SLR_NOSEARCH = 0x10, + /// + /// Do not use distributed link tracking. + /// + SLR_NOTRACK = 0x20, + /// + /// If the link object has changed, update its path and list + /// of identifiers. If SLR_UPDATE is set, you do not need to + /// call IPersistFile::IsDirty to determine whether or not + /// the link object has changed. + /// + SLR_UPDATE = 0x4 + } + + public enum LinkDisplayMode : uint + { + edmNormal = EShowWindowFlags.SW_NORMAL, + edmMinimized = EShowWindowFlags.SW_SHOWMINNOACTIVE, + edmMaximized = EShowWindowFlags.SW_MAXIMIZE + } + #endregion + + #region Member Variables + // Use Unicode (W) under NT, otherwise use ANSI + private IShellLinkW linkW; + private IShellLinkA linkA; + private string shortcutFile = ""; + #endregion + + #region Constructor + /// + /// Creates an instance of the Shell Link object. + /// + public ShellLink() + { + if (System.Environment.OSVersion.Platform == PlatformID.Win32NT) + { + linkW = (IShellLinkW)new CShellLink(); + } + else + { + linkA = (IShellLinkA)new CShellLink(); + } + } + + /// + /// Creates an instance of a Shell Link object + /// from the specified link file + /// + /// The Shortcut file to open + public ShellLink(string linkFile) : this() + { + Open(linkFile); + } + #endregion + + #region Destructor and Dispose + /// + /// Call dispose just in case it hasn't happened yet + /// + ~ShellLink() + { + Dispose(); + } + + /// + /// Dispose the object, releasing the COM ShellLink object + /// + public void Dispose() + { + if (linkW != null ) + { + Marshal.ReleaseComObject(linkW); + linkW = null; + } + if (linkA != null) + { + Marshal.ReleaseComObject(linkA); + linkA = null; + } + } + #endregion + + #region Implementation + public string ShortCutFile + { + get + { + return this.shortcutFile; + } + set + { + this.shortcutFile = value; + } + } + + /// + /// Gets a System.Drawing.Icon containing the icon for this + /// ShellLink object. + /// + public Icon LargeIcon + { + get + { + return getIcon(true); + } + } + + public Icon SmallIcon + { + get + { + return getIcon(false); + } + } + + private Icon getIcon(bool large) + { + // Get icon index and path: + int iconIndex = 0; + StringBuilder iconPath = new StringBuilder(260, 260); + if (linkA == null) + { + linkW.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); + } + else + { + linkA.GetIconLocation(iconPath, iconPath.Capacity, out iconIndex); + } + string iconFile = iconPath.ToString(); + + // If there are no details set for the icon, then we must use + // the shell to get the icon for the target: + if (iconFile.Length == 0) + { + // Use the FileIcon object to get the icon: + FileIcon.SHGetFileInfoConstants flags = + FileIcon.SHGetFileInfoConstants.SHGFI_ICON | + FileIcon.SHGetFileInfoConstants.SHGFI_ATTRIBUTES; + if (large) + { + flags = flags | FileIcon.SHGetFileInfoConstants.SHGFI_LARGEICON; + } + else + { + flags = flags | FileIcon.SHGetFileInfoConstants.SHGFI_SMALLICON; + } + FileIcon fileIcon = new FileIcon(Target, flags); + return fileIcon.ShellIcon; + } + else + { + // Use ExtractIconEx to get the icon: + IntPtr[] hIconEx = new IntPtr[1] {IntPtr.Zero}; + int iconCount = 0; + if (large) + { + iconCount = UnManagedMethods.ExtractIconEx( + iconFile, + iconIndex, + hIconEx, + null, + 1); + } + else + { + iconCount = UnManagedMethods.ExtractIconEx( + iconFile, + iconIndex, + null, + hIconEx, + 1); + } + // If success then return as a GDI+ object + Icon icon = null; + if (hIconEx[0] != IntPtr.Zero) + { + icon = Icon.FromHandle(hIconEx[0]); + //UnManagedMethods.DestroyIcon(hIconEx[0]); + } + return icon; + } + } + + /// + /// Gets the path to the file containing the icon for this shortcut. + /// + public string IconPath + { + get + { + StringBuilder iconPath = new StringBuilder(260, 260); + int iconIndex = 0; + if (linkA == null) + { + linkW.GetIconLocation(iconPath, iconPath.Capacity, out + iconIndex); + } + else + { + linkA.GetIconLocation(iconPath, iconPath.Capacity, out + iconIndex); + } + return iconPath.ToString(); + } + set + { + StringBuilder iconPath = new StringBuilder(260, 260); + int iconIndex = 0; + if (linkA == null) + { + linkW.GetIconLocation(iconPath, iconPath.Capacity, out + iconIndex); + } + else + { + linkA.GetIconLocation(iconPath, iconPath.Capacity, out + iconIndex); + } + if (linkA == null) + { + linkW.SetIconLocation(value, iconIndex); + } + else + { + linkA.SetIconLocation(value, iconIndex); + } + } + } + + /// + /// Gets the index of this icon within the icon path's resources + /// + public int IconIndex + { + get + { + StringBuilder iconPath = new StringBuilder(260, 260); + int iconIndex = 0; + if (linkA == null) + { + linkW.GetIconLocation(iconPath, iconPath.Capacity, out + iconIndex); + } + else + { + linkA.GetIconLocation(iconPath, iconPath.Capacity, out + iconIndex); + } + return iconIndex; + } + set + { + StringBuilder iconPath = new StringBuilder(260, 260); + int iconIndex = 0; + if (linkA == null) + { + linkW.GetIconLocation(iconPath, iconPath.Capacity, out + iconIndex); + } + else + { + linkA.GetIconLocation(iconPath, iconPath.Capacity, out + iconIndex); + } + if (linkA == null) + { + linkW.SetIconLocation(iconPath.ToString(), value); + } + else + { + linkA.SetIconLocation(iconPath.ToString(), value); + } + } + } + + /// + /// Gets/sets the fully qualified path to the link's target + /// + public string Target + { + get + { + StringBuilder target = new StringBuilder(260, 260); + if (linkA == null) + { + _WIN32_FIND_DATAW fd = new _WIN32_FIND_DATAW(); + linkW.GetPath(target, target.Capacity, ref fd, + (uint)EShellLinkGP.SLGP_UNCPRIORITY); + } + else + { + _WIN32_FIND_DATAA fd = new _WIN32_FIND_DATAA(); + linkA.GetPath(target, target.Capacity, ref fd, + (uint)EShellLinkGP.SLGP_UNCPRIORITY); + } + return target.ToString(); + } + set + { + if (linkA == null) + { + linkW.SetPath(value); + } + else + { + linkA.SetPath(value); + } + } + } + + /// + /// Gets/sets the Working Directory for the Link + /// + public string WorkingDirectory + { + get + { + StringBuilder path = new StringBuilder(260, 260); + if (linkA == null) + { + linkW.GetWorkingDirectory(path, path.Capacity); + } + else + { + linkA.GetWorkingDirectory(path, path.Capacity); + } + return path.ToString(); + } + set + { + if (linkA == null) + { + linkW.SetWorkingDirectory(value); + } + else + { + linkA.SetWorkingDirectory(value); + } + } + } + + /// + /// Gets/sets the description of the link + /// + public string Description + { + get + { + StringBuilder description = new StringBuilder(1024, 1024); + if (linkA == null) + { + linkW.GetDescription(description, description.Capacity); + } + else + { + linkA.GetDescription(description, description.Capacity); + } + return description.ToString(); + } + set + { + if (linkA == null) + { + linkW.SetDescription(value); + } + else + { + linkA.SetDescription(value); + } + } + } + + /// + /// Gets/sets any command line arguments associated with the link + /// + public string Arguments + { + get + { + StringBuilder arguments = new StringBuilder(260, 260); + if (linkA == null) + { + linkW.GetArguments(arguments, arguments.Capacity); + } + else + { + linkA.GetArguments(arguments, arguments.Capacity); + } + return arguments.ToString(); + } + set + { + if (linkA == null) + { + linkW.SetArguments(value); + } + else + { + linkA.SetArguments(value); + } + } + } + + /// + /// Gets/sets the initial display mode when the shortcut is + /// run + /// + public LinkDisplayMode DisplayMode + { + get + { + uint cmd = 0; + if (linkA == null) + { + linkW.GetShowCmd(out cmd); + } + else + { + linkA.GetShowCmd(out cmd); + } + return (LinkDisplayMode)cmd; + } + set + { + if (linkA == null) + { + linkW.SetShowCmd((uint)value); + } + else + { + linkA.SetShowCmd((uint)value); + } + } + } + + /// + /// Gets/sets the HotKey to start the shortcut (if any) + /// + public Keys HotKey + { + get + { + short key = 0; + if (linkA == null) + { + linkW.GetHotkey(out key); + } + else + { + linkA.GetHotkey(out key); + } + return (Keys)key; + } + set + { + if (linkA == null) + { + linkW.SetHotkey((short)value); + } + else + { + linkA.SetHotkey((short)value); + } + } + } + + /// + /// Saves the shortcut to ShortCutFile. + /// + public void Save() + { + Save(shortcutFile); + } + + /// + /// Saves the shortcut to the specified file + /// + /// The shortcut file (.lnk) + public void Save( + string linkFile + ) + { + // Save the object to disk + if (linkA == null) + { + ((IPersistFile)linkW).Save(linkFile, true); + shortcutFile = linkFile; + } + else + { + ((IPersistFile)linkA).Save(linkFile, true); + shortcutFile = linkFile; + } + } + + /// + /// Loads a shortcut from the specified file + /// + /// The shortcut file (.lnk) to load + public void Open( + string linkFile + ) + { + Open(linkFile, + IntPtr.Zero, + (EShellLinkResolveFlags.SLR_ANY_MATCH | + EShellLinkResolveFlags.SLR_NO_UI), + 1); + } + + /// + /// Loads a shortcut from the specified file, and allows flags controlling + /// the UI behaviour if the shortcut's target isn't found to be set. + /// + /// The shortcut file (.lnk) to load + /// The window handle of the application's UI, if any + /// Flags controlling resolution behaviour + public void Open( + string linkFile, + IntPtr hWnd, + EShellLinkResolveFlags resolveFlags + ) + { + Open(linkFile, + hWnd, + resolveFlags, + 1); + } + + /// + /// Loads a shortcut from the specified file, and allows flags controlling + /// the UI behaviour if the shortcut's target isn't found to be set. If + /// no SLR_NO_UI is specified, you can also specify a timeout. + /// + /// The shortcut file (.lnk) to load + /// The window handle of the application's UI, if any + /// Flags controlling resolution behaviour + /// Timeout if SLR_NO_UI is specified, in ms. + public void Open( + string linkFile, + IntPtr hWnd, + EShellLinkResolveFlags resolveFlags, + ushort timeOut + ) + { + uint flags; + + if ((resolveFlags & EShellLinkResolveFlags.SLR_NO_UI) + == EShellLinkResolveFlags.SLR_NO_UI) + { + flags = (uint)((int)resolveFlags | (timeOut << 16)); + } + else + { + flags = (uint)resolveFlags; + } + + if (linkA == null) + { + ((IPersistFile)linkW).Load(linkFile, 0); //STGM_DIRECT) + linkW.Resolve(hWnd, flags); + this.shortcutFile = linkFile; + } + else + { + ((IPersistFile)linkA).Load(linkFile, 0); //STGM_DIRECT) + linkA.Resolve(hWnd, flags); + this.shortcutFile = linkFile; + } + } + #endregion + } + #endregion +} diff --git a/Monitor/MonitorDataStore.cs b/Monitor/MonitorDataStore.cs new file mode 100644 index 0000000..a8c9e9b --- /dev/null +++ b/Monitor/MonitorDataStore.cs @@ -0,0 +1,1698 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Serialization; +using System.Collections; +using System.IO; +using Yaulw.Process; +using Yaulw.Other; +using Yaulw.Xml; + +namespace Yaulw.Monitor +{ + #region XML Data Definition + + /// + /// Serializable Xml Object used to store Monitor Configuration data + /// + [XmlRoot("configuration", Namespace = "Monitor", IsNullable = false)] + public class MonitorConfiguration : ICloneable + { + /// + /// MonitoredProcesses Member Tag <> holds list of ProcessExe's to Monitor + /// + public MonitoredProcessesW MonitoredProcesses = null; + + /// + /// MonitoredServices Member Tag <> holds list of Services to Monitor + /// + public MonitoredServicesW MonitoredServices = null; + + /// + /// Monitor Configuration - Constructor + /// + public MonitorConfiguration() + { + MonitoredProcesses = new MonitoredProcessesW(); + MonitoredServices = new MonitoredServicesW(); + } + + #region Internal Monitor Properties N' Methods + + /// + /// True if Either Any Processes or Any Services are Configured + /// + internal bool ConfiguredHasAny { get { return (MonitoredProcesses.ProcessExes.Length > 0 || MonitoredServices.ServiceExes.Length > 0); } } + + /// + /// True if Any Processes are Configured + /// + internal bool ConfiguredHasAnyProcesses { get { return (MonitoredProcesses.ProcessExes.Length > 0); } } + + /// + /// True if Any Services are Configured + /// + internal bool ConfiguredHasAnyServices { get { return (MonitoredServices.ServiceExes.Length > 0); } } + + /// + /// Clears all Start and Fail Times for Both Processes and Services + /// + internal void ClearAllStartNFailTimes() + { + MonitoredProcesses.ClearProcessExeStartNFailTimes(); + MonitoredServices.ClearServiceExeStartNFailTimes(); + } + + /// + /// Check to see if there are any Start or Fail Times, + /// if there aren't. Consider this configuration as newly loaded. + /// + /// true, if there are any Start or Fail Times recorded, false otherwise + internal bool HasAnyStartNFailTimes() + { + if (MonitoredProcesses.HasAnyProcessExeStartNFailTimes() || + MonitoredServices.HasAnyServiceExeStartNFailTimes()) + return true; + else + return false; + } + + #endregion + + /// + /// MonitoredProcessesW - (Wrapper) around ProcessExe ArrayList to work with XML + /// + public class MonitoredProcessesW : IComparable, ICloneable + { + #region Private Members + + private ArrayList m_ArrayList; + + #endregion + + #region Construction + + /// + /// Constructor for MonitoredProcessesW + /// + public MonitoredProcessesW() + { + m_ArrayList = new ArrayList(); + } + + #endregion + + #region XML Process Element + + /// + /// Property useful to directly Get/Set the ProcessExes as an Array + /// + [XmlElement("Process")] + public ProcessExe[] ProcessExes + { + get + { + ProcessExe[] processExes = new ProcessExe[m_ArrayList.Count]; + m_ArrayList.CopyTo(processExes); + return processExes; + } + set + { + if (value == null) return; + ProcessExe[] processExes = (ProcessExe[])value; + m_ArrayList.Clear(); + foreach (ProcessExe processExe in processExes) + AddProcessExe(processExe); + } + } + + #endregion + + #region Public Get Functions + + /// + /// Use this to get all processes settings for a specific Process Exe File + /// + /// specific process exe to get settings for + /// a list of process settings for the specified exe or empty list if not found + public ProcessExe[] GetProcessExesByProcessExeFileNameNPath(string ProcessExeFileNameNPath) + { + List foundProcessExes = new List(); + foreach (ProcessExe _cmpProcessExe in m_ArrayList) + { + if (String.Compare(_cmpProcessExe.ProcessExeFileNameNPath, ProcessExeFileNameNPath, true) == 0) + foundProcessExes.Add(_cmpProcessExe); + } + return foundProcessExes.ToArray(); + } + + /// + /// Use this to get all processes settings for a specific Process Exe FileName without Extension + /// + /// specific process exe fileName without Extension to get settings for + /// a list of process settings for the specified exe or empty list if not found + public ProcessExe[] GetProcessExesByProcessExeFileNameWithoutExtension(string ProcessExeFileNameWithoutExtension) + { + List foundProcessExes = new List(); + foreach (ProcessExe _cmpProcessExe in m_ArrayList) + { + if (String.Compare(Path.GetFileNameWithoutExtension(_cmpProcessExe.ProcessExeFileNameNPath), ProcessExeFileNameWithoutExtension, true) == 0) + foundProcessExes.Add(_cmpProcessExe); + } + return foundProcessExes.ToArray(); + } + + #endregion + + #region Public Add Functions + + /// + /// Helper function to Add a Process Exe + /// + /// ProcessExe to Add + /// true if successfull, false otherwise + public bool AddProcessExe(ProcessExe processExe) + { + bool bAddSuccess = false; + if (processExe != null && !String.IsNullOrEmpty(processExe.ProcessExeFileNameNPath)) + { + if (processExe.CommandLinePrms == null) + processExe.CommandLinePrms = String.Empty; + if (processExe.WorkingDirectory == null) + processExe.WorkingDirectory = String.Empty; + + ProcessExe pFound = null; + if (ProcessExeFileExistsLocally(processExe) && IsNotExcludedProcessExe(processExe) && IsUniqueProcessExe(processExe, out pFound)) + { + bAddSuccess = (m_ArrayList.Add(processExe) >= 0); + } + else if (pFound != null) + { + // if Working Directory isn't the same, we allow updating it via AddProcessExe Call * Perform Update * + bAddSuccess = (String.Compare(pFound.WorkingDirectory, processExe.WorkingDirectory, true) != 0); + if (bAddSuccess) + pFound.WorkingDirectory = processExe.WorkingDirectory; + } + } + return bAddSuccess; + } + + /// + /// Helper function to Add a Process Exe + /// + /// specify a fileName and path for a process + /// specify a command-line params to use for the process + /// specify the working directory to use for the process + /// true if successfull, false otherwise + //public bool AddProcessExe(string ProcessExeFileNameNPath, string CommandLinePrms, string WorkingDirectory = "") + public bool AddProcessExe(string ProcessExeFileNameNPath, string CommandLinePrms, string WorkingDirectory) + { + bool bAddSuccess = false; + if (!String.IsNullOrEmpty(ProcessExeFileNameNPath)) + { + if (CommandLinePrms == null) + CommandLinePrms = String.Empty; + if (WorkingDirectory == null) + WorkingDirectory = String.Empty; + + ProcessExe processToAdd = new ProcessExe(Yaulw.Win32.Functions.GetLongFileNameNPathOrPath(ProcessExeFileNameNPath.Trim()), CommandLinePrms.Trim(), WorkingDirectory.Trim()); + ProcessExe pFound = null; + if (ProcessExeFileExistsLocally(processToAdd) && IsNotExcludedProcessExe(processToAdd) && IsUniqueProcessExe(processToAdd, out pFound)) + { + bAddSuccess = (m_ArrayList.Add(processToAdd) >= 0); + } + else if (pFound != null) + { + // if Working Directory isn't the same, we allow updating it via AddProcessExe Call * Perform Update * + bAddSuccess = (String.Compare(pFound.WorkingDirectory, processToAdd.WorkingDirectory, true) != 0); + if (bAddSuccess) + pFound.WorkingDirectory = processToAdd.WorkingDirectory; + } + } + return bAddSuccess; + } + + /// + /// Helper function to check whether we can Add a Process Exe + /// + /// specify a fileName and path for a process + /// specify a command-line params to use for the process + /// specify the working directory to use for the process + /// true if successfull, false otherwise + //public bool CanAddProcessExe(string ProcessExeFileNameNPath, string CommandLinePrms, string WorkingDirectory = "") + public bool CanAddProcessExe(string ProcessExeFileNameNPath, string CommandLinePrms, string WorkingDirectory) + { + bool bCanAddSuccess = false; + if (!String.IsNullOrEmpty(ProcessExeFileNameNPath)) + { + if (CommandLinePrms == null) + CommandLinePrms = String.Empty; + if (WorkingDirectory == null) + WorkingDirectory = String.Empty; + + ProcessExe processToAdd = new ProcessExe(Yaulw.Win32.Functions.GetLongFileNameNPathOrPath(ProcessExeFileNameNPath.Trim()), CommandLinePrms.Trim(), WorkingDirectory.Trim()); + ProcessExe pFound = null; + if (ProcessExeFileExistsLocally(processToAdd) && IsNotExcludedProcessExe(processToAdd) && IsUniqueProcessExe(processToAdd, out pFound)) + { + bCanAddSuccess = true; + } + else if(pFound != null) + { + // if Working Directory isn't the same, we allow updating it via AddProcessExe Call + bCanAddSuccess = (String.Compare(pFound.WorkingDirectory, processToAdd.WorkingDirectory, true) != 0); + } + } + return bCanAddSuccess; + } + + #endregion + + #region Public Remove Functions + + /// + /// Helper function to Remove a Process Exe + /// + /// specify a fileName and path for a process + /// specify a command-line params to use for the process + /// specify the working directory to use for the process + /// true if successfull, false otherwise + public bool RemoveProcessExe(string ProcessExeFileNameNPath, string CommandLinePrms) + { + if (!String.IsNullOrEmpty(ProcessExeFileNameNPath)) + { + if (CommandLinePrms == null) + CommandLinePrms = String.Empty; + + int nIndex = -1; + for (int i = 0; i < m_ArrayList.Count; ++i) + { + ProcessExe _cmpProcessExe = (ProcessExe)m_ArrayList[i]; + if (_cmpProcessExe.FoundMatch(ProcessExeFileNameNPath.Trim(), CommandLinePrms.Trim())) + { + nIndex = i; + break; + } + } + + if (nIndex != -1) + { + m_ArrayList.RemoveAt(nIndex); + return true; + } + } + return false; + } + + #endregion + + #region Public Clear N' Query Functions + + /// + /// Clears all ProcessExes from the List + /// + public void Clear() { m_ArrayList.Clear(); } + + /// + /// Clears each Process's Start and Fail Time + /// + public void ClearProcessExeStartNFailTimes() { foreach (ProcessExe p in ProcessExes) p.ClearStartNFailTimes(); } + + /// + /// Check to see if there are any Start or Fail Times, + /// if there aren't. Consider this configuration as newly loaded. + /// + /// true, if there are any Start or Fail Times recorded, false otherwise + public bool HasAnyProcessExeStartNFailTimes() { foreach (ProcessExe p in ProcessExes) { if (p.LastFailedTimes.Count > 0) return true; } return false; } + + /// + /// Retrieve the Process Exe that matches the specified PID + /// + public ProcessExe ClearPidInformation() { foreach (ProcessExe p in ProcessExes) { p.PID = 0; } return null; } + + /// + /// Check to see if any of the Processes Configured has incomplete PID information, meaning that it never got started, + /// or failed to start, or was never mapped + /// + /// true, if any PID info is incomplete + public bool HasIncompletePidInformation() { foreach (ProcessExe p in ProcessExes) { if (p.PID <= 0) return true; } return false; } + + /// + /// Retrieve a list of all Pids that have been mapped. + /// Make sure to call HasIncompletePidInformation() prior calling this if you want to make sure all pid are returned. + /// * Function only returns valid pids * + /// + /// if set to true, includes Pids in an error state, false otherwise * should always use false * + /// an array of pids mapped + //public uint[] QueryAllPidInfo(bool bIncludeErrorStatePids = false) { List pids = new List(); foreach (ProcessExe p in ProcessExes) { if (p.PID > 0) { if (bIncludeErrorStatePids && p.InErrorState) { pids.Add(p.PID); } else if (!bIncludeErrorStatePids && !p.InErrorState) { pids.Add(p.PID); } } } return pids.ToArray(); } + public uint[] QueryAllPidInfo(bool bIncludeErrorStatePids, bool bIncludeNonRestartPids) + { + List pids = new List(); + foreach (ProcessExe p in ProcessExes) + { + if (p.PID > 0) + { + // bError = true && bInclude = true (include all) + // bError = false && bInclude = true (include all that don't have an error) + // bError = true && bInclude = false (include all that aren't restart) + // bError = false && bInclude = false (include none that have either) + if (bIncludeErrorStatePids && bIncludeNonRestartPids) + { + pids.Add(p.PID); + } + else if (!bIncludeErrorStatePids && bIncludeNonRestartPids) + { + if (!p.InErrorState) + pids.Add(p.PID); + } + else if (bIncludeErrorStatePids && !bIncludeNonRestartPids) + { + if (p.AllowRestart) + pids.Add(p.PID); + } + else if (!bIncludeErrorStatePids && !bIncludeNonRestartPids) + { + if (!p.InErrorState && p.AllowRestart) + pids.Add(p.PID); + } + } + } + return pids.ToArray(); + } + + /// + /// Retrieve the number of Processes now considered in an error state. + /// + /// Number of Pids in an error state + public uint NumberOfPidsInErrorState() { uint nErrorCount = 0; foreach (ProcessExe p in ProcessExes) { if (p.InErrorState) nErrorCount++; } return nErrorCount; } + + /// + /// Retrieve the Process Exe that matches the specified PID + /// + /// PID to look four + /// ProcessExe that matches the PID, null otherwise (if not found) + public ProcessExe GetProcessExeForPid(uint PID) { foreach (ProcessExe p in ProcessExes) { if (p.PID == PID) return p; } return null; } + + #endregion + + #region Private Helper Functions + + /// + /// Enforce Uniqueness, when adding Process Exe's to the DS + /// + /// a ProcessExe to check + /// the found ProcessExe that matches the check + /// true if unique, false otherwise + private bool IsUniqueProcessExe(ProcessExe processExe, out ProcessExe foundP) + { + foundP = null; + foreach (ProcessExe _cmpProcessExe in m_ArrayList) + { + // If ProcessExe FileName and Path, as well as CommandLine Prms Match, then it is NOT unique + if (_cmpProcessExe.FoundMatch(processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms)) + { + foundP = _cmpProcessExe; + return false; + } + } + return true; + } + + /// + /// Enforce that the called didn't specify to exclude this Process + /// + /// a ProcessExe to check + /// true if not exclusive, false otherwise + private bool IsNotExcludedProcessExe(ProcessExe processExe) + { + // * Allow caller via the Statically SPecified Exclusion list to filter out processes + // that are considered invalid * + if (MonitorDataStore.MonitoredProcessNamesExclusionList != null && + MonitorDataStore.MonitoredProcessNamesExclusionList.Count > 0) + { + foreach (string ProcessName in MonitorDataStore.MonitoredProcessNamesExclusionList) + { + if (String.Compare(ProcessName, Path.GetFileNameWithoutExtension(processExe.ProcessExeFileNameNPath), true) == 0) + return false; + } + } + return true; + } + + /// + /// Enforce that the Process/Exe File must exist on the system, prior being added + /// + /// true if exists, false otherwise + private bool ProcessExeFileExistsLocally(ProcessExe processExe) + { + return System.IO.File.Exists(processExe.ProcessExeFileNameNPath); + } + + #endregion + + #region IComparable Members + + /// + /// Compares tow MonitoredProcessesW Objects with each other + /// + /// Pass in a valid MonitoredProcessesW Object + /// 0 if equal, -1 if obj is smaller, 1 if obj is bigger + public int CompareTo(object obj) + { + if (obj is MonitoredProcessesW) + { + MonitoredProcessesW w = (MonitoredProcessesW)obj; + + // First, do a simple count comparison + int wListCount = w.m_ArrayList.Count; + int thisListCount = this.m_ArrayList.Count; + if (wListCount == 0 && thisListCount != 0) + return -1; + else if (wListCount == 0 && thisListCount == 0) + return 0; + else if (wListCount != 0 && thisListCount == 0) + return 1; + else if (wListCount < thisListCount) + return -1; + else if (wListCount > thisListCount) + return 1; + + // The count must be the same * Hence, now we must actually + // compare each member to make sure that the lists are the same * + // ~Before we do this we should sort both lists, so that we can correctly compare + ArrayList List1 = (ArrayList)w.m_ArrayList.Clone(); + ArrayList List2 = (ArrayList)this.m_ArrayList.Clone(); + List1.Sort(); + List2.Sort(); + + // ~Iterate this Object's array list and compare each member + int nCompare = 0; + for (int i = 0; i < thisListCount; ++i) + { + ProcessExe p1 = (ProcessExe)List1[i]; + ProcessExe p2 = (ProcessExe)List2[i]; + nCompare = p1.CompareTo(p2); + if (nCompare != 0) + break; + } + + // If they are both still exactly equal, then let's + // now also compare the order + if (nCompare == 0) + { + for (int i = 0; i < w.m_ArrayList.Count; ++i) + { + ProcessExe p1 = (ProcessExe) w.m_ArrayList[i]; + ProcessExe p2 = (ProcessExe) this.m_ArrayList[i]; + nCompare = p1.CompareTo(p2); + if (nCompare != 0) + break; + } + } + + return nCompare; + } + else + { + throw new ArgumentException("object is not a MonitoredProcessesW"); + } + } + + #endregion + + #region ICloneable Members + + /// + /// Performs a Shallow Copy of MonitoredProcessesW + /// + /// a new MonitoredProcessesW Object + public object Clone() + { + MonitoredProcessesW w = new MonitoredProcessesW(); + w.m_ArrayList = (ArrayList)this.m_ArrayList.Clone(); + return w; + } + + #endregion + } + + /// + /// Process Exe (Image) Class that contains all settings to monitor + /// a process / executable image + /// + public class ProcessExe : IComparable, ICloneable + { + #region Public Properties + + /// + /// specify a fileName and path for a process + /// + [XmlText] + public string ProcessExeFileNameNPath { get { return _ProcessExeFileNameNPath; } set { if (!String.IsNullOrEmpty(value)) _ProcessExeFileNameNPath = value; } } + private string _ProcessExeFileNameNPath = ""; + + /// + /// specify a command-line params to use for the process + /// + [XmlAttribute("CommandLinePrms")] + public string CommandLinePrms { get { if (String.IsNullOrEmpty(_CommandLinePrms)) return ""; else return _CommandLinePrms; } set { if (!String.IsNullOrEmpty(value)) _CommandLinePrms = value; } } + private string _CommandLinePrms = ""; + + + /// + /// specify the working directory to use for the process + /// + [XmlAttribute("WorkingDirectory")] + public string WorkingDirectory { get { if(String.IsNullOrEmpty(_WorkingDirectory)) return ""; else return _WorkingDirectory; } set { if (!String.IsNullOrEmpty(value)) _WorkingDirectory = value; } } + private string _WorkingDirectory; + + /// + /// specify if process is allowed to be restarted + /// + [XmlAttribute("AllowRestart")] + public bool AllowRestart { get { return _AllowRestart; } set { _AllowRestart = value; } } + private bool _AllowRestart = true; + + #endregion + + #region Construction + + /// + /// Default Constructor * Don't use this * used only by serialization + /// + public ProcessExe() + { + this.PID = 0; + this.InErrorState = false; + } + + /// + /// Main Constructor * Use this * + /// + /// specify a fileName and path for a process + /// specify a command-line params to use for the process + /// specify the working directory to use for the process + public ProcessExe(string ProcessExeFileNameNPath, string CommandLinePrms, string WorkingDirectory) + { + if(!String.IsNullOrEmpty(ProcessExeFileNameNPath)) + this.ProcessExeFileNameNPath = ProcessExeFileNameNPath.Trim(); + if(!String.IsNullOrEmpty(CommandLinePrms)) + this.CommandLinePrms = CommandLinePrms.Trim(); + if(!String.IsNullOrEmpty(WorkingDirectory)) + this.WorkingDirectory = WorkingDirectory.Trim(); + this.PID = 0; + this.InErrorState = false; + } + + #endregion + + #region Internal Helper Functions N Properties + + /// + /// The name of the Process + /// + internal string ProcessName + { + get + { + if (!String.IsNullOrEmpty(this.ProcessExeFileNameNPath)) + { + string ProcessName = Path.GetFileNameWithoutExtension(this.ProcessExeFileNameNPath); + return ProcessName.ToLower(); + } + return String.Empty; + } + } + + /// + /// Clears all Start N' Fail Times for a process + /// + internal void ClearStartNFailTimes() + { + LastStartedTimes.Clear(); + LastStartedTimes.Clear(); + } + + /// + /// Keep Track if this Process is in an Error State + /// + internal bool InErrorState; + + /// + /// Internal Variable to Keep track of PID + /// + internal uint PID; + + /// + /// Keep track of all Last Failed DT Stamps + /// + internal readonly List LastFailedTimes = new List(); + + /// + /// Keep track of all Last Started DT Stamps + /// + internal readonly List LastStartedTimes = new List(); + + /// + /// Returns the Last Failed Stamp + /// + internal DateTime LastFailTime + { + get + { + if (LastFailedTimes.Count > 0) + return LastFailedTimes.Last(); + else + return DateTime.MinValue; + } + } + + /// + /// Allow specific amount of time to have passed since Last-Fail + /// + /// timespan that has to have occured since Last-Fail Time + /// true if the Last-Fail Exceeded the specified amount of time, false otherwise + internal bool LastFailTimeTimeoutExceeded(TimeSpan ts) + { + if (LastFailedTimes.Count > 0) + { + TimeSpan diff = DateTime.Now - LastFailTime; + if (diff < ts) + return false; + } + return true; + } + + /// + /// Returns the Last Started DT Stamp + /// + internal DateTime LastStartTime + { + get + { + if (LastStartedTimes.Count > 0) + return LastStartedTimes.Last(); + else + return DateTime.MinValue; + } + } + + /// + /// Allow specific amount of time to have passed since Last-Start + /// + /// timespan that has to have occured since Last-Start Time + /// true if the Last-Start Exceeded the specified amount of time, false otherwise + internal bool LastStartTimeTimeoutExceeded(TimeSpan ts) + { + if (LastStartedTimes.Count > 0) + { + TimeSpan diff = DateTime.Now - LastStartTime; + if (diff < ts) + return false; + } + return true; + } + + /// + /// Add a FailTime to keep track of for the given process + /// + /// Fail-Time + /// max capacity to store + //internal void AddFailTime(DateTime dtFail, int nMaxCapacity = 1) + internal void AddFailTime(DateTime dtFail, int nMaxCapacity) + { + if (dtFail != DateTime.MinValue) + { + // Make sure to always at least store one + if (nMaxCapacity < 1) + nMaxCapacity = 1; + + // Add and adjust according to capacity + LastFailedTimes.Add(dtFail); + if (LastFailedTimes.Count > nMaxCapacity) + { + int nRemoveCount = nMaxCapacity - LastFailedTimes.Count; + for (int i = 0; i < nRemoveCount; ++i) + LastFailedTimes.RemoveAt(i); + } + } + } + + /// + /// Add a StartTime to keep track of for the given process + /// + /// Start-Time + /// max capacity to store + //internal void AddStartTime(DateTime dtStart, int nMaxCapacity = 1) + internal void AddStartTime(DateTime dtStart, int nMaxCapacity) + { + if (dtStart != DateTime.MinValue) + { + // Make sure to always at least store one + if (nMaxCapacity < 1) + nMaxCapacity = 1; + + // Add and adjust according to capacity + LastStartedTimes.Add(dtStart); + if (LastStartedTimes.Count > nMaxCapacity) + { + int nRemoveCount = nMaxCapacity - LastStartedTimes.Count; + for (int i = 0; i < nRemoveCount; ++i) + LastStartedTimes.RemoveAt(i); + } + } + } + + /// + /// Retrieves the Number of times the process failed in the given timespan + /// + /// TimeSpan to subtract from DateTime.Now + /// number of times this Process Failed in the given timespan + internal int GetFailTimesInGivenTimeSpan(TimeSpan span) + { + if (LastFailedTimes.Count == 0) + return 0; + + int nCount = 0; + DateTime dtMin = DateTime.Now - span; + foreach (DateTime dt in LastFailedTimes) + { + if (dt >= dtMin) + ++nCount; + } + return nCount; + } + + /// + /// Checks to see if the Number of times the process failed in the given timespan + /// exceeds nMaxTry + /// + /// number of max times it is allowed to fail + /// TimeSpan to subtract from DateTime.Now + /// true if number of times in the given timespan exceeds nMaxTry, false otherwise + internal bool GetFailTimesInGivenTimeSpanMaxTryExceeded(TimeSpan span, uint nMaxTry) + { + if (LastFailedTimes.Count == 0) + return false; + + int nCount = GetFailTimesInGivenTimeSpan(span); + return (nCount > nMaxTry); + } + + /// + /// Retrieves the Number of times the process Started in the given timespan + /// + /// TimeSpan to subtract from DateTime.Now + /// number of times this Process Started in the given timespan + internal int GetStartTimesInGivenTimeSpan(TimeSpan span) + { + if (LastStartedTimes.Count == 0) + return 0; + + int nCount = 0; + DateTime dtMin = DateTime.Now - span; + foreach (DateTime dt in LastStartedTimes) + { + if (dt >= dtMin) + ++nCount; + } + return nCount; + } + + /// + /// Checks to see if the Number of times the process started in the given timespan + /// exceeds nMaxTry + /// + /// number of max times it is allowed to have started + /// TimeSpan to subtract from DateTime.Now + /// true if number of times in the given timespan exceeds nMaxTry, false otherwise + internal bool GetStartTimesInGivenTimeSpanMaxTryExceeded(TimeSpan span, uint nMaxTry) + { + if (LastStartedTimes.Count == 0) + return false; + + int nCount = GetStartTimesInGivenTimeSpan(span); + return (nCount > nMaxTry); + } + + /// + /// Returns true if the passed in ProcessExeFileNameNPath, CommandLinePrms + /// matches this object's members * NOT THE SAME AS IComparable * + /// IComparable does a Full Compare, whereas this only Compares ProcessExeFileNameNPath and CommandLinePrms + /// ~IComparable also includes WorkingDirectory + /// + internal bool FoundMatch(string ProcessExeFileNameNPath, string CommandLinePrms) + { + if ((String.Compare(this.ProcessExeFileNameNPath.Trim(), ProcessExeFileNameNPath.Trim(), true) == 0) && + (String.Compare(this.CommandLinePrms.Trim(), CommandLinePrms.Trim(), true) == 0)) + { + return true; + } + return false; + } + + #endregion + + #region IComparable Members + + /// + /// Compare the ProcessExe + /// + /// a valid ProcessExe Obj + /// 0 if equal, -1 if obj is smaller, +1 if obj is bigger + public int CompareTo(object obj) + { + if (obj is ProcessExe) + { + ProcessExe p = (ProcessExe)obj; + int nCompare = String.Compare(p.ProcessExeFileNameNPath.Trim(), this.ProcessExeFileNameNPath.Trim(), true); + if (nCompare == 0) + nCompare = String.Compare(p.CommandLinePrms.Trim(), this.CommandLinePrms.Trim(), true); + if (nCompare == 0) + nCompare = String.Compare(p.WorkingDirectory.Trim(), this.WorkingDirectory.Trim(), true); + if (nCompare == 0) + nCompare = this.AllowRestart.CompareTo(p.AllowRestart); + return nCompare; + } + else + { + throw new ArgumentException("object is not a ProcessExe"); + } + } + + #endregion + + #region ICloneable Members + + /// + /// Clones the ProcessExe Object + /// + /// a new ProcessExe Object + public object Clone() + { + ProcessExe process = new ProcessExe(this.ProcessExeFileNameNPath, this.CommandLinePrms, this.WorkingDirectory); + process.AllowRestart = this.AllowRestart; + return process; + } + + #endregion + } + + /// + /// MonitoredServicesW - (Wrapper) around ServiceExe ArrayList to work with XML + /// + public class MonitoredServicesW : IComparable, ICloneable + { + #region Private Members + + private ArrayList m_ArrayList; + + #endregion + + #region Construction + + /// + /// Constructor for MonitoredServicesW + /// + public MonitoredServicesW() + { + m_ArrayList = new ArrayList(); + } + + #endregion + + #region XML Service Element + + /// + /// Property useful to directly Get/Set the ServiceExes as an Array + /// + [XmlElement("Service")] + public ServiceExe[] ServiceExes + { + get + { + ServiceExe[] serviceExes = new ServiceExe[m_ArrayList.Count]; + m_ArrayList.CopyTo(serviceExes); + return serviceExes; + } + set + { + if (value == null) return; + ServiceExe[] serviceExes = (ServiceExe[])value; + m_ArrayList.Clear(); + foreach (ServiceExe serviceExe in serviceExes) + AddServiceExe(serviceExe); + } + } + + #endregion + + #region Public Get Function + + /// + /// Use this to get the ServiceExe Object for the Specified Service Name + /// + /// Name of Service To look for + /// the Service Exe Object or null, if not found + public ServiceExe GetServiceExeByServiceName(string Name) + { + if (String.IsNullOrEmpty(Name)) + { + foreach (ServiceExe _cmpServiceExe in m_ArrayList) + { + if (String.Compare(_cmpServiceExe.Name, Name, true) == 0) + return _cmpServiceExe; + } + } + return null; + } + + #endregion + + #region Public Add Functions + + /// + /// Helper function to Add a Service Exe + /// + /// a valid ServiceExe Object + /// true if successfull, false otherwise + public bool AddServiceExe(ServiceExe serviceExe) + { + bool bAddSuccess = false; + if (serviceExe != null && !String.IsNullOrEmpty(serviceExe.Name)) + { + if (ServiceExistsLocally(serviceExe) && IsUniqueServiceExe(serviceExe)) + bAddSuccess = (m_ArrayList.Add(serviceExe) >= 0); + } + return bAddSuccess; + } + + /// + /// Helper function to Add a Service Exe + /// + /// Name of Service + /// true if successfull, false otherwise + public bool AddServiceExe(string Name) + { + bool bAddSuccess = false; + if (!String.IsNullOrEmpty(Name)) + { + ServiceExe serviceToAdd = new ServiceExe(Name.Trim()); + if (ServiceExistsLocally(serviceToAdd) && IsUniqueServiceExe(serviceToAdd)) + bAddSuccess = (m_ArrayList.Add(serviceToAdd) >= 0); + } + return bAddSuccess; + } + + #endregion + + #region Public Remove Function + + /// + /// Helper function to Remove a Service Exe + /// + /// Name of Service + /// true if successfull, false otherwise + public bool RemoveServiceExe(string Name) + { + if (!String.IsNullOrEmpty(Name)) + { + int nIndex = -1; + for (int i = 0; i < m_ArrayList.Count; ++i) + { + ServiceExe _cmpServiceExe = (ServiceExe)m_ArrayList[i]; + if (_cmpServiceExe.FoundMatch(Name)) + { + nIndex = i; + break; + } + } + + if (nIndex != -1) + { + m_ArrayList.RemoveAt(nIndex); + return true; + } + } + return false; + } + + #endregion + + #region Public Clear N' Query Functions + + /// + /// Clears all ServiceExes from the List + /// + public void Clear() { m_ArrayList.Clear(); } + + /// + /// Clears each Services's Start and Fail Time + /// + public void ClearServiceExeStartNFailTimes() { foreach (ServiceExe s in ServiceExes) s.ClearStartNFailTimes(); } + + /// + /// Check to see if there are any Start or Fail Times, + /// if there aren't. Consider this configuration as newly loaded. + /// + /// true, if there are any Start or Fail Times recorded, false otherwise + public bool HasAnyServiceExeStartNFailTimes() { foreach (ServiceExe s in ServiceExes) { if (s.LastFailedTimes.Count > 0) return true; } return false; } + + /// + /// Retrieve a list of all Services that have been mapped + /// + /// if set to true, includes Services in an error state, false otherwise * should always use false * + /// if set true, includes Services that are marked as NonRestart + /// + public string[] QueryAllServiceInfo(bool bIncludeErrorStateServices, bool bIncludeNonRestartServices) + { + List services = new List(); + foreach (ServiceExe s in ServiceExes) + { + if (!String.IsNullOrEmpty(s.Name)) + { + // bError = true && bInclude = true (include all) + // bError = false && bInclude = true (include all that don't have an error) + // bError = true && bInclude = false (include all that aren't restart) + // bError = false && bInclude = false (include none that have either) + if (bIncludeErrorStateServices && bIncludeNonRestartServices) + { + services.Add(s.Name); + } + else if (!bIncludeErrorStateServices && bIncludeNonRestartServices) + { + if (!s.InErrorState) + services.Add(s.Name); + } + else if (bIncludeErrorStateServices && !bIncludeNonRestartServices) + { + if (s.AllowRestart) + services.Add(s.Name); + } + else if (!bIncludeErrorStateServices && !bIncludeNonRestartServices) + { + if (!s.InErrorState && s.AllowRestart) + services.Add(s.Name); + } + } + } + return services.ToArray(); + } + #endregion + + #region Private Helper Functions + + /// + /// Enforce Uniqueness, when adding Service Exe's to the DS + /// + /// a valid ServiceExe Object + /// true if unique, false otherwise + private bool IsUniqueServiceExe(ServiceExe serviceExe) + { + foreach (ServiceExe _cmpServiceExe in m_ArrayList) + { + // If ServiceExe Name Matches, then it is NOT unique + if (_cmpServiceExe.FoundMatch(serviceExe.Name)) + return false; + } + return true; + } + + /// + /// Enforce that the Service must exist on the system, prior being added + /// + /// a valid ServiceExe Object + /// true if exists, false otherwise + private bool ServiceExistsLocally(ServiceExe serviceExe) + { + bool bExists = ServiceW.DoesServiceExist(serviceExe.Name); + return bExists; + } + + #endregion + + #region IComparable Members + + /// + /// Compares two MonitoredServicesW Objects + /// + /// a valid MonitoredServicesW object + /// 0 if equal, -1 if obj is less, +1 if obj is more + public int CompareTo(object obj) + { + if (obj is MonitoredServicesW) + { + MonitoredServicesW sw = (MonitoredServicesW)obj; + + // First, do a simple count comparison + int wListCount = sw.m_ArrayList.Count; + int thisListCount = this.m_ArrayList.Count; + + if (wListCount == 0 && thisListCount != 0) + return -1; + else if (wListCount == 0 && thisListCount == 0) + return 0; + else if (wListCount != 0 && thisListCount == 0) + return 1; + else if (wListCount < thisListCount) + return -1; + else if (wListCount > thisListCount) + return 1; + + // The count must be the same * Hence, now we must actually + // compare each member to make sure that the lists are the same * + // ~but before we do that, we must sort the List first + ArrayList List1 = (ArrayList) sw.m_ArrayList.Clone(); + ArrayList List2 = (ArrayList) this.m_ArrayList.Clone(); + List1.Sort(); + List2.Sort(); + + // ~Iterate this Object's array list and compare each member + int nCompare = 0; + for (int i = 0; i < thisListCount; ++i) + { + ServiceExe s1 = (ServiceExe)List1[i]; + ServiceExe s2 = (ServiceExe)List2[i]; + nCompare = s1.CompareTo(s2); + if (nCompare != 0) + break; + } + + // If they are both still exactly equal, then let's + // now also compare the order + if (nCompare == 0) + { + for (int i = 0; i < sw.m_ArrayList.Count; ++i) + { + ServiceExe s1 = (ServiceExe)sw.m_ArrayList[i]; + ServiceExe s2 = (ServiceExe)this.m_ArrayList[i]; + nCompare = s1.CompareTo(s2); + if (nCompare != 0) + break; + } + } + return nCompare; + } + else + { + throw new ArgumentException("object is not a MonitoredServicesW"); + } + } + + #endregion + + #region ICloneable Members + + /// + /// Creates a shallow copy of MonitoredServicesW Obj + /// + /// a new MonitoredServicesW Obj + public object Clone() + { + MonitoredServicesW sw = new MonitoredServicesW(); + sw.m_ArrayList = (ArrayList)this.m_ArrayList.Clone(); + return sw; + } + + #endregion + } + + /// + /// Service Name/Path Class that contains all settings to monitor a service + /// + public class ServiceExe : IComparable, ICloneable + { + #region Public Properties + + /// + /// specify a Service Name + /// + [XmlText] + public string Name { get { return _Name; } set { if(!String.IsNullOrEmpty(value)) _Name = value; } } + private string _Name; + + /// + /// specify if service is allowed to be restarted + /// + [XmlAttribute("AllowRestart")] + public bool AllowRestart { get { return _AllowRestart; } set { _AllowRestart = value; } } + private bool _AllowRestart = true; + + #endregion + + #region Construction + + /// + /// Default Constructor * Don't use this * used only by serialization + /// + public ServiceExe() + { + this.InErrorState = false; + } + + /// + /// Main Constructor * Use this * + /// + /// specify the service Name + public ServiceExe(string Name) + { + if (!String.IsNullOrEmpty(Name)) + this.Name = Name.Trim(); + this.InErrorState = false; + } + + #endregion + + #region Internal Helper Functions + + /// + /// Returns true if the passed in Name matches this object's members *same as IComparable, + /// but func doesn't need creation of an object* + /// + /// specify the service Name + /// true if matched, false otherwise + internal bool FoundMatch(string Name) + { + if (Name != null) + { + if (String.Compare(this.Name.Trim(), Name.Trim(), true) == 0) + return true; + } + return false; + } + + /// + /// Clears all Start N' Fail Times for a process + /// + internal void ClearStartNFailTimes() + { + LastStartedTimes.Clear(); + LastStartedTimes.Clear(); + } + + /// + /// Internal Variable to keep track if this Process is in an Error State + /// + internal bool InErrorState; + + /// + /// Keep track of all Last Failed DT Stamps + /// + internal readonly List LastFailedTimes = new List(); + + /// + /// Keep track of all Last Started DT Stamps + /// + internal readonly List LastStartedTimes = new List(); + + /// + /// Last Failed DT Stamp + /// + internal DateTime LastFailTime + { + get + { + if (LastFailedTimes.Count > 0) + return LastFailedTimes.Last(); + else + return DateTime.MinValue; + } + } + + /// + /// Allow specific amount of time to have passed since Last-Fail + /// + /// timespan that has to have occured since Last-Fail Time + /// true if the Last-Fail Exceeded the specified amount of time, false otherwise + internal bool LastFailTimeTimeoutExceeded(TimeSpan ts) + { + if (LastFailedTimes.Count > 0) + { + TimeSpan diff = DateTime.Now - LastFailTime; + if (diff < ts) + return false; + } + return true; + } + + /// + /// Checks to see if the Number of times the process failed in the given timespan + /// exceeds nMaxTry + /// + /// number of max times it is allowed to fail + /// TimeSpan to subtract from DateTime.Now + /// true if number of times in the given timespan exceeds nMaxTry, false otherwise + internal bool GetFailTimesInGivenTimeSpanMaxTryExceeded(TimeSpan span, uint nMaxTry) + { + if (LastFailedTimes.Count == 0) + return false; + + int nCount = GetFailTimesInGivenTimeSpan(span); + return (nCount > nMaxTry); + } + + /// + /// Last Started DT Stamp + /// + internal DateTime LastStartTime + { + get + { + if (LastStartedTimes.Count > 0) + return LastStartedTimes.Last(); + else + return DateTime.MinValue; + } + } + + /// + /// Allow specific amount of time to have passed since Last-Start + /// + /// timespan that has to have occured since Last-Start Time + /// true if the Last-Start Exceeded the specified amount of time, false otherwise + internal bool LastStartTimeTimeoutExceeded(TimeSpan ts) + { + if (LastStartedTimes.Count > 0) + { + TimeSpan diff = DateTime.Now - LastStartTime; + if (diff < ts) + return false; + } + return true; + } + + /// + /// Checks to see if the Number of times the process started in the given timespan + /// exceeds nMaxTry + /// + /// number of max times it is allowed to have started + /// TimeSpan to subtract from DateTime.Now + /// true if number of times in the given timespan exceeds nMaxTry, false otherwise + internal bool GetStartTimesInGivenTimeSpanMaxTryExceeded(TimeSpan span, uint nMaxTry) + { + if (LastStartedTimes.Count == 0) + return false; + + int nCount = GetStartTimesInGivenTimeSpan(span); + return (nCount > nMaxTry); + } + + /// + /// Add a FailTime to keep track of for the given Service + /// + /// Fail-Time + /// max capacity to store + //internal void AddFailTime(DateTime dtFail, int nMaxCapacity = 1) + internal void AddFailTime(DateTime dtFail, int nMaxCapacity) + { + if (dtFail != DateTime.MinValue) + { + // Make sure to always at least store one + if (nMaxCapacity < 1) + nMaxCapacity = 1; + + // Add and adjust according to capacity + LastFailedTimes.Add(dtFail); + if (LastFailedTimes.Count > nMaxCapacity) + { + int nRemoveCount = nMaxCapacity - LastFailedTimes.Count; + for (int i = 0; i < nRemoveCount; ++i) + LastFailedTimes.RemoveAt(i); + } + } + } + + /// + /// Add a StartTime to keep track of for the given Service + /// + /// Start-Time + /// max capacity to store + //internal void AddStartTime(DateTime dtStart, int nMaxCapacity = 1) + internal void AddStartTime(DateTime dtStart, int nMaxCapacity) + { + if (dtStart != DateTime.MinValue) + { + // Make sure to always at least store one + if (nMaxCapacity < 1) + nMaxCapacity = 1; + + // Add and adjust according to capacity + LastStartedTimes.Add(dtStart); + if (LastStartedTimes.Count > nMaxCapacity) + { + int nRemoveCount = nMaxCapacity - LastStartedTimes.Count; + for (int i = 0; i < nRemoveCount; ++i) + LastStartedTimes.RemoveAt(i); + } + } + } + + /// + /// Retrieves the Number of times the process failed / Started in the given timespan + /// + /// TimeSpan to subtract from DateTime.Now + /// number of times this Process Failed in the given timespan + internal int GetFailTimesInGivenTimeSpan(TimeSpan span) + { + if (LastFailedTimes.Count == 0) + return 0; + + int nCount = 0; + DateTime dtMin = DateTime.Now - span; + foreach (DateTime dt in LastFailedTimes) + { + if (dt >= dtMin) + ++nCount; + } + return nCount; + } + + /// + /// Retrieves the Number of times the process Started in the given timespan + /// + /// TimeSpan to subtract from DateTime.Now + /// number of times this Process Started in the given timespan + internal int GetStartTimesInGivenTimeSpan(TimeSpan span) + { + if (LastStartedTimes.Count == 0) + return 0; + + int nCount = 0; + DateTime dtMin = DateTime.Now - span; + foreach (DateTime dt in LastStartedTimes) + { + if (dt >= dtMin) + ++nCount; + } + return nCount; + } + + #endregion + + #region IComparable Members + + /// + /// Compare the ServiceExe + /// + /// a valid ServiceExe Obj + /// 0 if equal, -1 if obj is smaller, +1 if obj is bigger + public int CompareTo(object obj) + { + if (obj is ServiceExe) + { + ServiceExe s = (ServiceExe)obj; + int nCompare = String.Compare(s.Name.Trim(), this.Name.Trim(), true); + if (nCompare == 0) + { + nCompare = this.AllowRestart.CompareTo(s.AllowRestart); + } + return nCompare; + } + else + { + throw new ArgumentException("object is not a ServiceExe"); + } + } + + #endregion + + #region ICloneable Members + + /// + /// Clones the ServiceExe Object + /// + /// a new ServiceExe Object + public object Clone() + { + ServiceExe service = new ServiceExe(this.Name); + service.AllowRestart = this.AllowRestart; + return service; + } + + #endregion + } + + #region ICloneable Members + + public object Clone() + { + MonitorConfiguration config = new MonitorConfiguration(); + config.MonitoredProcesses = (MonitoredProcessesW) this.MonitoredProcesses.Clone(); + config.MonitoredServices = (MonitoredServicesW) this.MonitoredServices.Clone(); + return config; + } + + #endregion + } + + #endregion + + /// + /// Object is responsible for Loading/Saving the MonitorConfiguration to + /// and From XML File. + /// + public class MonitorDataStore + { + #region Private Members + + private MonitorConfiguration _RunTimeWatchdogXMLConfigurationData = null; + private XSerializer _xmlserializer = null; + private string _dsFileNameNPath = ""; + private DelegateCollection.Void_Param1_Exception_Func _exceptionHandler = null; + private bool _bForceRefreshOnNextRead = false; + private object _lock = new object(); + + #endregion + + #region internal statics + + internal static List MonitoredProcessNamesExclusionList = null; + + #endregion + + #region Constructor + + /// + /// Construct a MonitorDataStore Object, responsible for loading and + /// saving MonitorConfiguration from/to XML. + /// + /// a valid filename and path To load/store MonitorConfiguration + /// List of Process Names that will always be consider excluded * invalid * to add to configuration + /// Thrown if Empty dsFileNameNPath is passed in + //public MonitorDataStore(string dsFileNameNPath, List ExclusionListProcessNames = null, DelegateCollection.Void_Param1_Exception_Func exceptionHandler = null) + public MonitorDataStore(string dsFileNameNPath, List ExclusionListProcessNames, DelegateCollection.Void_Param1_Exception_Func exceptionHandler) + { + if (String.IsNullOrEmpty(dsFileNameNPath)) + throw new ArgumentException("Invalid dsFileNameNPath"); + + _dsFileNameNPath = dsFileNameNPath; + MonitoredProcessNamesExclusionList = ExclusionListProcessNames; + _exceptionHandler = exceptionHandler; + + // Initialize Serializer And Try to Read File configuration + _xmlserializer = new XSerializer(); + _RunTimeWatchdogXMLConfigurationData = _xmlserializer.ReadFromFile(_dsFileNameNPath); + } + + #endregion + + #region Public Static Accessor Methods + + /// + /// Use this function to force a Data Refresh the next time ReadData() is called + /// + public bool ForceRefreshOnNext_ReadData { get { return _bForceRefreshOnNextRead; } set { _bForceRefreshOnNextRead = value; } } + + /// + /// Read the XML Configuration Data (From a File in the System) or from Cache + /// + /// set to true to force a reload of configuration from the disk + /// Exception Handler Function, if an Exception is Thrown + /// an XMLDataStore object or Null if error occured + //public WatchdogConfiguration ReadData(bool bForceRefresh = false) + public MonitorConfiguration ReadData(bool bForceRefresh) + { + try + { + // Force Refresh on next Call? + if (_bForceRefreshOnNextRead) + { + bForceRefresh = true; + _bForceRefreshOnNextRead = false; + } + + // Load Configuration From the XML File + if (bForceRefresh || (_RunTimeWatchdogXMLConfigurationData == null)) + { + // XML could have been modified by the user, hence we it is unverified + MonitorConfiguration UnverifiedConfig = _xmlserializer.ReadFromFile(_dsFileNameNPath); + if(UnverifiedConfig != null) + { + // Make sure that all ProcessExes are Unique (ProcessExe and CommandLine and WorkingDirectory * together * are unique) + MonitorConfiguration VerifiedConfig = new MonitorConfiguration(); + + // calling AddProcessExe() will enforce uniqueness + foreach (MonitorConfiguration.ProcessExe p in UnverifiedConfig.MonitoredProcesses.ProcessExes) + VerifiedConfig.MonitoredProcesses.AddProcessExe(p); + + // calling AddServiceExe() will enforce uniqueness + foreach (MonitorConfiguration.ServiceExe s in UnverifiedConfig.MonitoredServices.ServiceExes) + VerifiedConfig.MonitoredServices.AddServiceExe(s); + + // always use the valid configuration + lock (_lock) + { + _RunTimeWatchdogXMLConfigurationData = VerifiedConfig; + } + + // save the verified configuration xml * potentially fixing any issue that were in it * + SaveData(); + } + } + } + catch (Exception e) { if (_exceptionHandler != null) _exceptionHandler(e); } + + // No File Exists, write out a blank file * and load a default * blank * configuration + // This function should NEVER, EVER pass out null + if (_RunTimeWatchdogXMLConfigurationData == null) + { + SaveData(null); + _RunTimeWatchdogXMLConfigurationData = _xmlserializer.ReadFromFile(_dsFileNameNPath); + } + return _RunTimeWatchdogXMLConfigurationData; + } + + /// + /// Writes the XML Configuration Data passed in out to Disk + /// + /// The XML Data To Write out + /// Exception Handler Function, if an Exception is Thrown + public void SaveData(MonitorConfiguration data) + { + try + { + _xmlserializer.WriteToFile(data, _dsFileNameNPath); + lock (_lock) + { + _RunTimeWatchdogXMLConfigurationData = data; + } + } + catch (Exception e) { if (_exceptionHandler != null) _exceptionHandler(e); } + } + + /// + /// Writes the XML Configuration Data that is currently stored in this Object out to Disk + /// + /// Exception Handler Function, if an Exception is Thrown + public void SaveData() + { + try + { + _xmlserializer.WriteToFile(_RunTimeWatchdogXMLConfigurationData, _dsFileNameNPath); + } + catch (Exception e) { if (_exceptionHandler != null) _exceptionHandler(e); } + } + + #endregion + } +} diff --git a/Net/Emailer.cs b/Net/Emailer.cs new file mode 100644 index 0000000..f7dc564 --- /dev/null +++ b/Net/Emailer.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net.Mail; +using System.Net; +using webmail = System.Web.Mail; +using Yaulw.Other; + +// Disable System.Web.Mail is depreciated Warning +#pragma warning disable 0618 + +namespace Yaulw.Net +{ + /// + /// Emailer Helps you send Emails using System.Net or System.Web.Mail. + /// System.Web.Mail is depreciated, but currently is the only way to send emails via + /// SSH/SMTP on port 465. + /// + public static class Emailer + { + #region Public Statics + + /// + /// Generic Function to handle Emails + /// + /// specifiy a SMTP Server to use * Required * + /// Email to use to send with * Required * + /// Display name for Sender * Required * + /// ';' seperated list of emails to send to * Required * + /// Email Message Subject * Required * + /// Email Message Text * Required * + /// Smtp Port to use + /// ';' seperated list of emails to cc to + /// true to use SmtpAuthentication, false otherwise + /// Smtp Authentication Username + /// Smtp Authentication Password + /// true to use ssl, false otherwise + /// Defaults to 100 Seconds + /// If an exception occurs this delegate will get called (can be null) + /// true if the Message Send Successfully, false otherwise + public static bool SendEmail(string SmtpServer, string SenderEmail, string SenderDisplayName, string ReceiverEmails, string MessageSubject, string MessageText, uint nPort, string ReceiverCCEmails, bool bUseSmtpAuth, string SmtpUser, string SmtpPassword, bool RequiresSSL, int nTimeOutInSeconds, DelegateCollection.Void_Param1_Exception_Func exceptionFunc) + { + // SSL on Port 25 is Explicit SSL, which is supported by System.Net + if (RequiresSSL && (nPort != 25)) + return SendEmailUsingWebMail(SmtpServer, SenderEmail, SenderDisplayName, ReceiverEmails, MessageSubject, MessageText, nPort, ReceiverCCEmails, bUseSmtpAuth, SmtpUser, SmtpPassword, RequiresSSL, nTimeOutInSeconds, exceptionFunc); + else + return SendEmailUsingNetMail(SmtpServer, SenderEmail, SenderDisplayName, ReceiverEmails, MessageSubject, MessageText, nPort, ReceiverCCEmails, bUseSmtpAuth, SmtpUser, SmtpPassword, RequiresSSL, nTimeOutInSeconds, exceptionFunc); + } + + #endregion + + #region Private Statics + + /// + /// Send an Email * Doesn't Support Implicit SSL (port 465) - ONLY Explicit SSL on 25 + /// + /// specifiy a SMTP Server to use * Required * + /// Email to use to send with * Required * + /// Display name for Sender * Required * + /// ';' seperated list of emails to send to * Required * + /// Email Message Subject * Required * + /// Email Message Text * Required * + /// Smtp Port to use + /// ';' seperated list of emails to cc to + /// true to use SmtpAuthentication, false otherwise + /// Smtp Authentication Username + /// Smtp Authentication Password + /// true to use ssl, false otherwise + /// Defaults to 100 Seconds + /// If an exception occurs, this delegate gets called (can be null) + /// true if the Message Send Successfully, false otherwise + private static bool SendEmailUsingNetMail(string SmtpServer, string SenderEmail, string SenderDisplayName, string ReceiverEmails, string MessageSubject, string MessageText, uint nPort, string ReceiverCCEmails, bool bUseSmtpAuth, string SmtpUser, string SmtpPassword, bool RequiresSSL, int nTimeOutInSeconds, DelegateCollection.Void_Param1_Exception_Func exceptionFunc) + { + try + { + if (String.IsNullOrEmpty(SmtpServer) || String.IsNullOrEmpty(SenderEmail) || String.IsNullOrEmpty(SenderDisplayName) || String.IsNullOrEmpty(ReceiverEmails) || String.IsNullOrEmpty(MessageSubject) || String.IsNullOrEmpty(MessageText)) + return false; + + SmtpClient sMail = new SmtpClient(SmtpServer); + sMail.DeliveryMethod = SmtpDeliveryMethod.Network; + if (bUseSmtpAuth && !String.IsNullOrEmpty(SmtpUser) && !String.IsNullOrEmpty(SmtpPassword)) + { + sMail.UseDefaultCredentials = false; + sMail.Credentials = new NetworkCredential(SmtpUser, SmtpPassword); + } + + // Mail Message + MailMessage msg = new MailMessage(); + msg.Subject = MessageSubject; + // From + msg.From = new MailAddress(SenderEmail, SenderDisplayName); + // To + foreach (string strToEmail in ReceiverEmails.Split(';')) + msg.To.Add(new MailAddress(strToEmail)); + // Body + msg.Body = MessageText; + // CC + if (!String.IsNullOrEmpty(ReceiverCCEmails)) + { + foreach (string strToEmail in ReceiverCCEmails.Split(';')) + msg.CC.Add(new MailAddress(strToEmail)); + } + + // Send it + if (nTimeOutInSeconds < 100) + nTimeOutInSeconds = 100; + sMail.Timeout = (int)TimeSpan.FromSeconds(nTimeOutInSeconds).TotalMilliseconds; + sMail.Port = ((nPort > 0) && (nPort < 65536)) ? (int)nPort : 25; + sMail.EnableSsl = RequiresSSL; + sMail.Send(msg); + return true; + } + catch (Exception e) { if (exceptionFunc != null) exceptionFunc(e); } + return false; + } + + /// + /// Send an Email * Does Support SSL (port 465) via Hacks - System.Web.Mail Space however is depreciated + /// + /// + /// specifiy a SMTP Server to use * Required * + /// Email to use to send with * Required * + /// Display name for Sender * Required * + /// ';' seperated list of emails to send to * Required * + /// Email Message Subject * Required * + /// Email Message Text * Required * + /// Smtp Port to use + /// ';' seperated list of emails to cc to + /// true to use SmtpAuthentication, false otherwise + /// Smtp Authentication Username + /// Smtp Authentication Password + /// true to use ssl, false otherwise + /// Defaults to 100 Seconds + /// If an exception occurs, this delegate gets called (can be null) + /// true if the Message Send Successfully, false otherwise + private static bool SendEmailUsingWebMail(string SmtpServer, string SenderEmail, string SenderDisplayName, string ReceiverEmails, string MessageSubject, string MessageText, uint nPort, string ReceiverCCEmails, bool bUseSmtpAuth, string SmtpUser, string SmtpPassword, bool RequiresSSL, int nTimeOutInSeconds, DelegateCollection.Void_Param1_Exception_Func exceptionFunc) + { + try + { + if (String.IsNullOrEmpty(SmtpServer) || String.IsNullOrEmpty(SenderEmail) || String.IsNullOrEmpty(SenderDisplayName) || String.IsNullOrEmpty(ReceiverEmails) || String.IsNullOrEmpty(MessageSubject) || String.IsNullOrEmpty(MessageText)) + return false; + + string[] Receivers = ReceiverEmails.Split(';'); + foreach (string Receiver in Receivers) + { + webmail.MailMessage Mail = new System.Web.Mail.MailMessage(); + Mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpserver"] = SmtpServer; + Mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendusing"] = 2; + Mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpserverport"] = nPort.ToString(); + if (RequiresSSL) + Mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpusessl"] = "true"; + if (bUseSmtpAuth) + { + Mail.Fields["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"] = 1; + Mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendusername"] = SmtpUser; + Mail.Fields["http://schemas.microsoft.com/cdo/configuration/sendpassword"] = SmtpPassword; + } + + // Send Message + Mail.To = Receiver; + Mail.From = SenderEmail; + Mail.Subject = MessageSubject; + Mail.Body = MessageText; + webmail.SmtpMail.SmtpServer = SmtpServer; + webmail.SmtpMail.Send(Mail); + } + + return true; + } + catch (Exception e) { if (exceptionFunc != null) exceptionFunc(e); } + return false; + } + + #endregion + } +} diff --git a/Net/IPHostHelper.cs b/Net/IPHostHelper.cs new file mode 100644 index 0000000..d5bb278 --- /dev/null +++ b/Net/IPHostHelper.cs @@ -0,0 +1,832 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using Yaulw.Win32; +using System.Management; +using System.IO; + +namespace Yaulw.Net +{ + /// + /// Helper for common IP / Host Machine + /// + public static class IPHostHelper + { + #region Public consts + + /// + /// This automation URL used to work, now it no longer works :( + /// Caller must set this before calling ExternalIP() Functions + /// + public static string WHAT_IS_MY_IP_AUTOMATION_URL = "http://automation.whatismyip.com/n09230945.asp"; + + #endregion + + #region IPv4 Checkers + + /// + /// Checks to see that the passed in ip is a valid Local IP + /// address that is of version 4 and not a broadcast/loopback/None + /// + /// + /// true to allow Loopback address as valid Local IP + /// + /// + /// IPAddress.IPv6Any = "::"; + /// IPAddress.Any = "0.0.0.0" + /// IPAddress.Broadcast = "255.255.255.255" + /// IPAddress.None = "255.255.255.255" + /// IPAddress.Loopback = "127.0.0.1" + /// + public static bool IsValidIPv4Address(IPAddress ip, bool bAllowLoopback) + { + if (ip != null) + { + if (ip.AddressFamily != AddressFamily.InterNetwork) + return false; + if (ip.AddressFamily.ToString().Contains(IPAddress.IPv6Any.ToString())) + return false; + if (ip == IPAddress.Any) + return false; + if (ip == IPAddress.Broadcast) + return false; + if (ip == IPAddress.None) + return false; + + // Loopback technically is a valid Local IP that can be used to communicate + // to ourselves (on the same machine) + if (ip == IPAddress.Loopback) + return bAllowLoopback; + + // This must be a valid local IPv4 + return true; + } + return false; + } + + /// + /// Checks to see if the passed in ip is in a valid Private subnet (i.e. not public) + /// + /// + /// + /// + /// Private subnets as defined in + /// Loopback address 127.0.0.1/8 + /// Zeroconf/bonjour self assigned addresses 169.254.0.0/16 + /// + public static bool IsIPv4AddressInPrivateSubnet(IPAddress ip) + { + if (!IsValidIPv4Address(ip, true)) + return false; + + String[] netAddrs = { "192.168.0.0", "10.0.0.0", "172.16.0.0", "127.0.0.1", "169.254.0.0" }; + String[] netMasks = { "255.255.0.0", "255.0.0.0", "255.240.0.0", "255.0.0.0", "255.255.0.0" }; + + UInt32 myIP = BitConverter.ToUInt32(ip.GetAddressBytes(), 0); + for (int i = 0; i < netMasks.Length; i++) + { + IPAddress netAddr = IPAddress.Parse(netAddrs[i]); + UInt32 netIP = BitConverter.ToUInt32(netAddr.GetAddressBytes(), 0); + + IPAddress maskAddr = IPAddress.Parse(netMasks[i]); + UInt32 maskIP = BitConverter.ToUInt32(maskAddr.GetAddressBytes(), 0); + + if ((myIP & maskIP) == (netIP & maskIP)) + return true; + } + return false; + } + + /// + /// Compares two IP Addresses, if the Left One is different from the right one, assigns + /// the right one to the LeftOrRight (simple) and returns true. Otherwise assigns left to it and returns false. + /// + public static bool AreIPAddressesDifferent(IPAddress left, IPAddress right, out IPAddress LeftOrRight) + { + LeftOrRight = left; + if (left != null && right != null) + { + if (String.Compare(left.ToString(), right.ToString(), true) != 0) + { + LeftOrRight = right; + return true; + } + } + else if (left == null && right != null) + { + LeftOrRight = right; + return true; + } + return false; + } + + #endregion + + #region Connectivity + + /// + /// Method to check if the Local Computer has a valid Network Connection + /// + /// + public static bool HasConnection() + { + //instance of our ConnectionStatusEnum + Definitions.ConnectionStatusEnum state = 0; + + //call the API + bool bIsConnected = Wininet.InternetGetConnectedState(ref state, 0); + if (!bIsConnected) + return false; + + //check the status, if in offline mode then return false + if (((int)Definitions.ConnectionStatusEnum.INTERNET_CONNECTION_OFFLINE & (int)state) != 0) + return false; + + return true; + } + + /// + /// Method to check to see if local Computer has a valid Network connection + /// and can open the specified ip and port + /// + /// + /// + /// + public static bool HasConnectivity(IPAddress ip, uint port) + { + if(HasConnection() && IsValidIPv4Address(ip, true) && port > 0) + return IsPortOpen(ip, (int) port); + return false; + } + + /// + /// Method to check to see if local Computer has a valid Network connection + /// and can open the specified host and port + /// + /// + /// + /// + /// + public static bool HasConnectivity(string host, uint port, int nTimeout) + { + if (HasConnection() && port > 0) + { + IPAddress ip = GetIpForHost(host); + if(ip != IPAddress.None && IsValidIPv4Address(ip, false)) + return IsPortOpen(ip, (int) port, nTimeout); + } + return false; + } + + /// + /// Retrieves the Servername from a DataBaseConnectionString, if possible, + /// then gets the ip for the server name and tries to open up the port specified + /// + /// pass in either a DataSource or ConnectionString + /// + /// + public static bool HasConnectivity(string DataBaseConnectionString, uint port) + { + if (HasConnection() && !String.IsNullOrEmpty(DataBaseConnectionString)) + { + // Allow the SharedConnectionDataSource to be a ConnectionString also via calling GetDataSource() + DataBaseConnectionString = GetDataSource(DataBaseConnectionString); + + // Retrieve the IP for the Data Source and test the ip and set it in the API + string host = GetServerNameFromADataSource(DataBaseConnectionString); + IPAddress ip = GetIpForHost(host); + if (ip != IPAddress.None) + { + if (IsPortOpen(ip, (int) port)) + return true; + } + } + return false; + } + + #endregion + + #region Core IPHostHelper Functionallity + + /// + /// Converts all incl. Loopback, if contained, valid IP addresses into a String array of IPs + /// + /// + public static string[] ConvertIPArrayToStringArray(IPAddress[] IPs) + { + List ips = new List(); + if (IPs != null && IPs.Length > 0) + { + foreach (IPAddress ip in IPs) + { + if (IsValidIPv4Address(ip, true)) + ips.Add(ip.ToString()); + } + } + return ips.ToArray(); + } + + /// + /// Get all valid IPv4 Local IP addresses besides the loopback address + /// + /// + public static IPAddress[] GetAllLocalIPAddresses() + { + // Get host name + String HostName = Dns.GetHostName(); + IPHostEntry iphostentry = Dns.GetHostEntry(HostName); + + // Enumerate IP addresses + List IPs = new List(); + foreach (IPAddress ipaddress in iphostentry.AddressList) + { + if (IsValidIPv4Address(ipaddress, false)) + IPs.Add(ipaddress); + } + + // Return them as a string[] of IPs + return IPs.ToArray(); + } + + /// + /// Get First Found Local IP Address (or IPAddress.None, if none found) + /// + /// get the first Local IP Address found + public static IPAddress GetFirstLocalIPAddress() + { + IPAddress[] IPs = GetAllLocalIPAddresses(); + if (IPs.Length > 0) + return IPs[0]; + else + return IPAddress.None; + } + + /// + /// Get Nth Found Local IP Address (or first one found, if n > IPs Found) + /// (or IPAddress.None, if none found) + /// + /// get the Nth Local IP Address found + public static IPAddress GetNthLocalIPAddress(int n) + { + IPAddress[] IPs = GetAllLocalIPAddresses(); + if (n >= 0 && IPs.Length < n) + return IPs[n]; + else if (IPs.Length > 0) + return IPs[0]; + else + return IPAddress.None; + } + + /// + /// Check to see if the host passed in is Local + /// + /// can be ip or host name + /// true, if host or ip is local, false otherwise + /// When host is empty + public static bool IsHostOrIPLocal(string host) + { + if (!String.IsNullOrEmpty(host)) + { + IPAddress ip = null; + if (IPAddress.TryParse(host, out ip)) + { + IPAddress[] localIPs = GetAllLocalIPAddresses(); + foreach(IPAddress localip in localIPs) + { + if (localip == ip) + return true; + } + return false; + } + else + { + return (String.Compare(host.Trim(), Dns.GetHostName(), true) == 0); + } + } + throw new ArgumentException("Invalid host"); + } + + /// + /// Method for resolving a Host (DNS Resolve) + /// + /// the host + /// Set to true, if the host must be in the Private IP Range + /// true, if host can be resolved to an IP Address, false otherwise + public static bool CanResolveHost(string host, bool bMustBeInPrivateIPRange) + { + if(!String.IsNullOrEmpty(host)) + { + IPAddress ip = GetIpForHost(host); + if (bMustBeInPrivateIPRange) + return IsIPv4AddressInPrivateSubnet(ip); + return (ip != IPAddress.None); + } + return false; + } + + /// + /// Method for retrieving the IP address for a Host (DNS Resolve) + /// + /// the host we need the ip address for + /// an IPAddress if found, otherwise IPAddress.None + public static IPAddress GetIpForHost(string host) + { + //variable to hold our error message (if something fails) + string errMessage = string.Empty; + + //IPAddress instance for holding the returned host + IPAddress address = IPAddress.None; + + //wrap the attempt in a try..catch to capture + //any exceptions that may occur + try + { + // if it's already an ip address being passed in, + // we are done + if (!IPAddress.TryParse(host, out address)) + { + //get the host IP from the name provided + address = Dns.GetHostEntry(host).AddressList[0]; + + // We must be called for the local host, that + // could explain why it is not a valid IP + if (!IsValidIPv4Address(address, false)) + address = GetFirstLocalIPAddress(); + } + else + { + address = IPAddress.Parse(host); + } + } + catch (Exception) { /* ignore */ } + if (address == null) + return IPAddress.None; + else + return address; + } + + /// + /// Ping a host + /// + /// url/host/ip + /// timeout value in miliseconds + /// number of times to ping + /// the number of successful pings + public static int PingHost(string host, int nTimeout, int nTrys) + { + //string to hold our return messge + //string returnMessage = string.Empty; + + // Check to see if the host passed in is an IP Address + // otherwise, just resolve the host + IPAddress address = null; + if(!IPAddress.TryParse(host, out address)) + address = GetIpForHost(host); + + // Error occured resolving host + if (!IsValidIPv4Address(address, false)) + return 0; + + //set the ping options, TTL 128 + PingOptions pingOptions = new PingOptions(128, true); + + //create a new ping instance + Ping ping = new Ping(); + + //32 byte buffer (create empty) + byte[] buffer = new byte[32]; + + //first make sure we actually have an internet connection + int nPingSuccess = 0; + if (HasConnection()) + { + //here we will ping the host nTrys times + for (int i = 0; i < nTrys; i++) + { + try + { + PingReply pingReply = ping.Send(address, nTimeout, buffer, pingOptions); + if (!(pingReply == null)) + { + switch (pingReply.Status) + { + case IPStatus.Success: + nPingSuccess++; + break; + case IPStatus.TimedOut: + default: + break; + } + } + } + catch (Exception) { /* ignore and continue iterating */ } + } + } + return nPingSuccess; + } + + #endregion + + #region Port Open + + /// + /// Check to see if a port is open for the specified ip + /// + /// + /// + /// + public static bool IsPortOpen(IPAddress ip, int port) + { + bool bCanConnect = false; + if (IsValidIPv4Address(ip, true) && port > 0) + { + try + { + TcpClient tcP = new System.Net.Sockets.TcpClient(); + tcP.Connect(ip, port); + bCanConnect = true; + tcP.Close(); + } + catch (Exception) { /* ignore */ } + } + return bCanConnect; + } + + /// + /// Check to see if a port is open for the specified ip + /// + /// + /// + /// + public static bool IsPortOpen(IPAddress ip, int port, int nTimeout) + { + bool bCanConnect = false; + if (IsValidIPv4Address(ip, true) && port > 0) + { + try + { + TcpClient tcP = new System.Net.Sockets.TcpClient(); + if (nTimeout > 0) + tcP.ReceiveTimeout = nTimeout; + tcP.Connect(ip, port); + bCanConnect = true; + tcP.Close(); + } + catch (Exception) { /* ignore */ } + } + return bCanConnect; + } + + #endregion + + #region Get External IP + + /// + /// Get the External IP + /// + /// Timeout in MiliSeconds + /// a valid external IPAddress or IPAddress.None, if error occured + public static IPAddress GetExternalIP(int pTimeOutMiliSeconds) + { + IPAddress extIP = IPAddress.None; + try + { + using (WebClient wc = new WebClient()) + { + wc.Headers.Add("user-agent", WCHelper.USER_AGENT_GENERIC); + UTF8Encoding utf8 = new UTF8Encoding(); + string ipaddr = null; + bool done = false; + + wc.DownloadDataCompleted += new + DownloadDataCompletedEventHandler((object sender, + DownloadDataCompletedEventArgs e) => + { + ipaddr = utf8.GetString(e.Result); + done = true; + }); + + wc.DownloadDataAsync(new Uri(WHAT_IS_MY_IP_AUTOMATION_URL)); + System.DateTime startTime = System.DateTime.Now; + while (!done) + { + System.TimeSpan sp = System.DateTime.Now - startTime; + + // We should get a response in less than timeout. + // If not, we cancel all and return the internal IP Address + if (sp.TotalMilliseconds > pTimeOutMiliSeconds) + { + done = true; + wc.CancelAsync(); + } + } + + if (IPAddress.TryParse(ipaddr, out extIP)) + return extIP; + } + } + catch { /* ignore */ } + return IPAddress.None; + } + + /// + /// Get the External IP + /// + /// a valid external IPAddress or IPAddress.None, if error occured + public static IPAddress GetExternalIP() + { + string ipaddr = WCHelper.ScreenScrapeFromURL(WHAT_IS_MY_IP_AUTOMATION_URL); + IPAddress extIP = IPAddress.None; + if (IPAddress.TryParse(ipaddr, out extIP)) + return extIP; + return IPAddress.None; + } + + /// + /// Call this to retrieve the external IP Address (also makes sure we can connect + /// to WhatIsMyIp site) + /// + /// + /// Forces 4 checks at 2.5/5.0/7.5/10 seconds timeout values + /// otherwise makes one request defaulting to 5.0 + /// + /// valid external IP Address or null if not found + public static IPAddress GetExternalIP(bool bForceMultipleChecks) + { + if (HasConnection()) + { + IPAddress ipExtIP = null; + if (bForceMultipleChecks) + { + // Check with an timout of 2.5/5.0/7.5/10 seconds + for (int n = 2500; n <= 10000; n = n + 2500) + { + ipExtIP = GetExternalIP(n); + if(ipExtIP != IPAddress.None) + return ipExtIP; + } + } + else + { + // Check with whatever is the default timeout + ipExtIP = Yaulw.Net.IPHostHelper.GetExternalIP(5000); + return ipExtIP; + } + } + return IPAddress.None; + } + + #endregion + + #region UNC and Path Related Windows Network Helpers + + /// + /// Allows us to use either pass in a ConnectionString or a DataSource * Added flexibility * + /// will return the DataSource part of the connection string, if exists, otherwise the connection string + /// + /// pass in either a DataSource or ConnectionString + /// + public static string GetDataSource(string DataBaseConnectionString) + { + if (IsDataBaseConnectionString(DataBaseConnectionString)) + { + String[] tokens = DataBaseConnectionString.Split(';'); + foreach (string Pair in tokens) + { + string[] KeyValuePair = Pair.Split('='); + try + { + string left = KeyValuePair[0]; + string right = KeyValuePair[1]; + string FoundValue = ""; + if (String.Compare(KeyValuePair[0].ToUpper().Trim(), "DATA SOURCE", true) == 0) + { + if (!String.IsNullOrEmpty(right)) + { + FoundValue = right.Trim(); + return FoundValue; + } + } + + } + catch (Exception) { /* ignore and continue iteration */ } + } + } + + // Migth still be a data source just not from a Connection string + // so just return the value trimmed + if (!String.IsNullOrEmpty(DataBaseConnectionString)) + return DataBaseConnectionString.Trim(); + else + return String.Empty; + } + + /// + /// Quick Check to see if a connection string is valid * must have DataSource Key and + /// a Value * in order to be considered a valid Connection String + /// + /// + /// + public static bool IsDataBaseConnectionString(string ConnectionString) + { + if (!String.IsNullOrEmpty(ConnectionString) && + ConnectionString.ToUpper().Contains("DATA SOURCE") && + ConnectionString.Contains("=")) + { + String[] tokens = ConnectionString.Split(';'); + foreach (string Pair in tokens) + { + string[] KeyValuePair = Pair.Split('='); + try + { + string left = KeyValuePair[0]; + string right = KeyValuePair[1]; + if (String.Compare(KeyValuePair[0].ToUpper().Trim(), "DATA SOURCE", true) == 0) + return !String.IsNullOrEmpty(right); + } + catch (Exception) { /* ignore and continue iteration */ } + } + return true; + } + return false; + } + + /// + /// Returns the Server Name from a Database DataSource Connection String + /// + /// + /// [DriveLetter]:\{SomeFileNameNPath} + /// \\{Server]\{SomeFileNameNPath} + /// \\{Server]:[Port]\{SomeFileNameNPath} + /// [Server]\Instance + /// [Server] + /// + /// Server string returned can be a name or an IP address + /// Server Host Name or IP + public static string GetServerNameFromADataSource(string DataSource) + { + if (!String.IsNullOrEmpty(DataSource)) + { + + // Always strip out port in the DataSource *before doing anything* + // only do this for non-local paths + if (DataSource.Contains(":") && DataSource[1] != ':') + { + int nLoc = DataSource.IndexOf(":"); + if (nLoc != -1) + DataSource = DataSource.Substring(0, nLoc); + } + + // Let's begin ... + string Server = String.Empty; + + // Local Medisoft + bool bIsLocalFilePath = DataSource.Contains(@":\"); + if (bIsLocalFilePath) + { + string PathInCaseMapped = ResolveToUNCIfNeeded(DataSource); + + // It's a local or a UNC path + if (!PathInCaseMapped.StartsWith(@"\\")) + { + // If Local file that the host is this computer + return Dns.GetHostName(); + } + else + { + // else it's the other host * so follow UNC logic * + // Stripe out first 2 chars + Server = PathInCaseMapped.Substring(2); + + // Stripe out the rest + int nLoc = Server.IndexOf(@"\"); + if (nLoc != -1) + Server = Server.Substring(0, nLoc); + } + } + + // Remote Medisoft + bool bIsNetworkFilePath = DataSource.StartsWith(@"\\"); + if (bIsNetworkFilePath) + { + // Stripe out first 2 chars + Server = DataSource.Substring(2); + + // Stripe out the rest + int nLoc = Server.IndexOf(@"\"); + if (nLoc != -1) + Server = Server.Substring(0, nLoc); + } + + // It's Lytec! :) + bool bIsSQLServerInstance = !bIsLocalFilePath && !bIsNetworkFilePath; + if (bIsSQLServerInstance) + { + // Stripe out Instance, if it exists + int nLoc = DataSource.IndexOf(@"\"); + if (nLoc != -1) + Server = DataSource.Substring(0, nLoc); + else + Server = DataSource; + } + + return Server; + } + return String.Empty; + } + + /// + /// Determines if the passed in string is a connection string or a data Source + /// and tries to make the best of things to retrieve the host/server name + /// + /// + /// + public static string GetServerNameFromAConnectionString(string ConnectionString) + { + if (IsDataBaseConnectionString(ConnectionString)) + { + string DataSource = GetDataSource(ConnectionString); + return GetServerNameFromADataSource(DataSource); + } + else + { + // Pretentd that it is just a DataSource + return GetServerNameFromADataSource(ConnectionString); + } + } + + /// Resolves the given path to a UNC path, or local drive path. + /// + /// \\server\share\[path] OR [driveletter]:\[path] + public static string ResolveToUNCIfNeeded(string pPath) + { + if (String.IsNullOrEmpty(pPath)) + return String.Empty; + + ManagementObject mo = new ManagementObject(); + + // Already UNC, we are done here + if (pPath.StartsWith(@"\\")) { return pPath; } + + // Get just the drive letter for WMI call + string driveletter = GetDriveLetter(pPath); + + mo.Path = new ManagementPath(string.Format("Win32_LogicalDisk='{0}'", driveletter)); + + // Get the data we need + uint DriveType = System.Convert.ToUInt32(mo["DriveType"]); + string NetworkRoot = System.Convert.ToString(mo["ProviderName"]); + mo = null; + + // Return the root UNC path if network drive, otherwise return the root path to the local drive + if (DriveType == 4) + { + return NetworkRoot + pPath.Substring(2); + } + else + { + return driveletter + pPath.Substring(2); + } + } + + /// Checks if the given path is on a network drive. + /// + /// + public static bool isNetworkDrive(string pPath) + { + ManagementObject mo = new ManagementObject(); + + if (pPath.StartsWith(@"\\")) { return true; } + + // Get just the drive letter for WMI call + string driveletter = GetDriveLetter(pPath); + + mo.Path = new ManagementPath(string.Format("Win32_LogicalDisk='{0}'", driveletter)); + + // Get the data we need + uint DriveType = System.Convert.ToUInt32(mo["DriveType"]); + mo = null; + + return DriveType == 4; + } + + #endregion + + #region Private Helpers + + /// Given a path will extract just the drive letter with volume separator. + /// + /// C: + private static string GetDriveLetter(string pPath) + { + if (pPath.StartsWith(@"\\")) { throw new ArgumentException("A UNC path was passed to GetDriveLetter"); } + return Directory.GetDirectoryRoot(pPath).Replace(Path.DirectorySeparatorChar.ToString(), ""); + } + + #endregion + } +} diff --git a/Net/RouterHelper.cs b/Net/RouterHelper.cs new file mode 100644 index 0000000..4cf01b2 --- /dev/null +++ b/Net/RouterHelper.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Yaulw.Net +{ + + public static class RouterHelper + { + + + } +} diff --git a/Net/WCHelper.cs b/Net/WCHelper.cs new file mode 100644 index 0000000..07b6be9 --- /dev/null +++ b/Net/WCHelper.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; + +namespace Yaulw.Net +{ + /// + /// WebClient Helper class to get commen stuff done + /// + public static class WCHelper + { + public const string USER_AGENT_IE8 = "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; GTB7.4; InfoPath.2; SV1; .Net CLR 3.3.69573; .NET CLR 1.0.3705; WOW64; rv:12.0; en-US) Gecko/20100101 Firefox/12.0"; + public const string USER_AGENT_GENERIC = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0"; + + /// + /// Download the specified URL file to the local path + /// + /// + /// + /// + /// + public static bool DownloadFileFromURL(string URLFileNameNPath, string LocalFileNameNPath, bool bOverwriteExisting) + { + if(!String.IsNullOrEmpty(URLFileNameNPath) && !String.IsNullOrEmpty(LocalFileNameNPath)) + { + try + { + using (WebClient fileReader = new WebClient()) + { + //string filename = URLFileNameNPath.Substring(URLFileNameNPath.LastIndexOf("/"), URLFileNameNPath.Length); + if (!System.IO.File.Exists(LocalFileNameNPath) || bOverwriteExisting) + fileReader.DownloadFile(URLFileNameNPath, LocalFileNameNPath); + } + return true; + } + catch (Exception) { /* ignore */ } + } + return false; + } + + /// + /// ScreenScrape the Text from the URL + /// + /// + /// + public static string ScreenScrapeFromURL(string URL) + { + if (!String.IsNullOrEmpty(URL)) + { + try + { + using (WebClient fileReader = new WebClient()) + { + fileReader.Headers.Add("user-agent", USER_AGENT_GENERIC); + using (Stream data = fileReader.OpenRead(URL)) + using (StreamReader sr = new StreamReader(data)) + { + string str = sr.ReadToEnd(); + return str; + } + } + } + catch (Exception) { /* ignore */ } + } + return ""; + } + + + } +} diff --git a/Net/WSCaller.cs b/Net/WSCaller.cs new file mode 100644 index 0000000..56abb53 --- /dev/null +++ b/Net/WSCaller.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Refl = System.Reflection; +using System.Net; +using System.Web.Services.Description; +using System.CodeDom; +using System.CodeDom.Compiler; +using Yaulw.Other; + +namespace Yaulw.Net +{ + /// + /// Class to Make Soap Web Services Calls Dynamically (Works only for Microsoft-based Web Services it seems) + /// + public class WSCaller + { + #region Private Members + + private string _ServiceName = ""; // example: "WebService" + private string _Url = ""; // example: "http://webservice.com/WebService/WebService.asmx" + + private Refl.Assembly _webServiceAssembly = null; + + // available services + private List _services = new List(); + + // available types + private Dictionary _availableTypes = new Dictionary(); + + private bool _InvokeMethodErrorWasThrown = false; + + #endregion + + #region Construction + + /// + /// Creates the service invoker using the specified web service. + /// + /// Name of Web Service + /// Url of Web Service + public WSCaller(string ServiceName, string ServiceUrl) + { + if (!String.IsNullOrEmpty(ServiceName) && !String.IsNullOrEmpty(ServiceUrl)) + { + // Store Service Specifics + _ServiceName = ServiceName; + _Url = ServiceUrl; + + // Try Creating the WS Assembly + CreateAssemblyFromWebServiceDescription(); + } + else + throw new ArgumentNullException("Service Name or Service Url can not be Null or Empty"); + } + + #endregion + + #region Public Methods + + /// + /// Check to see if the Web Service Is Available + /// + public bool WebServiceAvailable + { + get + { + CreateAssemblyFromWebServiceDescription(); + return (_webServiceAssembly != null); + } + } + + /// + /// Make the Soap Method Call + /// + /// Method Name to call + /// parameters to use on call + /// true if called successfully, false otherwise + /// the return value of the Method + public T MakeSoapMethodCall(string MethodName, object[] parameters, out bool bCallSuccess, DelegateCollection.Void_Param1_Exception_Func exception) + { + bCallSuccess = false; + try + { + List Methods = EnumerateServiceMethods(_ServiceName); + if (Methods.Count == 0) + return default(T); + + // Try making a CheckActive Call + T result = InvokeMethod(_ServiceName, MethodName, parameters); + bCallSuccess = true; + return result; + } + catch (Exception e) { _InvokeMethodErrorWasThrown = true; if (exception != null) exception(e); } + return default(T); + } + + #endregion + + #region Private Helpers + + /// + /// create an assembly from the web service description (WSDL) + /// + private void CreateAssemblyFromWebServiceDescription() + { + // Error Occured upon invoke, try rebuilding the WS + if (_InvokeMethodErrorWasThrown && _webServiceAssembly != null) + _webServiceAssembly = null; + + // In Order to properly build/rebuild the WS must be available + if (_webServiceAssembly == null) + { + _webServiceAssembly = BuildAssemblyFromWSDL(new Uri(_Url)); + if (_webServiceAssembly != null) + { + // Reset + _InvokeMethodErrorWasThrown = false; + + // see what service types are available + Type[] types = _webServiceAssembly.GetExportedTypes(); + + // and save them + foreach (Type type in types) + { + _services.Add(type.FullName); + _availableTypes.Add(type.FullName, type); + } + } + } + } + + /// + /// Gets a list of all methods available for the specified service. + /// + /// + /// + private List EnumerateServiceMethods(string serviceName) + { + List methods = new List(); + + if (!_availableTypes.ContainsKey(serviceName)) + { + throw new Exception("Service Not Available"); + } + else + { + Type type = _availableTypes[serviceName]; + + // only find methods of this object type (the one we generated) + // we don't want inherited members (this type inherited from SoapHttpClientProtocol) + foreach (Refl.MethodInfo minfo in type.GetMethods(Refl.BindingFlags.Instance | Refl.BindingFlags.Public | Refl.BindingFlags.DeclaredOnly)) + methods.Add(minfo.Name); + + return methods; + } + } + + /// + /// Invokes the specified method of the named service. + /// + /// The expected return type. + /// The name of the service to use. + /// The name of the method to call. + /// The arguments to the method. + /// The return value from the web service method. + private T InvokeMethod(string serviceName, string methodName, params object[] args) + { + // create an instance of the specified service + // and invoke the method + object obj = null; + Type type = null; + try + { + obj = _webServiceAssembly.CreateInstance(serviceName); + type = obj.GetType(); + } + catch (Exception e) + { + throw new Exception(e.Message + " " + "Creation of webServiceAssembly Failed"); + } + + // Find the Service Point for the Web Service! ~Fix 100 - HTTP 417 Error + ServicePoint servicePoint = ServicePointManager.FindServicePoint(new Uri(_Url)); + if (servicePoint.Expect100Continue == true) + servicePoint.Expect100Continue = false; + + return (T)type.InvokeMember(methodName, Refl.BindingFlags.InvokeMethod, null, obj, args); + } + + /// + /// Builds an assembly from a web service description. + /// The assembly can be used to execute the web service methods. + /// + /// Location of WSDL (only pass in the main asmx url without the ?wdsl). + /// A web service assembly. + private Refl.Assembly BuildAssemblyFromWSDL(Uri webServiceUri) + { + try + { + if (String.IsNullOrEmpty(webServiceUri.ToString())) + throw new Exception("Web Service Not Found"); + + ServiceDescriptionImporter descriptionImporter = BuildServiceDescriptionImporter((webServiceUri.ToString() + "?wsdl")); + return CompileAssembly(descriptionImporter); + } + catch (Exception) + { + return null; + } + } + + /// + /// Builds the web service description importer, which allows us to generate a proxy class based on the + /// content of the WSDL described by the XmlTextReader. + /// + /// The http location of the WSDL + /// A ServiceDescriptionImporter that can be used to create a proxy class. + private ServiceDescriptionImporter BuildServiceDescriptionImporter(string wsdlUrl) + { + try + { + WebClient http = new WebClient(); + ServiceDescription serviceDescription = ServiceDescription.Read(http.OpenRead(wsdlUrl)); + + // build an importer, that assumes the SOAP protocol, client binding, and generates properties + ServiceDescriptionImporter descriptionImporter = new ServiceDescriptionImporter(); + descriptionImporter.ProtocolName = "Soap"; + descriptionImporter.AddServiceDescription(serviceDescription, null, null); + descriptionImporter.Style = ServiceDescriptionImportStyle.Client; + descriptionImporter.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties; + return descriptionImporter; + } + catch (Exception) + { + return null; + } + } + + /// + /// Compiles an assembly from the proxy class provided by the ServiceDescriptionImporter. + /// + /// + /// An assembly that can be used to execute the web service methods. + private Refl.Assembly CompileAssembly(ServiceDescriptionImporter descriptionImporter) + { + try + { + // a namespace and compile unit are needed by importer + CodeNamespace codeNamespace = new CodeNamespace(); + CodeCompileUnit codeUnit = new CodeCompileUnit(); + codeUnit.Namespaces.Add(codeNamespace); + + ServiceDescriptionImportWarnings importWarnings = descriptionImporter.Import(codeNamespace, codeUnit); + if (importWarnings == 0) // no warnings + { + // create a c# compiler + CodeDomProvider compiler = CodeDomProvider.CreateProvider("CSharp"); + + // include the assembly references needed to compile + string[] references = new string[2] { "System.Web.Services.dll", "System.Xml.dll" }; + + CompilerParameters parameters = new CompilerParameters(references); + + // compile into assembly + CompilerResults results = compiler.CompileAssemblyFromDom(parameters, codeUnit); + + foreach (CompilerError oops in results.Errors) + { + // trap these errors and make them available to exception object + throw new Exception("Compilation Error Creating Assembly"); + } + + // all done.... + return results.CompiledAssembly; + } + else + { + // warnings issued from importers, something wrong with WSDL + throw new Exception("Invalid WSDL"); + } + } + catch (Exception) + { + return null; + } + } + + #endregion + } +} diff --git a/Other/CMDLine.cs b/Other/CMDLine.cs new file mode 100644 index 0000000..2c77066 --- /dev/null +++ b/Other/CMDLine.cs @@ -0,0 +1,423 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Collections.Specialized; + +using Yaulw.Tools; + +namespace Yaulw.Other +{ + /// + /// CommandLine Arguments Parser for an Application + /// + /// + /// internal CMDline cmdline = new CMDline(typeof(App.CommandLine_Option), typeof(App.CommandLine_Flag)); + /// + /// public enum CommandLine_Flag + /// { + /// START, + /// STOP, + /// SHOW, + /// } + /// + /// public enum CommandLine_Option + /// { + /// ADD_SOMETHING, + /// REMOVE_SOMETHING, + /// } + /// + /// cmdline.Parse(e.Args); + /// + /// if (cmdline.HasParams) + /// { + /// if (App.cmdline.ShowHelp || !App.cmdline.ParamsValid) + /// { + /// ShowCommandLineHelp(); + /// return; + /// } + /// + /// string AddSomething = cmdline.GetOptionValue(CommandLine_Option.ADD_SOMETHING, ""); // + /// } + /// + /// + /// + public class CMDline + { + #region private Variables + + // Flag or Option Specifiers CONSTS + private const String FLAG_OR_OPTION_SPECIFIER = "-/"; + private const String HELP_FLAG_SPECIFIER = "?"; + + // Private Parsing Enums + private Type _CMDLineOptions = null; + private Type _CMDLineFlags = null; + + // Private Parsing Data Structures + private Stack _stringStack = new Stack(); + private StringDictionary _stringDictionary = new StringDictionary(); + + // Keep track of the last parsed args as an array + private string[] _lastparsedArgs = null; + + #endregion + + #region Construction + + /// + /// The Command Line Class needs to know what Options and Flags to Parse for + /// + /// Pass an Enum used to determine CommandLine Options + /// Pass an Enum used to determine CommandLine Flags + public CMDline(Type Enum_CMDLineOptions, Type Enum_CMDLineFlags) + { + if (Enum_CMDLineOptions.IsEnum && Enum_CMDLineFlags.IsEnum) + { + _CMDLineOptions = Enum_CMDLineOptions; + _CMDLineFlags = Enum_CMDLineFlags; + } + else + throw new ArgumentException("Both CMDLineOptions and CMDLineFlags must be Enums"); + } + + #endregion + + #region Public Properties + + /// + /// True if User Passed in any Parameters + /// + public bool HasParams { get { return (_stringDictionary.Count >= 1); } } + + /// + /// True if User requested Command-Line Help + /// + public bool ShowHelp { get { return GetFlagOrOptionValueBool(HELP_FLAG_SPECIFIER); } } + + /// + /// True if all Parameters parsed are valid, False otherwise + /// + public bool ParamsValid + { + get + { + if (!HasParams) + return true; + + string[] OptionNames = Enum.GetNames(_CMDLineOptions); + string[] FlagNames = Enum.GetNames(_CMDLineFlags); + + // Get All Flags and Options + List AllFlagsAndOptions = new List(); + foreach (string Option in OptionNames) + AllFlagsAndOptions.Add(Option.ToLower()); + foreach (string Flag in FlagNames) + AllFlagsAndOptions.Add(Flag.ToLower()); + + // Verify the Parameters + bool InvalidParamFound = false; + foreach (string key in _stringDictionary.Keys) + { + InvalidParamFound = (key != HELP_FLAG_SPECIFIER) && !AllFlagsAndOptions.Contains(key); + if (InvalidParamFound) + break; + } + return !InvalidParamFound; + } + } + + #endregion + + #region Public Methods + + /// + /// Main Entry Function to Retrieve an Option Value + /// + /// Should be a System Type like string, bool, int32, double, decimal, etc... + /// option to retrieve (From CMDLineOptions Enum) + /// Default value to use if nothing was retrieved * Error occured * + /// value or default value, if not found + public T GetOptionValue(Enum option, T DefaultValue) + { + T RetVal = DefaultValue; + string StringVal = GetFlagOrOptionValueStr(option.ToString()); + try + { + if (ObjTool.IsNotNullAndNotEmpty(StringVal)) + { + RetVal = ObjTool.ConvertStringToObj(StringVal); + } + } + catch (Exception) { /* ignore */ } + return RetVal; + } + + /// + /// Main Entry Function to Retrieve a Flag Value + /// + /// flag enum to retrieve (From CMDLineFlags Enum) + /// Bool Value found or false if not found + public bool GetFlagValue(Enum flag) + { + return GetFlagOrOptionValueBool(flag.ToString()); + } + + /// + /// Returns the Last Parsed Arguments as a string + /// + /// returns last parsed args or String.Empty if none + public string ParsedArgs() + { + if (_lastparsedArgs != null && _lastparsedArgs.Length > 0) + { + StringBuilder sb = new StringBuilder(); + foreach (string s in _lastparsedArgs) + { + sb.Append(s); + sb.Append(" "); + } + + sb.Remove(sb.Length - 1, 1); // remove trailing " " + return sb.ToString(); + } + + return string.Empty; + } + #endregion + + #region Private Helper Functions + + /// + /// Main Function used to Retrieve a String Value + /// + /// Flag or Option to get Value for + /// Value or Empty if not found + private string GetFlagOrOptionValueStr(string FlagOrOption) + { + // If there is a FLAG_OR_OPTION_CHARS, stripe it + if (IsFlagOrOption(FlagOrOption)) + FlagOrOption = GetFlagOrOption(FlagOrOption); + + FlagOrOption = FlagOrOption.ToLower(); + if (_stringDictionary.ContainsKey(FlagOrOption)) + return _stringDictionary[FlagOrOption]; + return String.Empty; + } + + /// + /// Main Function used to Retrieve a Bool Value + /// + /// Flag or Option to get Value for + /// True or False if not found + private bool GetFlagOrOptionValueBool(string FlagOrOption) + { + try + { + string Value = GetFlagOrOptionValueStr(FlagOrOption); + if (!String.IsNullOrEmpty(Value)) + { + bool bValue = bool.Parse(Value); + return bValue; + } + } + catch (Exception) { /*Ignore*/ } + return false; + } + + #endregion + + #region Parsing Methods + + /// true if the param is a flag or option + private bool IsFlagOrOption(string arg) + { + return (FLAG_OR_OPTION_SPECIFIER.Contains(arg[0].ToString())); + } + + /// the Value of a Param + private string GetFlagOrOption(string arg) + { + return arg.Substring(1); + } + + /// + /// Main Entry Function used to Parse CommandLine parameters + /// + /// command line arguments + /// true if any parsing occured, false otherwise + public bool Parse(string[] args) + { + return Parse(args, true); + } + + /// + /// Main Entry Function used to Parse CommandLine parameters + /// + /// command line arguments + /// Allows you to not parse Options as True, if in case the option is blank it will just be considered a blank option/flag + /// true if any parsing occured, false otherwise + public bool Parse(string[] args, bool bAlwaysParseOptionsAsTrue) + { + if (args == null || args.Length <= 0) + return false; + + _stringDictionary.Clear(); + _stringStack.Clear(); + _lastparsedArgs = args; + foreach (string arg in args) + { + if (!String.IsNullOrEmpty(arg)) + { + if (IsFlagOrOption(arg)) + { + _stringStack.Push(GetFlagOrOption(arg).ToLower()); + if (bAlwaysParseOptionsAsTrue) + _stringDictionary[GetFlagOrOption(arg).ToLower()] = "TRUE"; + } + else + { + // must have a flag or option on the stack, + // if it does we use it + if (_stringStack.Count >= 1) + { + _stringDictionary[_stringStack.Pop()] = Parse_ArgCleanup(arg); + } + } + } + } + _stringStack.Clear(); + return true; + } + + /// + /// Used only when cmd is constructed with (null, null), + /// and the cmd.Parse function is called. will iterate thru the internal + /// dictionary and stack and make sure that the command string / line items + /// are valid + /// + /// + public string MakeValidCmdStrFromNewlyParsedCmdLine() + { + StringBuilder sb = new StringBuilder(); + foreach (string key in _stringDictionary.Keys) + { + sb.Append(FLAG_OR_OPTION_SPECIFIER[0]); + sb.Append(MakeValidCmdStr(key)); + sb.Append("="); + sb.Append(_stringDictionary[key]); + sb.Append(""); + } + + foreach (string flag in _stringStack) + { + sb.Append(FLAG_OR_OPTION_SPECIFIER[0]); + sb.Append(MakeValidCmdStr(flag)); + sb.Append(""); + } + + // remove trailing space + sb = sb.Remove(sb.Length - 1, 1); + return sb.ToString(); + } + + /// + /// It appears that the Command-Line can foul us up sometimes when passing + /// certain arguments. This function deals with these special cases. + /// + /// arg to check/poss.clean up + /// cleaned up arg, if needed + private string Parse_ArgCleanup(string arg) + { + if (!String.IsNullOrEmpty(arg) && + (arg.Length > 2) && + (arg[arg.Length - 1] == '"')) // when passing a \ at the end of the command-line with a " + { + arg = arg.Remove(arg.Length - 1); + arg += "\\"; + } + return arg; + } + + #endregion + + #region Static Generate CmdLine Method + + /// + /// Generates a CommandLine Parameter String from the Options/Flags Given + /// + /// StringDictionary that contains all the Options, to use + /// List that contains all the Flags to use + /// a string that can be passed via the commandLine + public static string GenerateCmdLine(StringDictionary Options, List Flags) + { + string CmdLine = String.Empty; + + // Iterate Options, and add them + if (Options != null && Options.Count > 0) + { + foreach (string Key in Options.Keys) + { + string cmdKey = MakeValidCmdStr(Key); + string cmdValue = Options[Key]; + CmdLine = CmdLine + FLAG_OR_OPTION_SPECIFIER[0] + cmdKey + " " + "\"" + cmdValue + "\"" + " "; + } + } + + // Iterate Flags, and add them + if (Flags != null && Flags.Count > 0) + { + foreach (string Flag in Flags) + { + string cmdFlag = MakeValidCmdStr(Flag); + CmdLine = CmdLine + FLAG_OR_OPTION_SPECIFIER[0] + cmdFlag + " "; + } + } + + // Return the generated Commmand Line + return CmdLine; + } + + /// + /// Ensures Integrity with the Generated CMDLine string that Keys don't + /// contain Illegal Characters + /// + /// a Key to make sure it is valid + /// a valid Key + private static string MakeValidCmdStr(string Key) + { + if (!String.IsNullOrEmpty(Key)) + { + //":\\ _!@#$%^&()-+{}[],.;`~"; + Key = Key.Replace(" ", "_"); + Key = Key.Replace(":", "_"); + Key = Key.Replace("\\", "_"); + Key = Key.Replace("/", "_"); + Key = Key.Replace("!", "_"); + Key = Key.Replace("@", "_"); + Key = Key.Replace("#", "_"); + Key = Key.Replace("$", "_"); + Key = Key.Replace("%", "_"); + Key = Key.Replace("^", "_"); + Key = Key.Replace("&", "_"); + Key = Key.Replace("(", "_"); + Key = Key.Replace(")", "_"); + Key = Key.Replace("-", "_"); + Key = Key.Replace("+", "_"); + Key = Key.Replace("{", "_"); + Key = Key.Replace("}", "_"); + Key = Key.Replace("[", "_"); + Key = Key.Replace("]", "_"); + Key = Key.Replace(",", "_"); + Key = Key.Replace(".", "_"); + Key = Key.Replace(";", "_"); + Key = Key.Replace("'", "_"); + Key = Key.Replace("~", "_"); + return Key; + } + return String.Empty; + } + + #endregion + } +} diff --git a/Other/CMDcommands.cs b/Other/CMDcommands.cs new file mode 100644 index 0000000..a72d959 --- /dev/null +++ b/Other/CMDcommands.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Yaulw.Other +{ + /// + /// Commonly used Batch/CMD command Line Command + /// + public static class CMDcommands + { + #region CMDCommands + + /// + /// map a credential to a network path + /// + /// network path + /// user + /// password + /// Command-Line Command + static public string net_use(string path, string user, string pwd) + { + return ("net use " + path + " /User:" + user + " " + pwd); + } + + /// + /// map a credential and drive letter to a network path + /// + /// network path + /// drive letter to map + /// user + /// password + /// + /// Command-Line Command + static public string net_use_persist(string path, string driveletter, string user, string pwd) + { + return ("net use " + driveletter + ": " + path + " /User:" + user + " " + pwd + " PERSISTENT:YES"); + } + + /// + /// Delete a Network Credential from a network path + /// + /// network path + /// Command-Line Command + static public string net_delete(string path) + { + return ("net use /delete " + path); + } + + /// + /// Delete a Drive Letter and Network Credential from a network path + /// + /// + /// Command-Line Command + static public string net_delete_persist(string driveletter) + { + return ("net use /delete " + driveletter + ":"); + } + + #endregion + + #region PSTools + + /// + /// Commands for System Internals PSExec Tools + /// + public static class PSTools + { + /// + /// PSExec is a System Internals Remote Execution CommandLine tool. + /// Use this to execute a .bat/.cmd file on the other computer + /// + /// Name of computer + /// .bat or .cmd file to execute + /// user + /// password + /// true to allow remote process to interact with desktop + /// Command-Line Command + static public string ps_exec(string computer, string file, string user, string pwd, bool interactive) + { + string Show = ""; + if (interactive) + Show = "-i "; + + if (file.ToLower().Contains(".bat") || file.ToLower().Contains(".cmd")) + { + // copy .bat or .cmd file over + return (@"psexec.exe \\" + computer + " -u " + user + " -p " + pwd + " -c -f -e -d " + Show + "/accepteula " + '\"' + file + '\"'); + } + else + { + // doesn't copy over, executes command from location, don't wait for process to terminate + return (@"psexec.exe \\" + computer + " -u " + user + " -p " + pwd + " -d -e " + Show + "/accepteula " + '\"' + file + '\"'); + } + + } + } + + #endregion + } +} diff --git a/Other/CMDexecute.cs b/Other/CMDexecute.cs new file mode 100644 index 0000000..cb2fff2 --- /dev/null +++ b/Other/CMDexecute.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +using Yaulw.File; + +namespace Yaulw.Other +{ + /// + /// Usefull for quickly generating a .bat file and executing it (without a command window) + /// + public static class CMDexecute + { + #region Public Static Methods + + /// + /// Executes the passed in command on the command line * No Command Window Created * + /// + /// a command-line command + /// true to wait till process ends, false otherwise + /// If true, will set "RunAs" For the Process, to Run as Administrator + public static void cmd(string command, bool bWait = true, bool bRunAs = false) + { + FileWriter fileW = new FileWriter(String.Empty, "bat"); + fileW.DeleteFile(); + fileW.WriteLineUTF8(command); + execBatFile(fileW.FileNameNPath, bWait, bRunAs); + fileW.DeleteFile(); + } + + /// + /// Executes the passed in commands on the command line * No Command Window Created * + /// + /// command-line commands + /// true to wait till process ends, false otherwise + /// If true, will set "RunAs" For the Process, to Run as Administrator + public static void cmd(string[] commands, bool bWait = true, bool bRunAs = false) + { + FileWriter fileW = new FileWriter(String.Empty, "bat"); + fileW.DeleteFile(); + foreach (string command in commands) + fileW.WriteLineUTF8(command); + execBatFile(fileW.FileNameNPath, bWait, bRunAs); + fileW.DeleteFile(); + } + + #endregion + + #region Private Static Helpers + + /// + /// Executes the Batch file via CMD.exe, by starting the CMD.exe Process with no Window + /// + /// File (.bat) scrip to execute + /// true to wait till process ends, false otherwise + private static void execBatFile(string FileNameNPath, bool bWait = true, bool bUseRunAs = false) + { + if (!String.IsNullOrEmpty(FileNameNPath) && System.IO.File.Exists(FileNameNPath)) + { + //The "/C" Tells Windows to Run The Command then Terminate + string strCmdLine = "/C " + '\"' + FileNameNPath + '\"'; + string WindowsSystem32Folder = System.Environment.GetFolderPath(Environment.SpecialFolder.System); + System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo((WindowsSystem32Folder + "\\" + "CMD.exe"), strCmdLine); + //startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; + startInfo.CreateNoWindow = true; + startInfo.UseShellExecute = false; + startInfo.WorkingDirectory = Path.GetDirectoryName(FileNameNPath); + if (bUseRunAs) + startInfo.Verb = "runas"; + + // Start the Cmd.exe Process + System.Diagnostics.Process p1; + p1 = System.Diagnostics.Process.Start(startInfo); + if(bWait) + p1.WaitForExit(); + } + } + + #endregion + } +} diff --git a/Other/DelegateCollection.cs b/Other/DelegateCollection.cs new file mode 100644 index 0000000..ae53504 --- /dev/null +++ b/Other/DelegateCollection.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; + +namespace Yaulw.Other +{ + /// + /// Common Delegates * Useful for Dispatching * + /// + public static class DelegateCollection + { + // 0 Params Functions + public delegate void Void_Func(); + public delegate bool Bool_Func(); + public delegate int Int_Func(); + public delegate object Obj_Func(); + public delegate DateTime DateTime_Func(); + + // Void Ret - 1 Params Functions + public delegate void Void_Param1_String_Func(string str1); + public delegate void Void_Param1_Int_Func(int int1); + public delegate void Void_Param1_DateTime_Func(DateTime dt1); + public delegate void Void_Param1_Exception_Func(Exception ex1); + + // Bool Ret - 1 Params Functions + public delegate bool Bool_Param1_String_Func(string str1); + public delegate bool Bool_Param1_Window_Func(Window window); + + // String Ret - 1 Params Functions + public delegate string String_Param1_Bool_Func(bool bool1); + public delegate string String_Param1_String_Func(string str1); + + } +} diff --git a/Other/Installer.cs b/Other/Installer.cs new file mode 100644 index 0000000..0126b9b --- /dev/null +++ b/Other/Installer.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Yaulw.Process; + +namespace Yaulw.Other +{ + public static class Installer + { + + /// + /// Run a command on the commandline * Hidden * + /// + /// cmd to run + public static string RunCmdLine(string cmdline) + { + string result = PStarter.RunDosCommand(cmdline); + return result; + } + + /// + /// To grant the specified User or group Full Control permissions to the folder and its contents + /// + /// full path to folder/directory + /// domainname\administrator, any windows user or group + /// + public static bool GrantFullPermissionToFolderForUserOrGroup(string FolderNPath, string UserOrGroup) + { + if (Directory.Exists(FolderNPath)) + { + string command = String.Format("cacls \"{0}\" /t /e /g {1}:f", FolderNPath, UserOrGroup); + string strResult = RunCmdLine(command); + if (strResult.Contains("Invalid arguments.")) + return false; + else + return true; + } + return false; + } + } +} diff --git a/Other/OSInfo.cs b/Other/OSInfo.cs new file mode 100644 index 0000000..17af845 --- /dev/null +++ b/Other/OSInfo.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Yaulw.Win32; + +namespace Yaulw.Other +{ + #region OSInfo Enums + + /// + /// Windows OS Types + /// + public enum OStype + { + WIN_2000 = 4, + WIN_2003 = 5, + WIN_VISTA = 6, + WIN_7 = 7, + WIN_8 = 8, + WIN_UNKNOWN = -1, + } + + #endregion + + /// + /// This class provides OS Specific and Version information to the caller + /// + public static class OSInfo + { + #region Public Properties + + /// + /// Get OSType and Version Information + /// + public static int Major { get; private set; } + public static int Minor { get; private set; } + public static OStype OS { get; private set; } + + /// + /// Set Supported Version for an Application + /// + public static OStype MinVersionSupported { get; set; } + public static OStype MaxVersionSupported { get; set; } + + /// + /// Is this OS Version Supported * Min/Max Should be set by Application * + /// + public static bool IsSupportedOS + { + get + { + if (MinVersionSupported != OStype.WIN_UNKNOWN && MaxVersionSupported != OStype.WIN_UNKNOWN) + return ((int) OS >= (int)MinVersionSupported) && ((int) OS <= (int)MaxVersionSupported); + else + return true; + } + } + + /// + /// Is Vista/Win7's Aero Enabled + /// + public static bool IsAero + { + get + { + if (((int)OS >= (int)OStype.WIN_VISTA) || ((int)OS <= (int)OStype.WIN_7)) + { + bool IsAero = false; + if (uxDwm.DwmIsCompositionEnabled(ref IsAero) < 0) + return false; + else + return IsAero; + } + return false; + } + } + + /// + /// Is WindowsXP/2003 Composition Enabled + /// + public static bool IsXPComposition + { + get + { + if ((int)OS == (int)OStype.WIN_2003) + return uxDwm.IsAppThemed(); + return false; + } + } + + #endregion + + #region Construction + + /// + /// Constructor + /// + static OSInfo() + { + // Get the OS Version + Major = System.Environment.OSVersion.Version.Major; + Minor = System.Environment.OSVersion.Version.Minor; + + // Parse the OS Version + try { OSInfo.OS = (OStype)Enum.Parse(typeof(OStype), Major.ToString()); } + catch (Exception) { OSInfo.OS = OStype.WIN_UNKNOWN; } + + // Set Min/Max to Unknown + MinVersionSupported = OStype.WIN_UNKNOWN; + MaxVersionSupported = OStype.WIN_UNKNOWN; + } + + #endregion + } +} diff --git a/Other/StackWalker.cs b/Other/StackWalker.cs new file mode 100644 index 0000000..314a673 --- /dev/null +++ b/Other/StackWalker.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics; +using System.Reflection; + +namespace Yaulw.Other +{ + /// + /// Use the StackWalker to Find out what Method/Type Called you. + /// This is useful for logging functions to know what method/type called the Logging Method. + /// + public static class StackWalker + { + /// + /// Hard-Coded in Stack Frame count depending on how this Class is getting called, + /// and how many frames we have to go up / down (use nPlusMinus) + /// + public const int DEFAULT_STACK_FRAME_COUNT = 3; + + /// + /// Use this to get the MethodName from the CallStack. + /// This allows the calling method to know which method called it + /// + /// Use this to add/substract from the base stack level you want to retrieve + /// Returns the MethodName as specified by the CallStack + public static string GetMethodNameFromStack(int nPlusMinus = 0) + { + StackTrace stackTrace = new StackTrace(); + StackFrame stackFrame; + MethodBase stackFrameMethod; + + stackFrame = stackTrace.GetFrame(DEFAULT_STACK_FRAME_COUNT + nPlusMinus); + stackFrameMethod = stackFrame.GetMethod(); + return stackFrameMethod.Name; + } + + /// + /// Use this to get the Type from the CallStack. + /// This allows the calling method to know which type called it. + /// + /// Use this to add/substract from the base stack level you want to retrieve + /// Returns the Type as specified by the CallStack + public static Type GetTypeFromStack(int nPlusMinus = 0) + { + StackTrace stackTrace = new StackTrace(); + StackFrame stackFrame; + MethodBase stackFrameMethod; + + stackFrame = stackTrace.GetFrame(DEFAULT_STACK_FRAME_COUNT + nPlusMinus); + stackFrameMethod = stackFrame.GetMethod(); + return stackFrameMethod.ReflectedType; + } + + } +} diff --git a/Other/StateM.cs b/Other/StateM.cs new file mode 100644 index 0000000..b687ba3 --- /dev/null +++ b/Other/StateM.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Collections.Specialized; +using Yaulw.Tools; + +namespace Yaulw.Other +{ + /// + /// Easy StateManager to store and retrieve various Application States using an Enum + /// + /// + /// internal enum State + /// { + /// App_Started_bool, + /// App_ErrorsOccured_bool, + /// SpecialMode_CommandLine_Mode_bool, + /// } + /// + /// internal static StateM AppState = new StateM(typeof(State)); + /// + /// + /// + public class StateM : IDisposable + { + #region Private Members + + // Private Parsing Enums + private Type _StateKeys = null; + private StringDictionary _sdCurrentState = new StringDictionary(); // For State String Convertible Types + private Dictionary _sdCurrentStateObj = new Dictionary(); // For State Object Types + private bool _disposed = false; + private object _lock = new object(); + + #endregion + + #region Construction + + /// + /// The StateM needs to know what Keys to Get/Set + /// + /// Pass an Enum used to determine StateKeys + public StateM(Type Enum_StateKeys) + { + if (Enum_StateKeys.IsEnum) + { + _StateKeys = Enum_StateKeys; + } + else + throw new ArgumentException("StateKeys must be an Enum"); + } + + /// + /// Finalizer + /// + ~StateM() + { + Dispose(true); + } + + #endregion + + #region Public Properties + + /// + /// True if User Has passed in State Values + /// + public bool HasStates { get { return (_sdCurrentState.Count >= 1 || _sdCurrentStateObj.Count >= 1); } } + + #endregion + + #region Public Methods + + /// + /// Main Entry Function to Retrieve an State Value + /// + /// Should be a System Type like string, bool, int32, double, decimal, etc... + /// State Value you want to retrieve + /// Default value to use if nothing was retrieved * Error occured * + /// value or default value, if not found + public T GetStateValue(Enum stateKey, T DefaultValue) + { + lock (_lock) + { + T RetVal = DefaultValue; + if (ObjTool.IsOfTypeConvertibleToString(DefaultValue)) + { + string Value = String.Empty; + if (GetStateValue(stateKey, out Value) && !String.IsNullOrEmpty(Value)) + RetVal = ObjTool.ConvertStringToObj(Value); + } + else + { + object o = null; + if (GetStateValue(stateKey, out o) && (o != null)) + RetVal = (T)o; + } + return RetVal; + } + } + + /// + /// Main Entry Function to Set a State Value + /// + /// Should be a System Type like string, bool, int32, double, decimal, etc... + /// State Value you want to set + /// Value you want to set + /// true if successful, false otherwise + public bool SetStateValue(Enum stateKey, T Value) + { + lock (_lock) + { + bool bSuccess = false; + if (ObjTool.IsOfTypeConvertibleToString(Value)) + { + string strValue = ObjTool.ConvertObjToString(Value); + bSuccess = SetStateValue(stateKey, strValue); + } + else + { + bSuccess = SetStateValue(stateKey, (object)Value); + } + return bSuccess; + } + } + + #endregion + + #region Private Helper Methods + + /// + /// Private GetStateValue Getter Function + /// + /// pass in the State to look for + /// Returns the value or String.Empty, if none + /// true if the State Value Exists, false otherwise + private bool GetStateValue(Enum stateKey, out string Value) + { + lock (_lock) + { + Value = String.Empty; + if (_sdCurrentState.ContainsKey(stateKey.ToString())) + { + Value = _sdCurrentState[stateKey.ToString()]; + return true; + } + return false; + } + } + + /// + /// Private GetStateValue Getter Function + /// + /// pass in the State to look for + /// Returns the object or null, if noone + /// true if the State Value Exists, false otherwise + private bool GetStateValue(Enum stateKey, out object o) + { + lock (_lock) + { + o = null; + if (_sdCurrentStateObj.ContainsKey(stateKey.ToString())) + { + o = _sdCurrentStateObj[stateKey.ToString()]; + return true; + } + return false; + } + } + + /// + /// Private SetStateValue Setter Function + /// + /// pass in the State to Set + /// The Value to Set + /// true if the State Value was set, false otherwise + private bool SetStateValue(Enum stateKey, string Value) + { + lock (_lock) + { + try + { + _sdCurrentState[stateKey.ToString()] = Value; + return true; + } + catch (Exception) { /* ignore */ } + return false; + } + } + + /// + /// Private SetStateValue Setter Function + /// + /// pass in the State to Set + /// The Object to Set + /// true if the State Value was set, false otherwise + private bool SetStateValue(Enum stateKey, object o) + { + lock (_lock) + { + try + { + _sdCurrentStateObj[stateKey.ToString()] = o; + return true; + } + catch (Exception) { /* ignore */ } + return false; + } + } + + #endregion + + #region IDisposable Members + + /// + /// Dispose the State Object + /// + public void Dispose() + { + Dispose(true); + + // Use SupressFinalize in case a subclass + // of this type implements a finalizer + GC.SuppressFinalize(this); + } + + /// + /// Dispose the State Object + /// + /// true, if called from within + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + foreach (object o in _sdCurrentStateObj) + { + if (o is IDisposable) + ((IDisposable)o).Dispose(); + } + _sdCurrentStateObj.Clear(); + } + + // Indicate that the instance has been disposed. + _sdCurrentState = null; + _sdCurrentStateObj = null; + _disposed = true; + } + } + + #endregion + } +} diff --git a/Other/TraceM.cs b/Other/TraceM.cs new file mode 100644 index 0000000..5aca81a --- /dev/null +++ b/Other/TraceM.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Threading = System.Threading; +using System.Diagnostics; + +namespace Yaulw.Other +{ + /// + /// Wrapper Class arround Tracing, designed to measure performance. + /// Use this class to measure performance arround your calls. + /// + /// TraceM.TraceBegin() + /// ... Some Code.... + /// ... Some More Code... + /// TraceM.TraceEnd("Custom Message"); + /// + /// + public static class TraceM + { + #region Private Statics + + private static Dictionary> s_ThreadTraceBeginTSMap = new Dictionary>(); + public static bool EnableTracing { get; set; } + + #endregion + + #region Construction + + /// + /// Construction * Tracing by default is disabled * + /// + static TraceM() + { + EnableTracing = false; + } + + #endregion + + #region Public Statics + + /// + /// Call this to Start the Performance Trace + /// + public static void TraceBegin() + { + if (EnableTracing) + { + uint curThreadId = (uint)Threading.Thread.CurrentThread.ManagedThreadId; + if (!s_ThreadTraceBeginTSMap.ContainsKey(curThreadId)) + s_ThreadTraceBeginTSMap[curThreadId] = new Stack(); + + s_ThreadTraceBeginTSMap[curThreadId].Push(DateTime.Now); + } + } + + /// + /// Us this to End the Performance Trace + /// + /// Custom Message you want displayed in the Trace Window + public static void TraceEnd(string CustomMessage = "") + { + if (EnableTracing) + { + uint curThreadId = (uint)Threading.Thread.CurrentThread.ManagedThreadId; + if (s_ThreadTraceBeginTSMap.ContainsKey(curThreadId)) + { + DateTime orgTime = s_ThreadTraceBeginTSMap[curThreadId].Pop(); + TimeSpan ts = DateTime.Now - orgTime; + Trace.WriteLine((CustomMessage + " (Time Taken in ms " + ts.TotalMilliseconds.ToString() + ")")); + } + } + } + + #endregion + } +} diff --git a/Other/Versioning.cs b/Other/Versioning.cs new file mode 100644 index 0000000..d95fcea --- /dev/null +++ b/Other/Versioning.cs @@ -0,0 +1,550 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics; + +namespace Yaulw.Other +{ + /// + /// Interface of Versioning, to check if the passed in version is supported by the Versioning Object + /// + public interface IVersionSupported + { + bool IsSupported(string Version); + bool IsSupported(Versioning Version); + bool IsSupported(Version Version); + } + + /// + /// Allows us to easily wrap Versioning Functionallity, + /// into Objects. Similar to Assembly.Version Class, however, + /// it allows the * character. This character specifies all and + /// allows for wildchar versioning schemes useful for certain + /// scenarios via IVersionSupported. + /// --------------------------------------------------- + /// [Major version].[Minor version].[Build number].[Revision number] + /// --------------------------------------------------- + /// Major version can be any valid uint as well as * + /// Minor version can be any valid uint as well as * + /// Build number can be any valid uint as well as * + /// Revision number can be any valid uint as well as * + /// + public class Versioning : ICloneable, IComparable, IVersionSupported + { + #region Public consts + + /// + /// Int value indicating a * (wildcard) character + /// + public const int STAR_ALL = -1; + + #endregion + + #region Private Members + + private int _MajorVersion = STAR_ALL; + private int _MinorVersion = STAR_ALL; + private int _BuildNumber = STAR_ALL; + private int _RevisionNumber = STAR_ALL; + + #endregion + + #region Construction + + /// + /// Use this to initialize the class with a version string, can not contain multiple versions seperated by ";" + /// + /// any version string in the format [Major].[Minor].[Build].[Revision], like 5.3.3.1 or 5.4.*.* + /// if Major,Minor,Build, or Revision < -1 + public Versioning(string strVersion) + { + if (!IsValidVersionStr(strVersion) || strVersion.Contains(";")) + throw new ArgumentException("Invalid Version String"); + ParseVersion(strVersion, out this._MajorVersion, out this._MinorVersion, out this._BuildNumber, out this._RevisionNumber); + } + + /// + /// Initialize Versioning with a single MajorVersion and MinorVersion. + /// + /// Major Version + /// Minor Version + /// if Major,Minor,Build, or Revision < -1 + public Versioning(int MajorVersion, int MinorVersion) + { + if (MajorVersion >= STAR_ALL && MinorVersion >= STAR_ALL) + { + _MajorVersion = MajorVersion; + _MinorVersion = MinorVersion; + } + else + throw new ArgumentException("MajorVersion or MinorVersion is invalid"); + } + + /// + /// Initialize Versioning with a single MajorVersion,MinorVersion, and BuildNumber. + /// + /// Major Version + /// Minor Version + /// Build Number + /// if Major,Minor,Build, or Revision < -1 + public Versioning(int MajorVersion, int MinorVersion, int BuildNumber) + { + if (MajorVersion >= STAR_ALL && MinorVersion >= STAR_ALL && _BuildNumber >= STAR_ALL) + { + _MajorVersion = MajorVersion; + _MinorVersion = MinorVersion; + _BuildNumber = BuildNumber; + } + else + throw new ArgumentException("MajorVersion,MinorVersion, or BuildNumber is invalid"); + } + + /// + /// Initialize Versioning with a single MajorVersion,MinorVersion,BuildNumber and RevisionNumber. + /// + /// Major Version + /// Minor Version + /// Build Number + /// Revision Number + /// if Major,Minor,Build, or Revision < -1 + public Versioning(int MajorVersion, int MinorVersion, int BuildNumber, int RevisionNumber) + { + if (MajorVersion >= STAR_ALL && MinorVersion >= STAR_ALL && _BuildNumber >= STAR_ALL && RevisionNumber >= STAR_ALL) + { + _MajorVersion = MajorVersion; + _MinorVersion = MinorVersion; + _BuildNumber = BuildNumber; + _RevisionNumber = RevisionNumber; + } + else + throw new ArgumentException("MajorVersion,MinorVersion, BuildNumber, or RevisionNumber is invalid"); + + } + + /// + /// Initialize Versioning with a .Net Version Object + /// + /// a .Net Version Object + public Versioning(Version version) + { + _MajorVersion = version.Major; + _MinorVersion = version.Minor; + _BuildNumber = version.Build; + _RevisionNumber = version.Revision; + } + + #endregion + + #region Versioning Getter/Setter + + /// + /// Set/Retrieve Version + /// + public String Version + { + get + { + return VersionToVersionString(_MajorVersion, _MinorVersion, _BuildNumber, _RevisionNumber); + } + set + { + if (IsValidVersionStr(value) && !value.Contains(";")) + ParseVersion(value, out this._MajorVersion, out this._MinorVersion, out this._BuildNumber, out this._RevisionNumber); + else + throw new ArgumentException("Invalid Version String"); + } + } + + #endregion + + #region Public Static Helpers + + /// + /// Gets a Versioning object for the Version Information of a File + /// + /// Full File Name and Path + /// returns a new Versioning Object for a File, or null if error occured + public static Versioning GetFileVersioning(string FileNameNPath) + { + if (!string.IsNullOrEmpty(FileNameNPath) && System.IO.File.Exists(FileNameNPath)) + { + try + { + FileVersionInfo info = FileVersionInfo.GetVersionInfo(FileNameNPath); + return new Versioning(info.FileMajorPart, info.FileMinorPart, info.FileBuildPart, info.FilePrivatePart); + } + catch (Exception) { /* ignore */ } + } + return null; + } + + /// + /// Parses out a single Version from a string + /// + /// pass in a Version format [Major].[Minor].[Build], like 5.3.3 or 5.4.* + /// returns an int or STAR_ALL + /// returns an int or STAR_ALL + /// returns an int or STAR_ALL + /// returns an int or STAR_ALL + public static void ParseVersion(string strVersion, out int MajorVersion, out int MinorVersion, out int BuildNumber, out int RevisionNumber) + { + if (!IsValidVersionStr(strVersion) || strVersion.Contains(";")) + throw new ArgumentException("Invalid Version String"); + + MajorVersion = STAR_ALL; + MinorVersion = STAR_ALL; + BuildNumber = STAR_ALL; + RevisionNumber = STAR_ALL; + string[] MajorMinorPossBuildVersion = strVersion.Split('.'); + try + { + for (int i = 0; i < MajorMinorPossBuildVersion.Length; ++i) + { + if (i == 0) + { + if (MajorMinorPossBuildVersion[0] != "*") + MajorVersion = int.Parse(MajorMinorPossBuildVersion[0]); + } + else if (i == 1) + { + if (MajorMinorPossBuildVersion[1] != "*") + MinorVersion = int.Parse(MajorMinorPossBuildVersion[1]); + } + else if (i == 2) + { + if (MajorMinorPossBuildVersion[2] != "*") + BuildNumber = int.Parse(MajorMinorPossBuildVersion[2]); + } + else if (i == 3) + { + if (MajorMinorPossBuildVersion[2] != "*") + RevisionNumber = int.Parse(MajorMinorPossBuildVersion[3]); + } + } + + } + catch (Exception) { /* Ignore */ } + } + + /// + /// Turns Version Numbers into a Version String + /// + /// Major Version + /// Minor Version, can be * + /// Build Number, can be * or UNINITIALIZED + /// the Version String + public static string VersionToVersionString(int MajorVersion, int MinorVersion = STAR_ALL, int BuildNumber = STAR_ALL, int RevisionNumber = STAR_ALL) + { + string strRetVal = String.Empty; + if (MajorVersion >= STAR_ALL) + { + // Major Version + if (MajorVersion == STAR_ALL) + strRetVal += "*"; + else + strRetVal += MajorVersion.ToString(); + + // Minor Version + if (MinorVersion >= STAR_ALL) + { + strRetVal += "."; + if (MinorVersion == STAR_ALL) + strRetVal += "*"; + else + strRetVal += MinorVersion.ToString(); + } + + // Build Number + if (BuildNumber >= STAR_ALL) + { + strRetVal += "."; + if (BuildNumber == STAR_ALL) + strRetVal += "*"; + else + strRetVal += BuildNumber.ToString(); + } + + // Revision Number + if (RevisionNumber >= STAR_ALL) + { + strRetVal += "."; + if (RevisionNumber == STAR_ALL) + strRetVal += "*"; + else + strRetVal += RevisionNumber.ToString(); + } + } + return strRetVal; + } + + #endregion + + #region Private Stats - Validation + + private const string CharSet_AllowedNumeric = "0123456789"; + private const string CharSet_AllowedVersionChars = CharSet_AllowedNumeric + "*;."; + + /// + /// Generic Function to use with Allowed Character Sets above + /// + /// string to evaluate + /// Pass in one of the legal character consts declared above + /// true if valid, false otherwise + private static bool ContainsOnlyLegalChars(string TextToEvaluate, string TextToEvaluateWith) + { + foreach (char c in TextToEvaluate.ToCharArray()) + { + bool bFound = (TextToEvaluateWith.IndexOf(c) >= 0); + if (!bFound) + return false; + } + return true; + } + + /// + /// Used to Validate a Version string of format 12.1.12;12.2.*;12.2.1 + /// + /// a Version String + /// true if valid, false otherwise + public static bool IsValidVersionStr(string VersionStr) + { + if (String.IsNullOrEmpty(VersionStr)) + { + return false; + } + else if (VersionStr.Length < 1 && VersionStr.Length > 14) // restrict Version String Length + { + return false; + } + else if (!ContainsOnlyLegalChars(VersionStr, CharSet_AllowedVersionChars)) + { + return false; + } + else + { + return true; + } + } + + #endregion + + #region ICloneable Members + + /// + /// Clone the Object + /// + /// a copy of this Versioning Object + public object Clone() + { + Versioning versioning = new Versioning(this._MajorVersion, this._MinorVersion, this._BuildNumber, this._RevisionNumber); + return versioning; + } + + #endregion + + #region IComparable Members + + /// + /// Compare two Versioning Objects + /// + /// a Versioning Object + /// -1, 0, +1 + public int CompareTo(object obj) + { + if (obj is Versioning) + { + Versioning v = (Versioning)obj; + //*.*.* + //*.2.* + //1.*.* + //1.1.* + //1.1.1 + //2.2.2 + //2.2.* + int nCompare = 0; + nCompare = this._MajorVersion.CompareTo(v._MajorVersion); + if(nCompare == 0) + nCompare = this._MinorVersion.CompareTo(v._MinorVersion); + if(nCompare == 0) + nCompare = this._BuildNumber.CompareTo(v._BuildNumber); + if (nCompare == 0) + nCompare = this._RevisionNumber.CompareTo(v._BuildNumber); + return nCompare; + } + else + { + throw new ArgumentException("object is not a Versioning"); + } + } + + #endregion + + #region IVersionSupported Members + + /// + /// Returns true if the Version String passed in is supported/matches Versioning for this object + /// + /// a Versioning string to validate against + /// true if supported, false otherwise + public bool IsSupported(string Version) + { + Versioning version = new Versioning(Version); + return IsSupported(version); + } + + /// + /// Returns true if the Version passed in is supported/matches Versioning for this object + /// + /// a Versioning Object to validate against + /// true if supported, false otherwise + public bool IsSupported(Versioning Version) + { + if (Version != null) + { + int nCompare = this.CompareTo(Version); + if (nCompare == 0) + { + return true; + } + else if (nCompare == 1) + { + return false; + } + else if (nCompare == -1) + { + if (((this._MajorVersion == STAR_ALL) || (this._MajorVersion == Version._MajorVersion)) && + ((this._MinorVersion == STAR_ALL) || (this._MinorVersion == Version._MinorVersion)) && + ((this._BuildNumber == STAR_ALL) || (this._BuildNumber == Version._BuildNumber)) && + ((this._RevisionNumber == STAR_ALL) || (this._RevisionNumber == Version._RevisionNumber)) + ) + { + return true; + } + else + { + return false; + } + } + } + return false; + } + + /// + /// Returns true if the Version passed in is supported/matches Versioning for this object + /// + /// a Version Object to Validate against + /// true if supported, false otherwise + public bool IsSupported(Version Version) + { + Versioning version = new Versioning(Version.Major, Version.Minor, Version.Build, Version.Revision); + return true; + } + + #endregion + } + + /// + /// Allows us to easily parse/sort/search multiple versioning classes + /// + public static class Versionings + { + /// + /// Used to parse multiple ";" seperated versions from a string + /// + /// single/multiple version strings, seperated by ";", in the format [Major].[Minor].[Build].[Revision], like 5.3.3.1 or 5.4.*.* + /// a sorted array of versionings or null if error occured + public static Versioning[] ParseVersions(string strVersions) + { + if (!Versioning.IsValidVersionStr(strVersions)) + return null; + + List RetValueVersions = new List(); + string[] Versions = strVersions.Split(';'); + foreach (string Version in Versions) + RetValueVersions.Add(new Versioning(Version)); + + RetValueVersions.Sort(); + return RetValueVersions.ToArray(); + } + + #region IsSupportedFile + + /// + /// Use this to find out if a File Version is supported by the passed in Versions string + /// + /// single/multiple version strings, seperated by ";", in the format [Major].[Minor].[Build], like 5.3.3 or 5.4.* + /// Full File Name and Path + /// true if the File Version is supported by the Versions string + public static bool IsSupportedFile(string strVersions, string FileNameNPath) + { + return IsSupported(ParseVersions(strVersions), Versioning.GetFileVersioning(FileNameNPath)); + } + + /// + /// Use this to find out if a File Version is supported by the passed in Versions [] + /// + /// single/multiple versioning objects + /// Full File Name and Path + /// true if the File Version is supported by the Versions string + public static bool IsSupportedFile(Versioning[] Versions, string FileNameNPath) + { + return IsSupported(Versions, Versioning.GetFileVersioning(FileNameNPath)); + } + + #endregion + + #region IsSupported + + /// + /// Pass in a Versions string, and a Version to check against. Returns true, if the Version is IVersionSupported by + /// the passed in Versions string. + /// + /// single/multiple version strings, seperated by ";", in the format [Major].[Minor].[Build].[Revision], like 5.3.3 or 5.4.* + /// any version string in the format [Major].[Minor].[Build], like 5.3.3 or 5.4.* + /// true, if a Versioning Object in the Versions returns true for IVersionSupported, false otherwise + public static bool IsSupported(string strVersions, string strVersion) + { + return IsSupported(ParseVersions(strVersions), new Versioning(strVersion)); + } + + /// + /// Pass in a single/multipe Versions object, and a Version object to check against. Returns true, if the Version is IVersionSupported by + /// the passed in Versions Objects. + /// + /// single/multiple versioning objects + /// single versioning object + /// true, if a Versioning Object in the Versions returns true for IVersionSupported, false otherwise + public static bool IsSupported(Versioning[] Versions, Versioning Version) + { + // Let IVersionSupported do all the work + foreach (Versioning _version in Versions) + { + if (_version.IsSupported(Version)) + return true; + } + return false; + } + + /// + /// Pass in a single/multipe Versions object, and a Version object to check against. Returns true, if the Version is IVersionSupported by + /// the passed in Versions Objects. + /// + /// single/multiple versioning objects + /// single version object + /// true, if a Versioning Object in the Versions returns true for IVersionSupported, false otherwise + public static bool IsSupported(Versioning[] Versions, Version Version) + { + // Let IVersionSupported do all the work + foreach (Versioning _version in Versions) + { + if (_version.IsSupported(Version)) + return true; + } + return false; + } + + #endregion + } +} + diff --git a/Process/PStartInfo.cs b/Process/PStartInfo.cs new file mode 100644 index 0000000..3ed3aa8 --- /dev/null +++ b/Process/PStartInfo.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics; +using System.IO; +using Yaulw.Tools; + +namespace Yaulw.Process +{ + /// + /// Helper Class to create ProcessStartInfo Objects + /// + public static class PStartInfo + { + /// + /// Creates a simple ProcessStartInfo Object + /// + /// Exe File and Path to execute + /// CmdLine Params to process, optional + /// If true, will set "RunAs" For the Process, to Run as Administrator + /// the windows style to initialize main window with + /// True to use Explorer.exe shellexecute, false otherise + /// a ProcessStartInfo Object or null if error occured + public static ProcessStartInfo CreateProcess(string ExeFileNPath, string CmdLine = "", string WorkingDir = "", bool bUseRunAs = false, ProcessWindowStyle WindowStyle = ProcessWindowStyle.Normal, bool bUseShellExecute = true) + { + if (!String.IsNullOrEmpty(ExeFileNPath) && System.IO.File.Exists(ExeFileNPath)) + { + ProcessStartInfo startInfo = new ProcessStartInfo(ExeFileNPath, CmdLine); + startInfo.WindowStyle = WindowStyle; + startInfo.UseShellExecute = bUseShellExecute; + if (bUseRunAs) + startInfo.Verb = "runas"; + + // Set up Working Directory, if one is found + if(!String.IsNullOrEmpty(WorkingDir)) + startInfo.WorkingDirectory = PathNaming.PathEndsWithSlash(WorkingDir); + else + startInfo.WorkingDirectory = Path.GetDirectoryName(ExeFileNPath); + return startInfo; + } + return null; + } + + /// + /// Creates an url ProcessStartInfo Object + /// + /// url to launche + /// a ProcessStartInfo Object or null if error occured + public static ProcessStartInfo LaunchUrl(string url) + { + if (!String.IsNullOrEmpty(url)) + { + ProcessStartInfo startInfo = new ProcessStartInfo(url); + startInfo.UseShellExecute = true; + return startInfo; + } + return null; + } + + /// + /// Creates a CMD.Exe CommandLine Executable ProcessStartInfo Object + /// + /// Full FileName and Path to script file to execute via CMD.exe + /// If true, will set "RunAs" For the Process, to Run as Administrator + /// a ProcessStartInfo Object or null if error occured + public static ProcessStartInfo CreateCMDScriptProcess(string scriptFileNPath, bool bUseRunAs = false) + { + if (!String.IsNullOrEmpty(scriptFileNPath) && System.IO.File.Exists(scriptFileNPath)) + { + //The "/C" Tells Windows to Run The Command then Terminate + string strCmdLine = "/C " + '\"' + scriptFileNPath + '\"'; + string WindowsSystem32Folder = System.Environment.GetFolderPath(Environment.SpecialFolder.System); + ProcessStartInfo startInfo = new ProcessStartInfo((WindowsSystem32Folder + "\\" + "CMD.exe"), strCmdLine); + //startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; + startInfo.CreateNoWindow = true; + startInfo.UseShellExecute = false; + if (bUseRunAs) + startInfo.Verb = "runas"; + startInfo.WorkingDirectory = Path.GetDirectoryName(scriptFileNPath); + return startInfo; + } + return null; + } + + /// + /// Creates a CMD.Exe CommandLine Executable ProcessStartInfo Object + /// + /// Dos Command to Execute + /// If true, will set "RunAs" For the Process, to Run as Administrator + /// a ProcessStartInfo Object or null if error occured + public static ProcessStartInfo CreateCMDDosCommandProcess(string DosCommand, bool bUseRunAs = false) + { + if (!String.IsNullOrEmpty(DosCommand)) + { + //The "/C" Tells Windows to Run The Command then Terminate + string strCmdLine = "/C " + '\"' + DosCommand + '\"'; + string WindowsSystem32Folder = System.Environment.GetFolderPath(Environment.SpecialFolder.System); + ProcessStartInfo startInfo = new ProcessStartInfo((WindowsSystem32Folder + "\\" + "CMD.exe"), strCmdLine); + //startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; + startInfo.CreateNoWindow = true; + startInfo.UseShellExecute = false; + if (bUseRunAs) + startInfo.Verb = "runas"; + return startInfo; + } + return null; + } + } +} diff --git a/Process/PStarter.cs b/Process/PStarter.cs new file mode 100644 index 0000000..0ad258a --- /dev/null +++ b/Process/PStarter.cs @@ -0,0 +1,274 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics; +using System.IO; + +namespace Yaulw.Process +{ + /// + /// Used to Manage Launching/Killing Processes In an Application + /// + public static class PStarter + { + #region Private Static Members + private static Dictionary _StartedProcesses = new Dictionary(); + #endregion + + #region Public Process Starter Methods + + /// + /// Use this to call a DosCommand in a hidden command prompt and have the command return + /// you a value back, read from standard output + /// + /// processStartInfo + /// true to wait till process ends, false otherwise + /// the PID of the the newly started Process, or 0 if an error occured + public static string RunDosCommand(string DosCommand) + { + ProcessStartInfo processStart = PStartInfo.CreateCMDDosCommandProcess(DosCommand, true); + processStart.RedirectStandardOutput = true; + string output = String.Empty; + if ((processStart != null) && System.IO.File.Exists(processStart.FileName)) + { + // Start the Process + try + { + System.Diagnostics.Process p1; + p1 = System.Diagnostics.Process.Start(processStart); + output = p1.StandardOutput.ReadToEnd(); + p1.WaitForExit(); + if (String.IsNullOrEmpty(output)) + output = p1.StandardOutput.ReadToEnd(); + } + catch (Exception e) { string msg = e.Message; /* ignore */ } + } + return output; + } + + + /// + /// Always Starts a New Process + /// + /// processStartInfo + /// true to wait till process ends, false otherwise + /// true to check if the process of the same name has already been started, false otherwise + /// the PID of the the newly started Process, or 0 if an error occured + //public static uint StartProcess(ProcessStartInfo processStart, bool bWait = false, bool bMustBeUnique = false) + public static uint StartProcess(ProcessStartInfo processStart, bool bWait, bool bMustBeUnique) + { + if ((processStart != null) && System.IO.File.Exists(processStart.FileName)) + { + // Enforce Uniqueness * DISABLED * We can't ENFORCE UNIQUENESS WITHOUT ALSO INCLUDING COMMAND-LINE PARAMETERS + // ~TO DO SOMETIME LATER * DOESN"T MATTER HERE * ALREADY HANDLED BY CALLER ANYWAY * + //if (bMustBeUnique && ContainsProcessExe(processStart.FileName)) + //{ + // return 0; + //} + //else if (bMustBeUnique) + //{ + // // Iterate the system's Processes's and make sure that the same process doesn't exist + // System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcessesByName(Path.GetFileNameWithoutExtension(processStart.FileName)); + // foreach (System.Diagnostics.Process process in processes) + // { + // // Found, return 0 + // if (String.Compare(process.MainModule.FileName, processStart.FileName, true) == 0) + // return 0; + // } + //} + + // Start the Process + try + { + System.Diagnostics.Process p1; + p1 = System.Diagnostics.Process.Start(processStart); + + // DISABLED + // Map if for later use + //_StartedProcesses.Add((uint)p1.Id, processStart.FileName.ToLower()); + + // Wait if asked + if (bWait) + p1.WaitForExit(); + + return (uint)p1.Id; + } + catch (Exception) { /* ignore */ } + } + return 0; + } + + /// + /// Kills any process started thru PStarter, if it matches the Passed in PID + /// + /// The Pid of the Process to Stop + /// true to only shutdown a process that was started by PStarted + /// The time in Seconds to wait after closing the Process + /// true to try sending WM_CLOSE message to Window prior Killing Process + /// The time in Seconds to wait after sending WM_CLOSE message for Process to Close * Only takes effect if bTryClosingMainWindowFirst is true * + /// true if process stopped, false otherwise + //public static bool KillProcess(uint PID, bool bMustBeHaveBeenStartedByUs = false, uint CloseWaitTimeInSeconds = 1, bool bTryClosingMainWindowFirst = true, uint MainWindowCloseWaitTimeInSeconds = 2) + public static bool KillProcess(uint PID, bool bMustBeHaveBeenStartedByUs, uint CloseWaitTimeInSeconds, bool bTryClosingMainWindowFirst, uint MainWindowCloseWaitTimeInSeconds) + { + + // Valid Process Id + bool bValidToClose = (PID != 0); + + // Make sure that PStarted Started This Process + if (bValidToClose && bMustBeHaveBeenStartedByUs) + bValidToClose = _StartedProcesses.ContainsKey(PID); + + // Is this process valid to close? + if (bValidToClose) + { + System.Diagnostics.Process p1 = null; + // Try getting the Process + try { p1 = System.Diagnostics.Process.GetProcessById((int)PID); } + catch (Exception) {/* ignore */} + + // Process Found + if (p1 != null) + { + // If Main Window Exists, Try Closing this first + if (bTryClosingMainWindowFirst) + { + IntPtr hWnd = p1.MainWindowHandle; + if (hWnd == IntPtr.Zero) + { + // Try getting the window handle by Iterating TopLevel Windows + hWnd = Win32.Functions.GetFirstTopLevelWindowForProcess((int)PID); + } + + // If we have a valid Window Handle, try closing it nicely + if (hWnd != IntPtr.Zero && Win32.User32.IsWindow(hWnd)) + { + // Try the new school way && the old school way !!! + p1.CloseMainWindow(); + Win32.User32.PostMessage(hWnd, (int)Win32.Definitions.WM.WM_CLOSE, IntPtr.Zero, IntPtr.Zero); + + // Wait a little.. as specified by the configuration + System.Threading.Thread.Sleep((int)TimeSpan.FromSeconds(MainWindowCloseWaitTimeInSeconds).TotalMilliseconds); + + // Retry getting the Process *Should be closed by now* + try { p1 = null; p1 = System.Diagnostics.Process.GetProcessById((int)PID); } + catch (Exception) {/* ignore */} + } + } + + // Last Resort, Always try to Kill the Process + try { if (p1 != null) p1.Kill(); } + catch (Exception) { /* ignore */ } + + // Wait a little + System.Threading.Thread.Sleep((int)TimeSpan.FromSeconds(CloseWaitTimeInSeconds).TotalMilliseconds); + + // Retry getting the Process *Must be closed by now* + try { p1 = null; p1 = System.Diagnostics.Process.GetProcessById((int)PID); } + catch (Exception) {/* ignore */} + if (p1 == null) + { + // DISABLED + //_StartedProcesses.Remove(PID); + return true; + } + } + } + return false; + } + + #endregion + + #region Public Process Starter Helper Methods + + /// + /// Retrieves the PIDs that were started using PStarter + /// + /// an uint[] array with Pids, or an empty array if none found + public static uint[] AllStartedProcessIDs() + { + // DISABLED + //List Pids = new List(); + //foreach (int key in _StartedProcesses.Keys) + // Pids.Add((uint)key); + //return Pids.ToArray(); + return null; + } + + /// + /// Since when we start a Process and it can die/killed externally we wouldn't know, + /// PIDs #'s could be re-used by the OS, so we could accidentially kill a Process that isn't our own. + /// This functions allows a monitoring program to call in with currently running PIDs they know are good + /// on the running system, we can use these numbers and discard any PIDs internally that are no longer accurate + /// * This function will DELETE PIDs/Keys internally * call it only with is a complete list of all PIDs. + /// + /// an uint[] array with currently Running PIDs, that we can compare to our started PIDs + public static void PerformGeneralCleanupOfAllStartedProcessIDs(uint[] currentlyRunningPIDs) + { + // DISABLED + //if ((currentlyRunningPIDs != null) && (currentlyRunningPIDs.Length > 0)) + //{ + // // Iterate thru the Internal DS and track any PID Keys that are no longer valid + // List RunningPidKeysToRemove = new List(); + // foreach (uint key in _StartedProcesses.Keys) + // { + // if (!currentlyRunningPIDs.Contains(key)) + // RunningPidKeysToRemove.Add(key); + // } + + // // Remove * no longer valid * PID / Keys + // foreach (uint keyToRemove in RunningPidKeysToRemove) + // _StartedProcesses.Remove(keyToRemove); + //} + } + + /// + /// Returns true if the ProcessExe was started with PStarter, False otherwise + /// + /// Process Executable Filename and Path + /// true, if found, false otherwise + public static bool ContainsProcessExe(string FileName) + { + return _StartedProcesses.ContainsValue(FileName.ToLower()); + } + + /// + /// Get's the first PID that matches the Given Exe Process FileName + /// + /// Process Executable Filename and Path + /// a Valid PID or 0 if not found + public static uint GetFirstPIDForProcessExe(string FileName) + { + if (ContainsProcessExe(FileName)) + { + foreach (uint key in _StartedProcesses.Keys) + { + if (String.Compare(_StartedProcesses[key], FileName, true) == 0) + return key; + } + } + return 0; + } + + /// + /// Get's all the PIDs that matches the Given Exe Process FileName + /// + /// Process Executable Filename and Path + /// valid PIDs or empty PID array, if not found + public static uint[] GetPIDsForProcessExe(string FileName) + { + List foundProcess = new List(); + if (ContainsProcessExe(FileName)) + { + foreach (uint key in _StartedProcesses.Keys) + { + if (String.Compare(_StartedProcesses[key], FileName, true) == 0) + foundProcess.Add(key); + } + } + return foundProcess.ToArray(); + } + + #endregion + } +} diff --git a/Process/ProcessW.cs b/Process/ProcessW.cs new file mode 100644 index 0000000..c9fbdb6 --- /dev/null +++ b/Process/ProcessW.cs @@ -0,0 +1,309 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Diag = System.Diagnostics; +using System.Management; + +namespace Yaulw.Process +{ + /// + /// Wrapper Class around System.Diagnostics's Process Class. + /// Helper functions to make sure that we can safely interact with Processes. + /// + public static class ProcessW + { + #region Process Caching + + /// + /// Process PID Lookup Cache - so that we can cache PIDs, we don't make a new system call + /// Use this Property to Cache Process PIDs Lookups + /// + public static List ProcessPidsRunningCache { get { return s_RunningProcessesPids; } } + private static List s_RunningProcessesPids = new List(); + + /// + /// Process Obj Lookup Cache - so that for processes with the same name, we don't make a new system call + /// Use this Property to Cache Process Obj Lookups + /// + public static Dictionary> ProcessObjsGetterCache { get { return s_processGetterCache; } } + private static Dictionary> s_processGetterCache = new Dictionary>(); + + /// + /// Clears the Process Obj Lookup Cache - Used in conjuction with AllRunningProcessesOf() + /// + public static void ClearProcessObjsGetterCache() { s_processGetterCache.Clear(); } + + #endregion + + #region Query For Processes + + /// + /// Safe GetProcessById Function, to make sure that it returns a valid Process Object + /// + /// the PID to get the Process Object for + /// a valid Process obj, or null + /// true if successful, false otherwise + public static bool GetProcessById(int PID, out Diag.Process p) + { + p = null; + try + { + p = Diag.Process.GetProcessById(PID); + return (p != null); + } + catch (Exception) { /*ignore */ } + return false; + } + + /// + /// Quick Check to see if other Process of that Name Are running + /// + /// name of process + /// set to true to ignore Visual Studio's host .exe (debugger process) + /// true, if only one Process of that Name is running, false otherwise + public static bool IsTheOnlyProcessRunning(string ProcessName, bool bIgnoreVSHost = true) + { + if (!string.IsNullOrEmpty(ProcessName)) + { + // Visual Studio could be running this... + int nLength1 = Diag.Process.GetProcessesByName(ProcessName).Length; + int nLength2 = (bIgnoreVSHost)? 0 : Diag.Process.GetProcessesByName(ProcessName + ".vshost").Length; + int nProcessCount = nLength1 + nLength2; + return (nProcessCount == 0 || nProcessCount == 1); + } + return true; + } + + /// + /// Use this to quickly retrieve all Running PID on this machine + /// * Excellent performance 10 MS * + /// + /// returns all running Pids + public static List AllRunningPids() + { + Diag.Process[] processes = Diag.Process.GetProcesses(); + List allRunningPids = new List(); + if (allRunningPids != null) + { + foreach (Diag.Process p in processes) + allRunningPids.Add((uint)p.Id); + } + return allRunningPids; + } + + /// + /// Returns all Processes of that ProcessName + /// + /// name of process + /// If true, for .exe's in the Cache will return immediatly without a system call. Use ClearProcessObjsGetterCache() to clear Cache. + /// set to true to ignore Visual Studio's host .exe (debugger process) + /// Returns all processes that match the process name, or empty array, if none found + public static Diag.Process[] AllRunningProcessesOf(string ProcessName, bool bUseLookupCache = false, bool bIgnoreVSHost = true) + { + if (!string.IsNullOrEmpty(ProcessName)) + { + try + { + // Immediatly Return from Cache the Process Getter Calls * Performance Improvement * + if (bUseLookupCache && s_processGetterCache.ContainsKey(ProcessName)) + return s_processGetterCache[ProcessName].ToArray(); + + // If a lookup cache is used, we won't deal with the complexity of also dealing with VSHost, + // so ignore this setting + if (bIgnoreVSHost && bUseLookupCache) + bIgnoreVSHost = true; + + // Running Processes found + List runningProcess = new List(); + + // Regular Process Lookup... + Diag.Process[] processes = Diag.Process.GetProcessesByName(ProcessName); + if (processes != null && processes.Length > 0) + { + foreach (Diag.Process p in processes) + runningProcess.Add(p); + } + + // Visual Studio could be running this... + if (!bIgnoreVSHost) + { + processes = Diag.Process.GetProcessesByName(ProcessName + ".vshost"); + if (processes != null && processes.Length > 0) + { + foreach (Diag.Process p in processes) + runningProcess.Add(p); + } + } + + // Immediatly Cache the Process Getter Calls + if (bUseLookupCache && !s_processGetterCache.ContainsKey(ProcessName)) + { + s_processGetterCache[ProcessName] = new List(); + s_processGetterCache[ProcessName].AddRange(runningProcess); + } + + // Return the found Processes + return runningProcess.ToArray(); + } + catch (Exception) { /* ignore */ } + } + return new Diag.Process[0]{}; // return empty array + } + + #endregion + + #region Process Exe FileName N CommandLine Getters + + /// + /// Uses WMI to retrieve Process's FileNameNPath and the Command-Line Parameters for the specified Process + /// + /// a valid Pid to retrieve Command-line and Process FileName for + /// passing out full process exe file name and path for pid, if found + /// passing out CommandLine params for the Pid, if found + /// true, if the process was found and at least ProcessFileNameNPath was written out, false otherwise + public static bool GetCommandLineArgumentsForProcessPID(int ProcessId, out string ProcessFileNameNPath, out string CommandLineParams) + { + ProcessFileNameNPath = String.Empty; + CommandLineParams = String.Empty; + try + { + // Use WMI to retrieve the info we need (.net does not provide any other way, it appears) + string wmiQuery = string.Format("SELECT CommandLine from Win32_Process WHERE ProcessId = {0}", ProcessId); + ManagementObjectSearcher searcher = new ManagementObjectSearcher(wmiQuery); + ManagementObjectCollection retObjectCollection = searcher.Get(); + if ((retObjectCollection != null) && (retObjectCollection.Count > 0)) + { + foreach (ManagementObject retObject in retObjectCollection) // This will always be only 1 * retObjectCollection doesn't implement [] + { + if (retObject != null) // to be even more save... + { + string strLocal = String.Empty; + if (retObject["CommandLine"] != null && !String.IsNullOrEmpty(retObject["CommandLine"].ToString())) + strLocal = retObject["CommandLine"].ToString().Trim(); + + if (!String.IsNullOrEmpty(strLocal)) + { + // This should work + int firstQuotIndex = strLocal.IndexOf('\"'); + int SecondQuotIndex = (firstQuotIndex >= 0) ? strLocal.IndexOf('\"', firstQuotIndex + 1) : -1; + + // Pass out * if we at least have a Process FileNameNPath * + if (firstQuotIndex > -1 && SecondQuotIndex > -1) + { + ProcessFileNameNPath = Win32.Functions.GetLongFileNameNPathOrPath(strLocal.Substring(firstQuotIndex + 1, SecondQuotIndex - 1)); + CommandLineParams = (strLocal.Length > SecondQuotIndex + 2) ? strLocal.Substring(SecondQuotIndex + 2) : String.Empty; + return true; + } + } + } + } + } + } + catch (Exception) { /* ignore */ } + return false; + } + + /// + /// Usefull for making sure that the exact Process Image (including Command-Line Prms) is currently in a running state. + /// + /// Pass in a list of Process[] where to search for ProcessFileNameNPath and CommandLineParams + /// The exact Process FileName and Path (Image .exe) to find match for + /// The exact Command-Line Parameters to find match for + /// a PID hint * to speed up look * caller can pass in a PID that 'most likely' is the PID we are looking for (Can be zero) + /// If we found the process match, we'll pass out the process to the caller (runningProcesses[]), null otherwise + /// If we found the process match, we'll pass out the index to the caller (runningProcesses[]), -1 otherwise + /// True, if exact Match was found, False Otherwise + public static bool FoundExactProcessExeNCommandLinePrmsInRunningProcesses(Diag.Process[] runningProcesses, string ProcessFileNameNPath, string CommandLineParams, int PID_Hint, out Diag.Process process, out int indexFound) + { + process = null; + indexFound = -1; + try + { + if (runningProcesses == null || runningProcesses.Length == 0) + return false; + + if (PID_Hint < 0) + PID_Hint = 0; + + bool bFoundProcessRunning = false; + + // * For Performance Reasons * ProcessW.GetCommandLineArgumentsForProcessPID() takes a long time, + // Use PID_Hint first, to check if that PID is it + if (PID_Hint > 0) + { + for (int i = 0; i < runningProcesses.Length; ++i) + { + Diag.Process rProcess = runningProcesses[i]; + if (rProcess != null && (rProcess.Id == PID_Hint)) + { + string ProcessFileNameNPath_Local; + string CommandLineParams_Local; + if (ProcessW.GetCommandLineArgumentsForProcessPID(rProcess.Id, out ProcessFileNameNPath_Local, out CommandLineParams_Local)) + { + // See if we find a match + if (ProcessFileExeNCommandLineParamsComparer_FoundMatch(ProcessFileNameNPath_Local, CommandLineParams_Local, ProcessFileNameNPath, CommandLineParams)) + { + bFoundProcessRunning = true; + process = rProcess; + indexFound = i; + } + } + break; + } + } + } + + // PID_Hint Worked, no need to continue... + if (bFoundProcessRunning) + return bFoundProcessRunning; + + // Iterate all others + for (int i = 0; i < runningProcesses.Length; ++i) + { + Diag.Process rProcess = runningProcesses[i]; + if (rProcess != null && (rProcess.Id != PID_Hint)) // Ignore PID_Hint * already been tried * + { + string ProcessFileNameNPath_Local; + string CommandLineParams_Local; + if (ProcessW.GetCommandLineArgumentsForProcessPID(rProcess.Id, out ProcessFileNameNPath_Local, out CommandLineParams_Local)) + { + // See if we find a match + if (ProcessFileExeNCommandLineParamsComparer_FoundMatch(ProcessFileNameNPath_Local, CommandLineParams_Local, ProcessFileNameNPath, CommandLineParams)) + { + bFoundProcessRunning = true; + process = rProcess; + indexFound = i; + break; + } + } + } + } + return bFoundProcessRunning; + } + catch (Exception) { /* ignore */ } + return false; + } + + /// + /// Returns true if the passed in ProcessExeFileNameNPath, CommandLinePrms match _Left and _Right + /// + /// Process FileNameNPath to compare to Right + /// CommandLine Parameters to compare to Right + /// Process FileNameNPath to compare to Left + /// CommandLine Parameters to compare to Left + /// true if _left and _right match, false otherwise + public static bool ProcessFileExeNCommandLineParamsComparer_FoundMatch(string ProcessFileNameNPath_Left, string CommandLineParams_Left, string ProcessFileNameNPath_Right, string CommandLineParams_Right) + { + if ((String.Compare(ProcessFileNameNPath_Left.Trim(), ProcessFileNameNPath_Right.Trim(), true) == 0) && + (String.Compare(CommandLineParams_Left.Trim(), CommandLineParams_Right.Trim(), true) == 0)) + { + return true; + } + return false; + } + + #endregion + } +} diff --git a/Process/ServiceW.cs b/Process/ServiceW.cs new file mode 100644 index 0000000..f1db082 --- /dev/null +++ b/Process/ServiceW.cs @@ -0,0 +1,212 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ServiceProcess; +using Yaulw.Win32; +using System.Runtime.InteropServices; + +namespace Yaulw.Process +{ + /// + /// Wrapper Class around System.ServiceProcess's ServiceControl Class, + /// ~Little helper functions to make sure that we can safely interact with Services + /// + public static class ServiceW + { + /// + /// Checks to see if the service exists on the local computer + /// + /// Name of Service to Check + /// true if it exists, false otherwise + public static bool DoesServiceExist(string ServiceName) + { + try + { + ServiceController scm = new ServiceController(ServiceName); + if (scm != null && !String.IsNullOrEmpty(scm.ServiceName)) + { + scm.Dispose(); + return true; + } + } + catch (Exception) { /* ignore */ } + return false; + } + + /// + /// Quick check to see if the service is running + /// + /// Name of Service to Check + /// true if the service is running, false otherwise, if it doesn't exist returns true + public static bool IsServiceRunning(string ServiceName) + { + ServiceController scm = null; + try + { + scm = new ServiceController(ServiceName); + } + catch (Exception) { /* ignore */ } + + if (scm != null) + { + bool bIsRunning = (scm.Status == ServiceControllerStatus.Running); + scm.Dispose(); + return bIsRunning; + } + else + return false; + } + + /// + /// Use this to Restart the Service + /// + /// Name of Service to Check + /// Set to true to try to force a Service Close by killing the Process, if the service is hanging + /// Number of Seconds to wait for Service To Close/Start + /// Number of Seconds to wait inbetween closing and starting + /// true if successful, false otherwise + public static bool RestartService(string ServiceName, bool bKillServiceByForceIfNeedBe = true, int WaitTimeoutInSeconds = 240, int nStartupDelayInSeconds = 2) + { + // Stop the Service + if (!StopService(ServiceName, bKillServiceByForceIfNeedBe, WaitTimeoutInSeconds)) + return false; + + // Delay Starting the Services for 'n' seconds + if (nStartupDelayInSeconds > 0) + System.Threading.Thread.Sleep(TimeSpan.FromSeconds(nStartupDelayInSeconds)); + + // Start the Service + bool bIsStarted = StartService(ServiceName, WaitTimeoutInSeconds); + return bIsStarted; + } + + /// + /// Use this to start a service + /// + /// Name of Service to Check + /// Number of seconds to wait for Service to Start + /// true if successful, false otherwise + public static bool StartService(string ServiceName, int WaitTimeoutInSeconds = 240) + { + ServiceController scm = null; + try + { + scm = new ServiceController(ServiceName); + + // Wait for 'Stopped' State + if (scm.Status != ServiceControllerStatus.Running) + { + scm.Start(); + if (WaitTimeoutInSeconds > 0) + scm.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(WaitTimeoutInSeconds)); + } + } + catch (Exception) { /* ignore */ } + + if (scm != null) + { + bool bSuccess = (scm.Status == ServiceControllerStatus.Running); + scm.Dispose(); + return bSuccess; + } + else + return false; + } + + /// + /// Use this to stop a service + /// + /// Name of Service to Check + /// Set to true to try to force a Service Close by killing the Process, if the service is hanging + /// Number of seconds to wait for Service to Stop + /// true if successful, false otherwise + public static bool StopService(string ServiceName, bool bKillServiceByForceIfNeedBe = true, int WaitTimeoutInSeconds = 240) + { + ServiceController scm = null; + try + { + scm = new ServiceController(ServiceName); + + // Wait for 'Stopped' State + if (scm.Status != ServiceControllerStatus.Stopped) + { + scm.Stop(); + if (WaitTimeoutInSeconds > 0) + scm.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(WaitTimeoutInSeconds)); + } + } + catch (Exception) { /* ignore */ } + + try + { + if (scm != null) + { + bool bSuccess = (scm.Status == ServiceControllerStatus.Stopped); + + // Always try Killing Service By Force, even if bSuccess is true, + // just to try to guarantee that the service is dead for good + if (bKillServiceByForceIfNeedBe) + bSuccess = StopServiceByForce(scm) || bSuccess; + + scm.Dispose(); + return bSuccess; + } + else + return false; + } + catch (Exception) { /* ignore */ } + return false; + } + + /// + /// Brutal way to stop a service * Not Recommended *, + /// according to article: + /// + /// + /// + /// a Service Controller Object whose PID you would like to kill + /// true if successfull, false otherwise + private static bool StopServiceByForce(ServiceController scm) + { + Structures.SERVICE_STATUS_PROCESS ssp = new Structures.SERVICE_STATUS_PROCESS(); + int ignored; + + try + { + // Obtain information about the service, and specifically its hosting process, + // from the Service Control Manager. + if (!Advapi32.QueryServiceStatusEx(scm.ServiceHandle.DangerousGetHandle(), Definitions.SC_STATUS_PROCESS_INFO, ref ssp, Marshal.SizeOf(ssp), out ignored)) + { + //throw new Exception("Couldn't obtain service process information."); + return false; + } + + // A few quick sanity checks that what the caller wants is *possible*. + if (ssp.dwServiceType != Definitions.SERVICE_WIN32_OWN_PROCESS) + { + //throw new Exception("Can't wait for the service's hosting process to exit because there may be multiple services in the process (dwServiceType is not SERVICE_WIN32_OWN_PROCESS"); + return false; + } + + if ((ssp.dwServiceFlags & Definitions.SERVICE_RUNS_IN_SYSTEM_PROCESS) != 0) + { + //throw new Exception("Can't wait for the service's hosting process to exit because the hosting process is a critical system process that will not exit (SERVICE_RUNS_IN_SYSTEM_PROCESS flag set)"); + return false; + } + + if (ssp.dwProcessId == 0) + { + //throw new Exception("Can't wait for the service's hosting process to exit because the process ID is not known."); + return false; + } + + // Try closing the Process Manually + bool bCloseSuccess = PStarter.KillProcess((uint)ssp.dwProcessId, false, 1, false, 2); + return bCloseSuccess; + } + catch (Exception) { /* ignore */ } + return false; + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..cc3606a --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Yaulw")] +[assembly: AssemblyDescription("Yet Another Utility Library for .Net Windows")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Ideas To Action, Inc.")] +[assembly: AssemblyProduct("Yaul")] +[assembly: AssemblyCopyright("Copyright Daniel A. Romischer © 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2f7e3593-1fc7-45d8-b2f6-7e51d5161763")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Registry/RegKey.cs b/Registry/RegKey.cs new file mode 100644 index 0000000..186ec95 --- /dev/null +++ b/Registry/RegKey.cs @@ -0,0 +1,487 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Win32; + +using Yaulw.Tools; + +namespace Yaulw.Registry +{ + #region RegKey Enums + + /// + /// Registry Root Key + /// + public enum HKEYRoot + { + ClassesRoot, + CurrentUser, + LocalMachine, + } + + /// + /// Registry Sub Key + /// + public enum KeySub + { + Software, + Custom + } + + #endregion + + /// + /// Wrapper Class around Registry Functionallity, + /// allows for easy reading/writing to the registry, especially HKEY_CURRENT_USER\Software + /// ~If you use this class directly make sure to call dispose(), + /// otherwise, use the Public Static Helper Functions to do the work for you + /// + public class RegKey : IDisposable + { + #region Private Members + + private RegistryKey _RegKey; + private bool _disposed = false; + + #endregion + + #region Constructors + + /// + /// Use this to Open or Automatically Create the specified Key in CURRENT_USER\Software\Key. + /// + /// Specify Key to Open + /// Set to true to Open, false to Create if not Exists + public RegKey(string Key, bool bReadOnly = true) : this(HKEYRoot.CurrentUser, KeySub.Software, Key, bReadOnly) {} + + /// + /// Use this to Open or Automatically Create a Registry Key + /// + /// specify registry root + /// specify a sub or custom + /// key you want to open / create + /// true if you want to force only reading it + private RegKey(HKEYRoot root, KeySub sub, string Key, bool bReadOnly) + { + string KeyToOpen = String.Empty; + if (sub == KeySub.Custom) + KeyToOpen = Key; + else + KeyToOpen = sub.ToString() + "\\" + Key; + + // Read Only Permission + RegistryKeyPermissionCheck permission = RegistryKeyPermissionCheck.ReadSubTree; + if (!bReadOnly) + permission = RegistryKeyPermissionCheck.ReadWriteSubTree; + + // Open or Create, if it doesn't exist and we have writable set + switch (root) + { + case HKEYRoot.ClassesRoot: + if (bReadOnly) + _RegKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(KeyToOpen); + else + _RegKey = Microsoft.Win32.Registry.ClassesRoot.CreateSubKey(KeyToOpen, permission); + break; + + case HKEYRoot.CurrentUser: + if (bReadOnly) + _RegKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(KeyToOpen, permission); + else + _RegKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(KeyToOpen, permission); + break; + + case HKEYRoot.LocalMachine: + if (bReadOnly) + _RegKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(KeyToOpen, permission); + else + _RegKey = Microsoft.Win32.Registry.LocalMachine.CreateSubKey(KeyToOpen, permission); + break; + } + } + + /// + /// Finalizer + /// + ~RegKey() + { + Dispose(true); + } + + #endregion + + #region GetValue Registry Functions + + /// + /// Generic Registry KeyValue Getter Function + /// + /// Should be a System Type like string, bool, int32, double, decimal, etc... + /// The Value to get from the Registry + /// Default value to use if nothing was retrieved * Error occured * + /// Value found or Default Value + public T GetVal(string strKeyValue, T DefaultValue) + { + T retVal = DefaultValue; + try + { + if (_RegKey != null) + { + object keyvalue = _RegKey.GetValue(strKeyValue); + string strvalue = string.Empty; + if (ObjTool.IsNotNullAndNotEmpty(keyvalue)) + { + #region First Handle the Value as a string + + // Handle Multi-Strings + if (keyvalue is object[]) + { + string keyVal2 = string.Empty; + string[] strVal2 = (string[])keyvalue; + foreach (string str in strVal2) + { + keyVal2 += str; + } + strvalue = keyVal2; + } + else + { + // Handle Single-Strings + strvalue = keyvalue.ToString(); + } + + #endregion + + // Now cast the Object Return type * Handle each object differently * + retVal = ObjTool.ConvertStringToObj(strvalue); + } + } + } + catch (Exception) { /* ignore */ } + return retVal; + } + + /// + /// Binary Registry KeyValue Getter Function + /// + /// The Value to get from the Registry + /// an array of Bytes found in the Registry, or null if none found, or error occured + public byte[] GetVal(string strKeyValue) + { + byte[] retVal = null; + try + { + string StringValue = GetVal(strKeyValue, String.Empty); + if (!String.IsNullOrEmpty(StringValue)) + { + Byte[] ByteValue = new Byte[StringValue.Length]; + for (int i = 0; i < ByteValue.Length; ++i) + { + string strByte = System.Convert.ToString(StringValue[i]); + ByteValue[i] = byte.Parse(strByte); + } + retVal = ByteValue; + } + } + catch (Exception) { /* ignore */ } + return retVal; + } + + #endregion + + #region SetValue Registry Functions + + /// + /// Generic Registry KeyValue Setter Function + /// + /// Should be a System Type like string, bool, int32, double, decimal, etc... + /// The Value to write to in the Registry + /// The Value to set to the Registry + /// true if successful, false otherwise + public bool SetVal(string strKeyValue, T Value) + { + bool bRetVal = false; + try + { + if (_RegKey != null) + { + if (Value is System.Int32) + { + _RegKey.SetValue(strKeyValue, Value, RegistryValueKind.DWord); + } + else if (Value is System.Int64) + { + _RegKey.SetValue(strKeyValue, Value, RegistryValueKind.QWord); + } + else + { + _RegKey.SetValue(strKeyValue, Value, RegistryValueKind.String); + } + bRetVal = true; + } + } + catch (Exception) { /* ignore */ } + return bRetVal; + } + + /// + /// Generic Registry KeyValue Setter Function + /// + /// The Value to write to in the Registry + /// A binary array of bytes to write to the Registry + /// true if successful, false otherwise + public bool SetVal(string strKeyValue, byte[] ByteValue) + { + try + { + _RegKey.SetValue(strKeyValue, ByteValue, RegistryValueKind.Binary); + return true; + } + catch (Exception) { /* ignore */ } + return false; + } + + #endregion + + #region Delete Registry Function + + /// + /// Registry KeyValue Delete Function + /// + /// The Value to delete in the Registry + /// true if successful, false otherwise + public bool DeleteKey(string strKeyValue) + { + try + { + if (_RegKey != null) + { + _RegKey.DeleteValue(strKeyValue, false); + return true; + } + } + catch (Exception) { /* ignore */ } + return false; + } + + #endregion + + #region Public Static Helper Functions + + /// + /// Generic Static Registry Writer Function * Allows writing to HKEY\CURRENT_USER\SOFTWARE * + /// + /// Should be a System Type like string, bool, int32, double, decimal, etc... + /// SubKey i.e. Microsoft, Microsoft\Windows + /// Value to write to + /// Value to write + /// true if successful, false otherwise + public static bool SetKey(String SubKey, String KeyValue, T Value) + { + bool bRetVal = false; + using (RegKey reg = new RegKey(HKEYRoot.CurrentUser, KeySub.Software, SubKey, false)) + { + bRetVal = reg.SetVal(KeyValue, Value); + } + return bRetVal; + } + + /// + /// Generic Static Registry Writer For Binary Function * Allows writing to HKEY\CURRENT_USER\SOFTWARE * + /// + /// SubKey i.e. Microsoft, Microsoft\Windows + /// Value to write to + /// Binary Value to write + /// true if successful, false otherwise + public static bool SetKey(String SubKey, String KeyValue, byte[] ByteValue) + { + bool bRetVal = false; + using (RegKey reg = new RegKey(HKEYRoot.CurrentUser, KeySub.Software, SubKey, false)) + { + bRetVal = reg.SetVal(KeyValue, ByteValue); + } + return bRetVal; + } + + /// + /// Generic Static Registry Writer Function * Allows writing to any key * + /// + /// Should be a System Type like string, bool, int32, double, decimal, etc... + /// RootKey as defined in this Class + /// SubKey i.e. Software, Software\Microsoft + /// Value to write to + /// Value to write + /// true if successful, false otherwise + public static bool SetKey(HKEYRoot rootKey, String SubKey, String KeyValue, T Value) + { + bool bRetVal = false; + using (RegKey reg = new RegKey(rootKey, KeySub.Custom, SubKey, false)) + { + bRetVal = reg.SetVal(KeyValue, Value); + } + return bRetVal; + } + + /// + /// Generic Static Registry Writer For Binary Function * Allows writing to any key * + /// + /// RootKey as defined in this Class + /// SubKey i.e. Software, Software\Microsoft + /// Value to write to + /// Binary Value to write + /// true if successful, false otherwise + public static bool SetKey(HKEYRoot rootKey, String SubKey, String KeyValue, byte[] ByteValue) + { + bool bRetVal = false; + using (RegKey reg = new RegKey(rootKey, KeySub.Custom, SubKey, false)) + { + bRetVal = reg.SetVal(KeyValue, ByteValue); + } + return bRetVal; + } + + /// + /// Static Registry Delete Function * Allows Deleting from HKEY\CURRENT_USER\SOFTWARE * + /// + /// SubKey i.e. Microsoft, Microsoft\Windows + /// Value to delete + /// true if successful, false otherwise + public static bool DeleteKey(String SubKey, String KeyValue) + { + bool bRetVal = false; + using (RegKey reg = new RegKey(HKEYRoot.CurrentUser, KeySub.Software, SubKey, false)) + { + bRetVal = reg.DeleteKey(KeyValue); + } + return bRetVal; + } + + /// + /// Static Registry Delete Function * Allows Deleting any key * + /// + /// RootKey as defined in this Class + /// SubKey i.e. Software, Software\Microsoft + /// Value to delete + /// true if successful, false otherwise + public static bool DeleteKey(HKEYRoot rootKey, String SubKey, String KeyValue) + { + bool bRetVal = false; + using (RegKey reg = new RegKey(rootKey, KeySub.Custom, SubKey, false)) + { + bRetVal = reg.DeleteKey(KeyValue); + } + return bRetVal; + } + + /// + /// Generic Static Registry Reader Function * Allows reading from HKEY\CURRENT_USER\SOFTWARE * + /// + /// Should be a System Type like string, bool, int32, double, decimal, etc... + /// SubKey i.e. Microsoft, Microsoft\Windows + /// Value to write to + /// Default Value to return if none found or error occured + /// true if successful, false otherwise + public static T GetKey(String SubKey, String KeyValue, T DefaultValue) + { + T tRetVal = default(T); + using (RegKey reg = new RegKey(HKEYRoot.CurrentUser, KeySub.Software, SubKey, true)) + { + tRetVal = reg.GetVal(KeyValue, DefaultValue); + } + return tRetVal; + } + + /// + /// Generic Static Registry Reader For Binary Function * Allows reading from HKEY\CURRENT_USER\SOFTWARE * + /// + /// SubKey i.e. Microsoft, Microsoft\Windows + /// Value to write to + /// true if successful, false otherwise + public static Byte[] GetKey(String SubKey, String KeyValue) + { + Byte[] retByte = null; + using (RegKey reg = new RegKey(HKEYRoot.CurrentUser, KeySub.Software, SubKey, true)) + { + retByte = reg.GetVal(KeyValue); + } + return retByte; + } + + /// + /// Generic Static Registry Reader Function * Allows reading from any key * + /// + /// Should be a System Type like string, bool, int32, double, decimal, etc... + /// RootKey as defined in this Class + /// SubKey i.e. Software, Software\Microsoft + /// Value to write to + /// Default Value to return if none found or error occured + /// true if successful, false otherwise + public static T GetKey(HKEYRoot rootKey, String SubKey, String KeyValue, T DefaultValue) + { + T tRetVal = default(T); + using (RegKey reg = new RegKey(rootKey, KeySub.Custom, SubKey, true)) + { + tRetVal = reg.GetVal(KeyValue, DefaultValue); + } + return tRetVal; + } + + /// + /// Generic Static Registry Reader For Binary Function * Allows reading from any key * + /// + /// Should be a System Type like string, bool, int32, double, decimal, etc... + /// RootKey as defined in this Class + /// SubKey i.e. Software, Software\Microsoft + /// Value to write to + /// true if successful, false otherwise + public static Byte[] GetKey(HKEYRoot rootKey, String SubKey, String KeyValue) + { + Byte[] retByte = null; + using (RegKey reg = new RegKey(rootKey, KeySub.Custom, SubKey, true)) + { + retByte = reg.GetVal(KeyValue); + } + return retByte; + } + + #endregion + + #region IDisposable Members + + /// + /// Dispose the Registry Handle + /// + public void Dispose() + { + Dispose(true); + + // Use SupressFinalize in case a subclass + // of this type implements a finalizer + GC.SuppressFinalize(this); + } + + /// + /// Dispose the Registry Handle + /// + /// true, if called from within + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { +#if NET4 + if (disposing) + { + if (_RegKey != null) + _RegKey.Dispose(); // Works in .Net 4.0 Only + } +#endif + + // Indicate that the instance has been disposed. + _RegKey = null; + _disposed = true; + } + } + + #endregion + } +} diff --git a/Structs/sbstring.cs b/Structs/sbstring.cs new file mode 100644 index 0000000..f42bd2a --- /dev/null +++ b/Structs/sbstring.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Utilities.GenericUtilities.Structs +{ + /// + /// String Builder Wrapper struct in order to be able to use += + /// + public struct sbstring + { + // StringBuilder to wrapp + private StringBuilder _sb; + + + public static string operator+ (sbstring op1, string op2) + { + op1.SBInit(); + op1._sb.Append(op2); + return op1._sb.ToString(); + } + + //public static sbstring operator +(sbstring op1, string op2) + //{ + // op1._sb.Append(op2); + // return op1; + //} + + /// + /// Call this function to initialize the StringBuilder * must be called by all ops first * + /// + private void SBInit() { if (_sb == null) _sb = new StringBuilder(); } + } +} diff --git a/Thread/DispatcherThread.cs b/Thread/DispatcherThread.cs new file mode 100644 index 0000000..8333204 --- /dev/null +++ b/Thread/DispatcherThread.cs @@ -0,0 +1,507 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Sys = System.Threading; +using System.Reflection; +using Yaulw.WPF; +using System.Threading; +using System.Windows.Threading; +using Yaulw.Other; + +namespace Yaulw.Thread +{ + /// + /// + /// + public enum DispatcherThreatType + { + WPF, + // Winforms // ~Not implmented + // Silverlight? // ~Not implemented + } + + /// + /// + /// + public class WindowType + { + public Type T; + public Enum Key; + public bool IsSingleton; + public bool IsPerformanceLoaded; + + public WindowType(Type t, Enum Key, bool IsSingleton, bool IsPerformanceLoaded) + { + this.T = t; + this.Key = Key; + this.IsSingleton = IsSingleton; + this.IsPerformanceLoaded = IsPerformanceLoaded; + } + } + + /// + /// + /// + public class WindowAttributes + { + public IntPtr hWndOwner; + public uint Height; + public uint Width; + public int Top; + public int Left; + public string Title; + + public WindowAttributes(IntPtr hWndOwner, uint Height, uint Width, int Top, int Left, string Title) + { + this.hWndOwner = hWndOwner; + this.Height = Height; + this.Width = Width; + this.Top = Top; + this.Left = Left; + this.Title = Title; + } + + public WindowAttributes(IntPtr hWndOwner, int Top, int Left) + { + this.hWndOwner = hWndOwner; + this.Height = 0; + this.Width = 0; + this.Top = Top; + this.Left = Left; + this.Title = String.Empty; + } + + public WindowAttributes(IntPtr hWndOwner) + { + this.hWndOwner = hWndOwner; + this.Height = 0; + this.Width = 0; + this.Top = int.MinValue; + this.Left = int.MinValue; + this.Title = String.Empty; + } + + } + + #region Window Manager Definition + + /// + /// Abstract Base Class all Window Managers must inherit from. + /// + /// + public abstract class WindowManager : IDisposable + { + #region Private Members + + private Dictionary> s_WindowsToManage = new Dictionary>(); + + #endregion + + #region Construction + + /// + /// Construct a Window Manager Object + /// + /// Thread Dispatcher Object + /// Window Types this WindowManager is managing + /// true, to load performance enabled windows upon construction + public WindowManager(Dispatcher dispatcher, WindowType[] windowTypes, bool PerformanceLoad) + { + Dispatcher = dispatcher; + WindowTypes = windowTypes; + if (Dispatcher == null) + throw new ArgumentException("dispatcher can't be null"); + if (windowTypes == null || windowTypes.Length < 1) + throw new ArgumentException("windowTypes can't be null or empty"); + + // Add All Window Types + foreach (WindowType type in windowTypes) + { + // Add the Type to be managed + AddWindowTypeToManage(type); + + // perform performance optimization + if (PerformanceLoad && type.IsPerformanceLoaded) + { + object o = CreateInstance(type); + AddObject(type, o); + } + } + } + + #endregion + + #region Public Properties + + /// + /// Returns the Dispatcher Thread responsible for this Window Manager + /// + public Dispatcher Dispatcher { get; private set; } + + /// + /// Returns all the Window Types that have been set to be managed by this Window Manager + /// + public WindowType[] WindowTypes { get; private set; } + + #endregion + + #region Protected Common Window Object Operations + + /// + /// Create a new Object Instance of type + /// + /// type of object to create + /// a new object for type, or null + protected object CreateInstance(WindowType window) + { + object o = null; + if (window != null) + o = Activator.CreateInstance(window.T); + return o; + } + + /// + /// Create a new Object Instance of type + /// + /// type of object to create + /// a new object for type, or null + protected object CreateInstance(Enum windowKey) + { + return CreateInstance(GetType(windowKey)); + } + + /// + /// + /// + /// + /// + protected List GetObjects(Enum windowKey) + { + foreach (WindowType type in s_WindowsToManage.Keys) + { + if (type.Key.ToString() == windowKey.ToString()) + return s_WindowsToManage[type]; + } + return null; + } + + /// + /// + /// + /// + /// + protected List GetObjects(WindowType window) + { + foreach (WindowType type in s_WindowsToManage.Keys) + { + if (type.Key.ToString() == window.Key.ToString()) + return s_WindowsToManage[type]; + } + return null; + } + + /// + /// + /// + /// + /// + protected WindowType GetType(Enum windowKey) + { + foreach (WindowType type in s_WindowsToManage.Keys) + { + if (type.Key.ToString() == windowKey.ToString()) + return type; + } + return null; + } + + /// + /// + /// + /// + /// + /// + protected bool AddObject(WindowType window, object o) + { + if (AddWindowTypeToManage(window)) + { + List objects = GetObjects(window); + if (objects.Count == 0) + { + objects.Add(o); + return true; + } + else if (objects.Count > 0 && !window.IsSingleton) + { + objects.Add(o); + return true; + } + } + return false; + } + + /// + /// + /// + /// + /// + /// + protected bool AddObject(Enum windowKey, object o) + { + WindowType type = GetType(windowKey); + if (type != null) + { + List objects = GetObjects(windowKey); + if (objects.Count == 0) + { + objects.Add(o); + return true; + } + else if (objects.Count > 0 && !type.IsSingleton) + { + objects.Add(o); + return true; + } + } + return false; + } + + /// + /// + /// + /// + /// + protected bool DeleteObject(WindowType window) + { + List objects = GetObjects(window); + if (objects != null && objects.Count > 0) + { + int nIndex = objects.Count - 1; + if (objects[nIndex] is IDisposable) + ((IDisposable)objects[nIndex]).Dispose(); + objects.RemoveAt(nIndex); + return true; + } + return false; + } + + /// + /// + /// + /// + /// + protected bool DeleteObject(Enum windowKey) + { + List objects = GetObjects(windowKey); + if (objects != null && objects.Count > 0) + { + int nIndex = objects.Count - 1; + if (objects[nIndex] is IDisposable) + ((IDisposable)objects[nIndex]).Dispose(); + objects.RemoveAt(nIndex); + return true; + } + return false; + } + + /// + /// + /// + protected void ClearAllObjects() + { + // Iterate thru all objects and call dispose on them if they support it, + // then clear main dictionary + foreach (WindowType type in s_WindowsToManage.Keys) + while (DeleteObject(type)) ; + + s_WindowsToManage.Clear(); + } + + /// + /// + /// + /// + /// + protected bool AddWindowTypeToManage(WindowType window) + { + if (window != null && !IsWindowTypeManaged(window)) + s_WindowsToManage[window] = new List(); + + return (IsWindowTypeManaged(window)); + } + + /// + /// + /// + /// + /// + protected bool IsWindowTypeManaged(WindowType window) + { + if (window != null) + { + foreach (WindowType type in s_WindowsToManage.Keys) + { + if (type.Key.ToString() == window.Key.ToString()) + return true; + } + } + return false; + } + + #endregion + + #region Abtract Methods + + public delegate object ShowDelegate(Enum windowKey, WindowAttributes attributes, object tag); + public abstract object ShowWindow(Enum windowKey, WindowAttributes attributes, object tag); + public abstract object ShowDialog(Enum windowKey, WindowAttributes attributes, object tag); + + public delegate bool HideCloseDelegate(Enum windowKey); + public abstract bool HideWindow(Enum windowKey); + public abstract bool CloseWindow(Enum windowKey); + + public delegate bool ActionDelegate(Enum windowKey, DelegateCollection.Bool_Param1_Window_Func action); + public abstract bool RunAction(Enum windowKey, DelegateCollection.Bool_Param1_Window_Func action); + + public abstract void Dispose(); + + #endregion + + #region Public Methods + + /// + /// Get All Window Objects for a Window, in a readonly fashion + /// + /// + /// + public object[] GetAllWindowObjectsForWindow(Enum windowKey) + { + List objects = GetObjects(windowKey); + if (objects != null) + return objects.ToArray(); + else + return null; + } + + #endregion + } + + #endregion + + #region Dispatcher Thread + + /// + /// + /// + static class DispatcherThread + { + #region Private Static Members + + private static Sys.Thread s_DispatcherThread = null; + private static bool s_DispatcherThreadIsInitialized = false; + + private static WPFWindowManager s_wpfWindowManager = null; + private static DispatcherThreatType s_useWindowManagerType = DispatcherThreatType.WPF; + private static WindowType[] s_windowTypes = null; + private static bool s_PerformPerformanceLoad = false; + + #endregion + + #region Private Static Methods + + /// + /// Message Loop Thread for ButtonForm + /// + private static void ThreadProc() + { + // Create WindowManager Object on this thread *Allow global access to the object* + if (s_useWindowManagerType == DispatcherThreatType.WPF) + s_wpfWindowManager = new WPFWindowManager(Dispatcher.CurrentDispatcher, s_windowTypes, s_PerformPerformanceLoad); + + s_DispatcherThreadIsInitialized = true; // always set to true to allow caller to exit out + if (s_wpfWindowManager != null) + { + //Enter Dispatcher Message Loop + System.Windows.Threading.Dispatcher.Run(); + } + } + + #endregion + + #region Public Static Methods + + /// + /// Retrieve the Window Manager for this Dispatcher Thread + /// + /// a valid window Manager or null, if none + public static WindowManager GetWMForDispatcher() + { + if (s_DispatcherThreadIsInitialized) + { + if (s_useWindowManagerType == DispatcherThreatType.WPF && s_wpfWindowManager != null) + return s_wpfWindowManager; + } + return null; + } + + /// + /// Use this function to start the Dispatcher ThreadProc(above), if needed. COM+ can shutdown the thread anytime, + /// we need to make sure that the thread is alive BEFORE calling WindowManager + /// + public static void Start(DispatcherThreatType threatType, WindowType[] windowTypes, bool PerformPerformanceLoad, bool bWait) + { + if (s_DispatcherThread != null && s_DispatcherThread.IsAlive) + { + return; + } + else + { + s_DispatcherThreadIsInitialized = false; + + // Specify Thread Attributes + s_useWindowManagerType = threatType; + s_windowTypes = windowTypes; + s_PerformPerformanceLoad = PerformPerformanceLoad; + + // Start a new Thread so it can become the Message Loop + s_DispatcherThread = new Sys.Thread(new Sys.ThreadStart(DispatcherThread.ThreadProc)); + + // GUI components require the thread to be STA; otherwise throws an error + s_DispatcherThread.SetApartmentState(ApartmentState.STA); + s_DispatcherThread.Start(); + + // Make sure the Application Object is running + // (COM will eventually just time out otherwise) + if (bWait) // Syncronous call + { + while (!s_DispatcherThreadIsInitialized) + System.Threading.Thread.Sleep(20); + } + } + } + + /// + /// Terminates all ButtonForm Objects and the Message Dispatcher Thread *do this only on shutdown* + /// + public static void Stop() + { + if (s_DispatcherThreadIsInitialized) + { + // Enforce that all WindowManagers support IDispose + if (s_wpfWindowManager != null) + { + s_wpfWindowManager.Dispose(); + s_wpfWindowManager = null; + } + s_DispatcherThread.Abort(); + s_DispatcherThreadIsInitialized = false; + s_DispatcherThread = null; + } + } + + #endregion + } + + #endregion +} diff --git a/Thread/SingleThreadTimer.cs b/Thread/SingleThreadTimer.cs new file mode 100644 index 0000000..9d95d45 --- /dev/null +++ b/Thread/SingleThreadTimer.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; + +namespace Yaulw.Thread +{ + /// + /// Single Threaded Timer * only calls the elapsed event handler once and only + /// will call it again, if the elapsed event handler returns + /// + public class SingleThreadTimer + { + #region Private Members + + private STElapsedEventHandler _elapsedHandler = null; + private uint _IntervalMilliseconds = 0; + private System.Threading.Thread _thread = null; + + #endregion + + #region Construction + + /// + /// Creates a new Single-threaded System.Timer + /// + /// Event Handler for Timer + /// Interval in Miliseconds + /// True to start the timer upon creation, false otherwise + /// A Timer Object, which should be Disposed by Caller + public SingleThreadTimer(STElapsedEventHandler ElapsedHandler, uint IntervalMilliseconds, bool StartEnabled) + { + _elapsedHandler = ElapsedHandler; + _IntervalMilliseconds = IntervalMilliseconds; + if (_elapsedHandler == null || _IntervalMilliseconds == 0) + throw new ArgumentException("Invalid ElapsedHandler or Milliseconds. ElapsedHandler can not be null. Milliseconds can not be 0."); + + if (StartEnabled) + Start(); + } + + #endregion + + #region Private Helpers + + /// + /// Main DoWork() Function, calls the ElapsedHandler at every interval + /// * Single threaded * ElapsedHandler will only get called again, once it returns + /// + private void STimerThreadMethod() + { + do + { + if (_IntervalMilliseconds > 0) + System.Threading.Thread.Sleep((int)_IntervalMilliseconds); + + if (_elapsedHandler != null) + _elapsedHandler(this, new STElapsedEventArgs(DateTime.Now)); + } + while (true); + } + + #endregion + + #region Public Delegates + + /// + /// ElapsedEvent Handler is called when the time interval elapsed + /// + /// Sends the STimer Object + /// sends the Timestampe when the interval elapsed + public delegate void STElapsedEventHandler(object sender, STElapsedEventArgs e); + + /// + /// ElapsedEventArgs are called with the time stamp when the Event was raised + /// + public class STElapsedEventArgs : EventArgs + { + public DateTime SignalTime { get; private set; } + public STElapsedEventArgs(DateTime SignalTime) { this.SignalTime = SignalTime; } + } + + #endregion + + #region Public Methods + + /// + /// Manually Start the STimer + /// + public bool Start() + { + Stop(); // First Stop(), an existing Timer + return Start(_IntervalMilliseconds); + } + + /// + /// Manually Start the STimer at a new Interval + /// + /// Interval as a TimeSpan + public bool Start(TimeSpan tsInterval) + { + Stop(); // First Stop(), an existing Timer + return Start((uint)tsInterval.TotalMilliseconds); + } + + /// + /// Manually Start the STimer at a new Interval + /// + /// Interval in Miliseconds + public bool Start(uint IntervalMiliseconds) + { + try + { + Stop(); // First Stop(), an existing Timer + if (IntervalMiliseconds > 0) + _IntervalMilliseconds = IntervalMiliseconds; + if (_thread == null) + _thread = TStarter.StartThread(STimerThreadMethod, ("STimer-" + _elapsedHandler.Method.Name), System.Threading.ApartmentState.MTA, false, System.Threading.ThreadPriority.Normal); + return ((_thread != null) && (_thread.IsAlive)); + } + catch (Exception) { /* ignore */ } + return false; + } + + /// + /// Manually Stop the STimer + /// + public void Stop() + { + try + { + if (_thread != null) + _thread.Abort(); + } + catch (Exception) { /* ignore */ } + _thread = null; + } + + #endregion + + } +} diff --git a/Thread/TStarter.cs b/Thread/TStarter.cs new file mode 100644 index 0000000..20bf8c5 --- /dev/null +++ b/Thread/TStarter.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Collections.Specialized; + +namespace Yaulw.Thread +{ + /// + /// Used to Manage/Launch Threads In an Application + /// + public static class TStarter + { + #region Public Delegates + + /// + /// Thread Delegate with a single Object as a parameter + /// + /// object to be passed to Delegate + public delegate void ParameterizedThreadMethod(object o); + + /// + /// No Parameter Thread Delegate + /// + public delegate void ThreadMethod(); + + #endregion + + #region Private Static Members + + private static Dictionary _StartedNamedThreads = new Dictionary(); + + #endregion + + #region Public Statics + + /// + /// Starts a Parameterized thread + /// + /// specifcy a method to invoke + /// specify a parameter to pass to the method + /// If you specify a ThreadName, it must be Unique to the Application + /// ApartmentState + /// Is Background Thread + /// set thread priority + /// a thread object if successful, null otherwise + public static System.Threading.Thread StartParameterizedThread(ParameterizedThreadMethod method, object parameter, string threadName = "", ApartmentState Apartment = ApartmentState.MTA, bool IsBackground = false, ThreadPriority priority = ThreadPriority.Normal) + { + // If a threadName is defined, make sure that it is unique to the application + if (!String.IsNullOrEmpty(threadName) && _StartedNamedThreads.ContainsKey(threadName)) + return null; + + System.Threading.Thread thread = null; + if (method != null && parameter != null) + { + thread = new System.Threading.Thread(new ParameterizedThreadStart(method)); + if (!String.IsNullOrEmpty(threadName)) + thread.Name = threadName; + + // Start the Thread + thread.SetApartmentState(Apartment); + thread.Priority = priority; + thread.IsBackground = IsBackground; + thread.Start(parameter); + + // Save the Thread in the Dictionary + if (!String.IsNullOrEmpty(threadName)) + _StartedNamedThreads[threadName] = thread; + } + return thread; + } + + /// + /// Starts a thread without Parameters + /// + /// specifcy a method to invoke + /// If you specify a ThreadName, it must be Unique to the Application + /// ApartmentState + /// Is Background Thread + /// set thread priority + /// a thread object if successful, null otherwise + public static System.Threading.Thread StartThread(ThreadMethod method, string threadName = "", ApartmentState Apartment = ApartmentState.MTA, bool IsBackground = false, ThreadPriority priority = ThreadPriority.Normal) + { + // If a threadName is defined, make sure that it is unique to the system + if (!String.IsNullOrEmpty(threadName) && _StartedNamedThreads.ContainsKey(threadName)) + return null; + + System.Threading.Thread thread = null; + if (method != null) + { + thread = new System.Threading.Thread(new ThreadStart(method)); + if (!String.IsNullOrEmpty(threadName)) + thread.Name = threadName; + + // Start the Thread + thread.SetApartmentState(Apartment); + thread.Priority = priority; + thread.IsBackground = IsBackground; + thread.Start(); + + // Save the Thread in the Dictionary + if (!String.IsNullOrEmpty(threadName)) + _StartedNamedThreads[threadName] = thread; + } + return thread; + + } + + /// + /// Retrieve all Named threads that were started thru here that are still running + /// + /// an array of running named threads, null if none + public static System.Threading.Thread[] GetStartedNamedThreadsThatAreStillRunning() + { + List threads = new List(); + List threadNamesThatAreNoLongerAlive = new List(); + + // Get Named Alive Threads + foreach (System.Threading.Thread thread in _StartedNamedThreads.Values) + { + if (thread.IsAlive) + threads.Add(thread); + else + threadNamesThatAreNoLongerAlive.Add(thread.Name); + } + + // Perform Cleanup + foreach (string threadName in threadNamesThatAreNoLongerAlive) + { + _StartedNamedThreads[threadName] = null; + _StartedNamedThreads.Remove(threadName); + } + + return threads.ToArray(); + } + + #endregion + } +} diff --git a/Thread/TTimer.cs b/Thread/TTimer.cs new file mode 100644 index 0000000..8eb592f --- /dev/null +++ b/Thread/TTimer.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; +using System.Windows.Threading; +using WinThread = System.Threading; + +namespace Yaulw.Thread +{ + /// + /// Wrapper class around Timer Object * Multi-Threated * + /// + public class TTimer : IDisposable + { + #region Private Members + + private Timer _Timer = new Timer(); + private bool _disposed = false; + + #endregion + + #region Construction + + /// + /// Creates a new Multi-threaded System.Timer + /// + /// Event Handler for Timer + /// Interval in Miliseconds + /// True to start the timer upon creation, false otherwise + /// A Timer Object, which should be Disposed by Caller + public TTimer(ElapsedEventHandler ElapsedHandler, int IntervalMiliseconds = 1000, bool StartEnabled = false) + { + if (ElapsedHandler != null) + { + _Timer = new System.Timers.Timer(); + _Timer.Elapsed += ElapsedHandler; + + // Set the Interval / start + _Timer.Interval = IntervalMiliseconds; + _Timer.Enabled = StartEnabled; + if (StartEnabled) + _Timer.Start(); + + // Keep the timer alive + GC.KeepAlive(_Timer); + } + } + + /// + /// Finalizer + /// + ~TTimer() + { + Dispose(true); + } + + #endregion + + #region Public Methods + + /// + /// Manually Start the Timer + /// + public void Start() + { + Stop(); // First Stop(), an existing Timer + _Timer.Enabled = true; + _Timer.Start(); + } + + /// + /// Manually Start the Timer at a new Interval + /// + /// Interval in Miliseconds + public void Start(uint IntervalMiliseconds) + { + Stop(); // First Stop(), an existing Timer + _Timer.Interval = IntervalMiliseconds; + _Timer.Enabled = true; + _Timer.Start(); + } + + /// + /// Manually Start the Timer at a new Interval + /// + /// Interval as a TimeSpan + public void Start(TimeSpan tsInterval) + { + Stop(); // First Stop(), an existing Timer + _Timer.Interval = tsInterval.TotalMilliseconds; + _Timer.Enabled = true; + _Timer.Start(); + } + + /// + /// Manually Stop the Timer + /// + public void Stop() + { + _Timer.Enabled = false; + _Timer.Stop(); + } + + #endregion + + #region IDisposable Members + + /// + /// Dispose the Timer Object + /// + public void Dispose() + { + Dispose(true); + + // Use SupressFinalize in case a subclass + // of this type implements a finalizer + GC.SuppressFinalize(this); + } + + /// + /// Dispose the Timer Object + /// + /// true, if called from within + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + if (_Timer != null) + _Timer.Dispose(); + } + + // Indicate that the instance has been disposed. + _Timer = null; + _disposed = true; + } + } + + #endregion + } +} diff --git a/Thread/TTimerDisp.cs b/Thread/TTimerDisp.cs new file mode 100644 index 0000000..9e6ad5b --- /dev/null +++ b/Thread/TTimerDisp.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Timers; +using System.Windows.Threading; +using WinThread = System.Threading; + +namespace Yaulw.Thread +{ + /// + /// Wrapper class around Timer Object * Uses Dispatcher * + /// + public class TTimerDisp : IDisposable + { + #region Private Members + + private Timer _Timer = new Timer(); + private bool _disposed = false; + private Dispatcher _Dispatcher = null; + private ElapsedEventHandler _DispatchedElapsedEvent = null; + + #endregion + + #region Construction + + /// + /// Creates a new Multi-threaded System.Timer that uses the Dispatcher to Fire the Event + /// + /// Event Handler for Timer + /// Interval in Miliseconds + /// True to start the timer upon creation, false otherwise + /// A Timer Object, which should be Disposed by Caller + public TTimerDisp(ElapsedEventHandler ElapsedHandler, int IntervalMiliseconds = 1000, bool StartEnabled = false) + { + if (ElapsedHandler != null) + { + _Timer = new System.Timers.Timer(); + + // The Primary Dispatcher thread is the thread that called us + _Dispatcher = Dispatcher.CurrentDispatcher; + _DispatchedElapsedEvent = ElapsedHandler; + _Timer.Elapsed += new ElapsedEventHandler(_Timer_Elapsed); + + // Set the Interval / start + _Timer.Interval = IntervalMiliseconds; + _Timer.Enabled = StartEnabled; + if (StartEnabled) + _Timer.Start(); + + // Keep the timer alive + GC.KeepAlive(_Timer); + } + } + + /// + /// Finalizer + /// + ~TTimerDisp() + { + Dispose(true); + } + + #endregion + + #region Private Helpers + + /// + /// For Dispatching the Event to the Primary Dispatcher Thread + /// + private void _Timer_Elapsed(object sender, ElapsedEventArgs e) + { + object[] param_s = new object[] { sender, e }; + _Dispatcher.Invoke((ElapsedEventHandler)_DispatchedElapsedEvent, param_s); + } + + #endregion + + #region Public Methods + + /// + /// Manually Start the Timer + /// + public void Start() + { + Stop(); // First Stop(), an existing Timer + _Timer.Enabled = true; + _Timer.Start(); + } + + /// + /// Manually Start the Timer at a new Interval + /// + /// Interval in Miliseconds + public void Start(uint IntervalMiliseconds) + { + Stop(); // First Stop(), an existing Timer + _Timer.Interval = IntervalMiliseconds; + _Timer.Enabled = true; + _Timer.Start(); + } + + /// + /// Manually Start the Timer at a new Interval + /// + /// Interval as a TimeSpan + public void Start(TimeSpan tsInterval) + { + Stop(); // First Stop(), an existing Timer + _Timer.Interval = tsInterval.TotalMilliseconds; + _Timer.Enabled = true; + _Timer.Start(); + } + + /// + /// Manually Stop the Timer + /// + public void Stop() + { + _Timer.Enabled = false; + _Timer.Stop(); + } + + #endregion + + #region IDisposable Members + + /// + /// Dispose the Timer Object + /// + public void Dispose() + { + Dispose(true); + + // Use SupressFinalize in case a subclass + // of this type implements a finalizer + GC.SuppressFinalize(this); + } + + /// + /// Dispose the Timer Object + /// + /// true, if called from within + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + if (_Timer != null) + _Timer.Dispose(); + } + + // Indicate that the instance has been disposed. + _Timer = null; + _disposed = true; + } + } + + #endregion + } +} diff --git a/Tools/Convert.cs b/Tools/Convert.cs new file mode 100644 index 0000000..278b3d5 --- /dev/null +++ b/Tools/Convert.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Yaulw.Tools +{ + /// + /// Common Conversion Operations + /// + public static class Convert + { + /// + /// Convert a a String to a UTF Byte Array + /// + /// string to convert + /// a Byte Array from a String + public static byte[] StrToByteArrayUTF(string str) + { + if (!String.IsNullOrEmpty(str)) + { + System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding(); + return encoding.GetBytes(str); + } + return null; + } + + /// + /// Convert a a String to an ASCII Byte Array + /// + /// string to convert + /// a Byte Array from a String + public static byte[] StrToByteArrayAscii(string str) + { + if (!String.IsNullOrEmpty(str)) + { + System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); + return encoding.GetBytes(str); + } + return null; + } + + + } +} diff --git a/Tools/EnumTool.cs b/Tools/EnumTool.cs new file mode 100644 index 0000000..e1b65a8 --- /dev/null +++ b/Tools/EnumTool.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Yaulw.Tools +{ + /// + /// Common Operations on Enumerations + /// + public static class EnumTool + { + #region Common Operations + + /// + /// Turns an Enum to a String (Replacing _ with ' ') + /// + /// an Enum which could possibly contain an '_' + /// a ' ' spaced String of Enum + public static string Enum_ToString(Enum _enum) + { + return _enum.ToString().Replace('_', ' '); + } + + /// + /// Using the '__' in the Enum Name allows to embed Categories in the Name + /// + /// an Enum with '__' + /// The String Value before the '__', also replacing '_' with ' ' + /// The String Value after the '__', also replacing '_' with ' ' + public static void Enum_ToCategoryNKey(Enum _enum, out string Category, out string Key) + { + Category = String.Empty; + Key = String.Empty; + + if (!Enum_HasCategoryNKey(_enum)) + throw new ArgumentException("_enum must contain a __ with category and key values"); + + string _enumStr = _enum.ToString(); + int nIndex = _enumStr.IndexOf("__"); + + // Assign out the Retrived Values + Category = _enumStr.Substring(0, nIndex).Replace('_', ' '); + Key = _enumStr.Substring(nIndex + 2).Replace('_', ' '); + } + + #endregion + + #region HasCategoryNKey + + /// + /// Determines if the Passed in Enum has both a Category and Key + /// + /// an Enum with '__' + /// true if '__' is present with both a category and key value + public static bool Enum_HasCategoryNKey(Enum _enum) + { + // Check to see there is only one __ + string _enumStr = _enum.ToString(); + int nIndexFirst = _enumStr.IndexOf("__"); + int nIndexLast = _enumStr.LastIndexOf("__"); + if (nIndexFirst == nIndexLast) + { + // Check to see that there are values infront and behind the __ + if (nIndexFirst != 0 && (nIndexLast < (_enumStr.Length - 1))) + return true; + } + return false; + } + + /// + /// Determines if all the Enums of the Passed in Enum Type have both a Category and Key + /// + /// an Enum Type that has all enums with '__' + /// true if '__' is present with both a category and key value on all enums + public static bool EnumType_HasCategoryNKey(Type _enumType) + { + if (_enumType.IsEnum) + { + foreach (string enumName in Enum.GetNames(_enumType)) + { + bool bIsValid = Enum_HasCategoryNKey((Enum)Enum.Parse(_enumType, enumName)); + if (!bIsValid) + return false; + } + return true; + } + return false; + } + + #endregion + + #region Category N' Key Traversals + + /// + /// Retrieves all the Categories of an Enum + /// + /// an Enum Type that has all enums with '__' + /// An Array of all unique Categories, '_' replaced with ' ', or null if error occured + public static string[] EnumType_GetCategories(Type _enumType) + { + if (_enumType.IsEnum && EnumType_HasCategoryNKey(_enumType)) + { + List retVal = new List(); + foreach (string enumName in Enum.GetNames(_enumType)) + { + string Category; + string Key; + Enum_ToCategoryNKey((Enum)Enum.Parse(_enumType, enumName), out Category, out Key); + if(!retVal.Contains(Category)) + retVal.Add(Category); + } + return retVal.ToArray(); + } + return null; + } + + /// + /// Retrieves all the Keys of an Enum for a specified Category + /// + /// an Enum Type that has all enums with '__' + /// An Array of all unique Keys, '_' replaced with ' ', or null if error occured + public static string[] EnumType_GetKeys(Type _enumType, string Category) + { + if (_enumType.IsEnum && EnumType_HasCategoryNKey(_enumType)) + { + List retVal = new List(); + foreach (string enumName in Enum.GetNames(_enumType)) + { + string CategoryTemp; + string Key; + Enum_ToCategoryNKey((Enum)Enum.Parse(_enumType, enumName), out CategoryTemp, out Key); + if (String.Compare(Category, CategoryTemp, true) == 0) + retVal.Add(Key); + } + return retVal.ToArray(); + } + return null; + } + + #endregion + } +} diff --git a/Tools/ObjTool.cs b/Tools/ObjTool.cs new file mode 100644 index 0000000..60ee1be --- /dev/null +++ b/Tools/ObjTool.cs @@ -0,0 +1,269 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Yaulw.Tools +{ + /// + /// Useful Utilities around objects + /// + public static class ObjTool + { + /// + /// Returns true if passed in object is valid and is not empty + /// + /// an object to validate + /// true if valid, false otherwise + public static bool IsNotNullAndNotEmpty(object oToValidate) + { + if ((oToValidate != null) && (oToValidate.ToString() != String.Empty)) + return true; + else + return false; + } + + /// + /// Use this to just check if the object can be converted to a string + /// via ConvertObjToString(). If false, this is most likely an object type that shouldn't be converted to a string. + /// + /// Can be Any type + /// an object of type T to check if String Convertible + /// true, if the object passed in a String Convertible type like string, bool, int32, double, decimal, etc..., false otherwise + public static bool IsOfTypeConvertibleToString(T objToCheckConvert) + { + bool retVal = false; + T objToConvert = objToCheckConvert; + + // Check if String Convertible + if (objToConvert is System.Char) + { + retVal = true; + } + else if (objToConvert is System.String) + { + retVal = true; + } + else if (objToConvert is System.Decimal) + { + retVal = true; + } + else if (objToConvert is System.Int32) + { + retVal = true; + } + else if (objToConvert is System.Int64) + { + retVal = true; + } + else if (objToConvert is System.Single) + { + retVal = true; + } + else if (objToConvert is System.Double) + { + retVal = true; + } + else if (objToConvert is System.Boolean) + { + retVal = true; + } + else if (objToConvert is System.DateTime) + { + retVal = true; + } +#if NET4 + else if (objToConvert is System.Guid) // Only works in .Net 4.0 + { + retVal = true; + } +#endif + else if (objToConvert is System.IntPtr) + { + retVal = true; + } + else if (objToConvert is System.UInt32) + { + retVal = true; + } + else if (objToConvert is System.UInt64) + { + retVal = true; + } + + return retVal; + } + + /// + /// Convert a string to an Object of Type T + /// + /// Should be a System Type like string, bool, int32, double, decimal, etc... + /// String Value to Convert + /// an converted Object of Type t, or T Default if error occured + public static T ConvertStringToObj(string strToConvert) + { + + // Create a Default Type T + T retVal = default(T); + + try + { + #region Conversion + + if (retVal is System.Char) + { + retVal = (T)((object)strToConvert[0]); + } + else if (retVal is System.String) + { + retVal = (T)((object)strToConvert); + } + else if (retVal is System.Decimal) + { + retVal = (T)((object)Decimal.Parse(strToConvert)); + } + else if (retVal is System.Int32) + { + retVal = (T)((object)Int32.Parse(strToConvert)); + } + else if (retVal is System.Int64) + { + retVal = (T)((object)Int64.Parse(strToConvert)); + } + else if (retVal is System.Single) + { + retVal = (T)((object)Single.Parse(strToConvert)); + } + else if (retVal is System.Double) + { + retVal = (T)((object)Double.Parse(strToConvert)); + } + else if (retVal is System.Boolean) + { + retVal = (T)((object)Boolean.Parse(strToConvert)); + } + else if (retVal is System.DateTime) + { + retVal = (T)((object)DateTime.Parse(strToConvert)); + } +#if NET4 + else if (retVal is System.Guid) // Works in .Net 4.0 and up + { + retVal = (T)((object)Guid.Parse(strToConvert)); + } +#endif + else if (retVal is System.IntPtr) + { + if (IntPtr.Size <= 4) + { + Int32 i = Int32.Parse(strToConvert); + retVal = (T)((object)new IntPtr(i)); + } + else + { + Int64 i = Int64.Parse(strToConvert); + retVal = (T)((object)new IntPtr(i)); + } + } + else if (retVal is System.UInt32) + { + retVal = (T)((object)UInt32.Parse(strToConvert)); + } + else if (retVal is System.UInt64) + { + retVal = (T)((object)UInt64.Parse(strToConvert)); + } + else + { + retVal = (T)((object)(strToConvert)); + } + + #endregion + } + catch (Exception e) { string Message = e.Message; /* ignore */ } + + // return T + return retVal; + } + + /// + /// Convert an object of type t to a String + /// + /// Should be a System Type like string, bool, int32, double, decimal, etc... + /// Object Value to Convert + /// a string that contains the value of the Object + public static string ConvertObjToString(T objToConvert) + { + String retVal = String.Empty; + try + { + #region Conversion + + if (objToConvert is System.Char) + { + retVal = ((Char)((object)objToConvert)).ToString(); + } + else if (objToConvert is System.String) + { + retVal = ((String)((object)objToConvert)).ToString(); + } + else if (objToConvert is System.Decimal) + { + retVal = ((Decimal)((object)objToConvert)).ToString(); + } + else if (objToConvert is System.Int32) + { + retVal = ((Int32)((object)objToConvert)).ToString(); + } + else if (objToConvert is System.Int64) + { + retVal = ((Int64)((object)objToConvert)).ToString(); + } + else if (objToConvert is System.Single) + { + retVal = ((Single)((object)objToConvert)).ToString(); + } + else if (objToConvert is System.Double) + { + retVal = ((Double)((object)objToConvert)).ToString(); + } + else if (objToConvert is System.Boolean) + { + retVal = ((Boolean)((object)objToConvert)).ToString(); + } + else if (objToConvert is System.DateTime) + { + retVal = ((DateTime)((object)objToConvert)).ToString(); + } +#if NET4 + else if (objToConvert is System.Guid) + { + retVal = ((Guid)((object)objToConvert)).ToString(); + } +#endif + else if (objToConvert is System.IntPtr) + { + retVal = ((IntPtr)((object)objToConvert)).ToString(); + } + else if (objToConvert is System.UInt32) + { + retVal = ((UInt32)((object)objToConvert)).ToString(); + } + else if (objToConvert is System.UInt64) + { + retVal = ((UInt64)((object)objToConvert)).ToString(); + } + else + { + retVal = ((object)objToConvert).ToString(); + } + + #endregion + } + catch (Exception) { /* ignore */ } + + // return T + return retVal; + } + } +} diff --git a/Tools/PathNaming.cs b/Tools/PathNaming.cs new file mode 100644 index 0000000..aadb31c --- /dev/null +++ b/Tools/PathNaming.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace Yaulw.Tools +{ + /// + /// Tools For File/Directory Paths + /// + public static class PathNaming + { + /// + /// Get the Root Path for a given path string + /// + /// path + /// the root folder, with '\' + public static string GetPathRoot(string path) + { + if(!String.IsNullOrEmpty(path)) + return PathEndsWithSlash(System.IO.Path.GetPathRoot(path)); + return ""; + } + + /// + /// Does Path contain file information + /// + /// returns true if the specified path information contains file information + public static bool PathContainsFile(string path) + { + if (!String.IsNullOrEmpty(path) && (path.LastIndexOf('.') > path.LastIndexOf('\\'))) + return true; + return false; + } + + /// + /// Makes sure that the specified path begins with a '\' + /// + /// a path string that begins with \ + public static string PathBeginsWithSlash(string path) + { + if (!String.IsNullOrEmpty(path) && (path[0] != '\\')) + return '\\' + path; + return path; + } + + /// + /// Makes sure that the specified path does NOT begin with a '\' + /// + /// a path string that does NOT begin with \ + public static string PathBeginsWithNoSlash(string path) + { + if (!String.IsNullOrEmpty(path) && (path[0] == '\\')) + return path.Substring(1); + return path; + } + + /// + /// Makes sure that the specified path ends with a '\' + /// + /// a path string that ends with \ + public static string PathEndsWithSlash(string path) + { + if (!String.IsNullOrEmpty(path) && (path[path.Length - 1] != '\\')) + return path + '\\'; + return path; + } + + /// + /// Makes sure that the specified path does NOT end with a '\' + /// + /// a path string that does NOT end with \ + public static string PathEndsWithNoSlash(string path) + { + if (!String.IsNullOrEmpty(path) && (path[path.Length - 1] == '\\')) + return path.Substring(0, path.Length - 1); + return path; + } + + /// + /// Returns True if the fileName is valid * Filename chars are More restrictive than Path * should always use this + /// + /// filename to check for + /// true, if valid, false otherwise + public static bool FileNameIsValid(string FileName) + { + if (!String.IsNullOrEmpty(FileName)) + { + // stripe out the filename, if possible, if not, just use what is + // passed into us + string file = Path.GetFileName(FileName); + if (!String.IsNullOrEmpty(file)) + FileName = file; + + char[] invalidChars = Path.GetInvalidFileNameChars(); + foreach (char c in invalidChars) + { + if (FileName.Contains(c)) + return false; + } + return true; + } + return false; + } + + /// + /// Returns True if the Directory Path is valid * Path chars are not as restrictive as File * should always use File Chars (better safe than sorry) + /// Use FileNameIsValid() + /// + /// DirectoryPath to check for + /// true, if valid, false otherwise + public static bool IsDirectoryPathValid(string DirectoryPath) + { + if (!String.IsNullOrEmpty(DirectoryPath)) + { + // stripe the trailing '\\', if possible, if not, just use what is + // passed into us + string path = Path.GetDirectoryName(DirectoryPath); + if (!String.IsNullOrEmpty(path)) + DirectoryPath = path; + + // FileName Char restrictions contain everything we need, + // except for a path we want to allow '\\' + char[] invalidChars = Path.GetInvalidFileNameChars(); + + foreach (char c in invalidChars) + { + // allow '\\' for paths + if (c != '\\' && DirectoryPath.Contains(c)) + return false; + } + return true; + } + return false; + } + + /// + /// return a valid directory path, a path that is invalid and contains invalid + /// characters is automatically converted, (illegal characters are skipped) + /// + /// DirectoryPath to change + /// the 'corrected' path string + public static string MakeDirectoryPathValid(string DirectoryPath) + { + return MakeDirectoryPathValid(DirectoryPath, String.Empty); + } + + /// + /// return a valid directory path, a path that is invalid and contains invalid + /// characters is automatically converted, (illegal characters are skipped) + /// + /// DirectoryPath to change + /// the string to put in place instead of the invalid char found, + /// it itself can't be an invalid char + /// the 'corrected' path string + public static string MakeDirectoryPathValid(string DirectoryPath, string replacementStr) + { + if(!String.IsNullOrEmpty(DirectoryPath)) + { + // stripe the trailing '\\', if possible, if not, just use what is + // passed into us + string path = Path.GetDirectoryName(DirectoryPath); + if (!String.IsNullOrEmpty(path)) + DirectoryPath = path; + + // FileName Char restrictions contain everything we need, + // except for a path we want to allow '\\' + char[] invalidChars = Path.GetInvalidFileNameChars(); + + // Make sure replacementStr is valid also, otherwise it somewhat defeats the purpose + // of this whole function + string strToUseForReplacement = ""; + bool replacementStrIsValid = false; + if (replacementStr != null) + { + replacementStrIsValid = true; + if (replacementStr != "") + { + foreach (char c in replacementStr) + { + invalidChars.Contains(c); + replacementStrIsValid = false; + break; + } + } + } + if (replacementStrIsValid) + strToUseForReplacement = replacementStr; + + // Construct new String Path + StringBuilder sb = new StringBuilder(); + foreach (char c in DirectoryPath) + { + // allow '\\' for paths + if (c != '\\' && invalidChars.Contains(c)) + { + sb.Append(strToUseForReplacement); + } + else + { + sb.Append(c); + } + } + return sb.ToString(); + } + return DirectoryPath; + } + + /// + /// Retrieve the last directory name from a path, can be a UNC path like \\{server}\Directory + /// or a file and system path like C:\{directory}\{directory}, will always return the name of the + /// last directory in the path. if it is not a path, i.e just a name is passed in without '\'s then + /// it will just stripe out all '\' that maybe there and return the name + /// + /// + /// the name of the last directory in the path + public static string RetrieveLastDirectoryNameInPath(string DirectoryPath) + { + if (!String.IsNullOrEmpty(DirectoryPath)) + { + // Find the last folder and return that information + // This is what this function was created for + string dp = Path.GetDirectoryName(DirectoryPath); + int nLast = dp.LastIndexOf('\\'); + if (nLast != -1 && dp.Length > 3) + { + dp = dp.Substring(nLast + 1); + return dp; + } + + // Return what was passed into us, probably doesn't have a '\\', + // but it could be that it just has one. like someone passed in \\{server}, + // then we'll just return the {server}, if someone passes in c:\directory, then, + // we should pass back just the directory + if (DirectoryPath.Length > 3 && DirectoryPath[1] == ':') + { + return DirectoryPath.Substring(3); + } + else + { + // try finding the last \\, again without using GetDirectoryName which + // stripes it, return the last stuff found, otherwise just return what was passed to us + nLast = DirectoryPath.LastIndexOf('\\'); + if (nLast != -1 && DirectoryPath.Length > 3) + { + return DirectoryPath.Substring(nLast + 1); + } + else + { + return DirectoryPath; + } + } + } + return String.Empty; + } + + } +} diff --git a/Tools/StringTool.cs b/Tools/StringTool.cs new file mode 100644 index 0000000..d98da01 --- /dev/null +++ b/Tools/StringTool.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Yaulw.Tools +{ + /// + /// Useful String Operations + /// + public static class StringTool + { + /// + /// Searches for the 'value' occurences that occur in 'str' + /// + /// string to search occurences for + /// string to search in + /// true to ignore case, false otherwise + /// the number of occurences of 'value' in 'str', 0 for none + public static uint NumberOfSpecStringFoundInString(string value, string str, bool bIgnoreCase = false) + { + uint nCount = 0; + if (!String.IsNullOrEmpty(str)) + { + int nIndex = 0; + StringComparison comparison = bIgnoreCase ? StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture; + do + { + nIndex = str.IndexOf(value, nIndex, comparison); + if(nIndex != -1) + ++nCount; + } while (nIndex != -1); + } + return nCount; + } + + /// + /// Check to see if string starts with a letter + /// + /// string to check + /// true if string starts with a letter + public static bool StringStartsWithLetter(string str) + { + if (!String.IsNullOrEmpty(str)) + return char.IsLetter(str[0]); + return false; + } + + /// + /// Check to see if string ends with a letter + /// + /// string to check + /// true if string ends with a letter + public static bool StringEndsWithLetter(string str) + { + if (!String.IsNullOrEmpty(str)) + return char.IsLetter(str[str.Length - 1]); + return false; + } + + /// + /// Returns the last word of a string (string must end with a letter) + /// + /// string to go thru + /// the last word of a string + public static string StringFetchLastWord(string str) + { + if (!String.IsNullOrEmpty(str) && StringEndsWithLetter(str)) + { + // Find the last word + StringBuilder sb = new StringBuilder(); + for (int i = str.Length - 1; i >= 0; --i) + { + if (char.IsLetter(str[i])) + sb.Append(str[i]); + else + break; + } + + // Reverse the String + string strRet = sb.ToString(); +#if NET4 + sb.Clear(); +#else + sb = new StringBuilder(); +#endif + for (int i = strRet.Length - 1; i >=0; --i) + sb.Append(strRet[i]); + + // return the string + return sb.ToString(); + } + return String.Empty; + } + + /// + /// Returns the first word of a string (string must start with a letter) + /// + /// string to go thru + /// the first word of a string + public static string StringFetchFirstWord(string str) + { + if (!String.IsNullOrEmpty(str) && StringStartsWithLetter(str)) + { + // Find the last word + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.Length; ++i) + { + if (char.IsLetter(str[i])) + sb.Append(str[i]); + else + break; + } + + // return the string + return sb.ToString(); + } + return String.Empty; + } + + /// + /// Stripes the last word from the referenced string (string must end with a letter) + /// + /// last word stripped from string + public static void StringStripeLastWord(ref string str) + { + if (!String.IsNullOrEmpty(str) && StringEndsWithLetter(str)) + { + // Find the last word + int i = 0; + for (i = str.Length - 1; i >= 0; --i) + { + if (!char.IsLetter(str[i])) + break; + } + + // Return string without last word + str = str.Substring(0, i + 1); + } + } + + /// + /// Stripes the first word from the referenced string (string must start with a letter) + /// + /// first word stripped from string + public static void StringStripeFirstWord(ref string str) + { + if (!String.IsNullOrEmpty(str) && StringStartsWithLetter(str)) + { + // Find the last word + int i = 0; + for (i = 0; i < str.Length; ++i) + { + if (!char.IsLetter(str[i])) + break; + } + + // Return string without first word + str = str.Substring(i); + } + } + + } + +} diff --git a/WPF/KeepHidden.cs b/WPF/KeepHidden.cs new file mode 100644 index 0000000..bdc3290 --- /dev/null +++ b/WPF/KeepHidden.cs @@ -0,0 +1,319 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Interop; +using System.Windows.Threading; +using System.Timers; +using Yaulw.Thread; + +namespace Yaulw.WPF +{ + /// + /// Class used to keep WPF Windows Hidden permanently. + /// This should ideally be called in the Constructor of a window you want to keep hidden. + /// + /// + /// private static KeepHidden _HiddenWindow = null; + /// public HiddenMainWindow() + /// { + /// // * Force this window to remain hidden indefinitly* + /// _HiddenWindow = new KeepHidden(this); + /// } + /// + /// + /// + public class KeepHidden + { + #region Private Statics + + // Static Map of All our Hidden Windows + private static Dictionary s_hWndToWindowMap = new Dictionary(); + private static List s_WindowList = new List(); + + private Window _wpfWindow = null; + private object _lockObj = new Object(); + + #endregion + + #region Public Properties + + /// + /// Get the WPF Object That is bound to this Keep Hidden Instance + /// + public Window wpfWindow { get { return _wpfWindow; } } + + /// + /// Get the HWND Handle to the WPF Object bound to this Keep Hidden Instance + /// + public IntPtr hwndWindow + { + get + { + if (_wpfWindow != null) + { + WindowInteropHelper interop = new WindowInteropHelper(_wpfWindow); + return interop.Handle; + } + return IntPtr.Zero; + } + } + + #endregion + + #region Construction + + /// + /// When Passing in a Wpf Window in this Construction, it will ensure that + /// This Window will remain hidden during it's life span. If the Window is Not Loaded + /// yet, call KeepHidden's Show() method to show it, after construction. + /// + /// a WPF Window Object that you want to keep Hidden + public KeepHidden(Window wpfWindow) + { + if (wpfWindow != null && !wpfWindow.IsLoaded) + { + // Assign the Load Event Handler we care about + wpfWindow.Loaded += new RoutedEventHandler(wpfWindow_Loaded); + } + else if (wpfWindow != null && wpfWindow.IsLoaded) + { + // Call the Loaded Event Handler Directly + wpfWindow_Loaded((object)wpfWindow, null); + } + else + throw new ArgumentException("Please pass valid WPF Window Object"); + + // Make sure we aren't already tracking this window + if (s_WindowList.Contains(wpfWindow)) + throw new ArgumentException("WPF Window Object already Kept Hidden"); + + // Assign remaining stuff + wpfWindow.Closing += new System.ComponentModel.CancelEventHandler(wpfWindow_Closing); + AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit); + _wpfWindow = wpfWindow; + + // Add to WPF Window List + s_WindowList.Add(wpfWindow); + + // Call this upon construction ~Done Way before Show() get's called + HideWPFWindow(_wpfWindow); + } + + #endregion + + #region Public Methods + + /// + /// Call this to execute a Show() call on the WPF Window that you want to Keep Hidden + /// + public void Show() + { + if (_wpfWindow != null && !_wpfWindow.IsLoaded) + { + lock (_lockObj) + { + // Make sure it can't be seen or interacted with + HideWPFWindow(_wpfWindow); + + // Show it + _wpfWindow.Show(); + + // Again... make sure... + HideWPFWindow(_wpfWindow); + + // Hide it immediatly, make sure it can't be interacted with + _wpfWindow.Hide(); + } + } + } + + /// + /// Call this to execute a Show() call on the WPF Window that you want to Keep Hidden + /// ~However, it will delay call the show allowing the hidden window's constructor to finish + /// + /// Milliseconds to delay show the HiddenWindow (at least 250 recommended) + public void Show(uint nDelayInMilliseconds) + { + // Calling Show() directly in the Constructor causes a .Net Run-time Crash + // upon first launch only. Calling it .5 seconds later does the trick of avoiding that error, + // because by then the object is fully constructed + ElapsedEventHandler DelayShowHiddenWindow = delegate(object sender, ElapsedEventArgs e) + { + Timer timer = (Timer)sender; + timer.Stop(); + + // * Show the Hidden Window * + Show(); + + timer.Dispose(); + timer = null; + }; + TTimerDisp timerDelayShowHiddenWindow = new TTimerDisp(DelayShowHiddenWindow, (int) nDelayInMilliseconds, true); + } + + /// + /// Call this to execute a Close() call on the WPF Window that you previously had hidden + /// ~Should only call this when you are done with the Keep Hidden Object + /// + public void Close() + { + if (_wpfWindow != null && _wpfWindow.IsLoaded) + { + _wpfWindow.Close(); + _wpfWindow = null; + } + } + + #endregion + + #region Public Static Methods + + /// + /// Call this function to determine if the passed in Window is a Hidden Window Object * Tracked by KeepHidden * + /// + /// pass in a wpf window object to check + /// + public static bool IsHiddenWindow(Window _wpfWindow) + { + if (_wpfWindow != null) + { + bool bIsFound = s_WindowList.Contains(_wpfWindow); + return bIsFound; + } + return false; + } + + #endregion + + #region Private Keep Hidden Event Handlers + + /// + /// Called by the WPFWindow's OnLoad Event + /// + private void wpfWindow_Loaded(object sender, RoutedEventArgs e) + { + lock (_lockObj) + { + Window wpfWindow = (Window)sender; + HideWPFWindow(wpfWindow); + + // Add Window Source Message Hook * We'll track the hWnd for the Message Hook + // vie s_hWndToWindowMap + WindowInteropHelper interop = new WindowInteropHelper(wpfWindow); + s_hWndToWindowMap[interop.Handle] = wpfWindow; + HwndSource source = HwndSource.FromHwnd(interop.Handle); + source.AddHook(new HwndSourceHook(MessageHook)); + } + } + + /// + /// Called by the WPFWindow's Closing Event + /// + private void wpfWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + lock (_lockObj) + { + // Delete from the hWndMap, so Message Hook + // Stops processing messages + Window wpfWindow = (Window)sender; + if (s_hWndToWindowMap.ContainsValue(wpfWindow)) + { + foreach (IntPtr hWnd in s_hWndToWindowMap.Keys) + { + if (s_hWndToWindowMap[hWnd] == wpfWindow) + { + s_hWndToWindowMap.Remove(hWnd); + break; + } + } + } + + // * Imp * Delete internal wpfWindow Reference, + // but DON'T Delete from the s_WindowList. Caller could still call IsHiddenWindow(), + // and we MUST return true if it is + _wpfWindow = null; + } + } + + /// + /// Called by the Process Exiting * We want this to happen last * + /// This allows all unload events for each window to happen first + /// + void CurrentDomain_ProcessExit(object sender, EventArgs e) + { + // Make sure to clear all windows + s_hWndToWindowMap.Clear(); + + // Make sure to clear all windows + s_WindowList.Clear(); + } + + /// + /// This Message Hook is for the WpfWindow to absolutely ensure that it remains Hidden. + /// We want to make sure that it is never displayed *It can occur that explorer.exe can show it, without this hook* + /// ~i.e. when explorer.exe crashes, and get's restarted, it can cause inconsitencies + /// + private IntPtr MessageHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + // Get the Proper WPF Object and make sure it is Hidden + if (s_hWndToWindowMap.ContainsKey(hwnd)) + { + Window wpfWindow = s_hWndToWindowMap[hwnd]; + if (wpfWindow != null) + HideWPFWindow(wpfWindow); + } + handled = false; + return System.IntPtr.Zero; + } + + #endregion + + #region Private Hide Window Functionality + + /// + /// * Core * 'Hiding' Function. Makes sure that the Window is and + /// stays hidden. + /// + /// + /// Height="2" Width="2" AllowsTransparency="True" Background="{x:Null}" WindowStyle="None" WindowStartupLocation="Manual" WindowState="Normal" ResizeMode="NoResize" Foreground="{x:Null}" ShowInTaskbar="False" ShowActivated="False" Left="0" Top="0" MaxWidth="2" MaxHeight="2" MinHeight="2" MinWidth="2" Visibility="Hidden" Opacity="0" IsHitTestVisible="False" IsTabStop="False" Focusable="False" IsEnabled="False" + /// + /// a WPF window object + private void HideWPFWindow(Window wpfWindow) + { + // Force a tiny Window in top-left corner + wpfWindow.Width = 2; + wpfWindow.Height = 2; + wpfWindow.MinWidth = 2; + wpfWindow.MinHeight = 2; + wpfWindow.MaxWidth = 2; + wpfWindow.MaxHeight = 2; + wpfWindow.Top = 0; + wpfWindow.Left = 0; + + // Force transparency + if (!wpfWindow.IsLoaded) // must not be already loaded + wpfWindow.AllowsTransparency = true; + wpfWindow.Background = null; + wpfWindow.Foreground = null; + + // Force no user interaction + wpfWindow.WindowStartupLocation = WindowStartupLocation.Manual; + wpfWindow.WindowState = WindowState.Normal; + wpfWindow.ResizeMode = ResizeMode.NoResize; + wpfWindow.ShowInTaskbar = false; + wpfWindow.ShowActivated = false; + wpfWindow.Visibility = Visibility.Hidden; + wpfWindow.IsHitTestVisible = false; + wpfWindow.IsTabStop = false; + wpfWindow.Focusable = false; + + // Force Opacity + wpfWindow.WindowStyle = WindowStyle.None; + wpfWindow.Opacity = 0.0; + } + + #endregion + } +} diff --git a/WPF/Snippets.cs b/WPF/Snippets.cs new file mode 100644 index 0000000..29c9c38 --- /dev/null +++ b/WPF/Snippets.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Controls; +using System.Windows; +using System.Windows.Media.Imaging; +using System.Windows.Media; +using System.Windows.Interop; + +namespace Yaulw.WPF +{ + /// + /// Useful WPF Snippets + /// + static public class Snippets + { + /// + /// Useful to create a simple wrapping tooltip for a wpf control + /// + /// + /// Set to 0 to leave default, else set to 0> to define max height + /// Set to 0 to leave default, else set to 0> to define max width + /// a ToolTip Object to assign to a control + public static ToolTip CreateNewStringToolTip(string strToolTipContent, int height, int width) + { + // Create the TextBlock + TextBlock tx = new TextBlock(); + if (height > 0) + tx.Height = height; + if (width > 0) + tx.Width = width; + tx.TextWrapping = TextWrapping.Wrap; + tx.Text = strToolTipContent; + + // Create the ToolTip with the TextBlock inside + ToolTip tt = new ToolTip(); + tt.Content = strToolTipContent; + return tt; + } + + /// + /// Useful if you want to know if the Mouse cursor is over the specified Wpf Window + /// (Uses windowsforms to do the calculations) + /// + /// true if the mouse cursor is over the specified form window, false otherwise + public static bool IsMouseCursorOverWPFormWindow(Window wpfWindow) + { + if (wpfWindow != null) + { + // Get the Point location of the mouse cursor + System.Drawing.Point point = System.Windows.Forms.Cursor.Position; + + // Get the rect for the Navigation Form + Point wpfFormLeftTopPoint = new Point(wpfWindow.Left, wpfWindow.Top); + Size wpfFormSize = new Size(wpfWindow.Width, wpfWindow.Height); + Rect rect = new Rect(wpfFormLeftTopPoint, wpfFormSize); + + // Return if the Mouse is over the Navigation Form + return (rect.Contains((double)point.X, (double)point.Y)); + } + return false; + } + + /// + /// For Dynamically Creating Text ComboBox Items + /// + /// Text Content to Display + /// object to tag to the menu item + /// + public static ComboBoxItem CreateAComboBoxItem(string strContent, object tag) + { + if (!String.IsNullOrEmpty(strContent) && (tag != null)) + { + ComboBoxItem item = new ComboBoxItem(); + item.Content = strContent; + item.Tag = tag; + return item; + } + return null; + } + + /// + /// For dynamically creating Image sources, this function helps you to create an image out of + /// a Resource Uri. You can then assign this image to a 'Source' property on an image wpf object + /// + /// a valid relative Resource Uri String to an image + /// the output image's desired height + /// the output image's desired width + /// + public static Image CreateWPFImageFromRelativeResourceUri(string strRelativeResourceUri, int ImageHeight, int ImageWidth) + { + if (!String.IsNullOrEmpty(strRelativeResourceUri)) + { + Image myImage = new Image(); + BitmapImage image = new BitmapImage(); + image.BeginInit(); + image.UriSource = new Uri(strRelativeResourceUri, UriKind.Relative); + image.EndInit(); + myImage.Stretch = Stretch.Fill; + myImage.Height = ImageHeight; + myImage.Width = ImageWidth; + myImage.Source = image; + return myImage; + } + return null; + } + + /// + /// Uses the InteropHelper to get the Handle of a WPF Window + /// + /// a WPF Window + /// an hWnd for the specified WPF Window + public static IntPtr GetHandleForWPFWindow(Window window) + { + WindowInteropHelper InteropHelper = new WindowInteropHelper(window); + if (InteropHelper.Handle != IntPtr.Zero) + return InteropHelper.Handle; +#if NET4 + else + return InteropHelper.EnsureHandle(); +#else + else + return IntPtr.Zero; +#endif + } + + /// + /// Uses the InteropHelper to set the Owner Property of a WPF Window + /// + /// a WPF Window + /// Owner hWnd Handle to set for WPF Window + public static void SetOwnerForWPFWindow(Window window, IntPtr hOwnerWnd) + { + WindowInteropHelper InteropHelper = new WindowInteropHelper(window); + InteropHelper.Owner = hOwnerWnd; + } + + /// + /// Adds the Specified Message Hook the the specified WPF Window. + /// Ensures that the WPF Window exists (must be loaded with valid hWnd). + /// + /// a WPF Window + /// a message hook function + public static void SetMessageHookForWPFWindow(Window window, HwndSourceHook hook) + { + WindowInteropHelper interop = new WindowInteropHelper(window); +#if NET4 + HwndSource source = HwndSource.FromHwnd(interop.EnsureHandle()); +#else + HwndSource source = HwndSource.FromHwnd(interop.Handle); +#endif + source.AddHook(hook); + } + } +} diff --git a/WPF/WPFWindowManager.cs b/WPF/WPFWindowManager.cs new file mode 100644 index 0000000..8148155 --- /dev/null +++ b/WPF/WPFWindowManager.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Threading; +using Yaulw.Thread; +using System.Windows; +using Yaulw.Other; +using System.Collections; +using System.Windows.Interop; + +namespace Yaulw.WPF +{ + + /// + /// Responsible for Managing WPFWindows + /// + public class WPFWindowManager : WindowManager + { + #region Construction + + /// + /// WPFWindow Manager Contructor + /// + /// Thread Dispatcher Object + /// Window Types this WindowManager is managing + /// true, to load performance enabled windows upon construction + public WPFWindowManager(Dispatcher dispatcher, WindowType[] windowTypes, bool PerformanceLoad) + : base(dispatcher, windowTypes, PerformanceLoad) + { + // Here i can load Show/hide all windows that are performance loaded * To further load stuff WPF related * + if (PerformanceLoad) + { + foreach (WindowType type in windowTypes) + { + List objects = GetObjects(type); + if (objects.Count == 1 && type.IsPerformanceLoaded) + { + // Show/Hide Window! + } + } + } + } + + #endregion + + #region Public Overriden Methods + + /// + /// + /// + /// Window to run action on + /// Window Attributes to use + /// object to pass into Window + /// the tag object from the window + public override object ShowWindow(Enum windowKey, WindowAttributes attributes, object tag) + { + DelegateCollection.Obj_Func func = delegate() + { + object o = CreateInstance(windowKey); + if (AddObject(windowKey, o)) + { + Window wpfWin = (Window)o; + wpfWin.Tag = tag; + + // Set Attributes + SetAttributesOnWindow(wpfWin, attributes); + + // Show + wpfWin.Show(); + object retVal = wpfWin.Tag; + wpfWin = null; + return retVal; + } + return null; + }; + + if (Dispatcher.Thread == System.Threading.Thread.CurrentThread) + { + return func(); + } + else + { + object[] parameters = new object[] { windowKey, attributes, tag }; + return Dispatcher.Invoke((ShowDelegate)ShowWindow, DispatcherPriority.Normal, parameters); + } + } + + /// + /// Show the WPF Window as a Dialog + /// + /// Window to run action on + /// Window Attributes to use + /// object to pass into Window + /// the tag object from the window + public override object ShowDialog(Enum windowKey, WindowAttributes attributes, object tag) + { + DelegateCollection.Obj_Func func = delegate() + { + object o = CreateInstance(windowKey); + if (AddObject(windowKey, o)) + { + Window wpfWin = (Window)o; + wpfWin.Tag = tag; + + // Set Attributes + SetAttributesOnWindow(wpfWin, attributes); + + // ShowDialog() + wpfWin.ShowDialog(); + object retVal = wpfWin.Tag; + DeleteObject(windowKey); + wpfWin = null; + return retVal; + } + return null; + }; + + if (Dispatcher.Thread == System.Threading.Thread.CurrentThread) + { + return func(); + } + else + { + object[] parameters = new object[] { windowKey, attributes, tag }; + return Dispatcher.Invoke((ShowDelegate)ShowDialog, DispatcherPriority.Normal, parameters); + } + } + + /// + /// Run any generic action on a window + /// + /// Window to run action on + /// action to perform on the windo + public override bool RunAction(Enum windowKey, DelegateCollection.Bool_Param1_Window_Func action) + { + if (Dispatcher.Thread == System.Threading.Thread.CurrentThread) + { + List objects = GetObjects(windowKey); + object o = null; + if (objects != null) + o = objects.Last(); + if (o != null && o is Window && action != null) + return action((Window)o); + else + return action(null); + } + else + { + object[] parameters = new object[] { windowKey, action }; + return (bool)Dispatcher.Invoke((ActionDelegate)RunAction, DispatcherPriority.Normal, parameters); + } + } + + /// + /// Hide the Specified Window + /// + /// Window to run action on + /// true, if successful, false otherwise + public override bool HideWindow(Enum windowKey) + { + DelegateCollection.Bool_Param1_Window_Func func = delegate(Window wpfWin) + { + wpfWin.Hide(); + return true; + }; + return RunAction(windowKey, func); + } + + /// + /// Close the Specified Window + /// + /// Window to run action on + /// true, if successful, false otherwise + public override bool CloseWindow(Enum windowKey) + { + DelegateCollection.Bool_Param1_Window_Func func = delegate(Window wpfWin) + { + DeleteObject(windowKey); + return true; + }; + return RunAction(windowKey, func); + } + + /// + /// Dispose of this manager Properly + /// + public override void Dispose() + { + // We must call the base's ClearAllObjects() + DelegateCollection.Bool_Param1_Window_Func func = delegate(Window wpfWin) + { + ClearAllObjects(); + return true; + }; + RunAction(null, func); + } + + #endregion + + #region Private Helpers + + /// + /// Set the Window Attributes as specifed by the WindowAttributes type + /// + /// Attributes to set + /// Window to set them on + private void SetAttributesOnWindow(Window wpfWin, WindowAttributes attributes) + { + // Set Attributes + if (attributes != null && wpfWin != null) + { + WindowInteropHelper InteropHelper = new WindowInteropHelper(wpfWin); + if (attributes.hWndOwner != IntPtr.Zero) + InteropHelper.Owner = (IntPtr)attributes.hWndOwner; + if (attributes.Height != 0) + wpfWin.Height = attributes.Height; + if (attributes.Width != 0) + wpfWin.Width = attributes.Width; + if (attributes.Left != int.MinValue) + wpfWin.Left = attributes.Left; + if (attributes.Top != int.MinValue) + wpfWin.Top = attributes.Top; + if (!String.IsNullOrEmpty(attributes.Title)) + wpfWin.Title = attributes.Title; + } + } + + #endregion + } + +} diff --git a/Web/HTTP.cs b/Web/HTTP.cs new file mode 100644 index 0000000..7a2bf0e --- /dev/null +++ b/Web/HTTP.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; + +namespace Yaulw.Web +{ + public class HTTP + { + + + /// + /// urlCreated.Text = Url; + /// var toPost = Url.Split('?'); + /// var result = HttpPost(toPost[0], toPost[1]); + /// + /// + /// + /// String.Empty if error occured, non-empty string otherwise + string HttpPost(string uri, string parameters) + { + var cookies = new CookieContainer(); + var webRequest = (HttpWebRequest)WebRequest.Create(uri); + + webRequest.CookieContainer = cookies; + webRequest.ContentType = "application/x-www-form-urlencoded"; + webRequest.Method = "POST"; + byte[] bytes = Encoding.ASCII.GetBytes(parameters); + Stream os = null; + try + { + webRequest.ContentLength = bytes.Length; //Count bytes to send + os = webRequest.GetRequestStream(); + os.Write(bytes, 0, bytes.Length); //Send it + } + catch (Exception) { /* ignore */ } + finally + { + if (os != null) + { + os.Close(); + } + } + + try + { + WebResponse webResponse = webRequest.GetResponse(); + if (webResponse == null) + { return null; } + StreamReader sr = new StreamReader(webResponse.GetResponseStream()); + return sr.ReadToEnd().Trim(); + } + catch (Exception) { /* ignore */ } + return String.Empty; + } + } +} diff --git a/Web/WebClientWithTimeout.cs b/Web/WebClientWithTimeout.cs new file mode 100644 index 0000000..053339f --- /dev/null +++ b/Web/WebClientWithTimeout.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; + +namespace Yaulw.Web +{ + /// + /// + /// + public class WebClientWithTimeout : WebClient + { + /// + /// + /// + public int Timeout { get; private set; } + + /// + /// + /// + /// + public WebClientWithTimeout(int timeout) + { + this.Timeout = timeout; + } + + /// + /// + /// + /// + /// + protected override WebRequest GetWebRequest(Uri address) + { + var result = base.GetWebRequest(address); + result.Timeout = this.Timeout; + return result; + } + + + + } +} diff --git a/Win32/Advapi32.cs b/Win32/Advapi32.cs new file mode 100644 index 0000000..d52d099 --- /dev/null +++ b/Win32/Advapi32.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + + +namespace Yaulw.Win32 +{ + /// + /// Advapi.dll Entry Points * http://pinvoke.net/ * + /// + public static class Advapi32 + { + /// + /// Retrieves the current status of the specified service based on the specified information level + /// + /// A handle to the service + /// The service attributes to be returned + /// A pointer to the buffer that receives the status information + /// he size of the buffer pointed to by the lpBuffer + /// A pointer to a variable that receives the number of bytes needed to store all status information + /// If the function succeeds, the return value is nonzero + [DllImport("Advapi32.dll")] + extern public static bool QueryServiceStatusEx(IntPtr hService, int InfoLevel, ref Structures.SERVICE_STATUS_PROCESS lpBuffer, int cbBufSize, out int pcbBytesNeeded); + } +} diff --git a/Win32/AtomMessenger.cs b/Win32/AtomMessenger.cs new file mode 100644 index 0000000..d2e113f --- /dev/null +++ b/Win32/AtomMessenger.cs @@ -0,0 +1,321 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace Yaulw.Win32 +{ + /// + /// Quick and Easy Atom Window Messages Communication between windows of different + /// Processes. + /// + public class AtomMessenger : IDisposable + { + #region Private Members + private const string _AtomMsgIDFormat = "AtomMsgID-[{0}]"; + private string _AtomMsgStr = ""; + private uint _AtomMsg = 0; + private bool _disposed = false; + + // For Posted Atom Messages + private const int MAGIC_NUMBER_POSTED_ATOM_KEYS = 7; + private List _postedAtomKeys = new List(); + #endregion + + #region Construction + + /// + /// Create an Atom Messaging Component with a Unique Identifier. + /// The same Identifier should be used by all applications that want to pass + /// Messages back and forth. + /// ~You need to dispose of this Object only if you plan on using PostAtomMessage() + /// + /// An identifier for the Messaging + public AtomMessenger(Guid UniqueWindowMessageIdentifier) + { + if (UniqueWindowMessageIdentifier == null) + throw new ArgumentNullException(); + + // Register the Unique Window Message + _AtomMsgStr = String.Format(_AtomMsgIDFormat, UniqueWindowMessageIdentifier); + _AtomMsg = User32.RegisterWindowMessage(_AtomMsgStr); + if (_AtomMsg == 0) + throw new Exception("RegisterWindowMessage Failed"); + } + + /// + /// Finalizer + /// + ~AtomMessenger() + { + Dispose(true); + } + + #endregion + + #region Atom Message Construction Helpers + + /// + /// Retrieve ';' seperated values from an Atom Message + /// + /// an atom message that contains multiple args + /// multiple args + public string[] RetrieveMultipleValuesFromAtomMessage(string AtomMessage) + { + if (!String.IsNullOrEmpty(AtomMessage)) + return AtomMessage.Split(';'); + else + return null; + } + + /// + /// Create ';' seperated Atom message from multiple values + /// + /// an Atom Message containing multiple args + public string SetMultipleValueInAtomMessage(params object[] args) + { + if (args != null) + { + StringBuilder sb = new StringBuilder(); + foreach (object a in args) + { + sb.Append(a.ToString()); + sb.Append(";"); + } + + // Remove Trailing ";" + sb = sb.Remove(sb.Length - 1, 1); + return sb.ToString(); + } + return String.Empty; + } + + #endregion + + #region Atom Message Identify / Retrieval + + /// + /// Use this to check if the passed in Message is an Atom Message. + /// The identifier of the Message must match the Message registered with the UniqueIdentifier + /// + /// a Windows.Forms Message + /// true if the Messages matches the one created by the UniqueIdentifier + public bool IsAtomMessage(ref Message m) + { + return (m.Msg == _AtomMsg); + } + + /// + /// Retrieves the values passed in by an Atom Message + /// + /// a Windows.Forms Message + /// the Message Retrieved from the Atom + /// the source window who send it, if valid + public void GetAtomMessageValues(ref Message m, out string AtomMessage, out IntPtr hWndSrc) + { + AtomMessage = String.Empty; + hWndSrc = IntPtr.Zero; + + // Check to make sure this is an Atom Message + if (!IsAtomMessage(ref m)) + return; + + // Source is passed to us by wParam + if(m.WParam != IntPtr.Zero && User32.IsWindow(m.WParam)) + hWndSrc = m.WParam; + + // Now retrieve the Atom Message + StringBuilder sb = new StringBuilder( 254 ); + uint AtomKey = (uint) m.LParam; + uint slen = Kernel32.GlobalGetAtomName(AtomKey, sb, 254); + if (slen == 0) + return; + + // Write out Retrieved Message + AtomMessage = sb.ToString(); + } + + /// + /// Retrieves the values passed in by an Atom Message + /// + /// a Windows.Forms Message + /// the Message with multiple values retrieved from the Atom + /// the source window who send it, if valid + public void GetAtomMessageValues(ref Message m, out string[] AtomMessage, out IntPtr hWndSrc) + { + AtomMessage = null; + hWndSrc = IntPtr.Zero; + + // Check to make sure this is an Atom Message + if (!IsAtomMessage(ref m)) + return; + + // Source is passed to us by wParam + if (m.WParam != IntPtr.Zero && User32.IsWindow(m.WParam)) + hWndSrc = m.WParam; + + // Now retrieve the Atom Message + StringBuilder sb = new StringBuilder(254); + uint AtomKey = (uint)m.LParam; + uint slen = Kernel32.GlobalGetAtomName(AtomKey, sb, 254); + if (slen == 0) + return; + + // Write out Retrieved Message + AtomMessage = RetrieveMultipleValuesFromAtomMessage(sb.ToString()); + } + + #endregion + + #region Send/Post Atom Message + + /// + /// Sends an Atom Message To the Specified Window or All Windows + /// + /// Sender Windows (Nice to have), in case Receiver Needs it + /// Can be IntPtr.Zero to BroadCast the Message, otherwise specify window to send to + /// The Message to Send * Can not be longer than 254 chars * + public void SendAtomMessage(IntPtr hWndSrc, IntPtr hWndDst, string AtomMessage) + { + // Is Broadcast? + bool bBroadcast = (hWndDst==IntPtr.Zero); + + // Check to make sure Atom Message is proper + if(String.IsNullOrEmpty(AtomMessage) || AtomMessage.Length > 254) + throw new ArgumentException("AtomMessage Invalid"); + + // Register Atom + uint AtomKey = Kernel32.GlobalAddAtom(AtomMessage); + if (AtomKey == 0) + throw new Exception("GlobalAddAtom Failed"); + + // Send the Message + if(bBroadcast) + User32.SendMessage(Definitions.HWND_BROADCAST, (int)_AtomMsg, hWndSrc, (IntPtr)AtomKey); + else + User32.SendMessage(hWndDst, (int)_AtomMsg, hWndSrc, (IntPtr)AtomKey); + + // We are done with the Atom + Kernel32.GlobalDeleteAtom(AtomKey); + } + + /// + /// Sends an Atom Message To the Specified Window or All Windows + /// + /// Sender Windows (Nice to have), in case Receiver Needs it + /// Can be IntPtr.Zero to BroadCast the Message, otherwise specify window to send to + /// The Message to Send that has multiple values * Total string can not be longer than 254 chars * + public void SendAtomMessage(IntPtr hWndSrc, IntPtr hWndDst, string[] AtomMessage) + { + SendAtomMessage(hWndSrc, hWndDst, SetMultipleValueInAtomMessage(AtomMessage)); + } + + /// + /// Post an Atom Message To the Specified Window or All Windows + /// + /// Sender Windows (Nice to have), in case Receiver Needs it + /// Can be IntPtr.Zero to BroadCast the Message, otherwise specify window to send to + /// The Message to Send * Can not be longer than 254 chars * + public void PostAtomMessage(IntPtr hWndSrc, IntPtr hWndDst, string AtomMessage) + { + // Is Broadcast? + bool bBroadcast = (hWndDst == IntPtr.Zero); + + // Check to make sure Atom Message is proper + if (String.IsNullOrEmpty(AtomMessage) || AtomMessage.Length > 254) + throw new ArgumentException("AtomMessage Invalid"); + + // Register a new Atom + uint nAtomKey = Kernel32.GlobalAddAtom(AtomMessage); + if (nAtomKey == 0) + throw new Exception("GlobalAddAtom Failed"); + + // Send the Message + if (bBroadcast) + User32.PostMessage(Definitions.HWND_BROADCAST, (int)_AtomMsg, hWndSrc, (IntPtr)nAtomKey); + else + User32.PostMessage(hWndDst, (int)_AtomMsg, hWndSrc, (IntPtr)nAtomKey); + + // Imp! Atom still must get Deleted, that is why we add it to DS + AddPostedAtomKey(nAtomKey); + } + + /// + /// Post an Atom Message To the Specified Window or All Windows + /// + /// Sender Windows (Nice to have), in case Receiver Needs it + /// Can be IntPtr.Zero to BroadCast the Message, otherwise specify window to send to + /// The Message to Send that has multiple values * Can not be longer than 254 chars * + public void PostAtomMessage(IntPtr hWndSrc, IntPtr hWndDst, string[] AtomMessage) + { + PostAtomMessage(hWndSrc, hWndDst, SetMultipleValueInAtomMessage(AtomMessage)); + } + + #endregion + + #region Posted Atom Keys handling + + /// + /// Adds the Specified Posted Atom Key to the Posted Atoms DS. + /// Clears the DS, if MAGIC_NUMBER_POSTED_ATOM_KEY has been reached. + /// + /// a unique AtomKey + private void AddPostedAtomKey(uint nKey) + { + if (_postedAtomKeys.Count >= MAGIC_NUMBER_POSTED_ATOM_KEYS) + DeleteAllPostedAtomKeys(); + + _postedAtomKeys.Add(nKey); + } + + /// + /// Deletes all posted Atoms and Clears the PostedAtoms DS + /// + private void DeleteAllPostedAtomKeys() + { + foreach (uint Key in _postedAtomKeys) + Kernel32.GlobalDeleteAtom(Key); + + _postedAtomKeys.Clear(); + } + + #endregion + + #region IDisposable Members + + /// + /// Dispose Posted Atom Strings + /// + public void Dispose() + { + Dispose(true); + + // Use SupressFinalize in case a subclass + // of this type implements a finalizer + GC.SuppressFinalize(this); + } + + /// + /// Dispose Posted Atom Strings + /// + /// true, if called from within + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + if (_postedAtomKeys.Count != 0) + DeleteAllPostedAtomKeys(); + } + + // Indicate that the instance has been disposed. + _postedAtomKeys = null; + _disposed = true; + } + } + + #endregion + } +} diff --git a/Win32/COM.cs b/Win32/COM.cs new file mode 100644 index 0000000..60f3f17 --- /dev/null +++ b/Win32/COM.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; + +namespace Yaulw.Win32 +{ + /// + /// COM Win32 Helper Functions + /// + public static class COM + { + /// + /// Returns a pointer to an implementation of IBindCtx (a bind context object). This object stores information about a particular moniker-binding operation + /// + /// This parameter is reserved and must be 0 + /// Address of an IBindCtx* pointer variable that receives the interface pointer to the new bind context object. + /// This function can return the standard return values E_OUTOFMEMORY and S_OK + [DllImport("ole32.dll")] + public static extern int CreateBindCtx(int reserved, out IBindCtx ppbc); + + /// + /// Use this to retrieve the Actice COM Object from the ROT, for the specified progId + /// + /// + /// a valid com object, or null if error occured + public static Object GetActiceCOMObject(string progId) + { + Object app = null; + try + { + app = Marshal.GetActiveObject(progId); + } + catch (SystemException) { /* ignore */ } + return app; + } + + /// + /// ROT Object Entry + /// + public struct RunningObject + { + public string name; + public object o; + } + + /// + /// Use this to Get All Running Objects in the ROT + /// + /// list of all Runnint Objects in the ROT + public static List GetRunningObjects() + { + // Get the table. + var res = new List(); + IBindCtx bc; + + CreateBindCtx(0, out bc); + IRunningObjectTable runningObjectTable; + + bc.GetRunningObjectTable(out runningObjectTable); + IEnumMoniker monikerEnumerator; + runningObjectTable.EnumRunning(out monikerEnumerator); + monikerEnumerator.Reset(); + + // Enumerate and fill our nice dictionary. + IMoniker[] monikers = new IMoniker[1]; + IntPtr numFetched = IntPtr.Zero; + + while (monikerEnumerator.Next(1, monikers, numFetched) == 0) + { + RunningObject running; + monikers[0].GetDisplayName(bc, null, out running.name); + runningObjectTable.GetObject(monikers[0], out running.o); + res.Add(running); + } + return res; + } + + /// + /// Use this to Get A specific type of Object from the ROT + /// + /// List of a specific type of Object in the ROT + public static List GetRunningObjectsOfType() + { + // Get the table. + var res = new List(); + IBindCtx bc; + + CreateBindCtx(0, out bc); + IRunningObjectTable runningObjectTable; + + bc.GetRunningObjectTable(out runningObjectTable); + IEnumMoniker monikerEnumerator; + runningObjectTable.EnumRunning(out monikerEnumerator); + monikerEnumerator.Reset(); + + // Enumerate and fill our nice dictionary. + IMoniker[] monikers = new IMoniker[1]; + IntPtr numFetched = IntPtr.Zero; + + while (monikerEnumerator.Next(1, monikers, numFetched) == 0) + { + object o; + runningObjectTable.GetObject(monikers[0], out o); + + if (o is T) + res.Add((T)o); + o = null; + } + return res; + } + } + +} diff --git a/Win32/Convert.cs b/Win32/Convert.cs new file mode 100644 index 0000000..90af9df --- /dev/null +++ b/Win32/Convert.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Drawing; + +namespace Yaulw.Win32 +{ + /// + /// Helper class to Convert Win32 Structures to DotNet and vice versa + /// + public static class Convert + { + /// + /// Use this to Convert to Win32 Window Message Enum from a C# Message + /// + /// a C# Message + /// a Win32 Message Enum + public static Definitions.WM MessageToWin32WM(Message m) + { + Definitions.WM wm = Definitions.WM.WM_NULL; + try { wm = (Definitions.WM)m.Msg; } + catch (Exception) { wm = Definitions.WM.WM_NULL; } + return wm; + } + + /// + /// Use this to Convert to a C# Rectangle from a Win32 RECT + /// + /// a Win32 Rect + /// a C# Rectancle + public static Rectangle RECTToRectangle(Structures.RECT rect) + { + return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + } + + /// + /// Use this to Convert a Win32 RECT to a C# Rectangle + /// + /// a C# Rectangle + /// a Win32 Rect + public static Structures.RECT RectangleToRECT(Rectangle rect) + { + return Structures.RECT.FromXYWH(rect.Left, rect.Top, rect.Width, rect.Height); + } + + #region Win32Owner + + /// + /// a IWin32Window Object commonly used by WinForms + /// + public class Win32Owner : IWin32Window + { + public IntPtr Handle { get; set; } + } + + /// + /// Convert a Handle to a Win32Owner Object + /// + /// pass in a Window Handle + /// a newly created Win32Owner Object + public static IWin32Window ConverthWndToIWin32Window(IntPtr hWnd) + { + return new Win32Owner() { Handle = hWnd }; + } + + #endregion + } +} diff --git a/Win32/Definitions.cs b/Win32/Definitions.cs new file mode 100644 index 0000000..96bef3b --- /dev/null +++ b/Win32/Definitions.cs @@ -0,0 +1,601 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Yaulw.Win32 +{ + /// + /// Win32 Enums and Consts (Definitions) + /// + public static class Definitions + { + #region Constants + + /// + /// Virtual Key Board Key Up Event + /// + public const ushort KEYEVENTF_KEYUP = 0x0002; + + /// + /// Source Copy Option + /// + public const int SRCCOPY = 13369376; + + /// + /// Default Monitor Nearest + /// + public const int MONITOR_DEFAULTTONEAREST = 0x00000002; + + /// + /// Universal Max Path Const used in Many Win32 Paths + /// + public const int MAX_PATH = 255; + + /// + /// For Windows Hooking + /// + public const int WH_CBT = 5; + + /// + /// System is about to activate a window + /// + public const int HCBT_ACTIVATE = 5; + + /// + /// Used for SendMessage/PostMessage + /// + public static readonly IntPtr HWND_BROADCAST = new IntPtr(0xffff); + + #region Service Constants - Advapi32.dll + + /// + /// Service Option + /// + public const int SERVICE_WIN32_OWN_PROCESS = 0x00000010; + + /// + /// Service Option + /// + public const int SERVICE_RUNS_IN_SYSTEM_PROCESS = 0x00000001; + + /// + /// Service Option + /// + public const int SC_STATUS_PROCESS_INFO = 0; + + #endregion + + #endregion + + #region Enums + + /// + /// AssocQueryString Options + /// + public enum AssocF : ushort + { + Init_NoRemapCLSID = 0x1, + Init_ByExeName = 0x2, + Open_ByExeName = 0x2, + Init_DefaultToStar = 0x4, + Init_DefaultToFolder = 0x8, + NoUserSettings = 0x10, + NoTruncate = 0x20, + Verify = 0x40, + RemapRunDll = 0x80, + NoFixUps = 0x100, + IgnoreBaseClass = 0x200 + } + + /// + /// DDE Commands + /// + public enum AssocStr : ushort + { + Command = 1, + Executable, + FriendlyDocName, + FriendlyAppName, + NoOpen, + ShellNewValue, + DDECommand, + DDEIfExec, + DDEApplication, + DDETopic + } + + /// + /// For Process DEP + /// + public enum DEP : int + { + PROCESS_DEP_DISABLE = 0x00000000, + PROCESS_DEP_ENABLE = 0x00000001, + PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION = 0x00000002, + } + + /// + /// Layered Windows + /// + public enum LayeredWindowAttribute_Value : int + { + LWA_COLORKEY = 0x00000001, + LWA_ALPHA = 0x00000002 + } + + /// + /// Mouse Events + /// + public enum MouseEvent : ushort + { + MOUSEEVENTF_LEFTDOWN = 0x02, + MOUSEEVENTF_LEFTUP = 0x04, + MOUSEEVENTF_RIGHTDOWN = 0x08, + MOUSEEVENTF_RIGHTUP = 0x10 + } + + /// + /// OpenProcess Values + /// + public enum OpenProcess_Value : int + { + DELETE = 0x00010000, + READ_CONTROL = 0x00020000, + SYNCHRONIZE = 0x00100000, + WRITE_DAC = 0x00040000, + WRITE_OWNER = 0x00080000 + } + + /// + /// Scrollbar values + /// + public enum ScrollBar_Value : int + { + SB_HORZ = 0, + SB_VERT = 1, + SB_CTL = 2, + SB_BOTH = 3, + } + + /// + /// Set Window Pos Actions + /// + public enum SetWindowPos_Action : uint + { + SWP_NOSIZE = 0x0001, + SWP_NOMOVE = 0x0002, + SWP_NOZORDER = 0x0004, + SWP_NOREDRAW = 0x0008, + SWP_NOACTIVATE = 0x0010, + SWP_FRAMECHANGED = 0x0020, + SWP_SHOWWINDOW = 0x0040, + SWP_HIDEWINDOW = 0x0080, + SWP_NOCOPYBITS = 0x0100, + SWP_NOOWNERZORDER = 0x0200, + SWP_NOSENDCHANGING = 0x0400, + SWP_DRAWFRAME = SWP_FRAMECHANGED, + SWP_NOREPOSITION = SWP_NOOWNERZORDER + } + + /// + /// Show Window Actions + /// + public enum ShowWindow_Action : int + { + SW_HIDE = 0, + SW_SHOWNORMAL = 1, + SW_NORMAL = 1, + SW_SHOWMINIMIZED = 2, + SW_SHOWMAXIMIZED = 3, + SW_MAXIMIZE = 3, + SW_SHOWNOACTIVATE = 4, + SW_SHOW = 5, + SW_MINIMIZE = 6, + SW_SHOWMINNOACTIVE = 7, + SW_SHOWNA = 8, + SW_RESTORE = 9, + SW_SHOWDEFAULT = 10, + SW_FORCEMINIMIZE = 11, + SW_MAX = 11 + } + + /// + /// System Metrix + /// + public enum SystemMetric_Value : int + { + SM_CXSCREEN = 0, + SM_CYSCREEN = 1, + SM_CXVSCROLL = 2, + SM_CYHSCROLL = 3, + SM_CYCAPTION = 4, + SM_CXBORDER = 5, + SM_CYBORDER = 6, + SM_CXDLGFRAME = 7, + SM_CYDLGFRAME = 8, + SM_CYVTHUMB = 9, + SM_CXHTHUMB = 10, + SM_CXICON = 11, + SM_CYICON = 12, + SM_CXCURSOR = 13, + SM_CYCURSOR = 14, + SM_CYMENU = 15, + SM_CXFULLSCREEN = 16, + SM_CYFULLSCREEN = 17, + SM_CYKANJIWINDOW = 18, + SM_MOUSEPRESENT = 19, + SM_CYVSCROLL = 20, + SM_CXHSCROLL = 21, + SM_DEBUG = 22, + SM_SWAPBUTTON = 23, + SM_RESERVED1 = 24, + SM_RESERVED2 = 25, + SM_RESERVED3 = 26, + SM_RESERVED4 = 27, + SM_CXMIN = 28, + SM_CYMIN = 29, + SM_CXSIZE = 30, + SM_CYSIZE = 31, + SM_CXFRAME = 32, + SM_CYFRAME = 33, + SM_CXMINTRACK = 34, + SM_CYMINTRACK = 35, + SM_CXDOUBLECLK = 36, + SM_CYDOUBLECLK = 37, + SM_CXICONSPACING = 38, + SM_CYICONSPACING = 39, + SM_MENUDROPALIGNMENT = 40, + SM_PENWINDOWS = 41, + SM_DBCSENABLED = 42, + SM_CMOUSEBUTTONS = 43 + } + + /// + /// GetWindowLong Values + /// + public enum WindowLong_Value : int + { + GWL_EXSTYLE = -20, + GWL_STYLE = -16, + GWL_WNDPROC = -4, + GWL_HINSTANCE = -6, + GWL_ID = -12, + GWL_USERDATA = -21, + DWL_DLGPROC = 4, + DWL_MSGRESULT = 0, + DWL_USER = 8 + } + + /// + /// Window Positions + /// + public enum WindowPos_Value : int + { + HWND_TOP = 0, + HWND_BOTTOM = 1, + HWND_TOPMOST = -1, + HWND_NOTOPMOST = -2 + } + + /// + /// Windows Styles + /// + public enum WindowStyle_Value : uint + { + WS_MAXIMIZE = 0x01000000, + WS_VISIBLE = 0x10000000, + WS_POPUP = 0x80000000, + WS_BORDER = 0x00800000, + WS_CAPTION = 0x00C00000, + WS_CHILD = 0x40000000, + WS_CHILDWINDOW = 0x40000000, + WS_EX_CLIENTEDGE = 0x00000200, + WS_CLIPCHILDREN = 0x02000000, + WS_CLIPSIBLINGS = 0x04000000, + WS_DISABLED = 0x08000000, + WS_DLGFRAME = 0x00400000, + WS_GROUP = 0x00020000, + } + + /// + /// Windows Messages + /// + public enum WM : int + { + WM_NULL = 0x0000, + WM_CREATE = 0x0001, + WM_DESTROY = 0x0002, + WM_MOVE = 0x0003, + WM_SIZE = 0x0005, + WM_ACTIVATE = 0x0006, + WM_SETFOCUS = 0x0007, + WM_KILLFOCUS = 0x0008, + WM_ENABLE = 0x000A, + WM_SETREDRAW = 0x000B, + WM_SETTEXT = 0x000C, + WM_GETTEXT = 0x000D, + WM_GETTEXTLENGTH = 0x000E, + WM_PAINT = 0x000F, + WM_CLOSE = 0x0010, + WM_QUERYENDSESSION = 0x0011, + WM_QUIT = 0x0012, + WM_QUERYOPEN = 0x0013, + WM_ERASEBKGND = 0x0014, + WM_SYSCOLORCHANGE = 0x0015, + WM_ENDSESSION = 0x0016, + WM_SHOWWINDOW = 0x0018, + WM_CTLCOLOR = 0x0019, + WM_WININICHANGE = 0x001A, + WM_SETTINGCHANGE = 0x001A, + WM_DEVMODECHANGE = 0x001B, + WM_ACTIVATEAPP = 0x001C, + WM_FONTCHANGE = 0x001D, + WM_TIMECHANGE = 0x001E, + WM_CANCELMODE = 0x001F, + WM_SETCURSOR = 0x0020, + WM_MOUSEACTIVATE = 0x0021, + WM_CHILDACTIVATE = 0x0022, + WM_QUEUESYNC = 0x0023, + WM_GETMINMAXINFO = 0x0024, + WM_PAINTICON = 0x0026, + WM_ICONERASEBKGND = 0x0027, + WM_NEXTDLGCTL = 0x0028, + WM_SPOOLERSTATUS = 0x002A, + WM_DRAWITEM = 0x002B, + WM_MEASUREITEM = 0x002C, + WM_DELETEITEM = 0x002D, + WM_VKEYTOITEM = 0x002E, + WM_CHARTOITEM = 0x002F, + WM_SETFONT = 0x0030, + WM_GETFONT = 0x0031, + WM_SETHOTKEY = 0x0032, + WM_GETHOTKEY = 0x0033, + WM_QUERYDRAGICON = 0x0037, + WM_COMPAREITEM = 0x0039, + WM_GETOBJECT = 0x003D, + WM_COMPACTING = 0x0041, + WM_COMMNOTIFY = 0x0044, + WM_WINDOWPOSCHANGING = 0x0046, + WM_WINDOWPOSCHANGED = 0x0047, + WM_POWER = 0x0048, + WM_COPYDATA = 0x004A, + WM_CANCELJOURNAL = 0x004B, + WM_NOTIFY = 0x004E, + WM_INPUTLANGCHANGEREQUEST = 0x0050, + WM_INPUTLANGCHANGE = 0x0051, + WM_TCARD = 0x0052, + WM_HELP = 0x0053, + WM_USERCHANGED = 0x0054, + WM_NOTIFYFORMAT = 0x0055, + WM_CONTEXTMENU = 0x007B, + WM_STYLECHANGING = 0x007C, + WM_STYLECHANGED = 0x007D, + WM_DISPLAYCHANGE = 0x007E, + WM_GETICON = 0x007F, + WM_SETICON = 0x0080, + WM_NCCREATE = 0x0081, + WM_NCDESTROY = 0x0082, + WM_NCCALCSIZE = 0x0083, + WM_NCHITTEST = 0x0084, + WM_NCPAINT = 0x0085, + WM_NCACTIVATE = 0x0086, + WM_GETDLGCODE = 0x0087, + WM_SYNCPAINT = 0x0088, + WM_NCMOUSEMOVE = 0x00A0, + WM_NCLBUTTONDOWN = 0x00A1, + WM_NCLBUTTONUP = 0x00A2, + WM_NCLBUTTONDBLCLK = 0x00A3, + WM_NCRBUTTONDOWN = 0x00A4, + WM_NCRBUTTONUP = 0x00A5, + WM_NCRBUTTONDBLCLK = 0x00A6, + WM_NCMBUTTONDOWN = 0x00A7, + WM_NCMBUTTONUP = 0x00A8, + WM_NCMBUTTONDBLCLK = 0x00A9, + WM_KEYDOWN = 0x0100, + WM_KEYUP = 0x0101, + WM_CHAR = 0x0102, + WM_DEADCHAR = 0x0103, + WM_SYSKEYDOWN = 0x0104, + WM_SYSKEYUP = 0x0105, + WM_SYSCHAR = 0x0106, + WM_SYSDEADCHAR = 0x0107, + WM_KEYLAST = 0x0108, + WM_IME_STARTCOMPOSITION = 0x010D, + WM_IME_ENDCOMPOSITION = 0x010E, + WM_IME_COMPOSITION = 0x010F, + WM_IME_KEYLAST = 0x010F, + WM_INITDIALOG = 0x0110, + WM_COMMAND = 0x0111, + WM_SYSCOMMAND = 0x0112, + WM_TIMER = 0x0113, + WM_HSCROLL = 0x0114, + WM_VSCROLL = 0x0115, + WM_INITMENU = 0x0116, + WM_INITMENUPOPUP = 0x0117, + WM_MENUSELECT = 0x011F, + WM_MENUCHAR = 0x0120, + WM_ENTERIDLE = 0x0121, + WM_MENURBUTTONUP = 0x0122, + WM_MENUDRAG = 0x0123, + WM_MENUGETOBJECT = 0x0124, + WM_UNINITMENUPOPUP = 0x0125, + WM_MENUCOMMAND = 0x0126, + WM_CTLCOLORMSGBOX = 0x0132, + WM_CTLCOLOREDIT = 0x0133, + WM_CTLCOLORLISTBOX = 0x0134, + WM_CTLCOLORBTN = 0x0135, + WM_CTLCOLORDLG = 0x0136, + WM_CTLCOLORSCROLLBAR = 0x0137, + WM_CTLCOLORSTATIC = 0x0138, + WM_MOUSEMOVE = 0x0200, + WM_LBUTTONDOWN = 0x0201, + WM_LBUTTONUP = 0x0202, + WM_LBUTTONDBLCLK = 0x0203, + WM_RBUTTONDOWN = 0x0204, + WM_RBUTTONUP = 0x0205, + WM_RBUTTONDBLCLK = 0x0206, + WM_MBUTTONDOWN = 0x0207, + WM_MBUTTONUP = 0x0208, + WM_MBUTTONDBLCLK = 0x0209, + WM_MOUSEWHEEL = 0x020A, + WM_XBUTTONDOWN = 0x020B, + WM_XBUTTONUP = 0x020C, + WM_XBUTTONDBLCLK = 0x020D, + WM_PARENTNOTIFY = 0x0210, + WM_ENTERMENULOOP = 0x0211, + WM_EXITMENULOOP = 0x0212, + WM_NEXTMENU = 0x0213, + WM_SIZING = 0x0214, + WM_CAPTURECHANGED = 0x0215, + WM_MOVING = 0x0216, + WM_DEVICECHANGE = 0x0219, + WM_MDICREATE = 0x0220, + WM_MDIDESTROY = 0x0221, + WM_MDIACTIVATE = 0x0222, + WM_MDIRESTORE = 0x0223, + WM_MDINEXT = 0x0224, + WM_MDIMAXIMIZE = 0x0225, + WM_MDITILE = 0x0226, + WM_MDICASCADE = 0x0227, + WM_MDIICONARRANGE = 0x0228, + WM_MDIGETACTIVE = 0x0229, + WM_MDISETMENU = 0x0230, + WM_ENTERSIZEMOVE = 0x0231, + WM_EXITSIZEMOVE = 0x0232, + WM_DROPFILES = 0x0233, + WM_MDIREFRESHMENU = 0x0234, + WM_IME_SETCONTEXT = 0x0281, + WM_IME_NOTIFY = 0x0282, + WM_IME_CONTROL = 0x0283, + WM_IME_COMPOSITIONFULL = 0x0284, + WM_IME_SELECT = 0x0285, + WM_IME_CHAR = 0x0286, + WM_IME_REQUEST = 0x0288, + WM_IME_KEYDOWN = 0x0290, + WM_IME_KEYUP = 0x0291, + WM_MOUSEHOVER = 0x02A1, + WM_MOUSELEAVE = 0x02A3, + WM_CUT = 0x0300, + WM_COPY = 0x0301, + WM_PASTE = 0x0302, + WM_CLEAR = 0x0303, + WM_UNDO = 0x0304, + WM_RENDERFORMAT = 0x0305, + WM_RENDERALLFORMATS = 0x0306, + WM_DESTROYCLIPBOARD = 0x0307, + WM_DRAWCLIPBOARD = 0x0308, + WM_PAINTCLIPBOARD = 0x0309, + WM_VSCROLLCLIPBOARD = 0x030A, + WM_SIZECLIPBOARD = 0x030B, + WM_ASKCBFORMATNAME = 0x030C, + WM_CHANGECBCHAIN = 0x030D, + WM_HSCROLLCLIPBOARD = 0x030E, + WM_QUERYNEWPALETTE = 0x030F, + WM_PALETTEISCHANGING = 0x0310, + WM_PALETTECHANGED = 0x0311, + WM_HOTKEY = 0x0312, + WM_PRINT = 0x0317, + WM_PRINTCLIENT = 0x0318, + WM_HANDHELDFIRST = 0x0358, + WM_HANDHELDLAST = 0x035F, + WM_AFXFIRST = 0x0360, + WM_AFXLAST = 0x037F, + WM_PENWINFIRST = 0x0380, + WM_PENWINLAST = 0x038F, + WM_APP = 0x8000, + WM_USER = 0x0400, + WM_REFLECT = WM_USER + 0x1c00, + WM_CHANGEUISTATE = 0x0127, + WM_UPDATEUISTATE = 0x0128, + WM_QUERYUISTATE = 0x0129 + } + + /// + /// Virtual Keyboard Keys + /// + public enum VK : ushort + { + SHIFT = 0x10, + CONTROL = 0x11, + MENU = 0x12, + ESCAPE = 0x1B, + BACK = 0x08, + TAB = 0x09, + RETURN = 0x0D, + SPACE = 0x20, + PRIOR = 0x21, + NEXT = 0x22, + END = 0x23, + HOME = 0x24, + LEFT = 0x25, + UP = 0x26, + RIGHT = 0x27, + DOWN = 0x28, + SELECT = 0x29, + PRINT = 0x2A, + EXECUTE = 0x2B, + SNAPSHOT = 0x2C, + INSERT = 0x2D, + DELETE = 0x2E, + HELP = 0x2F, + NUMPAD0 = 0x60, + NUMPAD1 = 0x61, + NUMPAD2 = 0x62, + NUMPAD3 = 0x63, + NUMPAD4 = 0x64, + NUMPAD5 = 0x65, + NUMPAD6 = 0x66, + NUMPAD7 = 0x67, + NUMPAD8 = 0x68, + NUMPAD9 = 0x69, + MULTIPLY = 0x6A, + ADD = 0x6B, + SEPARATOR = 0x6C, + SUBTRACT = 0x6D, + DECIMAL = 0x6E, + DIVIDE = 0x6F, + F1 = 0x70, + F2 = 0x71, + F3 = 0x72, + F4 = 0x73, + F5 = 0x74, + F6 = 0x75, + F7 = 0x76, + F8 = 0x77, + F9 = 0x78, + F10 = 0x79, + F11 = 0x7A, + F12 = 0x7B, + OEM_1 = 0xBA, // ',:' for US + OEM_PLUS = 0xBB, // '+' any country + OEM_COMMA = 0xBC, // ',' any country + OEM_MINUS = 0xBD, // '-' any country + OEM_PERIOD = 0xBE, // '.' any country + OEM_2 = 0xBF, // '/?' for US + OEM_3 = 0xC0, // '`~' for US + MEDIA_NEXT_TRACK = 0xB0, + MEDIA_PREV_TRACK = 0xB1, + MEDIA_STOP = 0xB2, + MEDIA_PLAY_PAUSE = 0xB3, + LWIN = 0x5B, + RWIN = 0x5C + } + + /// + /// enum to hold the possible connection states + /// + [Flags] + public enum ConnectionStatusEnum : int + { + INTERNET_CONNECTION_MODEM = 0x1, + INTERNET_CONNECTION_LAN = 0x2, + INTERNET_CONNECTION_PROXY = 0x4, + INTERNET_RAS_INSTALLED = 0x10, + INTERNET_CONNECTION_OFFLINE = 0x20, + INTERNET_CONNECTION_CONFIGURED = 0x40 + } + + #endregion + } +} diff --git a/Win32/Functions.cs b/Win32/Functions.cs new file mode 100644 index 0000000..78112b3 --- /dev/null +++ b/Win32/Functions.cs @@ -0,0 +1,388 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Drawing; +using System.Diagnostics; +using System.Windows.Forms; +using System.Runtime.InteropServices; +using SysIO = System.IO; +using Diag = System.Diagnostics; +using Yaulw.Tools; + +namespace Yaulw.Win32 +{ + /// + /// Common Win32 .Net Wrapper Functions around Win32 for easier consumption by C# + /// + public static class Functions + { + #region Byte / Word + + /// + /// Returns the Low word of the specified int + /// + /// int to retrieve low word from + /// low word of the int + public static int LOWORD(int n) { return (n & 0xffff); } + + /// + /// Returns the Low byte of the specified int + /// + /// int to retrieve low byte from + /// low byte of the int + public static int LOBYTE(int n) { return (n & 0xff); } + + /// + /// Returns the High word of the specified int + /// + /// int to retrieve high word from + /// high word of the int + public static int HIWORD(int n) { return ((n >> 16) & 0xffff); } + + /// + /// Returns the High byte of the specified int + /// + /// int to retrieve high byte from + /// high byte of the int + public static int HIBYTE(int n) { return ((n >> 8) & 0xff); } + + #endregion + + #region RGB + + /// + /// Win32 RGB + /// + /// red value as int + /// green value as int + /// blue value as int + /// returns a Win32 RGB Value as int + public static int RGB(int r, int g, int b) + { + int rs = r & 0xffff; + int gs = (g << 8) & 0xffff; + int bs = (b << 16) & 0xffff; + return (rs | gs | bs); + } + + /// + /// Retrieve the Red Value from Win32 RGB Value + /// + /// + /// returns Red value + public static int GetRValue(int rgbDWORD) + { + return LOBYTE(rgbDWORD); + } + + /// + /// Retrieve the Green Value from Win32 RGB Value + /// + /// + /// returns Green value + public static int GetGValue(int rgbDWORD) + { + return (LOBYTE(rgbDWORD >> 8)); + } + + /// + /// Retrieve the Blue Value from Win32 RGB Value + /// + /// + /// returns Blue value + public static int GetBValue(int rgbDWORD) + { + return (LOBYTE(rgbDWORD >> 16)); + } + + #endregion + + #region Keyboard Functionallity + + /// + /// Use this Function to determine whether the user is holding down a specified key + /// + /// VK such as Ctrl/Alt/Shift + /// true if pressed down, false otherwise + public static bool IsKeyPressed(Definitions.VK vKey) + { + short retVal = User32.GetKeyState(vKey); + return (retVal < 0); + } + + #endregion + + #region ClientRect Functionallity + + /// + /// Get a windows client rectangle + /// + /// A Window Handle + /// A Rectangle + public static Rectangle GetClientRect(IntPtr hWnd) + { + Structures.RECT rect = new Structures.RECT(); + User32.GetClientRect(hWnd, out rect); + return Convert.RECTToRectangle(rect); + } + + /// + /// Get a windows rectangle + /// + /// The Window handle + /// A Rectangle + public static Rectangle GetWindowRect(IntPtr hWnd) + { + Structures.RECT rect = new Structures.RECT(); + User32.GetWindowRect(hWnd, out rect); + return Convert.RECTToRectangle(rect); + } + + /// + /// Returns the Client Size, taking into account the Chrome width + /// + /// A Window Handle + /// A Rectangle + public static Rectangle GetAbsoluteClientRect(IntPtr hWnd) + { + Rectangle windowRect = GetWindowRect(hWnd); + Rectangle clientRect = GetClientRect(hWnd); + int chromeWidth = (int)((windowRect.Width - clientRect.Width) / 2); + return new Rectangle(new Point(windowRect.X + chromeWidth, windowRect.Y + (windowRect.Height - clientRect.Height - chromeWidth)), clientRect.Size); + } + + #endregion + + #region Window Functionallity + + /// + /// Use this to get the name of a Window Class + /// + /// A Window Handle + /// The name of the Window Class + public static string GetWindowClassName(IntPtr hWnd) + { + StringBuilder ClassName = new StringBuilder(100); + int nRet = User32.GetClassName(hWnd, ClassName, ClassName.Capacity); + return ClassName.ToString(); + } + + /// + /// Use this to get the text of a Window + /// + /// A Window Handle + /// The window Text + public static string GetWindowText(IntPtr hWnd) + { + int length = User32.GetWindowTextLength(hWnd); + StringBuilder sb = new StringBuilder(length + 1); + User32.GetWindowText(hWnd, sb, sb.Capacity); + return sb.ToString(); + } + + /// + /// Get the Window Text of a Dialog Item + /// + /// handle to Dialog + /// uint Dialog Item + /// Window Text from the Dialog Item + public static string GetDlgItemText(IntPtr hDlg, int nIDDlgItem) + { + // Get the handle of the dialog item + IntPtr hItem = User32.GetDlgItem(hDlg, nIDDlgItem); + if (hItem == IntPtr.Zero) + return String.Empty; + + return GetWindowText(hItem); + } + + /// + /// Use this to retrieve the Process Object for a specific Window Handle + /// + /// A Window Handle + /// A valid Process object or Null if error occured + public static System.Diagnostics.Process GetProcessFromWindowHandle(IntPtr hWnd) + { + int findPID = 0; + User32.GetWindowThreadProcessId(hWnd, ref findPID); + System.Diagnostics.Process process = null; + try + { + if (findPID > 0) + process = System.Diagnostics.Process.GetProcessById(findPID); + } + catch (ArgumentException) { /* ignore */ } + return process; + } + + /// + /// Find the first top level window for the specified process + /// + /// Process ID + /// the hWnd for the first Window of the specifed or IntPtr.Zero if none found + public static IntPtr GetFirstTopLevelWindowForProcess(int pid) + { + IntPtr Result = IntPtr.Zero; + + // Enumerate the top-level windows + User32.EnumWindowsProc proc = delegate(IntPtr hWnd, IntPtr lParam) + { + // Find the first Window that belongs to the specified Process + if (User32.IsWindowVisible(hWnd) && (pid == GetProcessFromWindowHandle(hWnd).Id)) + { + Result = hWnd; + return false; + } + return true; // Keep Looking + }; + User32.EnumWindows(proc, IntPtr.Zero); + + // yippie + return Result; + } + + /// + /// + /// + /// + /// + public static IntPtr GetTopLevelChildWindowForProcess(int pid) + { + // To Do + return IntPtr.Zero; + } + + #endregion + + #region Desktop Window Functionallity + + /// + /// Get's the Desktop Window as a Win32Window Object + /// + /// a Win32Window Object + public static IWin32Window GetDestopWindow() + { + return Convert.ConverthWndToIWin32Window(User32.GetDesktopWindow()); + } + + /// + /// Refreshes orphaned Icons in the Taskbar + /// + /// + /// + public static void RefreshTaskbarNotificationArea() + { + // Find the Notification Area + IntPtr hNotificationArea = IntPtr.Zero; + hNotificationArea = User32.FindWindowEx + (User32.FW(User32.FW(User32.FW(IntPtr.Zero, "Shell_TrayWnd"), "TrayNotifyWnd"), "SysPager"), + IntPtr.Zero, + "ToolbarWindow32", + "Notification Area"); + + if (hNotificationArea == IntPtr.Zero || !User32.IsWindow(hNotificationArea)) + return; + + // Get the Client Rect of the Notification Area + Structures.RECT r; + User32.GetClientRect(hNotificationArea, out r); + + // Send Mouse Messages to the Notification Area + for (int x = 0; x < r.right; x += 5) + for (int y = 0; y < r.bottom; y += 5) + User32.SendMessage(hNotificationArea, (int) Definitions.WM.WM_MOUSEMOVE, (IntPtr) 0, (IntPtr) ((y << 16) + x)); + } + + #endregion + + #region Process Kernel Functionallity + + // SetProcessDEPPolicy Helpers + private delegate bool SetProcessDEPPolicy_Delegate(int dwFlags); + private static SetProcessDEPPolicy_Delegate SetProcessPolicy = null; + + /// + /// Use SetProcessDEPPolicy to set the DEP Policy for the currently running + /// Process. This function checks to make sure that kernel32 has the function, + /// before calling it. For Windows Systems that don't have DEP. + /// + /// the specified dep to set + /// the return value of the SetProcessDEPPolicy, or false if it doesn't exist + public static bool SetProcessDEPPolicy(Definitions.DEP dep) + { + IntPtr hKernel32 = Kernel32.LoadLibrary("Kernel32.dll"); // Get the DLL + if (hKernel32 != IntPtr.Zero) + { + IntPtr procaddr = IntPtr.Zero; + procaddr = Kernel32.GetProcAddress(hKernel32, "SetProcessDEPPolicy"); // Get the Function + if (procaddr != null) + { + // Cast the Delegate + SetProcessPolicy = (SetProcessDEPPolicy_Delegate)Marshal.GetDelegateForFunctionPointer(procaddr, typeof(SetProcessDEPPolicy_Delegate)); + + // Call it * Disabling DEP * + return SetProcessPolicy((int)dep); + } + } + return false; + } + + #endregion + + #region Short / Long FileName N' Path Conversions + + /// + /// Converts a LongFileNameNPath (Modern Windows Path) into a ShortFileNPath (Old Dos 8.3) + /// ~File Must exist on the system + /// + /// Long (Modern Windows Path) FileNameNPath or Path + /// Old Dos 8.3 Path + public static string GetShortFileNameNPathOrPath(string longFileNameNPathOrPath) + { + if (String.IsNullOrEmpty(longFileNameNPathOrPath)) + return String.Empty; + + // File MUST exist on the system * Otherwise conversions will fail * + if(PathNaming.PathContainsFile(longFileNameNPathOrPath) && !SysIO.File.Exists(longFileNameNPathOrPath)) + return String.Empty; + + // Directory MUST exist on the system * Otherwise conversions will fail * + if(!PathNaming.PathContainsFile(longFileNameNPathOrPath) && !SysIO.Directory.Exists(longFileNameNPathOrPath)) + return String.Empty; + + if (String.IsNullOrEmpty(longFileNameNPathOrPath) || !SysIO.Directory.Exists(longFileNameNPathOrPath)) + return ""; + + StringBuilder sb = new StringBuilder(Definitions.MAX_PATH); + Kernel32.GetShortPathName(longFileNameNPathOrPath, sb, sb.Capacity); + return sb.ToString(); + } + + /// + /// Converts a ShortFileNameNPath (Old Dos 8.3) into a long (Modern Windows Path) + /// ~File Must exist on the system + /// + /// Old Dos 8.3 FileNameNPath or Path + /// new Modern Windows Path + public static string GetLongFileNameNPathOrPath(string shortFileNameNPathOrPath) + { + if (String.IsNullOrEmpty(shortFileNameNPathOrPath)) + return String.Empty; + + // File MUST exist on the system * Otherwise conversions will fail * + if (PathNaming.PathContainsFile(shortFileNameNPathOrPath) && !SysIO.File.Exists(shortFileNameNPathOrPath)) + return String.Empty; + + // Directory MUST exist on the system * Otherwise conversions will fail * + if (!PathNaming.PathContainsFile(shortFileNameNPathOrPath) && !SysIO.Directory.Exists(shortFileNameNPathOrPath)) + return String.Empty; + + StringBuilder sb = new StringBuilder(Definitions.MAX_PATH); + Kernel32.GetLongPathName(shortFileNameNPathOrPath, sb, sb.Capacity); + return sb.ToString(); + } + + #endregion + } +} diff --git a/Win32/Gdi32.cs b/Win32/Gdi32.cs new file mode 100644 index 0000000..f147b6c --- /dev/null +++ b/Win32/Gdi32.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + +namespace Yaulw.Win32 +{ + /// + /// Gdi32.dll Entry Points * http://pinvoke.net/ * + /// + public static class Gdi32 + { + /// + /// The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle of pixels from + /// the specified source device context into a destination device context. + /// + /// A handle to the destination device context + /// The x-coordinate, in logical units, of the upper-left corner of the destination rectangle + /// The y-coordinate, in logical units, of the upper-left corner of the destination rectangle + /// he width, in logical units, of the source and destination rectangles + /// The height, in logical units, of the source and the destination rectangles + /// A handle to the source device context + /// The x-coordinate, in logical units, of the upper-left corner of the source rectangle + /// The y-coordinate, in logical units, of the upper-left corner of the source rectangle + /// A raster-operation code. These codes define how the color data for the source rectangle is to be combined with the color data for the destination rectangle to achieve the final color + /// If the function succeeds, the return value is nonzero + [DllImport("gdi32.dll")] + extern public static bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, int RasterOp); + + /// + /// This function creates a bitmap compatible with the device associated with the specified device context + /// + /// Handle to a device context + /// Specifies the bitmap width, in pixels + /// Specifies the bitmap height, in pixels + /// A handle to the bitmap indicates success. NULL indicates failure. + [DllImport("gdi32.dll")] + extern public static IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight); + + /// + /// This function creates a memory device context (DC) compatible with the specified device + /// + /// Handle to an existing device context + /// The handle to a memory device context indicates success. NULL indicates failure. + [DllImport("gdi32.dll")] + extern public static IntPtr CreateCompatibleDC(IntPtr hdc); + + /// + /// + /// + /// + /// + /// + /// + /// + [DllImport("gdi32.dll")] + extern public static IntPtr CreateDC(string lpszDriver, IntPtr passNULL, IntPtr passNULL2, IntPtr passNULL3); + + /// + /// + /// + /// + /// + [DllImport("gdi32.dll")] + extern public static IntPtr DeleteDC(IntPtr hDc); + + /// + /// + /// + /// + /// + [DllImport("gdi32.dll")] + extern public static IntPtr DeleteObject(IntPtr hDc); + + /// + /// + /// + /// + /// + /// + [DllImport("gdi32.dll")] + extern public static IntPtr SelectObject(IntPtr hdc, IntPtr bmp); + } +} diff --git a/Win32/Kernel32.cs b/Win32/Kernel32.cs new file mode 100644 index 0000000..25daef6 --- /dev/null +++ b/Win32/Kernel32.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + +namespace Yaulw.Win32 +{ + /// + /// Kernel32.dll Entry Points * http://pinvoke.net/ * + /// + public static class Kernel32 + { + [DllImport("kernel32.dll")] + extern public static bool AttachConsole(int dwProcessId); + + [DllImport("kernel32.dll")] + extern public static IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, [In] StringBuilder lpName); + + [DllImport("kernel32.dll")] + extern public static bool CloseHandle(IntPtr hHandle); + + [DllImport("kernel32.dll")] + extern public static bool FreeConsole(); + + [DllImport("kernel32.dll")] + extern public static int GetLastError(); + + [DllImport("kernel32.dll")] + extern public static uint GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, StringBuilder retVal, uint nSize, string lpFileName); + + [DllImport("kernel32.dll")] + extern public static IntPtr GetProcAddress(IntPtr hModule, String procname); + + [DllImport("kernel32.dll")] + extern public static int GetProcessId(IntPtr hProcess); + + [DllImport("kernel32.dll")] + extern public static uint GlobalAddAtom(string lpString); + + [DllImport("kernel32.dll")] + extern public static uint GlobalGetAtomName(uint nAtom, [Out] StringBuilder lpBuffer, int nSize); + + [DllImport("kernel32.dll")] + extern public static uint GlobalDeleteAtom(uint nAtom); + + [DllImport("kernel32.dll")] + extern public static IntPtr LoadLibrary(String dllname); + + [DllImport("kernel32.dll")] + extern public static IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); + + [DllImport("kernel32.dll")] + extern public static bool SetEvent(IntPtr hEvent); + + [DllImport("kernel32.dll")] + extern public static bool SetProcessDEPPolicy(int dwFlags); + + [DllImport("kernel32.dll")] + extern public static int WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); + + [DllImport("kernel32.dll")] + extern public static bool WritePrivateProfileString(string lpAppName, string lpKeyName, string lpString, string lpFileName); + + [DllImport("kernel32.dll", SetLastError = true)] + extern public static int GetShortPathName(string path, [Out] StringBuilder shortPath, int shortPathLength); + + [DllImport("kernel32.dll", SetLastError = true)] + extern public static int GetLongPathName(string path, [Out] StringBuilder longPath, int longPathLength); + } +} diff --git a/Win32/Shell32.cs b/Win32/Shell32.cs new file mode 100644 index 0000000..3bae995 --- /dev/null +++ b/Win32/Shell32.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + +namespace Yaulw.Win32 +{ + /// + /// Shell32.dll Entry Points * http://pinvoke.net/ * + /// + public static class Shell32 + { + [DllImport("Shlwapi.dll")] + extern public static uint AssocQueryString(Definitions.AssocF flags, Definitions.AssocStr str, string pszAssoc, string pszExtra, [Out] StringBuilder pszOut, [In][Out] ref uint pcchOut); + + [DllImport("shell32.dll")] + extern public static IntPtr FindExecutable(string lfFile, string lpDirectory, [Out] StringBuilder lpResult); + + [DllImport("shell32.dll")] + extern public static IntPtr ShellExecute(IntPtr hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd); + + [DllImport("shell32.dll")] + extern public static bool ShellExecuteEx(ref Structures.ShellExecuteInfo lpExecInfo); + + [DllImport("shell32.dll")] + extern public static long SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, int dwFlags, [Out] StringBuilder pszPath); + } +} diff --git a/Win32/Structures.cs b/Win32/Structures.cs new file mode 100644 index 0000000..6c4f9d1 --- /dev/null +++ b/Win32/Structures.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + +namespace Yaulw.Win32 +{ + /// + /// Win32 Structs + /// + public static class Structures + { + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int left; + public int top; + public int right; + public int bottom; + public static RECT FromXYWH(int x, int y, int width, int height) + { + RECT rect = new RECT(); + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; + return rect; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct SIZE + { + public int cx; + public int cy; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ShellExecuteInfo + { + public int cbSize; + public uint fMask; + public IntPtr hwnd; + public string lpVerb; + public string lpFile; + public string lpParameters; + public string lpDirectory; + public uint nShow; + public IntPtr hInstApp; + public IntPtr lpIDList; + public string lpClass; + public IntPtr hkeyClass; + public uint dwHotKey; + public IntPtr hIcon_Monitor; // union DUMMYUNIONNAME + public IntPtr hProcess; + } + + [StructLayout(LayoutKind.Explicit, Size = 28)] + public struct INPUT + { + [FieldOffset(0)] + public uint type; + [FieldOffset(4)] + public KEYBDINPUT ki; + }; + + [StructLayout(LayoutKind.Sequential)] + public struct KEYBDINPUT + { + public ushort wVk; + public ushort wScan; + public uint dwFlags; + public long time; + public uint dwExtraInfo; + }; + + [StructLayout(LayoutKind.Sequential)] + public struct MONITORINFOEX + { + public int cbSize; + public RECT rcMonitor; + public RECT rcWork; + public int dwFlags; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string szDeviceName; + } + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct PAINTSTRUCT + { + public IntPtr hdc; + public int fErase; + public RECT rcPaint; + public int fRestore; + public int fIncUpdate; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] rgbReserved; + } + + [StructLayout(LayoutKind.Sequential)] + public struct NCCALCSIZE_PARAMS + { + public RECT rgrc0, rgrc1, rgrc2; + public IntPtr lppos; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SERVICE_STATUS_PROCESS + { + public int dwServiceType; + public int dwCurrentState; + public int dwControlsAccepted; + public int dwWin32ExitCode; + public int dwServiceSpecificExitCode; + public int dwCheckPoint; + public int dwWaitHint; + public int dwProcessId; + public int dwServiceFlags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct FileDescriptorSet + { + // how many are set? + public int Count; + // an array of Socket handles + [MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxCount)] + public IntPtr[] Array; + + public static readonly int Size = Marshal.SizeOf(typeof(FileDescriptorSet)); + public static readonly FileDescriptorSet Empty = new FileDescriptorSet(0); + public const int MaxCount = 64; + + public FileDescriptorSet(int count) + { + Count = count; + Array = count == 0 ? null : new IntPtr[MaxCount]; + } + } + + /// + /// Structure used in select() call, taken from the BSD file sys/time.h. + /// + [StructLayout(LayoutKind.Sequential)] + public struct TimeValue + { + public int Seconds; // seconds + public int Microseconds; // and microseconds + } + + [StructLayout(LayoutKind.Sequential)] + public struct WSAData + { + public short wVersion; + public short wHighVersion; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)] + public string szDescription; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)] + public string szSystemStatus; + public short iMaxSockets; + public short iMaxUdpDg; + public int lpVendorInfo; + } + } +} diff --git a/Win32/User32.cs b/Win32/User32.cs new file mode 100644 index 0000000..4fb4213 --- /dev/null +++ b/Win32/User32.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + +namespace Yaulw.Win32 +{ + /// + /// User32.dll Entry Points * http://pinvoke.net/ * + /// + public static class User32 + { + // Arg for SetWindowsHookEx() + public delegate int WindowsHookProc(int nCode, IntPtr wParam, IntPtr lParam); + + // Arg for EnumWindows (EnumWindows Callback) + public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); + + [DllImport("user32.dll")] + extern public static IntPtr BeginPaint(IntPtr hWnd, ref Structures.PAINTSTRUCT paintStruct); + + [DllImport("user32.dll")] + extern public static int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll")] + extern public static bool EmptyClipboard(); + + [DllImport("user32.dll")] + extern public static bool EndPaint(IntPtr hWnd, ref Structures.PAINTSTRUCT paintStruct); + + [DllImport("User32.dll")] + extern public static bool EnumChildWindows(IntPtr hParent, Delegate lpEnumFunc, IntPtr lParam); + + [DllImport("User32.dll")] + extern public static bool EnumWindows(Delegate lpEnumFunc, IntPtr lParam); + + [DllImport("User32.dll")] + extern public static IntPtr FindWindow(string lpClassName, string lpWindowName); + + [DllImport("user32.dll")] + extern public static IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); + + /// + /// Helper FindWindowEx Function * Useful * + /// + public static IntPtr FW(IntPtr hwndParent, string lpszClass) + { + return FindWindowEx(hwndParent, IntPtr.Zero, lpszClass, String.Empty); + } + + [DllImport("user32.dll")] + extern public static int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + [DllImport("user32.dll")] + extern public static bool GetClientRect(IntPtr hWnd, out Structures.RECT rect); + + [DllImport("user32.dll")] + extern public static int GetClipboardSequenceNumber(); + + [DllImport("user32.dll")] + extern public static IntPtr GetDC(IntPtr ptr); + + [DllImport("user32.dll")] + extern public static IntPtr GetDCEx(IntPtr hWnd, IntPtr hrgnClip, int flags); + + [DllImport("user32.dll")] + extern public static IntPtr GetDesktopWindow(); + + [DllImport("user32.dll")] + extern public static IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem); + + [DllImport("user32.dll")] + extern public static uint GetDlgItemText(IntPtr hDlg, int nIDDlgItem, [Out] StringBuilder lpString, int nMaxCount); + + [DllImport("user32.dll")] + extern public static IntPtr GetFocus(); + + [DllImport("user32.dll")] + extern public static IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + extern public static short GetKeyState(Definitions.VK vKey); + + [DllImport("user32.dll")] + extern public static bool GetMonitorInfo(IntPtr hMonitor, ref Structures.MONITORINFOEX monitorinfo); + + [DllImport("user32.dll")] + extern public static IntPtr GetParent(IntPtr hWnd); + + [DllImport("user32.dll")] + extern public static int GetSystemMetrics(int index); + + [DllImport("user32.dll")] + extern public static IntPtr GetTopWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + extern public static IntPtr GetWindowDC(IntPtr hWnd); + + [DllImport("user32.dll")] + extern public static long GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + extern public static bool GetWindowRect(IntPtr hWnd, out Structures.RECT rect); + + [DllImport("user32.dll")] + extern public static int GetWindowText(IntPtr hWnd, StringBuilder text, int count); + + [DllImport("user32.dll")] + extern public static int GetWindowTextLength(IntPtr hWnd); + + [DllImport("User32.dll")] + extern public static int GetWindowThreadProcessId(IntPtr hWnd, ref int lpdwProcessId); + + [DllImport("user32.dll")] + extern public static IntPtr MonitorFromRect(ref Structures.RECT rect, int dwFlags); + + [DllImport("user32.dll")] + extern public static void mouse_event(Definitions.MouseEvent dwMouseEventFlags, long dx, long dy, long cButtons, long dwExtraInfo); + + [DllImport("user32.dll")] + extern public static bool IsWindow(IntPtr hwnd); + + [DllImport("user32.dll")] + extern public static bool IsWindowEnabled(IntPtr hwnd); + + [DllImport("User32.dll")] + extern public static bool IsWindowVisible(IntPtr hwnd); + + [DllImport("user32.dll")] + public static extern bool LockWorkStation(); + + [DllImport("User32.dll")] + extern public static bool PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll")] + extern public static uint RegisterWindowMessage(string lpString); + + [DllImport("user32.dll")] + extern public static bool ReleaseCapture(); + + [DllImport("user32.dll")] + extern public static IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc); + + [DllImport("user32.dll")] + extern public static uint SendInput(uint nInputs, ref Structures.INPUT pInputs, int cbSize); + + [DllImport("User32.dll")] + extern public static int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll")] + extern public static IntPtr SetActiveWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + extern public static IntPtr SetCapture(IntPtr hWnd); + + [DllImport("user32.dll")] + extern public static IntPtr SetFocus(IntPtr hwnd); + + [DllImport("user32.dll")] + extern public static int SetForegroundWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + extern public static bool SetLayeredWindowAttributes(IntPtr hwnd, Int32 crKey, byte bAlpha, uint dwFlags); + + [DllImport("user32.dll")] + extern public static IntPtr SetParent(IntPtr child, IntPtr newParent); + + [DllImport("user32.dll")] + extern public static int SetWindowsHookEx(int idHook, WindowsHookProc lpfn, IntPtr hInstance, int threadId); + + [DllImport("user32.dll")] + extern public static long SetWindowLong(IntPtr hWnd, int nIndex, long dwNewLong); + + [DllImport("user32.dll")] + extern public static bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + + [DllImport("user32.dll")] + extern public static int ShowScrollBar(IntPtr hWnd, int wBar, int bShow); + + [DllImport("user32.dll")] + extern public static int ShowWindow(IntPtr hwnd, int nCmdShow); + + [DllImport("user32.dll")] + extern public static bool UnhookWindowsHookEx(int idHook); + } +} diff --git a/Win32/WSock32.cs b/Win32/WSock32.cs new file mode 100644 index 0000000..8e9515e --- /dev/null +++ b/Win32/WSock32.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; +using System.Net.Sockets; + +namespace Yaulw.Win32 +{ + /// + /// WSock32.dll Entry Points * http://pinvoke.net/ * + /// + public static class WSock32 + { + + [DllImport("wsock32.dll", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, SetLastError = true)] + extern public static SocketError WSAStartup([In] short wVersionRequested, [Out] out Structures.WSAData lpWSAData); + + [DllImport("wsock32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + extern public static int WSACleanup(); + + [DllImport("wsock32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + extern public static int select([In] int ignoredParameter, + [In, Out] ref Structures.FileDescriptorSet readfds, + [In, Out] ref Structures.FileDescriptorSet writefds, + [In, Out] ref Structures.FileDescriptorSet exceptfds, + [In] ref Structures.TimeValue timeout); + + [DllImport("wsock32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + extern public static int select([In] int ignoredParameter, + [In, Out] ref Structures.FileDescriptorSet readfds, + [In, Out] ref Structures.FileDescriptorSet writefds, + [In, Out] ref Structures.FileDescriptorSet exceptfds, + [In] IntPtr nullTimeout); + + [DllImport("wsock32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + extern public static int select([In] int ignoredParameter, + [In, Out] ref Structures.FileDescriptorSet readfds, + [In] IntPtr ignoredA, + [In] IntPtr ignoredB, + [In] ref Structures.TimeValue timeout); + + [DllImport("wsock32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + extern public static int select([In] int ignoredParameter, + [In, Out] ref Structures.FileDescriptorSet readfds, + [In] IntPtr ignoredA, + [In] IntPtr ignoredB, + [In] IntPtr nullTimeout); + + [DllImport("wsock32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + extern public static int select([In] int ignoredParameter, + [In] IntPtr ignoredA, + [In, Out] ref Structures.FileDescriptorSet writefds, + [In] IntPtr ignoredB, + [In] ref Structures.TimeValue timeout); + + [DllImport("wsock32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + extern public static int select([In] int ignoredParameter, + [In] IntPtr ignoredA, + [In, Out] ref Structures.FileDescriptorSet writefds, + [In] IntPtr ignoredB, + [In] IntPtr nullTimeout); + + [DllImport("wsock32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + extern public static int select([In] int ignoredParameter, + [In] IntPtr ignoredA, + [In] IntPtr ignoredB, + [In, Out] ref Structures.FileDescriptorSet exceptfds, + [In] ref Structures.TimeValue timeout); + + [DllImport("wsock32.dll", CharSet = CharSet.Ansi, SetLastError = true)] + extern public static int select([In] int ignoredParameter, + [In] IntPtr ignoredA, + [In] IntPtr ignoredB, + [In, Out] ref Structures.FileDescriptorSet exceptfds, + [In] IntPtr nullTimeout); + + } +} diff --git a/Win32/Wininet.cs b/Win32/Wininet.cs new file mode 100644 index 0000000..5e2f79f --- /dev/null +++ b/Win32/Wininet.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + +namespace Yaulw.Win32 +{ + /// + /// User32.dll Entry Points * http://pinvoke.net/ * + /// + public static class Wininet + { + [DllImport("wininet", CharSet = CharSet.Auto)] + extern public static bool InternetGetConnectedState(ref Definitions.ConnectionStatusEnum flags, int dw); + + + } +} diff --git a/Win32/uxDwm.cs b/Win32/uxDwm.cs new file mode 100644 index 0000000..ad29978 --- /dev/null +++ b/Win32/uxDwm.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + +namespace Yaulw.Win32 +{ + /// + /// uxTheme.dll and dwmapi.dll Entry Points * http://pinvoke.net/ * + /// + public static class uxDwm + { + /// + /// Obtains a value that indicates whether Desktop Window Manager (DWM) composition is enabled + /// + /// TRUE if DWM composition is enabled; otherwise, FALSE + /// If this function succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code + [DllImport("dwmapi.dll")] + extern public static long DwmIsCompositionEnabled(ref bool pfEnabled); + + /// + /// Reports whether the current application's user interface displays using visual styles + /// + /// TRUE, if the application has a visual style applied. FALSE otherwise + [DllImport("uxTheme.dll")] + extern public static bool IsAppThemed(); + } +} diff --git a/WinForms/ControlClickHelper.cs b/WinForms/ControlClickHelper.cs new file mode 100644 index 0000000..e20b9b6 --- /dev/null +++ b/WinForms/ControlClickHelper.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using Yaulw.Thread; +using System.Timers; + +namespace Yaulw.WinForms +{ + /// + /// Initiate ControlClickHelper with a Control that you want to implement a Single/Double Mouse Click + /// This Class helps you get accurate Single/Double Mouse Click Events + /// + public class ControlClickHelper + { + #region Public Events + + /// + /// Single Mouse Click Delegate for subscribing to SingleMouseClick Events + /// + /// MouseEventArgs + public delegate void SingleMouseClick(MouseEventArgs e); + + /// + /// Double Mouse Click Delegate for subscribing to DoubleMouseClick Events + /// + /// MouseEventArgs + public delegate void DoubleMouseClick(MouseEventArgs e); + + /// + /// Left Mouse Click Event + /// + public event SingleMouseClick LeftMouseClick; + + /// + /// Right Mouse Click Event + /// + public event SingleMouseClick RightMouseClick; + + /// + /// Left Mouse Double Click Event + /// + public event DoubleMouseClick LeftMouseDoubleClick; + + /// + /// Right Mouse Double Click Event + /// + public event DoubleMouseClick RightMouseDoubleClick; + + #endregion + + #region Private Members + + private Control _Control = null; + private TTimerDisp SingleLeftClickDetectTimer = null; + private TTimerDisp SingleRightClickDetectTimer = null; + private TimeSpan _LastFiredEvent = new TimeSpan(DateTime.Now.Ticks); + private const int _MILISECONDS_FOR_SINGLEMOUSE_CLICKEVENT_TOCOUNT = 350; + private const int _N_SECONDS_TOIGNORE_NEXT_SIGNLEMOUSE_CLICKEVENT = 2; // to avoid tripple clicks, etc... (only sends one double click) + private MouseEventArgs _LeftClickEventArgs; + private MouseEventArgs _RightClickEventArgs; + + /// + /// Returns true if enough time since _LastFiredEvent has passed + /// + private bool EnoughTimeSinceLastEventHasElapsed + { + get + { + return (DateTime.Now.Ticks - _LastFiredEvent.Ticks) >= (TimeSpan.FromSeconds(_N_SECONDS_TOIGNORE_NEXT_SIGNLEMOUSE_CLICKEVENT).Ticks); + } + } + + #endregion + + #region Construction + + /// + /// Initiate ControlClickHelper with a Control that you want to implement a Single/Double Mouse Click + /// This Class helps you get accurate Single/Double Mouse Click Events + /// + /// a control for which you want to implement Single/Double Mouse Click + public ControlClickHelper(Control control) + { + this._Control = control; + this.SingleLeftClickDetectTimer = new TTimerDisp(new ElapsedEventHandler(RealSingleLeftClickDetectTimer_ElapsedEventHandler), _MILISECONDS_FOR_SINGLEMOUSE_CLICKEVENT_TOCOUNT); + this.SingleRightClickDetectTimer = new TTimerDisp(new ElapsedEventHandler(RealSingleRightClickDetectTimer_ElapsedEventHandler), _MILISECONDS_FOR_SINGLEMOUSE_CLICKEVENT_TOCOUNT); + + // Add Event Handlers + control.MouseClick += new MouseEventHandler(control_MouseClick); + control.MouseDoubleClick += new MouseEventHandler(control_MouseDoubleClick); + } + + #endregion + + #region Click Event Handlers + + /// + /// Called by Control DoubleClick Event, We filter for only the left/right mouse double-click, + /// event and fire event when neccessary + /// + /// control + /// MouseEventArgs + private void control_MouseDoubleClick(object sender, MouseEventArgs e) + { + MouseEventArgs args = (MouseEventArgs)e; + if (args.Button == MouseButtons.Left) + { + SingleLeftClickDetectTimer.Stop(); + if (LeftMouseDoubleClick != null && EnoughTimeSinceLastEventHasElapsed) + { + _LastFiredEvent = new TimeSpan(DateTime.Now.Ticks); + LeftMouseDoubleClick(new MouseEventArgs(MouseButtons.Left, 2, args.X, args.Y, args.Delta)); + } + } + else if (args.Button == MouseButtons.Right) + { + SingleRightClickDetectTimer.Stop(); + if (RightMouseDoubleClick != null && EnoughTimeSinceLastEventHasElapsed) + { + _LastFiredEvent = new TimeSpan(DateTime.Now.Ticks); + RightMouseDoubleClick(new MouseEventArgs(MouseButtons.Right, 2, args.X, args.Y, args.Delta)); + } + } + } + + /// + // Called by NotifyIcon Click Event, We filter for only the left mouse click, + /// event and fire event when neccessary + /// + /// control + /// MouseEventArgs + private void control_MouseClick(object sender, MouseEventArgs e) + { + MouseEventArgs args = (MouseEventArgs)e; + if (args.Button == MouseButtons.Left && EnoughTimeSinceLastEventHasElapsed) + { + SingleLeftClickDetectTimer.Start(); // Start Single Click Detect Timer + _LeftClickEventArgs = e; + } + else if (args.Button == MouseButtons.Right && EnoughTimeSinceLastEventHasElapsed) + { + SingleRightClickDetectTimer.Start(); // Start Single Click Detect Timer + _RightClickEventArgs = e; + } + } + + /// + /// Used to detect ONLY Single Left Clicks, since a single-click and then a double-click fires, + /// we want to ignore the first click,and first see if a double-click comes in, if so, ignore + /// the single click, otherwise send it. + /// + /// control + /// MouseEventArgs + private void RealSingleLeftClickDetectTimer_ElapsedEventHandler(object sender, ElapsedEventArgs e) + { + SingleLeftClickDetectTimer.Stop(); + if (LeftMouseClick != null) + { + _LastFiredEvent = new TimeSpan(DateTime.Now.Ticks); + LeftMouseClick(new MouseEventArgs(MouseButtons.Right, 1, _LeftClickEventArgs.X, _LeftClickEventArgs.Y, _LeftClickEventArgs.Delta)); + } + } + + /// + /// Used to detect ONLY Single Right Clicks, since a single-click and then a double-click fires, + /// we want to ignore the first click,and first see if a double-click comes in, if so, ignore + /// the single click, otherwise send it. + /// + /// control + /// MouseEventArgs + private void RealSingleRightClickDetectTimer_ElapsedEventHandler(object sender, ElapsedEventArgs e) + { + SingleRightClickDetectTimer.Stop(); + if (RightMouseClick != null) + { + _LastFiredEvent = new TimeSpan(DateTime.Now.Ticks); + RightMouseClick(new MouseEventArgs(MouseButtons.Right, 1, _RightClickEventArgs.X, _RightClickEventArgs.Y, _RightClickEventArgs.Delta)); + } + } + + #endregion + } +} diff --git a/WinForms/MDIHelper.cs b/WinForms/MDIHelper.cs new file mode 100644 index 0000000..f6c4929 --- /dev/null +++ b/WinForms/MDIHelper.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace Yaulw.WinForms +{ + /// + /// Helper functions for WinForms MDI Layout + /// + public static class MDIHelper + { + /// + /// Use this function to retrive the actual MDIClient Object from + /// an MDIContainer Form + /// + /// Window form that is a valid MDIContainter + /// Valid MdiClient object if found, null otherwise + public static MdiClient GetMDIClientObj(Form mdiFormContainer) + { + if (mdiFormContainer.IsMdiContainer) + { + for (int i = 0; i < mdiFormContainer.Controls.Count; ++i) + { + MdiClient mdiClient = mdiFormContainer.Controls[i] as MdiClient; + if (mdiClient != null) + return mdiClient; + } + } + return null; + } + + /// + /// Closes all MDI Children for the specified MDI Form Container + /// + /// Window form that is a valid MDIContainter + public static void CloseAllChildWindows(Form mdiFormContainer) + { + if (mdiFormContainer.IsMdiContainer) + { + if (mdiFormContainer.MdiChildren.Length > 0) + { + Form[] form = mdiFormContainer.MdiChildren; + foreach (Form f in form) + { + f.Close(); + } + } + } + } + + /// + /// Changes the Child MDI Window Layout for the specified MDI Form Containter + /// + /// Window form that is a valid MDIContainter + /// Specify the MdiLaoyt to use + public static void HandleChildWindowsLayout(Form mdiFormContainer, MdiLayout pLayout) + { + if (!mdiFormContainer.IsMdiContainer) + throw new InvalidOperationException("mdiFormContainter not and MDI Container"); + + // We don't handle Vertical * we are done * + if (pLayout == MdiLayout.TileVertical) + return; + + // Arrange Icon works fine with MDI + if (pLayout == MdiLayout.ArrangeIcons) + { + mdiFormContainer.LayoutMdi(pLayout); + return; + } + + //// + // For Some Reason *YTBD* Cascade and Horizontal MDI Layout stopped working, + // So now we just do the same logic manually and don't use the build in functions + // provided by the MDI + //// + if (pLayout == MdiLayout.Cascade) + { + // Incr. Consts + const int N_INCR_TOP = 30; + const int N_INCR_LEFT = 18; + + Form ActiveMDI = mdiFormContainer.ActiveMdiChild; + if (ActiveMDI != null) + { + int nTop = -1 * N_INCR_TOP; + int nLeft = -1 * N_INCR_LEFT; + + // By Default, Cascade windows increases the size of the Form + // to a percentage of available with on the screen. any screen size > 800 + // width we'll use the calculated width + MdiClient client = GetMDIClientObj(mdiFormContainer); + int nCalculatedWidth = (client.Width / 100) * 80; + int nCalculatedHeight = (client.Height / 100) * 60; + + foreach (Form child in mdiFormContainer.MdiChildren) + { + if (child != ActiveMDI) + { + // Incr. nTop N' nLeft + nTop = nTop + N_INCR_TOP; + nLeft = nLeft + N_INCR_LEFT; + + // Position Childe + child.Top = nTop; + child.Left = nLeft; + child.Height = (nCalculatedHeight > child.MinimumSize.Height) ? nCalculatedHeight : child.MinimumSize.Height; + child.Width = (nCalculatedWidth > child.MinimumSize.Width) ? nCalculatedWidth : child.MinimumSize.Width; + child.Activate(); + } + } + + // Position Active MDI + ActiveMDI.Top = nTop + N_INCR_TOP; + ActiveMDI.Left = nLeft + N_INCR_LEFT; + ActiveMDI.Height = (nCalculatedHeight > ActiveMDI.MinimumSize.Height) ? nCalculatedHeight : ActiveMDI.MinimumSize.Height; + ActiveMDI.Width = (nCalculatedWidth > ActiveMDI.MinimumSize.Width) ? nCalculatedWidth : ActiveMDI.MinimumSize.Width; + ActiveMDI.Activate(); + } + } + else if (pLayout == MdiLayout.TileHorizontal) + { + Form ActiveMDI = mdiFormContainer.ActiveMdiChild; + if (ActiveMDI != null && mdiFormContainer.MdiChildren.Length > 1) + { + MdiClient client = GetMDIClientObj(mdiFormContainer); + int nMaxHeight = client.Height; + int nMaxWidth = client.Width; + + // MDI Window Counts + int nCount = mdiFormContainer.MdiChildren.Length; + int nHalf = (nCount / 2); + + // Position the first half of the Windows + int nTop = 0; + int nCalculatedHeight = nMaxHeight / nHalf; + int nCalculatedWidth = (nMaxWidth / 2); + for (int i = 0; i < nHalf; ++i) + { + Form child = mdiFormContainer.MdiChildren[i]; + + // Title the Window + child.Top = nTop; + child.Left = 0; + + // Always set to Mins + child.Height = (child.MinimumSize.Height < nCalculatedHeight) ? nCalculatedHeight : child.MinimumSize.Height; + child.Width = (child.MinimumSize.Width < nCalculatedWidth) ? nCalculatedWidth : child.MinimumSize.Width; + + // incr. nTop + nTop = nTop + nCalculatedHeight; + } + + // Position the remaining half of the Windows + nTop = 0; + nCalculatedHeight = (nMaxHeight / (nCount - nHalf)); + for (int i = nHalf; i < nCount; ++i) + { + Form child = mdiFormContainer.MdiChildren[i]; + + // Title the Window + child.Top = nTop; + child.Left = nCalculatedWidth; + + // Always set to Mins + child.Height = (child.MinimumSize.Height < nCalculatedHeight) ? nCalculatedHeight : child.MinimumSize.Height; + child.Width = (child.MinimumSize.Width < (nCalculatedWidth - 20)) ? (nCalculatedWidth - 20) : child.MinimumSize.Width; + + // incr. nTop + nTop = nTop + nCalculatedHeight; + } + } + else if (ActiveMDI != null && mdiFormContainer.MdiChildren.Length == 1) + { + ActiveMDI.Top = 0; + ActiveMDI.Left = 0; + ActiveMDI.Height = ActiveMDI.MinimumSize.Height; + ActiveMDI.Width = ActiveMDI.MinimumSize.Width; + } + } + + } + + } +} diff --git a/WinForms/MsgBox.cs b/WinForms/MsgBox.cs new file mode 100644 index 0000000..91f1416 --- /dev/null +++ b/WinForms/MsgBox.cs @@ -0,0 +1,731 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using Yaulw.Win32; +using System.Drawing; +using System.ComponentModel; +using WPFWin = System.Windows; +using WPFWinInterop = System.Windows.Interop; +using Yaulw.Tools; + +namespace Yaulw.WinForms +{ + /// + /// MsgBox is a WindowsForms MessageBox that can be centered to the Parent + /// + public static class MsgBox + { + #region Public Properties + + /// + /// Message Box Icon to use + /// + public static Icon DefaultTitleBarIcon { get; set; } + + /// + /// Set the max number of characters to show on each line + /// + public static uint MaxNumberOfCharactersPerLine { get; set; } + + /// + /// the default max number of characters on each line + /// + public const uint DefaultNumberOfCharactersPerLine = 62; + + #endregion + + #region Public Header Properties + + /// + /// Get / Set to show a header in the Message Box + /// + public static bool ShowHeader { get; set; } + + /// + /// Get / Set the Error Header + /// + public static string MsgBox_ErrorHeader { get; set; } + + /// + /// Get / Set the Fatal Error Header + /// + public static string MsgBox_FatalErrorHeader { get; set; } + + /// + /// Get / Set the Warning Header + /// + public static string MsgBox_WarningHeader { get; set; } + + /// + /// Get / Set the Information Header + /// + public static string MsgBox_InfoHeader { get; set; } + + #endregion + + #region Public Footer Properties + + /// + /// Get / Set to show a footer in the Message Box + /// + public static bool ShowFooter { get; set; } + + /// + /// Get / Set the Error Footer + /// + public static string MsgBox_ErrorFooter { get; set; } + + /// + /// Get / Set the Fatal Error Footer + /// + public static string MsgBox_FatalErrorFooter { get; set; } + + /// + /// Get / Set the Warning Footer + /// + public static string MsgBox_WarningFooter { get; set; } + + /// + /// Get / Set the Information Footer + /// + public static string MsgBox_InfoFooter { get; set; } + + #endregion + + #region Public Title Header Properties + + /// + /// Get / Set to show a header in the Message Box Title + /// + public static bool ShowTitleHeader { get; set; } + + /// + /// Get / Set the Error Title Header + /// + public static string MsgBox_ErrorTitleHeader { get; set; } + + /// + /// Get / Set the Fatal Error Title Header + /// + public static string MsgBox_FatalErrorTitleHeader { get; set; } + + /// + /// Get / Set the Warning Title Header + /// + public static string MsgBox_WarningTitleHeader { get; set; } + + /// + /// Get / Set the Information Title Header + /// + public static string MsgBox_InfoTitleHeader { get; set; } + + #endregion + + #region Construction + + /// + /// MsgBox Construction, ShowHeader, ShowFooter, ShowTitleHeader defaulted to true + /// + static MsgBox() + { + DefaultTitleBarIcon = null; + MaxNumberOfCharactersPerLine = DefaultNumberOfCharactersPerLine; + + // Header Init + ShowHeader = true; + ShowFooter = true; + ShowTitleHeader = true; + } + + #endregion + + #region Public Static Methods + + /// + /// Shows a custom Message Box (centered to parent), with the DefaultTitleBarIcon + /// + /// Window To center Message Box Against + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult Show(WPFWin.Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon) + { + if (parent != null) + { + WPFWinInterop.WindowInteropHelper interop = new WPFWinInterop.WindowInteropHelper(parent); + if(interop.Handle != IntPtr.Zero) + return Show(Yaulw.Win32.Convert.ConverthWndToIWin32Window(interop.Handle), Title, Text, MsgBoxButtons, MsgBoxIcon, DefaultTitleBarIcon); + } + return Show(Functions.GetDestopWindow(), Title, Text, MsgBoxButtons, MsgBoxIcon, DefaultTitleBarIcon); + } + + /// + /// Shows a custom Message Box (centered to parent), with the DefaultTitleBarIcon + /// + /// Window To center Message Box Against + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult Show(IWin32Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon) + { + return Show(parent, Title, Text, MsgBoxButtons, MsgBoxIcon, DefaultTitleBarIcon); + } + + /// + /// Shows a custom Message Box (centered to Desktop), with the DefaultTitleBarIcon + /// + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult Show(String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon) + { + return Show(Functions.GetDestopWindow(), Title, Text, MsgBoxButtons, MsgBoxIcon, DefaultTitleBarIcon); + } + + #endregion + + #region Public Static Methods Extended + + /// + /// Shows Warning MessageBox (WPF) + /// + /// Window To center Message Box Against + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult ShowWarning(WPFWin.Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + { + return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Exclamation); + } + + /// + /// Shows Warning MessageBox (WinForms) + /// + /// Window To center Message Box Against + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult ShowWarning(IWin32Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + { + return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Exclamation); + } + + /// + /// Shows Warning MessageBox (Desktop) + /// + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult ShowWarning(String Title, String Text, MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + { + return Show(Title, Text, MsgBoxButtons, MessageBoxIcon.Exclamation); + } + + /// + /// Shows Fatal Error MessageBox (WPF) + /// + /// Window To center Message Box Against + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult ShowFatalError(WPFWin.Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + { + _ShowFatal = true; + DialogResult dr = Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Error); + _ShowFatal = false; + return dr; + } + + /// + /// Shows Fatal Error MessageBox (WinForms) + /// + /// Window To center Message Box Against + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult ShowFatalError(IWin32Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + { + _ShowFatal = true; + DialogResult dr = Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Error); + _ShowFatal = false; + return dr; + } + + /// + /// Shows Fatal Error MessageBox (Desktop) + /// + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult ShowFatalError(String Title, String Text, MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + { + _ShowFatal = true; + DialogResult dr = Show(Title, Text, MsgBoxButtons, MessageBoxIcon.Error); + _ShowFatal = false; + return dr; + } + + /// + /// Shows Error MessageBox (WPF) + /// + /// Window To center Message Box Against + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult ShowError(WPFWin.Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + { + return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Error); + } + + /// + /// Shows Error MessageBox (WinForms) + /// + /// Window To center Message Box Against + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult ShowError(IWin32Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + { + return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Error); + } + + /// + /// Shows Error MessageBox (Desktop) + /// + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult ShowError(String Title, String Text, MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + { + return Show(Title, Text, MsgBoxButtons, MessageBoxIcon.Error); + } + + /// + /// Shows Information MessageBox (WPF) + /// + /// Window To center Message Box Against + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult ShowInfo(WPFWin.Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + { + return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Information); + } + + /// + /// Shows Information MessageBox (WinForms) + /// + /// Window To center Message Box Against + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult ShowInfo(IWin32Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + { + return Show(parent, Title, Text, MsgBoxButtons, MessageBoxIcon.Information); + } + + /// + /// Shows Information MessageBox (Desktop) + /// + /// Title to show + /// Text to show + /// buttons to show + /// icon to show + /// the result of the Message Box + public static DialogResult ShowInfo(String Title, String Text, MessageBoxButtons MsgBoxButtons = MessageBoxButtons.OK) + { + return Show(Title, Text, MsgBoxButtons, MessageBoxIcon.Information); + } + + #endregion + + #region Private Static Members + + private static User32.WindowsHookProc _hookProcDelegate = null; + private static int _hHook = 0; + private static string _title = null; + private static string _msg = null; + private static IntPtr _hIcon = IntPtr.Zero; + private static bool _IsDesktopOwner = false; + private static bool _ShowFatal = false; + + #endregion + + #region Private Methods + + /// + /// Delegate to make All Message Boxes Thread-Safe + /// + private delegate DialogResult ShowMsgBoxDelegate(IWin32Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon, Icon TitleBarIcon); + + /// + /// *Main MessageBox Show Function* allows you to center the Message Box to the Parent * Dispatcher Safe * + /// + /// Result of Dialog + private static DialogResult Show(IWin32Window parent, String Title, String Text, MessageBoxButtons MsgBoxButtons, MessageBoxIcon MsgBoxIcon, Icon TitleBarIcon) + { + ISynchronizeInvoke InvokeObject = null; + if (parent != null && parent is ISynchronizeInvoke) + InvokeObject = (ISynchronizeInvoke)parent; + + // Invoke if we need to * Make MessageBoxes generally Thread-safe * + if ((InvokeObject != null) && InvokeObject.InvokeRequired) + { + DialogResult result = (DialogResult)InvokeObject.Invoke(new ShowMsgBoxDelegate(MsgBox.Show), new object[] { parent, Title, Text, MsgBoxButtons, MsgBoxIcon, TitleBarIcon }); + return result; + } + else + { + return MsgBox.ShowInternal(parent, Text, Title, MsgBoxButtons, MsgBoxIcon, TitleBarIcon); + } + } + + /// + /// Internal Message Box Showing Function, responsible for showing the message box, setting the hook, etc + /// + /// any IWin32Window + /// message to show + /// title to show + /// buttons to show + /// messageboxicon + /// Title bar Icon + /// + private static DialogResult ShowInternal(IWin32Window owner, string msg, string title, MessageBoxButtons btns, MessageBoxIcon icon, Icon TitleBarIcon) + { + // Create a callback delegate + _hookProcDelegate = new User32.WindowsHookProc(HookCallback); + + // Perform header/footer/title actions + HeaderFooterTitleAction(ref msg, ref title, icon); + + // Properly Format and Pad the Message + MsgTextPaddingAndFormatting(ref msg); + + // Remember the title & message that we'll look for. + // The hook sees *all* windows, so we need to make sure we operate on the right one. + _msg = msg; + _title = title; + + // if Owner is the Desktop Window + _IsDesktopOwner = (owner.Handle == Functions.GetDestopWindow().Handle); + + // Icon could not be present + if (TitleBarIcon != null) + _hIcon = TitleBarIcon.ToBitmap().GetHicon(); + else + _hIcon = IntPtr.Zero; + + // Set the hook. + // Suppress "GetCurrentThreadId() is deprecated" warning. + // It's documented that Thread.ManagedThreadId doesn't work with SetWindowsHookEx() +#pragma warning disable 0618 + _hHook = User32.SetWindowsHookEx(Definitions.WH_CBT, _hookProcDelegate, IntPtr.Zero, AppDomain.GetCurrentThreadId()); +#pragma warning restore 0618 + + // Pop a standard MessageBox. The hook will center it. + DialogResult rslt = DialogResult.None; + if (_IsDesktopOwner) + rslt = MessageBox.Show(_msg, _title, btns, icon); + else + rslt = MessageBox.Show(owner, _msg, _title, btns, icon); + + // Release hook, clean up (may have already occurred) + Unhook(); + + return rslt; + } + + /// + /// Responsible for adding Header / Footer / Title Information + /// + /// text to add header/footer for, if set + /// text to add title header for, if set + /// Message Icon used in MessageBox + private static void HeaderFooterTitleAction(ref string msg, ref string title, MessageBoxIcon icon) + { + #region Header Action + if (ShowHeader) + { + if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_FatalErrorHeader) && _ShowFatal) + msg = MsgBox_FatalErrorHeader + "\n\n" + msg; + else if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_ErrorHeader) && !_ShowFatal) + msg = MsgBox_ErrorHeader + "\n\n" + msg; + else if (icon == MessageBoxIcon.Exclamation && !String.IsNullOrEmpty(MsgBox_WarningHeader)) + msg = MsgBox_WarningHeader + "\n\n" + msg; + else if (icon == MessageBoxIcon.Information && !String.IsNullOrEmpty(MsgBox_InfoHeader)) + msg = MsgBox_InfoHeader + "\n\n" + msg; + } + #endregion + + #region Footer Action + if (ShowFooter) + { + if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_FatalErrorFooter) && _ShowFatal) + msg = msg + "\n\n" + MsgBox_FatalErrorFooter; + else if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_ErrorFooter) && !_ShowFatal) + msg = msg + "\n\n" + MsgBox_ErrorFooter; + else if (icon == MessageBoxIcon.Exclamation && !String.IsNullOrEmpty(MsgBox_WarningFooter)) + msg = msg + "\n\n" + MsgBox_WarningFooter; + else if (icon == MessageBoxIcon.Information && !String.IsNullOrEmpty(MsgBox_InfoFooter)) + msg = msg + "\n\n" + MsgBox_InfoFooter; + } + #endregion + + #region Title Header + if (ShowTitleHeader) + { + if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_FatalErrorTitleHeader) && _ShowFatal) + title = MsgBox_FatalErrorTitleHeader + ((!String.IsNullOrEmpty(title)) ? (" (" + title + ")") : ""); + else if (icon == MessageBoxIcon.Error && !String.IsNullOrEmpty(MsgBox_ErrorTitleHeader) && !_ShowFatal) + title = MsgBox_ErrorTitleHeader + ((!String.IsNullOrEmpty(title)) ? (" (" + title + ")") : ""); + else if (icon == MessageBoxIcon.Exclamation && !String.IsNullOrEmpty(MsgBox_WarningTitleHeader)) + title = MsgBox_WarningTitleHeader + ((!String.IsNullOrEmpty(title)) ? (" (" + title + ")") : ""); + else if (icon == MessageBoxIcon.Information && !String.IsNullOrEmpty(MsgBox_InfoTitleHeader)) + title = MsgBox_InfoTitleHeader + ((!String.IsNullOrEmpty(title)) ? (" (" + title + ")") : ""); + } + #endregion + } + + /// + /// Perform Message Text padding and Text Wrapping + /// + /// the Padded and Text Wrapped Message + private static void MsgTextPaddingAndFormatting(ref string msg) + { + // Stripe last \n, if exists + if (!String.IsNullOrEmpty(msg) && (msg.Length > 0) && (msg[msg.Length - 1] == '\n')) + msg = msg.Remove(msg.Length - 1); + + // Make sure the text looks good, by using padding + if (!String.IsNullOrEmpty(msg) && (msg.Length > 0) && (msg.Length < MaxNumberOfCharactersPerLine)) + { + string[] lines = msg.Split('\n'); + StringBuilder sb = new StringBuilder(); + foreach (string line in lines) + { + sb.Append(line.PadRight((int)MaxNumberOfCharactersPerLine)); + sb.Append("\n"); + } + msg = sb.ToString(); + } + else if (!String.IsNullOrEmpty(msg) && (msg.Length > 0) && (msg.Length > MaxNumberOfCharactersPerLine)) + { + // Incredible and amazing Padding code + string[] lines = msg.Split('\n'); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < lines.Length; ++i) + { + if (lines[i].Length < MaxNumberOfCharactersPerLine) + { + sb.Append(lines[i].PadRight((int)MaxNumberOfCharactersPerLine)); + sb.Append("\n"); + } + else if (lines[i].Length == MaxNumberOfCharactersPerLine) + { + sb.Append(lines[i]); + sb.Append("\n"); + } + else if (lines[i].Length > MaxNumberOfCharactersPerLine) + { + // Split up all the SubLines into a List + List nSubLinesList = new List(); + for (int j = 0; j < lines[i].Length; j = j + (int)MaxNumberOfCharactersPerLine) + { + string line = lines[i].Substring(j, ((lines[i].Length - j) > (int)MaxNumberOfCharactersPerLine) ? (int)MaxNumberOfCharactersPerLine : (lines[i].Length - j)); + nSubLinesList.Add(line); + } + + // Perform proper Text Wrapping on all Sub Lines + for (int j = 0; j < nSubLinesList.Count; ++j) + { + string line1 = nSubLinesList[j]; + string line2 = (j + 1 < nSubLinesList.Count) ? nSubLinesList[j + 1] : String.Empty; + if (!String.IsNullOrEmpty(line2)) + { + if (StringTool.StringEndsWithLetter(line1) && StringTool.StringStartsWithLetter(line2)) + { + string word1 = StringTool.StringFetchLastWord(line1); + string word2 = StringTool.StringFetchFirstWord(line2); + string word = word1 + word2; + + // Use Text Wrapping to make sure word is wrapped correctly + if (word.Length < (int)MaxNumberOfCharactersPerLine) + { + StringTool.StringStripeLastWord(ref line1); + StringTool.StringStripeFirstWord(ref line2); + + // Now perform the proper corrections to the actual list + nSubLinesList[j] = line1.PadRight((int)MaxNumberOfCharactersPerLine); + nSubLinesList[j + 1] = word + line2; + + // Adjust lines + string toMoveDown = ""; + for (int z = j + 1; z < nSubLinesList.Count; ++z) + { + // First Append the Move down + if (!String.IsNullOrEmpty(toMoveDown)) + { + nSubLinesList[z] = toMoveDown + nSubLinesList[z]; + toMoveDown = ""; + } + + // Check Char Limit * make adjustment as needed * + if (nSubLinesList[z].Length > MaxNumberOfCharactersPerLine) + { + toMoveDown = nSubLinesList[z].Substring((int)MaxNumberOfCharactersPerLine); + nSubLinesList[z] = nSubLinesList[z].Substring(0, (int)MaxNumberOfCharactersPerLine); + } + } + + // Add the dangling Move Down as a new Line(s) + if (!String.IsNullOrEmpty(toMoveDown)) + { + for (int z = 0; z < toMoveDown.Length; z = z + (int)MaxNumberOfCharactersPerLine) + { + string line = toMoveDown.Substring(z, ((toMoveDown.Length - z) > (int)MaxNumberOfCharactersPerLine) ? (int)MaxNumberOfCharactersPerLine : (toMoveDown.Length - z)); + nSubLinesList.Add(line); + } + } + } + } + } + } + + // Iterate all subLines and add them to the main list * Padded * + foreach (string line in nSubLinesList) + { + if (line.Length == (int)MaxNumberOfCharactersPerLine) + { + sb.Append(line); + sb.Append("\n"); + } + else + { + sb.Append(line.PadRight((int)MaxNumberOfCharactersPerLine)); + sb.Append("\n"); + } + } + } + } + + // Write nicely formatted Message out + msg = sb.ToString(); + } + else + { + // do nothing, string is miracioulsy exactly correct + } + } + + /// + /// Unhook the User Window Hook + /// + private static void Unhook() + { + User32.UnhookWindowsHookEx(_hHook); + _hHook = 0; + _hookProcDelegate = null; + _msg = null; + _title = null; + } + + /// + /// Callback for the User Window Hook + /// + /// + /// wParam Passed in + /// lParam Passed in + /// the result of the next hook in the chain + private static int HookCallback(int code, IntPtr wParam, IntPtr lParam) + { + int hHook = _hHook; // Local copy for CallNextHookEx() JIC we release _hHook + + // Look for HCBT_ACTIVATE, *not* HCBT_CREATEWND: + // child controls haven't yet been created upon HCBT_CREATEWND. + if (code == Definitions.HCBT_ACTIVATE) + { + string cls = Functions.GetWindowClassName(wParam); + if (cls == "#32770") // MessageBoxes are Dialog boxes + { + string title = Functions.GetWindowText(wParam); + string msg = Functions.GetDlgItemText(wParam, 0xFFFF); // -1 aka IDC_STATIC + if ((title == _title) && (msg == _msg)) + { + // Only Center the Window, if the Owner is NOT the Desktop + if (!_IsDesktopOwner) + CenterWindowOnParent(wParam); + + Unhook(); // Release hook - we've done what we needed + + // Now we also want to set the Icon on the Dialog + if (_hIcon != IntPtr.Zero) + { + User32.SendMessage(wParam, (int)Definitions.WM.WM_SETICON, (IntPtr)1, _hIcon); + User32.SendMessage(wParam, (int)Definitions.WM.WM_SETICON, (IntPtr)0, _hIcon); + } + } + } + } + return User32.CallNextHookEx(hHook, code, wParam, lParam); + } + + /// + /// Boilerplate window-centering code. + /// Split out of HookCallback() for clarity. + /// + /// handle of child window to center + private static void CenterWindowOnParent(IntPtr hChildWnd) + { + // Get child (MessageBox) size + Structures.RECT rcChild = new Structures.RECT(); + User32.GetWindowRect(hChildWnd, out rcChild); + int cxChild = rcChild.right - rcChild.left; + int cyChild = rcChild.bottom - rcChild.top; + + // Get parent (Form) size & location + IntPtr hParent = User32.GetParent(hChildWnd); + Structures.RECT rcParent = new Structures.RECT(); + User32.GetWindowRect(hParent, out rcParent); + int cxParent = rcParent.right - rcParent.left; + int cyParent = rcParent.bottom - rcParent.top; + + // Center the MessageBox on the Form + int x = rcParent.left + (cxParent - cxChild) / 2; + int y = rcParent.top + (cyParent - cyChild) / 2; + uint uFlags = 0x15; // SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE; + User32.SetWindowPos(hChildWnd, IntPtr.Zero, x, y, 0, 0, uFlags); + } + + #endregion + } +} diff --git a/WinForms/SysTray.cs b/WinForms/SysTray.cs new file mode 100644 index 0000000..c543373 --- /dev/null +++ b/WinForms/SysTray.cs @@ -0,0 +1,408 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Drawing; +using System.Windows.Forms; +using Yaulw.Thread; +using System.Timers; + +namespace Yaulw.WinForms +{ + /// + /// Wrapper Class around .Net NotifyIcon, to make it easier to work with an System Tray Icon. + /// Use the InitializeContextMenu Callback to create the ContextMenu. + /// Subscripe to the MouseLeftClick, and MouseLeftDoubleClick event to get accurate events to handle, + /// Call Show()/Hide() to show/hide the System Tray Icon, respectively. + /// + public class SysTray : IDisposable + { + #region Public States + + /// + /// Allow caller to specify multipe states to show for the tray icon + /// + public struct TrayState + { + string nameOfState; + string toolTip; + Icon icon; + } + + // keep track of added States + private List _states = new List(); + + /// + /// Add a possible state to the collection of tray icon states + /// + /// + public void AddState(TrayState state) + { + _states.Add(state); + } + + #endregion + + #region Public Properties + + /// + /// Get/Set the Icon to show on the System Tray Notification + /// + public Icon Icon { get { return trayNotify.Icon; } set { trayNotify.Icon = value; } } + + /// + /// Get/Set ToolTip to show over System Tray Notification + /// + public string toolTip { get { return trayNotify.Text; } set { trayNotify.Text = value; } } + + /// + /// Is System Tray Notification Visible? + /// + public bool IsVisible { get { return trayNotify.Visible; } } + + #endregion + + #region Private Properties + + private NotifyIcon trayNotify { get; set; } + private ContextMenu trayMenu { get { return trayNotify.ContextMenu; } set { trayNotify.ContextMenu = value; } } + private ContextMenuStrip trayMenuStrip { get { return trayNotify.ContextMenuStrip; } set { trayNotify.ContextMenuStrip = value; } } + + #endregion + + #region Public Events + + /// + /// System Tray Left Mouse Click Delegate + /// + /// MouseEventArgs + public delegate void SingleLeftMouseClick(MouseEventArgs e); + + /// + /// Subscribe to get the Left Mouse Single Click Event + /// + public event SingleLeftMouseClick LeftMouseClick; + + /// + /// System Tray Left Mouse Double Click Delegate + /// + /// MouseEventArgs + public delegate void DoubleLeftMouseClick(MouseEventArgs e); + + /// + /// Subscribe to get the Left Mouse Double Click Event + /// + public event DoubleLeftMouseClick LeftMouseDoubleClick; + + /// + /// Initialize Context Menu Dynamically Call-back * i.e. On every Right Click * + /// Allows the Context Menu to change depending on application state + /// + /// Expected a new ContextMenuStrip to Display + /// Event Handle to handle Menu Click Events + public delegate void InitializeContextMenu(out ContextMenuStrip trayMenuStrip, out EventHandler MenuClickEventHandler); + + #endregion + + #region Private Members + + private InitializeContextMenu _ContextMenuInitializer = null; + private TTimerDisp SingleClickDetectTimer = null; + private TimeSpan _LastFiredEvent = new TimeSpan(DateTime.Now.Ticks); + private const int _MILISECONDS_FOR_SINGLEMOUSE_CLICKEVENT_TOCOUNT = 350; + private const int _N_SECONDS_TOIGNORE_NEXT_SIGNLEMOUSE_CLICKEVENT = 2; // to avoid tripple clicks, etc... (only sends one double click) + private bool _disposed = false; + + /// + /// Returns true if enough time since _LastFiredEvent has passed + /// + private bool EnoughTimeSinceLastEventHasElapsed + { + get + { + return (DateTime.Now.Ticks - _LastFiredEvent.Ticks) >= (TimeSpan.FromSeconds(_N_SECONDS_TOIGNORE_NEXT_SIGNLEMOUSE_CLICKEVENT).Ticks); + } + } + + #endregion + + #region Construction + + /// + /// Construct a System Tray Icon (use public properties like ContextMenu,Icon,toolTip to customize further) + /// + /// Callback to initialize the Context Menu (can't be null) + /// pass in an initial ToolTip to display, defaults to "" + /// if null, defaults to systemIcons.Application + public SysTray(InitializeContextMenu ContextMenuInitializer, string toolTip = "", Icon icon = null) + { + if (ContextMenuInitializer == null) + throw new ArgumentNullException("ContextMenuInitializer can not be Null"); + + // Imp, in order to initialize the Context Menu Everytime it is needed + _ContextMenuInitializer = ContextMenuInitializer; + + // Create internal objects + this.trayNotify = new NotifyIcon(); + this.SingleClickDetectTimer = new TTimerDisp(new ElapsedEventHandler(RealSingleClickDetectTimer_ElapsedEventHandler), _MILISECONDS_FOR_SINGLEMOUSE_CLICKEVENT_TOCOUNT); + + // Add Single / Double-Click Event Handlers + trayNotify.Click += new EventHandler(trayNotify_Click); + trayNotify.DoubleClick += new EventHandler(trayNotify_DoubleClick); + trayNotify.MouseDown += new MouseEventHandler(trayNotify_MouseDown); + + // Set ToolTip + if (!String.IsNullOrEmpty(toolTip)) + this.toolTip = toolTip; + + // Set Icon + if (icon == null) + this.Icon = new Icon(SystemIcons.Application, 40, 40); + else + this.Icon = icon; + } + + /// + /// Finalizer + /// + ~SysTray() + { + Dispose(true); + } + + #endregion + + #region Click Event Handlers + + /// + /// Called by NotifyIcon DoubleClick Event, We filter for only the left mouse double-click, + /// event and fire event when neccessary + /// + /// + /// + private void trayNotify_DoubleClick(object sender, EventArgs e) + { + MouseEventArgs args = (MouseEventArgs)e; + if (args.Button == MouseButtons.Left) + { + SingleClickDetectTimer.Stop(); + if (LeftMouseDoubleClick != null && EnoughTimeSinceLastEventHasElapsed) + { + _LastFiredEvent = new TimeSpan(DateTime.Now.Ticks); + LeftMouseDoubleClick(new MouseEventArgs(MouseButtons.Left, 2, 0, 0, 0)); + } + } + } + + /// + // Called by NotifyIcon Click Event, We filter for only the left mouse click, + /// event and fire event when neccessary + /// + /// + /// + private void trayNotify_Click(object sender, EventArgs e) + { + MouseEventArgs args = (MouseEventArgs) e; + if (args.Button == MouseButtons.Left && EnoughTimeSinceLastEventHasElapsed) + SingleClickDetectTimer.Start(); // Start Single Click Detect Timer + } + + /// + /// In order to accurately re-do a context menu, we handle MouseDown for the + /// Right-Mouse click. Mouse Down comes in before the click event, which gives + /// the caller an opportunity to handle/recreate the context menu dynamically, if needed + /// + /// + /// + void trayNotify_MouseDown(object sender, MouseEventArgs e) + { + MouseEventArgs args = (MouseEventArgs)e; + if (args.Button == MouseButtons.Right) + { + // Dynamically re-create Menu on every Right-Click + InitializeContextMenuFromScratchUsingInitializer(); + } + } + + /// + /// Used to detect ONLY Single Clicks, since a single-click and then a double-click fires, + /// we want to ignore the first click,and first see if a double-click comes in, if so, ignore + /// the single click, otherwise send it. (this is done by trayNotify_Click & transNotify_DoubleClick) + /// + /// + /// + private void RealSingleClickDetectTimer_ElapsedEventHandler(object sender, ElapsedEventArgs e) + { + SingleClickDetectTimer.Stop(); + if (LeftMouseClick != null) + { + _LastFiredEvent = new TimeSpan(DateTime.Now.Ticks); + LeftMouseClick(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0)); + } + } + + #endregion + + #region Private Helpers + + /// + /// (Re)Initialize Context Menu from scratch, allows us to build different menu's + /// depending on the application state * External Caller responsible for passing in + /// the context Menu and Click Event Handler * + /// + private void InitializeContextMenuFromScratchUsingInitializer() + { + if (_ContextMenuInitializer != null) + { + ContextMenuStrip menuStrip = null; + EventHandler clickHandler = null; + + // Call Outside Caller's Initialize Context menu Function + _ContextMenuInitializer(out menuStrip, out clickHandler); + + // Set up the Click Event + if (menuStrip != null && clickHandler != null) + AssignClickEventHandlerToAllMenuItems(menuStrip, clickHandler); + + // Assign New Context Menu + trayMenuStrip = menuStrip; + } + } + + /// + /// Calls AssignClickEventHandlerToMenuItem() for each Root Menu Item + /// + /// + /// + private void AssignClickEventHandlerToAllMenuItems(ContextMenuStrip menuStrip, EventHandler clickHandler) + { + if (menuStrip != null) + { + foreach (ToolStripMenuItem item in menuStrip.Items.OfType()) + AssignClickEventHandlerToMenuItem(item, clickHandler); + } + } + + /// + /// Recursive Function, in order to assign all MenuItems as well as + /// all subsequent MenuItems, in the DropDownList, the ToolStripMI_Click + /// event Handler + /// + /// pass in a ToolStripMenuItem to asign the click handler to + /// + private void AssignClickEventHandlerToMenuItem(ToolStripMenuItem item, EventHandler clickHandler) + { + if (item.DropDownItems == null || item.DropDownItems.Count == 0) + { + if(clickHandler != null) + item.Click += clickHandler; + return; + } + else + { + foreach (ToolStripMenuItem _item in item.DropDownItems.OfType()) + AssignClickEventHandlerToMenuItem(_item, clickHandler); + } + } + + #endregion + + #region Show N' Hide + + /// + /// Show the System Tray Icon + /// + public void Show() + { + // Create Context Menu + InitializeContextMenuFromScratchUsingInitializer(); + trayNotify.Visible = true; + } + + /// + /// Hide the System Tray Icon + /// + public void Hide() + { + trayNotify.Visible = false; + } + + #endregion + + #region Public ShowBallon + + /// + /// Type of Icon to show over Ballon + /// + public enum BallonIcon + { + None, + Error, + Warning, + Info + } + + /// + /// Pops up a Ballon over the System Tray Icon + /// + /// Title to show on the Ballon Tip + /// Text to show on the Ballon Tip + /// Icon to show on the Ballon tip + /// Specify the Timeout in Seconds (System mininimum is 10 seconds) + public void ShowBallon(string BallonTipTitle, string BallonTipText, BallonIcon tipIcon = BallonIcon.None, int nTimeoutInSeconds = 10) + { + ToolTipIcon _tipIcon = ToolTipIcon.None; + switch (tipIcon) + { + case BallonIcon.Error: + _tipIcon = ToolTipIcon.Error; + break; + case BallonIcon.Info: + _tipIcon = ToolTipIcon.Info; + break; + case BallonIcon.Warning: + _tipIcon = ToolTipIcon.Warning; + break; + } + trayNotify.ShowBalloonTip((int)TimeSpan.FromSeconds(nTimeoutInSeconds).TotalMilliseconds, BallonTipTitle, BallonTipText, _tipIcon); + } + + #endregion + + #region IDisposable Members + + /// + /// Dispose the Registry Handle + /// + public void Dispose() + { + Dispose(true); + + // Use SupressFinalize in case a subclass + // of this type implements a finalizer + GC.SuppressFinalize(this); + } + + /// + /// Dispose the Registry Handle + /// + /// true, if called from within + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + if (trayNotify != null) + trayNotify.Dispose(); + } + + // Indicate that the instance has been disposed. + trayNotify = null; + _disposed = true; + } + } + + #endregion + } +} diff --git a/Xml/XSerializer.cs b/Xml/XSerializer.cs new file mode 100644 index 0000000..6499063 --- /dev/null +++ b/Xml/XSerializer.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Serialization; +using System.IO; +using System.Xml; + +namespace Yaulw.Xml +{ + /// + /// Wrapper class around XMLReading/Writing (Serialization) + /// + public class XSerializer + { + #region Private Members + + private XmlSerializer _Serializer = null; + private Type _SerializerAssignedToType = null; + + #endregion + + #region * Private Initialization * Called at the begining of every function call + + /// + /// Pass in a valid XML Serializable Object in order to initialize the XML Serializer + /// ~Only newes a NEW Serializer object if the underlying Type for this class changed + /// + /// Thrown if an invalid XmlObject is passed in + /// Thrown if by the end of this function, no Serializer Object Exists + private void InitializeSerializer() + { + // Create the Serializer Object, if Needed + bool bCreateSerializer = false; + if (_Serializer == null) + bCreateSerializer = true; + else if (_SerializerAssignedToType != typeof(T)) + bCreateSerializer = true; + + if (bCreateSerializer) + { + _Serializer = null; + _SerializerAssignedToType = null; + + // Create Serializer Obj + _SerializerAssignedToType = typeof(T); + _Serializer = new XmlSerializer(typeof(T)); + } + + // Something is wrong, if Serializer, is not created at this point + if (_Serializer == null) + throw new OutOfMemoryException(); + } + + #endregion + + #region Construction + + public XSerializer() { } + + #endregion + + #region Public Methods + + /// + /// Reads in an XML Object from a String + /// + /// XMLSerializable Type + /// an xml string to read + /// Deserialized Object + public T ReadFromString(string xml) + { + InitializeSerializer(); + + // Deserialize the Resource and return the Object + using (StringReader reader = new StringReader(xml)) + { + T retVal = (T)_Serializer.Deserialize(reader); + return retVal; + } + } + + /// + /// Reads in an XML Object from a String + /// + /// XMLSerializable Type + /// an xml stream to read + /// Deserialized Object + public T ReadFromStream(Stream xml) + { + InitializeSerializer(); + + // Deserialize the Resource and return the Object + using (StreamReader reader = new StreamReader(xml)) + { + T retVal = (T)_Serializer.Deserialize(reader); + return retVal; + } + } + + /// + /// Reads in an XML Object from a Stream (Resource) + /// + /// XMLSerializable Type + /// A Stream to read from + /// Deserialized Object + public T ReadFromResource(Stream ResourceStream) + { + InitializeSerializer(); + + // Deserialize the Resource and return the Object + using (TextReader reader = new StreamReader(ResourceStream)) + { + T retVal = (T)_Serializer.Deserialize(reader); + return retVal; + } + } + + /// + /// Reads in an XML Object from a File + /// + /// XMLSerializable Type + /// Valid Path to XML File to Deserialize + /// Deserialized Object, or null if file not found + public T ReadFromFile(string PathNFileName) + { + InitializeSerializer(); + + // Check File Existence + if (!System.IO.File.Exists(PathNFileName)) + return default(T); + + // Deserialize the File and return the Object + using (TextReader reader = new StreamReader(PathNFileName)) + { + T retVal = (T)_Serializer.Deserialize(reader); + return retVal; + } + } + + /// + /// Writes an XML Object to a String + /// + /// XMLSerializable Type + /// An XML Serializable Object, can be null to write Blank Object + /// a serialized XML Object as a string + public string WriteToString(T XmlObject) where T : new() + { + InitializeSerializer(); + + // Determine 'Blank' or Running Object + T objectToSerialize = default(T); + if (XmlObject == null) + objectToSerialize = new T(); + else + objectToSerialize = XmlObject; + + using (StringWriter writer = new StringWriter()) + { + _Serializer.Serialize(writer, objectToSerialize); + return writer.ToString(); + } + } + + /// + /// Writes an XML Object to a File + /// + /// XMLSerializable Type + /// An XML Serializable Object, can be null to write Blank Object + /// Valid Path to XML File to Serialize + public void WriteToFile(T XmlObject, string PathNFileName) where T : new() + { + InitializeSerializer(); + + // Create output directory, if not exists + if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(PathNFileName))) + System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(PathNFileName)); + + // Determine 'Blank' or Running Object + T objectToSerialize = default(T); + if (XmlObject == null) + objectToSerialize = new T(); + else + objectToSerialize = XmlObject; + + // Write File; + using (TextWriter writer = new StreamWriter(PathNFileName)) + { + _Serializer.Serialize(writer, objectToSerialize); + } + } + + #endregion + } +} diff --git a/Yaulw.csproj b/Yaulw.csproj new file mode 100644 index 0000000..edcc7f8 --- /dev/null +++ b/Yaulw.csproj @@ -0,0 +1,151 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {1FA1096D-829D-4EBC-8155-E3702B680EEA} + Library + Properties + Yaulw + Yaulw + v3.5 + 512 + + + + + + + + + + + + true + full + false + ..\Target\Debug\ + TRACE;DEBUG;NET35 + prompt + 4 + + + pdbonly + true + ..\Target\Release\ + TRACE;NET35 + prompt + 4 + + + + Components\log4net.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Component + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Yaulw.csproj.vspscc b/Yaulw.csproj.vspscc new file mode 100644 index 0000000..b6d3289 --- /dev/null +++ b/Yaulw.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..4021946 --- /dev/null +++ b/license.txt @@ -0,0 +1,13 @@ +Copyright 2012 Ideas To Actions, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file