diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore index 7f91721..9491a2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1,363 @@ -# ---> Windows -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore -# Dump file -*.stackdump +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates -# Folder config file -[Dd]esktop.ini +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs -# Recycle Bin used on file shares -$RECYCLE.BIN/ +# Mono auto generated files +mono_crash.* -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ -# Windows shortcuts -*.lnk +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/CommonLib/CommonLib.csproj b/CommonLib/CommonLib.csproj new file mode 100644 index 0000000..a549282 --- /dev/null +++ b/CommonLib/CommonLib.csproj @@ -0,0 +1,132 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {C88B26CE-093C-475E-B8FA-E70BA5A0D16E} + Library + Properties + CommonLib + CommonLib + v4.8 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + frmChildForm.cs + + + + + + + Form + + + + + + + + + + + + + + MessageBoxExForm.cs + + + + + + + + \ No newline at end of file diff --git a/CommonLib/CommonLib.sln b/CommonLib/CommonLib.sln new file mode 100644 index 0000000..8d2c7fc --- /dev/null +++ b/CommonLib/CommonLib.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.34601.136 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonLib", "CommonLib.csproj", "{C88B26CE-093C-475E-B8FA-E70BA5A0D16E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C88B26CE-093C-475E-B8FA-E70BA5A0D16E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C88B26CE-093C-475E-B8FA-E70BA5A0D16E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C88B26CE-093C-475E-B8FA-E70BA5A0D16E}.Debug|x64.ActiveCfg = Debug|x64 + {C88B26CE-093C-475E-B8FA-E70BA5A0D16E}.Debug|x64.Build.0 = Debug|x64 + {C88B26CE-093C-475E-B8FA-E70BA5A0D16E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C88B26CE-093C-475E-B8FA-E70BA5A0D16E}.Release|Any CPU.Build.0 = Release|Any CPU + {C88B26CE-093C-475E-B8FA-E70BA5A0D16E}.Release|x64.ActiveCfg = Release|x64 + {C88B26CE-093C-475E-B8FA-E70BA5A0D16E}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BE528BB8-3FB5-4D7C-8534-C024B287B265} + EndGlobalSection +EndGlobal diff --git a/CommonLib/Library/Collections/MultiMap.cs b/CommonLib/Library/Collections/MultiMap.cs new file mode 100644 index 0000000..c7c453a --- /dev/null +++ b/CommonLib/Library/Collections/MultiMap.cs @@ -0,0 +1,143 @@ +///====================================================================================== +/// File: MultiMap.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// Provides MultiMap functionality +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le +///====================================================================================== +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace CommonLib.Collections +{ + ///====================================================================================== + /// MultiMap + ///====================================================================================== + /// + /// This class defines a MultiMap where one can add multiple values to a + /// single key in a Dictionary. This code uses string keys + /// + /// Usage example: + /// bool b1 = true; + /// bool b2 = false; + /// bool b3 = false; + /// + /// MultiMap m1 = new MultiMap(); + /// m1.Add("key1", b1); + /// m1.Add("key1", b2); + /// m1.Add("key2", b3); + /// + /// foreach (string k in m1.Keys) + /// { + /// foreach (bool b in m1[k]) + /// { + /// Console.WriteLine(k + "=" + b); + /// } + /// } + /// + /// + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ----------- ----------- ----- ------------------------------------------ + /// 06/06/12 D.Le + ///====================================================================================== + public class MultiMap + { + // 1 + Dictionary> _dictionary = new Dictionary>(); + + public int keyCount = 0; + + ///========================================================================== + /// MultiMap.Add + ///========================================================================== + /// + /// Adds the specified value under the specified key. If key doesn't exist + /// yet, add the key and increment the key count + /// + /// a string key + /// a string value associated with the key + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public void Add(string key, V value) + { + List list; + if (this._dictionary.TryGetValue(key, out list)) + { + // 2A. + list.Add(value); + } + else + { + // 2B. + list = new List(); + list.Add(value); + this._dictionary[key] = list; + keyCount++; + } + } + + ///========================================================================== + /// MultiMap.GetKeyValueCount + ///========================================================================== + /// + /// For a specified key, return the number of entries associated with it + /// + /// a string key + /// a string value associated with the key + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public int GetKeyValueCount(string key) + { + List list; + if (this._dictionary.TryGetValue(key, out list)) + { + return list.Count; + } + + return 0; + } + + // 3 + public IEnumerable Keys + { + get + { + return this._dictionary.Keys; + } + } + + // 4 + public List this[string key] + { + get + { + List list; + if (this._dictionary.TryGetValue(key, out list)) + { + return list; + } + else + { + return new List(); + } + } + } + } +} \ No newline at end of file diff --git a/CommonLib/Library/Diagnostics/ProcessStarter.cs b/CommonLib/Library/Diagnostics/ProcessStarter.cs new file mode 100644 index 0000000..bbc7ac6 --- /dev/null +++ b/CommonLib/Library/Diagnostics/ProcessStarter.cs @@ -0,0 +1,107 @@ +///====================================================================================== +/// File: clsProcessStarter.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// Provides a class to start a process, get output message if any and exit code +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le +///====================================================================================== +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Diagnostics; + +namespace CommonLib.Diagnostics +{ + ///====================================================================================== + /// clsProcessStarter + ///====================================================================================== + /// + /// Provides a class to start a process, get output message if any and exit code + /// + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ----------- ----------- ----- ------------------------------------------ + /// 06/06/12 D.Le + ///====================================================================================== + public class ProcessStarter + { + private ProcessStartInfo procInfo = null; + + public int procExitCode + { + get; + set; + } + + public string outputMsg + { + get; + set; + } + + public string procArguments + { + get; + set; + } + + public string fileName + { + get; + set; + } + + public ProcessStarter() + { + procExitCode = -1; + outputMsg = ""; + procArguments = ""; + fileName = ""; + } + + ///========================================================================== + /// clsProcessStarter.Run + ///========================================================================== + /// + /// Run another process, save output message if any and exit code + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public void Run() + { + int exitCode = this.procExitCode; + + try + { + procInfo = new ProcessStartInfo(this.fileName); + procInfo.RedirectStandardOutput = true; + procInfo.UseShellExecute = false; + procInfo.CreateNoWindow = true; + procInfo.Arguments = this.procArguments; + Process proc = Process.Start(procInfo); + this.outputMsg = proc.StandardOutput.ReadToEnd(); + proc.WaitForExit(); + exitCode = proc.ExitCode; + proc.Close(); + } + catch + { + + } + + this.procExitCode = exitCode; + } + } +} \ No newline at end of file diff --git a/CommonLib/Library/Diagnostics/StopWatchCustom.cs b/CommonLib/Library/Diagnostics/StopWatchCustom.cs new file mode 100644 index 0000000..07e9b15 --- /dev/null +++ b/CommonLib/Library/Diagnostics/StopWatchCustom.cs @@ -0,0 +1,178 @@ +///====================================================================================== +/// File: StopWatchCustom.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// This is a custom Stopwatch with extra functionality since there's already a StopWatch +/// in Microsoft Libraries, but with limited functionality +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le +///====================================================================================== +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace CommonLib.Diagnostics +{ + ///====================================================================================== + /// StopWatchCustom + ///====================================================================================== + /// + /// This class can return the elapsed time in multiple formats + /// + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ----------- ----------- ----- ------------------------------------------ + /// 06/06/12 D.Le + ///====================================================================================== + public class StopWatchCustom + { + private DateTime startTime; + private DateTime stopTime; + private DateTime currentTime; + private bool running = false; + + ///========================================================================== + /// StopWatchCustom.Start + ///========================================================================== + /// + /// Keeps track of the start time + /// + /// None + /// void + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public void Start() + { + this.startTime = DateTime.Now; + this.running = true; + } + + + ///========================================================================== + /// StopWatchCustom.Stop + ///========================================================================== + /// + /// Keeps track of the stop time + /// + /// None + /// void + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public void Stop() + { + this.stopTime = DateTime.Now; + this.running = false; + } + + + ///========================================================================== + /// StopWatchCustom.GetElapsedTimeString + ///========================================================================== + /// + /// Calculates elapsed time and format the time into days, hours, minutes + /// and seconds + /// + /// None + /// a string indicating days, hours, minutes and seconds + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public string GetElapsedTimeString() + { + TimeSpan interval; + + if (!running) + return ""; //interval = DateTime.Now - startTime; + else + { + this.currentTime = DateTime.Now; + interval = currentTime - startTime; + } + + int days = interval.Days; + double hours = interval.Hours; + double mins = interval.Minutes; + double secs = interval.Seconds; + string x = ""; + if (days != 0) + { + x += days.ToString() + ":"; + } + if (hours != 0) + { + x += hours.ToString("00") + ":"; + } + x += mins.ToString("00") + ":"; + x += secs.ToString("00"); + + return x; + } + + ///========================================================================== + /// StopWatchCustom.GetElapsedMilliseconds + ///========================================================================== + /// + /// Calculates elapsed time and return the time in milliseconds + /// + /// None + /// time in milliseconds + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public double GetElapsedMilliseconds() + { + TimeSpan interval; + + if (running) + interval = DateTime.Now - startTime; + else + interval = stopTime - startTime; + + return interval.TotalMilliseconds; + } + + + ///========================================================================== + /// StopWatchCustom.GetElapsedTimeSecs + ///========================================================================== + /// + /// Calculates elapsed time and return the time in seconds + /// + /// None + /// time in seconds + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public double GetElapsedTimeSecs() + { + TimeSpan interval; + + if (running) + interval = DateTime.Now - startTime; + else + interval = stopTime - startTime; + + return interval.TotalSeconds; + } + } +} \ No newline at end of file diff --git a/CommonLib/Library/DirectoryServices/ActiveDirectory.cs b/CommonLib/Library/DirectoryServices/ActiveDirectory.cs new file mode 100644 index 0000000..a9737fb --- /dev/null +++ b/CommonLib/Library/DirectoryServices/ActiveDirectory.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.DirectoryServices; + +namespace CommonLib.DirectoryServices +{ + public static class ActiveDirectoryQuery + { + public static LDAPSearchResult LDAPanonymousConnect(string LDAPserver, string LDAPpath) + { + LDAPSearchResult LDAPsearchMe = new LDAPSearchResult(); + LDAPsearchMe.LDAPsearch = null; + + // create and return new LDAP connection with desired settings + DirectoryEntry ldapConnection = new DirectoryEntry("LDAP://" + LDAPserver + "/" + LDAPpath); + ldapConnection.AuthenticationType = AuthenticationTypes.None; + + try + { + // create search object which operates on LDAP connection object + // and set search object to only find the user specified + LDAPsearchMe.LDAPsearch = new DirectorySearcher(ldapConnection); + + LDAPsearchMe.ConnectStatus = LDAPQueryResult.LDAPConnectSuccess; + } + + catch (Exception e) + { + LDAPsearchMe.ErrorMsg = e.ToString(); + LDAPsearchMe.ConnectStatus = LDAPQueryResult.LDAPConnectFailed; + } + + return LDAPsearchMe; + } + + public static LDAPSearchResult LDAPsecureConnect(string LDAPserver, string domain, string userName, string LDAPpassword, string LDAPpath) + { + LDAPSearchResult LDAPsearchMe = new LDAPSearchResult(); + LDAPsearchMe.LDAPsearch = null; + + // create and return new LDAP connection with desired settings + DirectoryEntry ldapConnection = new DirectoryEntry(LDAPserver); + ldapConnection.AuthenticationType = AuthenticationTypes.Secure; + ldapConnection.Username = domain + @"\" + userName; + ldapConnection.Password = LDAPpassword; + ldapConnection.Path = LDAPpath; + + try + { + // create search object which operates on LDAP connection object + // and set search object to only find the user specified + LDAPsearchMe.LDAPsearch = new DirectorySearcher(ldapConnection); + + LDAPsearchMe.ConnectStatus = LDAPQueryResult.LDAPConnectSuccess; + } + + catch (Exception e) + { + LDAPsearchMe.ErrorMsg = e.ToString(); + LDAPsearchMe.ConnectStatus = LDAPQueryResult.LDAPConnectFailed; + } + + return LDAPsearchMe; + } + + public struct LDAPSearchResult + { + public string ErrorMsg + { + get; + set; + } + public LDAPQueryResult ConnectStatus + { + get; + set; + } + public DirectorySearcher LDAPsearch + { + get; + set; + } + } + + public enum LDAPQueryResult + { + // Summary: + // If connection to LDAP is successful + LDAPConnectSuccess = 0, + // + // Summary: + // If connection to LDAP fails + LDAPConnectFailed = 1, + } + } +} diff --git a/CommonLib/Library/IO/FileManip.cs b/CommonLib/Library/IO/FileManip.cs new file mode 100644 index 0000000..56add25 --- /dev/null +++ b/CommonLib/Library/IO/FileManip.cs @@ -0,0 +1,1198 @@ +///====================================================================================== +/// File: FileManip.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// Manipulate content of file +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le +///====================================================================================== +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; +using System.Diagnostics; + +namespace CommonLib.IO +{ + ///========================================================================== + /// FileManip + ///========================================================================== + /// + /// Performs all kinds of file manipulations + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public class FileManip + { + public delegate void FileCopyProgressChangeDelegate(UInt64 totalBytesCopied, UInt64 totalBytesFile); + + // provide a way to describe file size in various resolutions + // a resolution of AUTO will only determine the highest order of the byte description + // DO NOT CHANGE THE ORDER OF THIS ENUMERATION. OTHER FUNCTIONS DEPEND ON THE ORDER + // TO WORK CORRECTLY + public enum eFileSizeResolution { AUTO, BYTES, KB, MB, GB, TB }; + + ///========================================================================== + /// FileManip.FileDownloadCompleteStatus + ///========================================================================== + /// + /// This class tracks various information about the file download progress + /// so we can determine when to update the user with information about the + /// download progress, etc. + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public class FileDownloadProgressTracker + { + // only useful if downloading file from web server + public bool downloadCompleted = false; + + public string errorMsg = ""; + public string warningMsg = ""; + + // time in milliseconds to update user with download progress + // such as percentage complete, bytes received + // SHOULD NOT SET THIS MORE THAN 1000 milliseconds. + // File download progress update shouldn't occur more than 1000 milliseconds + public int updateIntervalInMs = 500; + + // keeps track of total bytes received + public UInt64 totalBytesReceived = 0; + + public string strTotalBytesReceived = ""; + + public eFileSizeResolution fileSizeResolution = eFileSizeResolution.AUTO; + + // average bytes being received per update interval defined in variable "updateIntervalInMs" + // this is the minimum number of bytes that has to be received before we update + // download progress to the user + public UInt64 averageBytesReceivedPerUpdateInterval = 0; + + // elapsed time in milliseconds for each update interval + public Stopwatch stopwatchForUpdateInterval = new Stopwatch(); + + // elapsed time in milliseconds for file download + public Stopwatch stopwatchForFileDownload = new Stopwatch(); + + Array values = Enum.GetValues(typeof(FileManip.eFileSizeResolution)); + + // going to keep track of how many times a particular unit B, KB, MB, GB, TB change per second + // base on what's changing the most per second, that's the unit being used to update file transfer + // progress + public Dictionary unitCount = new Dictionary(); + + public int millisecondsCount = 0; + + public FileDownloadProgressTracker() + { + foreach (FileManip.eFileSizeResolution val in values) + { + unitCount[val] = 0; + } + } + + public FileManip.eFileSizeResolution getResolutionWithHighestCount() + { + int maxCount = 0; + FileManip.eFileSizeResolution res = eFileSizeResolution.AUTO; + + foreach (FileManip.eFileSizeResolution val in values) + { + if (maxCount == 0 && unitCount[val] > 0) + { + maxCount = unitCount[val]; + res = val; + } + else if (maxCount < unitCount[val]) + { + maxCount = unitCount[val]; + res = val; + } + } + + return res; + } + + public void resetUnitCount() + { + foreach (FileManip.eFileSizeResolution val in values) + { + unitCount[val] = 0; + } + } + + public void reset() + { + + totalBytesReceived = 0; + + strTotalBytesReceived = ""; + + averageBytesReceivedPerUpdateInterval = 0; + + if (stopwatchForFileDownload.IsRunning) + { + stopwatchForFileDownload.Reset(); + } + + if (stopwatchForUpdateInterval.IsRunning) + { + stopwatchForUpdateInterval.Reset(); + } + + foreach (FileManip.eFileSizeResolution val in values) + { + unitCount[val] = 0; + } + + millisecondsCount = 0; + } + } + + ///========================================================================== + /// FileManip.ShortenFileSizeDescription + ///========================================================================== + /// + /// Given a full file size description, shorten it. + /// Examples + /// 1 TB -> 1 TB + /// 1 TB 1 GB -> 1.1 TB + /// 1 MB 1 KB 1 B -> 1MB 1.1 KB + /// + /// the full file size description + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string ShortenFileSizeDescription(string fileSizeDescription) + { + string describe = ""; + + Match regExMatch = Regex.Match(fileSizeDescription, @"(.+\s)?(\d+)(\s[^\d]+)\s(\d+)\s[^\d]+$", RegexOptions.IgnoreCase); + + if (regExMatch.Success) + { + string match1 = regExMatch.Groups[1].Value; + + // 2nd to the last value + string match2 = regExMatch.Groups[2].Value; + // unit of the 2nd value + string match3 = regExMatch.Groups[3].Value; + // last value + string match4 = regExMatch.Groups[4].Value; + + if (match4.Length >= 3) + { + int divisor = (match4.Length - 2) * 10; + int val = (int)Math.Ceiling((double)Int32.Parse(match4) / (double)divisor); + match4 = val.ToString(); + } + + // shorten the size description + describe = match1 + match2 + "." + match4 + match3; + } + else + describe = fileSizeDescription; + + return describe; + } + + ///========================================================================== + /// FileManip.GetFileResolutionOfTransferRate + ///========================================================================== + /// + /// At a certain interval, download progress is updated. Also at every update + /// interval, there's a certain amount of data that has been transferred. + /// We compare the size of the data transfer during latest interval to the size + /// of the data transfer during last interval. We determine for whatever the + /// interval (1 sec, 1/2 sec, etc), what's the maximum transfer rate. Is it + /// in Bytes, KB or MB. This allows us to only display the maximum transfer + /// rate per interval. + /// + /// the string is in the form "1 TB 1 GB 1 MB 1 KB 1 B" + /// the string is in the form "1 TB 1 GB 1 MB 1 KB 1 B" + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static eFileSizeResolution GetFileResolutionOfTransferRate(string currentDownloadSize, string previousDownloadSize) + { + eFileSizeResolution changingResolution = eFileSizeResolution.AUTO; + eFileSizeResolution lowestResolution = eFileSizeResolution.AUTO; + List resolution1 = new List(); + List resolution2 = new List(); + Regex ItemRegex = new Regex(@"\d+\s[^\d\s]+", RegexOptions.Compiled); + + foreach (Match ItemMatch in ItemRegex.Matches(currentDownloadSize)) + { + resolution1.Add(ItemMatch.ToString()); + } + + foreach (Match ItemMatch in ItemRegex.Matches(previousDownloadSize)) + { + resolution2.Add(ItemMatch.ToString()); + } + + if (resolution1.Count > 0 && resolution2.Count > 0) + { + // going from highest resolution to lowest resolution + // i.e TB -> GB -> MB -> KB -> B + foreach (string res in resolution1) + { + string[] pair = res.Split(' '); + foreach (string res2 in resolution2) + { + string[] pair2 = res2.Split(' '); + + // if units are the same, i.e TB GB MB KB B + if (string.Equals(pair[1], pair2[1], StringComparison.OrdinalIgnoreCase)) + { + // if the values are equal + if (string.Equals(pair[0], pair2[0], StringComparison.OrdinalIgnoreCase)) + { + lowestResolution = (eFileSizeResolution)Enum.Parse(typeof(eFileSizeResolution), pair[1]); + } + else + { + changingResolution = (eFileSizeResolution)Enum.Parse(typeof(eFileSizeResolution), pair[1]); + break; + } + } + } + + if (changingResolution != eFileSizeResolution.AUTO) + break; + } + + } + + if (lowestResolution != eFileSizeResolution.AUTO && lowestResolution > eFileSizeResolution.BYTES && changingResolution == eFileSizeResolution.AUTO) + changingResolution = --lowestResolution; + + return changingResolution; + } + + ///=================================================================================== + /// FileManip.DescribeFileSizeLowestDenominator + ///=================================================================================== + /// + /// Given a total size in bytes of a file, return the file's size lowest denominator + /// using the resolution + /// + /// total size of a file in bytes + /// resolution for the lowest denominator + ///=================================================================================== + /// Date Programmer Proj.ID SAR / STRIP Description + /// --/--/-- ---------- ---------- ------------- ---------------------------------- + /// 05/29/12 D.Le + ///=================================================================================== + public static string DescribeFileSizeLowestDenominator(UInt64 fileSizeInBytes, eFileSizeResolution resolution) + { + UInt64 oneKb = 1024; + UInt64 oneMb = oneKb * oneKb; + UInt64 oneGb = oneMb * oneKb; + UInt64 oneTb = oneGb * oneKb; + + double kilobytes = (double)(fileSizeInBytes % oneMb) / (double)oneKb; + double megabytes = (double)(fileSizeInBytes % oneGb) / (double)oneMb; + double gigabytes = (double)(fileSizeInBytes % oneTb) / (double)oneGb; + double terabytes = (double)fileSizeInBytes / (double)oneTb; + + UInt64 convertedSize = 0; + string describe = ""; + + if (resolution == eFileSizeResolution.BYTES) + { + convertedSize = fileSizeInBytes % oneKb; + if (convertedSize > 0) + describe = convertedSize.ToString() + " B"; + } + else if (resolution == eFileSizeResolution.KB) + { + convertedSize = (UInt64)Math.Truncate(kilobytes); + if (convertedSize > 0) + describe = convertedSize.ToString() + " KB"; + } + else if (resolution == eFileSizeResolution.MB) + { + convertedSize = (UInt64)Math.Truncate(megabytes); + if (convertedSize > 0) + describe = convertedSize.ToString() + " MB"; + } + else if (resolution == eFileSizeResolution.GB) + { + convertedSize = (UInt64)Math.Truncate(gigabytes); + if (convertedSize > 0) + describe = convertedSize.ToString() + " GB"; + } + else if (resolution == eFileSizeResolution.TB) + { + convertedSize = (UInt64)Math.Truncate(terabytes); + if (convertedSize > 0) + describe = convertedSize.ToString() + " TB"; + } + + return describe; + } + + ///=================================================================================== + /// FileManip.DescribeFileSize + ///=================================================================================== + /// + /// Given a total size in bytes of a file, return a string describing the size in various + /// resolutions + /// example: 1 b, 1kb, 1 Mb, 1 Gb, 1 Tb + /// + /// total size of a file in bytes + /// descibe the file size in various resolutions + /// i.e.: 1 Bytes, 1 KB 1 Bytes, 1 MB 1KB, 1 GB 1 MB, etc + /// + ///=================================================================================== + /// Date Programmer Proj.ID SAR / STRIP Description + /// --/--/-- ---------- ---------- ------------- ---------------------------------- + /// 05/29/12 D.Le + ///=================================================================================== + public static string DescribeFileSize(UInt64 fileSizeInBytes, eFileSizeResolution resolution) + { + UInt64 oneKb = 1024; + UInt64 oneMb = oneKb * oneKb; + UInt64 oneGb = oneMb * oneKb; + UInt64 oneTb = oneGb * oneKb; + + string description = ""; + + if (resolution == eFileSizeResolution.AUTO) + { + double multiples = 0.0; + + if (fileSizeInBytes < oneKb) + { + description = fileSizeInBytes.ToString() + " B"; + } + else if (fileSizeInBytes >= oneKb && fileSizeInBytes < oneMb) + { + multiples = (double)fileSizeInBytes / (double)oneKb; + description = string.Format("{0:N2}", multiples) + " KB"; + } + else if (fileSizeInBytes >= oneMb && fileSizeInBytes < oneGb) + { + multiples = (double)fileSizeInBytes / (double)oneMb; + description = string.Format("{0:N2}", multiples) + " MB"; + } + else if (fileSizeInBytes >= oneGb && fileSizeInBytes < oneTb) + { + multiples = (double)fileSizeInBytes / (double)oneGb; + description = string.Format("{0:N2}", multiples) + " GB"; + } + else + { + multiples = (double)fileSizeInBytes / (double)oneTb; + description = string.Format("{0:N2}", multiples) + " TB"; + } + } + else + { + var values = Enum.GetValues(typeof(FileManip.eFileSizeResolution)); + + foreach (FileManip.eFileSizeResolution val in values) + { + if (resolution == val) + { + if (DescribeFileSizeLowestDenominator(fileSizeInBytes, val).Length > 0) + description = DescribeFileSizeLowestDenominator(fileSizeInBytes, val); + } + else + { + if (DescribeFileSizeLowestDenominator(fileSizeInBytes, val).Length > 0) + { + string prevDesc = description; + description = DescribeFileSizeLowestDenominator(fileSizeInBytes, val); + if (prevDesc.Length > 0) + description += " "; + description += prevDesc; + } + } + } + } + + return description; + } + + ///=================================================================================== + /// FileManip.DownloadFileFromWebServerWithProgress + ///=================================================================================== + /// + /// Download file with progress reporting of percentage complete + /// Caller must provide 2 callbacks to handle 2 events + /// One callback handles the percentage complete event + /// It is very important that this callback must implement a mutex to protect the + /// entire execution of the callback. The reason is that if this function CopyFileWithProgress + /// is called in a non-GUI thread, then every time this callback is called to update + /// file progress, it is always in a different thread so the callbacks will be executed + /// concurrently and things might look weird depending on what the user executes in the + /// callback. Implement a mutex to make sure the callback is called synchronously i.e. + /// sequentially + /// One function handles the File completed event ( this function updates a flag in the + /// the object of type FileDownloadCompleteStatus. ) + /// + /// source directory. If source directory is not specified, + /// then a blank file will be created at the destination + /// + /// source file name + /// destination directory + /// destination file name + /// true (overwrite destination) or false (dont overwrite file) + /// set the file attribute at the destination + /// callback that is called when download progress changed event fires + /// callback that is called when download is complete event fires + /// object that stores error/warning message as a result of calling this function. + /// Also it stores a flag to indicate whether file download has compeltely so that this function can return to the caller + /// after file transfer is complete + ///=================================================================================== + /// Date Programmer Proj.ID SAR / STRIP Description + /// --/--/-- ---------- ---------- ------------- ---------------------------------- + /// 05/29/12 D.Le + ///=================================================================================== + public static bool DownloadFileFromWebServerWithProgress(string dirFrom, string fileFrom, string dirTo, string fileTo, bool overrideFlag, FileAttributes fileAttrAtDestination, DownloadProgressChangedEventHandler fileDownloadPercentageDelegate, AsyncCompletedEventHandler fileDownloadCompleteDelegate, FileDownloadProgressTracker downloadProgressTracker) + { + downloadProgressTracker.errorMsg = ""; + downloadProgressTracker.warningMsg = ""; + bool successful = true; + + dirFrom = PathManip.AddTrailingSlashToPath(dirFrom); + dirTo = PathManip.AddTrailingSlashToPath(dirTo); + + string fileNPathTo = Path.Combine(dirTo, fileTo); + + if (!PathManip.PathIsWritable(dirTo)) + { + downloadProgressTracker.errorMsg = String.Format("Unable to write to folder {0}.", dirTo); + successful = false; + } + + if (successful) + { + if (File.Exists(fileNPathTo) && overrideFlag == false) + { + downloadProgressTracker.errorMsg = String.Format("File {0} already exists at the destination.", fileNPathTo); + successful = false; + } + } + + if (successful) + { + // if destination folder doesn't exist, create it + if (!Directory.Exists(dirTo) && PathManip.PathContainsAtLeastOneFolder(dirTo)) + { + try + { + // creates all directories and subdirectories + Directory.CreateDirectory(dirTo); + } + catch (Exception e) + { + downloadProgressTracker.errorMsg = String.Format("Unable to create destination folder {0}. {1}", dirTo, e.Message); + successful = false; + } + } + + if (!File.Exists(fileNPathTo)) + { + try + { + StreamWriter fwr = new StreamWriter(fileNPathTo); + + fwr.Close(); + } + catch (Exception e) + { + downloadProgressTracker.errorMsg = String.Format("Unable to create file {0}. {1}", fileNPathTo, e.Message); + successful = false; + } + } + } + + if (successful) + { + // if the source directory is specified, then file already exists. Perform file copy + if (dirFrom.Length > 0) + { + // create source file path + string fileNPathFrom = Path.Combine(dirFrom, fileFrom); + + // if source file exists + if (File.Exists(fileNPathFrom)) + { + DateTime dt = File.GetLastWriteTime(fileNPathFrom); + + // there is no exception handling for WebClient, so don't try to handle + // exceptions + var webClient = new WebClient(); + webClient.DownloadProgressChanged += fileDownloadPercentageDelegate; + webClient.DownloadFileCompleted += fileDownloadCompleteDelegate; + + // this is an asynchronous call, which means it's non-blocking + webClient.DownloadFileAsync(new Uri(fileNPathFrom), fileNPathTo); + + // we don't know when the file is completely transferred, so we wait here + while (!downloadProgressTracker.downloadCompleted) + { + Thread.Sleep(100); + } + + downloadProgressTracker.downloadCompleted = false; + + try + { + + if (File.Exists(fileNPathTo)) + { + File.SetLastWriteTime(fileNPathTo, dt); + + File.SetAttributes(fileNPathTo, fileAttrAtDestination); + } + } + catch (Exception e) + { + downloadProgressTracker.warningMsg = String.Format("Unable set file attribute for {1}", fileNPathTo, e.Message); + } + } + else + { + if (fileNPathFrom.Length > 0) + downloadProgressTracker.errorMsg = String.Format("Source file {0} does not exist", fileNPathFrom); + else + downloadProgressTracker.errorMsg = String.Format("Source file is not provided", fileNPathFrom); + successful = false; + } + } + } + + return successful; + } + + ///=================================================================================== + /// FileManip.CopyFileWithProgress + ///=================================================================================== + /// + /// Copy file with progress reporting of percentage complete + /// + /// source directory. If source directory is not specified, + /// then a blank file will be created at the destination + /// + /// source file name + /// destination directory + /// destination file name + /// true (overwrite destination) or false (dont overwrite file) + /// set the file attribute at the destination + /// callback that is called to notify of change in size being copied + /// object that stores error/warning message as a result of calling this function. + /// Also it stores a flag to indicate whether file download has compeltely so that this function can return to the caller + /// after file transfer is complete + ///=================================================================================== + /// Date Programmer Proj.ID SAR / STRIP Description + /// --/--/-- ---------- ---------- ------------- ---------------------------------- + /// 05/29/12 D.Le + ///=================================================================================== + public static bool CopyFileWithProgress(string dirFrom, string fileFrom, string dirTo, string fileTo, bool overrideFlag, FileAttributes fileAttrAtDestination, FileCopyProgressChangeDelegate copyProgressChangeDelegate, FileDownloadProgressTracker downloadProgressTracker) + { + downloadProgressTracker.errorMsg = ""; + downloadProgressTracker.warningMsg = ""; + bool successful = true; + + dirFrom = PathManip.AddTrailingSlashToPath(dirFrom); + dirTo = PathManip.AddTrailingSlashToPath(dirTo); + + string fileNPathTo = Path.Combine(dirTo, fileTo); + + if (!PathManip.PathIsWritable(dirTo)) + { + downloadProgressTracker.errorMsg = String.Format("Unable to write to folder {0}.", dirTo); + successful = false; + } + + if (successful) + { + if (File.Exists(fileNPathTo) && overrideFlag == false) + { + downloadProgressTracker.errorMsg = String.Format("File {0} already exists at the destination.", fileNPathTo); + successful = false; + } + } + + if (successful) + { + // if destination folder doesn't exist, create it + if (!Directory.Exists(dirTo) && PathManip.PathContainsAtLeastOneFolder(dirTo)) + { + try + { + // creates all directories and subdirectories + Directory.CreateDirectory(dirTo); + } + catch (Exception e) + { + downloadProgressTracker.errorMsg = String.Format("Unable to create destination folder {0}. {1}", dirTo, e.Message); + successful = false; + } + } + + // create a new file at the destination, since source file is not specified + if (!File.Exists(fileNPathTo) && dirFrom.Length == 0) + { + try + { + StreamWriter fwr = new StreamWriter(fileNPathTo); + + fwr.Close(); + } + catch (Exception e) + { + downloadProgressTracker.errorMsg = String.Format("Unable to create file {0}. {1}", fileNPathTo, e.Message); + successful = false; + } + } + } + + if (successful) + { + // if the source directory is specified, then file already exists. Perform file copy + if (dirFrom.Length > 0) + { + // create source file path + string fileNPathFrom = Path.Combine(dirFrom, fileFrom); + + // if source file exists + if (File.Exists(fileNPathFrom)) + { + DateTime dt = File.GetLastWriteTime(fileNPathFrom); + + FileInfo file = new FileInfo(fileNPathFrom); + FileInfo destination = new FileInfo(fileNPathTo); + const int bufferSize = 1024 * 1024 * 4; //4MB + byte[] buffer = new byte[bufferSize], buffer2 = new byte[bufferSize]; + bool swap = false; + int read = 0; + UInt64 totalFileSize = (UInt64)file.Length; + Task writer = null; + + using (var source = file.OpenRead()) + using (var dest = destination.OpenWrite()) + { + dest.SetLength(source.Length); + for (UInt64 size = 0; size <= totalFileSize; size += (UInt64)read) + { + if (size != totalFileSize) + { + read = source.Read(swap ? buffer : buffer2, 0, bufferSize); + if (writer != null) writer.Wait(); + writer = dest.WriteAsync(swap ? buffer : buffer2, 0, read); + swap = !swap; + } + + copyProgressChangeDelegate(size, totalFileSize); ; + + if (size == totalFileSize) + break; + } + if (writer != null) writer.Wait(); + } + + // reset all progress information when file copy is done + downloadProgressTracker.reset(); + + try + { + + if (File.Exists(fileNPathTo)) + { + File.SetLastWriteTime(fileNPathTo, dt); + + File.SetAttributes(fileNPathTo, fileAttrAtDestination); + } + } + catch (Exception e) + { + downloadProgressTracker.warningMsg = String.Format("Unable set file attribute for {1}", fileNPathTo, e.Message); + } + } + else + { + if (fileNPathFrom.Length > 0) + downloadProgressTracker.errorMsg = String.Format("Source file {0} does not exist", fileNPathFrom); + else + downloadProgressTracker.errorMsg = String.Format("Source file is not provided", fileNPathFrom); + successful = false; + } + } + } + + return successful; + } + + ///=================================================================================== + /// FileManip.CopyFile + ///=================================================================================== + /// + /// Copy file from source folder to destination's folder + /// + /// source directory. If source directory is not specified, + /// then a blank file will be created at the destination + /// + /// source file name + /// destination directory + /// destination file name + /// true (overwrite destination) or false (dont overwrite file) + /// set the file attribute at the destination + ///=================================================================================== + /// Date Programmer Proj.ID SAR / STRIP Description + /// --/--/-- ---------- ---------- ------------- ---------------------------------- + /// 05/29/12 D.Le + ///=================================================================================== + public static bool CopyFile(string dirFrom, string fileFrom, string dirTo, string fileTo, bool overrideFlag, FileAttributes fileAttrAtDestination, ref string errMsg) + { + errMsg = ""; + bool successful = true; + + dirFrom = PathManip.AddTrailingSlashToPath(dirFrom); + dirTo = PathManip.AddTrailingSlashToPath(dirTo); + + string fileNPathTo = Path.Combine(dirTo, fileTo); + + // if destination folder doesn't exist, create it + if (!Directory.Exists(dirTo) && PathManip.PathContainsAtLeastOneFolder(dirTo)) + { + try + { + // creates all directories and subdirectories + Directory.CreateDirectory(dirTo); + } + catch (Exception e) + { + errMsg = String.Format("Unable to create destination folder {0}. {1}", dirTo, e.Message); + successful = false; + } + } + + if (successful) + { + // if the source directory is specified, then file already exists. Perform file copy + if (dirFrom.Length > 0) + { + // create source file path + string fileNPathFrom = Path.Combine(dirFrom, fileFrom); + + // if source file exists + if (File.Exists(fileNPathFrom)) + { + try + { + File.Copy(fileNPathFrom, fileNPathTo, overrideFlag); + } + catch (Exception e) + { + errMsg = String.Format("Unable to copy file {0} from {1} to {2}. {3}", fileFrom, dirFrom, dirTo, e.Message); + successful = false; + } + + try + { + if (File.Exists(fileNPathTo)) + { + File.SetAttributes(fileNPathTo, fileAttrAtDestination); + } + } + catch { } + } + else + { + errMsg = String.Format("Source file {0} does not exist", fileNPathFrom); + successful = false; + } + } + else // create a new file if source directory is not specified + { + if (!File.Exists(fileNPathTo)) + { + try + { + StreamWriter fwr = new StreamWriter(fileNPathTo); + + fwr.Close(); + } + catch (Exception e) + { + errMsg = String.Format("Unable to create file {0}. {1}", fileNPathTo, e.Message); + successful = false; + } + } + } + } + + return successful; + } + + public static bool SetFileAttributeToReadOnly(string pathAndFilename, ref string errMsg) + { + try + { + File.SetAttributes(pathAndFilename, File.GetAttributes(pathAndFilename) | FileAttributes.ReadOnly); + } + catch (Exception e) + { + errMsg = e.Message; + return false; + } + + return true; + } + + public static bool SetFileAttributeToReadAndWrite(string pathAndFilename, ref string errMsg) + { + try + { + FileAttributes attributes = File.GetAttributes(pathAndFilename); + File.SetAttributes(pathAndFilename, attributes & ~FileAttributes.ReadOnly); + } + catch (Exception e) + { + errMsg = e.Message; + return false; + } + + return true; + } + + ///========================================================================== + /// FileManip.GenerateUniqueFileName + ///========================================================================== + /// + /// Generate a unique file name + /// Example: file.txt -> file_121116_R01 + /// + /// Path to a directory + /// file name without extension + /// file extension + /// Unique file name + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string GenerateUniqueFileName(string path, string filePrefix, string fileExtension) + { + int logCount = 0; + string logDir; + + string sLogCount = "00"; + string logPathAndFileName = ""; + + DateTime dt = DateTime.Now; + string strYear = dt.ToString("yy"); + string strMonth = dt.ToString("MM"); + string strDay = dt.ToString("dd"); + + if (Directory.Exists(path)) + logDir = PathManip.AddTrailingSlashToPath(path); + else + logDir = PathManip.AddTrailingSlashToPath(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location)); + + fileExtension = Regex.Replace(fileExtension, @"^\.+([^\.]+)", "$1", RegexOptions.IgnoreCase); + + while (true) + { + logPathAndFileName = filePrefix; + logPathAndFileName += "_" + strMonth + strDay + strYear + "_R" + sLogCount + "." + fileExtension; + + logPathAndFileName = logDir + logPathAndFileName; + + logCount++; + if (File.Exists(logPathAndFileName)) + { + if (logCount < 10) + { + sLogCount = "0" + logCount.ToString(); + } + else + sLogCount = logCount.ToString(); + } + else + break; + } + + return logPathAndFileName; + } + + ///========================================================================== + /// FileManip.FilesContentsAreEqual + ///========================================================================== + /// + /// Wrapper to compare 2 files in binary + /// + /// FileInfo object 1 + /// FileInfo object 2 + /// true if 2 files are equal, otherwise false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool FilesContentsAreEqual(FileInfo fileInfo1, FileInfo fileInfo2) + { + bool result; + if (fileInfo1.Length != fileInfo2.Length) + { + result = false; + } + else + { + using (var file1 = fileInfo1.OpenRead()) + { + using (var file2 = fileInfo2.OpenRead()) + { + result = StreamsContentsAreEqual(file1, file2); + } + } + } + return result; + } + + ///========================================================================== + /// FileManip.FilesContentsAreEqual + ///========================================================================== + /// + /// Binary compare of 2 files to see if they are equal + /// + /// Stream object 1 + /// Stream object 2 + /// true if 2 files are equal, otherwise false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + private static bool StreamsContentsAreEqual(Stream stream1, Stream stream2) + { + const int bufferSize = 2048 * 2; + var buffer1 = new byte[bufferSize]; + var buffer2 = new byte[bufferSize]; + while (true) + { + int count1 = stream1.Read(buffer1, 0, bufferSize); + int count2 = stream2.Read(buffer2, 0, bufferSize); + if (count1 != count2) + { + return false; + } + if (count1 == 0) + { + return true; + } + int iterations = (int)Math.Ceiling((double)count1 / sizeof(Int64)); + for (int i = 0; i < iterations; i++) + { + if (BitConverter.ToInt64(buffer1, i * sizeof(Int64)) != BitConverter.ToInt64(buffer2, i * sizeof(Int64))) + { + return false; + } + } + } + } + + ///========================================================================== + /// FileManip.GetLogText + ///========================================================================== + /// + /// Reads an ASCII file usually a log file and return the amount of text + /// that is less than or equal to the size defined by maxLogSizeKB. + /// Any text exceeding the size defined by maxLogSizeKB is thrown out + /// + /// path to the ASCII file + /// Maximum amount of data in KB to be read + /// text strings + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string GetLogText(string logFilePath, int maxLogSizeKB) + { + string logText = string.Empty; + + if (File.Exists(logFilePath)) + { + // read the log file + FileStream fs = File.OpenRead(logFilePath); + + int byteCount = maxLogSizeKB * 1024; // convert KB to Bytes + + // convert string to byte array + byte[] logFileBytes = new byte[fs.Length]; + + fs.Read(logFileBytes, 0, Convert.ToInt32(fs.Length)); + fs.Close(); + + // if the file size exceeds the maximum allowable file size + // we want to throw out the extra data and only keep relevant data + if (logFileBytes.Length > byteCount && byteCount > 0) + { + int cutOffIndex = byteCount - 1; // get the index of the byte where the cut-off occurs + + // if the value at cut-off index is either carriage return or newline character + // traverse back until we find the first character that is neither carriage return nor newline + if (logFileBytes[cutOffIndex] == 13 || logFileBytes[cutOffIndex] == 10) + { + while (logFileBytes[cutOffIndex] == 13 || logFileBytes[cutOffIndex] == 10) + { + cutOffIndex--; + if (cutOffIndex < 0) + break; + } + } + else + { + while (cutOffIndex < logFileBytes.Length) + { + // 13 is the decimal value for the carriage return character + // 10 is the decimal value for the newline character + // we have located the cut-off location + if (logFileBytes[cutOffIndex] == 13 || logFileBytes[cutOffIndex] == 10) + { + cutOffIndex--; // we dont want to include carriage return or newline character + break; + } + else if (cutOffIndex - byteCount > 100) // allow maximum 100 bytes after the cut-off should occur + break; + else if (cutOffIndex == logFileBytes.Length - 1) + break; + + cutOffIndex++; + } + } + // if the cut-off index is less than the max index of logFileBytes + if (cutOffIndex >= 0 && cutOffIndex < logFileBytes.Length - 1) + { + // allocate memory for the data we want to keep + byte[] newLogFileBytes = new byte[cutOffIndex + 1]; + + // copy the relevant bytes in logFileBytes to newLogFileBytes + Buffer.BlockCopy(logFileBytes, 0, newLogFileBytes, 0, cutOffIndex + 1); + + logFileBytes = newLogFileBytes; + } + } + + if (logFileBytes.Length > 0) + { + // convert the byte array back to string + logText = Encoding.ASCII.GetString(logFileBytes); + } + + fs.Close(); + } + + return logText; + } + + //////========================================================================== + /// FileManip.ReplaceTextInFile + ///========================================================================== + /// + /// Replace a string in a text file with another string + /// Replacement string doesn't have to be a string literal + /// It can contain $1, $2, etc to keep that matched strings during the replacement + /// In the pattern, use the () the open and close parenthesis will be associate with + /// $1, $2, and so on + /// + /// file path + /// the pattern to search in the text file + /// text to replace if the pattern is found + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool ReplaceTextInFile(string filePath, string pattern, string replaceText, ref string errMsg) + { + StreamReader reader = null; + StreamWriter writer = null; + FileStream fs = null; + string content = string.Empty; + Match regExMatch; + bool patternFound = false; + + var re = new Regex(pattern, RegexOptions.Singleline | RegexOptions.IgnoreCase); + + try + { + fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); + reader = new StreamReader(fs, Encoding.Default); + content = reader.ReadToEnd(); + + regExMatch = Regex.Match(content, pattern, RegexOptions.Singleline | RegexOptions.IgnoreCase); + + if (regExMatch.Success) + patternFound = true; + + content = re.Replace(content, replaceText); + } + catch (System.Exception ex) + { + errMsg = ex.Message; + return false; + } + finally + { + if (reader != null) + { + reader.Close(); + fs.Close(); + } + } + + try + { + fs = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite); + writer = new StreamWriter(fs, Encoding.Default); + writer.Write(content); + } + catch (System.Exception ex) + { + errMsg = ex.Message; + return false; + } + finally + { + if (writer != null) + { + writer.Close(); + fs.Close(); + } + } + + if (patternFound) + return true; + else + return false; + } + } +} diff --git a/CommonLib/Library/IO/Hardware.cs b/CommonLib/Library/IO/Hardware.cs new file mode 100644 index 0000000..f6b3e53 --- /dev/null +++ b/CommonLib/Library/IO/Hardware.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.IO; + +using CommonLib.Runtime.InteropServices; + +namespace CommonLib.IO +{ + public class Hardware + { + ///========================================================================== + /// Hardware.GetAllCdDrives + ///========================================================================== + /// + /// Get all the drive letters of all the cd drives in the computer + /// + /// list of cd drive letters + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static List GetAllCdDrives() + { + List cdDrives = new List(); + + foreach (var drive in DriveInfo.GetDrives().Where(d => d.DriveType == DriveType.CDRom)) + { + cdDrives.Add(drive.Name); + } + + return cdDrives; + } + + ///========================================================================== + /// Hardware.IsCdDrive + ///========================================================================== + /// + /// Determine if drive letter is CD Drive + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool IsCdDrive(string driveLetter) + { + bool isCdRom = false; + string driveLetterFormatted = ""; + Match regExMatch; + + regExMatch = Regex.Match(driveLetter, @"([a-zA-Z]).*"); + + if (regExMatch.Success) + { + driveLetterFormatted = regExMatch.Groups[1].Value; + + foreach (var drive in DriveInfo.GetDrives().Where(d => d.DriveType == DriveType.CDRom)) + { + if (Regex.IsMatch(driveLetterFormatted + @":\", @"^" + Regex.Escape(drive.Name), RegexOptions.IgnoreCase)) + { + isCdRom = true; + break; + } + } + } + + return isCdRom; + } + + ///========================================================================== + /// Hardware.OpenCdDrive + ///========================================================================== + /// + /// Open CD Drive + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool OpenCdDrive(string driveLetter) + { + const int OPEN_EXISTING = 3; + const uint GENERIC_READ = 0x80000000; + const uint GENERIC_WRITE = 0x40000000; + const uint IOCTL_STORAGE_EJECT_MEDIA = 2967560; + const uint FILE_SHARE_READ = 0x00000001; + const uint FILE_SHARE_WRITE = 0x00000002; + + bool successful = true; + + Match regExMatch; + string driveLetterFormatted = ""; + + if (driveLetter.Length == 0) + successful = false; + + if (successful) + { + regExMatch = Regex.Match(driveLetter, @"([a-zA-Z]).*"); + + if (regExMatch.Success) + { + driveLetterFormatted = regExMatch.Groups[1].Value; + } + + if (IsCdDrive(driveLetterFormatted)) + { + string path = "\\\\.\\" + driveLetterFormatted + ":"; + + IntPtr handle = Win32Helpers.CreateFile(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + IntPtr.Zero, OPEN_EXISTING, 0, + IntPtr.Zero); + + if ((long)handle != -1) + { + int dummy = 0; + Win32Helpers.DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, IntPtr.Zero, 0, + IntPtr.Zero, 0, ref dummy, IntPtr.Zero); + Win32Helpers.CloseHandle(handle); + } + + } + else + successful = false; + } + + return successful; + } + } +} diff --git a/CommonLib/Library/IO/IniFileManip.cs b/CommonLib/Library/IO/IniFileManip.cs new file mode 100644 index 0000000..e11bda8 --- /dev/null +++ b/CommonLib/Library/IO/IniFileManip.cs @@ -0,0 +1,204 @@ +///====================================================================================== +/// File: IniFileManip.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// Manipulate INI file +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le +///====================================================================================== +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +using CommonLib.Misc; + +namespace CommonLib.IO +{ + ///====================================================================================== + /// IniFileManip + ///====================================================================================== + /// + /// This class can read, write or query any section, key and values in an INI file + /// + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ----------- ----------- ----- ------------------------------------------ + /// 06/06/12 D.Le + ///====================================================================================== + public class IniFileManip + { + public string iniFile; + + [DllImport("kernel32")] + private static extern long WritePrivateProfileString(string section, + string key, string val, string filePath); + + [DllImport("kernel32")] + private static extern int GetPrivateProfileString(string section, + string key, string def, StringBuilder retVal, int size, string filePath); + + [DllImport("kernel32")] + static extern uint GetPrivateProfileSectionNames(IntPtr lpszReturnBuffer, + uint nSize, string lpFileName); + + [DllImport("kernel32")] + private static extern int GetPrivateProfileSection(string section, byte[] lpReturnedString, int nSize, string lpFileName); + + public IniFileManip(string path) + { + iniFile = path; + } + + ///========================================================================== + /// IniFileManip.IniWriteValue + ///========================================================================== + /// + /// Modify a value of a key in Ini file. If key doesn't exist, add it + /// + /// + /// + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public void WriteValue(string Section, string Key, string Value) + { + WritePrivateProfileString(Section, Key, Value, this.iniFile); + } + + ///========================================================================== + /// IniFileManip.IniReadValue + ///========================================================================== + /// + /// Read a value of a key in Ini file + /// + /// + /// + /// Value of a key in a section of the ini file + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public string ReadValue(string Section, string Key) + { + StringBuilder temp = new StringBuilder(255); + int i = GetPrivateProfileString(Section, Key, "", temp, 255, this.iniFile); + return temp.ToString(); + } + + ///========================================================================== + /// IniFileManip.SectionExists + ///========================================================================== + /// + /// Determines if a section exist in Ini file + /// + /// + /// true if a section exists, otherwise false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public bool SectionExists(string Section) + { + StringBuilder temp = new StringBuilder(255); + int i = GetPrivateProfileString(Section, null, "", temp, 255, this.iniFile); + return (i > 0); + } + + ///========================================================================== + /// IniFileManip.getSectionNames + ///========================================================================== + /// + /// Get names of all sections in the ini file, even duplicate section names + /// + /// an array of strings with section names + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public string[] GetSectionNames() + { + + uint MAX_BUFFER = 32767; + IntPtr pReturnedString = Marshal.AllocCoTaskMem((int)MAX_BUFFER); + uint bytesReturned = GetPrivateProfileSectionNames(pReturnedString, MAX_BUFFER, iniFile); + if (bytesReturned == 0) + { + Marshal.FreeCoTaskMem(pReturnedString); + return null; + } + string local = Marshal.PtrToStringAnsi(pReturnedString, (int)bytesReturned).ToString(); + Marshal.FreeCoTaskMem(pReturnedString); + //use of Substring below removes terminating null for split + return local.Substring(0, local.Length - 1).Split('\0'); + } + + ///========================================================================== + /// IniFileManip.getUniqueSectionNames + ///========================================================================== + /// + /// Get names of only unique sections in the ini file. If there are duplicate + /// section names, only 1 is chosen + /// + /// an array of strings with unique section names + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public string[] GetUniqueSectionNames() + { + uint MAX_BUFFER = 32767; + IntPtr pReturnedString = Marshal.AllocCoTaskMem((int)MAX_BUFFER); + uint bytesReturned = GetPrivateProfileSectionNames(pReturnedString, MAX_BUFFER, iniFile); + if (bytesReturned == 0) + { + Marshal.FreeCoTaskMem(pReturnedString); + return null; + } + string local = Marshal.PtrToStringAnsi(pReturnedString, (int)bytesReturned).ToString(); + Marshal.FreeCoTaskMem(pReturnedString); + + return ArrayManip.GetDistinctValues(local.Substring(0, local.Length - 1).Split('\0')); + } + + ///========================================================================== + /// IniFileManip.readSectionData + ///========================================================================== + /// + /// Get all the keys and their associated values in a section + /// + /// + /// an array of strings with each entry containing key and value pair + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public string[] ReadSectionData(string section) + { + const int bufferSize = 32767; + + StringBuilder returnedString = new StringBuilder(); + + byte[] pReturnedString = new byte[bufferSize]; + + GetPrivateProfileSection(section, pReturnedString, bufferSize, iniFile); + return Encoding.ASCII.GetString(pReturnedString).Trim('\0').Split('\0'); + } + } +} diff --git a/CommonLib/Library/IO/PathManip.cs b/CommonLib/Library/IO/PathManip.cs new file mode 100644 index 0000000..53bfa19 --- /dev/null +++ b/CommonLib/Library/IO/PathManip.cs @@ -0,0 +1,716 @@ +///====================================================================================== +/// File: PathManip.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// This class deals with anything having to do with directory and file paths +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le +///====================================================================================== +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.ComponentModel; + +using CommonLib.Misc; +using CommonLib.Windows.Misc; + +namespace CommonLib.IO +{ + public class PathManip + { + [DllImport("shlwapi.dll")] + public static extern bool PathIsNetworkPath(string path); + + [DllImport("Shlwapi.dll")] + public static extern bool PathIsUNC(String pszPath); + + public enum enumWindowsDesignatedPaths { USER_DESKTOP, USER_PROFILE, ALL_USER_DESKTOP, USER_STARTUP_FOLDER, ALL_USER_STARTUP_FOLDER, USER_TEMP_FOLDER, CD_DRIVE }; + + ///========================================================================== + /// PathManip.PathIsFile + ///========================================================================== + /// + /// Determine if a given path is a file + /// + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool PathIsFile(string path) + { + bool isFile = false; + + if (File.Exists(path)) + isFile = true; + else if (Regex.IsMatch(path, @".+\.[^\\//.]+$", RegexOptions.IgnoreCase)) + { + isFile = true; + } + + return isFile; + } + + ///========================================================================== + /// PathManip.IsAbsolutePath + ///========================================================================== + /// + /// Determine if a given path is an absolute path + /// + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool IsAbsolutePath(string path) + { + bool pathIsAbsolute = false; + + if (Regex.IsMatch(path, @"^(?:[a-zA-Z]\:|\\\\[\w\.]+\\[\w.$]+)\\(?:[\w]+\\)*\w([\w.])+$")) + { + pathIsAbsolute = true; + } + + return pathIsAbsolute; + } + + ///========================================================================== + /// PathManip.GetUserDesktopPath + ///========================================================================== + /// + /// Get the path to the user desktop + /// + /// User's desktop path + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string GetUserDesktopPath() + { + string path = ""; + + // get the full domain and logon id + string currentWindowLogin = WindowsAdministration.GetWindowLoginID(); + // get only logon id + currentWindowLogin = Regex.Replace(currentWindowLogin, @"[^\\]+\\([^\\]+)", "$1"); + path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); + + // if this application is run as an account other than the account used to log into windows + if (!Regex.IsMatch(path, currentWindowLogin)) + { + // Modify the desktop path to reflect the desktop path of the actual account used to log into windows + path = Regex.Replace(path, @"(.+)\\[^\\]+\\Desktop", "$1\\" + currentWindowLogin + "\\Desktop", RegexOptions.IgnoreCase); + } + + path = RemoveTrailingSlashInPath(path); + return path; + } + + ///========================================================================== + /// PathManip.GetUserProfilePath + ///========================================================================== + /// + /// Get the path to user's home directory + /// + /// User's desktop path + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string GetUserProfilePath() + { + string path = ""; + + path = RemoveTrailingSlashInPath(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)); + + return path; + } + + ///========================================================================== + /// PathManip.GetAllUserDesktopPath + ///========================================================================== + /// + /// Get the path to the all user's desktop + /// + /// User's desktop path + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string GetAllUserDesktopPath() + { + string path = ""; + + path = RemoveTrailingSlashInPath(Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory)); + + return path; + } + + ///========================================================================== + /// PathManip.GetUserStartupFolderPath + ///========================================================================== + /// + /// Get the path to the user startup folder + /// + /// User's desktop path + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string GetUserStartupFolderPath() + { + string path = ""; + + // get the full domain and logon id + string currentWindowLogin = WindowsAdministration.GetWindowLoginID(); + // get only logon id + currentWindowLogin = Regex.Replace(currentWindowLogin, @"[^\\]+\\([^\\]+)", "$1"); + path = Environment.GetFolderPath(Environment.SpecialFolder.Startup); + + // if this application is run as an account other than the account used to log into windows + if (!Regex.IsMatch(path, currentWindowLogin)) + { + // Modify the desktop path to reflect the desktop path of the actual account used to log into windows + path = Regex.Replace(path, @"(.+\\Users\\)[^\\]+(\\.+Start Menu)", "${1}" + currentWindowLogin + "$2", RegexOptions.IgnoreCase); + } + + path = RemoveTrailingSlashInPath(path); + return path; + } + + ///========================================================================== + /// PathManip.GetAllUserStartupFolderPath + ///========================================================================== + /// + /// Get the path to the all user startup folder + /// + /// User's desktop path + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string GetAllUserStartupFolderPath() + { + string path = ""; + + path = RemoveTrailingSlashInPath(Environment.GetFolderPath(Environment.SpecialFolder.CommonStartup)); + + return path; + } + + ///========================================================================== + /// PathManip.GetUserTempPath + ///========================================================================== + /// + /// Get the path to the user's temporary folder + /// + /// User's temporary path + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string GetUserTempPath() + { + string path = ""; + + // get the full domain and logon id + string currentWindowLogin = WindowsAdministration.GetWindowLoginID(); + // get only logon id + currentWindowLogin = Regex.Replace(currentWindowLogin, @"[^\\]+\\([^\\]+)", "$1"); + path = Path.GetTempPath(); + + // if this application is run as an account other than the account used to log into windows + if (!Regex.IsMatch(path, currentWindowLogin)) + { + // Modify the desktop path to reflect the desktop path of the actual account used to log into windows + path = Regex.Replace(path, @"(.+)\\[^\\]+\\(appdata.+[^\\])\\?", "$1\\" + currentWindowLogin + "\\$2", RegexOptions.IgnoreCase); + } + + path = RemoveTrailingSlashInPath(path); + return path; + } + + ///========================================================================== + /// PathManip.DeleteFoldersRecursive + ///========================================================================== + /// + /// Delete folder including all file and subfolders within it + /// + /// Path to a directory + /// None + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static void DeleteFoldersRecursive(string directory) + { + var dirInfo = new DirectoryInfo(directory) { Attributes = FileAttributes.Normal }; + + foreach (var info in dirInfo.GetFileSystemInfos("*", SearchOption.AllDirectories)) + { + info.Attributes = FileAttributes.Normal; + } + + dirInfo.Delete(true); + } + + ///========================================================================== + /// PathManip.PathIsWritable + ///========================================================================== + /// + /// Given a path, make sure we can create a folder if it doesn't exist + /// or if it does exist, create an empty file in it to see if user has write + /// access + /// + /// Path to a directory + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool PathIsWritable(string path) + { + bool writable = true; + + path = PathManip.AddTrailingSlashToPath(path); + + // if the path doesn't exist and it contains at least one folder, + // we want to check if upper level folder exists + if (!Directory.Exists(path) && PathContainsAtLeastOneFolder(path)) + { + string tempDir = ""; + string topLevelDirNotFound = path; + + while (!Directory.Exists(topLevelDirNotFound)) + { + if (!Regex.IsMatch(topLevelDirNotFound, @"[^\\]+\\", RegexOptions.IgnoreCase)) + { + writable = false; + break; + } + tempDir = topLevelDirNotFound; + // remove the inner most folder + topLevelDirNotFound = Regex.Replace(topLevelDirNotFound, @"\\[^\\]+\\?$", "", RegexOptions.IgnoreCase); + } + + if (writable) + { + if (tempDir.Length > 0) + topLevelDirNotFound = tempDir; + else + tempDir = path; + + try + { + Directory.CreateDirectory(path); + // delete only the directories that were created + Directory.Delete(tempDir, true); + } + catch + { + writable = false; + } + } + } + else // if the path exists + { + string filename = "test"; + string fileExt = ".txt"; + int count = 1; + string errMsg = ""; + string filePathAndName = Path.Combine(path, filename + fileExt); + while (File.Exists(filePathAndName)) + { + filename = "test" + (count++).ToString(); + filePathAndName = Path.Combine(path, filename + fileExt); + } + + // create a new file + if (!FileManip.CopyFile("", "", Path.GetDirectoryName(filePathAndName), Path.GetFileName(filePathAndName), true, FileAttributes.Normal, ref errMsg)) + writable = false; + else + File.Delete(filePathAndName); + } + + return writable; + } + + ///========================================================================== + /// PathManip.GenerateUniquePath + ///========================================================================== + /// + /// Generate a unique path + /// Example: C:\path -> C:\path_07252015_R1 + /// + /// Path to a directory + /// Unique path + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string GenerateUniquePath(string path) + { + int logCount = 0; + string logDir; + + string sLogCount = "00"; + + DateTime dt = DateTime.Now; + + string strYear = dt.ToString("yy"); + string strMonth = dt.ToString("MM"); + string strDay = dt.ToString("dd"); + + while (true) + { + logDir = Path.GetFullPath(path); + logDir += "_" + strMonth + strDay + strYear + "_R" + sLogCount; + + logCount++; + if (Directory.Exists(logDir)) + { + if (logCount < 10) + { + sLogCount = "0" + logCount.ToString(); + } + else + sLogCount = logCount.ToString(); + } + else + break; + } + + return logDir; + } + + ///========================================================================== + /// PathManip.PathContainsAtLeastOneFolder + ///========================================================================== + /// + /// Given a path, indicate whether the path contains at least one folder + /// + /// Path to a directory + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool PathContainsAtLeastOneFolder(string path) + { + bool pathContainsFolders = false; + if (Regex.IsMatch(path, @"([a-zA-Z]):\\[^\\]+", RegexOptions.IgnoreCase) + || + Regex.IsMatch(path, @"(\\\\[^\\]+)\\[^\\]+", RegexOptions.IgnoreCase) + ) + { + pathContainsFolders = true; + } + + return pathContainsFolders; + } + + ///========================================================================== + /// PathManip.GetDriveLetterOrHostnameFromPath + ///========================================================================== + /// + /// Given a path, return the drive letter if the path is a local path or + /// host name if the path is network path + /// + /// Path to a directory + /// drive letter or hostname + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string GetDriveLetterOrHostnameFromPath(string path) + { + string driveLetterOrHostname = ""; + if (PathIsNetworkPath(path)) + { + Uri bb = new Uri(path); + + driveLetterOrHostname = bb.Host; + } + else + { + Match regExMatch = Regex.Match(path, @"([a-zA-Z]):.*", RegexOptions.IgnoreCase); + + if (regExMatch.Success) + driveLetterOrHostname = regExMatch.Groups[1].Value; + } + + + return driveLetterOrHostname; + } + + ///========================================================================== + /// PathManip.DirectoryVisible + ///========================================================================== + /// + /// Check to see if Directory is there even though user has no access to it + /// This is the case where the directory exists, but user has no access to + /// it so the call Directory.Exists() would fail making us think that the + /// directory doesn't exist when in fact it does + /// + /// Path to a directory + /// error message + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool DirectoryVisible(string path, ref string errMsg) + { + bool successful = true; + + try + { + Directory.GetAccessControl(path); + } + catch (UnauthorizedAccessException) + { + errMsg = "The account used by this application does not have required permission to access directory " + path; + } + catch (Exception e) + { + int errorCode = 0; + if (ErrorHandling.GetExceptionErrorCode(e.Message, ref errorCode)) + { + if (errorCode == 1326) + errMsg = "Error code " + errorCode.ToString() + " - Logon Failure. The account used by this application is not permitted to access directory " + path; + else + successful = false; + } + else + { + errMsg = "Unknown error occurred"; + successful = false; + } + } + + return successful; + } + + ///========================================================================== + /// PathManip.PathIsLocalPath + ///========================================================================== + /// + /// Given a path (either UNC or local path), determine if the path is local path + /// + /// path + /// true/false + ///========================================================================== + public static bool PathIsLocalPath(string path) + { + bool isLocalPath = true; + + if (!PathIsUNC(path)) + { + isLocalPath = !PathIsNetworkPath(path); + } + else + { + Uri uri = new Uri(path); + isLocalPath = HostIsLocal(uri.Host); // Refer to David's answer + } + + return isLocalPath; + } + + ///========================================================================== + /// PathManip.HostIsLocal + ///========================================================================== + /// + /// Given a hostname, determine if it is a local hostname + /// + /// hostname + /// true/false + ///========================================================================== + public static bool HostIsLocal(string hostname) + { + IPAddress[] host; + //get host addresses + try { host = Dns.GetHostAddresses(hostname); } + catch (Exception) { return false; } + //get local adresses + IPAddress[] local = Dns.GetHostAddresses(Dns.GetHostName()); + //check if local + return host.Any(hostAddress => IPAddress.IsLoopback(hostAddress) || local.Contains(hostAddress)); + } + + ///========================================================================== + /// PathManip.GetProperFilePathCapitalization + ///========================================================================== + /// + /// Takes a file path along with the file name and return the exact + /// case-lettering of directory name and file name as they are named + /// in the Windows file system + /// + /// File name along with its path + /// Same path and file name as the path passed in as a parameter + /// except the path and file name has the exact case lettering as how they + /// are named in the Windows file system + /// + ///========================================================================== + public static string GetProperFilePathCapitalization(string filePathAndName) + { + string path = filePathAndName; + + if (File.Exists(filePathAndName)) + { + FileInfo fileInfo = new FileInfo(filePathAndName); + DirectoryInfo dirInfo = fileInfo.Directory; + + string dir = GetProperDirectoryCapitalization(dirInfo.FullName); + string file = dirInfo.GetFiles(fileInfo.Name)[0].Name; + + // if the path is a UNC path, the GetProperDirectoryCapitalization() will + // only return the path without the hostname, we must then extract the hostname + // then prepend it to the path + if (PathIsUNC(filePathAndName)) + { + Uri bb = new Uri(filePathAndName); + + dir = @"\\" + bb.Host + @"\" + dir; + } + + path = Path.Combine(dir, file); + } + + return path; + } + + ///========================================================================== + /// PathManip.GetProperDirectoryCapitalization + ///========================================================================== + /// + /// Takes a directory path and return the exact case-lettering of + /// directory name as they are named in the Windows file system + /// + /// Path to a directory + /// Same path as the path passed in as a parameter except the + /// path has the exact case lettering as how they are named in the Windows + /// file system + /// + ///========================================================================== + public static string GetProperDirectoryCapitalization(string path) + { + string dir = path; + + if (Directory.Exists(path)) + { + DirectoryInfo dirInfo = new DirectoryInfo(path); + DirectoryInfo parentDirInfo = dirInfo.Parent; + if (null == parentDirInfo) + return dirInfo.Name; + + dir = Path.Combine(GetProperDirectoryCapitalization(parentDirInfo.FullName), parentDirInfo.GetDirectories(dirInfo.Name)[0].Name); + } + + return dir; + } + + ///========================================================================== + /// PathManip.AddTrailingSlashToPath + ///========================================================================== + /// + /// Add trailing slash at the end of a directory path if there's none + /// + /// path to directory + /// Path without the slash at the end + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string AddTrailingSlashToPath(string path) + { + return Regex.Replace(path, @"(.+[^\\/])$", "$1\\"); + } + + ///========================================================================== + /// PathManip.RemoveTrailingSlashInPath + ///========================================================================== + /// + /// Remove trailing slash at the end of a directory path if it exists + /// + /// path to directory + /// Path without the slash at the end + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string RemoveTrailingSlashInPath(string path) + { + return Regex.Replace(path, @"(\\+|/+)$", ""); + } + + ///========================================================================== + /// PathManip.RemoveLeadingSlashInPath + ///========================================================================== + /// + /// Remove leading slash at the beginning of a directory path if it exists + /// + /// path to directory + /// Path without the slash at the front + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string RemoveLeadingSlashInPath(string path) + { + return Regex.Replace(path, @"^(\\+|/+)", ""); + } + + ///========================================================================== + /// PathManip.PathsEqual + ///========================================================================== + /// + /// Compare if 2 paths are equal + /// + /// path 1 + /// path 2 + /// case sensitivity + /// true if 2 paths are equal, otherwise false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool PathsEqual(string path1, string path2, StringComparison caseSensitivity) + { + string path1WithoutSlashes = Regex.Replace(path1, @"[\\/]", ","); + string path2WithoutSlashes = Regex.Replace(path2, @"[\\/]", ","); + + if (string.Equals(path1WithoutSlashes, path2WithoutSlashes, caseSensitivity)) + return true; + else + return false; + } + } +} diff --git a/CommonLib/Library/IO/TreeUtils.cs b/CommonLib/Library/IO/TreeUtils.cs new file mode 100644 index 0000000..f409bad --- /dev/null +++ b/CommonLib/Library/IO/TreeUtils.cs @@ -0,0 +1,169 @@ +//=========================================================================================== +// TreeUtils.cs +//=========================================================================================== + +// GENERAL DESCRIPTION OR PURPOSE: +// Generic C# Tree utility +// +//=========================================================================================== +// Date Programmer Proj. SAR / STRIP Description +// -------- ---------- ---------- ----------- ----------------------------------------- +// 06/29/12 J.Weichers SSIRU-742X 1448 Initial Version +//=========================================================================================== +using System; +using System.IO; +using System.Text; +using System.Linq; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Windows.Forms; + + +namespace CommonLib.IO +{ + public class TreeUtils + { + static private bool DEBUG = false; + private string m_treeRoot = ""; + + ///=================================================================================== + /// TreeUtils::TreeUtils + ///=================================================================================== + /// + /// Class Ctor for TreeUtils + /// + /// String that specifies the tree's root directory + ///=================================================================================== + /// Date Programmer Proj.ID SAR / STRIP Description + /// --/--/-- ---------- ---------- ------------- ---------------------------------- + /// 06/12/12 J.Weichers SSIRU-742X 1448 Original + ///=================================================================================== + public TreeUtils(string treeRoot) + { + m_treeRoot = treeRoot; + } + + ///=================================================================================== + /// TreeUtils::DeleteFileSystemInfo + ///=================================================================================== + /// + /// Delete a file, or folder and any files or sub-folders if necessary + /// + /// A file or a directory + ///=================================================================================== + /// Date Programmer Proj.ID SAR / STRIP Description + /// --/--/-- ---------- ---------- ------------- ---------------------------------- + /// 06/12/12 J.Weichers SSIRU-742X 1448 Original + ///=================================================================================== + public static void DeleteFileSystemInfo(FileSystemInfo fsi) + { + fsi.Attributes = FileAttributes.Normal; + var di = fsi as DirectoryInfo; + + if (di != null) + { + foreach (var dirInfo in di.GetFileSystemInfos()) + { + DeleteFileSystemInfo(dirInfo); + } + } + + fsi.Delete(); + } + + ///=================================================================================== + /// TreeUtils::TreeCopy + ///=================================================================================== + /// + /// Recursive function to copy a tree from one location to another. The Directory.Move() + /// has a recursive option, but is buggy, and throws random exceptions. It also is + /// limited + /// + /// A directory that has a sub-directory to be copied + /// The name of the direcory to be copied + /// A destination directory of the copy + ///=================================================================================== + /// Date Programmer Proj.ID SAR / STRIP Description + /// --/--/-- ---------- ---------- ------------- ---------------------------------- + /// 06/12/12 J.Weichers SSIRU-742X 1448 Original + ///=================================================================================== + static public void TreeCopy(String srcFolderPath, // folder and path to copy + String srcFolderName, // folder to copy + String destFolderPath) // where to copy to + { + try + { + DirectoryInfo srcDirInfo = new + DirectoryInfo(srcFolderPath + "\\"); + + // 1. Create the new destination directory + String newDestFolder = Path.Combine(destFolderPath, srcFolderName); + try + { + Directory.CreateDirectory(newDestFolder); + } + catch (Exception Exc) + { + MessageBox.Show("Error creating directory '" + newDestFolder + + "': " + Exc.Message, "Directory Error", MessageBoxButtons.OK, + MessageBoxIcon.Error); + } + + // 2. copy the files first + FileInfo[] fileInfoArray = srcDirInfo.GetFiles(); + foreach (FileInfo file in fileInfoArray) + { + String newFileAndPath = Path.Combine(newDestFolder, file.Name); + try + { + file.CopyTo(newFileAndPath, true); + } + catch (Exception Exc) + { + MessageBox.Show("Error copying file '" + file.Name + "': " + + Exc.Message, "File Error", MessageBoxButtons.OK, + MessageBoxIcon.Error); + } + } + + // 3. copy the sub-directories last via recursion + DirectoryInfo[] dirInfoArray = srcDirInfo.GetDirectories(); + foreach (DirectoryInfo di in dirInfoArray) + { + TreeCopy(Path.Combine(srcFolderPath, di.Name), + di.Name, + Path.Combine(destFolderPath, srcFolderName)); + } + } + catch (Exception Exc) { ExceptionMessageBox(Exc, Environment.StackTrace); } + } + + ///=================================================================================== + /// TreeUtils::ExceptionMessageBox + ///=================================================================================== + /// + /// Convenience exception utility + /// + /// The exception that was thrown + /// The call stack when the exception was thrown + ///=================================================================================== + /// Date Programmer Proj.ID SAR / STRIP Description + /// --/--/-- ---------- ---------- ------------- ---------------------------------- + /// 06/12/12 J.Weichers SSIRU-742X 1448 Original + ///=================================================================================== + static public void ExceptionMessageBox(Exception Exc, String stack) + { + if (DEBUG == true) + { + MessageBox.Show(Exc.ToString() + "\nStack trace: " + + stack, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + else + { + MessageBox.Show(Exc.ToString(), "Error", MessageBoxButtons.OK, + MessageBoxIcon.Error); + } + } + + } +} diff --git a/CommonLib/Library/Misc/ArrayManip.cs b/CommonLib/Library/Misc/ArrayManip.cs new file mode 100644 index 0000000..102b0a1 --- /dev/null +++ b/CommonLib/Library/Misc/ArrayManip.cs @@ -0,0 +1,63 @@ +///====================================================================================== +/// File: ArrayManip.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// Manipulate arrays +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le +///====================================================================================== +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace CommonLib.Misc +{ + ///========================================================================== + /// ArrayManip + ///========================================================================== + /// + /// Manipulate arrays + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public class ArrayManip + { + ///========================================================================== + /// ArrayManip.GetDistinctValues + ///========================================================================== + /// + /// Given an array of any type, return an array of the same type with only + /// unique values + /// + /// an array of any type + /// an array of any type with only unique values + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public T[] GetDistinctValues(T[] array) + { + List tmp = new List(); + for (int i = 0; i < array.Length; i++) + { + if (tmp.Contains(array[i])) + continue; + tmp.Add(array[i]); + } + return tmp.ToArray(); + } + } +} diff --git a/CommonLib/Library/Misc/Conversion.cs b/CommonLib/Library/Misc/Conversion.cs new file mode 100644 index 0000000..de0907e --- /dev/null +++ b/CommonLib/Library/Misc/Conversion.cs @@ -0,0 +1,73 @@ +///====================================================================================== +/// File: Conversion.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// Perform various conversions +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le +///====================================================================================== +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace CommonLib.Misc +{ + ///========================================================================== + /// Conversion + ///========================================================================== + /// + /// Perform various conversions + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public class Conversion + { + ///========================================================================== + /// Conversion.UInt32ToIP + ///========================================================================== + /// + /// Convert an octet (8bit) of type UInt32 to a string representation + /// + /// an octet of type UInt32 + /// an octet of type string + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public string UInt32ToIP(UInt32 ipAddress) + { + return new IPAddress(BitConverter.GetBytes(ipAddress).Reverse().ToArray()).ToString(); + } + + ///========================================================================== + /// Conversion.IP2UInt32 + ///========================================================================== + /// + /// Convert an octet (8bit) of type string to type UInt32 representation + /// + /// an octet of type string + /// an octet of type UInt32 + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static UInt32 IP2UInt32(string ipAddress) + { + return BitConverter.ToUInt32(IPAddress.Parse(ipAddress).GetAddressBytes().Reverse().ToArray(), 0); + } + } +} diff --git a/CommonLib/Library/Misc/DateTimeManip.cs b/CommonLib/Library/Misc/DateTimeManip.cs new file mode 100644 index 0000000..83ecfc8 --- /dev/null +++ b/CommonLib/Library/Misc/DateTimeManip.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.ComponentModel; +using System.Threading; +using System.Diagnostics; + +using Microsoft.Win32; +using System.Xml; +using System.Xml.XPath; + +using CommonLib.Windows.Forms; +using CommonLib.IO; +using CommonLib.Misc; +using CommonLib.DirectoryServices; +using Assembly = System.Reflection.Assembly; + +namespace CommonLib.Misc +{ + public static class DateTimeManip + { + ///=================================================================================== + /// DateTimeManip.DescribeTimeElapsed + ///=================================================================================== + /// + /// Give a variable of type TimeSpan, describe the time in days, hours, mins, seconds + /// + /// use the stopwatch.elapsed + ///=================================================================================== + /// Date Programmer Proj.ID SAR / STRIP Description + /// --/--/-- ---------- ---------- ------------- ---------------------------------- + /// 05/29/12 D.Le + ///=================================================================================== + public static string DescribeTimeElapsed(TimeSpan ts) + { + string describe = ""; + + if (ts.Days > 0) + describe += ts.Days.ToString() + " d"; + + if (ts.Hours > 0) + { + if (describe.Length > 0) + describe += " "; + describe += ts.Hours.ToString() + " h"; + } + + if (ts.Minutes > 0) + { + if (describe.Length > 0) + describe += " "; + describe += ts.Minutes.ToString() + " m"; + } + + if (ts.Seconds > 0) + { + if (describe.Length > 0) + describe += " "; + describe += ts.Seconds.ToString() + " s"; + } + + if (describe.Length == 0) + describe = "0 s"; + + return describe; + } + + ///=================================================================================== + /// DateTimeManip.GetLinkerDateTime + ///=================================================================================== + /// + /// Get the date and time of when the assembly was built by reading its PE information + /// Usage: Assembly.GetExecutingAssembly().GetLinkerDateTime() + /// + /// use the stopwatch.elapsed + ///=================================================================================== + /// Date Programmer Proj.ID TEMA Description + /// --/--/-- ---------- ---------- ------------- ---------------------------------- + /// 05/29/12 D.Le + ///=================================================================================== + public static DateTime GetLinkerDateTime(this Assembly assembly, TimeZoneInfo tzi = null) + { + // Constants related to the Windows PE file format. + const int PE_HEADER_OFFSET = 60; + const int LINKER_TIMESTAMP_OFFSET = 8; + + // Discover the base memory address where our assembly is loaded + var entryModule = assembly.ManifestModule; + var hMod = Marshal.GetHINSTANCE(entryModule); + if (hMod == IntPtr.Zero - 1) throw new Exception("Failed to get HINSTANCE."); + + // Read the linker timestamp + var offset = Marshal.ReadInt32(hMod, PE_HEADER_OFFSET); + var secondsSince1970 = Marshal.ReadInt32(hMod, offset + LINKER_TIMESTAMP_OFFSET); + + // Convert the timestamp to a DateTime + var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + var linkTimeUtc = epoch.AddSeconds(secondsSince1970); + var dt = TimeZoneInfo.ConvertTimeFromUtc(linkTimeUtc, tzi ?? TimeZoneInfo.Local); + return dt; + } + } +} diff --git a/CommonLib/Library/Misc/ErrorHandling.cs b/CommonLib/Library/Misc/ErrorHandling.cs new file mode 100644 index 0000000..d6eb81b --- /dev/null +++ b/CommonLib/Library/Misc/ErrorHandling.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace CommonLib.Misc +{ + public class ErrorHandling + { + ///========================================================================== + /// Misc.ErrorHandling + ///========================================================================== + /// + /// Get error code from the error message + /// + /// error message + /// error code returned + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool GetExceptionErrorCode(string errorMsg, ref int errorCode) + { + bool successful = true; + Match regExMatch; + + regExMatch = Regex.Match(errorMsg, @".+ error code ([\d]+)[^d]+", RegexOptions.IgnoreCase); + + if (regExMatch.Success) + { + errorCode = Convert.ToInt32(regExMatch.Groups[1].Value); + } + else + successful = false; + + return successful; + } + } +} diff --git a/CommonLib/Library/Misc/StringManip.cs b/CommonLib/Library/Misc/StringManip.cs new file mode 100644 index 0000000..db2b82c --- /dev/null +++ b/CommonLib/Library/Misc/StringManip.cs @@ -0,0 +1,193 @@ +///====================================================================================== +/// File: ArrayManip.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// Manipulate string +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le +///====================================================================================== +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace CommonLib.Misc +{ + public class StringManip + { + public enum enumTextHorizontalAlignment + { + Left, + Right + } + + ///========================================================================== + /// StringManip.IsValidIPv4 + ///========================================================================== + /// + /// Given an IP version 4 address. Determine if it is valid or not + /// + /// IP Address + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool IsValidIPv4(string ipString) + { + if (String.IsNullOrWhiteSpace(ipString)) + { + return false; + } + + string[] splitValues = ipString.Split('.'); + if (splitValues.Length != 4) + { + return false; + } + + byte tempForParsing; + + return splitValues.All(r => byte.TryParse(r, out tempForParsing)); + + } + + ///========================================================================== + /// StringManip.GetPhraseWordIndexInText + ///========================================================================== + /// + /// Return the word index of the first word of the phrase in a string + /// + /// a string + /// a phrase to look for in the text + /// index + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static int GetPhraseWordIndexInText(string text, string phrase) + { + int index = -1; + int matchIndex = -1; + Regex ItemRegex = new Regex(@"\s*[^\s]+", RegexOptions.Compiled); + string[] wordsInPhrase = phrase.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); + string wordBuilder = ""; + + if (wordsInPhrase.Length > 0) + { + // going through each word in text + foreach (Match ItemMatch in ItemRegex.Matches(text)) + { + index++; + + if (wordBuilder.Length > 0) + wordBuilder += " "; + + wordBuilder += ItemMatch.ToString().Trim(); + + string[] wordsInWordBuilder = wordBuilder.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); + + if (wordsInWordBuilder.Length == wordsInPhrase.Length) + { + if (String.Equals(wordBuilder, phrase, StringComparison.OrdinalIgnoreCase)) + { + matchIndex = index - (wordsInPhrase.Length - 1); + break; + } + else + { + wordBuilder = ""; + for (int i = 1; i < wordsInPhrase.Length; i++) + { + if (wordBuilder.Length > 0) + wordBuilder += " "; + + wordBuilder += wordsInWordBuilder[i]; + } + } + } + } + } + + return matchIndex; + } + + ///========================================================================== + /// StringManip.ShortenStringToSize + ///========================================================================== + /// + /// Given a string and a desired string length, if the actual string's length + /// is greater than the desired string length, we keep only the left-most and + /// the right-most parts of the string. + /// the size of the left-most part of the string will be (Desired String Length / 2) + /// the size of the right-most part of the string will be (Desired String Length / 2) + /// + /// + /// + /// a string + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public string ShortenStringToSize(string str, int desiredStrLen) + { + string shortenString = str; + if (str.Length > desiredStrLen && str.Length >= 5) + { + int leftCharCount = (int)Math.Ceiling((desiredStrLen - 3.0) / 2.0); + int rightCharCount = desiredStrLen - 3 - leftCharCount; + shortenString = str.Substring(0, leftCharCount) + "..." + str.Substring(str.Length - rightCharCount, rightCharCount); + } + + return shortenString; + } + + ///========================================================================== + /// TextAlign + ///========================================================================== + /// + /// Align text to left-aligned or right-aligned given the maximum number + /// of characters. If maximum of characters is bigger than the length of + /// the text provided, space will be padded to make up the difference + /// + /// a string + /// + /// + /// a string + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public string TextAlign(string text, int maxNumCharacters, enumTextHorizontalAlignment horizonAlignmentPos) + { + string str = text; + string padding = ""; + + int diff = maxNumCharacters - text.Length; + + for (int i = 1; i <= diff; i++) + { + padding += " "; + } + + if (horizonAlignmentPos == enumTextHorizontalAlignment.Left) + str += padding; + else + str = padding + str; + + return str; + } + } +} diff --git a/CommonLib/Library/Runtime/InteropServices/DocumentFormat.cs b/CommonLib/Library/Runtime/InteropServices/DocumentFormat.cs new file mode 100644 index 0000000..12729a8 --- /dev/null +++ b/CommonLib/Library/Runtime/InteropServices/DocumentFormat.cs @@ -0,0 +1,112 @@ +///====================================================================================== +/// File: DocumentFormat.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// Formatting form controls that display text +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le SSIRU_PL 1448 Original +///====================================================================================== +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.Windows.Forms; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; + +namespace Northgrum.Runtime.InteropServices +{ + ///========================================================================== + /// FileManip + ///========================================================================== + /// + /// Format any form control that display text in it + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le SSIRU_PL 1448 Original + ///========================================================================== + public class DocumentFormat + { + [DllImport("user32", CharSet = CharSet.Auto)] + private static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, ref PARAFORMAT lParam); + + const int PFM_SPACEBEFORE = 0x00000040; + const int PFM_SPACEAFTER = 0x00000080; + const int PFM_LINESPACING = 0x00000100; + + const int SCF_SELECTION = 1; + const int EM_SETPARAFORMAT = 1095; + + ///========================================================================== + /// DocumentFormat.setLineFormatInRichTextBox + ///========================================================================== + /// + /// Allow the setting of line spacing in RichTextBox + /// + /// For rule, there are only 6 possible values, 0-5 + /// 0 - Single Spacing, line spacing is ignored + /// 1 - One-and-a-half spacing. line spacing is ignored + /// 2 - Double spacing. line spacing is ignored + /// 3 - Line spacing specifies spacing from one line to the next + /// 4 - Line spacing specifies spacing from one line to the next + /// 5 - Line spacing/20 is the spacing from one line to the next + /// + /// rich text box control + /// can only be 0-5 (the summary describes each rule) + /// amount of spacing between 2 lines + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le SSIRU_PL 1448 Original + ///========================================================================== + static public void setLineFormatInRichTextBox(RichTextBox rTextbox, byte rule, int space) + { + PARAFORMAT fmt = new PARAFORMAT(); + fmt.cbSize = Marshal.SizeOf(fmt); + fmt.dwMask = PFM_LINESPACING; + fmt.dyLineSpacing = space; + fmt.bLineSpacingRule = rule; + rTextbox.SelectAll(); + SendMessage(new HandleRef(rTextbox, rTextbox.Handle), EM_SETPARAFORMAT, SCF_SELECTION, ref fmt); + } + + [StructLayout(LayoutKind.Sequential)] + public struct PARAFORMAT + { + public int cbSize; + public uint dwMask; + public short wNumbering; + public short wReserved; + public int dxStartIndent; + public int dxRightIndent; + public int dxOffset; + public short wAlignment; + public short cTabCount; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public int[] rgxTabs; + // PARAFORMAT2 from here onwards + public int dySpaceBefore; + public int dySpaceAfter; + public int dyLineSpacing; + public short sStyle; + public byte bLineSpacingRule; + public byte bOutlineLevel; + public short wShadingWeight; + public short wShadingStyle; + public short wNumberingStart; + public short wNumberingStyle; + public short wNumberingTab; + public short wBorderSpace; + public short wBorderWidth; + public short wBorders; + } + } +} diff --git a/CommonLib/Library/Runtime/InteropServices/Win32Helpers.cs b/CommonLib/Library/Runtime/InteropServices/Win32Helpers.cs new file mode 100644 index 0000000..a5be4bc --- /dev/null +++ b/CommonLib/Library/Runtime/InteropServices/Win32Helpers.cs @@ -0,0 +1,1099 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Threading; +using System.Windows.Forms; +using System.Globalization; +using System.Windows.Interop; + +namespace CommonLib.Runtime.InteropServices +{ + public class Win32Helpers + { + [DllImport("kernel32")] + public static extern IntPtr CreateFile + (string filename, uint desiredAccess, + uint shareMode, IntPtr securityAttributes, + int creationDisposition, int flagsAndAttributes, + IntPtr templateFile); + + [DllImport("kernel32")] + public static extern int DeviceIoControl + (IntPtr deviceHandle, uint ioControlCode, + IntPtr inBuffer, int inBufferSize, + IntPtr outBuffer, int outBufferSize, + ref int bytesReturned, IntPtr overlapped); + + [DllImport("kernel32")] + public static extern int CloseHandle(IntPtr handle); + + [DllImport("user32.dll", SetLastError = true)] + private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo); + + [DllImport("user32.dll")] + private static extern IntPtr GetMessageExtraInfo(); + + [DllImport("User32.dll")] + public static extern int SendInput(uint nInputs, INPUT[] pInputs, int cbSize); + + public const int INPUT_MOUSE = 0; + public const int INPUT_KEYBOARD = 1; + public const int INPUT_HARDWARE = 2; + public const uint KEYEVENTF_EXTENDEDKEY = 0x0001; + public const uint KEYEVENTF_KEYUP = 0x0002; + public const uint KEYEVENTF_UNICODE = 0x0004; + public const uint KEYEVENTF_SCANCODE = 0x0008; + public const uint XBUTTON1 = 0x0001; + public const uint XBUTTON2 = 0x0002; + public const uint MOUSEEVENTF_MOVE = 0x0001; + public const uint MOUSEEVENTF_LEFTDOWN = 0x0002; + public const uint MOUSEEVENTF_LEFTUP = 0x0004; + public const uint MOUSEEVENTF_RIGHTDOWN = 0x0008; + public const uint MOUSEEVENTF_RIGHTUP = 0x0010; + public const uint MOUSEEVENTF_MIDDLEDOWN = 0x0020; + public const uint MOUSEEVENTF_MIDDLEUP = 0x0040; + public const uint MOUSEEVENTF_XDOWN = 0x0080; + public const uint MOUSEEVENTF_XUP = 0x0100; + public const uint MOUSEEVENTF_WHEEL = 0x0800; + public const uint MOUSEEVENTF_VIRTUALDESK = 0x4000; + public const uint MOUSEEVENTF_ABSOLUTE = 0x8000; + + public enum InputType : int + { + Mouse = 0, + Keyboard = 1, + Hardware = 2 + }; + + public enum MouseFlags : uint + { + Move = 0x0001, // MOUSEEVENTF_MOVE - Specifies that movement occurred. + LeftDown = 0x0002, // MOUSEEVENTF_LEFTDOWN - Specifies that the left button was pressed. + LeftUp = 0x0004, // MOUSEEVENTF_LEFTUP - Specifies that the left button was released. + RightDown = 0x0008, // MOUSEEVENTF_RIGHTDOWN - Specifies that the right button was pressed. + RightUp = 0x0010, // MOUSEEVENTF_RIGHTUP - Specifies that the right button was released. + Absolute = 0x8000, // MOUSEEVENTF_ABSOLUTE - Specifies that the dx and dy members contain normalized absolute coordinates. If the flag is not set, dxand dy contain relative data (the change in position since the last reported position). This flag can be set, or not set, regardless of what kind of mouse or other pointing device, if any, is connected to the system. For further information about relative mouse motion, see the following Remarks section. + Wheel = 0x0080, // MOUSEEVENTF_WHEEL Windows NT/2000/XP: Specifies that the wheel was moved, if the mouse has a wheel. The amount of movement is specified in mouseData. + MiddleDown = 0x0020, // MOUSEEVENTF_MIDDLEDOWN Specifies that the middle button was pressed. + MiddleUp = 0x0040, // MOUSEEVENTF_MIDDLEUP Specifies that the middle button was released. + VirtualDesk = 0x4000, //MOUSEEVENTF_VIRTUALDESK Windows 2000/XP:Maps coordinates to the entire desktop. Must be used with MOUSEEVENTF_ABSOLUTE. + XDown = 0x0080, // MOUSEEVENTF_XDOWN Windows 2000/XP: Specifies that an X button was pressed. + XUp = 0x0100, // MOUSEEVENTF_XUP Windows 2000/XP: Specifies that an X button was released. + HWheel = 0x1000 // MOUSEEVENTF_HWHEEL Windows Vista: Specifies that the wheel was moved horizontally, if the mouse has a wheel. The amount of movement is specified in mouseData. + }; + + public enum VK : ushort + { + /// + ///Left mouse button + /// + LBUTTON = 0x01, + /// + ///Right mouse button + /// + RBUTTON = 0x02, + /// + ///Control-break processing + /// + CANCEL = 0x03, + /// + ///Middle mouse button (three-button mouse) + /// + MBUTTON = 0x04, + /// + ///Windows 2000/XP: X1 mouse button + /// + XBUTTON1 = 0x05, + /// + ///Windows 2000/XP: X2 mouse button + /// + XBUTTON2 = 0x06, + /// + ///BACKSPACE key + /// + BACK = 0x08, + /// + ///TAB key + /// + TAB = 0x09, + /// + ///CLEAR key + /// + CLEAR = 0x0C, + /// + ///ENTER key + /// + RETURN = 0x0D, + /// + ///SHIFT key + /// + SHIFT = 0x10, + /// + ///CTRL key + /// + CONTROL = 0x11, + /// + ///ALT key + /// + MENU = 0x12, + /// + ///PAUSE key + /// + PAUSE = 0x13, + /// + ///CAPS LOCK key + /// + CAPITAL = 0x14, + /// + ///Input Method Editor (IME) Kana mode + /// + KANA = 0x15, + /// + ///IME Hangul mode + /// + HANGUL = 0x15, + /// + ///IME Junja mode + /// + JUNJA = 0x17, + /// + ///IME final mode + /// + FINAL = 0x18, + /// + ///IME Hanja mode + /// + HANJA = 0x19, + /// + ///IME Kanji mode + /// + KANJI = 0x19, + /// + ///ESC key + /// + ESCAPE = 0x1B, + /// + ///IME convert + /// + CONVERT = 0x1C, + /// + ///IME nonconvert + /// + NONCONVERT = 0x1D, + /// + ///IME accept + /// + ACCEPT = 0x1E, + /// + ///IME mode change request + /// + MODECHANGE = 0x1F, + /// + ///SPACEBAR + /// + SPACE = 0x20, + /// + ///PAGE UP key + /// + PRIOR = 0x21, + /// + ///PAGE DOWN key + /// + NEXT = 0x22, + /// + ///END key + /// + END = 0x23, + /// + ///HOME key + /// + HOME = 0x24, + /// + ///LEFT ARROW key + /// + LEFT = 0x25, + /// + ///UP ARROW key + /// + UP = 0x26, + /// + ///RIGHT ARROW key + /// + RIGHT = 0x27, + /// + ///DOWN ARROW key + /// + DOWN = 0x28, + /// + ///SELECT key + /// + SELECT = 0x29, + /// + ///PRINT key + /// + PRINT = 0x2A, + /// + ///EXECUTE key + /// + EXECUTE = 0x2B, + /// + ///PRINT SCREEN key + /// + SNAPSHOT = 0x2C, + /// + ///INS key + /// + INSERT = 0x2D, + /// + ///DEL key + /// + DELETE = 0x2E, + /// + ///HELP key + /// + HELP = 0x2F, + /// + ///0 key + /// + KEY_0 = 0x30, + /// + ///1 key + /// + KEY_1 = 0x31, + /// + ///2 key + /// + KEY_2 = 0x32, + /// + ///3 key + /// + KEY_3 = 0x33, + /// + ///4 key + /// + KEY_4 = 0x34, + /// + ///5 key + /// + KEY_5 = 0x35, + /// + ///6 key + /// + KEY_6 = 0x36, + /// + ///7 key + /// + KEY_7 = 0x37, + /// + ///8 key + /// + KEY_8 = 0x38, + /// + ///9 key + /// + KEY_9 = 0x39, + /// + ///A key + /// + KEY_A = 0x41, + /// + ///B key + /// + KEY_B = 0x42, + /// + ///C key + /// + KEY_C = 0x43, + /// + ///D key + /// + KEY_D = 0x44, + /// + ///E key + /// + KEY_E = 0x45, + /// + ///F key + /// + KEY_F = 0x46, + /// + ///G key + /// + KEY_G = 0x47, + /// + ///H key + /// + KEY_H = 0x48, + /// + ///I key + /// + KEY_I = 0x49, + /// + ///J key + /// + KEY_J = 0x4A, + /// + ///K key + /// + KEY_K = 0x4B, + /// + ///L key + /// + KEY_L = 0x4C, + /// + ///M key + /// + KEY_M = 0x4D, + /// + ///N key + /// + KEY_N = 0x4E, + /// + ///O key + /// + KEY_O = 0x4F, + /// + ///P key + /// + KEY_P = 0x50, + /// + ///Q key + /// + KEY_Q = 0x51, + /// + ///R key + /// + KEY_R = 0x52, + /// + ///S key + /// + KEY_S = 0x53, + /// + ///T key + /// + KEY_T = 0x54, + /// + ///U key + /// + KEY_U = 0x55, + /// + ///V key + /// + KEY_V = 0x56, + /// + ///W key + /// + KEY_W = 0x57, + /// + ///X key + /// + KEY_X = 0x58, + /// + ///Y key + /// + KEY_Y = 0x59, + /// + ///Z key + /// + KEY_Z = 0x5A, + /// + ///Left Windows key (Microsoft Natural keyboard) + /// + LWIN = 0x5B, + /// + ///Right Windows key (Natural keyboard) + /// + RWIN = 0x5C, + /// + ///Applications key (Natural keyboard) + /// + APPS = 0x5D, + /// + ///Computer Sleep key + /// + SLEEP = 0x5F, + /// + ///Numeric keypad 0 key + /// + NUMPAD0 = 0x60, + /// + ///Numeric keypad 1 key + /// + NUMPAD1 = 0x61, + /// + ///Numeric keypad 2 key + /// + NUMPAD2 = 0x62, + /// + ///Numeric keypad 3 key + /// + NUMPAD3 = 0x63, + /// + ///Numeric keypad 4 key + /// + NUMPAD4 = 0x64, + /// + ///Numeric keypad 5 key + /// + NUMPAD5 = 0x65, + /// + ///Numeric keypad 6 key + /// + NUMPAD6 = 0x66, + /// + ///Numeric keypad 7 key + /// + NUMPAD7 = 0x67, + /// + ///Numeric keypad 8 key + /// + NUMPAD8 = 0x68, + /// + ///Numeric keypad 9 key + /// + NUMPAD9 = 0x69, + /// + ///Multiply key + /// + MULTIPLY = 0x6A, + /// + ///Add key + /// + ADD = 0x6B, + /// + ///Separator key + /// + SEPARATOR = 0x6C, + /// + ///Subtract key + /// + SUBTRACT = 0x6D, + /// + ///Decimal key + /// + DECIMAL = 0x6E, + /// + ///Divide key + /// + DIVIDE = 0x6F, + /// + ///F1 key + /// + F1 = 0x70, + /// + ///F2 key + /// + F2 = 0x71, + /// + ///F3 key + /// + F3 = 0x72, + /// + ///F4 key + /// + F4 = 0x73, + /// + ///F5 key + /// + F5 = 0x74, + /// + ///F6 key + /// + F6 = 0x75, + /// + ///F7 key + /// + F7 = 0x76, + /// + ///F8 key + /// + F8 = 0x77, + /// + ///F9 key + /// + F9 = 0x78, + /// + ///F10 key + /// + F10 = 0x79, + /// + ///F11 key + /// + F11 = 0x7A, + /// + ///F12 key + /// + F12 = 0x7B, + /// + ///F13 key + /// + F13 = 0x7C, + /// + ///F14 key + /// + F14 = 0x7D, + /// + ///F15 key + /// + F15 = 0x7E, + /// + ///F16 key + /// + F16 = 0x7F, + /// + ///F17 key + /// + F17 = 0x80, + /// + ///F18 key + /// + F18 = 0x81, + /// + ///F19 key + /// + F19 = 0x82, + /// + ///F20 key + /// + F20 = 0x83, + /// + ///F21 key + /// + F21 = 0x84, + /// + ///F22 key, (PPC only) Key used to lock device. + /// + F22 = 0x85, + /// + ///F23 key + /// + F23 = 0x86, + /// + ///F24 key + /// + F24 = 0x87, + /// + ///NUM LOCK key + /// + NUMLOCK = 0x90, + /// + ///SCROLL LOCK key + /// + SCROLL = 0x91, + /// + ///Left SHIFT key + /// + LSHIFT = 0xA0, + /// + ///Right SHIFT key + /// + RSHIFT = 0xA1, + /// + ///Left CONTROL key + /// + LCONTROL = 0xA2, + /// + ///Right CONTROL key + /// + RCONTROL = 0xA3, + /// + ///Left MENU key + /// + LMENU = 0xA4, + /// + ///Right MENU key + /// + RMENU = 0xA5, + /// + ///Windows 2000/XP: Browser Back key + /// + BROWSER_BACK = 0xA6, + /// + ///Windows 2000/XP: Browser Forward key + /// + BROWSER_FORWARD = 0xA7, + /// + ///Windows 2000/XP: Browser Refresh key + /// + BROWSER_REFRESH = 0xA8, + /// + ///Windows 2000/XP: Browser Stop key + /// + BROWSER_STOP = 0xA9, + /// + ///Windows 2000/XP: Browser Search key + /// + BROWSER_SEARCH = 0xAA, + /// + ///Windows 2000/XP: Browser Favorites key + /// + BROWSER_FAVORITES = 0xAB, + /// + ///Windows 2000/XP: Browser Start and Home key + /// + BROWSER_HOME = 0xAC, + /// + ///Windows 2000/XP: Volume Mute key + /// + VOLUME_MUTE = 0xAD, + /// + ///Windows 2000/XP: Volume Down key + /// + VOLUME_DOWN = 0xAE, + /// + ///Windows 2000/XP: Volume Up key + /// + VOLUME_UP = 0xAF, + /// + ///Windows 2000/XP: Next Track key + /// + MEDIA_NEXT_TRACK = 0xB0, + /// + ///Windows 2000/XP: Previous Track key + /// + MEDIA_PREV_TRACK = 0xB1, + /// + ///Windows 2000/XP: Stop Media key + /// + MEDIA_STOP = 0xB2, + /// + ///Windows 2000/XP: Play/Pause Media key + /// + MEDIA_PLAY_PAUSE = 0xB3, + /// + ///Windows 2000/XP: Start Mail key + /// + LAUNCH_MAIL = 0xB4, + /// + ///Windows 2000/XP: Select Media key + /// + LAUNCH_MEDIA_SELECT = 0xB5, + /// + ///Windows 2000/XP: Start Application 1 key + /// + LAUNCH_APP1 = 0xB6, + /// + ///Windows 2000/XP: Start Application 2 key + /// + LAUNCH_APP2 = 0xB7, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_1 = 0xBA, + /// + ///Windows 2000/XP: For any country/region, the '+' key + /// + OEM_PLUS = 0xBB, + /// + ///Windows 2000/XP: For any country/region, the ',' key + /// + OEM_COMMA = 0xBC, + /// + ///Windows 2000/XP: For any country/region, the '-' key + /// + OEM_MINUS = 0xBD, + /// + ///Windows 2000/XP: For any country/region, the '.' key + /// + OEM_PERIOD = 0xBE, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_2 = 0xBF, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_3 = 0xC0, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_4 = 0xDB, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_5 = 0xDC, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_6 = 0xDD, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_7 = 0xDE, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_8 = 0xDF, + /// + ///Windows 2000/XP: Either the angle bracket key or the backslash key on the RT 102-key keyboard + /// + OEM_102 = 0xE2, + /// + ///Windows 95/98/Me, Windows NT 4.0, Windows 2000/XP: IME PROCESS key + /// + PROCESSKEY = 0xE5, + /// + ///Windows 2000/XP: Used to pass Unicode characters as if they were keystrokes. + ///The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, + ///see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP + /// + PACKET = 0xE7, + /// + ///Attn key + /// + ATTN = 0xF6, + /// + ///CrSel key + /// + CRSEL = 0xF7, + /// + ///ExSel key + /// + EXSEL = 0xF8, + /// + ///Erase EOF key + /// + EREOF = 0xF9, + /// + ///Play key + /// + PLAY = 0xFA, + /// + ///Zoom key + /// + ZOOM = 0xFB, + /// + ///Reserved + /// + NONAME = 0xFC, + /// + ///PA1 key + /// + PA1 = 0xFD, + /// + ///Clear key + /// + OEM_CLEAR = 0xFE + }; + + [StructLayout(LayoutKind.Sequential)] + public struct MOUSEINPUT + { + public int dx; // 0 - 65535 + public int dy; // 0 - 65535 + public int mouseData; // if dwFlags = MOUSEEVENTF_WHEEL or MOUSE_EVENTF_HWHEEL, then mouseData specifies the amount of wheel movement. +/- multiples of WHEEL_DELTA which is 120. + public MouseFlags dwFlags; + public uint time; // Time stamp for the event, in milliseconds. If this parameter is 0, the system will provide its own time stamp. + public IntPtr dwExtraInfo; // Specifies an additional value associated with the mouse event. An application calls GetMessageExtraInfo to obtain this extra information. + + public MOUSEINPUT(MouseFlags flags) + { + dx = 0; + dy = 0; + mouseData = 0; + time = 0; + dwExtraInfo = GetMessageExtraInfo(); + dwFlags = flags; + } + + public MOUSEINPUT(int dx, int dy, MouseFlags flags) + { + this.dx = dx; + this.dy = dy; + mouseData = 0; + time = 0; + dwExtraInfo = GetMessageExtraInfo(); + dwFlags = flags; + } + + public MOUSEINPUT(int mouseScroll) + { + this.dx = 0; + this.dy = 0; + mouseData = 120 * mouseScroll; // WHEEL_DELTA = 120 + time = 0; + dwExtraInfo = GetMessageExtraInfo(); + dwFlags = MouseFlags.Wheel; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct KEYBDINPUT + { + public VK wVk; + public ushort wScan; + public uint dwFlags; + public uint time; + public IntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + public struct HARDWAREINPUT + { + public uint uMsg; + public ushort wParamL; + public ushort wParamH; + } + + [StructLayout(LayoutKind.Explicit)] + public struct INPUT + { +#if TARGET_OS_X64 + // this is needed for SendInput() to work in 64-bit Operating System + [FieldOffset(0)] + public InputType type; // INPUT_MOUSE, INPUT_KEYBOARD, INPUT_HARDWARE + [FieldOffset(8)] + public MOUSEINPUT mi; + [FieldOffset(8)] + public KEYBDINPUT ki; + [FieldOffset(8)] + public HARDWAREINPUT hi; +#else + // this is needed for SendInput() to work in 32-bit Operating System + [FieldOffset(0)] + public InputType type; // INPUT_MOUSE, INPUT_KEYBOARD, INPUT_HARDWARE + [FieldOffset(4)] + public MOUSEINPUT mi; + [FieldOffset(4)] + public KEYBDINPUT ki; + [FieldOffset(4)] + public HARDWAREINPUT hi; +#endif + + public INPUT(MOUSEINPUT mi) + { + this.type = InputType.Mouse; + ki = new KEYBDINPUT(); + hi = new HARDWAREINPUT(); + this.mi = mi; + } + + public INPUT(KEYBDINPUT ki) + { + this.type = InputType.Keyboard; + mi = new MOUSEINPUT(); + hi = new HARDWAREINPUT(); + this.ki = ki; + } + + public INPUT(HARDWAREINPUT hi) + { + this.type = InputType.Hardware; + mi = new MOUSEINPUT(); + ki = new KEYBDINPUT(); + this.hi = hi; + } + } + + // this function takes a string of unicode characters and outputs it to another application + // simulate the typing of a string of character to any application that is in focus + public static void SendTextToApplication(string text) + { + int count = 1; + INPUT[] inputs = new INPUT[1]; + inputs[0].type = (InputType)INPUT_KEYBOARD; + inputs[0].ki.dwFlags = KEYEVENTF_UNICODE; + + foreach (char c in text) + { + inputs[0].ki.wScan = (ushort)c; + SendInput(1, inputs, Marshal.SizeOf(inputs[0])); + + // SendInput has problems sending the same character more than once in a row + // put in a wait in order for all characters to be sent correctly + if (count < text.Length && text[count] == c) + Thread.Sleep(5); + else if (count % 10 == 0) + Thread.Sleep(10); + + count++; + } + } + + public static void Simulate_2_Key_Press(VK virtualKey, char key) + { + keybd_event((byte)virtualKey, 0, KEYEVENTF_EXTENDEDKEY, 0); + Thread.Sleep(50); + keybd_event(Convert.ToByte(key), 0, KEYEVENTF_EXTENDEDKEY, 0); + Thread.Sleep(50); + keybd_event(Convert.ToByte(key), 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); + Thread.Sleep(50); + keybd_event((byte)virtualKey, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); + } + + // ***************************** Start Get Windows Placement ************************************// + // Get Windows Placement: Is it Minimized, Maximized, Normal + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); + + public struct WINDOWPLACEMENT + { + public int length; + public int flags; + public int showCmd; + public System.Drawing.Point ptMinPosition; + public System.Drawing.Point ptMaxPosition; + public System.Drawing.Rectangle rcNormalPosition; + } + // ***************************** End Get Windows Placement ************************************// + + // ***************************** Start Get Windows Rectangle ************************************// + // Get top, left, bottom, right coordinates of a window + [DllImport("user32.dll")] + public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect); + + [StructLayout(LayoutKind.Sequential)] + public struct Rect + { + public int left; + public int top; + public int right; + public int bottom; + } + // ***************************** End Get Windows Rectangle ************************************// + + // ***************************** Start Get Pixel Color ************************************// + // Get pixel color at a x,y coordinate on screen + + [DllImport("user32.dll")] + static extern IntPtr GetDC(IntPtr hwnd); + + [DllImport("user32.dll")] + static extern Int32 ReleaseDC(IntPtr hwnd, IntPtr hdc); + + [DllImport("gdi32.dll")] + static extern uint GetPixel(IntPtr hdc, int nXPos, int nYPos); + + static public System.Drawing.Color GetPixelColor(int x, int y) + { + IntPtr hdc = GetDC(IntPtr.Zero); + uint pixel = GetPixel(hdc, x, y); + ReleaseDC(IntPtr.Zero, hdc); + Color color = Color.FromArgb((int)(pixel & 0x000000FF), + (int)(pixel & 0x0000FF00) >> 8, + (int)(pixel & 0x00FF0000) >> 16); + return color; + } + // ***************************** End Get Pixel Color ************************************// + + // ***************************** Start Set Window to ForeGround ************************************// + [DllImport("user32.dll")] + public static extern bool SetForegroundWindow(IntPtr hWnd); + // ***************************** End Set Window to ForeGround ************************************// + + // ***************************** Start Perform Mouse click ************************************// + [DllImport("user32.dll", CharSet = CharSet.Auto)] + public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo); + + public static void LeftMouseClick(Point position) + { + Cursor.Position = new Point(position.X, position.Y); + mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, (uint)position.X, (uint)position.Y, 0, 0); + } + // ***************************** End Perform Mouse click ************************************// + + [DllImport("user32.dll", SetLastError = true)] + public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); + + /// Enumeration of the different ways of showing a window using + /// ShowWindow + public enum WindowShowStyle : uint + { + /// Hides the window and activates another window. + /// See SW_HIDE + Hide = 0, + /// Activates and displays a window. If the window is minimized + /// or maximized, the system restores it to its original size and + /// position. An application should specify this flag when displaying + /// the window for the first time. + /// See SW_SHOWNORMAL + ShowNormal = 1, + /// Activates the window and displays it as a minimized window. + /// See SW_SHOWMINIMIZED + ShowMinimized = 2, + /// Activates the window and displays it as a maximized window. + /// See SW_SHOWMAXIMIZED + ShowMaximized = 3, + /// Maximizes the specified window. + /// See SW_MAXIMIZE + Maximize = 3, + /// Displays a window in its most recent size and position. + /// This value is similar to "ShowNormal", except the window is not + /// actived. + /// See SW_SHOWNOACTIVATE + ShowNormalNoActivate = 4, + /// Activates the window and displays it in its current size + /// and position. + /// See SW_SHOW + Show = 5, + /// Minimizes the specified window and activates the next + /// top-level window in the Z order. + /// See SW_MINIMIZE + Minimize = 6, + /// Displays the window as a minimized window. This value is + /// similar to "ShowMinimized", except the window is not activated. + /// See SW_SHOWMINNOACTIVE + ShowMinNoActivate = 7, + /// Displays the window in its current size and position. This + /// value is similar to "Show", except the window is not activated. + /// See SW_SHOWNA + ShowNoActivate = 8, + /// Activates and displays the window. If the window is + /// minimized or maximized, the system restores it to its original size + /// and position. An application should specify this flag when restoring + /// a minimized window. + /// See SW_RESTORE + Restore = 9, + /// Sets the show state based on the SW_ value specified in the + /// STARTUPINFO structure passed to the CreateProcess function by the + /// program that started the application. + /// See SW_SHOWDEFAULT + ShowDefault = 10, + /// Windows 2000/XP: Minimizes a window, even if the thread + /// that owns the window is hung. This flag should only be used when + /// minimizing windows from a different thread. + /// See SW_FORCEMINIMIZE + ForceMinimized = 11 + } + + [DllImport("user32.dll")] + public static extern bool ShowWindow(IntPtr hWnd, WindowShowStyle nCmdShow); + + public delegate bool EnumDelegate(IntPtr hWnd, int lParam); + + [DllImport("user32.dll", EntryPoint = "EnumDesktopWindows", ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool EnumDesktopWindows(IntPtr hDesktop, EnumDelegate lpEnumCallbackFunction, IntPtr lParam); + + [DllImport("user32.dll", EntryPoint = "GetWindowText", ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)] + public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpWindowText, int nMaxCount); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsWindowVisible(IntPtr hWnd); + + // set a new window style + public const int GWL_STYLE = -16; + // maximize, minimize and close buttons + public const int WS_SYSMENU = 0x80000; + [DllImport("user32.dll", SetLastError = true)] + public static extern int GetWindowLong(IntPtr hWnd, int nIndex); + [DllImport("user32.dll")] + public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + public static void WpfHideAllButtonsOnTitleBar(System.Windows.Window window) + { + var hwnd = new WindowInteropHelper(window).Handle; + SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU); + } + + public static void WpfShowAllButtonsOnTitleBar(System.Windows.Window window) + { + var hwnd = new WindowInteropHelper(window).Handle; + SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_SYSMENU); + } + } +} diff --git a/CommonLib/Library/Windows/Forms/FormControlsFormatting.cs b/CommonLib/Library/Windows/Forms/FormControlsFormatting.cs new file mode 100644 index 0000000..030b495 --- /dev/null +++ b/CommonLib/Library/Windows/Forms/FormControlsFormatting.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Runtime.InteropServices; +using System.Drawing; + + +namespace CommonLib.Windows.Forms +{ + ///========================================================================== + /// TextFormatting + ///========================================================================== + /// + /// Primary purpose is to define the format of text such as text font and color + /// mainly for display in rich text box + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public class TextFormat + { + // this struct define the text formatting of a selected number of words in a string + // must define word index and word count, word index starts at 0 + public struct TextFontAndColor + { + public int wordIndex; + public int wordCount; + public Font textFont; + public Color textColor; + + public TextFontAndColor(Font fontProp, Color fontColor, int textWordIndex = -1, int textWordCount = -1) + { + wordIndex = textWordIndex; + wordCount = textWordCount; + textFont = fontProp; + textColor = fontColor; + } + + public TextFontAndColor(string nothing) + { + wordIndex = -1; + wordCount = -1; + textFont = new Font("Microsoft Sans Serif", 9, FontStyle.Regular); + textColor = Color.Black; + } + } + } + + ///========================================================================== + /// FormatRichTextBox + ///========================================================================== + /// + /// Format rich text box + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public class FormatRichTextBox + { + [DllImport("user32", CharSet = CharSet.Auto)] + private static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, ref PARAFORMAT lParam); + + const int PFM_SPACEBEFORE = 0x00000040; + const int PFM_SPACEAFTER = 0x00000080; + const int PFM_LINESPACING = 0x00000100; + + const int SCF_SELECTION = 1; + const int EM_SETPARAFORMAT = 1095; + + ///========================================================================== + /// FormatRichTextBox.setLineFormat + ///========================================================================== + /// + /// Allow the setting of line spacing in RichTextBox + /// + /// For rule, there are only 6 possible values, 0-5 + /// 0 - Single Spacing, line spacing is ignored + /// 1 - One-and-a-half spacing. line spacing is ignored + /// 2 - Double spacing. line spacing is ignored + /// 3 - Line spacing specifies spacing from one line to the next + /// 4 - Line spacing specifies spacing from one line to the next + /// 5 - Line spacing/20 is the spacing from one line to the next + /// + /// rich text box control + /// can only be 0-5 (the summary describes each rule) + /// amount of spacing between 2 lines + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public void setLineFormat(RichTextBox rTextbox, byte rule, int space) + { + PARAFORMAT fmt = new PARAFORMAT(); + fmt.cbSize = Marshal.SizeOf(fmt); + fmt.dwMask = PFM_LINESPACING; + fmt.dyLineSpacing = space; + fmt.bLineSpacingRule = rule; + rTextbox.SelectAll(); + SendMessage(new HandleRef(rTextbox, rTextbox.Handle), EM_SETPARAFORMAT, SCF_SELECTION, ref fmt); + } + + [StructLayout(LayoutKind.Sequential)] + public struct PARAFORMAT + { + public int cbSize; + public uint dwMask; + public short wNumbering; + public short wReserved; + public int dxStartIndent; + public int dxRightIndent; + public int dxOffset; + public short wAlignment; + public short cTabCount; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public int[] rgxTabs; + // PARAFORMAT2 from here onwards + public int dySpaceBefore; + public int dySpaceAfter; + public int dyLineSpacing; + public short sStyle; + public byte bLineSpacingRule; + public byte bOutlineLevel; + public short wShadingWeight; + public short wShadingStyle; + public short wNumberingStart; + public short wNumberingStyle; + public short wNumberingTab; + public short wBorderSpace; + public short wBorderWidth; + public short wBorders; + } + } +} diff --git a/CommonLib/Library/Windows/Forms/FormControlsManipThreadSafe.cs b/CommonLib/Library/Windows/Forms/FormControlsManipThreadSafe.cs new file mode 100644 index 0000000..1ebbf7e --- /dev/null +++ b/CommonLib/Library/Windows/Forms/FormControlsManipThreadSafe.cs @@ -0,0 +1,741 @@ +///====================================================================================== +/// File: FormControlsManipThreadSafe.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// Provide a mechanism so that the main thread(main form) or its backgroundworker can +/// manipulate controls in a container +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le +///====================================================================================== +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Threading; +using System.Text.RegularExpressions; + +namespace CommonLib.Windows.Forms +{ + ///========================================================================== + /// FormControlsManipThreadSafe + ///========================================================================== + /// + /// This class allows backgroundworker(child threads) to manipulate controls + /// in a container + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public class FormControlsManipThreadSafe + { + public enum ControlVisibility { Display, Hidden }; + + ///========================================================================== + /// FormControlsManipThreadSafe.AddControlToContainer + ///========================================================================== + /// + /// Add a control to a container(form, panel, etc) + /// + /// any form control + /// a container control (form, panel, groupbox, + /// tabcontrol, etc) + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public void AddControlToContainer(Control control, Control container) + { + // when a different thread trying to access the value this control on a form that was created by another thread + if (container.InvokeRequired) + { + container.Invoke(new Action(() => AddControlToContainer(control, container))); + } + // when the thread tries to access the value of the control on the form that belongs to the same thread + else + { + container.Controls.Add(control); + } + } + + ///========================================================================== + /// FormControlsManipThreadSafe.ModifyLabelToDisplayWithinContainerVisibleWidth + ///========================================================================== + /// + /// If the text in a label is too long and it goes past the boundary of the + /// container, want to shorten it so that it ends at the boundary of the + /// container. 3 periods will be inserted in the middle of the text of the + /// label to indicate that text were cut off in the middle + /// + /// a label or linklabel control + /// the width of the container in which + /// the control is located + /// the right margin of the control's text with + /// respect to the container in which it is located + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public void ModifyLabelToDisplayWithinContainerVisibleWidth(Control control, int widthOfContainingPanel, int d_rightMargin) + { + int minimumTextLength = 10, minimumWidthOfContainingPanel = 150; + int rightMargin = 2; + string connector = "..."; + bool controlTextOverrunsVisibleLine = false; + + // when a different thread trying to access the value this control on a form that was created by another thread + if (control.InvokeRequired) + { + control.Invoke(new Action(() => ModifyLabelToDisplayWithinContainerVisibleWidth(control, widthOfContainingPanel, d_rightMargin))); + } + else + { + // only perform this for LinkLabel and Label types + if (control.GetType() != typeof(LinkLabel) && control.GetType() != typeof(Label)) + return; + + if (control.Text.Length < minimumTextLength || widthOfContainingPanel < minimumWidthOfContainingPanel) + return; + + if (d_rightMargin > rightMargin) + rightMargin = d_rightMargin; + + string text = control.Text; + Size textsize = TextRenderer.MeasureText(text, control.Font); + int maxTextLength = widthOfContainingPanel - control.Left - rightMargin; + + string left = text.Substring(0, (int)Math.Floor(text.Length / 2.0)); + string right = text.Substring(left.Length, (int)Math.Ceiling(text.Length / 2.0)); + + while (textsize.Width > maxTextLength) + { + if (!controlTextOverrunsVisibleLine) + controlTextOverrunsVisibleLine = true; + + if (left.Length >= right.Length) + left = left.Substring(0, left.Length - 1); + else + right = right.Substring(1, right.Length - 1); + + string newText = left + connector + right; + + textsize = TextRenderer.MeasureText(newText, control.Font); + } + + if (controlTextOverrunsVisibleLine) + control.Text = left + connector + right; + } + } + + ///========================================================================== + /// FormControlsManipThreadSafe.RemoveControlFromContainer + ///========================================================================== + /// + /// Remove a control from a container(form, panel, etc) + /// + /// any form control + /// a container control (form, panel, groupbox, + /// tabcontrol, etc) + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public void RemoveControlFromContainer(Control control, Control container) + { + // when a different thread trying to access the value this control on a form that was created by another thread + if (container.InvokeRequired) + { + container.Invoke(new Action(() => RemoveControlFromContainer(control, container))); + } + // when the thread tries to access the value of the control on the form that belongs to the same thread + else + { + container.Controls.Remove(control); + } + } + + ///========================================================================== + /// FormControlsManipThreadSafe.BaseClassObjectIsOfASpecificType + ///========================================================================== + /// + /// Given any class object, usually of type base class, determine if the object + /// is of a specific type + /// + /// an object + /// a type + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public bool BaseClassObjectIsOfASpecificType(object obj, Type objectSpecificType) + { + if (obj.GetType().IsAssignableFrom(objectSpecificType)) + return true; + else + return false; + } + + ///========================================================================== + /// FormControlsManipThreadSafe.RemoveControlFromContainer + ///========================================================================== + /// + /// Resize a container (form, panel, groupbox, etc) + /// + /// a container control (form, panel, groupbox, + /// tabcontrol, etc) + /// width to resize to + /// height to resize to + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public void ResizeContainer(Control container, int resizeWidth, int resizeHeight) + { + // when a different thread trying to access the value this control on a form that was created by another thread + if (container.InvokeRequired) + { + container.Invoke(new Action(() => ResizeContainer(container, resizeWidth, resizeHeight))); + } + // when the thread tries to access the value of the control on the form that belongs to the same thread + else + { + container.Width = resizeWidth; + container.Height = resizeHeight; + } + } + + ///========================================================================== + /// FormControlsManipThreadSafe.PositionControl + ///========================================================================== + /// + /// Position a control within a container (form, panel, groupbox, etc) + /// + /// any form control + /// a Point object with X and Y position defined + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public void PositionControl(Control control, Point pos) + { + // when a different thread trying to access the value this control on a form that was created by another thread + if (control.InvokeRequired) + { + control.Invoke(new Action(() => PositionControl(control, pos))); + } + // when the thread tries to access the value of the control on the form that belongs to the same thread + else + { + control.Location = new System.Drawing.Point(pos.X, pos.Y); + } + } + + ///========================================================================== + /// FormControlsManipThreadSafe.SetControlVisibility + ///========================================================================== + /// + /// Hide or Show a control on in the container + /// + /// any form control + /// Enum type + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public void SetControlVisibility(Control control, ControlVisibility visibility) + { + // when a different thread trying to access the value this control on a form that was created by another thread + if (control.InvokeRequired) + { + control.Invoke(new Action(() => SetControlVisibility(control, visibility))); + } + // when the thread tries to access the value of the control on the form that belongs to the same thread + else + { + if (visibility == ControlVisibility.Display) + control.Visible = true; + else + control.Visible = false; + } + } + + ///========================================================================== + /// FormControlsManipThreadSafe.ModifyControlText + ///========================================================================== + /// + /// Modify the text of a control + /// + /// any form control + /// a string + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public void ModifyControlText(Control control, string text) + { + // when a different thread trying to access the value this control on a form that was created by another thread + if (control.InvokeRequired) + { + control.Invoke(new Action(() => ModifyControlText(control, text))); + } + // when the thread tries to access the value of the control on the form that belongs to the same thread + else + { + if (BaseClassObjectIsOfASpecificType(control, typeof(TextBox)) + || + BaseClassObjectIsOfASpecificType(control, typeof(Button))) + control.Text = text; + else if (BaseClassObjectIsOfASpecificType(control, typeof(RichTextBox))) + { + RichTextBox rTextBox = control as RichTextBox; + rTextBox.AppendText(text); + } + } + } + + ///========================================================================== + /// FormControlsManipThreadSafe.RichTextBoxEraseLine + ///========================================================================== + /// + /// Erase a paritcular line in Rich Text Box with the option to delete the + /// line completely. + /// Erase is just erasing all the characters on a line, but the line still + /// exists. If DeleteLine option is true, the whole line will be removed + /// + /// rich text box control + /// line index, starting at index 0 + /// /// deleteLine + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public void RichTextBoxEraseLine(RichTextBox control, int lineIndex, bool deleteLine) + { + // when a different thread trying to access the value this control on a form that was created by another thread + if (control.InvokeRequired) + { + control.Invoke(new Action(() => RichTextBoxEraseLine(control, lineIndex, deleteLine))); + } + // when the thread tries to access the value of the control on the form that belongs to the same thread + else + { + int LineCharStartIndex; + int LineCharEndIndex; + int numCharsToBeRemove; + + if (lineIndex >= 0 && lineIndex < control.Lines.Count()) + { + LineCharStartIndex = control.GetFirstCharIndexFromLine(lineIndex); + + if (control.Lines.Count() > (lineIndex + 1)) + LineCharEndIndex = control.GetFirstCharIndexFromLine(lineIndex + 1) - 1; + else + LineCharEndIndex = control.TextLength; + + numCharsToBeRemove = LineCharEndIndex - LineCharStartIndex; + + if (numCharsToBeRemove > 0) + { + control.Select(LineCharStartIndex, numCharsToBeRemove); + control.SelectedText = ""; + + if (deleteLine) + { + if (lineIndex > 0 && control.Lines.Count() > 1) + { + // remove the line, this just means that we just shifted all the lines + // below this line up 1 line. The last line is empty + LineCharStartIndex = control.GetFirstCharIndexFromLine(lineIndex) - 1; + control.Select(LineCharStartIndex, 1); + control.SelectedText = ""; + } + else if (lineIndex == 0 && control.Lines.Count() > 1) + { + // remove the line, this just means that we just shfited all the lines + // below this line up 1 line. The last line is empty + LineCharStartIndex = control.GetFirstCharIndexFromLine(lineIndex); + control.Select(LineCharStartIndex, 1); + control.SelectedText = ""; + } + } + } + else if (lineIndex <= control.Lines.Count() - 1) // remove blank line + { + if (deleteLine) + { + if (lineIndex > 0) + { + control.Select(control.GetFirstCharIndexFromLine(lineIndex) - 1, 1); + } + else + { + control.Select(control.GetFirstCharIndexFromLine(lineIndex), 1); + } + control.SelectedText = ""; + } + } + } + } + } + + ///========================================================================== + /// FormControlsManipThreadSafe.ModifyRichTextBoxText + ///========================================================================== + /// + /// Modify the text of a rich text box in only 1 color + /// + /// rich text box control + /// a string + /// font properties + /// color property + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public void ModifyRichTextBoxText(RichTextBox control, string text, Font fontProp, Color textColor) + { + // when a different thread trying to access the value this control on a form that was created by another thread + if (control.InvokeRequired) + { + control.Invoke(new Action(() => ModifyRichTextBoxText(control, text, fontProp, textColor))); + } + // when the thread tries to access the value of the control on the form that belongs to the same thread + else + { + control.SelectionFont = fontProp; + control.SelectionColor = textColor; + control.AppendText(text); + } + } + + ///========================================================================== + /// FormControlsManipThreadSafe.ModifyRichTextBoxTextAtLine + ///========================================================================== + /// + /// Modify the text of a rich text box in only 1 color at a particular line + /// starting a particular character index on that line + /// + /// rich text box control + /// line index of the text box starting at 0 + /// number of characters that have already been processed + /// a string + /// font properties + /// color property + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public bool ModifyRichTextBoxTextAtLine(RichTextBox control, int lineIndex, int charsCount, string word, Font fontProp, Color textColor) + { + bool success = true; + + // when a different thread trying to access the value this control on a form that was created by another thread + if (control.InvokeRequired) + { + return (bool)control.Invoke(new Func( + delegate + { + return ModifyRichTextBoxTextAtLine(control, lineIndex, charsCount, word, fontProp, textColor); + } + )); + } + // when the thread tries to access the value of the control on the form that belongs to the same thread + else + { + if (lineIndex < control.Lines.Count()) + { + // determine the starting index of the new word + int startCharIndex = control.GetFirstCharIndexFromLine(lineIndex) + charsCount; + control.Select(startCharIndex, word.Length); + control.SelectionFont = fontProp; + control.SelectionColor = textColor; + control.SelectedText = control.SelectedText; + } + else + success = false; + + return success; + } + } + + ///========================================================================== + /// FormControlsManipThreadSafe.getRichTextBoxLastLineIndex + ///========================================================================== + /// + /// Modify text in rich text box in various colors and font at a particular + /// line + /// + /// rich text box control + /// line index of the text box starting at 0 + /// contains font properties for particular words + /// the default font for words that are not defined in textPropList + /// the default font color for words that are not defined in textPropList + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public int getRichTextBoxLastLineIndex(RichTextBox control) + { + // when a different thread trying to access the value this control on a form that was created by another thread + if (control.InvokeRequired) + { + return (int)control.Invoke(new Func( + delegate + { + return getRichTextBoxLastLineIndex(control); + } + )); + } + // when the thread tries to access the value of the control on the form that belongs to the same thread + else + { + return (control.Lines.Count() - 1); + } + } + + ///========================================================================== + /// FormControlsManipThreadSafe.ModifyRichTextBoxPerWordsAtLine + ///========================================================================== + /// + /// Modify text in rich text box in various colors and font at a particular + /// line + /// + /// rich text box control + /// line index of the text box starting at 0 + /// contains font properties for particular words + /// the default font for words that are not defined in textPropList + /// the default font color for words that are not defined in textPropList + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public bool ModifyRichTextBoxPerWordsAtLine(RichTextBox control, int lineIndex, string text, List textPropList, Font defaultFont, Color defaultFontColor) + { + int wordIndex = 0; + int wordCount = 0; + int textPropListIndex = -1; + Regex ItemRegex = new Regex(@"\s*[^ ]+", RegexOptions.Compiled); + bool success = true; + + // when a different thread trying to access the value this control on a form that was created by another thread + if (control.InvokeRequired) + { + return (bool)control.Invoke(new Func( + delegate + { + return ModifyRichTextBoxPerWordsAtLine(control, lineIndex, text, textPropList, defaultFont, defaultFontColor); + } + )); + } + else + { + if (lineIndex < control.Lines.Count()) + { + int startCharIndex = control.GetFirstCharIndexFromLine(lineIndex); + int endCharIndex = lineIndex < control.Lines.Count() - 1 ? + control.GetFirstCharIndexFromLine(lineIndex + 1) - 1 : + control.Text.Length; + // select the whole line + control.Select(startCharIndex, endCharIndex - startCharIndex); + control.SelectionFont = defaultFont; + control.SelectionColor = defaultFontColor; + // replace line with new text + control.SelectedText = text; + + // number of characters in all the words that have been processed + int charsProcessed = 0; + + foreach (Match ItemMatch in ItemRegex.Matches(text)) + { + if (wordCount == 0) + textPropListIndex = getTextPropListIndexForWordIndex(textPropList, wordIndex); + + if ((textPropListIndex >= 0 && wordIndex == textPropList[textPropListIndex].wordIndex) || wordCount > 0) + { + if (wordCount == 0) + wordCount = textPropList[textPropListIndex].wordCount; + + FormControlsManipThreadSafe.ModifyRichTextBoxTextAtLine(control, lineIndex, charsProcessed, ItemMatch.ToString(), textPropList[textPropListIndex].textFont, textPropList[textPropListIndex].textColor); + + wordCount--; + + if (wordCount == 0) + { + textPropList.RemoveAt(textPropListIndex); + } + } + else + { + FormControlsManipThreadSafe.ModifyRichTextBoxTextAtLine(control, lineIndex, charsProcessed, ItemMatch.ToString(), defaultFont, defaultFontColor); + + } + + wordIndex++; + charsProcessed += ItemMatch.ToString().Length; + } + + if (textPropList != null) + textPropList.Clear(); + + Match regExMatch; + regExMatch = Regex.Match(text, @"[^ ]?( +)$"); + + // if there are spaces at the end of text, want to display it + if (regExMatch.Success) + { + FormControlsManipThreadSafe.ModifyRichTextBoxTextAtLine(control, lineIndex, charsProcessed, regExMatch.Groups[1].Value, defaultFont, defaultFontColor); + } + } + else + success = false; + + return success; + } + } + + ///========================================================================== + /// FormControlsManipThreadSafe.ModifyRichTextBoxPerWords + ///========================================================================== + /// + /// Modify text in rich text box in various colors and font + /// + /// rich text box control + /// contains font properties for particular words + /// the default font for words that are not defined in textPropList + /// the default font color for words that are not defined in textPropList + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public void ModifyRichTextBoxPerWords(RichTextBox control, string text, List textPropList, Font defaultFont, Color defaultFontColor) + { + int wordIndex = 0; + int wordCount = 0; + int textPropListIndex = -1; + Regex ItemRegex = new Regex(@"\s*[^ ]+", RegexOptions.Compiled); + + foreach (Match ItemMatch in ItemRegex.Matches(text)) + { + if (wordCount == 0) + textPropListIndex = getTextPropListIndexForWordIndex(textPropList, wordIndex); + + if ((textPropListIndex >= 0 && wordIndex == textPropList[textPropListIndex].wordIndex) || wordCount > 0) + { + if (wordCount == 0) + wordCount = textPropList[textPropListIndex].wordCount; + + FormControlsManipThreadSafe.ModifyRichTextBoxText(control, ItemMatch.ToString(), textPropList[textPropListIndex].textFont, textPropList[textPropListIndex].textColor); + + wordCount--; + + if (wordCount == 0) + { + textPropList.RemoveAt(textPropListIndex); + } + } + else + { + FormControlsManipThreadSafe.ModifyRichTextBoxText(control, ItemMatch.ToString(), defaultFont, defaultFontColor); + } + + wordIndex++; + } + + if ( textPropList != null ) + textPropList.Clear(); + + Match regExMatch; + regExMatch = Regex.Match(text, @"[^ ]?( +)$"); + + // if there are spaces at the end of text, want to display it + if (regExMatch.Success) + { + FormControlsManipThreadSafe.ModifyControlText(control, regExMatch.Groups[1].Value); + } + } + + ///========================================================================== + /// FormControlsManipThreadSafe.getTextPropListIndexForWordIndex + ///========================================================================== + /// + /// textPropList contains a list of font properties of various words in a string + /// want to find the font property of a word or words starting at a particular word index + /// in the string + /// + /// contains font properties for particular words + /// index of the word in a string + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static private int getTextPropListIndexForWordIndex(List textPropList, int wordIndex) + { + int index = -1; + + if (textPropList != null) + { + for (int i = 0; i < textPropList.Count; i++) + { + if (textPropList[i].wordIndex == wordIndex) + index = i; + } + } + + return index; + } + + ///========================================================================== + /// FormControlsManipThreadSafe.ModifyControlColors + ///========================================================================== + /// + /// Modify back color and fore color of a control + /// + /// any form control + /// a string + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 06/06/12 D.Le + ///========================================================================== + static public void ModifyControlColors(Control control, Color backColor, Color foreColor) + { + // when a different thread trying to access the value this control on a form that was created by another thread + if (control.InvokeRequired) + { + control.Invoke(new Action(() => ModifyControlColors(control, backColor, foreColor))); + } + // when the thread tries to access the value of the control on the form that belongs to the same thread + else + { + if (backColor != null) + control.BackColor = backColor; + + if (foreColor != null) + control.ForeColor = foreColor; + } + } + } +} diff --git a/CommonLib/Library/Windows/Forms/MessageBoxCustom.cs b/CommonLib/Library/Windows/Forms/MessageBoxCustom.cs new file mode 100644 index 0000000..f955bfd --- /dev/null +++ b/CommonLib/Library/Windows/Forms/MessageBoxCustom.cs @@ -0,0 +1,190 @@ +///====================================================================================== +/// File: MessageBoxCustom.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// Provides an advanced message box with custom buttons and timeout +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le +///====================================================================================== +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Threading; + +namespace CommonLib.Windows.Forms +{ + ///========================================================================== + /// clsCustomButton + ///========================================================================== + /// + /// This class defines a custom message box with custom buttons and timeout + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public class MessageBoxCustom + { + public enum PopUpMsgType + { + // Summary: + // Indicates this is a error message to display with an OK button + ERROR = 0, + // + // Summary: + // Indicates this is just a info message with an OK button + INFO = 1, + + // Summary: + // Indicates this is a pre-defined question and needs answer from user, 2 buttons + YESNO = 2, + + // Summary: + // Indicates this is a pre-defined question and needs answer from user, 2 buttons + OKCANCEL = 3, + + // Summary: + // Indicates this is a custom question and needs answer from user, buttons must be defined also + CUSTOMQUESTION = 4, + } + + ///========================================================================== + /// MessageBoxCustom.Show + ///========================================================================== + /// + /// Displays a message box at the center of the calling parent form if it exists + /// + /// the parent form that calls this message box. + /// If parent form doesn't exist, pass in null pointer + /// + /// type of message (error,warning,info,custom,etc) + /// a list of custom buttons if the msgType + /// is custom, otherwise pass in null pointer + /// + /// The message to be display on the message box + /// The caption to be display at the top of + /// the message box + /// + /// Specify timeout in milliseconds before + /// the message box will be automatically be closed. If timeout is not desired, + /// specify 0 + /// + /// The value of the button that was pressed + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string Show(Form parentForm, PopUpMsgType msgType, List customButtons, string msg, string caption, int timeOut_ms) + { + string result; + MessageBoxEx msgBox; + + // create and customize an error message box + msgBox = MessageBoxExManager.CreateMessageBox(parentForm, "MsgBox"); + msgBox.Font = new Font("Tahoma", 10); + + switch (msgType) + { + case PopUpMsgType.ERROR: + msgBox.AddButtons(MessageBoxButtons.OK); + msgBox.Icon = (MessageBoxExIcon)MessageBoxIcon.Error; + //msgBox.SaveResponseText = "Don't ask me again"; + break; + case PopUpMsgType.INFO: + msgBox.AddButtons(MessageBoxButtons.OK); + msgBox.Icon = (MessageBoxExIcon)MessageBoxIcon.Information; + //msgBox.SaveResponseText = "Don't ask me again"; + break; + case PopUpMsgType.YESNO: + msgBox.AddButtons(MessageBoxButtons.YesNo); + msgBox.Icon = (MessageBoxExIcon)MessageBoxIcon.Question; + //msgBox.SaveResponseText = "Don't ask me again"; + break; + case PopUpMsgType.OKCANCEL: + msgBox.AddButtons(MessageBoxButtons.OKCancel); + msgBox.Icon = (MessageBoxExIcon)MessageBoxIcon.Information; + //msgBox.SaveResponseText = "Don't ask me again"; + break; + case PopUpMsgType.CUSTOMQUESTION: + if (customButtons != null && customButtons.Count > 1) + { + foreach (clsCustomButton button in customButtons) + { + msgBox.AddButton(button.buttonText, button.buttonValue); + } + msgBox.Icon = (MessageBoxExIcon)MessageBoxIcon.Question; + } + else + { + msgBox.AddButtons(MessageBoxButtons.OK); + msgBox.Icon = (MessageBoxExIcon)MessageBoxIcon.Error; + msg = "Custom question requires customized buttons with text and value for each button.\nPlease define the buttons and try again!"; + } + //msgBox.SaveResponseText = "Don't ask me again"; + break; + default: + break; + } + + msgBox.Caption = caption; + msgBox.Text = msg; + if (timeOut_ms > 0) + { + msgBox.Timeout = timeOut_ms; + msgBox.TimeoutResult = TimeoutResult.Default; + } + + result = msgBox.Show(); + + // if the message box was closed by a defined timeout + // must put in a delay so we can delete the messagebox instance + if (timeOut_ms > 0) + Thread.Sleep(500); + + MessageBoxExManager.DeleteMessageBox("MsgBox"); + + msgBox = null; + + return result; + } + + ///========================================================================== + /// clsCustomButton + ///========================================================================== + /// + /// This class defines a custom button with text and value + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public class clsCustomButton + { + public string buttonText; + public string buttonValue; + + public clsCustomButton() + { + buttonText = ""; + buttonValue = ""; + } + + public clsCustomButton(string d_buttonText, string d_buttonValue) + { + buttonText = d_buttonText; + buttonValue = d_buttonValue; + } + } + } +} diff --git a/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxEx.cs b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxEx.cs new file mode 100644 index 0000000..f6e50d4 --- /dev/null +++ b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxEx.cs @@ -0,0 +1,311 @@ +///====================================================================================== +/// File: MessageBoxEx.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// An extended MessageBox with lot of customizing capabilities +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le +///====================================================================================== +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace CommonLib.Windows.Forms +{ + /// + /// An extended MessageBox with lot of customizing capabilities. + /// + public class MessageBoxEx + { + #region Fields + private MessageBoxExForm _msgBox; + + private bool _useSavedResponse = true; + private string _name = null; + #endregion + + #region Properties + internal string Name + { + get{ return _name; } + set{ _name = value; } + } + + /// + /// Sets the caption of the message box + /// + public string Caption + { + set{_msgBox.Caption = value;} + } + + /// + /// Sets the text of the message box + /// + public string Text + { + set{_msgBox.Message = value;} + } + + /// + /// Sets the icon to show in the message box + /// + public Icon CustomIcon + { + set{_msgBox.CustomIcon = value;} + } + + /// + /// Sets the icon to show in the message box + /// + public MessageBoxExIcon Icon + { + set{ _msgBox.StandardIcon = (MessageBoxIcon)Enum.Parse(typeof(MessageBoxIcon), value.ToString());} + } + + /// + /// Sets the font for the text of the message box + /// + public Font Font + { + set{_msgBox.Font = value;} + } + + /// + /// Sets or Gets the ability of the user to save his/her response + /// + public bool AllowSaveResponse + { + get{ return _msgBox.AllowSaveResponse; } + set{ _msgBox.AllowSaveResponse = value; } + } + + /// + /// Sets the text to show to the user when saving his/her response + /// + public string SaveResponseText + { + set{_msgBox.SaveResponseText = value; } + } + + /// + /// Sets or Gets whether the saved response if available should be used + /// + public bool UseSavedResponse + { + get{ return _useSavedResponse; } + set{ _useSavedResponse = value; } + } + + /// + /// Sets or Gets whether an alert sound is played while showing the message box. + /// The sound played depends on the the Icon selected for the message box + /// + public bool PlayAlsertSound + { + get{ return _msgBox.PlayAlertSound; } + set{ _msgBox.PlayAlertSound = value; } + } + + /// + /// Sets or Gets the time in milliseconds for which the message box is displayed. + /// + public int Timeout + { + get{ return _msgBox.Timeout; } + set{ _msgBox.Timeout = value; } + } + + /// + /// Controls the result that will be returned when the message box times out. + /// + public TimeoutResult TimeoutResult + { + get{ return _msgBox.TimeoutResult; } + set{ _msgBox.TimeoutResult = value; } + } + #endregion + + #region Methods + /// + /// Shows the message box + /// + /// + public string Show() + { + return Show(null); + } + + /// + /// Shows the messsage box with the specified owner + /// + /// + /// + public string Show(IWin32Window owner) + { + if(_useSavedResponse && this.Name != null) + { + string savedResponse = MessageBoxExManager.GetSavedResponse(this); + if( savedResponse != null) + return savedResponse; + } + + if(owner == null) + { + _msgBox.ShowDialog(); + } + else + { + _msgBox.ShowDialog(owner); + } + + if(this.Name != null) + { + if(_msgBox.AllowSaveResponse && _msgBox.SaveResponse) + MessageBoxExManager.SetSavedResponse(this, _msgBox.Result); + else + MessageBoxExManager.ResetSavedResponse(this.Name); + } + else + { + Dispose(); + } + + return _msgBox.Result; + } + + /// + /// Add a custom button to the message box + /// + /// The button to add + public void AddButton(MessageBoxExButton button) + { + if(button == null) + throw new ArgumentNullException("button","A null button cannot be added"); + + _msgBox.Buttons.Add(button); + + if(button.IsCancelButton) + { + _msgBox.CustomCancelButton = button; + } + } + + /// + /// Add a custom button to the message box + /// + /// The text of the button + /// The return value in case this button is clicked + public void AddButton(string text, string val) + { + if(text == null) + throw new ArgumentNullException("text","Text of a button cannot be null"); + + if(val == null) + throw new ArgumentNullException("val","Value of a button cannot be null"); + + MessageBoxExButton button = new MessageBoxExButton(); + button.Text = text; + button.Value = val; + + AddButton(button); + } + + /// + /// Add a standard button to the message box + /// + /// The standard button to add + public void AddButton(MessageBoxExButtons button) + { + string buttonText = MessageBoxExManager.GetLocalizedString(button.ToString()); + if(buttonText == null) + { + buttonText = button.ToString(); + } + + string buttonVal = button.ToString(); + + MessageBoxExButton btn = new MessageBoxExButton(); + btn.Text = buttonText; + btn.Value = buttonVal; + + if(button == MessageBoxExButtons.Cancel) + { + btn.IsCancelButton = true; + } + + AddButton(btn); + } + + /// + /// Add standard buttons to the message box. + /// + /// The standard buttons to add + public void AddButtons(MessageBoxButtons buttons) + { + switch(buttons) + { + case MessageBoxButtons.OK: + AddButton(MessageBoxExButtons.Ok); + break; + + case MessageBoxButtons.AbortRetryIgnore: + AddButton(MessageBoxExButtons.Abort); + AddButton(MessageBoxExButtons.Retry); + AddButton(MessageBoxExButtons.Ignore); + break; + + case MessageBoxButtons.OKCancel: + AddButton(MessageBoxExButtons.Ok); + AddButton(MessageBoxExButtons.Cancel); + break; + + case MessageBoxButtons.RetryCancel: + AddButton(MessageBoxExButtons.Retry); + AddButton(MessageBoxExButtons.Cancel); + break; + + case MessageBoxButtons.YesNo: + AddButton(MessageBoxExButtons.Yes); + AddButton(MessageBoxExButtons.No); + break; + + case MessageBoxButtons.YesNoCancel: + AddButton(MessageBoxExButtons.Yes); + AddButton(MessageBoxExButtons.No); + AddButton(MessageBoxExButtons.Cancel); + break; + } + } + #endregion + + #region Ctor + /// + /// Ctor is internal because this can only be created by MBManager + /// + internal MessageBoxEx() + { + _msgBox = new MessageBoxExForm(null); + } + + public MessageBoxEx(Form parent) + { + _msgBox = new MessageBoxExForm(parent); + } + + /// + /// Called by the manager when it is disposed + /// + internal void Dispose() + { + if(_msgBox != null) + { + _msgBox.Dispose(); + } + } + #endregion + } +} diff --git a/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExButton.cs b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExButton.cs new file mode 100644 index 0000000..3cc0573 --- /dev/null +++ b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExButton.cs @@ -0,0 +1,52 @@ +using System; + +namespace CommonLib.Windows.Forms +{ + /// + /// Internal DataStructure used to represent a button + /// + public class MessageBoxExButton + { + private string _text = null; + /// + /// Gets or Sets the text of the button + /// + public string Text + { + get{ return _text; } + set{ _text = value; } + } + + private string _value = null; + /// + /// Gets or Sets the return value when this button is clicked + /// + public string Value + { + get{ return _value; } + set{_value = value; } + } + + private string _helpText = null; + /// + /// Gets or Sets the tooltip that is displayed for this button + /// + public string HelpText + { + get{ return _helpText; } + set{ _helpText = value; } + } + + private bool _isCancelButton = false; + /// + /// Gets or Sets wether this button is a cancel button. i.e. the button + /// that will be assumed to have been clicked if the user closes the message box + /// without pressing any button. + /// + public bool IsCancelButton + { + get{ return _isCancelButton; } + set{ _isCancelButton = value; } + } + } +} diff --git a/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExButtons.cs b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExButtons.cs new file mode 100644 index 0000000..3dd4412 --- /dev/null +++ b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExButtons.cs @@ -0,0 +1,18 @@ +using System; + +namespace CommonLib.Windows.Forms +{ + /// + /// Standard MessageBoxEx buttons + /// + public enum MessageBoxExButtons + { + Ok = 0, + Cancel = 1, + Yes = 2, + No = 4, + Abort = 8, + Retry = 16, + Ignore = 32 + } +} diff --git a/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExForm.cs b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExForm.cs new file mode 100644 index 0000000..89dd095 --- /dev/null +++ b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExForm.cs @@ -0,0 +1,1091 @@ +using System; +using System.Drawing; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Windows.Forms; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace CommonLib.Windows.Forms +{ + /// + /// An advanced MessageBox that supports customizations like Font, Icon, + /// Buttons and Saved Responses + /// + internal class MessageBoxExForm : Form + { + #region Constants + private const int LEFT_PADDING = 12; + private const int RIGHT_PADDING = 12; + private const int TOP_PADDING = 12; + private const int BOTTOM_PADDING = 12; + + private const int BUTTON_LEFT_PADDING = 4; + private const int BUTTON_RIGHT_PADDING = 4; + private const int BUTTON_TOP_PADDING = 4; + private const int BUTTON_BOTTOM_PADDING = 4; + + private const int MIN_BUTTON_HEIGHT = 23; + private const int MIN_BUTTON_WIDTH = 74; + + private const int ITEM_PADDING = 10; + private const int ICON_MESSAGE_PADDING = 15; + + private const int BUTTON_PADDING = 5; + + private const int CHECKBOX_WIDTH = 20; + + private const int IMAGE_INDEX_EXCLAMATION = 0; + private const int IMAGE_INDEX_QUESTION = 1; + private const int IMAGE_INDEX_STOP = 2; + private const int IMAGE_INDEX_INFORMATION = 3; + #endregion + + #region Fields + + private System.ComponentModel.IContainer components; + private System.Windows.Forms.CheckBox chbSaveResponse; + private System.Windows.Forms.ImageList imageListIcons; + private System.Windows.Forms.ToolTip buttonToolTip; + + private ArrayList _buttons = new ArrayList(); + private bool _allowSaveResponse; + private bool _playAlert = true; + private MessageBoxExButton _cancelButton = null; + private Button _defaultButtonControl = null; + + private int _maxLayoutWidth; + private int _maxLayoutHeight; + + private int _maxWidth; + private int _maxHeight; + + private bool _allowCancel = true; + private string _result = null; + + /// + /// Used to determine the alert sound to play + /// + private MessageBoxIcon _standardIcon = MessageBoxIcon.None; + private Icon _iconImage = null; + + private Timer timerTimeout = null; + private int _timeout = 0; + private TimeoutResult _timeoutResult = TimeoutResult.Default; + private System.Windows.Forms.Panel panelIcon; + private System.Windows.Forms.RichTextBox rtbMessage; + + /// + /// Maps MessageBoxEx buttons to Button controls + /// + private Hashtable _buttonControlsTable = new Hashtable(); + #endregion + + #region Properties + public Form parentForm; + + public string Message + { + set { rtbMessage.Text = value; } + } + + public string Caption + { + set { this.Text = value; } + } + + public Font CustomFont + { + set { this.Font = value; } + } + + public ArrayList Buttons + { + get { return _buttons; } + } + + public bool AllowSaveResponse + { + get { return _allowSaveResponse; } + set { _allowSaveResponse = value; } + } + + public bool SaveResponse + { + get { return chbSaveResponse.Checked; } + } + + public string SaveResponseText + { + set { chbSaveResponse.Text = value; } + } + + public MessageBoxIcon StandardIcon + { + set { SetStandardIcon(value); } + } + + public Icon CustomIcon + { + set + { + _standardIcon = MessageBoxIcon.None; + _iconImage = value; + } + } + + public MessageBoxExButton CustomCancelButton + { + set { _cancelButton = value; } + } + + public string Result + { + get { return _result; } + } + + public bool PlayAlertSound + { + get { return _playAlert; } + set { _playAlert = value; } + } + + public int Timeout + { + get { return _timeout; } + set { _timeout = value; } + } + + public TimeoutResult TimeoutResult + { + get { return _timeoutResult; } + set { _timeoutResult = value; } + } + + #endregion + + #region Ctor/Dtor + public MessageBoxExForm(Form parent) + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + this.parentForm = parent; + + // do not show the message box in taskbar + this.ShowInTaskbar = false; + + _maxWidth = (int)(SystemInformation.WorkingArea.Width * 0.60); + _maxHeight = (int)(SystemInformation.WorkingArea.Height * 0.90); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose(disposing); + } + #endregion + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(MessageBoxExForm)); + this.panelIcon = new System.Windows.Forms.Panel(); + this.chbSaveResponse = new System.Windows.Forms.CheckBox(); + this.imageListIcons = new System.Windows.Forms.ImageList(this.components); + this.buttonToolTip = new System.Windows.Forms.ToolTip(this.components); + this.rtbMessage = new System.Windows.Forms.RichTextBox(); + this.SuspendLayout(); + // + // panelIcon + // + this.panelIcon.BackColor = System.Drawing.Color.Transparent; + this.panelIcon.Location = new System.Drawing.Point(8, 8); + this.panelIcon.Name = "panelIcon"; + this.panelIcon.Size = new System.Drawing.Size(32, 32); + this.panelIcon.TabIndex = 3; + this.panelIcon.Visible = false; + // + // chbSaveResponse + // + this.chbSaveResponse.FlatStyle = System.Windows.Forms.FlatStyle.System; + this.chbSaveResponse.Location = new System.Drawing.Point(56, 56); + this.chbSaveResponse.Name = "chbSaveResponse"; + this.chbSaveResponse.Size = new System.Drawing.Size(104, 16); + this.chbSaveResponse.TabIndex = 0; + // + // imageListIcons + // + this.imageListIcons.ColorDepth = System.Windows.Forms.ColorDepth.Depth16Bit; + this.imageListIcons.ImageSize = new System.Drawing.Size(32, 32); + this.imageListIcons.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageListIcons.ImageStream"))); + this.imageListIcons.TransparentColor = System.Drawing.Color.Transparent; + // + // rtbMessage + // + this.rtbMessage.BackColor = System.Drawing.SystemColors.Control; + this.rtbMessage.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.rtbMessage.Location = new System.Drawing.Point(200, 8); + this.rtbMessage.Name = "rtbMessage"; + this.rtbMessage.ReadOnly = true; + this.rtbMessage.Size = new System.Drawing.Size(100, 48); + this.rtbMessage.TabIndex = 4; + this.rtbMessage.Text = ""; + this.rtbMessage.Visible = false; + // + // MessageBoxExForm + // + //this.AutoScale = false; + this.AutoScaleMode = AutoScaleMode.Inherit; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 14); + this.ClientSize = new System.Drawing.Size(322, 224); + this.Controls.Add(this.rtbMessage); + this.Controls.Add(this.chbSaveResponse); + this.Controls.Add(this.panelIcon); + this.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "MessageBoxExForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.ResumeLayout(false); + + } + #endregion + + #region Overrides + /// + /// This will get called everytime we call ShowDialog on the form + /// + /// + protected override void OnLoad(EventArgs e) + { + //Reset result + _result = null; + + this.Size = new Size(_maxWidth, _maxHeight); + + //This is the rectangle in which all items will be layed out + _maxLayoutWidth = this.ClientSize.Width - LEFT_PADDING - RIGHT_PADDING; + _maxLayoutHeight = this.ClientSize.Height - TOP_PADDING - BOTTOM_PADDING; + + AddOkButtonIfNoButtonsPresent(); + DisableCloseIfMultipleButtonsAndNoCancelButton(); + + SetIconSizeAndVisibility(); + SetMessageSizeAndVisibility(); + SetCheckboxSizeAndVisibility(); + + SetOptimumSize(); + + LayoutControls(); + + CenterFormRelativeToParentForm(); + + PlayAlert(); + + SelectDefaultButton(); + + StartTimerIfTimeoutGreaterThanZero(); + + base.OnLoad(e); + } + + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if ((int)keyData == (int)(Keys.Alt | Keys.F4) && !_allowCancel) + { + return true; + } + + return base.ProcessCmdKey(ref msg, keyData); + } + + + protected override void OnClosing(CancelEventArgs e) + { + if (_result == null) + { + if (_allowCancel) + { + _result = _cancelButton.Value; + } + else + { + e.Cancel = true; + return; + } + } + + if (timerTimeout != null) + { + timerTimeout.Stop(); + } + + base.OnClosing(e); + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + if (_iconImage != null) + { + e.Graphics.DrawIcon(_iconImage, new Rectangle(panelIcon.Location, new Size(32, 32))); + } + } + + #endregion + + #region Methods + /// + /// Measures a string using the Graphics object for this form with + /// the specified font + /// + /// The string to measure + /// The maximum width available to display the string + /// The font with which to measure the string + /// + private Size MeasureString(string str, int maxWidth, Font font) + { + Graphics g = this.CreateGraphics(); + SizeF strRectSizeF = g.MeasureString(str, font, maxWidth); + g.Dispose(); + + return new Size((int)Math.Ceiling(strRectSizeF.Width), (int)Math.Ceiling(strRectSizeF.Height)); + } + + /// + /// Measures a string using the Graphics object for this form and the + /// font of this form + /// + /// + /// + /// + private Size MeasureString(string str, int maxWidth) + { + return MeasureString(str, maxWidth, this.Font); + } + + /// + /// Gets the longest button text + /// + /// + private string GetLongestButtonText() + { + int maxLen = 0; + string maxStr = null; + foreach (MessageBoxExButton button in _buttons) + { + if (button.Text != null && button.Text.Length > maxLen) + { + maxLen = button.Text.Length; + maxStr = button.Text; + } + } + + return maxStr; + } + + /// + /// Sets the size and visibility of the Message + /// + private void SetMessageSizeAndVisibility() + { + if (rtbMessage.Text == null || rtbMessage.Text.Trim().Length == 0) + { + rtbMessage.Size = Size.Empty; + rtbMessage.Visible = false; + } + else + { + int maxWidth = _maxLayoutWidth; + if (panelIcon.Size.Width != 0) + { + maxWidth = maxWidth - (panelIcon.Size.Width + ICON_MESSAGE_PADDING); + } + + //We need to account for scroll bar width and height, otherwise for certains + //kinds of text the scroll bar shows up unnecessarily + maxWidth = maxWidth - SystemInformation.VerticalScrollBarWidth; + Size messageRectSize = MeasureString(rtbMessage.Text, maxWidth); + + messageRectSize.Width += SystemInformation.VerticalScrollBarWidth; + messageRectSize.Height = Math.Max(panelIcon.Height, messageRectSize.Height) + SystemInformation.HorizontalScrollBarHeight; + + rtbMessage.Size = messageRectSize; + rtbMessage.Visible = true; + } + } + + /// + /// Sets the size and visibility of the Icon + /// + private void SetIconSizeAndVisibility() + { + if (_iconImage == null) + { + panelIcon.Visible = false; + panelIcon.Size = Size.Empty; + } + else + { + panelIcon.Size = new Size(32, 32); + panelIcon.Visible = true; + } + } + + /// + /// Sets the size and visibility of the save response checkbox + /// + private void SetCheckboxSizeAndVisibility() + { + if (!AllowSaveResponse) + { + chbSaveResponse.Visible = false; + chbSaveResponse.Size = Size.Empty; + } + else + { + Size saveResponseTextSize = MeasureString(chbSaveResponse.Text, _maxLayoutWidth); + saveResponseTextSize.Width += CHECKBOX_WIDTH; + chbSaveResponse.Size = saveResponseTextSize; + chbSaveResponse.Visible = true; + } + } + + /// + /// Calculates the button size based on the text of the longest + /// button text + /// + /// + private Size GetButtonSize() + { + string longestButtonText = GetLongestButtonText(); + if (longestButtonText == null) + { + //TODO:Handle this case + } + + Size buttonTextSize = MeasureString(longestButtonText, _maxLayoutWidth); + Size buttonSize = new Size(buttonTextSize.Width + BUTTON_LEFT_PADDING + BUTTON_RIGHT_PADDING, + buttonTextSize.Height + BUTTON_TOP_PADDING + BUTTON_BOTTOM_PADDING); + + if (buttonSize.Width < MIN_BUTTON_WIDTH) + buttonSize.Width = MIN_BUTTON_WIDTH; + if (buttonSize.Height < MIN_BUTTON_HEIGHT) + buttonSize.Height = MIN_BUTTON_HEIGHT; + + return buttonSize; + } + + /// + /// Calculates the button size based on the text of the longest + /// button text + /// + /// + private Size GetButtonSize(MessageBoxExButton button) + { + string buttonText = button.Text; + + Size buttonTextSize = MeasureString(buttonText, _maxLayoutWidth); + Size buttonSize = new Size(buttonTextSize.Width + BUTTON_LEFT_PADDING + BUTTON_RIGHT_PADDING, + buttonTextSize.Height + BUTTON_TOP_PADDING + BUTTON_BOTTOM_PADDING); + + if (buttonSize.Width < MIN_BUTTON_WIDTH) + buttonSize.Width = MIN_BUTTON_WIDTH; + if (buttonSize.Height < MIN_BUTTON_HEIGHT) + buttonSize.Height = MIN_BUTTON_HEIGHT; + + return buttonSize; + } + + /// + /// Set the icon + /// + /// + private void SetStandardIcon(MessageBoxIcon icon) + { + _standardIcon = icon; + + switch (icon) + { + case MessageBoxIcon.Asterisk: + _iconImage = SystemIcons.Asterisk; + break; + case MessageBoxIcon.Error: + _iconImage = SystemIcons.Error; + break; + case MessageBoxIcon.Exclamation: + _iconImage = SystemIcons.Exclamation; + break; + // case MessageBoxIcon.Hand: + // _iconImage = SystemIcons.Hand; + // break; + // case MessageBoxIcon.Information: + // _iconImage = SystemIcons.Information; + // break; + case MessageBoxIcon.Question: + _iconImage = SystemIcons.Question; + break; + // case MessageBoxIcon.Stop: + // _iconImage = SystemIcons.Stop; + // break; + // case MessageBoxIcon.Warning: + // _iconImage = SystemIcons.Warning; + // break; + + case MessageBoxIcon.None: + _iconImage = null; + break; + } + } + + private void AddOkButtonIfNoButtonsPresent() + { + if (_buttons.Count == 0) + { + MessageBoxExButton okButton = new MessageBoxExButton(); + okButton.Text = MessageBoxExButtons.Ok.ToString(); + okButton.Value = MessageBoxExButtons.Ok.ToString(); + + _buttons.Add(okButton); + } + } + + private void CenterFormRelativeToScreen() + { + foreach (Screen screen in Screen.AllScreens) + { + if (screen.Primary) + { + int centerScreenX = screen.WorkingArea.Width / 2; + int centerScreenY = screen.WorkingArea.Height / 2; + + this.Left = centerScreenX - (this.Width / 2); + this.Top = centerScreenY - (this.Height / 2); + } + } + } + + /// + /// Centers the form relative to the parent form + /// Also moves the form into visible area if it's determined that + /// an area of the form is blocked + /// + private void CenterFormRelativeToParentForm() + { + if (this.parentForm == null) + { + CenterFormRelativeToScreen(); + return; + } + + List allScreens = new List(); + int parentCenterX, parentCenterY, childTopX, childTopY, + childVisibleTopX, childVisibleBottomX, + screenVisibleBottomX, screenVisibleBottomY, rectWidth, trueChildWidth, screensCount = 0; + + // create the smallest rectangle + Rectangle rect = new Rectangle(int.MaxValue, int.MaxValue, int.MinValue, int.MinValue); + + // determines the coordinate of the center of the parent form + // that will be used as the center of the pop-up box + parentCenterX = this.parentForm.Left + this.parentForm.Width / 2; + parentCenterY = this.parentForm.Top + this.parentForm.Height / 2; + + childTopX = parentCenterX - this.Width / 2; + childTopY = parentCenterY - this.Height / 2; + + // if the x coordinate is off the screen to the left, we move it to the visible area of screen + if (childTopX < 0) + { + trueChildWidth = childTopX + this.Width; + childTopX = 0; + } + else + trueChildWidth = this.Width; + + // if the y coordinate is off the screen up above, we move it to the visible area of screen + if (childTopY < 0) + childTopY = 0; + + foreach (Screen screen in Screen.AllScreens) + { + if (screen.Primary) + allScreens.Insert(0, screen); + else + allScreens.Add(screen); + } + + foreach (Screen screen in allScreens) + { + screensCount++; + + if (rect.Width < 0) + rectWidth = 0; + else + rectWidth = rect.Width; + + rect = Rectangle.Union(rect, screen.Bounds); // join screens + + // if we're on the last screen + if (Screen.AllScreens.Length == screensCount) + { + // if the x coordinate is off the screen to the right, we move it to the visible area of screen + if ((childTopX + this.Width) > rect.Width) + { + trueChildWidth = rect.Width - childTopX; + } + } + + screenVisibleBottomX = screen.WorkingArea.X + screen.WorkingArea.Width; + screenVisibleBottomY = screen.WorkingArea.Y + screen.WorkingArea.Height; + + if (childTopX <= rectWidth) + { + childVisibleTopX = rectWidth + 1; + } + else + childVisibleTopX = childTopX; + + if ((childTopX + trueChildWidth) > (rectWidth + screen.Bounds.Width)) + childVisibleBottomX = rectWidth + screen.Bounds.Width; + else + childVisibleBottomX = childTopX + trueChildWidth; + + // determine if the visible width of the form is 50% or greater, then we choose + // to display this form on this screen + if (Convert.ToInt32(Math.Ceiling(((double)(childVisibleBottomX - childVisibleTopX) / (double)trueChildWidth) * 100.0)) >= 50) + { + if (childTopX < screen.WorkingArea.X) + childTopX = screen.WorkingArea.X; + else if ((childTopX + this.Width) > screenVisibleBottomX) + childTopX -= (childTopX + this.Width) - screenVisibleBottomX; + + if (childTopY < screen.WorkingArea.Y) + childTopY = screen.WorkingArea.Y; + else if ((childTopY + this.Height) > screenVisibleBottomY) + childTopY -= (childTopY + this.Height) - screenVisibleBottomY; + + break; + } + } + + + // if the x coordinate is off the screen to the right, we move it to the visible area of screen + //if ((x + this.Width) > rect.Width) + // x = x - ((x + this.Width) - rect.Width); + + this.Location = new Point(childTopX, childTopY); + } + + /// + /// Sets the optimum size for the form based on the controls that + /// need to be displayed + /// + private void SetOptimumSize() + { + int ncWidth = this.Width - this.ClientSize.Width; + int ncHeight = this.Height - this.ClientSize.Height; + + int iconAndMessageRowWidth = rtbMessage.Width + ICON_MESSAGE_PADDING + panelIcon.Width; + int saveResponseRowWidth = chbSaveResponse.Width + (int)(panelIcon.Width / 2); + int buttonsRowWidth = GetWidthOfAllButtons(); + int captionWidth = GetCaptionSize().Width; + + int maxItemWidth = Math.Max(saveResponseRowWidth, Math.Max(iconAndMessageRowWidth, buttonsRowWidth)); + + int requiredWidth = LEFT_PADDING + maxItemWidth + RIGHT_PADDING + ncWidth; + //Since Caption width is not client width, we do the check here + if (requiredWidth < captionWidth) + requiredWidth = captionWidth; + + int requiredHeight = TOP_PADDING + Math.Max(rtbMessage.Height, panelIcon.Height) + ITEM_PADDING + chbSaveResponse.Height + ITEM_PADDING + GetButtonSize().Height + BOTTOM_PADDING + ncHeight; + + //Fix the bug where if the message text is huge then the buttons are overwritten. + //Incase the required height is more than the max height then adjust that in the + //message height + if (requiredHeight > _maxHeight) + { + rtbMessage.Height -= requiredHeight - _maxHeight; + } + + int height = Math.Min(requiredHeight, _maxHeight); + int width = Math.Min(requiredWidth, _maxWidth); + this.Size = new Size(width, height); + } + + /// + /// Returns the width that will be occupied by all buttons including + /// the inter-button padding + /// + private int GetWidthOfAllButtons() + { + int widthOfAllButtons = 0; + + foreach (MessageBoxExButton button in _buttons) + { + Size size = GetButtonSize(button); + widthOfAllButtons += size.Width; + } + + int allButtonsWidth = widthOfAllButtons + BUTTON_PADDING * (_buttons.Count - 1); + + return allButtonsWidth; + } + + /// + /// Gets the width of the caption + /// + private Size GetCaptionSize() + { + Font captionFont = GetCaptionFont(); + if (captionFont == null) + { + //some error occured while determining system font + captionFont = new Font("Tahoma", 11); + } + + int availableWidth = _maxWidth - SystemInformation.CaptionButtonSize.Width - SystemInformation.Border3DSize.Width * 2; + Size captionSize = MeasureString(this.Text, availableWidth, captionFont); + + captionSize.Width += SystemInformation.CaptionButtonSize.Width + SystemInformation.Border3DSize.Width * 2; + return captionSize; + } + + /// + /// Layout all the controls + /// + private void LayoutControls() + { + panelIcon.Location = new Point(LEFT_PADDING, TOP_PADDING); + rtbMessage.Location = new Point(LEFT_PADDING + panelIcon.Width + ICON_MESSAGE_PADDING * (panelIcon.Width == 0 ? 0 : 1), TOP_PADDING); + + chbSaveResponse.Location = new Point(LEFT_PADDING + (int)(panelIcon.Width / 2), + TOP_PADDING + Math.Max(panelIcon.Height, rtbMessage.Height) + ITEM_PADDING); + + Size buttonSize = GetButtonSize(); + int allButtonsWidth = GetWidthOfAllButtons(); + + int firstButtonX = ((int)(this.ClientSize.Width - allButtonsWidth) / 2); + int firstButtonY = this.ClientSize.Height - BOTTOM_PADDING - buttonSize.Height; + Point nextButtonLocation = new Point(firstButtonX, firstButtonY); + + bool foundDefaultButton = false; + foreach (MessageBoxExButton button in _buttons) + { + buttonSize = GetButtonSize(button); + Button buttonCtrl = GetButton(button, buttonSize, nextButtonLocation); + + if (!foundDefaultButton) + { + _defaultButtonControl = buttonCtrl; + foundDefaultButton = true; + } + + nextButtonLocation.X += buttonSize.Width + BUTTON_PADDING; + } + } + + /// + /// Gets the button control for the specified MessageBoxExButton, if the + /// control has not been created this method creates the control + /// + /// + /// + /// + /// + private Button GetButton(MessageBoxExButton button, Size size, Point location) + { + Button buttonCtrl = null; + if (_buttonControlsTable.ContainsKey(button)) + { + buttonCtrl = _buttonControlsTable[button] as Button; + buttonCtrl.Size = size; + buttonCtrl.Location = location; + } + else + { + buttonCtrl = CreateButton(button, size, location); + _buttonControlsTable[button] = buttonCtrl; + this.Controls.Add(buttonCtrl); + } + + return buttonCtrl; + } + + /// + /// Creates a button control based on info from MessageBoxExButton + /// + /// + /// + /// + /// + private Button CreateButton(MessageBoxExButton button, Size size, Point location) + { + Button buttonCtrl = new Button(); + buttonCtrl.Size = size; + buttonCtrl.Text = button.Text; + buttonCtrl.TextAlign = ContentAlignment.MiddleCenter; + buttonCtrl.FlatStyle = FlatStyle.System; + if (button.HelpText != null && button.HelpText.Trim().Length != 0) + { + buttonToolTip.SetToolTip(buttonCtrl, button.HelpText); + } + buttonCtrl.Location = location; + buttonCtrl.Click += new EventHandler(OnButtonClicked); + buttonCtrl.Tag = button.Value; + + return buttonCtrl; + } + + private void DisableCloseIfMultipleButtonsAndNoCancelButton() + { + if (_buttons.Count > 1) + { + if (_cancelButton != null) + return; + + //See if standard cancel button is present + foreach (MessageBoxExButton button in _buttons) + { + if (button.Text == MessageBoxExButtons.Cancel.ToString() && button.Value == MessageBoxExButtons.Cancel.ToString()) + { + _cancelButton = button; + return; + } + } + + //Standard cancel button is not present, Disable + //close button + DisableCloseButton(this); + _allowCancel = false; + + } + else if (_buttons.Count == 1) + { + _cancelButton = _buttons[0] as MessageBoxExButton; + } + else + { + //This condition should never get called + _allowCancel = false; + } + } + + /// + /// Plays the alert sound based on the icon set for the message box + /// + private void PlayAlert() + { + if (_playAlert) + { + if (_standardIcon != MessageBoxIcon.None) + { + MessageBeep((uint)_standardIcon); + } + else + { + MessageBeep(0 /*MB_OK*/); + } + } + } + + private void SelectDefaultButton() + { + if (_defaultButtonControl != null) + { + _defaultButtonControl.Select(); + } + } + + private void StartTimerIfTimeoutGreaterThanZero() + { + if (_timeout > 0) + { + if (timerTimeout == null) + { + timerTimeout = new Timer(this.components); + timerTimeout.Tick += new EventHandler(timerTimeout_Tick); + } + + if (!timerTimeout.Enabled) + { + timerTimeout.Interval = _timeout; + timerTimeout.Start(); + } + } + } + + private void SetResultAndClose(string result) + { + _result = result; + this.DialogResult = DialogResult.OK; + } + + #endregion + + #region Event Handlers + private void OnButtonClicked(object sender, EventArgs e) + { + Button btn = sender as Button; + if (btn == null || btn.Tag == null) + return; + + string result = btn.Tag as string; + SetResultAndClose(result); + } + + private void timerTimeout_Tick(object sender, EventArgs e) + { + timerTimeout.Stop(); + + switch (_timeoutResult) + { + case TimeoutResult.Default: + _defaultButtonControl.PerformClick(); + break; + + case TimeoutResult.Cancel: + if (_cancelButton != null) + { + SetResultAndClose(_cancelButton.Value); + } + else + { + _defaultButtonControl.PerformClick(); + } + break; + + case TimeoutResult.Timeout: + SetResultAndClose(MessageBoxExResult.Timeout); + break; + } + } + #endregion + + #region P/Invoke - SystemParametersInfo, GetSystemMenu, EnableMenuItem, MessageBeep + private Font GetCaptionFont() + { + + NONCLIENTMETRICS ncm = new NONCLIENTMETRICS(); + ncm.cbSize = Marshal.SizeOf(typeof(NONCLIENTMETRICS)); + try + { + bool result = SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, ref ncm, 0); + + if (result) + { + return Font.FromLogFont(ncm.lfCaptionFont); + + } + else + { + int lastError = Marshal.GetLastWin32Error(); + return null; + } + } + catch (Exception /*ex*/) + { + //System.Console.WriteLine(ex.Message); + } + + return null; + } + + private const int SPI_GETNONCLIENTMETRICS = 41; + private const int LF_FACESIZE = 32; + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private struct LOGFONT + { + public int lfHeight; + public int lfWidth; + public int lfEscapement; + public int lfOrientation; + public int lfWeight; + public byte lfItalic; + public byte lfUnderline; + public byte lfStrikeOut; + public byte lfCharSet; + public byte lfOutPrecision; + public byte lfClipPrecision; + public byte lfQuality; + public byte lfPitchAndFamily; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string lfFaceSize; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + private struct NONCLIENTMETRICS + { + public int cbSize; + public int iBorderWidth; + public int iScrollWidth; + public int iScrollHeight; + public int iCaptionWidth; + public int iCaptionHeight; + public LOGFONT lfCaptionFont; + public int iSmCaptionWidth; + public int iSmCaptionHeight; + public LOGFONT lfSmCaptionFont; + public int iMenuWidth; + public int iMenuHeight; + public LOGFONT lfMenuFont; + public LOGFONT lfStatusFont; + public LOGFONT lfMessageFont; + } + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + private static extern bool SystemParametersInfo(int uiAction, int uiParam, + ref NONCLIENTMETRICS ncMetrics, int fWinIni); + + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert); + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, + uint uEnable); + + private const int SC_CLOSE = 0xF060; + private const int MF_BYCOMMAND = 0x0; + private const int MF_GRAYED = 0x1; + private const int MF_ENABLED = 0x0; + + private void DisableCloseButton(Form form) + { + try + { + EnableMenuItem(GetSystemMenu(form.Handle, false), SC_CLOSE, MF_BYCOMMAND | MF_GRAYED); + } + catch (Exception /*ex*/) + { + //System.Console.WriteLine(ex.Message); + } + } + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern bool MessageBeep(uint type); + #endregion + } +} diff --git a/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExForm.resx b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExForm.resx new file mode 100644 index 0000000..0ed0c9f --- /dev/null +++ b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExForm.resx @@ -0,0 +1,387 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + True + + + True + + + Private + + + Private + + + 8, 8 + + + False + + + Private + + + Private + + + Private + + + Private + + + 17, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFpTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0xLjAuNTAw + MC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZT + eXN0ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMA + AADELQAAAk1TRnQBSQFMAgEBBAEAAQUBAAEEAQABIAEAASABAAT/AREBEAj/AUIBTQE2BwABNgMAASgD + AAGAAwABQAMAAQEBAAEQBgABQP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/ + AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wA8AAEQAUIBEAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCARAB + QgEQAUIBEAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCARAB + QgEQAUIqAAEQAUIBEAFCMAABEAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCARABQjwAARABQgEQAUIe + AAEQAUIBEAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCARAB + QgEQAUIBEAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCARABQiYAARABQgEQAUIB + EAFCKgABEAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCARAB + QjQAARABQgEQAUIBEAFCGwABQjAAARABQgEQAUIBEAFCARABQgEQAUImAAEQAUIBEAFCKAABEAFCARAB + QgEAAUABAAFAAQABQAEAAUABAAFAAQABQAEAAUABAAFAARABQgEQAUIBEAFCARABQgEQAUIBEAFCNAAB + EAFCARABQhkAAUIB4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B + 4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8BGAFjAgABEAFCARAB + QgEQAUIBEAFCIgAB/wF/AgABEAFCARABQiQAARABQgEAAUABAAFAAQABQAEAAXwBAAF8AQABfAEAAXwB + AAF8AQABfAEAAXwBAAF8AQABQAEAAUABAAFAARABQgEQAUIBEAFCARABQgEQAUIsAAH/AX8CAAEQAUIB + EAFCFwABQgHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B + 4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/ARgBYwIAARAB + QgEQAUIBEAFCIAAB/wF/Af8BfwIAARABQgEQAUIiAAEQAUIBAAFAAQABfAEAAXwBAAF8AQABfAEAAXwB + AAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAFAARABQgEQAUIBEAFCARABQgEQAUIo + AAH/AX8B/wF/AgABEAFCARABQhcAAUIB4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B + 4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B + 4AF/AeABfwHgAX8CAAEQAUIBEAFCARABQhoAARABQgIAAf8BfwH/AX8B/wF/AgABEAFCARABQiEAAUAB + AAFAAQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwB + AAF8AQABfAEAAUABAAFAARABQgEQAUIBEAFCARABQiAAARABQgIAAf8BfwH/AX8B/wF/AgABEAFCARAB + QhcAAUIB4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8BGAFjBAAB + GAFjAeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8CAAEQAUIBEAFCFgAB + EAFCARABQgEQAUIBEAFCAgAB/wF/Af8BfwH/AX8CAAEQAUIBEAFCARABQgEQAUIBEAFCGQABQAEAAXwB + AAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwB + AAF8AQABfAEAAXwBAAF8AQABQAEQAUIBEAFCARABQhoAARABQgEQAUIBEAFCARABQgIAAf8BfwH/AX8B + /wF/AgABEAFCARABQgEQAUIBEAFCARABQhEAAUIB4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeAB + fwHgAX8B4AF/AeABfwHgAX8IAAHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B + GAFjAgABEAFCARABQhIAARABQgEQAUIBEAFCBgABGAFjAf8BfwH/AX8B/wF/AgABEAFCARABQgEQAUIB + EAFCARABQgEQAUIBEAFCEwABQAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwB + AAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAFAARABQgEQAUIB + EAFCFAABEAFCARABQgEQAUIGAAEYAWMB/wF/Af8BfwH/AX8CAAEQAUIBEAFCARABQgEQAUIBEAFCARAB + QgEQAUIPAAFCAeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8IAAHgAX8B + 4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8CAAEQAUIBEAFCEgABEAFCBgABGAFjAf8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/ARgBYwYAARABQgEQAUIBEAFCARABQgEQAUIRAAFAAQABfAEAAXwB + AAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwB + AAF8AQABfAEAAXwBAAF8AQABfAEAAUABEAFCARABQgEQAUIBEAFCEAABEAFCBgABGAFjAf8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/ARgBYwYAARABQgEQAUIBEAFCARABQgEQAUINAAFCAeABfwHgAX8B4AF/AeAB + fwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8BGAFjBAABGAFjAeABfwHgAX8B4AF/AeABfwHgAX8B + 4AF/AeABfwHgAX8B4AF/ARgBYwIAARABQgEQAUIUAAEYAWMB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8BGAFjBAABEAFCARABQgEQAUIBEAFCDQABQAEAAXwBAAF8AQAB + fAEAAXwBAAF8AQABfAH/AX8BAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8Af8B + fwEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAUABEAFCARABQgEQAUISAAEYAWMB/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8BGAFjBAABEAFCARABQgEQAUIBEAFCDQAB + QgHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeAB + fwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AgABEAFCARABQhIAARgBYwH/AX8B/wF/Af8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8BGAFjAgAB + EAFCARABQgEQAUIBEAFCCQABQAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAH/AX8B/wF/Af8BfwEAAXwB + AAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8Af8BfwH/AX8B/wF/AQABfAEAAXwBAAF8AQABfAEAAXwB + AAF8AQABQAEQAUIBEAFCDgABGAFjAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwEYAWMCAAEQAUIBEAFCARABQgEQAUILAAFCAeABfwHgAX8B + 4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8CAAHgAX8B4AF/AeABfwHgAX8B4AF/AeAB + fwHgAX8B4AF/AeABfwHgAX8BGAFjAgABEAFCARABQhAAAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8CAAEQAUIB + EAFCARABQgEQAUIHAAFAAQABfAEAAXwBAAF8AQABfAEAAXwB/wF/Af8BfwH/AX8B/wF/Af8BfwEAAXwB + AAF8AQABfAEAAXwBAAF8AQABfAH/AX8B/wF/Af8BfwH/AX8B/wF/AQABfAEAAXwBAAF8AQABfAEAAXwB + AAFAARABQgEQAUIBEAFCCgAB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwIAARABQgEQAUIBEAFCARABQgsAAUIB + 4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8BGAFjAgABGAFjAeABfwHgAX8B4AF/AeAB + fwHgAX8B4AF/AeABfwHgAX8B4AF/AgABEAFCARABQhAAAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwH/AX8B/wF/ARgBYwEfAQABHwEAARgBYwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwIAARABQgEQAUIBEAFCARABQgUAAUABAAF8AQABfAEAAXwBAAF8AQABfAEAAXwB/wF/Af8BfwH/AX8B + /wF/Af8BfwEAAXwBAAF8AQABfAEAAXwB/wF/Af8BfwH/AX8B/wF/Af8BfwEAAXwBAAF8AQABfAEAAXwB + AAF8AQABfAEAAUABEAFCARABQgEQAUIIAAH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8CAAEQAUIB + EAFCARABQgEQAUIJAAFCAeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AQABQgMAAUIB + 4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwEYAWMCAAEQAUIBEAFCDAABEAFCAf8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwEfAQABHwEAAR8BAAEfAQAB/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/AgABEAFCARABQgEQAUIDAAFAAQABfAEAAXwBAAF8AQAB + fAEAAXwBAAF8AQABfAEAAXwB/wF/Af8BfwH/AX8B/wF/Af8BfwEAAXwBAAF8Af8BfwH/AX8B/wF/Af8B + fwH/AX8BAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAUABEAFCARABQgQAARABQgH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/AR8BAAEfAQABHwEAAR8BAAEfAQABHwEAAR8BAAEfAQAB + HwEAAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwIAARABQgEQAUIBEAFCCwABQgHgAX8B4AF/AeAB + fwHgAX8B4AF/AeABfwHgAX8B4AF/BgAB4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwIAARAB + QgEQAUIMAAEQAUIBGAFjAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwEfAQAB + HwEAAR8BAAEfAQAB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/ARgBYwIAARAB + QgEQAUIBEAFCAQABQAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAH/AX8B/wF/Af8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8BAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQAB + fAEAAXwBAAFAARABQgEQAUICAAEQAUIBGAFjAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwEfAQABHwEAAR8BAAEfAQABHwEAAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/ARgBYwIAARABQgEQAUIBEAFCCQABQgHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8BGAFjBgAB + GAFjAeABfwHgAX8B4AF/AeABfwHgAX8B4AF/ARgBYwIAARABQgEQAUIMAAEQAUIB/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwEYAWMBHwEAAR8BAAEYAWMB/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwIAARABQgEQAUIBEAFCAQABQAEAAXwBAAF8AQAB + fAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAFAARABQgEQAUICAAEQAUIB + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwEfAQABHwEAAR8BAAEfAQAB + HwEAAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwIAARABQgEQAUIBEAFCCwAB + QgHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwEAAUIHAAFCAeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AgAB + EAFCARABQgwAARABQgEYAWMB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwEYAWMCAAEQAUIBEAFCAQABQAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwB + AAF8Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwB + AAF8AQABfAEAAXwBAAFAARABQgEQAUIBEAFCARgBYwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/AR8BAAEfAQABHwEAAR8BAAEfAQAB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/ARgBYwIAARABQgEQAUILAAFCAeABfwHgAX8B4AF/AeABfwHgAX8B4AF/CgAB + 4AF/AeABfwHgAX8B4AF/AeABfwEYAWMCAAEQAUIBEAFCDAABEAFCAf8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwEfAQABHwEAAf8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwIAARABQgEQAUIBAAFAAQABfAEAAXwBAAF8AQAB + fAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8BAAF8AQAB + fAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAUABEAFCARABQgEQAUIB/wF/Af8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8BHwEAAR8BAAEfAQABHwEAAR8B + AAH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/AgABEAFCARABQg0AAUIB + 4AF/AeABfwHgAX8B4AF/AeABfwoAAeABfwHgAX8B4AF/AeABfwHgAX8CAAEQAUIBEAFCDgABEAFCAf8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwEfAQABHwEAARgB + YwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwIAARABQgEQAUIB + AAFAAQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAUAB + EAFCARABQgEQAUIB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + HwEAAR8BAAEfAQABHwEAAR8BAAH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/AgABEAFCARABQg0AAUIB4AF/AeABfwHgAX8B4AF/AeABfwoAAeABfwHgAX8B4AF/AeABfwEYAWMC + AAEQAUIBEAFCDgABEAFCAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwEfAQABHwEAAR8BAAH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwIAARABQgEQAUIBAAFAAQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8Af8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwEAAXwBAAF8AQABfAEAAXwBAAF8AQAB + fAEAAXwBAAF8AQABfAEAAUABEAFCAgABEAFCAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/AR8BAAEfAQABHwEAAR8BAAEfAQAB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwIAARABQgEQAUIPAAFCAeABfwHgAX8B4AF/AeABfwoAAeABfwHgAX8B + 4AF/AeABfwIAARABQgEQAUIQAAEQAUIB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/ARgBYwEfAQABHwEAAR8BAAH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/AgABEAFCARABQgEAAUABAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQAB + fAH/AX8B/wF/Af8BfwH/AX8B/wF/AQABfAEAAXwB/wF/Af8BfwH/AX8B/wF/Af8BfwEAAXwBAAF8AQAB + fAEAAXwBAAF8AQABfAEAAXwBAAF8AQABQAEQAUICAAEQAUIB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8BHwEAAR8BAAEfAQABHwEAAR8BAAH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/AgABEAFCARABQg8AAUIB4AF/AeABfwHgAX8B4AF/CgAB + 4AF/AeABfwHgAX8BGAFjAgABEAFCARABQhAAARABQgH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwEYAWMBHwEAAR8BAAEYAWMB/wF/AR8BAAEfAQABHwEAAR8BAAH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8CAAEQAUIFAAFAAQABfAEAAXwBAAF8AQABfAEAAXwBAAF8Af8B + fwH/AX8B/wF/Af8BfwH/AX8BAAF8AQABfAEAAXwBAAF8Af8BfwH/AX8B/wF/Af8BfwH/AX8BAAF8AQAB + fAEAAXwBAAF8AQABfAEAAXwBAAFAARABQgEQAUICAAEQAUIB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/AR8BAAEfAQABHwEAAR8BAAEfAQABHwEAAR8BAAH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/AgABEAFCEwABQgHgAX8B4AF/AeABfwEYAWMGAAEYAWMB + 4AF/AeABfwHgAX8CAAEQAUIBEAFCEgABEAFCARgBYwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/AR8BAAEfAQABHwEAAR8BAAH/AX8BGAFjAR8BAAEfAQABHwEAAR8BAAH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/ARgBYwIAARABQgUAAUABAAF8AQABfAEAAXwBAAF8AQABfAH/AX8B/wF/Af8B + fwH/AX8B/wF/AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8Af8BfwH/AX8B/wF/Af8BfwH/AX8BAAF8AQAB + fAEAAXwBAAF8AQABfAEAAUABEAFCBAABEAFCARgBYwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/ARgBYwIAARABQhMAAUIB4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeAB + fwHgAX8B4AF/ARgBYwIAARABQgEQAUIUAAEQAUIB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwEfAQABHwEAAR8BAAEfAQAB/wF/Af8BfwEfAQABHwEAAR8BAAEfAQAB/wF/Af8BfwH/AX8B/wF/Af8B + fwH/AX8B/wF/Af8BfwIAARABQgcAAUABAAF8AQABfAEAAXwBAAF8AQABfAEAAXwB/wF/Af8BfwH/AX8B + AAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAH/AX8B/wF/Af8BfwEAAXwBAAF8AQABfAEAAXwB + AAF8AQABfAEAAUAIAAEQAUIB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwIAARABQhcAAUIB4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeABfwHgAX8CAAEQAUIBEAFCFgAB + EAFCARgBYwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8BHwEAAR8BAAH/AX8B/wF/Af8BfwH/AX8B + HwEAAR8BAAEfAQABHwEAAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwEYAWMNAAFAAQABfAEAAXwB + AAF8AQABfAEAAXwBAAF8Af8BfwEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwB + /wF/AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABQAEQAUIIAAEQAUIBGAFjAf8BfwH/AX8B/wF/Af8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/ARgBYwEfAQABHwEAAR8BAAEfAQABGAFjAf8BfwH/AX8B/wF/Af8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/ARgBYxsAAUIB4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AeAB + fwEYAWMCAAEQAUIBEAFCGAABEAFCAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwEYAWMBHwEAARgB + YwH/AX8B/wF/AR8BAAEfAQABHwEAAR8BAAEYAWMB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/EQAB + QAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQAB + fAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAFAARABQgwAARABQgH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwEfAQABHwEAAR8BAAEfAQABHwEAAR8BAAH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8Bfx8AAUIB4AF/AeABfwHgAX8B4AF/AeABfwHgAX8B4AF/AgABEAFCARAB + QhwAARABQgH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8BGAFjAR8BAAEfAQABHwEAAR8BAAEfAQAB + HwEAARgBYwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8TAAFAAQABfAEAAXwBAAF8AQABfAEAAXwB + AAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwB + AAF8AQABfAEAAUAQAAEQAUIB/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwEfAQABHwEAAR8B + AAEfAQABHwEAAR8BAAH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/IQABQgHgAX8B4AF/AeAB + fwHgAX8B4AF/AeABfwEYAWMCAAEQAUIBEAFCHgABEAFCAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8XAAFAAQAB + fAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQAB + fAEAAXwBAAF8AQABfAEAAXwBAAFAFAABEAFCAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwEYAWMB + HwEAAR8BAAEfAQABHwEAARgBYwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8lAAFCAeABfwHgAX8B + 4AF/AeABfwHgAX8CAAEQAUIBEAFCIgABEAFCARgBYwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8BGAFjARABQhkAAUABAAFAAQABfAEAAXwB + AAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAUAB + AAFAGAABEAFCARgBYwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8B + fwH/AX8B/wF/Af8BfwH/AX8BGAFjARABQiUAAUIB4AF/AeABfwHgAX8B4AF/ARgBYwIAARABQiYAARAB + QgEQAUIBGAFjAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/ARgB + YwEQAUIBEAFCHwABQAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAXwBAAF8AQAB + fAEAAXwBAAF8AQABQB4AARABQgEQAUIBGAFjAf8BfwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwH/AX8B + /wF/Af8BfwH/AX8B/wF/ARgBYwEQAUIBEAFCKQABQgHgAX8B4AF/ARgBYzAAARABQgEQAUIBEAFCARgB + YwH/AX8B/wF/Af8BfwH/AX8B/wF/Af8BfwEYAWMBEAFCARABQgEQAUIlAAFAAQABQAEAAUABAAF8AQAB + fAEAAXwBAAF8AQABfAEAAXwBAAF8AQABfAEAAUABAAFAAQABQCQAARABQgEQAUIBEAFCARgBYwH/AX8B + /wF/Af8BfwH/AX8B/wF/Af8BfwEYAWMBEAFCARABQgEQAUIvAAFCAQABQgEAAUI2AAEQAUIBEAFCARAB + QgEQAUIBEAFCARABQgEQAUIBEAFCMQABQAEAAUABAAFAAQABQAEAAUABAAFAAQABQAEAAUAwAAEQAUIB + EAFCARABQgEQAUIBEAFCARABQgEQAUIBEAFCGgABQgFNAT4HAAE+AwABKAMAAYADAAFAAwABAQEAAQEG + AAEEFgAD//8A/wADAAH4AgABAwL/AecC/wH4AQcD/wHnAf8B8AIAAQEC/wHHAv8BwAEAA/8BxwH/AcAD + AAL/AYcC/wGAAQABfwL/AYcB/wGAAwAC/wEHAf8B/gIAAR8C/wEHAf8EAAH/Af4BBwH/AfwCAAEPAf8B + /gEHAf8EAAH/AfgBBwH/AfgCAAEHAf8B+AEHAf8DAAEBAf8BwAEAAf8B8AIAAQcB/wHAAQAB/wMAAQEB + /wIAAT8B4AIAAQMB/wIAAT8BgAIAAQMB/gIAAR8B4AIAAQEB/gIAAR8BgAIAAQMB/AIAAQ8BwAIAAQEB + /AIAAQ8BwAIAAQcB+AIAAQcBgAIAAQEB+AIAAQcBwAIAAQcB8AIAAQMBgAMAAfACAAEDAeACAAEPAeAC + AAEBAYADAAHgAgABAQHgAgABDwHAAgABAQQAAcACAAEBAfACAAEfAYAHAAGAAwAB8AIAAR8BgAcAAYAD + AAH4AgABPwwAAfgCAAE/DAAB/AIAAX8MAAH8AgABfwcAAQEEAAH+AgAB/wcAAQEEAAH+AgAB/wMAAQEB + gAIAAQEDAAEBAf8BAAEBAf8DAAEBAYACAAEDAwABAQH/AQABAQH/AYACAAEDAYACAAEHAYACAAEDAf8B + gAEDAf8BgAIAAQcBwAIAAQcBgAIAAQcB/wGAAQMB/wHAAgABDwHgAgABDwHAAgABDwH/AcABBwH/AeAC + AAEfAeACAAEfAeACAAEfAf8BwAEHAf8B8AIAAT8B8AIAAT8B8AIAAT8B/wHgAQ8B/wH4AgABfwH4AgAB + fwH4AgABfwH/AeABHwH/AfwCAAH/Af4BAAEBAf8B/AIAAv8B8AF/Av8BAAEDAv8BAAEDAv8BAAEDAv8B + +AP/AeABHwL/AeABHwL/AeABHwH/FgAL + + + + Private + + + 142, 17 + + + Private + + + False + + + Private + + + Private + + + False + + + (Default) + + + False + + + False + + + 8, 8 + + + True + + + 80 + + + MessageBoxExForm + + + True + + + Private + + \ No newline at end of file diff --git a/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExIcon.cs b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExIcon.cs new file mode 100644 index 0000000..e39261b --- /dev/null +++ b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExIcon.cs @@ -0,0 +1,20 @@ +using System; + +namespace CommonLib.Windows.Forms +{ + /// + /// Standard MessageBoxEx icons + /// + public enum MessageBoxExIcon + { + None, + Asterisk, + Error, + Exclamation, + Hand, + Information, + Question, + Stop, + Warning + } +} diff --git a/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExManager.cs b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExManager.cs new file mode 100644 index 0000000..abb0185 --- /dev/null +++ b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExManager.cs @@ -0,0 +1,218 @@ +using System; +using System.IO; +using System.Collections; +using System.Windows.Forms; +using System.Resources; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace CommonLib.Windows.Forms +{ + /// + /// Manages a collection of MessageBoxes. Basically manages the + /// saved response handling for messageBoxes. + /// + public class MessageBoxExManager + { + #region Fields + private static Hashtable _messageBoxes = new Hashtable(); + private static Hashtable _savedResponses = new Hashtable(); + + private static Hashtable _standardButtonsText = new Hashtable(); + #endregion + + #region Static ctor + static MessageBoxExManager() + { + try + { + Assembly current = typeof(MessageBoxExManager).Assembly; + string[] resources = current.GetManifestResourceNames(); + string standardButtonsTextResourceName = ""; + Match SPMatch; + foreach (string resource in resources) + { + SPMatch = Regex.Match(resource,@"^(.+StandardButtonsText)\.resources$", RegexOptions.IgnoreCase); + if (SPMatch.Success && SPMatch.Groups.Count > 1) + { + standardButtonsTextResourceName = SPMatch.Groups[1].Value; + break; + } + } + ResourceManager rm = new ResourceManager(standardButtonsTextResourceName, typeof(MessageBoxExManager).Assembly); + _standardButtonsText[MessageBoxExButtons.Ok.ToString()] = rm.GetString("Ok"); + _standardButtonsText[MessageBoxExButtons.Cancel.ToString()] = rm.GetString("Cancel"); + _standardButtonsText[MessageBoxExButtons.Yes.ToString()] = rm.GetString("Yes"); + _standardButtonsText[MessageBoxExButtons.No.ToString()] = rm.GetString("No"); + _standardButtonsText[MessageBoxExButtons.Abort.ToString()] = rm.GetString("Abort"); + _standardButtonsText[MessageBoxExButtons.Retry.ToString()] = rm.GetString("Retry"); + _standardButtonsText[MessageBoxExButtons.Ignore.ToString()] = rm.GetString("Ignore"); + } + catch(Exception ex) + { + System.Diagnostics.Debug.Assert(false, "Unable to load resources for MessageBoxEx", ex.ToString()); + + //Load default resources + _standardButtonsText[MessageBoxExButtons.Ok.ToString()] = "Ok"; + _standardButtonsText[MessageBoxExButtons.Cancel.ToString()] = "Cancel"; + _standardButtonsText[MessageBoxExButtons.Yes.ToString()] = "Yes"; + _standardButtonsText[MessageBoxExButtons.No.ToString()] = "No"; + _standardButtonsText[MessageBoxExButtons.Abort.ToString()] = "Abort"; + _standardButtonsText[MessageBoxExButtons.Retry.ToString()] = "Retry"; + _standardButtonsText[MessageBoxExButtons.Ignore.ToString()] = "Ignore"; + } + } + #endregion + + #region Methods + /// + /// Creates a new message box with the specified name. If null is specified + /// in the message name then the message box is not managed by the Manager and + /// will be disposed automatically after a call to Show() + /// + /// The name of the message box + /// A new message box + public static MessageBoxEx CreateMessageBox(Form parentForm, string name) + { + if(name != null && _messageBoxes.ContainsKey(name)) + { + string err = string.Format("A MessageBox with the name {0} already exists.",name); + throw new ArgumentException(err,"name"); + } + + MessageBoxEx msgBox = new MessageBoxEx(parentForm); + msgBox.Name = name; + if(msgBox.Name != null) + { + _messageBoxes[name] = msgBox; + } + + return msgBox; + } + + /// + /// Gets the message box with the specified name + /// + /// The name of the message box to retrieve + /// The message box with the specified name or null if a message box + /// with that name does not exist + public static MessageBoxEx GetMessageBox(string name) + { + if(_messageBoxes.Contains(name)) + { + return _messageBoxes[name] as MessageBoxEx; + } + else + { + return null; + } + } + + /// + /// Deletes the message box with the specified name + /// + /// The name of the message box to delete + public static void DeleteMessageBox(string name) + { + if(name == null) + return; + + if(_messageBoxes.Contains(name)) + { + MessageBoxEx msgBox = _messageBoxes[name] as MessageBoxEx; + msgBox.Dispose(); + _messageBoxes.Remove(name); + } + } + + public static void WriteSavedResponses(Stream stream) + { + throw new NotImplementedException("This feature has not yet been implemented"); + } + + public static void ReadSavedResponses(Stream stream) + { + throw new NotImplementedException("This feature has not yet been implemented"); + } + + /// + /// Reset the saved response for the message box with the specified name. + /// + /// The name of the message box whose response is to be reset. + public static void ResetSavedResponse(string messageBoxName) + { + if(messageBoxName == null) + return; + + if(_savedResponses.ContainsKey(messageBoxName)) + { + _savedResponses.Remove(messageBoxName); + } + } + + /// + /// Resets the saved responses for all message boxes that are managed by the manager. + /// + public static void ResetAllSavedResponses() + { + _savedResponses.Clear(); + } + #endregion + + #region Internal Methods + /// + /// Set the saved response for the specified message box + /// + /// The message box whose response is to be set + /// The response to save for the message box + internal static void SetSavedResponse(MessageBoxEx msgBox, string response) + { + if(msgBox.Name == null) + return; + + _savedResponses[msgBox.Name] = response; + } + + /// + /// Gets the saved response for the specified message box + /// + /// The message box whose saved response is to be retrieved + /// The saved response if exists, null otherwise + internal static string GetSavedResponse(MessageBoxEx msgBox) + { + string msgBoxName = msgBox.Name; + if(msgBoxName == null) + { + return null; + } + + if(_savedResponses.ContainsKey(msgBoxName)) + { + return _savedResponses[msgBox.Name].ToString(); + } + else + { + return null; + } + } + + /// + /// Returns the localized string for standard button texts like, + /// "Ok", "Cancel" etc. + /// + /// + /// + internal static string GetLocalizedString(string key) + { + if(_standardButtonsText.ContainsKey(key)) + { + return (string)_standardButtonsText[key]; + } + else + { + return null; + } + } + #endregion + } +} diff --git a/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExResult.cs b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExResult.cs new file mode 100644 index 0000000..b857f67 --- /dev/null +++ b/CommonLib/Library/Windows/Forms/MessageBoxExLib/MessageBoxExResult.cs @@ -0,0 +1,19 @@ +using System; + +namespace CommonLib.Windows.Forms +{ + /// + /// Standard MessageBoxEx results + /// + public struct MessageBoxExResult + { + public const string Ok = "Ok"; + public const string Cancel = "Cancel"; + public const string Yes = "Yes"; + public const string No = "No"; + public const string Abort = "Abort"; + public const string Retry = "Retry"; + public const string Ignore = "Ignore"; + public const string Timeout = "Timeout"; + } +} diff --git a/CommonLib/Library/Windows/Forms/MessageBoxExLib/Resources/StandardButtonsText.de.resx b/CommonLib/Library/Windows/Forms/MessageBoxExLib/Resources/StandardButtonsText.de.resx new file mode 100644 index 0000000..792d589 --- /dev/null +++ b/CommonLib/Library/Windows/Forms/MessageBoxExLib/Resources/StandardButtonsText.de.resx @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + O.K. + + + Löschen + + + Ja + + + Nein + + + Abbruch + + + Wiederholung + + + Ignorieren + + \ No newline at end of file diff --git a/CommonLib/Library/Windows/Forms/MessageBoxExLib/Resources/StandardButtonsText.fr.resx b/CommonLib/Library/Windows/Forms/MessageBoxExLib/Resources/StandardButtonsText.fr.resx new file mode 100644 index 0000000..9f82ebe --- /dev/null +++ b/CommonLib/Library/Windows/Forms/MessageBoxExLib/Resources/StandardButtonsText.fr.resx @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Ok + + + Annuler + + + Oui + + + Non + + + Arrêt + + + Nouvelle + + + Ignorez + + \ No newline at end of file diff --git a/CommonLib/Library/Windows/Forms/MessageBoxExLib/Resources/StandardButtonsText.resx b/CommonLib/Library/Windows/Forms/MessageBoxExLib/Resources/StandardButtonsText.resx new file mode 100644 index 0000000..af43d13 --- /dev/null +++ b/CommonLib/Library/Windows/Forms/MessageBoxExLib/Resources/StandardButtonsText.resx @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Ok + + + Cancel + + + Yes + + + No + + + Abort + + + Retry + + + Ignore + + \ No newline at end of file diff --git a/CommonLib/Library/Windows/Forms/MessageBoxExLib/TimeoutResult.cs b/CommonLib/Library/Windows/Forms/MessageBoxExLib/TimeoutResult.cs new file mode 100644 index 0000000..e8636b9 --- /dev/null +++ b/CommonLib/Library/Windows/Forms/MessageBoxExLib/TimeoutResult.cs @@ -0,0 +1,29 @@ +using System; + +namespace CommonLib.Windows.Forms +{ + /// + /// Enumerates the kind of results that can be returned when a + /// message box times out + /// + public enum TimeoutResult + { + /// + /// On timeout the value associated with the default button is set as the result. + /// This is the default action on timeout. + /// + Default, + + /// + /// On timeout the value associated with the cancel button is set as the result. If + /// the messagebox does not have a cancel button then the value associated with + /// the default button is set as the result. + /// + Cancel, + + /// + /// On timeout MessageBoxExResult.Timeout is set as the result. + /// + Timeout + } +} diff --git a/CommonLib/Library/Windows/Forms/frmChildForm.cs b/CommonLib/Library/Windows/Forms/frmChildForm.cs new file mode 100644 index 0000000..fe2b919 --- /dev/null +++ b/CommonLib/Library/Windows/Forms/frmChildForm.cs @@ -0,0 +1,184 @@ +///====================================================================================== +/// File: frmChildForm.cs +/// Created: 06/06/12 +///====================================================================================== +/// +/// A base child form with unique characteristics +/// +/// Date Programmer Proj.ID SAR REVISION HISTORY: +/// --/--/-- ----------- ----------- ----- ------------------------------------------ +/// 06/06/12 D.Le +///====================================================================================== +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Threading; + +namespace CommonLib.Windows.Forms +{ + ///====================================================================================== + /// ChildForm + ///====================================================================================== + /// + /// This class is a base class for any child form that has a calling parent form. Any + /// class inheriting this class will be able to display the child form at the center + /// of the parent form by notifying the child form of its parent form + /// + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ----------- ----------- ----- ------------------------------------------ + /// 06/06/12 D.Le + ///====================================================================================== + public partial class ChildForm : Form + { + //property + public Form parentForm + { + get; + set; + } + + public ChildForm() + { + InitializeComponent(); + + parentForm = null; + } + + ///========================================================================== + /// ChildForm.OnLoad + ///========================================================================== + /// + /// Form's OnLoad Event Handler. If the parent form is passed in then display + /// this form at the center of the parent form + /// + /// + /// void + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + protected override void OnLoad(EventArgs e) + { + if (parentForm != null) + CenterForm(parentForm); + base.OnLoad(e); + } + + ///========================================================================== + /// ChildForm.OnLoad + ///========================================================================== + /// + /// Centers the form relative to the parent form. Also moves the form into + /// visible area of the screen if it's determined that an area of the form + /// is position outside of the screen's visible area + /// + /// The parent form that calls this form + /// void + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + private void CenterForm(Form parentForm) + { + List allScreens = new List(); + int parentCenterX, parentCenterY, childTopX, childTopY, + childVisibleTopX, childVisibleBottomX, + screenVisibleBottomX, screenVisibleBottomY, rectWidth, trueChildWidth, screensCount=0; + + // create the smallest rectangle + Rectangle rect = new Rectangle(int.MaxValue, int.MaxValue, int.MinValue, int.MinValue); + + // determines the coordinate of the center of the parent form + // that will be used as the center of the pop-up box + parentCenterX = parentForm.Left + parentForm.Width / 2; + parentCenterY = parentForm.Top + parentForm.Height / 2; + + childTopX = parentCenterX - this.Width / 2; + childTopY = parentCenterY - this.Height / 2; + + // if the x coordinate is off the screen to the left, we move it to the visible area of screen + if (childTopX < 0) + { + trueChildWidth = childTopX + this.Width; + childTopX = 0; + } + else + trueChildWidth = this.Width; + + // if the y coordinate is off the screen up above, we move it to the visible area of screen + if (childTopY < 0) + childTopY = 0; + + foreach (Screen screen in Screen.AllScreens) + { + if (screen.Primary) + allScreens.Insert(0, screen); + else + allScreens.Add(screen); + } + + foreach (Screen screen in allScreens) + { + screensCount++; + + if (rect.Width < 0) + rectWidth = 0; + else + rectWidth = rect.Width; + + rect = Rectangle.Union(rect, screen.Bounds); // join screens + + // if we're on the last screen + if ( Screen.AllScreens.Length == screensCount ) + { + // if the x coordinate is off the screen to the right, we move it to the visible area of screen + if ((childTopX + this.Width) > rect.Width) + { + trueChildWidth = rect.Width - childTopX; + } + } + + screenVisibleBottomX = screen.WorkingArea.X + screen.WorkingArea.Width; + screenVisibleBottomY = screen.WorkingArea.Y + screen.WorkingArea.Height; + + if (childTopX <= rectWidth) + { + childVisibleTopX = rectWidth + 1; + } + else + childVisibleTopX = childTopX; + + if ((childTopX + trueChildWidth) > (rectWidth + screen.Bounds.Width)) + childVisibleBottomX = rectWidth + screen.Bounds.Width; + else + childVisibleBottomX = childTopX + trueChildWidth; + + // determine if the visible width of the form is 50% or greater, then we choose + // to display this form on this screen + if ( Convert.ToInt32(Math.Ceiling(((double)(childVisibleBottomX - childVisibleTopX) / (double)trueChildWidth)*100.0)) >= 50 ) + { + if (childTopX < screen.WorkingArea.X) + childTopX = screen.WorkingArea.X; + else if ((childTopX + this.Width) > screenVisibleBottomX) + childTopX -= (childTopX + this.Width) - screenVisibleBottomX; + + if (childTopY < screen.WorkingArea.Y) + childTopY = screen.WorkingArea.Y; + else if ((childTopY + this.Height) > screenVisibleBottomY) + childTopY -= (childTopY + this.Height) - screenVisibleBottomY; + + break; + } + } + + this.Location = new Point(childTopX, childTopY); + } + } +} diff --git a/CommonLib/Library/Windows/Forms/frmChildForm.designer.cs b/CommonLib/Library/Windows/Forms/frmChildForm.designer.cs new file mode 100644 index 0000000..e1e0483 --- /dev/null +++ b/CommonLib/Library/Windows/Forms/frmChildForm.designer.cs @@ -0,0 +1,38 @@ +namespace CommonLib.Windows.Forms +{ + partial class ChildForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Text = "Forms"; + } + + #endregion + } +} \ No newline at end of file diff --git a/CommonLib/Library/Windows/Misc/NetworkManagement.cs b/CommonLib/Library/Windows/Misc/NetworkManagement.cs new file mode 100644 index 0000000..f5a280a --- /dev/null +++ b/CommonLib/Library/Windows/Misc/NetworkManagement.cs @@ -0,0 +1,612 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Net.NetworkInformation; +using System.Management; +using System.Runtime.InteropServices; +using System.IO; +using Microsoft.Win32; +using System.Text.RegularExpressions; +using System.Security.Principal; +using System.Security.AccessControl; + +using CommonLib.IO; +using CommonLib.Misc; +using CommonLib.Diagnostics; + +namespace CommonLib.Windows.Misc +{ + public class NetworkManagement + { + //////========================================================================== + /// NetworkSharing.GetNetworkAdapterAndConfigurationBasedOnPciLocation + ///========================================================================== + /// + /// Given PCI Location (bus,device,function), obtain references to objects of + /// type Win32_NetworkAdapter and Win32_NetworkAdapterConfiguration. + /// These two object provides us with a wide array of information about the + /// network adapter + /// + /// + /// This object must be of type Win32_NetworkAdapter + /// + /// + /// This object must be of type Win32_NetworkAdapterConfiguration + /// + /// + /// Must be in this format: #,#,# (bus,device,function) + /// i.e.: 61,7,2 + /// Every PCI network adapter has a unique location. + /// + /// error message returned if error encountered + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 12/07/16 D.Le + ///========================================================================== + public static bool GetNetworkAdapterAndConfigurationBasedOnPciLocation(ref ManagementObject networkAdapter, ref ManagementObject networkAdapterConfiguration, string expectedPciLocation, ref string errMsg) + { + bool successful = true; + errMsg = ""; + + networkAdapter = null; + networkAdapterConfiguration = null; + + ManagementObjectSearcher mos = null; + + Match regExMatch; + string valueName = "LocationInformation"; + string registryValueNameValue = ""; + string actualPciLocationInfo = ""; + + try + { + if (expectedPciLocation.Length == 0) + { + throw new Exception("PCI Location cannot be empty. Provide PCI Location in the form: #,#,# representing bus,device,function"); + } + else + { + expectedPciLocation = Regex.Replace(expectedPciLocation, @"\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*", "$1,$2,$3", RegexOptions.IgnoreCase); + } + + mos = new ManagementObjectSearcher(@"SELECT * + FROM Win32_NetworkAdapter + WHERE Manufacturer != 'Microsoft' + AND NOT PNPDeviceID LIKE 'ROOT\\%'"); + + if (mos == null) + { + throw new Exception("No network adapter found in the system"); + } + + foreach (ManagementObject adapter in mos.Get()) + { + // print out all the properties name/value for each adapter + //foreach (PropertyData pd in adapter.Properties) + // Console.WriteLine(pd.Name + ": " + (pd.Value ?? "N/A")); + + //Console.WriteLine("Index: {0}, Name: {1}", adapter["DeviceID"],adapter["Name"]); + + string pnpDeviceIdRegistryPath = @"SYSTEM\CurrentControlSet\Enum\" + adapter["PNPDeviceID"]; + + RegistryKey hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); + RegistryKey rkSubKey = hklm.OpenSubKey(pnpDeviceIdRegistryPath, false); + + List list = new List(rkSubKey.GetValueNames()); + + // if there's a name "LocationInformation" found in the registry key + if (list.FindIndex(x => x.Equals(valueName, StringComparison.OrdinalIgnoreCase)) != -1) + { + // get the value associated with LocationInformation + registryValueNameValue = rkSubKey.GetValue(valueName).ToString(); + + regExMatch = Regex.Match(registryValueNameValue, @".+\((\d+,\d+,\d+)\)$", RegexOptions.IgnoreCase); + + if (regExMatch.Success) + { + // get actual pci location in this format #,#,# which corresponds to bus,device,function + actualPciLocationInfo = regExMatch.Groups[1].Value; + } + } + + if (expectedPciLocation == actualPciLocationInfo) + { + networkAdapter = adapter; + + foreach (ManagementObject configuration in + adapter.GetRelated("Win32_NetworkAdapterConfiguration")) + { + // print out all the properties name/value for each configuration + //foreach (PropertyData pd in configuration.Properties) + // Console.WriteLine(pd.Name + ": " + (pd.Value ?? "N/A")); + + networkAdapterConfiguration = configuration; + } + + break; + } + } + + if (networkAdapterConfiguration == null) + { + throw new Exception("No network adapter found at PCI Location: " + expectedPciLocation); + } + + } + catch (Exception ex) + { + errMsg = ex.Message; + successful = false; + } + + return successful; + } + + //////========================================================================== + /// NetworkSharing.SetNetworkAdapterForDhcp + ///========================================================================== + /// + /// Set network adapter to use DHCP. This is the same thing as setting the adapter + /// to obtain IP and DNS automatically + /// + /// + /// This object must be of type Win32_NetworkAdapter + /// + /// + /// This object must be of type Win32_NetworkAdapterConfiguration + /// + /// error message returned if error encountered + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 12/07/16 D.Le + ///========================================================================== + public static bool SetNetworkAdapterForDhcp(ManagementObject networkAdapter, ManagementObject networkAdapterConfiguration, ref string errMsg) + { + bool successful = true; + errMsg = ""; + + try + { + if (networkAdapter == null) + { + throw new Exception("Object networkAdapter cannot be null"); + } + else if (networkAdapterConfiguration == null) + { + throw new Exception("Object adapterNetworkConfiguration cannot be null"); + } + + ManagementBaseObject setDhcp; + + setDhcp = networkAdapterConfiguration.InvokeMethod("EnableDHCP", null, null); + + uint returnedVal = UInt32.Parse(setDhcp["returnValue"].ToString()); + + // failed because the adapter is disconnected + if (returnedVal == 84) + { + string adapterGuid = @"SYSTEM\CurrentControlSet\services\Tcpip\Parameters\Interfaces\" + networkAdapter["GUID"]; + + successful = WindowsRegistry.AddOrModifyRegistryKeySingleValue(adapterGuid, RegistryHive.LocalMachine, "EnableDHCP", RegistryValueKind.String, "1", ref errMsg); + + if (!successful) + throw new Exception(errMsg); + + // set dns server to be obtained automatically + successful = WindowsRegistry.AddOrModifyRegistryKeySingleValue(adapterGuid, RegistryHive.LocalMachine, "NameServer", RegistryValueKind.String, "", ref errMsg); + + if (!successful) + throw new Exception(errMsg); + + successful = WindowsRegistry.DeleteRegistryValueName(adapterGuid, RegistryHive.LocalMachine, "DisableDhcpOnConnect", ref errMsg); + + if (!successful) + throw new Exception(errMsg); + + successful = WindowsRegistry.DeleteRegistryValueName(adapterGuid, RegistryHive.LocalMachine, "DefaultGateway", ref errMsg); + + if (!successful) + throw new Exception(errMsg); + + successful = WindowsRegistry.DeleteRegistryValueName(adapterGuid, RegistryHive.LocalMachine, "DefaultGatewayMetric", ref errMsg); + + if (!successful) + throw new Exception(errMsg); + + successful = WindowsRegistry.DeleteRegistryValueName(adapterGuid, RegistryHive.LocalMachine, "IPAddress", ref errMsg); + + if (!successful) + throw new Exception(errMsg); + + successful = WindowsRegistry.DeleteRegistryValueName(adapterGuid, RegistryHive.LocalMachine, "SubnetMask", ref errMsg); + + if (!successful) + throw new Exception(errMsg); + } + else if (returnedVal != 0) + { + throw new Exception("Unable to enable DHCP for this network adapter. Error code: " + setDhcp["returnValue"]); + } + } + catch (Exception ex) + { + errMsg = ex.Message; + successful = false; + } + + return successful; + } + + //////========================================================================== + /// NetworkSharing.SetNetworkAdapterIpAddress + ///========================================================================== + /// + /// Set network adapter's IP address and subnet mask + /// + /// + /// This object must be of type Win32_NetworkAdapterConfiguration + /// + /// i.e 192.168.0.1 + /// i.e 255.255.255.0 + /// error message returned if error encountered + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 12/07/16 D.Le + ///========================================================================== + public static bool SetNetworkAdapterIpAddress(ManagementObject networkAdapterConfiguration, string ipAddress, string subnetMask, ref string errMsg) + { + bool successful = true; + errMsg = ""; + + try + { + if (!StringManip.IsValidIPv4(ipAddress)) + { + throw new Exception(ipAddress + " is an invalid IPv4 Address"); + } + else if (!StringManip.IsValidIPv4(subnetMask)) + { + throw new Exception(subnetMask + " is an invalid IPv4 Address"); + } + + ManagementBaseObject setIP; + ManagementBaseObject newIP = + networkAdapterConfiguration.GetMethodParameters("EnableStatic"); + + newIP["IPAddress"] = new string[] { ipAddress }; + newIP["SubnetMask"] = new string[] { subnetMask }; + + setIP = networkAdapterConfiguration.InvokeMethod("EnableStatic", newIP, null); + + uint returnedVal = UInt32.Parse(setIP["returnValue"].ToString()); + + if (returnedVal != 0) + { + throw new Exception("Unable to change IP address/subnet mask for this network adapter. Error code: " + setIP["returnValue"]); + } + } + catch (Exception ex) + { + errMsg = ex.Message; + successful = false; + } + + return successful; + } + + //////========================================================================== + /// NetworkSharing.SetNetworkAdapterDefaultGateway + ///========================================================================== + /// + /// Set default gateway for network adapter + /// + /// + /// This object must be of type Win32_NetworkAdapterConfiguration + /// + /// + /// IP address for the gateway + /// + /// error message returned if error encountered + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 12/07/16 D.Le + ///========================================================================== + public static bool SetNetworkAdapterDefaultGateway(ManagementObject networkAdapterConfiguration, string defaultGateway, ref string errMsg) + { + bool successful = true; + errMsg = ""; + + try + { + if (!StringManip.IsValidIPv4(defaultGateway)) + { + throw new Exception(defaultGateway + " is an invalid IPv4 IP Address"); + } + + ManagementBaseObject setGateway; + ManagementBaseObject newGateway = networkAdapterConfiguration.GetMethodParameters("SetGateways"); + + newGateway["DefaultIPGateway"] = new string[] { defaultGateway }; + newGateway["GatewayCostMetric"] = new int[] { 1 }; + + setGateway = networkAdapterConfiguration.InvokeMethod("SetGateways", newGateway, null); + + uint returnedVal = UInt32.Parse(setGateway["returnValue"].ToString()); + + if (returnedVal != 0) + { + throw new Exception("Unable to change default gateway for this network adapter. Error code: " + setGateway["returnValue"]); + } + } + catch (Exception ex) + { + errMsg = ex.Message; + successful = false; + } + + return successful; + } + + //////========================================================================== + /// NetworkSharing.SetNetworkAdapterDnsServers + ///========================================================================== + /// + /// Set DNS servers for the network adapter + /// + /// + /// This object must be of type Win32_NetworkAdapter + /// + /// + /// This object must be of type Win32_NetworkAdapterConfiguration + /// + /// + /// Must provide at least one IP address for DNS server, maximum is 2 IP addresses + /// + /// error message returned if error encountered + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 12/07/16 D.Le + ///========================================================================== + public static bool SetNetworkAdapterDnsServers(ManagementObject networkAdapter, ManagementObject networkAdapterConfiguration, List dnsServerList, ref string errMsg) + { + bool successful = true; + errMsg = ""; + string nameServer = ""; + + try + { + if (networkAdapter == null) + { + throw new Exception("Object networkAdapter cannot be null"); + } + else if (networkAdapterConfiguration == null) + { + throw new Exception("Object adapterNetworkConfiguration cannot be null"); + } + else if (dnsServerList.Count == 0 || dnsServerList == null) + { + throw new Exception("DNS Server information not provided"); + } + + for (int i = 0; i < dnsServerList.Count; i++) + { + if (!StringManip.IsValidIPv4(dnsServerList[i].Trim())) + { + throw new Exception(dnsServerList[i].Trim() + " is an invalid IPv4 IP Address"); + } + + if (nameServer.Length == 0) + nameServer = dnsServerList[i].Trim(); + else + nameServer += "," + dnsServerList[i].Trim(); ; + } + + string[] dnsServers = dnsServerList.ToArray(); + ManagementBaseObject newDNS = + networkAdapterConfiguration.GetMethodParameters("SetDNSServerSearchOrder"); + newDNS["DNSServerSearchOrder"] = dnsServers; + ManagementBaseObject setDNS = + networkAdapterConfiguration.InvokeMethod("SetDNSServerSearchOrder", newDNS, null); + + uint returnedVal = UInt32.Parse(setDNS["returnValue"].ToString()); + + // failed because the adapter is disconnected + if (returnedVal == 84) + { + string adapterGuid = @"SYSTEM\CurrentControlSet\services\Tcpip\Parameters\Interfaces\" + networkAdapter["GUID"]; + + successful = WindowsRegistry.AddOrModifyRegistryKeySingleValue(adapterGuid, RegistryHive.LocalMachine, "NameServer", RegistryValueKind.String, nameServer, ref errMsg); + + if (!successful) + throw new Exception(errMsg); + } + else if (returnedVal != 0) + { + throw new Exception("Unable to change default gateway for this network adapter. Error code: " + setDNS["returnValue"]); + } + } + catch (Exception ex) + { + errMsg = ex.Message; + successful = false; + } + + return successful; + } + + //////========================================================================== + /// NetworkSharing.ChangeNetworkAdapterName + ///========================================================================== + /// + /// Change the name of the network adapter + /// + /// + /// This object must be of type Win32_NetworkAdapter + /// + /// + /// This object must be of type Win32_NetworkAdapterConfiguration + /// + /// + /// Name to give the network adapter + /// + /// error message returned if error encountered + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 12/07/16 D.Le + ///========================================================================== + public static bool ChangeNetworkAdapterName(ManagementObject networkAdapter, ManagementObject networkAdapterConfiguration, string adapterName, ref string errMsg) + { + bool successful = true; + errMsg = ""; + string uniqueID = ""; + + try + { + if (networkAdapter == null) + { + throw new Exception("Object networkAdapter cannot be null"); + } + else if (networkAdapterConfiguration == null) + { + throw new Exception("Object adapterNetworkConfiguration cannot be null"); + } + else if (adapterName.Length == 0) + { + throw new Exception("adapterName cannot be empty"); + } + + string adapterGuid = @"SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\" + networkAdapter["GUID"] + @"\Connection"; + + uniqueID = Guid.NewGuid().ToString(); + successful = WindowsRegistry.AddOrModifyRegistryKeySingleValue(adapterGuid, RegistryHive.LocalMachine, "Name", RegistryValueKind.String, uniqueID, ref errMsg); + + if (!successful) + throw new Exception(errMsg); + + ProcessStarter proc = new ProcessStarter(); + proc.fileName = @"C:\Windows\System32\netsh.exe"; + proc.procArguments = "interface set interface name=\"" + uniqueID + "\" newname=\"" + adapterName + "\""; + proc.Run(); + + if (proc.procExitCode != 0) + { + throw new Exception("Process netsh.exe returned the following error\n" + proc.outputMsg); + } + } + catch (Exception ex) + { + errMsg = ex.Message; + successful = false; + } + + return successful; + } + + //////========================================================================== + /// NetworkSharing.SetNetworkAdapterLinkSpeed + ///========================================================================== + /// + /// Set network adapter to use DHCP. This is the same thing as setting the adapter + /// to obtain IP and DNS automatically + /// + /// + /// This object must be of type Win32_NetworkAdapter + /// + /// + /// This object must be of type Win32_NetworkAdapterConfiguration + /// + /// + /// Value of the link speed + /// + /// error message returned if error encountered + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 12/07/16 D.Le + ///========================================================================== + public static bool SetNetworkAdapterLinkSpeed(ManagementObject networkAdapter, ManagementObject networkAdapterConfiguration, string linkSpeedValue, ref string errMsg) + { + bool successful = false; + errMsg = ""; + + try + { + uint linkSpeed = 0; + + if (networkAdapter == null) + { + throw new Exception("Object networkAdapter cannot be null"); + } + else if (networkAdapterConfiguration == null) + { + throw new Exception("Object adapterNetworkConfiguration cannot be null"); + } + else if (linkSpeedValue.Length == 0) + { + throw new Exception("linkSpeedValue cannot be empty"); + } + else if (!UInt32.TryParse(linkSpeedValue, out linkSpeed)) + { + throw new Exception("linkSpeedValue must be a number"); + } + + var hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); + + RegistryKey regSubKey = null; + + string registryPath = @"SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}"; + + regSubKey = hklm.OpenSubKey(registryPath, false); + + // get a list of folder names under the registry path above + List keyNameList = new List(regSubKey.GetSubKeyNames()); + + regSubKey.Close(); + + // loop through each key name to find the entry for the adapter we are interested in + foreach (string keyName in keyNameList) + { + string vendorRegistryPath = registryPath + @"\" + keyName; + + string adapterGuid = WindowsRegistry.GetRegistryValueInString(vendorRegistryPath, RegistryHive.LocalMachine, "NetCfgInstanceId"); + + // if this is the adapter we are interested in + if (String.Equals(adapterGuid, networkAdapter["GUID"].ToString(), StringComparison.OrdinalIgnoreCase)) + { + successful = WindowsRegistry.AddOrModifyRegistryKeySingleValue(vendorRegistryPath, RegistryHive.LocalMachine, "*SpeedDuplex", RegistryValueKind.String, linkSpeedValue, ref errMsg); + + if (!successful) + throw new Exception(errMsg); + + break; + } + } + + if (!successful && errMsg.Length == 0) + throw new Exception("Unable to find a registry entry for this network adapter under " + registryPath); + } + catch (Exception ex) + { + errMsg = ex.Message; + successful = false; + } + + return successful; + } + } +} diff --git a/CommonLib/Library/Windows/Misc/NetworkSharing.cs b/CommonLib/Library/Windows/Misc/NetworkSharing.cs new file mode 100644 index 0000000..d672d2e --- /dev/null +++ b/CommonLib/Library/Windows/Misc/NetworkSharing.cs @@ -0,0 +1,363 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Net.NetworkInformation; +using System.Management; +using System.Runtime.InteropServices; +using System.IO; +using Microsoft.Win32; +using System.Text.RegularExpressions; +using System.Security.Principal; +using System.Security.AccessControl; + +using CommonLib.IO; + +namespace CommonLib.Windows.Misc +{ + public class NetworkSharing + { + public static Dictionary userDefinedAccountNameToWellKnowSid = new Dictionary(StringComparer.OrdinalIgnoreCase) { + { "Everyone" , WellKnownSidType.WorldSid }, + { "Authenticated Users" , WellKnownSidType.AuthenticatedUserSid } + }; + + //////========================================================================== + /// NetworkSharing.EnableNetworkFolderSharing + ///========================================================================== + /// + /// Enable network sharing of a local folder + /// + /// Local path of folder to be shared + /// name of the network share + /// Description of the share + /// error message returned if error encountered + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 12/07/16 D.Le + ///========================================================================== + public static bool EnableNetworkFolderSharing(string folderPath, string shareName, string description, ref string errMsg) + { + bool successful = true; + errMsg = ""; + + if (!Regex.IsMatch(folderPath, @"[a-zA-Z]:.*", RegexOptions.IgnoreCase)) + { + errMsg = "Unable to share path " + folderPath + ". Path is invalid"; + + successful = false; + } + + if (successful) + { + // if the path contains at least one folder in it + if (Regex.IsMatch(folderPath, @"[a-zA-Z]:\\[^\\]+", RegexOptions.IgnoreCase)) + folderPath = PathManip.RemoveTrailingSlashInPath(folderPath); + else + folderPath = PathManip.AddTrailingSlashToPath(folderPath); + + if (!Directory.Exists(folderPath)) + { + errMsg = folderPath + "\" does not exist"; + successful = false; + } + } + + if (successful) + { + try + { + // Create a ManagementClass object + ManagementClass managementClass = new ManagementClass("Win32_Share"); + + // Create ManagementBaseObjects for in and out parameters + ManagementBaseObject inParams = managementClass.GetMethodParameters("Create"); + ManagementBaseObject outParams; + + // Set the input parameters + inParams["Description"] = description; + inParams["Name"] = shareName; + inParams["Path"] = folderPath; + inParams["Type"] = 0x0; // Disk Drive + //Another Type: + // DISK_DRIVE = 0x0 + // PRINT_QUEUE = 0x1 + // DEVICE = 0x2 + // IPC = 0x3 + // DISK_DRIVE_ADMIN = 0x80000000 + // PRINT_QUEUE_ADMIN = 0x80000001 + // DEVICE_ADMIN = 0x80000002 + // IPC_ADMIN = 0x8000003 + inParams["MaximumAllowed"] = null; + inParams["Password"] = null; + inParams["Access"] = null; // Make Everyone has full control access. + //inParams["MaximumAllowed"] = int maxConnectionsNum; + + // Invoke the method on the ManagementClass object + outParams = managementClass.InvokeMethod("Create", inParams, null); + // Check to see if the method invocation was successful + if ((uint)(outParams.Properties["ReturnValue"].Value) != 0 && (uint)(outParams.Properties["ReturnValue"].Value) != 22) + { + errMsg = "Error encountered when calling managementClass.InvokeMethod(\"Create\"). Error code: " + outParams.Properties["ReturnValue"].Value.ToString(); + successful = false; + } + } + catch (Exception ex) + { + errMsg = ex.Message; + successful = false; + } + } + + return successful; + } + + //////========================================================================== + /// NetworkSharing.addAccountToNetworkShare + ///========================================================================== + /// + /// To add windows account to the permission list of the network + /// share + /// + /// Local path of folder to be shared + /// name of the network share + /// user account. Should be in in the form: + /// DOMAIN\AccountName + /// + /// Description of the share + /// error message returned if error encountered + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 12/07/16 D.Le + ///========================================================================== + public static bool addAccountToNetworkShare(string folderPath, string shareName, string account, string description, ref string errMsg) + { + bool successful = true; + Match regExMatch; + string domain = "", accountName = ""; + + if (!Regex.IsMatch(folderPath, @"[a-zA-Z]:.*", RegexOptions.IgnoreCase)) + { + errMsg = "Unable to modify user permission for " + folderPath + ". Path is invalid"; + + successful = false; + } + + if (successful) + { + // if the path contains at least one folder in it + if (Regex.IsMatch(folderPath, @"[a-zA-Z]:\\[^\\]+", RegexOptions.IgnoreCase)) + folderPath = PathManip.RemoveTrailingSlashInPath(folderPath); + else + folderPath = PathManip.AddTrailingSlashToPath(folderPath); + + if (!Directory.Exists(folderPath)) + { + errMsg = folderPath + "\" does not exist"; + successful = false; + } + } + + if (successful) + { + regExMatch = Regex.Match(account, @"(\\\\)?([^\\]+)\\([^\\]+)", RegexOptions.IgnoreCase); + + if (regExMatch.Success) + { + domain = regExMatch.Groups[2].Value; + accountName = regExMatch.Groups[3].Value; + } + else if (!isValidWellKnownSid(account)) + { + errMsg = "Account \"" + account + "\" is invalid"; + successful = false; + } + } + + if (successful) + { + try + { + // Create a ManagementClass object + ManagementClass managementClass = new ManagementClass("Win32_Share"); + + // Create ManagementBaseObjects for in and out parameters + ManagementBaseObject inParams = managementClass.GetMethodParameters("Create"); + ManagementBaseObject outParams; + + // Set the input parameters + inParams["Description"] = description; + inParams["Name"] = shareName; + inParams["Path"] = folderPath; + inParams["Type"] = 0x0; // Disk Drive + //Another Type: + // DISK_DRIVE = 0x0 + // PRINT_QUEUE = 0x1 + // DEVICE = 0x2 + // IPC = 0x3 + // DISK_DRIVE_ADMIN = 0x80000000 + // PRINT_QUEUE_ADMIN = 0x80000001 + // DEVICE_ADMIN = 0x80000002 + // IPC_ADMIN = 0x8000003 + inParams["MaximumAllowed"] = null; + inParams["Password"] = null; + inParams["Access"] = null; // Make Everyone has full control access. + //inParams["MaximumAllowed"] = int maxConnectionsNum; + + // Invoke the method on the ManagementClass object + outParams = managementClass.InvokeMethod("Create", inParams, null); + // Check to see if the method invocation was successful + if ((uint)(outParams.Properties["ReturnValue"].Value) != 0 && (uint)(outParams.Properties["ReturnValue"].Value) != 22) + { + errMsg = "Error encountered when calling managementClass.InvokeMethod(\"Create\"). Error code: " + outParams.Properties["ReturnValue"].Value.ToString(); + successful = false; + } + + if (successful) + { + SecurityIdentifier userSID = null; + if (isValidWellKnownSid(account)) + { + // get built-in security principals ( Everyone, Authenticated Users, Guests, Administrators, etc) + userSID = new SecurityIdentifier(GetWellKnownSid(account), null); + + } + else + { + //user selection + NTAccount ntAccount = new NTAccount(domain, accountName); + //SID + userSID = (SecurityIdentifier)ntAccount.Translate(typeof(SecurityIdentifier)); + } + byte[] utenteSIDArray = new byte[userSID.BinaryLength]; + userSID.GetBinaryForm(utenteSIDArray, 0); + + //Trustee + ManagementObject userTrustee = new ManagementClass(new ManagementPath("Win32_Trustee"), null); + if (domain.Length > 0) + { + userTrustee["Domain"] = domain; + userTrustee["Name"] = accountName; + } + userTrustee["SID"] = utenteSIDArray; + + //ACE + ManagementObject userACE = new ManagementClass(new ManagementPath("Win32_Ace"), null); + userACE["AccessMask"] = 2032127; //Full access + userACE["AceFlags"] = AceFlags.ObjectInherit | AceFlags.ContainerInherit; + userACE["AceType"] = AceType.AccessAllowed; + userACE["Trustee"] = userTrustee; + + string rootPath = Regex.Replace(managementClass.Path.ToString(), @"([^:]+:).+", "${1}", RegexOptions.IgnoreCase); + //After we have the new Win_32Ace, now we need to get the existing Ace instances (DACL). + //Create an instance of Win32_LogicalSecuritySetting, set the path to the server and the share. + ManagementObject Win32LogicalSecuritySetting = new ManagementObject(rootPath + "Win32_LogicalShareSecuritySetting.Name='" + shareName + "'"); + + //Call the GetSecurityDescriptor method. This method returns one out parameter. + ManagementBaseObject Return = Win32LogicalSecuritySetting.InvokeMethod("GetSecurityDescriptor", null, null); + + //The return value of that call above has two properties, ReturnValue, which you can use + //to read the status of the call (failed, success, etc.), and Descriptor, which is an instance + //of Win32_SecurityDescriptor. + Int32 returnValue = Convert.ToInt32(Return.Properties["ReturnValue"].Value); + + if (returnValue != 0) + throw new Exception(String.Format("Error when calling GetSecurityDescriptor. Error code: {0}.", returnValue)); + + //Retrieve the array of DACL from the Security Descriptor. + + ManagementBaseObject securityDescriptor = Return.Properties["Descriptor"].Value as ManagementBaseObject; + + ManagementBaseObject[] DACL = securityDescriptor["DACL"] as ManagementBaseObject[]; + + + if (DACL == null) + DACL = new ManagementBaseObject[] { userACE }; + else + { + Array.Resize(ref DACL, DACL.Length + 1); + + DACL[DACL.Length - 1] = userACE; + } + + //Reassign the new DACL array with the new user Ace back to the Win32_SecurityDescriptor instance, and call the + //SetSecurityDescriptor method. + securityDescriptor["DACL"] = DACL; + + ManagementObject share = new ManagementObject(managementClass.Path + ".Name='" + shareName + "'"); + + returnValue = Convert.ToInt32(share.InvokeMethod("SetShareInfo", new object[] { Int32.MaxValue, description, securityDescriptor })); + + if (returnValue != 0) + throw new Exception(String.Format("Error when calling GetSecurityDescriptor. Error code: {0}.", returnValue)); + } + + } + catch (Exception ex) + { + errMsg = ex.Message; + successful = false; + } + } + + + return successful; + } + + //////========================================================================== + /// NetworkSharing.isValidWellKnownSid + ///========================================================================== + /// + /// Verify if the windows account name user provided is valid + /// + /// Windows account name + /// could be Users, Groups or Built-In Security Principals (i.e.: + /// Everyone, Authenticated Users, Guests, etc) + /// + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 12/07/16 D.Le + ///========================================================================== + public static bool isValidWellKnownSid(string userDefinedSidAccountName) + { + bool isValid = true; + + if (!userDefinedAccountNameToWellKnowSid.ContainsKey(userDefinedSidAccountName)) + isValid = false; + + return isValid; + } + + //////========================================================================== + /// NetworkSharing.GetWellKnownSid + ///========================================================================== + /// + /// Return the actual Well Known Sid type that matches the windows account + /// name provided by user + /// + /// Windows account name + /// could be Users, Groups or Built-In Security Principals (i.e.: + /// Everyone, Authenticated Users, Guests, etc) + /// + /// true/false + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- ----------------------------- + /// 12/07/16 D.Le + ///========================================================================== + public static WellKnownSidType GetWellKnownSid(string userDefinedSidAccountName) + { + if (isValidWellKnownSid(userDefinedSidAccountName)) + return userDefinedAccountNameToWellKnowSid[userDefinedSidAccountName]; + else + throw new Exception(userDefinedSidAccountName + " is invalid."); + } + } +} diff --git a/CommonLib/Library/Windows/Misc/WindowShortcut.cs b/CommonLib/Library/Windows/Misc/WindowShortcut.cs new file mode 100644 index 0000000..a657192 --- /dev/null +++ b/CommonLib/Library/Windows/Misc/WindowShortcut.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Runtime.InteropServices; + +namespace CommonLib.Windows.Misc +{ + public class WindowShortcut + { + private static Type m_type = Type.GetTypeFromProgID("WScript.Shell"); + private static object m_shell = Activator.CreateInstance(m_type); + + [ComImport, TypeLibType((short)0x1040), Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B")] + private interface IWshShortcut + { + [DispId(0)] + string FullName { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0)] get; } + [DispId(0x3e8)] + string Arguments { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3e8)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3e8)] set; } + [DispId(0x3e9)] + string Description { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3e9)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3e9)] set; } + [DispId(0x3ea)] + string Hotkey { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3ea)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ea)] set; } + [DispId(0x3eb)] + string IconLocation { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3eb)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3eb)] set; } + [DispId(0x3ec)] + string RelativePath { [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ec)] set; } + [DispId(0x3ed)] + string TargetPath { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3ed)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ed)] set; } + [DispId(0x3ee)] + int WindowStyle { [DispId(0x3ee)] get; [param: In] [DispId(0x3ee)] set; } + [DispId(0x3ef)] + string WorkingDirectory { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3ef)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ef)] set; } + [TypeLibFunc((short)0x40), DispId(0x7d0)] + void Load([In, MarshalAs(UnmanagedType.BStr)] string PathLink); + [DispId(0x7d1)] + void Save(); + } + + ///========================================================================== + /// Create + ///========================================================================== + /// + /// Create window short cut + /// + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static void Create(string fileName, string targetPath, string arguments, string workingDirectory, string description, string hotkey, string iconPath) + { + IWshShortcut shortcut = (IWshShortcut)m_type.InvokeMember("CreateShortcut", System.Reflection.BindingFlags.InvokeMethod, null, m_shell, new object[] { fileName }); + shortcut.Description = description; + shortcut.Hotkey = hotkey; + shortcut.TargetPath = targetPath; + shortcut.WorkingDirectory = workingDirectory; + shortcut.Arguments = arguments; + if (!string.IsNullOrEmpty(iconPath)) + shortcut.IconLocation = iconPath; + shortcut.Save(); + } + + } +} diff --git a/CommonLib/Library/Windows/Misc/WindowsAdministration.cs b/CommonLib/Library/Windows/Misc/WindowsAdministration.cs new file mode 100644 index 0000000..c962adf --- /dev/null +++ b/CommonLib/Library/Windows/Misc/WindowsAdministration.cs @@ -0,0 +1,407 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Diagnostics; +using System.Text.RegularExpressions; +using System.IO; +using System.Security.AccessControl; +using System.Management; +using System.Security.Principal; +using System.Runtime.InteropServices; +using Microsoft.Win32; +using System.Drawing; +using System.DirectoryServices; +using System.DirectoryServices.AccountManagement; + +namespace CommonLib.Windows.Misc +{ + public class WindowsAdministration + { + ///========================================================================== + /// GetWindowLoginID + ///========================================================================== + /// + /// To get windows login ID, usually it's a simple API call. The problem + /// is when trying to get the windows login ID from a application that is + /// run as an Administrator. In this case, the simple API call will return + /// the Administrator user's ID. This function gets the actual Windows Logon + /// ID regardless what the application is run as. + /// + /// none + /// string + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string GetWindowLoginID() + { + string login = ""; + + string[] properties = { "*" }; + SelectQuery s = new SelectQuery("Win32_Process", "Name = 'explorer.exe' ", properties); + ManagementObjectSearcher searcher = new ManagementObjectSearcher(s); + foreach (ManagementObject o in searcher.Get()) + { + string[] ownerInfo = { "", "" }; + o.InvokeMethod("GetOwner", ownerInfo); + + login = ownerInfo[1] + "\\" + ownerInfo[0]; + } + + return login; + } + + ///========================================================================== + /// GetDomainName + ///========================================================================== + /// + /// Get the domain name in which the user is logged into. If the computer is + /// not a member of a domain, then the domain name will be the name of the + /// local computer + /// + /// none + /// string + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string GetDomainName() + { + string fullLoginID = GetWindowLoginID(); + + return Regex.Replace(fullLoginID, @"^([^\\]+)\\.+", "$1"); + } + + ///========================================================================== + /// GetUserName + ///========================================================================== + /// + /// Get the user name in which the user is logged into. + /// + /// none + /// string + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static string GetUserName() + { + string fullLoginID = GetWindowLoginID(); + + return Regex.Replace(fullLoginID, @"^[^\\]+\\(.+)", "$1"); + } + + ///========================================================================== + /// IsAccountAnAdminAccount + ///========================================================================== + /// + /// Get the domain name in which the user is logged into. If the computer is + /// not a member of a domain, then the domain name will be the name of the + /// local computer + /// + /// Format: [domain]\username, i.e. US\1117637 + /// string + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool IsAccountAnAdminAccount(string fullAccountName) + { + bool accountIsAnAdmin = false; + List adminAccounts = GetListOfMemberAccountsFromLocalGroups("Administrators"); + + foreach (string account in adminAccounts) + { + string tempAccount = Regex.Replace(account, @"/", "\\"); + + if (Regex.IsMatch(tempAccount, Regex.Escape(fullAccountName) + @"$", RegexOptions.IgnoreCase)) + { + accountIsAnAdmin = true; + break; + } + } + + return accountIsAnAdmin; + } + + ///========================================================================== + /// IsApplicationRunAsAdmin + ///========================================================================== + /// + /// Check if the application is run with elevated privilege, aka admin + /// + /// + /// string + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static bool IsApplicationRunAsAdmin() + { + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + WindowsPrincipal principal = new WindowsPrincipal(identity); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + + ///========================================================================== + /// GetListOfMemberAccountsFromLocalGroups + ///========================================================================== + /// + /// Get a list of member accounts from a local group + /// + /// Administrators, Guests, Users, etc + /// string + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 06/06/12 D.Le + ///========================================================================== + public static List GetListOfMemberAccountsFromLocalGroups(string groupName) + { + List myItems = new List(); + + using (DirectoryEntry machine = new DirectoryEntry("WinNT://" + Environment.MachineName)) + { + //get local admin group + using (DirectoryEntry group = machine.Children.Find(groupName, "Group")) + { + //get all members of local admin group + object members = group.Invoke("Members", null); + foreach (object member in (IEnumerable)members) + { + //get account name + myItems.Add(new DirectoryEntry(member).Path); + } + } + } + + return myItems; + } + + ///========================================================================== + /// IsUserInGroup + ///========================================================================== + /// + /// Check if a particular user account is in local group + /// + /// i.e.: US\1117637, ZTUAI362318\COEUser + /// Administrators, etc + /// + /// true/false + ///========================================================================== + public static bool IsUserInGroup(string fullQualifiedUsername, string groupName, ref string errMsg) + { + bool isInGroup = false; + errMsg = ""; + + try + { + List membersOfLocalAdminGroup = WindowsAdministration.GetListOfMemberAccountsFromLocalGroups(groupName); + + fullQualifiedUsername = Regex.Replace(fullQualifiedUsername, @"\\", "/"); + + if (membersOfLocalAdminGroup.Any(member => Regex.IsMatch(member.ToString(), Regex.Escape(fullQualifiedUsername), RegexOptions.IgnoreCase))) + { + isInGroup = true; + } + } + catch (Exception ex) + { + errMsg = ex.Message; + } + + return isInGroup; + } + + ///========================================================================== + /// AddUserToGroup + ///========================================================================== + /// + /// Add an existing user account to a group + /// + /// i.e.: 1117637 + /// + /// US, etc + /// Administrators, etc + /// + /// None + ///========================================================================== + public static void AddUserToGroup(string userName, string userPassword, string domain, string groupName, ref string errMsg) + { + bool impersonatingLoggedInUser = false; + PrincipalContext context = null; + GroupPrincipal group = null; + errMsg = ""; + string appRunner = Environment.UserDomainName + "\\" + Environment.UserName; + + try + { + context = new PrincipalContext(ContextType.Machine); + + if (context != null) + group = GroupPrincipal.FindByIdentity(context, "Administrators"); + + if (group != null) + { + if (!String.IsNullOrEmpty(domain) && !String.Equals(domain, Environment.MachineName, StringComparison.CurrentCultureIgnoreCase)) + { + if (!String.Equals(appRunner, GetWindowLoginID(), StringComparison.CurrentCultureIgnoreCase)) + { + context = new PrincipalContext(ContextType.Domain, domain, userName, userPassword); + } + else + context = new PrincipalContext(ContextType.Domain, domain); + } + + if (context != null) + { + if (!String.Equals(appRunner, GetWindowLoginID(), StringComparison.CurrentCultureIgnoreCase)) + { + if (!Impersonate(userName, userPassword, domain)) + errMsg = "Impersonation failed. Username or password is incorrect for: " + domain + "\\" + userName; + else + impersonatingLoggedInUser = true; + } + + group.Members.Add(context, IdentityType.SamAccountName, userName); + + if (impersonatingLoggedInUser) + UnImpersonate(); + + group.Save(); + + group.Dispose(); + + context.Dispose(); + } + } + } + catch (Exception ex) + { + errMsg = ex.Message; + }; + } + + ///========================================================================== + /// removeUserFromGroup + ///========================================================================== + /// + /// Remove user from a group + /// + /// full qualified windows login id, i.e. DOMAIN\User + /// name of the group, i.e. Administrators + /// error message + /// + ///========================================================================== + public static void removeUserFromGroup(string windowsLoginId, string groupName, ref string errMsg) + { + string loginId = windowsLoginId; + loginId = Regex.Replace(loginId, @"\\", "/", RegexOptions.IgnoreCase); + + try + { + using (DirectoryEntry machine = new DirectoryEntry("WinNT://" + Environment.MachineName)) + { + //get local admin group + using (DirectoryEntry group = machine.Children.Find(groupName, "Group")) + { + //get all members of local admin group + object members = group.Invoke("Members", null); + foreach (object member in (IEnumerable)members) + { + using (var memberEntry = new DirectoryEntry(member)) + { + if (!Regex.IsMatch(memberEntry.Path, @".+/" + Environment.MachineName + "/[^/]+$") && Regex.IsMatch(memberEntry.Path, loginId, RegexOptions.IgnoreCase)) + { + group.Invoke("Remove", new[] { memberEntry.Path }); + } + } + } + } + } + } + catch (Exception ex) + { + errMsg = ex.Message; + } + } + + ///========================================================================== + /// Impersonate + ///========================================================================== + /// + /// Impersonate given logon information. Only can impersonate a regular + /// user. Cannot impersonate administrator if the UAC is set to Elevated + /// + /// Windows logon name. + /// password + /// domain name + /// + ///========================================================================== + public static bool Impersonate(string logon, string password, string + domain) + { + WindowsIdentity tempWindowsIdentity; + IntPtr token = IntPtr.Zero; + IntPtr tokenDuplicate = IntPtr.Zero; + + if (LogonUser(logon, domain, password, LOGON32_LOGON_INTERACTIVE, + LOGON32_PROVIDER_DEFAULT, ref token) != 0) + { + + if (DuplicateToken(token, 2, ref tokenDuplicate) != 0) + { + tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); + impersonationContext = tempWindowsIdentity.Impersonate(); + if (null != impersonationContext) return true; + } + } + + return false; + } + + ///========================================================================== + /// UnImpersonate + ///========================================================================== + /// + /// Return to the original user account that starts up the program + /// + /// + /// + ///========================================================================== + public static void UnImpersonate() + { + impersonationContext.Undo(); + } + + [DllImport("advapi32.dll", CharSet = CharSet.Auto)] + public static extern int LogonUser( + string lpszUserName, + String lpszDomain, + String lpszPassword, + int dwLogonType, + int dwLogonProvider, + ref IntPtr phToken); + + [DllImport("advapi32.dll", + CharSet = System.Runtime.InteropServices.CharSet.Auto, + SetLastError = true)] + public extern static int DuplicateToken( + IntPtr hToken, + int impersonationLevel, + ref IntPtr hNewToken); + + private const int LOGON32_LOGON_INTERACTIVE = 2; + private const int LOGON32_LOGON_NETWORK_CLEARTEXT = 4; + private const int LOGON32_PROVIDER_DEFAULT = 0; + private static WindowsImpersonationContext impersonationContext; + } +} diff --git a/CommonLib/Library/Windows/Misc/WindowsRegistry.cs b/CommonLib/Library/Windows/Misc/WindowsRegistry.cs new file mode 100644 index 0000000..5f6c086 --- /dev/null +++ b/CommonLib/Library/Windows/Misc/WindowsRegistry.cs @@ -0,0 +1,467 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Win32; + +namespace CommonLib.Windows.Misc +{ + public class WindowsRegistry + { + public enum valueKind { STRING, MULTI_STRING, DWORD, QWORD }; + + ///========================================================================== + /// GetRegistryValueInString + ///========================================================================== + /// + /// Get the value of a particular value name of a particular registry key + /// A registry value can be of many types, we have to convert them differently + /// into string + /// + /// Registry folder path + /// Local Machine, Classes Root, Current User + /// the registry name that has a value associated with it + /// a string representing the value of the registry name + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 12/04/16 D.Le + ///========================================================================== + public static string GetRegistryValueInString(string registryPath, RegistryHive registryType, string valueName) + { + string value = ""; + + var hklm = RegistryKey.OpenBaseKey(registryType, RegistryView.Registry64); + + RegistryKey regSubKey = null; + + regSubKey = hklm.OpenSubKey(registryPath, false); + + registryPath = registryPath.Trim(); + + if (registryPath.Length > 0 && regSubKey != null && valueName.Length > 0) + { + List list = new List(regSubKey.GetValueNames()); + + if (list.FindIndex(x => x.Equals(valueName, StringComparison.OrdinalIgnoreCase)) != -1) + { + object obj = regSubKey.GetValue(valueName); + + switch (regSubKey.GetValueKind(valueName)) + { + case RegistryValueKind.String: + case RegistryValueKind.ExpandString: + value = (string)obj; + break; + case RegistryValueKind.Binary: + foreach (byte b in (byte[])obj) + { + if (value.Length == 0) + value = b.ToString("x2"); + else + value += " " + b.ToString("x2"); + } + Console.WriteLine(); + + break; + case RegistryValueKind.DWord: + value = Convert.ToString((Int32)obj); + break; + case RegistryValueKind.QWord: + value = Convert.ToString((Int64)obj); + break; + case RegistryValueKind.MultiString: + foreach (string s in (string[])obj) + { + if (value.Length == 0) + value = s; + else + value = "\n" + s; + } + break; + default: + break; + } + } + } + + return value; + } + + ///========================================================================== + /// AddOrModifyRegistryKeySingleValue + ///========================================================================== + /// + /// Add a value name/pair if the value name doesn't already exist for a particular + /// registry key. Otherwise, we modify the value of the value name. + /// + /// Registry folder path + /// Local Machine, Classes Root, Current User + /// the registry name that has a value associated with it + /// if the valueName doesn't exist, we need to create a new + /// value name of one of these types: string, dword, qword + /// + /// the value to be assigned to the registry name + /// error message if the function execution fails + /// true/false as to the success/failure of the function execution + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 12/04/16 D.Le + ///========================================================================== + public static bool AddOrModifyRegistryKeySingleValue(string registryPath, RegistryHive registryType, string valueName, RegistryValueKind valueType, string value, ref string errMsg) + { + bool successful = true; + errMsg = ""; + + var hklm = RegistryKey.OpenBaseKey(registryType, RegistryView.Registry64); + + RegistryKey regSubKey = null; + + registryPath = registryPath.Trim(); + + if (registryPath.Length > 0) + { + try + { + regSubKey = hklm.CreateSubKey(registryPath); + } + catch (Exception e) + { + errMsg = e.Message; + successful = false; + } + } + else + { + errMsg = "Registry Path is empty"; + successful = false; + } + + if (successful && regSubKey != null && valueName.Length > 0) + { + List list = new List(regSubKey.GetValueNames()); + + // modify registry value + if (list.FindIndex(x => x.Equals(valueName, StringComparison.OrdinalIgnoreCase)) != -1) + { + object obj = regSubKey.GetValue(valueName); + + valueType = regSubKey.GetValueKind(valueName); + } + + switch (valueType) + { + case RegistryValueKind.String: + try + { + regSubKey.SetValue(valueName, value, RegistryValueKind.String); + } + catch (Exception e) + { + errMsg = e.Message; + successful = false; + } + break; + case RegistryValueKind.DWord: + try + { + regSubKey.SetValue(valueName, value, RegistryValueKind.DWord); + } + catch (Exception e) + { + errMsg = e.Message; + successful = false; + } + break; + case RegistryValueKind.QWord: + try + { + regSubKey.SetValue(valueName, value, RegistryValueKind.QWord); + } + catch (Exception e) + { + errMsg = e.Message; + successful = false; + } + break; + default: + errMsg = "Unable to add value name of type " + valueType.ToString(); + successful = false; + break; + } + + } + + if (regSubKey != null) + regSubKey.Close(); + + return successful; + } + + ///========================================================================== + /// AddOrModifyRegistryKeyMultiStringValue + ///========================================================================== + /// + /// Add/modify a value name of type multi-string + /// + /// Registry folder path + /// Local Machine, Classes Root, Current User + /// the registry name that has a value associated with it + /// list of values + /// error message if the function execution fails + /// true/false as to the success/failure of the function execution + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 12/04/16 D.Le + ///========================================================================== + public static bool AddOrModifyRegistryKeyMultiStringValue(string registryPath, RegistryHive registryType, string valueName, List values, ref string errMsg) + { + bool successful = true; + errMsg = ""; + + var hklm = RegistryKey.OpenBaseKey(registryType, RegistryView.Registry64); + + RegistryKey regSubKey = null; + + registryPath = registryPath.Trim(); + + if (registryPath.Length > 0) + { + try + { + regSubKey = hklm.CreateSubKey(registryPath); + } + catch (Exception e) + { + errMsg = e.Message; + successful = false; + } + } + else + { + errMsg = "Registry Path is empty"; + successful = false; + } + + if (successful && regSubKey != null && valueName.Length > 0) + { + try + { + regSubKey.SetValue(valueName, values.ToArray(), RegistryValueKind.MultiString); + } + catch (Exception e) + { + errMsg = e.Message; + successful = false; + } + + } + + if (regSubKey != null) + regSubKey.Close(); + + return successful; + } + + ///========================================================================== + /// DeleteRegistryKey + ///========================================================================== + /// + /// Delete registry key and all subkeys within it, recursively + /// + /// Registry folder path + /// Local Machine, Classes Root, Current User + /// error message if the function execution fails + /// true/false as to the success/failure of the function execution + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 12/04/16 D.Le + ///========================================================================== + public static bool DeleteRegistryKey(string registryPath, RegistryHive registryType, ref string errMsg) + { + bool successful = true; + RegistryKey rkSubKey = null; + var hklm = RegistryKey.OpenBaseKey(registryType, RegistryView.Registry64); + + registryPath = registryPath.Trim(); + errMsg = ""; + + if (registryPath.Length > 0) + { + rkSubKey = hklm.OpenSubKey(registryPath, false); + + if (rkSubKey == null) + { + errMsg = registryPath + " doesn't exist in registry"; + } + } + else + { + errMsg = "Registry Path is empty"; + successful = false; + } + + if (successful && rkSubKey != null) + { + try + { + hklm.DeleteSubKeyTree(registryPath); + } + catch (Exception e) + { + errMsg = e.Message; + successful = false; + } + } + + if (rkSubKey != null) + rkSubKey.Close(); + + return successful; + } + + ///========================================================================== + /// DeleteRegistryValueName + ///========================================================================== + /// + /// Delete a value name within a registry key + /// + /// Registry folder path + /// Local Machine, Classes Root, Current User + /// the registry name to be deleted + /// error message if the function execution fails + /// true/false as to the success/failure of the function execution + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 12/04/16 D.Le + ///========================================================================== + public static bool DeleteRegistryValueName(string registryPath, RegistryHive registryType, string valueName, ref string errMsg) + { + bool successful = true; + RegistryKey rkSubKey = null; + var hklm = RegistryKey.OpenBaseKey(registryType, RegistryView.Registry64); + + bool valueNameExists = true; + registryPath = registryPath.Trim(); + errMsg = ""; + + if (registryPath.Length > 0) + { + try + { + rkSubKey = hklm.OpenSubKey(registryPath, true); + + if (rkSubKey == null) + { + errMsg = registryPath + " doesn't exist in registry"; + } + else + { + List list = new List(rkSubKey.GetValueNames()); + + if (list.FindIndex(x => x.Equals(valueName, StringComparison.OrdinalIgnoreCase)) == -1) + { + errMsg = "Value name " + valueName + " doesn't exist in registry " + registryPath; + valueNameExists = false; + } + } + } + catch (Exception e) + { + errMsg = e.Message; + successful = false; + } + } + else + { + errMsg = "Registry Path is empty"; + successful = false; + } + + if (successful && rkSubKey != null && valueNameExists) + { + try + { + rkSubKey.DeleteValue(valueName); + } + catch (Exception e) + { + errMsg = e.Message; + successful = false; + } + } + + if (rkSubKey != null) + rkSubKey.Close(); + + return successful; + } + + ///========================================================================== + /// GetRegistryDynamicUninstallKeys + ///========================================================================== + /// + /// Get the registry keys (full path) for a particular software by using the + /// uninstall key. The uninstall keys for software are randomly generated. + /// So in order to get the key, we go through all the entries in the Uninstall + /// key to find the software name we are looking for. Under every uninstall + /// key, there's a valuename called DisplayName which corresponds to the name + /// of the software. + /// + /// Local Machine, Classes Root, Current User + /// the name of the software that is recorded in the registry + ///========================================================================== + /// Date Programmer Proj.ID SAR REVISION HISTORY: + /// --/--/-- ------------ -------- ---- -------------------------- + /// 12/04/16 D.Le + ///========================================================================== + public static List GetRegistryDynamicUninstallKeys(RegistryHive registryType, string softwareName) + { + List appUninstallKeys = new List(); + + var hklm = RegistryKey.OpenBaseKey(registryType, RegistryView.Registry64); + + string uninstallKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"; + + for (int i = 1; i <= 2; i++) + { + if ( i==2) + uninstallKey = @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"; + + using (RegistryKey rk = hklm.OpenSubKey(uninstallKey)) + { + foreach (string skName in rk.GetSubKeyNames()) + { + using (RegistryKey sk = rk.OpenSubKey(skName)) + { + List list = new List(sk.GetValueNames()); + + if (list.FindIndex(x => x.Equals("DisplayName", StringComparison.OrdinalIgnoreCase)) != -1) + { + if (String.Equals(sk.GetValue("DisplayName").ToString(), softwareName, StringComparison.CurrentCultureIgnoreCase)) + { + appUninstallKeys.Add(uninstallKey + @"\" + skName); + } + } + + if ( sk != null) + sk.Close(); + } + } + + if ( rk != null ) + rk.Close(); + } + } + + if ( hklm != null) + hklm.Close(); + + return appUninstallKeys; + } + } +} diff --git a/CommonLib/Properties/AssemblyInfo.cs b/CommonLib/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..fd40d70 --- /dev/null +++ b/CommonLib/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("DucCommonLibrary")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Northrop Grumman Corporation")] +[assembly: AssemblyProduct("DucCommonLibrary")] +[assembly: AssemblyCopyright("Copyright © Northrop Grumman Corporation 2013")] +[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("e082b05a-d65f-4778-8a4c-b056fc3e0737")] + +// 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")]