diff --git a/wumgr/AppLog.cs b/wumgr/AppLog.cs deleted file mode 100644 index 1217f49..0000000 --- a/wumgr/AppLog.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace wumgr -{ - class AppLog - { - private List mLogList = new List(); - - static public void Line(String line) - { - if (mInstance != null) - mInstance.logLine(line); - } - - public void logLine(String line) - { - if (Logger != null) - { - mLogList.Add(line); - while (mLogList.Count > 100) - mLogList.RemoveAt(0); - - LogEventArgs args = new LogEventArgs(); - args.line = line; - Logger(this, args); - } - } - - static public List GetLog() { return mInstance.mLogList; } - - public class LogEventArgs : EventArgs - { - public string line { get; set; } - } - - static public event EventHandler Logger; - - static void LineLogger(object sender, LogEventArgs args) - { - Console.WriteLine("LOG: " + args.line); - } - - static private AppLog mInstance = null; - - public static AppLog GetInstance() { return mInstance; } - - public AppLog() - { - mInstance = this; - - Logger += LineLogger; - } - } - -} diff --git a/wumgr/Common/AppLog.cs b/wumgr/Common/AppLog.cs new file mode 100644 index 0000000..00c37d4 --- /dev/null +++ b/wumgr/Common/AppLog.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +class AppLog +{ + private List mLogList = new List(); + + static public void Line(String line) + { + if (mInstance != null) + mInstance.logLine(line); + } + + public void logLine(String line) + { + if (Logger != null) + { + mLogList.Add(line); + while (mLogList.Count > 100) + mLogList.RemoveAt(0); + + LogEventArgs args = new LogEventArgs(); + args.line = line; + Logger(this, args); + } + } + + static public List GetLog() { return mInstance.mLogList; } + + public class LogEventArgs : EventArgs + { + public string line { get; set; } + } + + static public event EventHandler Logger; + + static void LineLogger(object sender, LogEventArgs args) + { + Console.WriteLine("LOG: " + args.line); + } + + static private AppLog mInstance = null; + + public static AppLog GetInstance() { return mInstance; } + + public AppLog() + { + mInstance = this; + + Logger += LineLogger; + } +} diff --git a/wumgr/Common/FileOps.cs b/wumgr/Common/FileOps.cs new file mode 100644 index 0000000..4aac188 --- /dev/null +++ b/wumgr/Common/FileOps.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Security.AccessControl; +using System.Security.Principal; +using System.IO; + + +class FileOps +{ + static public bool MoveFile(string from, string to, bool Overwrite = false) + { + try + { + if (File.Exists(to)) + { + if (!Overwrite) + return false; + File.Delete(to); + } + + File.Move(from, to); + + if (File.Exists(from)) + return false; + } + catch (Exception e) + { + Console.WriteLine("The process failed: {0}", e.ToString()); + return false; + } + return true; + } + + static public bool DeleteFile(string path) + { + try + { + File.Delete(path); + return true; + } + catch + { + return false; + } + } + + static public int TestFileAdminSec(String filePath) + { + //get file info + FileInfo fi = new FileInfo(filePath); + if (!fi.Exists) + return 2; + + //get security access + FileSecurity fs = fi.GetAccessControl(); + + //get any special user access + AuthorizationRuleCollection rules = fs.GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)); // get as SID not string + + + //remove any special access + foreach (FileSystemAccessRule rule in rules) + { + if (rule.AccessControlType != AccessControlType.Allow) + continue; + if (rule.IdentityReference.Value.Equals(SID_Admins) || rule.IdentityReference.Value.Equals(SID_System)) + continue; + if ((rule.FileSystemRights & (FileSystemRights.Write | FileSystemRights.Delete)) != 0) + return 0; + } + return 1; + } + + static public void SetFileAdminSec(String filePath) + { + //get file info + FileInfo fi = new FileInfo(filePath); + if(!fi.Exists){ + FileStream f_out = fi.OpenWrite(); + f_out.Close(); + } + + //get security access + FileSecurity fs = fi.GetAccessControl(); + + //remove any inherited access + fs.SetAccessRuleProtection(true, false); + + //get any special user access + AuthorizationRuleCollection rules = fs.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount)); // show as names + + //remove any special access + foreach (FileSystemAccessRule rule in rules) + fs.RemoveAccessRule(rule); + + fs.AddAccessRule(new FileSystemAccessRule(new SecurityIdentifier(SID_Admins), FileSystemRights.FullControl, AccessControlType.Allow)); + fs.AddAccessRule(new FileSystemAccessRule(new SecurityIdentifier(SID_System), FileSystemRights.FullControl, AccessControlType.Allow)); + fs.AddAccessRule(new FileSystemAccessRule(new SecurityIdentifier(SID_Users), FileSystemRights.Read, AccessControlType.Allow)); + + //add current user with full control. + //fs.AddAccessRule(new FileSystemAccessRule(domainName + "\\" + userName, FileSystemRights.FullControl, AccessControlType.Allow)); + + //add all other users delete only permissions. + //SecurityIdentifier sid = new SecurityIdentifier("S-1-5-11"); // Authenticated Users + //fs.AddAccessRule(new FileSystemAccessRule(sid, FileSystemRights.Delete, AccessControlType.Allow)); + + //flush security access. + File.SetAccessControl(filePath, fs); + } + + static string SID_null = "S-1-0-0"; // Null SID + static string SID_Worls = "S-1-1-0"; // World + static string SID_Local = "S-1-2-0"; // Local + static string SID_Console = "S-1-2-1"; // Console Logon + static string SID_OwnerID = "S-1-3-0"; // Creator Owner ID + static string SID_GroupeID = "S-1-3-1"; // Creator Group ID + static string SID_OwnerSvr = "S-1-3-2"; // Creator Owner Server + static string SID_CreatorSvr = "S-1-3-3"; // Creator Group Server + static string SID_OwnerRights = "S-1-3-4"; // Owner Rights + static string SID_NonUnique = "S-1-4"; // Non-unique Authority + static string SID_NTAuth = "S-1-5"; // NT Authority + static string SID_AllServices = "S-1-5-80-0"; // All Services + static string SID_DialUp = "S-1-5-1"; // Dialup + static string SID_LocalAcc = "S-1-5-113"; // Local account + static string SID_LocalAccAdmin = "S-1-5-114"; // Local account and member of Administrators group + static string SID_Net = "S-1-5-2"; // Network + static string SID_Natch = "S-1-5-3"; // Batch + static string SID_Interactive = "S-1-5-4"; // Interactive + //static string SID_ = "S-1-5-5- *X*- *Y* Logon Session + static string SID_Service = "S-1-5-6"; // Service + static string SID_AnonLogin = "S-1-5-7"; // Anonymous Logon + + static string SID_Proxy = "S-1-5-8"; // Proxy + static string SID_EDC = "S-1-5-9"; // Enterprise Domain Controllers + static string SID_Self = "S-1-5-10"; // Self + static string SID_AuthenticetedUser = "S-1-5-11"; // Authenticated Users + + static string SID_Restricted = "S-1-5-12"; // Restricted Code + static string SID_TermUser = "S-1-5-13"; // Terminal Server User + static string SID_RemoteLogin = "S-1-5-14"; // Remote Interactive Logon + static string SID_ThisORg = "S-1-5-15"; // This Organization + static string SID_IIS = "S-1-5-17"; // IIS_USRS + static string SID_System = "S-1-5-18"; // System(or LocalSystem) + + static string SID_NTAuthL = "S-1-5-19"; // NT Authority(LocalService) + static string SID_NetServices = "S-1-5-20"; // Network Service + + static string SID_Admins = "S-1-5-32-544"; // Administrators + static string SID_Users = "S-1-5-32-545"; // Users + static string SID_Guests = "S-1-5-32-546"; // Guests + static string SID_PowerUsers = "S-1-5-32-547"; // Power Users + static string SID_AccOps = "S-1-5-32-548"; // Account Operators + static string SID_ServerOps = "S-1-5-32-549"; // Server Operators + static string SID_PrintOps = "S-1-5-32-550"; // Print Operators + static string SID_BackupOps = "S-1-5-32-551"; // Backup Operators + static string SID_Replicators = "S-1-5-32-552"; // Replicators + static string SID_NTLM_Auth = "S-1-5-64-10"; // NTLM Authentication + static string SID_SCh_Auth = "S-1-5-64-14"; // SChannel Authentication + static string SID_DigestAuth = "S-1-5-64-21"; // Digest Authentication + static string SID_NT_Service = "S-1-5-80"; // NT Service + static string SID_All_Services = "S-1-5-80-0"; // All Services + static string SID_VM = "S-1-5-83-0"; // NT VIRTUAL MACHINE\Virtual Machines + static string SID_UntrustedLevel = "S-1-16-0"; // Untrusted Mandatory Level + static string SID_LowLevel = "S-1-16-4096"; // Low Mandatory Level + static string SID_MediumLevel = "S-1-16-8192"; // Medium Mandatory Level + static string SID_MediumPLevel = "S-1-16-8448"; // Medium Plus Mandatory Level + static string SID_HighLevel = "S-1-16-12288"; // High Mandatory Level + static string SID_SysLevel = "S-1-16-16384"; // System Mandatory Level + static string SID_PPLevel = "S-1-16-20480"; // Protected Process Mandatory Level + static string SID_SPLevel = "S-1-16-28672"; // Secure Process Mandatory Level + +} diff --git a/wumgr/Common/HttpTask.cs b/wumgr/Common/HttpTask.cs new file mode 100644 index 0000000..d1c3685 --- /dev/null +++ b/wumgr/Common/HttpTask.cs @@ -0,0 +1,331 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Threading; + + +class HttpTask +{ + //const int DefaultTimeout = 2 * 60 * 1000; // 2 minutes timeout + const int BUFFER_SIZE = 1024; + private byte[] BufferRead; + private HttpWebRequest request; + private HttpWebResponse response; + private Stream streamResponse; + private Stream streamWriter; + private Dispatcher mDispatcher; + private string mUrl; + private string mDlPath; + private string mDlName; + private int mLength = -1; + private int mOffset = -1; + private bool Canceled = false; + private DateTime lastTime; + + public string DlPath { get { return mDlPath; } } + public string DlName { get { return mDlName; } } + + public HttpTask(string Url, string DlPath, string DlName = null, bool Update = false) + { + mUrl = Url; + mDlPath = DlPath; + mDlName = DlName; + + BufferRead = null; + request = null; + response = null; + streamResponse = null; + streamWriter = null; + mDispatcher = Dispatcher.CurrentDispatcher; + } + + // Abort the request if the timer fires. + /*private static void TimeoutCallback(object state, bool timedOut) + { + if (timedOut) + { + HttpWebRequest request = state as HttpWebRequest; + if (request != null) + request.Abort(); + } + }*/ + + public bool Start() + { + Canceled = false; + try + { + // Create a HttpWebrequest object to the desired URL. + request = (HttpWebRequest)WebRequest.Create(mUrl); + //myHttpWebRequest.AllowAutoRedirect = false; + + /** + * If you are behind a firewall and you do not have your browser proxy setup + * you need to use the following proxy creation code. + + // Create a proxy object. + WebProxy myProxy = new WebProxy(); + + // Associate a new Uri object to the _wProxy object, using the proxy address + // selected by the user. + myProxy.Address = new Uri("http://myproxy"); + + + // Finally, initialize the Web request object proxy property with the _wProxy + // object. + myHttpWebRequest.Proxy=myProxy; + ***/ + + BufferRead = new byte[BUFFER_SIZE]; + mOffset = 0; + + // Start the asynchronous request. + IAsyncResult result = (IAsyncResult)request.BeginGetResponse(new AsyncCallback(RespCallback), this); + + // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted + //ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, DefaultTimeout, true); + return true; + } + catch (Exception e) + { + Console.WriteLine("\nMain Exception raised!"); + Console.WriteLine("\nMessage:{0}", e.Message); + } + return false; + } + + public void Cancel() + { + Canceled = true; + if (request != null) + request.Abort(); + } + + private void Finish(int Success, int ErrCode, Exception Error = null) + { + // Release the HttpWebResponse resource. + if (response != null) + { + response.Close(); + if (streamResponse != null) + streamResponse.Close(); + if (streamWriter != null) + streamWriter.Close(); + + } + response = null; + request = null; + streamResponse = null; + BufferRead = null; + + if (Success == 1) + { + try + { + if (File.Exists(mDlPath + @"\" + mDlName)) + File.Delete(mDlPath + @"\" + mDlName); + File.Move(mDlPath + @"\" + mDlName + ".tmp", mDlPath + @"\" + mDlName); + } + catch + { + AppLog.Line(MiscFunc.fmt("Failed to rename download {0}", mDlPath + @"\" + mDlName + ".tmp")); + mDlName += ".tmp"; + } + + try { File.SetLastWriteTime(mDlPath + @"\" + mDlName, lastTime); } catch { } // set last mod time + } + else if (Success == 2) + { + AppLog.Line(MiscFunc.fmt("File already dowllaoded {0}", mDlPath + @"\" + mDlName)); + } + else + { + try { File.Delete(mDlPath + @"\" + mDlName + ".tmp"); } catch { } // delete partial file + AppLog.Line(MiscFunc.fmt("Failed to download file {0}", mDlPath + @"\" + mDlName)); + } + + Finished?.Invoke(this, new FinishedEventArgs(Success > 0 ? 0 : Canceled ? -1 : ErrCode, Error)); + } + + private void ReportProgress() + { + Progress?.Invoke(this, new ProgressEventArgs(mLength > 0 ? (int)((Int64)100 * mOffset / mLength) : -1)); + } + + static public string GetNextTempFile(string path, string baseName) + { + for (int i = 0; i < 10000; i++) + { + if (!File.Exists(path + @"\" + baseName + "_" + i + ".tmp")) + return baseName + "_" + i; + } + return baseName; + } + + private static void RespCallback(IAsyncResult asynchronousResult) + { + int Success = 0; + int ErrCode = 0; + Exception Error = null; + HttpTask task = (HttpTask)asynchronousResult.AsyncState; + try + { + // State of request is asynchronous. + task.response = (HttpWebResponse)task.request.EndGetResponse(asynchronousResult); + + ErrCode = (int)task.response.StatusCode; + + Console.WriteLine("The server at {0} returned {1}", task.response.ResponseUri, task.response.StatusCode); + + string fileName = Path.GetFileName(task.response.ResponseUri.ToString()); + task.lastTime = DateTime.Now; + + Console.WriteLine("With headers:"); + foreach (string key in task.response.Headers.AllKeys) + { + Console.WriteLine("\t{0}:{1}", key, task.response.Headers[key]); + + if (key.Equals("Content-Length", StringComparison.CurrentCultureIgnoreCase)) + { + task.mLength = int.Parse(task.response.Headers[key]); + } + else if (key.Equals("Content-Disposition", StringComparison.CurrentCultureIgnoreCase)) + { + string cd = task.response.Headers[key]; + fileName = cd.Substring(cd.IndexOf("filename=") + 9).Replace("\"", ""); + } + else if (key.Equals("Last-Modified", StringComparison.CurrentCultureIgnoreCase)) + { + task.lastTime = DateTime.Parse(task.response.Headers[key]); + } + } + + //Console.WriteLine(task.lastTime); + + if (task.mDlName == null) + task.mDlName = fileName; + + FileInfo testInfo = new FileInfo(task.mDlPath + @"\" + task.mDlName); + if (testInfo.Exists && testInfo.LastWriteTime == task.lastTime && testInfo.Length == task.mLength) + { + task.request.Abort(); + Success = 2; + } + else + { + // prepare download filename + if (!Directory.Exists(task.mDlPath)) + Directory.CreateDirectory(task.mDlPath); + if (task.mDlName.Length == 0 || task.mDlName[0] == '?') + task.mDlName = GetNextTempFile(task.mDlPath, "Download"); + + FileInfo info = new FileInfo(task.mDlPath + @"\" + task.mDlName + ".tmp"); + if (info.Exists) + info.Delete(); + + // Read the response into a Stream object. + task.streamResponse = task.response.GetResponseStream(); + + task.streamWriter = info.OpenWrite(); + + // Begin the Reading of the contents of the HTML page and print it to the console. + IAsyncResult asynchronousInputRead = task.streamResponse.BeginRead(task.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), task); + return; + } + } + catch (Exception e) + { + ErrCode = -2; + Error = e; + Console.WriteLine("\nRespCallback Exception raised!"); + Console.WriteLine("\nMessage:{0}", e.Message); + } + task.mDispatcher.Invoke(new Action(() => { + task.Finish(Success, ErrCode, Error); + })); + } + + private static void ReadCallBack(IAsyncResult asyncResult) + { + int Success = 0; + int ErrCode = 0; + Exception Error = null; + HttpTask task = (HttpTask)asyncResult.AsyncState; + try + { + int read = task.streamResponse.EndRead(asyncResult); + // Read the HTML page and then print it to the console. + if (read > 0) + { + task.streamWriter.Write(task.BufferRead, 0, read); + task.mOffset += read; + + task.mDispatcher.Invoke(new Action(() => { + task.ReportProgress(); + })); + + // setup next read + IAsyncResult asynchronousResult = task.streamResponse.BeginRead(task.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), task); + return; + } + else + { + // this is done on finisch + //task.streamWriter.Close(); + //task.streamResponse.Close(); + Success = 1; + } + + } + catch (Exception e) + { + ErrCode = -3; + Error = e; + Console.WriteLine("\nReadCallBack Exception raised!"); + Console.WriteLine("\nMessage:{0}", e.Message); + } + task.mDispatcher.Invoke(new Action(() => { + task.Finish(Success, ErrCode, Error); + })); + } + + public class FinishedEventArgs : EventArgs + { + public FinishedEventArgs(int ErrCode = 0, Exception Error = null) + { + this.ErrCode = ErrCode; + this.Error = Error; + } + public string GetError() + { + if (Error != null) + return Error.ToString(); + switch(ErrCode) + { + case -1: return "Canceled"; + default: return ErrCode.ToString(); + } + } + public bool Cancelled { get { return ErrCode == -1; } } + + public int ErrCode = 0; + public Exception Error = null; + } + public event EventHandler Finished; + + public class ProgressEventArgs : EventArgs + { + public ProgressEventArgs(int Percent) + { + this.Percent = Percent; + } + public int Percent = 0; + } + public event EventHandler Progress; +} diff --git a/wumgr/Common/MiscFunc.cs b/wumgr/Common/MiscFunc.cs new file mode 100644 index 0000000..e50139f --- /dev/null +++ b/wumgr/Common/MiscFunc.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + + +internal struct LASTINPUTINFO +{ + public uint cbSize; + public uint dwTime; +} + +class MiscFunc +{ + [DllImport("User32.dll")] + private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii); + + [DllImport("Kernel32.dll")] + private static extern uint GetLastError(); + + public static uint GetIdleTime() // in seconds + { + LASTINPUTINFO lastInPut = new LASTINPUTINFO(); + lastInPut.cbSize = (uint)Marshal.SizeOf(lastInPut); + if (!GetLastInputInfo(ref lastInPut)) + { + throw new Exception(GetLastError().ToString()); + } + return ((uint)Environment.TickCount - lastInPut.dwTime)/1000; + } + + public static int parseInt(string str, int def = 0) + { + try + { + return int.Parse(str); + } + catch + { + return def; + } + } + + public static String fmt(string str, params object[] args) + { + return string.Format(str, args); + } +} diff --git a/wumgr/Common/MultiValueDictionary.cs b/wumgr/Common/MultiValueDictionary.cs new file mode 100644 index 0000000..0644339 --- /dev/null +++ b/wumgr/Common/MultiValueDictionary.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +public class MultiValueDictionary : Dictionary> +{ + + public MultiValueDictionary() : base() + { + } + + public void Add(TKey key, TValue value) + { + List container = null; + if (!this.TryGetValue(key, out container)) + { + container = new List(); + base.Add(key, container); + } + container.Add(value); + } + + public bool ContainsValue(TKey key, TValue value) + { + bool toReturn = false; + List values = null; + if (this.TryGetValue(key, out values)) + { + toReturn = values.Contains(value); + } + return toReturn; + } + + public void Remove(TKey key, TValue value) + { + List container = null; + if (this.TryGetValue(key, out container)) + { + container.Remove(value); + if (container.Count <= 0) + { + this.Remove(key); + } + } + } + + public List GetValues(TKey key, bool returnEmptySet = true) + { + List toReturn = null; + if (!base.TryGetValue(key, out toReturn) && returnEmptySet) + { + toReturn = new List(); + } + return toReturn; + } + + public int GetCount() + { + int Count = 0; + foreach (KeyValuePair> pair in this) + Count += pair.Value.Count; + return Count; + } + + public TValue GetAt(int index) + { + int Count = 0; + foreach (KeyValuePair> pair in this) + { + if (Count + pair.Value.Count >= index) + return pair.Value[index - Count]; + Count += pair.Value.Count; + } + throw new IndexOutOfRangeException(); + } +} diff --git a/wumgr/ServiceHelper.cs b/wumgr/Common/ServiceHelper.cs similarity index 100% rename from wumgr/ServiceHelper.cs rename to wumgr/Common/ServiceHelper.cs diff --git a/wumgr/WinConsole.cs b/wumgr/Common/WinConsole.cs similarity index 100% rename from wumgr/WinConsole.cs rename to wumgr/Common/WinConsole.cs diff --git a/wumgr/MsUpdate.cs b/wumgr/MsUpdate.cs new file mode 100644 index 0000000..2289296 --- /dev/null +++ b/wumgr/MsUpdate.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WUApiLib; + +namespace wumgr +{ + public class MsUpdate + { + public MsUpdate() + { + } + + public MsUpdate(IUpdate update, UpdateState state) + { + Entry = update; + UUID = update.Identity.UpdateID; + + Title = update.Title; + Category = GetCategory(update.Categories); + Description = update.Description; + Size = update.MaxDownloadSize; + Date = update.LastDeploymentChangeTime; + KB = GetKB(update); + SupportUrl = update.SupportUrl; + + UpdateDownloads(); + + State = state; + + Attributes |= update.IsBeta ? (int)UpdateAttr.Beta : 0; + Attributes |= update.IsDownloaded ? (int)UpdateAttr.Downloaded : 0; + Attributes |= update.IsHidden ? (int)UpdateAttr.Hidden : 0; + Attributes |= update.IsInstalled ? (int)UpdateAttr.Installed : 0; + Attributes |= update.IsMandatory ? (int)UpdateAttr.Mandatory : 0; + Attributes |= update.IsUninstallable ? (int)UpdateAttr.Uninstallable : 0; + Attributes |= update.AutoSelectOnWebSites ? (int)UpdateAttr.AutoSelect : 0; + + if (update.InstallationBehavior.Impact == InstallationImpact.iiRequiresExclusiveHandling) + Attributes |= (int)UpdateAttr.Exclusive; + + switch (update.InstallationBehavior.RebootBehavior) + { + case InstallationRebootBehavior.irbAlwaysRequiresReboot: + Attributes |= (int)UpdateAttr.Reboot; + break; + case InstallationRebootBehavior.irbCanRequestReboot: + case InstallationRebootBehavior.irbNeverReboots: + break; + } + } + + public MsUpdate(IUpdateHistoryEntry2 update) + { + UUID = update.UpdateIdentity.UpdateID; + + Title = update.Title; + Category = GetCategory(update.Categories); + Description = update.Description; + Date = update.Date; + SupportUrl = update.SupportUrl; + + State = UpdateState.History; + + ResultCode = (int)update.ResultCode; + HResult = update.HResult; + } + + public void UpdateDownloads() + { + foreach (IUpdate bundle in Entry.BundledUpdates) + { + foreach (IUpdateDownloadContent udc in bundle.DownloadContents) + { + if (String.IsNullOrEmpty(udc.DownloadUrl)) + continue; // sanity check + Downloads.Add(udc.DownloadUrl); + } + } + } + + static string GetKB(IUpdate update) + { + return update.KBArticleIDs.Count > 0 ? "KB" + update.KBArticleIDs[0] : "KBUnknown"; + } + + static public string GetCategory(ICategoryCollection cats) + { + string classification = ""; + string product = ""; + foreach (ICategory cat in cats) + { + if (cat.Type.Equals("UpdateClassification")) + classification = cat.Name; + else if (cat.Type.Equals("Product")) + product = cat.Name; + + } + return product + "; " + classification; + } + + public void Invalidate() { Entry = null; } + + public IUpdate GetUpdate() + { + /*if (Entry == null) + { + WuAgent agen = WuAgent.GetInstance(); + if (agen.IsActive()) + Entry = agen.FindUpdate(UUID); + }*/ + return Entry; + } + + private IUpdate Entry = null; + + public string UUID = ""; + public String Title = ""; + public String Description = ""; + public String Category = ""; + public String KB = ""; + public DateTime Date = DateTime.MinValue; + public decimal Size = 0; + public String SupportUrl = ""; + public System.Collections.Specialized.StringCollection Downloads = new System.Collections.Specialized.StringCollection(); + + public enum UpdateState + { + None = 0, + Pending, + Installed, + Hidden, + History + } + public UpdateState State = UpdateState.None; + public enum UpdateAttr + { + None = 0x0000, + Beta = 0x0001, + Downloaded = 0x0002, + Hidden = 0x0004, + Installed = 0x0008, + Mandatory = 0x0010, + Uninstallable = 0x0020, + Exclusive = 0x0040, + Reboot = 0x0080, + AutoSelect = 0x0100 + } + public int Attributes = 0; + public int ResultCode = 0; + public int HResult = 0; + } +} diff --git a/wumgr/Program.cs b/wumgr/Program.cs index e9da347..ea7c704 100644 --- a/wumgr/Program.cs +++ b/wumgr/Program.cs @@ -11,16 +11,12 @@ using System.Threading.Tasks; using System.Windows.Forms; using TaskScheduler; +using System.Collections.Specialized; namespace wumgr { static class Program { - public static String fmt(string str, params object[] args) - { - return string.Format(str, args); - } - public static string[] args = null; public static bool mConsole = false; public static string mVersion = "0.0"; @@ -28,6 +24,20 @@ public static String fmt(string str, params object[] args) private static string nTaskName = "WuMgrNoUAC"; public static string appPath = ""; private static string mINIPath = ""; + public static WuAgent Agent = null; + + public const Int32 WM_USER = 0x0400; + public const Int32 WM_APP = 0x0800; // to 0xBFFF + [DllImport("user32.dll")] + static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); + [DllImport("user32.dll")] + public static extern IntPtr FindWindow(string lpClassName, String lpWindowName); + [DllImport("user32.dll")] + public static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam); + [DllImport("user32.dll")] + public static extern bool SetForegroundWindow(IntPtr hWnd); + [DllImport("user32.dll")] + public static extern int SendMessageTimeout(IntPtr windowHandle, uint Msg, IntPtr wParam, IntPtr lParam, uint flags, uint timeout, out IntPtr result); /// /// Der Haupteinstiegspunkt für die Anwendung. @@ -52,13 +62,45 @@ static void Main(string[] args) mVersion = fvi.FileMajorPart + "." + fvi.FileMinorPart; AppLog Log = new AppLog(); - AppLog.Line(Program.fmt("{0}, Version v{1} by David Xanatos", mName, mVersion)); - AppLog.Line(Program.fmt("This Tool is Open Source under the GNU General Public License, Version 3\r\n")); + AppLog.Line(MiscFunc.fmt("{0}, Version v{1} by David Xanatos", mName, mVersion)); + AppLog.Line(MiscFunc.fmt("This Tool is Open Source under the GNU General Public License, Version 3\r\n")); appPath = Path.GetDirectoryName(Application.ExecutablePath); + Process current = Process.GetCurrentProcess(); + foreach (Process process in Process.GetProcessesByName(current.ProcessName)) + { + if (process.Id != current.Id) + { + AppLog.Line(MiscFunc.fmt("Application is already running. Only one instance of this application is allowed")); + //IntPtr WindowToFind = FindWindow(null, Program.mName); + IntPtr result = IntPtr.Zero; + if (SendMessageTimeout(process.MainWindowHandle, WM_APP, IntPtr.Zero, IntPtr.Zero, 0, 3000, out result) == 0) + { + MessageBox.Show(MiscFunc.fmt("Application is already running, but not responding.\r\nClose it using a task manager and restart.")); + } + //SetForegroundWindow(process.MainWindowHandle); + return; + } + } + + mINIPath = appPath + @"\wumgr.ini"; + /*switch(FileOps.TestFileAdminSec(mINIPath)) + { + case 0: + AppLog.Line(MiscFunc.fmt("Warning wumgr.ini was writable by non administrative users, it was renamed to wumgr.ini.old and replaced with a empty one.\r\n")); + if (!FileOps.MoveFile(mINIPath, mINIPath + ".old", true)) + return; + goto case 2; + case 2: // file missing, create + FileOps.SetFileAdminSec(mINIPath); + break; + case 1: // every thign's fine ini file is only writable by admins + break; + }*/ + #if DEBUG Test(); #endif @@ -79,14 +121,9 @@ static void Main(string[] args) return; } - WuAgent Agent = new WuAgent(); - - if (int.Parse(IniReadValue("OnStart", "EnableWuAuServ", "0")) != 0) - Agent.EnableWuAuServ(true); + Agent = new WuAgent(); - string OnStart = IniReadValue("OnStart", "Exec", ""); - if(OnStart.Length > 0) - Process.Start(OnStart); + ExecOnStart(); Agent.Init(); @@ -96,17 +133,45 @@ static void Main(string[] args) Agent.UnInit(); + ExecOnClose(); + } + + static private void ExecOnStart() + { + if (int.Parse(IniReadValue("OnStart", "EnableWuAuServ", "0")) != 0) + Agent.EnableWuAuServ(true); + + string OnStart = IniReadValue("OnStart", "Exec", ""); + if (OnStart.Length > 0) + { + Process proc = Process.Start(OnStart); + proc.WaitForExit(); + } + } + + static private void ExecOnClose() + { string OnClose = IniReadValue("OnClose", "Exec", ""); if (OnClose.Length > 0) - Process.Start(OnClose); + { + Process proc = Process.Start(OnClose); + proc.WaitForExit(); + } if (int.Parse(IniReadValue("OnClose", "DisableWuAuServ", "0")) != 0) Agent.EnableWuAuServ(false); - for (int i = 0; i < Program.args.Length; i++) + // Note: With the UAC bypass the onclose parameter can be used for a local privilege escalation exploit + if (!TestArg("-NoUAC")) { - if (Program.args[i].Equals("-onclose", StringComparison.CurrentCultureIgnoreCase)) - Process.Start(Program.args[++i]); + for (int i = 0; i < Program.args.Length; i++) + { + if (Program.args[i].Equals("-onclose", StringComparison.CurrentCultureIgnoreCase)) + { + Process proc = Process.Start(Program.args[++i]); + proc.WaitForExit(); + } + } } } @@ -117,18 +182,25 @@ static private void Test() [DllImport("kernel32")] private static extern long WritePrivateProfileString(string section, string key, string val, string filePath); - public static void IniWriteValue(string Section, string Key, string Value) + public static void IniWriteValue(string Section, string Key, string Value, string INIPath = null) { - WritePrivateProfileString(Section, Key, Value, mINIPath); + WritePrivateProfileString(Section, Key, Value, INIPath != null ? INIPath : mINIPath); } [DllImport("kernel32")] - private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath); - public static string IniReadValue(string Section, string Key, string Default = "") + private static extern int GetPrivateProfileString(string section, string key, string def, [In, Out] char[] retVal, int size, string filePath); + public static string IniReadValue(string Section, string Key, string Default = "", string INIPath = null) + { + char[] chars = new char[8193]; + int size = GetPrivateProfileString(Section, Key, Default, chars, 8193, INIPath != null ? INIPath : mINIPath); + return new String(chars, 0, size); + } + + public static string[] IniEnumSections(string INIPath = null) { - StringBuilder temp = new StringBuilder(1025); - int i = GetPrivateProfileString(Section, Key, Default, temp, 1025, mINIPath); - return temp.ToString(); + char[] chars = new char[8193]; + int size = GetPrivateProfileString(null, null, null, chars, 8193, INIPath != null ? INIPath : mINIPath); + return new String(chars, 0, size).Split('\0'); } private static bool IsAdministrator() @@ -218,7 +290,7 @@ static public bool SkipUacEnable(bool is_enable) IExecAction action = (IExecAction)task.Actions.Create(_TASK_ACTION_TYPE.TASK_ACTION_EXEC); action.Path = System.Reflection.Assembly.GetExecutingAssembly().Location; action.WorkingDirectory = appPath; - action.Arguments = "$(Arg0)"; + action.Arguments = "-NoUAC $(Arg0)"; IRegisteredTask registered_task = folder.RegisterTaskDefinition(nTaskName, task, (int)_TASK_CREATION.TASK_CREATE_OR_UPDATE, null, null, _TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN); @@ -232,7 +304,7 @@ static public bool SkipUacEnable(bool is_enable) } catch (Exception err) { - AppLog.Line(Program.fmt("SkipUacEnable Error {0}", err.ToString())); + AppLog.Line(MiscFunc.fmt("SkipUacEnable Error {0}", err.ToString())); return false; } } @@ -269,7 +341,7 @@ static public bool SkipUacRun() } catch (Exception err) { - AppLog.Line(Program.fmt("SkipUacRun Error {0}", err.ToString())); + AppLog.Line(MiscFunc.fmt("SkipUacRun Error {0}", err.ToString())); } return false; } diff --git a/wumgr/Properties/AssemblyInfo.cs b/wumgr/Properties/AssemblyInfo.cs index f1adee1..818b3c3 100644 --- a/wumgr/Properties/AssemblyInfo.cs +++ b/wumgr/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, // übernehmen, indem Sie "*" eingeben: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.4.0.0")] -[assembly: AssemblyFileVersion("0.4.0.0")] +[assembly: AssemblyVersion("0.5.0.0")] +[assembly: AssemblyFileVersion("0.5.0.0")] diff --git a/wumgr/UpdateDownloader.cs b/wumgr/UpdateDownloader.cs new file mode 100644 index 0000000..bbec1d9 --- /dev/null +++ b/wumgr/UpdateDownloader.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Threading; + +namespace wumgr +{ + class UpdateDownloader + { + public struct Task { + public string Url; + public string Path; + public string FileName; + public bool Failed; + public string KB; + } + + private List mDownloads = null; + private List mUpdates = null; + private int mCurrentTask = 0; + private HttpTask mCurTask = null; + + public UpdateDownloader() + { + } + + public bool Download(List Downloads, List Updates = null) + { + if (mDownloads != null) + return false; + + mDownloads = Downloads; + mCurrentTask = 0; + mUpdates = Updates; + + DownloadNextFile(); + return true; + } + + public bool IsBusy() + { + return (mDownloads != null); + } + + public void CancelOperations() + { + if(mCurTask != null) + mCurTask.Cancel(); + } + + private void DownloadNextFile() + { + while (mCurrentTask != -1 && mDownloads.Count > mCurrentTask) + { + Task Download = mDownloads[mCurrentTask]; + mCurTask = new HttpTask(Download.Url, Download.Path, Download.FileName, true); // todo update flag + mCurTask.Progress += OnProgress; + mCurTask.Finished += OnFinished; + if (mCurTask.Start()) + return; + } + + FinishedEventArgs args = new FinishedEventArgs(); + args.Downloads = mDownloads; + mDownloads = null; + args.Updates = mUpdates; + mUpdates = null; + Finished?.Invoke(this, args); + } + + void OnProgress(object sender, HttpTask.ProgressEventArgs args) + { + Progress?.Invoke(this, new ProgressEventArgs(mDownloads.Count, mDownloads.Count == 0 ? 0 : (100 * mCurrentTask + args.Percent) / mDownloads.Count, mCurrentTask, args.Percent)); + } + + void OnFinished(object sender, HttpTask.FinishedEventArgs args) + { + if (!args.Cancelled) + { + Task Download = mDownloads[mCurrentTask]; + if (args.ErrCode != 0) + { + AppLog.Line(MiscFunc.fmt("Download failed: {0}", args.GetError())); + if (File.Exists(mCurTask.DlPath + @"\" + mCurTask.DlName)) + AppLog.Line(MiscFunc.fmt("An older version is present and will be used.")); + else + Download.Failed = true; + } + + Download.FileName = mCurTask.DlName; + mDownloads[mCurrentTask] = Download; + mCurTask = null; + + mCurrentTask++; + } + else + mCurrentTask = -1; + DownloadNextFile(); + } + + public class FinishedEventArgs : EventArgs + { + public List Downloads; + public List Updates; + public bool Success + { + get { + foreach (Task task in Downloads) + { + if (task.Failed) + return false; + } + return true; + } + } + } + public event EventHandler Finished; + + public class ProgressEventArgs : EventArgs + { + public ProgressEventArgs(int TotalFiles, int TotalPercent, int CurrentIndex, int CurrentPercent) + { + this.TotalFiles = TotalFiles; + this.TotalPercent = TotalPercent; + this.CurrentIndex = CurrentIndex; + this.CurrentPercent = CurrentPercent; + } + + public int TotalFiles = 0; + public int TotalPercent = 0; + public int CurrentIndex = 0; + public int CurrentPercent = 0; + } + public event EventHandler Progress; + } +} diff --git a/wumgr/WinErrors.cs b/wumgr/UpdateErrors.cs similarity index 99% rename from wumgr/WinErrors.cs rename to wumgr/UpdateErrors.cs index 895108c..88bb456 100644 --- a/wumgr/WinErrors.cs +++ b/wumgr/UpdateErrors.cs @@ -6,7 +6,7 @@ namespace wumgr { - static class WinErrors + static class UpdateErrors { static public string GetErrorStr(uint id) { diff --git a/wumgr/UpdateInstaller.cs b/wumgr/UpdateInstaller.cs new file mode 100644 index 0000000..bffd68e --- /dev/null +++ b/wumgr/UpdateInstaller.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Threading; + +namespace wumgr +{ + class UpdateInstaller + { + List mUpdates = null; + private MultiValueDictionary mAllFiles = null; + private int mCurrentTask = 0; + private Thread mThread = null; + private Dispatcher mDispatcher; + + public UpdateInstaller() + { + mDispatcher = Dispatcher.CurrentDispatcher; + } + + public bool Install(List Updates, MultiValueDictionary AllFiles) + { + /*mUpdates = Updates; + mAllFiles = AllFiles; + mCurrentTask = 0; + + InstallNextFile(); + return true;*/ + AppLog.Line("\"Manual\" update instalation is not yet implemented."); + return false; + } + + public bool UnInstall(List Updates) + { + AppLog.Line("\"Manual\" update deinstalation is not yet implemented."); + return false; + } + + public bool IsBusy() + { + return mUpdates != null; + } + + public void CancelOperations() + { + // TODO + } + + private void InstallNextFile() + { + while (mCurrentTask != -1 && mAllFiles.GetCount() > mCurrentTask) + { + string File = mAllFiles.GetAt(mCurrentTask); + + mThread = new Thread(new ParameterizedThreadStart(Run)); + mThread.Start(File); + } + + FinishedEventArgs args = new FinishedEventArgs(); + //args.AllFiles = mAllFiles; + mAllFiles = null; + args.Updates = mUpdates; + mUpdates = null; + Finished?.Invoke(this, args); + } + + public void Run(object parameters) + { + string File = (string)parameters; + + string ext = Path.GetExtension(File); + if (ext.Equals(".exe", StringComparison.CurrentCultureIgnoreCase)) + ; + else if (ext.Equals(".msi", StringComparison.CurrentCultureIgnoreCase)) + ; + else if (ext.Equals(".msu", StringComparison.CurrentCultureIgnoreCase)) + ; + else if (ext.Equals(".zip", StringComparison.CurrentCultureIgnoreCase)) + ; + else if (ext.Equals(".cab", StringComparison.CurrentCultureIgnoreCase)) + ; + else + AppLog.Line(MiscFunc.fmt("Unknown Update format: {0}", ext)); + // TODO do install + + mDispatcher.BeginInvoke(new Action(() => { + Finish(); + })); + } + + private void Finish() + { + mThread.Join(); + mThread = null; + + mCurrentTask++; + + int Percent = 0; // TODO if possible get progress of individual updates + Progress?.Invoke(this, new ProgressEventArgs(mAllFiles.Count, mAllFiles.Count == 0 ? 0 : (100 * mCurrentTask + Percent) / mAllFiles.Count, mCurrentTask, Percent)); + + InstallNextFile(); + } + + public class FinishedEventArgs : EventArgs + { + public List Updates; + public bool Success = true; + public bool Reboot = false; + //public MultiValueDictionary AllFiles; + } + public event EventHandler Finished; + + public class ProgressEventArgs : EventArgs + { + public ProgressEventArgs(int TotalFiles, int TotalPercent, int CurrentIndex, int CurrentPercent) + { + this.TotalFiles = TotalFiles; + this.TotalPercent = TotalPercent; + this.CurrentIndex = CurrentIndex; + this.CurrentPercent = CurrentPercent; + } + + public int TotalFiles = 0; + public int TotalPercent = 0; + public int CurrentIndex = 0; + public int CurrentPercent = 0; + } + public event EventHandler Progress; + } +} diff --git a/wumgr/WebDownloader.cs b/wumgr/WebDownloader.cs deleted file mode 100644 index 2e1aac3..0000000 --- a/wumgr/WebDownloader.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; - -namespace wumgr -{ - class MyWebClient : WebClient - { - Uri mResponseUri; - - public Uri ResponseUri - { - get { return mResponseUri; } - } - - private bool mNewUri = false; - public bool HasNewUri() - { - if (mNewUri) - { - mNewUri = false; - return true; - } - return false; - } - - protected override WebResponse GetWebResponse(WebRequest request, IAsyncResult result) - { - LastException = null; - try - { - WebResponse response = base.GetWebResponse(request, result); - mResponseUri = response.ResponseUri; - mNewUri = true; - return response; - } - catch(Exception err) // note: thats a realy uggly workaround - { - LastException = err; - return null; - } - } - - public Exception LastException = null; - } - - class WebDownloader - { - public struct Task { - public string Url; - public string Path; - public string FileName; - public bool Failed; - } - - private MyWebClient mWebClient = null; - private List mDownloads = null; - private int mCurrentTask = 0; - private string mDownloadFileName = ""; - private string mDownloadPath = ""; - - public WebDownloader() - { - mWebClient = new MyWebClient(); - mWebClient.DownloadProgressChanged += wc_dlProgress; - mWebClient.DownloadFileCompleted += wd_Finish; - } - - public bool Download(List Downloads) - { - if (mDownloads != null) - return false; - - mDownloads = Downloads; - mCurrentTask = 0; - - DownloadNextFile(); - return true; - } - - public bool IsBusy() - { - return (mDownloads != null); - } - - public void CancelOperations() - { - mWebClient.CancelAsync(); - } - - static public string GetNextTempFile(string path, string baseName) - { - for (int i = 0; i < 10000; i++) - { - var fi = new FileInfo(path + @"\" + baseName + "_" + i + ".tmp"); - if (!fi.Exists) - return baseName + "_" + i; - } - return baseName; - } - - private void DownloadNextFile() - { - while (mCurrentTask != -1 && mDownloads.Count > mCurrentTask) - { - Task Download = mDownloads[mCurrentTask]; - try - { - if (!Directory.Exists(Download.Path)) - Directory.CreateDirectory(Download.Path); - - if (Download.FileName == null) - { - mDownloadFileName = Path.GetFileName(Download.Url); - if (mDownloadFileName.Length == 0 || mDownloadFileName[0] == '?') - mDownloadFileName = GetNextTempFile(Download.Path, "Download"); - } - else - mDownloadFileName = Download.FileName; - - mDownloadPath = Download.Path + @"\" + mDownloadFileName + ".tmp"; - - var fi = new FileInfo(mDownloadPath); - - if (fi.Exists) - fi.Delete(); - } - catch - { - mCurrentTask++; - Download.Failed = true; - mDownloads[mCurrentTask] = Download; - continue; - } - - mWebClient.DownloadFileAsync(new Uri(Download.Url), mDownloadPath); - return; - } - - FinishedEventArgs args = new FinishedEventArgs(); - args.Downloads = mDownloads; - mDownloads = null; - Finished?.Invoke(this, args); - } - - void wc_dlProgress(object sender, DownloadProgressChangedEventArgs e) - { - //string cd = mWebClient.ResponseHeaders["Content-Disposition"]; - //string fileName = cd != null ? cd.Substring(cd.IndexOf("filename=") + 9).Replace("\"", "") : ""; - Task Download = mDownloads[mCurrentTask]; - if (Download.FileName == null && mWebClient.HasNewUri()) - { - if (mWebClient.ResponseUri != null) - { - string FileName = Path.GetFileName(mWebClient.ResponseUri.ToString()); - if (FileName.Length > 0 && FileName[0] != '?') - mDownloadFileName = FileName; - } - } - - Progress?.Invoke(this, new ProgressEventArgs(mDownloads.Count, mDownloads.Count == 0 ? 0 : (100 * mCurrentTask + e.ProgressPercentage) / mDownloads.Count, mCurrentTask, e.ProgressPercentage)); - } - - void wd_Finish(object sender, AsyncCompletedEventArgs args) - { - Task Download = mDownloads[mCurrentTask]; - if (args.Error != null || args.Cancelled) - { - var fi = new FileInfo(Download.Path + @"\" + mDownloadFileName); - try { fi.Delete(); } catch { } // delete partial file - if (!args.Cancelled) - { - AppLog.Line(Program.fmt("Download failed: {0}", (mWebClient.LastException != null ? mWebClient.LastException : args.Error).ToString())); - Download.Failed = true; - } - } - else - { - var fi = new FileInfo(Download.Path + @"\" + mDownloadFileName); - try - { - if (fi.Exists) - fi.Delete(); - var tfi = new FileInfo(mDownloadPath); - tfi.MoveTo(Download.Path + @"\" + mDownloadFileName); - - Download.FileName = mDownloadFileName; - } - catch - { - AppLog.Line(Program.fmt("Failed to rename download {0} to {1}", mDownloadPath, mDownloadFileName)); - - Download.FileName = mDownloadFileName + ".tmp"; - } - } - mDownloads[mCurrentTask] = Download; - - if(args.Cancelled) - mCurrentTask = -1; - else - mCurrentTask++; - DownloadNextFile(); - } - - public class FinishedEventArgs : EventArgs - { - public List Downloads; - public bool Success - { - get { - foreach (Task task in Downloads) - { - if (task.Failed) - return false; - } - return true; - } - } - } - public event EventHandler Finished; - - public class ProgressEventArgs : EventArgs - { - public ProgressEventArgs(int TotalFiles, int TotalPercent, int CurrentIndex, int CurrentPercent) - { - this.TotalFiles = TotalFiles; - this.TotalPercent = TotalPercent; - this.CurrentIndex = CurrentIndex; - this.CurrentPercent = CurrentPercent; - } - - public int TotalFiles = 0; - public int TotalPercent = 0; - public int CurrentIndex = 0; - public int CurrentPercent = 0; - } - public event EventHandler Progress; - } -} diff --git a/wumgr/WuAgent.cs b/wumgr/WuAgent.cs index f0bf93d..cfe519b 100644 --- a/wumgr/WuAgent.cs +++ b/wumgr/WuAgent.cs @@ -11,6 +11,7 @@ using System.ComponentModel; using System.Windows.Forms; using System.ServiceProcess; +using System.Collections.Specialized; namespace wumgr { @@ -21,43 +22,52 @@ class WuAgent IUpdateService mOfflineService = null; IUpdateSearcher mUpdateSearcher = null; ISearchJob mSearchJob = null; - UpdateDownloader mDownloader = null; + WUApiLib.UpdateDownloader mDownloader = null; IDownloadJob mDownloadJob = null; - UpdateInstaller mInstaller = null; + IUpdateInstaller mInstaller = null; IInstallationJob mInstalationJob = null; - public List mUpdateHistory = null; - public UpdateCollection mPendingUpdates = null; - public UpdateCollection mInstalledUpdates = null; - public UpdateCollection mHiddenUpdates = null; + public List mUpdateHistory = new List(); + public List mPendingUpdates = new List(); + public List mInstalledUpdates = new List(); + public List mHiddenUpdates = new List(); - public StringCollection mServiceList = new StringCollection(); + public System.Collections.Specialized.StringCollection mServiceList = new System.Collections.Specialized.StringCollection(); private static WuAgent mInstance = null; public static WuAgent GetInstance() { return mInstance; } - WebDownloader mWebDownloader = null; + UpdateDownloader mUpdateDownloader = null; + UpdateInstaller mUpdateInstaller = null; public WuAgent() { mInstance = this; mDispatcher = Dispatcher.CurrentDispatcher; - mWebDownloader = new WebDownloader(); - mWebDownloader.Finished += DownloadsFinished; - mWebDownloader.Progress += DownloadProgress; + mUpdateDownloader = new UpdateDownloader(); + mUpdateDownloader.Finished += DownloadsFinished; + mUpdateDownloader.Progress += DownloadProgress; + + + mUpdateInstaller = new UpdateInstaller(); + mUpdateInstaller.Finished += InstallFinished; + mUpdateInstaller.Progress += InstallProgress; dlPath = Program.appPath + @"\Downloads"; WindowsUpdateAgentInfo info = new WindowsUpdateAgentInfo(); var currentVersion = info.GetInfo("ApiMajorVersion").ToString().Trim() + "." + info.GetInfo("ApiMinorVersion").ToString().Trim() + " (" + info.GetInfo("ProductVersionString").ToString().Trim() + ")"; - AppLog.Line(Program.fmt("Windows Update Agent Version: {0}", currentVersion)); + AppLog.Line(MiscFunc.fmt("Windows Update Agent Version: {0}", currentVersion)); mUpdateSession = new UpdateSession(); mUpdateSession.ClientApplicationID = Program.mName; - mUpdateSession.UserLocale = 1033; // alwys show strings in englisch, we need that to know what each catergory means + //mUpdateSession.UserLocale = 1033; // alwys show strings in englisch mUpdateServiceManager = new UpdateServiceManager(); + + if(MiscFunc.parseInt(Program.IniReadValue("Options", "LoadLists", "0")) != 0) + LoadUpdates(); } protected Dispatcher mDispatcher = null; @@ -78,6 +88,8 @@ public bool Init() public void UnInit() { ClearOffline(); + + mUpdateSearcher = null; } public bool IsActive() @@ -87,7 +99,7 @@ public bool IsActive() public bool IsBusy() { - if (mWebDownloader.IsBusy()) + if (mUpdateDownloader.IsBusy()) return true; return mCurOperation != AgentOperation.None; } @@ -125,7 +137,7 @@ public bool LoadServices(bool cleanUp = false) private void LogError(Exception error) { uint errCode = (uint)error.HResult; - AppLog.Line("Error: " + WinErrors.GetErrorStr(errCode)); + AppLog.Line(MiscFunc.fmt("Error 0x{0}: {1}", errCode.ToString("X").PadLeft(8,'0'), UpdateErrors.GetErrorStr(errCode))); } public static string MsUpdGUID = "7971f918-a847-4430-9279-4a52d1efe18d"; // Microsoft Update @@ -181,15 +193,15 @@ public string GetServiceName(string ID, bool bAdd = false) public void UpdateHistory() { + mUpdateHistory.Clear(); int count = mUpdateSearcher.GetTotalHistoryCount(); - mUpdateHistory = new List(); - if (count == 0) + if (count == 0) // sanity check return; foreach (IUpdateHistoryEntry2 update in mUpdateSearcher.QueryHistory(0, count)) { - if (update.Title == null) + if (update.Title == null) // sanity check continue; - mUpdateHistory.Add(update); + mUpdateHistory.Add(new MsUpdate(update)); } } @@ -201,7 +213,7 @@ private bool SetupOffline() { if (mOfflineService == null) { - AppLog.Line(Program.fmt("Setting up 'Offline Sync Service'")); + AppLog.Line(MiscFunc.fmt("Setting up 'Offline Sync Service'")); // http://go.microsoft.com/fwlink/p/?LinkID=74689 mOfflineService = mUpdateServiceManager.AddScanPackageService(mMyOfflineSvc, dlPath + @"\wsusscn2.cab"); @@ -219,10 +231,26 @@ private bool SetupOffline() } } + private bool mIsValid = false; + public bool IsValid() { return mIsValid; } + private void ClearOffline() { if (mOfflineService != null) { + // note: if we keep references to updates reffering to an removed service we may got a crash + foreach (MsUpdate Update in mUpdateHistory) + Update.Invalidate(); + foreach (MsUpdate Update in mPendingUpdates) + Update.Invalidate(); + foreach (MsUpdate Update in mInstalledUpdates) + Update.Invalidate(); + foreach (MsUpdate Update in mHiddenUpdates) + Update.Invalidate(); + mIsValid = false; + + OnUpdatesChanged(); + mUpdateServiceManager.RemoveService(mOfflineService.ServiceID); mOfflineService = null; } @@ -260,7 +288,7 @@ public enum AgentOperation public bool SearchForUpdates(String Source = "", bool IncludePotentiallySupersededUpdates = false) { - if (IsBusy()) + if (mCallback != null) return false; mUpdateSearcher.IncludePotentiallySupersededUpdates = IncludePotentiallySupersededUpdates; @@ -271,50 +299,33 @@ public bool SearchForUpdates(String Source = "", bool IncludePotentiallySupersed return true; } - public bool SearchForUpdates(int Download, bool IncludePotentiallySupersededUpdates = false) + public bool SearchForUpdates(bool Download, bool IncludePotentiallySupersededUpdates = false) { - if (IsBusy()) + if (IsBusy()) // if (mCallback != null) return false; mUpdateSearcher.IncludePotentiallySupersededUpdates = IncludePotentiallySupersededUpdates; mCurOperation = AgentOperation.PreparingCheck; - if (Download != 0) + if (Download) { - bool isDownloaded = false; + OnProgress(-1, 0, 0, 0); - if (!Directory.Exists(dlPath)) - Directory.CreateDirectory(dlPath); + AppLog.Line(MiscFunc.fmt("downloading wsusscn2.cab")); - var fi = new FileInfo(dlPath + @"\wsusscn2.cab"); - if (fi.Exists) { - if (!(Download == 1 || fi.LastWriteTime < DateTime.Today.Subtract(new TimeSpan(1, 0, 0, 0)))) - { - isDownloaded = true; - AppLog.Line(Program.fmt("up to date wsusscn2.cab is already downloaded")); - } - } - - if (!isDownloaded) + List downloads = new List(); + UpdateDownloader.Task download = new UpdateDownloader.Task(); + download.Url = Program.IniReadValue("Options", "OfflineCab", "http://go.microsoft.com/fwlink/p/?LinkID=74689"); + download.Path = dlPath; + download.FileName = "wsusscn2.cab"; + downloads.Add(download); + if(!mUpdateDownloader.Download(downloads)) { - OnProgress(-1, 0, 0, 0); - - AppLog.Line(Program.fmt("downloading wsusscn2.cab")); - - List downloads = new List(); - WebDownloader.Task download = new WebDownloader.Task(); - download.Url = "http://go.microsoft.com/fwlink/p/?LinkID=74689"; - download.Path = dlPath; - download.FileName = "wsusscn2.cab"; - downloads.Add(download); - if(!mWebDownloader.Download(downloads)) - { - mCurOperation = AgentOperation.None; - return false; - } - return true; + mCurOperation = AgentOperation.None; + return false; } + return true; } SetupOffline(); @@ -331,15 +342,27 @@ private void SearchForUpdates() mCallback = new UpdateCallback(this); - AppLog.Line(Program.fmt("Searching for updates")); + AppLog.Line(MiscFunc.fmt("Searching for updates")); //for the above search criteria refer to // http://msdn.microsoft.com/en-us/library/windows/desktop/aa386526(v=VS.85).aspx mSearchJob = mUpdateSearcher.BeginSearch("(IsInstalled = 0 and IsHidden = 0) or (IsInstalled = 1 and IsHidden = 0) or (IsHidden = 1)", mCallback, null); } + public IUpdate FindUpdate(string UUID) + { + if (mUpdateSearcher == null) + return null; + // Note: this is sloooow! + ISearchResult result = mUpdateSearcher.Search("UpdateID = '" + UUID + "'"); + if (result.Updates.Count > 0) + return result.Updates[0]; + return null; + } + public void CancelOperations() { - mWebDownloader.CancelOperations(); + mUpdateDownloader.CancelOperations(); + mUpdateInstaller.CancelOperations(); if (mSearchJob != null) { @@ -379,85 +402,173 @@ public void CancelOperations() mCallback = null; } - public bool DownloadUpdatesOffline(UpdateCollection Updates, bool Install = false) + public bool DownloadUpdatesOffline(List Updates, bool Install = false) { - if (IsBusy()) + if (mUpdateDownloader.IsBusy()) return false; mCurOperation = Install ? AgentOperation.PreparingUpdates : AgentOperation.DownloadingUpdates; - - OnProgress(-1, 0, 0, 0); - - List downloads = new List(); - foreach (IUpdate update in Updates) + + List downloads = new List(); + foreach (MsUpdate Update in Updates) { - string KB = update.KBArticleIDs.Count > 0 ? "KB" + update.KBArticleIDs[0] : "KBUnknown"; - int Counter = 0; - foreach (IUpdate bundle in update.BundledUpdates) + if (Update.Downloads.Count == 0) { - foreach (IUpdateDownloadContent udc in bundle.DownloadContents) - { - if (String.IsNullOrEmpty(udc.DownloadUrl)) - continue; - Counter++; - WebDownloader.Task download = new WebDownloader.Task(); - download.Url = udc.DownloadUrl; - download.Path = dlPath + @"\" + KB; - downloads.Add(download); - } + AppLog.Line(MiscFunc.fmt("Error: No Download Url's found for update {0}", Update.Title)); + continue; } - if (Counter == 0) - AppLog.Line(Program.fmt("Error: No Download Url's found for update {0}", update.Title)); + + foreach (string url in Update.Downloads) + { + UpdateDownloader.Task download = new UpdateDownloader.Task(); + download.Url = url; + download.Path = dlPath + @"\" + Update.KB; + download.KB = Update.KB; + downloads.Add(download); + } + } + + if (mUpdateDownloader.Download(downloads, Updates)) + { + OnProgress(-1, 0, 0, 0); + return true; } - /*WebDownloader.Task download = new WebDownloader.Task(); - //download.Url = "http://download.windowsupdate.com/d/msdownload/update/software/secu/2018/08/windows10.0-kb4343902-x64_346f95cde08164057941d1182e28cf35ff6dfca7.cab"; - download.Url = "http://go.microsoft.com/fwlink/p/?LinkID=74689"; - download.Path = dlPath + @"\" + "KB1234567890"; - downloads.Add(download);*/ - return mWebDownloader.Download(downloads); + return false; } - void DownloadsFinished(object sender, WebDownloader.FinishedEventArgs args) + private bool InstallUpdatesOffline(List Updates, MultiValueDictionary AllFiles) { - if (mCurOperation == AgentOperation.CheckingUpdates) + if (mUpdateInstaller.IsBusy()) + return false; + + mCurOperation = AgentOperation.InstallingUpdates; + + if (mUpdateInstaller.Install(Updates, AllFiles)) { - AppLog.Line(Program.fmt("wsusscn2.cab downloaded")); + OnProgress(-1, 0, 0, 0); + return true; + } + return false; + } + + + public bool UnInstallUpdatesOffline(List Updates) + { + if (mUpdateInstaller.IsBusy()) + return false; + + mCurOperation = AgentOperation.RemoveingUpdtes; + + if (mUpdateInstaller.UnInstall(Updates)) + { + OnProgress(-1, 0, 0, 0); + return true; + } + return false; + } + + void DownloadsFinished(object sender, UpdateDownloader.FinishedEventArgs args) + { + if (mCurOperation == AgentOperation.PreparingCheck) + { + AppLog.Line(MiscFunc.fmt("wsusscn2.cab downloaded")); ClearOffline(); SetupOffline(); SearchForUpdates(); } - else if (mCurOperation == AgentOperation.InstallingUpdates) + else { - // TODO: - MessageBox.Show("\"Manual\" update instalation is not yet implemented.\r\nYou have to install the downloaded updates manually"); - AppLog.Line(Program.fmt("Updates downloaded to {0}", dlPath)); + MultiValueDictionary AllFiles = new MultiValueDictionary(); + foreach (UpdateDownloader.Task task in args.Downloads) + AllFiles.Add(task.KB, task.Path + @"\" + task.FileName); + + // TODO + /*string INIPath = dlPath + @"\updates.ini"; + foreach (string KB in AllFiles.Keys) + { + string Files = ""; + foreach (string FileName in AllFiles.GetValues(KB)) + { + if (Files.Length > 0) + Files += "|"; + Files += FileName; + } + Program.IniWriteValue(KB, "Files", Files, INIPath); + }*/ + + if (mCurOperation != AgentOperation.PreparingUpdates) + AppLog.Line(MiscFunc.fmt("Updates downloaded to {0}", dlPath)); + else if (InstallUpdatesOffline(args.Updates, AllFiles)) + return; + OnFinished(args.Success); } - else + } + + void DownloadProgress(object sender, UpdateDownloader.ProgressEventArgs args) + { + OnProgress(args.TotalFiles, args.TotalPercent, args.CurrentIndex, args.CurrentPercent); + } + + void InstallFinished(object sender, UpdateInstaller.FinishedEventArgs args) + { + if (args.Success) { - AppLog.Line(Program.fmt("Updates downloaded to {0}", dlPath)); - OnFinished(args.Success); + AppLog.Line(MiscFunc.fmt("Updates (Un)Installed succesfully")); + + foreach (MsUpdate Update in args.Updates) + { + if (mCurOperation == AgentOperation.InstallingUpdates) + { + if (RemoveFrom(mPendingUpdates, Update)) + { + Update.Attributes |= (int)MsUpdate.UpdateAttr.Installed; + mInstalledUpdates.Add(Update); + } + } + else if (mCurOperation == AgentOperation.RemoveingUpdtes) + { + if (RemoveFrom(mInstalledUpdates, Update)) + { + Update.Attributes &= ~(int)MsUpdate.UpdateAttr.Installed; + mPendingUpdates.Add(Update); + } + } + } + + if (args.Reboot) + AppLog.Line(MiscFunc.fmt("Reboot is required for one of more updates")); } + else + AppLog.Line(MiscFunc.fmt("Updates failed to (Un)Install")); + + OnUpdatesChanged(); + + OnFinished(args.Success, args.Reboot); } - void DownloadProgress(object sender, WebDownloader.ProgressEventArgs args) + void InstallProgress(object sender, UpdateInstaller.ProgressEventArgs args) { OnProgress(args.TotalFiles, args.TotalPercent, args.CurrentIndex, args.CurrentPercent); } - public bool DownloadUpdates(UpdateCollection Updates, bool Install = false) + public bool DownloadUpdates(List Updates, bool Install = false) { - if (IsBusy()) + if (mCallback != null) return false; if (mDownloader == null) mDownloader = mUpdateSession.CreateUpdateDownloader(); mDownloader.Updates = new UpdateCollection(); - foreach (IUpdate update in Updates) + foreach (MsUpdate Update in Updates) { + IUpdate update = Update.GetUpdate(); + if (update == null) + continue; + if (update.EulaAccepted == false) { update.AcceptEula(); @@ -467,7 +578,7 @@ public bool DownloadUpdates(UpdateCollection Updates, bool Install = false) if (mDownloader.Updates.Count == 0) { - AppLog.Line(Program.fmt("No updates selected for download")); + AppLog.Line(MiscFunc.fmt("No updates selected for download")); return false; } @@ -477,24 +588,32 @@ public bool DownloadUpdates(UpdateCollection Updates, bool Install = false) mCallback = new UpdateCallback(this); - AppLog.Line(Program.fmt("Downloading Updates... This may take several minutes.")); - mDownloadJob = mDownloader.BeginDownload(mCallback, mCallback, null); + AppLog.Line(MiscFunc.fmt("Downloading Updates... This may take several minutes.")); + mDownloadJob = mDownloader.BeginDownload(mCallback, mCallback, Updates); return true; } - private bool InstallUpdates(UpdateCollection Updates) + private bool InstallUpdates(List Updates) { - if (IsBusy()) + if (mCallback != null) return false; if (mInstaller == null) - mInstaller = mUpdateSession.CreateUpdateInstaller() as UpdateInstaller; + mInstaller = mUpdateSession.CreateUpdateInstaller() as IUpdateInstaller; - mInstaller.Updates = Updates; + mInstaller.Updates = new UpdateCollection(); + foreach (MsUpdate Update in Updates) + { + IUpdate update = Update.GetUpdate(); + if (update == null) + continue; - if (mDownloader.Updates.Count == 0) + mInstaller.Updates.Add(update); + } + + if (mInstaller.Updates.Count == 0) { - AppLog.Line(Program.fmt("No updates selected or downloaded for instalation")); + AppLog.Line(MiscFunc.fmt("No updates selected for instalation")); return false; } @@ -504,32 +623,36 @@ private bool InstallUpdates(UpdateCollection Updates) mCallback = new UpdateCallback(this); - AppLog.Line(Program.fmt("Installing Updates... This may take several minutes.")); - mInstalationJob = mInstaller.BeginInstall(mCallback, mCallback, null); + AppLog.Line(MiscFunc.fmt("Installing Updates... This may take several minutes.")); + mInstalationJob = mInstaller.BeginInstall(mCallback, mCallback, Updates); return true; } - public bool UnInstallUpdates(UpdateCollection Updates) + public bool UnInstallUpdates(List Updates) { - if (IsBusy()) + if (mCallback != null) return false; if (mInstaller == null) - mInstaller = mUpdateSession.CreateUpdateInstaller() as UpdateInstaller; + mInstaller = mUpdateSession.CreateUpdateInstaller() as IUpdateInstaller; mInstaller.Updates = new UpdateCollection(); - foreach (IUpdate update in Updates) + foreach (MsUpdate Update in Updates) { + IUpdate update = Update.GetUpdate(); + if (update == null) + continue; + if (!update.IsUninstallable) { - AppLog.Line(Program.fmt("Update can not be uninstalled: {0}", update.Title)); + AppLog.Line(MiscFunc.fmt("Update can not be uninstalled: {0}", update.Title)); continue; } mInstaller.Updates.Add(update); } if (mDownloader.Updates.Count == 0) { - AppLog.Line(Program.fmt("No updates selected or eligible for uninstallation")); + AppLog.Line(MiscFunc.fmt("No updates selected or eligible for uninstallation")); return false; } @@ -539,40 +662,49 @@ public bool UnInstallUpdates(UpdateCollection Updates) mCallback = new UpdateCallback(this); - AppLog.Line(Program.fmt("Removing Updates... This may take several minutes.")); - mInstalationJob = mInstaller.BeginUninstall(mCallback, mCallback, null); + AppLog.Line(MiscFunc.fmt("Removing Updates... This may take several minutes.")); + mInstalationJob = mInstaller.BeginUninstall(mCallback, mCallback, Updates); return true; } - public void RemoveFrom(UpdateCollection Updates, IUpdate Update) + public bool RemoveFrom(List Updates, MsUpdate Update) { for (int i = 0; i < Updates.Count; i++) { if (Updates[i] == Update) { Updates.RemoveAt(i); - break; + return true; } } + return false; } - public void HideUpdates(UpdateCollection Updates, bool Hide) + public void HideUpdates(List Updates, bool Hide) { - foreach (IUpdate update in Updates) + foreach (MsUpdate Update in Updates) { try { + IUpdate update = Update.GetUpdate(); + if (update == null) + continue; update.IsHidden = Hide; + if (Hide) { - mHiddenUpdates.Add(update); - RemoveFrom(mPendingUpdates, update); + Update.Attributes |= (int)MsUpdate.UpdateAttr.Hidden; + mHiddenUpdates.Add(Update); + RemoveFrom(mPendingUpdates, Update); } else { - mPendingUpdates.Add(update); - RemoveFrom(mHiddenUpdates, update); + Update.Attributes &= ~(int)MsUpdate.UpdateAttr.Hidden; + mPendingUpdates.Add(Update); + RemoveFrom(mHiddenUpdates, Update); } + + OnUpdatesChanged(); } catch { } // Hide update may throw an exception, if the user has hidden the update manually while the search was in progress. } @@ -592,44 +724,36 @@ protected void OnUpdatesFound(ISearchJob searchJob) } catch (Exception err) { - AppLog.Line(Program.fmt("Search for updats failed, Error: {0}", WinErrors.GetErrorStr((uint)err.HResult))); + AppLog.Line(MiscFunc.fmt("Search for updats failed")); + LogError(err); OnFinished(false); return; } - mPendingUpdates = new UpdateCollection(); - mInstalledUpdates = new UpdateCollection(); - mHiddenUpdates = new UpdateCollection(); + mPendingUpdates.Clear(); + mInstalledUpdates.Clear(); + mHiddenUpdates.Clear(); + mIsValid = true; foreach (IUpdate update in SearchResults.Updates) { if (update.IsHidden) - mHiddenUpdates.Add(update); + mHiddenUpdates.Add(new MsUpdate(update, MsUpdate.UpdateState.Hidden)); else if (update.IsInstalled) - mInstalledUpdates.Add(update); + mInstalledUpdates.Add(new MsUpdate(update, MsUpdate.UpdateState.Installed)); else - mPendingUpdates.Add(update); - + mPendingUpdates.Add(new MsUpdate(update, MsUpdate.UpdateState.Pending)); Console.WriteLine(update.Title); - foreach (IUpdate bundle in update.BundledUpdates) - { - foreach (IUpdateDownloadContent udc in bundle.DownloadContents) - { - if (String.IsNullOrEmpty(udc.DownloadUrl)) - continue; - - Console.WriteLine(udc.DownloadUrl); - } - } - Console.WriteLine(""); } - AppLog.Line(Program.fmt("Found {0} pending updates.", mPendingUpdates.Count)); + AppLog.Line(MiscFunc.fmt("Found {0} pending updates.", mPendingUpdates.Count)); + + OnUpdatesChanged(true); - OnFinished(SearchResults.ResultCode == OperationResultCode.orcSucceeded, false, true); + OnFinished(SearchResults.ResultCode == OperationResultCode.orcSucceeded, false); } - protected void OnUpdatesDownloaded(IDownloadJob downloadJob) + protected void OnUpdatesDownloaded(IDownloadJob downloadJob, List Updates) { if (downloadJob != mDownloadJob) return; @@ -643,21 +767,24 @@ protected void OnUpdatesDownloaded(IDownloadJob downloadJob) } catch (Exception err) { - AppLog.Line(Program.fmt("Downloading updates failed, Error: {0}", WinErrors.GetErrorStr((uint)err.HResult))); + AppLog.Line(MiscFunc.fmt("Downloading updates failed")); + LogError(err); OnFinished(false); return; } + OnUpdatesChanged(); + if (mCurOperation == AgentOperation.PreparingUpdates) - InstallUpdates(downloadJob.Updates); + InstallUpdates(Updates); else { - AppLog.Line(Program.fmt("Updates downloaded to %windir%\\SoftwareDistribution\\Download")); + AppLog.Line(MiscFunc.fmt("Updates downloaded to %windir%\\SoftwareDistribution\\Download")); OnFinished(DownloadResults.ResultCode == OperationResultCode.orcSucceeded); } } - protected void OnInstalationCompleted(IInstallationJob installationJob) + protected void OnInstalationCompleted(IInstallationJob installationJob, List Updates) { if (installationJob != mInstalationJob) return; @@ -675,20 +802,43 @@ protected void OnInstalationCompleted(IInstallationJob installationJob) } catch (Exception err) { - AppLog.Line(Program.fmt("(Un)Installing updates failed, Error: {0}", WinErrors.GetErrorStr((uint)err.HResult))); + AppLog.Line(MiscFunc.fmt("(Un)Installing updates failed")); + LogError(err); OnFinished(false); return; } if (InstallationResults.ResultCode == OperationResultCode.orcSucceeded) { - AppLog.Line(Program.fmt("Updates (Un)Installed succesfully")); + AppLog.Line(MiscFunc.fmt("Updates (Un)Installed succesfully")); + + foreach (MsUpdate Update in Updates) + { + if (mCurOperation == AgentOperation.InstallingUpdates) + { + if (RemoveFrom(mPendingUpdates, Update)) + { + Update.Attributes |= (int)MsUpdate.UpdateAttr.Installed; + mInstalledUpdates.Add(Update); + } + } + else if (mCurOperation == AgentOperation.RemoveingUpdtes) + { + if (RemoveFrom(mInstalledUpdates, Update)) + { + Update.Attributes &= ~(int)MsUpdate.UpdateAttr.Installed; + mPendingUpdates.Add(Update); + } + } + } if (InstallationResults.RebootRequired == true) - AppLog.Line(Program.fmt("Reboot is required for one of more updates")); + AppLog.Line(MiscFunc.fmt("Reboot is required for one of more updates")); } else - AppLog.Line(Program.fmt("Updates failed to (Un)Install")); + AppLog.Line(MiscFunc.fmt("Updates failed to (Un)Install")); + + OnUpdatesChanged(); OnFinished(InstallationResults.ResultCode == OperationResultCode.orcSucceeded, InstallationResults.RebootRequired); } @@ -721,6 +871,14 @@ public void EnableWuAuServ(bool enable = true) svc.Close(); } + public bool TestWuAuServ() + { + ServiceController svc = new ServiceController("wuauserv"); + bool ret = svc.Status == ServiceControllerStatus.Running; + svc.Close(); + return ret; + } + public class ProgressArgs : EventArgs { public ProgressArgs(int TotalUpdates, int TotalPercent, int CurrentIndex, int UpdatePercent, String Info) @@ -748,30 +906,113 @@ protected void OnProgress(int TotalUpdates, int TotalPercent, int CurrentIndex, public class FinishedArgs : EventArgs { - public FinishedArgs(bool success, bool needReboot = false, bool foundUpdates = false) + public FinishedArgs(bool success, bool needReboot = false) { Success = success; RebootNeeded = needReboot; - FoundUpdates = foundUpdates; } public bool Success = false; - public bool FoundUpdates = false; public bool RebootNeeded = false; } public event EventHandler Finished; - protected void OnFinished(bool success, bool needReboot = false, bool foundUpdates = false) + protected void OnFinished(bool success, bool needReboot = false) { mCurOperation = AgentOperation.None; - Finished?.Invoke(this, new FinishedArgs(success, needReboot, foundUpdates)); + Finished?.Invoke(this, new FinishedArgs(success, needReboot)); + } + + public class UpdatesArgs : EventArgs + { + public UpdatesArgs(bool found) + { + Found = found; + } + public bool Found = false; + } + public event EventHandler UpdatesChaged; + + protected void OnUpdatesChanged(bool found = false) + { + string INIPath = Program.appPath + @"\updates.ini"; + FileOps.DeleteFile(INIPath); + + StoreUpdates(mUpdateHistory); + StoreUpdates(mPendingUpdates); + StoreUpdates(mInstalledUpdates); + StoreUpdates(mHiddenUpdates); + + UpdatesChaged?.Invoke(this, new UpdatesArgs(found)); + } + + private void StoreUpdates(List Updates) + { + string INIPath = Program.appPath + @"\updates.ini"; + foreach (MsUpdate Update in Updates) + { + Program.IniWriteValue(Update.KB, "UUID", Update.UUID, INIPath); + + Program.IniWriteValue(Update.KB, "Title", Update.Title, INIPath); + Program.IniWriteValue(Update.KB, "Info", Update.Description, INIPath); + Program.IniWriteValue(Update.KB, "Category", Update.Category, INIPath); + + Program.IniWriteValue(Update.KB, "Date", Update.Date.ToString("dd.MM.yyyy"), INIPath); + Program.IniWriteValue(Update.KB, "Size", Update.Size.ToString(), INIPath); + + Program.IniWriteValue(Update.KB, "SupportUrl", Update.SupportUrl, INIPath); + + Program.IniWriteValue(Update.KB, "Downloads", string.Join("|",Update.Downloads.Cast().ToArray()), INIPath); + + Program.IniWriteValue(Update.KB, "State", ((int)Update.State).ToString(), INIPath); + Program.IniWriteValue(Update.KB, "Attributes", Update.Attributes.ToString(), INIPath); + Program.IniWriteValue(Update.KB, "ResultCode", Update.ResultCode.ToString(), INIPath); + Program.IniWriteValue(Update.KB, "HResult", Update.HResult.ToString(), INIPath); + } + } + + private void LoadUpdates() + { + string INIPath = Program.appPath + @"\updates.ini"; + foreach (string KB in Program.IniEnumSections(INIPath)) + { + if (KB.Length == 0) + continue; + + MsUpdate Update = new MsUpdate(); + Update.KB = KB; + Update.UUID = Program.IniReadValue(Update.KB, "UUID", "", INIPath); + + Update.Title = Program.IniReadValue(Update.KB, "Title", "", INIPath); + Update.Description = Program.IniReadValue(Update.KB, "Info", "", INIPath); + Update.Category = Program.IniReadValue(Update.KB, "Category", "", INIPath); + + try { Update.Date = DateTime.Parse(Program.IniReadValue(Update.KB, "Date", "", INIPath)); } catch { } + Update.Size = (decimal)MiscFunc.parseInt(Program.IniReadValue(Update.KB, "Size", "0", INIPath)); + + Update.SupportUrl = Program.IniReadValue(Update.KB, "SupportUrl", "", INIPath); + Update.Downloads.AddRange(Program.IniReadValue(Update.KB, "Downloads", "", INIPath).Split('|')); + + Update.State = (MsUpdate.UpdateState)MiscFunc.parseInt(Program.IniReadValue(Update.KB, "State", "0", INIPath)); + Update.Attributes = MiscFunc.parseInt(Program.IniReadValue(Update.KB, "Attributes", "0", INIPath)); + Update.ResultCode = MiscFunc.parseInt(Program.IniReadValue(Update.KB, "ResultCode", "0", INIPath)); + Update.HResult = MiscFunc.parseInt(Program.IniReadValue(Update.KB, "HResult", "0", INIPath)); + + switch (Update.State) + { + case MsUpdate.UpdateState.Pending: mPendingUpdates.Add(Update); break; + case MsUpdate.UpdateState.Installed: mInstalledUpdates.Add(Update); break; + case MsUpdate.UpdateState.Hidden: mHiddenUpdates.Add(Update); break; + case MsUpdate.UpdateState.History: mUpdateHistory.Add(Update); break; + } + } } class UpdateCallback : ISearchCompletedCallback, IDownloadProgressChangedCallback, IDownloadCompletedCallback, IInstallationProgressChangedCallback, IInstallationCompletedCallback { private WuAgent agent; - + public UpdateCallback(WuAgent agent) { this.agent = agent; @@ -801,7 +1042,7 @@ public void Invoke(IDownloadJob downloadJob, IDownloadCompletedCallbackArgs call { // !!! warning this function is invoced from a different thread !!! agent.mDispatcher.Invoke(new Action(() => { - agent.OnUpdatesDownloaded(downloadJob); + agent.OnUpdatesDownloaded(downloadJob, downloadJob.AsyncState); })); } @@ -820,7 +1061,7 @@ public void Invoke(IInstallationJob installationJob, IInstallationCompletedCallb { // !!! warning this function is invoced from a different thread !!! agent.mDispatcher.Invoke(new Action(() => { - agent.OnInstalationCompleted(installationJob); + agent.OnInstalationCompleted(installationJob, installationJob.AsyncState); })); } } diff --git a/wumgr/WuMgr.Designer.cs b/wumgr/WuMgr.Designer.cs index 2fbd7f2..848f17c 100644 --- a/wumgr/WuMgr.Designer.cs +++ b/wumgr/WuMgr.Designer.cs @@ -31,10 +31,9 @@ private void InitializeComponent() this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(WuMgr)); this.toolTip = new System.Windows.Forms.ToolTip(this.components); - this.checkStateRenderer1 = new BrightIdeasSoftware.CheckStateRenderer(); - this.notifyIcon1 = new System.Windows.Forms.NotifyIcon(this.components); + this.chkAutoRun = new System.Windows.Forms.CheckBox(); + this.notifyIcon = new System.Windows.Forms.NotifyIcon(this.components); this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); - this.splitContainer1 = new System.Windows.Forms.SplitContainer(); this.updateView = new BrightIdeasSoftware.ObjectListView(); this.olvColumn1 = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); this.olvColumn2 = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); @@ -43,6 +42,8 @@ private void InitializeComponent() this.olvColumn5 = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); this.olvColumn6 = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn())); this.logBox = new System.Windows.Forms.RichTextBox(); + this.tableLayoutPanel7 = new System.Windows.Forms.TableLayoutPanel(); + this.lblSupport = new System.Windows.Forms.LinkLabel(); this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel(); this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); @@ -69,8 +70,8 @@ private void InitializeComponent() this.dlShDay = new System.Windows.Forms.ComboBox(); this.dlPolMode = new System.Windows.Forms.ComboBox(); this.tabPage2 = new System.Windows.Forms.TabPage(); + this.dlAutoCheck = new System.Windows.Forms.ComboBox(); this.chkNoUAC = new System.Windows.Forms.CheckBox(); - this.chkAutoRun = new System.Windows.Forms.CheckBox(); this.tabPage3 = new System.Windows.Forms.TabPage(); this.chkMsUpd = new System.Windows.Forms.CheckBox(); this.tabControl2 = new System.Windows.Forms.TabControl(); @@ -83,11 +84,8 @@ private void InitializeComponent() this.chkManual = new System.Windows.Forms.CheckBox(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel3.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); - this.splitContainer1.Panel1.SuspendLayout(); - this.splitContainer1.Panel2.SuspendLayout(); - this.splitContainer1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.updateView)).BeginInit(); + this.tableLayoutPanel7.SuspendLayout(); this.tableLayoutPanel2.SuspendLayout(); this.tableLayoutPanel4.SuspendLayout(); this.flowLayoutPanel1.SuspendLayout(); @@ -103,11 +101,24 @@ private void InitializeComponent() this.tableLayoutPanel1.SuspendLayout(); this.SuspendLayout(); // - // notifyIcon1 + // chkAutoRun // - this.notifyIcon1.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon1.Icon"))); - this.notifyIcon1.Text = "notifyIcon1"; - this.notifyIcon1.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.notifyIcon1_MouseDoubleClick); + this.chkAutoRun.AutoSize = true; + this.chkAutoRun.Location = new System.Drawing.Point(7, 7); + this.chkAutoRun.Name = "chkAutoRun"; + this.chkAutoRun.Size = new System.Drawing.Size(117, 17); + this.chkAutoRun.TabIndex = 0; + this.chkAutoRun.Text = "Run in background"; + this.toolTip.SetToolTip(this.chkAutoRun, "Auto Start with Windows"); + this.chkAutoRun.UseVisualStyleBackColor = true; + this.chkAutoRun.CheckedChanged += new System.EventHandler(this.chkAutoRun_CheckedChanged); + // + // notifyIcon + // + this.notifyIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon.Icon"))); + this.notifyIcon.Text = "notifyIcon1"; + this.notifyIcon.BalloonTipClicked += new System.EventHandler(this.notifyIcon_BalloonTipClicked); + this.notifyIcon.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.notifyIcon1_MouseDoubleClick); // // tableLayoutPanel3 // @@ -116,37 +127,19 @@ private void InitializeComponent() | System.Windows.Forms.AnchorStyles.Right))); this.tableLayoutPanel3.ColumnCount = 1; this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel3.Controls.Add(this.splitContainer1, 0, 1); + this.tableLayoutPanel3.Controls.Add(this.updateView, 0, 1); + this.tableLayoutPanel3.Controls.Add(this.logBox, 0, 2); + this.tableLayoutPanel3.Controls.Add(this.tableLayoutPanel7, 0, 0); this.tableLayoutPanel3.Location = new System.Drawing.Point(188, 0); this.tableLayoutPanel3.Margin = new System.Windows.Forms.Padding(0); this.tableLayoutPanel3.Name = "tableLayoutPanel3"; - this.tableLayoutPanel3.RowCount = 2; + this.tableLayoutPanel3.RowCount = 3; this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 100F)); this.tableLayoutPanel3.Size = new System.Drawing.Size(695, 482); this.tableLayoutPanel3.TabIndex = 1; // - // splitContainer1 - // - this.splitContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.splitContainer1.Location = new System.Drawing.Point(3, 23); - this.splitContainer1.Name = "splitContainer1"; - this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; - // - // splitContainer1.Panel1 - // - this.splitContainer1.Panel1.Controls.Add(this.updateView); - // - // splitContainer1.Panel2 - // - this.splitContainer1.Panel2.Controls.Add(this.logBox); - this.splitContainer1.Size = new System.Drawing.Size(689, 456); - this.splitContainer1.SplitterDistance = 295; - this.splitContainer1.TabIndex = 2; - // // updateView // this.updateView.AllColumns.Add(this.olvColumn1); @@ -166,12 +159,13 @@ private void InitializeComponent() this.olvColumn4, this.olvColumn5, this.olvColumn6}); - this.updateView.Location = new System.Drawing.Point(0, 0); + this.updateView.Location = new System.Drawing.Point(3, 23); this.updateView.Name = "updateView"; - this.updateView.Size = new System.Drawing.Size(689, 295); + this.updateView.Size = new System.Drawing.Size(689, 356); this.updateView.TabIndex = 3; this.updateView.UseCompatibleStateImageBehavior = false; this.updateView.View = System.Windows.Forms.View.Details; + this.updateView.SelectedIndexChanged += new System.EventHandler(this.updateView_SelectedIndexChanged); // // olvColumn1 // @@ -219,13 +213,44 @@ private void InitializeComponent() this.logBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.logBox.Location = new System.Drawing.Point(0, 2); + this.logBox.Location = new System.Drawing.Point(3, 385); this.logBox.Name = "logBox"; this.logBox.ReadOnly = true; - this.logBox.Size = new System.Drawing.Size(689, 155); - this.logBox.TabIndex = 3; + this.logBox.Size = new System.Drawing.Size(689, 94); + this.logBox.TabIndex = 4; this.logBox.Text = ""; // + // tableLayoutPanel7 + // + this.tableLayoutPanel7.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel7.ColumnCount = 3; + this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 101F)); + this.tableLayoutPanel7.Controls.Add(this.lblSupport, 2, 0); + this.tableLayoutPanel7.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel7.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel7.Name = "tableLayoutPanel7"; + this.tableLayoutPanel7.RowCount = 1; + this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel7.Size = new System.Drawing.Size(695, 20); + this.tableLayoutPanel7.TabIndex = 5; + // + // lblSupport + // + this.lblSupport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.lblSupport.AutoSize = true; + this.lblSupport.Location = new System.Drawing.Point(597, 3); + this.lblSupport.Name = "lblSupport"; + this.lblSupport.Size = new System.Drawing.Size(95, 13); + this.lblSupport.TabIndex = 0; + this.lblSupport.TabStop = true; + this.lblSupport.Text = "Support URL"; + this.lblSupport.Visible = false; + this.lblSupport.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lblSupport_LinkClicked); + // // tableLayoutPanel2 // this.tableLayoutPanel2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) @@ -265,7 +290,7 @@ private void InitializeComponent() this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); - this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 35F)); + this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 37F)); this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 35F)); this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 15F)); this.tableLayoutPanel4.Size = new System.Drawing.Size(186, 210); @@ -353,11 +378,11 @@ private void InitializeComponent() this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 29F)); this.tableLayoutPanel5.Controls.Add(this.btnCancel, 1, 0); this.tableLayoutPanel5.Controls.Add(this.progTotal, 0, 0); - this.tableLayoutPanel5.Location = new System.Drawing.Point(3, 158); + this.tableLayoutPanel5.Location = new System.Drawing.Point(3, 160); this.tableLayoutPanel5.Name = "tableLayoutPanel5"; this.tableLayoutPanel5.RowCount = 1; this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 29F)); - this.tableLayoutPanel5.Size = new System.Drawing.Size(180, 29); + this.tableLayoutPanel5.Size = new System.Drawing.Size(180, 28); this.tableLayoutPanel5.TabIndex = 5; // // btnCancel @@ -419,7 +444,7 @@ private void InitializeComponent() this.btnInstalled.Location = new System.Drawing.Point(3, 33); this.btnInstalled.Name = "btnInstalled"; this.btnInstalled.Size = new System.Drawing.Size(180, 23); - this.btnInstalled.TabIndex = 0; + this.btnInstalled.TabIndex = 8; this.btnInstalled.Text = "Installed Updates"; this.btnInstalled.UseVisualStyleBackColor = true; this.btnInstalled.CheckedChanged += new System.EventHandler(this.btnInstalled_CheckedChanged); @@ -433,7 +458,7 @@ private void InitializeComponent() this.btnWinUpd.Location = new System.Drawing.Point(3, 3); this.btnWinUpd.Name = "btnWinUpd"; this.btnWinUpd.Size = new System.Drawing.Size(180, 23); - this.btnWinUpd.TabIndex = 8; + this.btnWinUpd.TabIndex = 0; this.btnWinUpd.Text = "Windows Updates"; this.btnWinUpd.UseVisualStyleBackColor = true; this.btnWinUpd.CheckedChanged += new System.EventHandler(this.btnWinUpd_CheckedChanged); @@ -442,7 +467,7 @@ private void InitializeComponent() // this.lblStatus.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); this.lblStatus.AutoSize = true; - this.lblStatus.Location = new System.Drawing.Point(3, 193); + this.lblStatus.Location = new System.Drawing.Point(3, 194); this.lblStatus.Name = "lblStatus"; this.lblStatus.Size = new System.Drawing.Size(180, 13); this.lblStatus.TabIndex = 9; @@ -588,6 +613,7 @@ private void InitializeComponent() // // tabPage2 // + this.tabPage2.Controls.Add(this.dlAutoCheck); this.tabPage2.Controls.Add(this.chkNoUAC); this.tabPage2.Controls.Add(this.chkAutoRun); this.tabPage2.Location = new System.Drawing.Point(4, 22); @@ -598,10 +624,26 @@ private void InitializeComponent() this.tabPage2.Text = "Start"; this.tabPage2.UseVisualStyleBackColor = true; // + // dlAutoCheck + // + this.dlAutoCheck.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.dlAutoCheck.Enabled = false; + this.dlAutoCheck.FormattingEnabled = true; + this.dlAutoCheck.Items.AddRange(new object[] { + "No auto search for updates", + "Search updates every day", + "Search updates once a week", + "Search updates every month"}); + this.dlAutoCheck.Location = new System.Drawing.Point(5, 26); + this.dlAutoCheck.Name = "dlAutoCheck"; + this.dlAutoCheck.Size = new System.Drawing.Size(163, 21); + this.dlAutoCheck.TabIndex = 2; + this.dlAutoCheck.SelectedIndexChanged += new System.EventHandler(this.dlAutoCheck_SelectedIndexChanged); + // // chkNoUAC // this.chkNoUAC.AutoSize = true; - this.chkNoUAC.Location = new System.Drawing.Point(7, 25); + this.chkNoUAC.Location = new System.Drawing.Point(7, 74); this.chkNoUAC.Name = "chkNoUAC"; this.chkNoUAC.Size = new System.Drawing.Size(164, 17); this.chkNoUAC.TabIndex = 1; @@ -609,17 +651,6 @@ private void InitializeComponent() this.chkNoUAC.UseVisualStyleBackColor = true; this.chkNoUAC.CheckedChanged += new System.EventHandler(this.chkNoUAC_CheckedChanged); // - // chkAutoRun - // - this.chkAutoRun.AutoSize = true; - this.chkAutoRun.Location = new System.Drawing.Point(7, 7); - this.chkAutoRun.Name = "chkAutoRun"; - this.chkAutoRun.Size = new System.Drawing.Size(142, 17); - this.chkAutoRun.TabIndex = 0; - this.chkAutoRun.Text = "Auto Start with Windows"; - this.chkAutoRun.UseVisualStyleBackColor = true; - this.chkAutoRun.CheckedChanged += new System.EventHandler(this.chkAutoRun_CheckedChanged); - // // tabPage3 // this.tabPage3.Controls.Add(this.chkMsUpd); @@ -648,7 +679,7 @@ private void InitializeComponent() this.tabControl2.Location = new System.Drawing.Point(3, 3); this.tabControl2.Name = "tabControl2"; this.tabControl2.SelectedIndex = 0; - this.tabControl2.Size = new System.Drawing.Size(182, 99); + this.tabControl2.Size = new System.Drawing.Size(182, 98); this.tabControl2.TabIndex = 2; // // tabPage4 @@ -659,7 +690,7 @@ private void InitializeComponent() this.tabPage4.Location = new System.Drawing.Point(4, 22); this.tabPage4.Name = "tabPage4"; this.tabPage4.Padding = new System.Windows.Forms.Padding(3); - this.tabPage4.Size = new System.Drawing.Size(174, 73); + this.tabPage4.Size = new System.Drawing.Size(174, 72); this.tabPage4.TabIndex = 0; this.tabPage4.Text = "Update Source"; this.tabPage4.UseVisualStyleBackColor = true; @@ -706,7 +737,7 @@ private void InitializeComponent() this.tabPage5.Location = new System.Drawing.Point(4, 22); this.tabPage5.Name = "tabPage5"; this.tabPage5.Padding = new System.Windows.Forms.Padding(3); - this.tabPage5.Size = new System.Drawing.Size(174, 73); + this.tabPage5.Size = new System.Drawing.Size(174, 72); this.tabPage5.TabIndex = 1; this.tabPage5.Text = "Offline Options"; this.tabPage5.UseVisualStyleBackColor = true; @@ -715,7 +746,7 @@ private void InitializeComponent() // this.chkDownload.AutoSize = true; this.chkDownload.Checked = true; - this.chkDownload.CheckState = System.Windows.Forms.CheckState.Indeterminate; + this.chkDownload.CheckState = System.Windows.Forms.CheckState.Checked; this.chkDownload.Location = new System.Drawing.Point(5, 6); this.chkDownload.Name = "chkDownload"; this.chkDownload.Size = new System.Drawing.Size(145, 17); @@ -723,7 +754,7 @@ private void InitializeComponent() this.chkDownload.Text = "Download wsusscn2.cab"; this.chkDownload.ThreeState = true; this.chkDownload.UseVisualStyleBackColor = true; - this.chkDownload.CheckStateChanged += new System.EventHandler(this.chkDownload_CheckStateChanged); + this.chkDownload.CheckedChanged += new System.EventHandler(this.chkDownload_CheckedChanged); // // chkManual // @@ -766,11 +797,9 @@ private void InitializeComponent() this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.WuMgr_FormClosing); this.Load += new System.EventHandler(this.WuMgr_Load); this.tableLayoutPanel3.ResumeLayout(false); - this.splitContainer1.Panel1.ResumeLayout(false); - this.splitContainer1.Panel2.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); - this.splitContainer1.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.updateView)).EndInit(); + this.tableLayoutPanel7.ResumeLayout(false); + this.tableLayoutPanel7.PerformLayout(); this.tableLayoutPanel2.ResumeLayout(false); this.tableLayoutPanel4.ResumeLayout(false); this.tableLayoutPanel4.PerformLayout(); @@ -796,18 +825,8 @@ private void InitializeComponent() #endregion private System.Windows.Forms.ToolTip toolTip; - private BrightIdeasSoftware.CheckStateRenderer checkStateRenderer1; - private System.Windows.Forms.NotifyIcon notifyIcon1; + private System.Windows.Forms.NotifyIcon notifyIcon; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; - private System.Windows.Forms.SplitContainer splitContainer1; - private BrightIdeasSoftware.ObjectListView updateView; - private BrightIdeasSoftware.OLVColumn olvColumn1; - private BrightIdeasSoftware.OLVColumn olvColumn2; - private BrightIdeasSoftware.OLVColumn olvColumn3; - private BrightIdeasSoftware.OLVColumn olvColumn4; - private BrightIdeasSoftware.OLVColumn olvColumn5; - private BrightIdeasSoftware.OLVColumn olvColumn6; - private System.Windows.Forms.RichTextBox logBox; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4; private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; @@ -847,6 +866,17 @@ private void InitializeComponent() private System.Windows.Forms.CheckBox chkDownload; private System.Windows.Forms.CheckBox chkManual; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private BrightIdeasSoftware.ObjectListView updateView; + private BrightIdeasSoftware.OLVColumn olvColumn1; + private BrightIdeasSoftware.OLVColumn olvColumn2; + private BrightIdeasSoftware.OLVColumn olvColumn3; + private BrightIdeasSoftware.OLVColumn olvColumn4; + private BrightIdeasSoftware.OLVColumn olvColumn5; + private BrightIdeasSoftware.OLVColumn olvColumn6; + private System.Windows.Forms.RichTextBox logBox; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel7; + private System.Windows.Forms.LinkLabel lblSupport; + private System.Windows.Forms.ComboBox dlAutoCheck; } } diff --git a/wumgr/WuMgr.cs b/wumgr/WuMgr.cs index 7a834ba..fc5d141 100644 --- a/wumgr/WuMgr.cs +++ b/wumgr/WuMgr.cs @@ -13,40 +13,69 @@ using Microsoft.Win32; using System.Security.AccessControl; using System.Runtime.InteropServices; +using System.IO; +using System.Diagnostics; namespace wumgr { public partial class WuMgr : Form { public const Int32 WM_SYSCOMMAND = 0x112; + + public const Int32 MF_BITMAP = 0x00000004; + public const Int32 MF_CHECKED = 0x00000008; + public const Int32 MF_DISABLED = 0x00000002; + public const Int32 MF_ENABLED = 0x00000000; + public const Int32 MF_GRAYED = 0x00000001; + public const Int32 MF_MENUBARBREAK = 0x00000020; + public const Int32 MF_MENUBREAK = 0x00000040; + public const Int32 MF_OWNERDRAW = 0x00000100; + public const Int32 MF_POPUP = 0x00000010; + public const Int32 MF_SEPARATOR = 0x00000800; + public const Int32 MF_STRING = 0x00000000; + public const Int32 MF_UNCHECKED = 0x00000000; + public const Int32 MF_BYPOSITION = 0x400; - public const Int32 MF_SEPARATOR = 0x800; + public const Int32 MF_BYCOMMAND = 0x000; + //public const Int32 MF_REMOVE = 0x1000; + public const Int32 MYMENU_ABOUT = 1000; [DllImport("user32.dll")] private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert); [DllImport("user32.dll")] private static extern bool InsertMenu(IntPtr hMenu, Int32 wPosition, Int32 wFlags, Int32 wIDNewItem, string lpNewItem); + [DllImport("user32.dll")] + private static extern int AppendMenu(IntPtr hMenu, int Flags, int NewID, String Item); + [DllImport("user32.dll")] + static extern int GetMenuItemCount(IntPtr hMenu); + [DllImport("user32.dll")] + static extern bool RemoveMenu(IntPtr hMenu, uint uPosition, uint uFlags); protected override void WndProc(ref Message msg) { - if (msg.Msg == WM_SYSCOMMAND) + switch (msg.Msg) { - switch (msg.WParam.ToInt32()) - { - case MYMENU_ABOUT: menuAbout_Click(); return; - } + case WM_SYSCOMMAND: + { + switch (msg.WParam.ToInt32()) + { + case MYMENU_ABOUT: menuAbout_Click(null, null); return; + } + } + break; + case Program.WM_APP: + if (msg.WParam == IntPtr.Zero && msg.LParam == IntPtr.Zero) + { + notifyIcon_BalloonTipClicked(null, null); + msg.Result = IntPtr.Zero; + return; + } + break; } base.WndProc(ref msg); } - private void WuMgr_Load(object sender, EventArgs e) - { - IntPtr MenuHandle = GetSystemMenu(this.Handle, false); - InsertMenu(MenuHandle, 5, MF_BYPOSITION | MF_SEPARATOR, 0, string.Empty); // <-- Add a menu seperator - InsertMenu(MenuHandle, 6, MF_BYPOSITION, MYMENU_ABOUT, "&About"); - } - WuAgent agent; void LineLogger(object sender, AppLog.LogEventArgs args) @@ -64,29 +93,33 @@ protected override void SetVisibleCore(bool value) private bool mSuspendUpdate = false; - public WuMgr() + + enum AutoUpdateOptions { - InitializeComponent(); + No = 0, + EveryDay, + EveryWeek, + EveryMonth + } - notifyIcon1.ContextMenu = new System.Windows.Forms.ContextMenu(); - MenuItem menuExit = new System.Windows.Forms.MenuItem(); + AutoUpdateOptions AutoUpdate = AutoUpdateOptions.No; + int IdleDelay = 0; + DateTime LastCheck = DateTime.MaxValue; - menuExit.Index = 0; - menuExit.Text = "E&xit"; - menuExit.Click += new System.EventHandler(menuExit_Click); - - notifyIcon1.ContextMenu.MenuItems.AddRange(new MenuItem[] { menuExit }); + public WuMgr() + { + InitializeComponent(); //notifyIcon1.Icon = System.Drawing.Icon.ExtractAssociatedIcon(System.Reflection.Assembly.GetExecutingAssembly().Location); - notifyIcon1.Text = Program.mName; + notifyIcon.Text = Program.mName; if (Program.TestArg("-tray")) { allowshowdisplay = false; - notifyIcon1.Visible = true; + notifyIcon.Visible = true; } - this.Text = Program.fmt("{0} by David Xanatos", Program.mName); + this.Text = MiscFunc.fmt("{0} by David Xanatos", Program.mName); toolTip.SetToolTip(btnSearch, "Search"); toolTip.SetToolTip(btnInstall, "Install"); @@ -109,10 +142,11 @@ public WuMgr() foreach (string line in AppLog.GetLog()) logBox.AppendText(line + Environment.NewLine); logBox.ScrollToCaret(); - + agent = WuAgent.GetInstance(); agent.Progress += OnProgress; + agent.UpdatesChaged += OnUpdates; agent.Finished += OnFinished; if (!agent.IsActive()) @@ -128,7 +162,7 @@ public WuMgr() updateView.ShowItemCountOnGroups = true; updateView.AlwaysGroupByColumn = updateView.ColumnsInDisplayOrder[1]; updateView.Sort(); - + mSuspendUpdate = true; chkDrivers.CheckState = (CheckState)GPO.GetDriverAU(); @@ -136,15 +170,17 @@ public WuMgr() dlPolMode.SelectedIndex = GPO.GetAU(out day, out time); dlShDay.SelectedIndex = day; dlShTime.SelectedIndex = time; chkBlockMS.CheckState = (CheckState)GPO.GetBlockMS(); + dlAutoCheck.SelectedIndex = MiscFunc.parseInt(GetConfig("AutoUpdate", "0")); chkAutoRun.Checked = Program.IsAutoStart(); + IdleDelay = MiscFunc.parseInt(GetConfig("IdleDelay", "20")); chkNoUAC.Checked = Program.IsSkipUacRun(); - chkOffline.Checked = int.Parse(GetConfig("Offline", "1")) != 0; - chkDownload.CheckState = (CheckState)int.Parse(GetConfig("Download", "2")); - chkManual.Checked = int.Parse(GetConfig("Manual", "0")) != 0; - chkOld.Checked = int.Parse(GetConfig("IncludeOld", "0")) != 0; + chkOffline.Checked = MiscFunc.parseInt(GetConfig("Offline", "1")) != 0; + chkDownload.Checked = MiscFunc.parseInt(GetConfig("Download", "1")) != 0; + chkManual.Checked = MiscFunc.parseInt(GetConfig("Manual", "0")) != 0; + chkOld.Checked = MiscFunc.parseInt(GetConfig("IncludeOld", "0")) != 0; string source = GetConfig("Source", "Windows Update"); string Online = Program.GetArg("-online"); @@ -159,17 +195,19 @@ public WuMgr() if (Offline != null) { chkOffline.Checked = true; - if (Offline.Equals("download", StringComparison.CurrentCultureIgnoreCase)) - chkDownload.CheckState = CheckState.Checked; + chkDownload.Checked = true; else if (Offline.Equals("no_download", StringComparison.CurrentCultureIgnoreCase)) - chkDownload.CheckState = CheckState.Unchecked; - else if (Offline.Equals("download_new", StringComparison.CurrentCultureIgnoreCase)) - chkDownload.CheckState = CheckState.Indeterminate; + chkDownload.Checked = false; } if (Program.TestArg("-manual")) - chkManual.Checked = false; + chkManual.Checked = true; + + try { + LastCheck = DateTime.Parse(GetConfig("LastCheck", "")); + AppLog.Line(MiscFunc.fmt("Last Checked for updates: {0}", LastCheck.ToString())); + } catch { } LoadProviders(source); @@ -182,28 +220,134 @@ public WuMgr() } mSuspendUpdate = false; - + + mToolsMenu = new MenuItem(); + mToolsMenu.Text = "&Tools"; + + BuildToolsMenu(); + + notifyIcon.ContextMenu = new ContextMenu(); + + MenuItem menuAbout = new MenuItem(); + menuAbout.Text = "&About"; + menuAbout.Click += new System.EventHandler(menuAbout_Click); + + MenuItem menuExit = new MenuItem(); + menuExit.Text = "E&xit"; + menuExit.Click += new System.EventHandler(menuExit_Click); + + notifyIcon.ContextMenu.MenuItems.AddRange(new MenuItem[] { mToolsMenu, menuAbout, new MenuItem("-"), menuExit }); + + + IntPtr MenuHandle = GetSystemMenu(this.Handle, false); // Note: to restore default set true + InsertMenu(MenuHandle, 5, MF_BYPOSITION | MF_SEPARATOR, 0, string.Empty); // <-- Add a menu seperator + InsertMenu(MenuHandle, 6, MF_BYPOSITION | MF_POPUP, (int)mToolsMenu.Handle, "Tools"); + InsertMenu(MenuHandle, 7, MF_BYPOSITION, MYMENU_ABOUT, "&About"); + + UpdateCounts(); SwitchList(UpdateLists.UpdateHistory); - if (Program.TestArg("-update")) + doUpdte = Program.TestArg("-update"); + + mTimer = new Timer(); + mTimer.Interval = 1000; // once epr second + mTimer.Tick += OnTimedEvent; + mTimer.Enabled = true; + } + + private static Timer mTimer = null; + private bool doUpdte = false; + private DateTime LastBaloon = DateTime.MinValue; + + private void OnTimedEvent(Object source, EventArgs e) + { + bool updateNow = false; + if (notifyIcon.Visible) + { + int daysDue = GetAutoUpdateDue(); + if (daysDue != 0 && !agent.IsBusy()) + { + // ensure we only start a check when user is not doing anything + uint idleTime = MiscFunc.GetIdleTime(); + if (IdleDelay * 60 < idleTime) + { + AppLog.Line(MiscFunc.fmt("Starting automatic search for updates.")); + updateNow = true; + } + else if(daysDue > GetGraceDays()) + { + if (LastBaloon < DateTime.Now.AddHours(-4)) + { + LastBaloon = DateTime.Now; + notifyIcon.ShowBalloonTip(int.MaxValue, MiscFunc.fmt("Please Check For Updates"), + MiscFunc.fmt("WuMgr couldn't check for updates for {0} days, please check for updates manually and resolve possible issues", daysDue), ToolTipIcon.Warning); + } + } + } + + if (agent.mPendingUpdates.Count > 0) + { + if (LastBaloon < DateTime.Now.AddHours(-4)) + { + LastBaloon = DateTime.Now; + notifyIcon.ShowBalloonTip(int.MaxValue, MiscFunc.fmt("New Updates found"), + MiscFunc.fmt("WuMgr has found {0} new updates, please review the upates and install them", agent.mPendingUpdates), ToolTipIcon.Info); + } + } + } + + if ((doUpdte || updateNow) && agent.IsActive()) { - aTimer = new Timer(); - aTimer.Interval = 1000; - // Hook up the Elapsed event for the timer. - aTimer.Tick += OnTimedEvent; - aTimer.Enabled = true; + doUpdte = false; + if (chkOffline.Checked) + agent.SearchForUpdates(chkDownload.Checked, chkOld.Checked); + else + agent.SearchForUpdates(dlSource.Text, chkOld.Checked); } } - private void WuMgr_FormClosing(object sender, FormClosingEventArgs e) + private void WuMgr_Load(object sender, EventArgs e) { + + } + private int GetAutoUpdateDue() + { + DateTime NextUpdate = DateTime.MaxValue; + switch (AutoUpdate) + { + case AutoUpdateOptions.EveryDay: NextUpdate = LastCheck.AddDays(1); break; + case AutoUpdateOptions.EveryWeek: NextUpdate = LastCheck.AddDays(7); break; + case AutoUpdateOptions.EveryMonth: NextUpdate = LastCheck.AddMonths(1); break; + } + if (NextUpdate >= DateTime.Now) + return 0; + return (int)Math.Ceiling((DateTime.Now - NextUpdate).TotalDays); } - private void menuExit_Click(object Sender, EventArgs e) + private int GetGraceDays() { - Application.Exit(); + switch (AutoUpdate) + { + case AutoUpdateOptions.EveryMonth: return 15; + default: return 3; + } + } + + private void WuMgr_FormClosing(object sender, FormClosingEventArgs e) + { + if (notifyIcon.Visible && allowshowdisplay) + { + e.Cancel = true; + allowshowdisplay = false; + this.Hide(); + return; + } + + agent.Progress -= OnProgress; + agent.UpdatesChaged -= OnUpdates; + agent.Finished -= OnFinished; } private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e) @@ -220,32 +364,6 @@ private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e) } } - private static void menuAbout_Click() - { - string About = ""; - About += Program.fmt("Author: \tDavid Xanatos\r\n"); - About += Program.fmt("Licence: \tGNU General Public License v3\r\n"); - About += Program.fmt("Version: \t{0}\r\n", Program.mVersion); - About += "\r\n"; - About += "Icons from: https://icons8.com/"; - - MessageBox.Show(About, Program.mName); - } - - private static Timer aTimer = null; - - private void OnTimedEvent(Object source, EventArgs e) - { - aTimer.Stop(); - aTimer = null; - if (!agent.IsActive()) - return; - if (chkOffline.Checked) - agent.SearchForUpdates((int)chkDownload.CheckState, chkOld.Checked); - else - agent.SearchForUpdates(dlSource.Text, chkOld.Checked); - } - private void LoadProviders(string source = null) { dlSource.Items.Clear(); @@ -262,21 +380,21 @@ private void LoadProviders(string source = null) void UpdateCounts() { if (agent.mPendingUpdates != null) - btnWinUpd.Text = Program.fmt("Windows Update ({0})", agent.mPendingUpdates.Count); + btnWinUpd.Text = MiscFunc.fmt("Windows Update ({0})", agent.mPendingUpdates.Count); if (agent.mInstalledUpdates != null) - btnInstalled.Text = Program.fmt("Installed Updates ({0})", agent.mInstalledUpdates.Count); + btnInstalled.Text = MiscFunc.fmt("Installed Updates ({0})", agent.mInstalledUpdates.Count); if (agent.mHiddenUpdates != null) - btnHidden.Text = Program.fmt("Hidden Updates ({0})", agent.mHiddenUpdates.Count); + btnHidden.Text = MiscFunc.fmt("Hidden Updates ({0})", agent.mHiddenUpdates.Count); if (agent.mUpdateHistory != null) - btnHistory.Text = Program.fmt("Update History ({0})", agent.mUpdateHistory.Count); + btnHistory.Text = MiscFunc.fmt("Update History ({0})", agent.mUpdateHistory.Count); } void LoadList() { - updateView.ClearObjects(); - updateView.CheckBoxes = CurrentList != UpdateLists.UpdateHistory; + updateView.ForeColor = updateView.CheckBoxes && !agent.IsValid() ? Color.Gray : Color.Black; + switch (CurrentList) { case UpdateLists.PendingUpdates: LoadList(agent.mPendingUpdates); break; @@ -286,39 +404,20 @@ void LoadList() } } - void LoadList(List List) - { - if (List != null) - { - List list = new List(); - foreach (IUpdateHistoryEntry2 update in List) - { - list.Add(new Update(update)); - } - updateView.AddObjects(list); - } - } - - void LoadList(UpdateCollection List) + void LoadList(List List) { - if (List != null) - { - List list = new List(); - foreach (IUpdate update in List) - { - list.Add(new Update(update)); - } - updateView.AddObjects(list); - } + updateView.ClearObjects(); + List list = new List(); + foreach (MsUpdate update in List) + list.Add(new UpdateItem(update)); + updateView.AddObjects(list); } - public UpdateCollection GetUpdates() + public List GetUpdates() { - UpdateCollection updates = new UpdateCollection(); - foreach (Update item in updateView.CheckedObjects) - { - updates.Add(item.Entry); - } + List updates = new List(); + foreach (UpdateItem item in updateView.CheckedObjects) + updates.Add(item.Update); return updates; } @@ -349,6 +448,8 @@ void SwitchList(UpdateLists List) LoadList(); UpdateState(); + + lblSupport.Visible = false; } private void UpdateState() @@ -358,15 +459,125 @@ private void UpdateState() progTotal.Visible = busy; lblStatus.Visible = busy; + bool isValid = agent.IsValid(); + bool isValid2 = isValid || chkManual.Checked; + bool enable = agent.IsActive() && !busy; btnSearch.Enabled = enable; - btnDownload.Enabled = enable && (CurrentList == UpdateLists.PendingUpdates); - btnInstall.Enabled = enable && (CurrentList == UpdateLists.PendingUpdates); - btnUnInstall.Enabled = enable && (CurrentList == UpdateLists.InstaledUpdates); - btnHide.Enabled = enable && (CurrentList == UpdateLists.PendingUpdates || CurrentList == UpdateLists.HiddenUpdates); + btnDownload.Enabled = enable && isValid2 && (CurrentList == UpdateLists.PendingUpdates); + btnInstall.Enabled = enable && isValid2 && (CurrentList == UpdateLists.PendingUpdates); + btnUnInstall.Enabled = enable && isValid2 && (CurrentList == UpdateLists.InstaledUpdates); + btnHide.Enabled = enable && isValid && (CurrentList == UpdateLists.PendingUpdates || CurrentList == UpdateLists.HiddenUpdates); btnGetLink.Enabled = CurrentList != UpdateLists.UpdateHistory; } + private MenuItem mToolsMenu = null; + private MenuItem wuauMenu = null; + + private void BuildToolsMenu() + { + wuauMenu = new MenuItem(); + wuauMenu.Text = "Windows Update Service"; + wuauMenu.Checked = agent.TestWuAuServ(); + wuauMenu.Click += new System.EventHandler(menuWuAu_Click); + mToolsMenu.MenuItems.Add(wuauMenu); + + mToolsMenu.MenuItems.Add(new MenuItem("-")); + + if (Directory.Exists(Program.appPath + @"\Tools")) + { + foreach (string subDir in Directory.GetDirectories(Program.appPath + @"\Tools")) + { + string Name = Path.GetFileName(subDir); + string INIPath = subDir + @"\" + Name + ".ini"; + + MenuItem toolMenu = new MenuItem(); + toolMenu.Text = Program.IniReadValue("Root", "Name", Name, INIPath); + + string Exec = Program.IniReadValue("Root", "Exec", "", INIPath); + if (Exec.Length > 0) + toolMenu.Click += delegate (object sender, EventArgs e) { menuExec_Click(sender, e, Exec); }; + else + { + int count = MiscFunc.parseInt(Program.IniReadValue("Root", "Entries", "", INIPath), 99); + for (int i = 1; i <= count; i++) + { + string name = Program.IniReadValue("Entry" + i.ToString(), "Name", "", INIPath); + if (name.Length == 0) + { + if (count != 99) + continue; + break; + } + + MenuItem subMenu = new MenuItem(); + subMenu.Text = name; + + string exec = Program.IniReadValue("Entry" + i.ToString(), "Exec", "", INIPath); + subMenu.Click += delegate (object sender, EventArgs e) { menuExec_Click(sender, e, exec); }; + + toolMenu.MenuItems.Add(subMenu); + } + } + + mToolsMenu.MenuItems.Add(toolMenu); + } + + mToolsMenu.MenuItems.Add(new MenuItem("-")); + } + + MenuItem refreshMenu = new MenuItem(); + refreshMenu.Text = "&Refresh"; + refreshMenu.Click += new System.EventHandler(menuRefresh_Click); + mToolsMenu.MenuItems.Add(refreshMenu); + } + + private void menuExec_Click(object Sender, EventArgs e, string exec) + { + Process.Start(exec); + } + + private void menuExit_Click(object Sender, EventArgs e) + { + Application.Exit(); + } + + private void menuAbout_Click(object Sender, EventArgs e) + { + string About = ""; + About += MiscFunc.fmt("Author: \tDavid Xanatos\r\n"); + About += MiscFunc.fmt("Licence: \tGNU General Public License v3\r\n"); + About += MiscFunc.fmt("Version: \t{0}\r\n", Program.mVersion); + About += "\r\n"; + About += "Icons from: https://icons8.com/"; + MessageBox.Show(About, Program.mName); + } + + private void menuWuAu_Click(object Sender, EventArgs e) + { + wuauMenu.Checked = !wuauMenu.Checked; + if (wuauMenu.Checked) + { + agent.EnableWuAuServ(true); + agent.Init(); + } + else + { + agent.UnInit(); + agent.EnableWuAuServ(false); + } + UpdateState(); + } + + private void menuRefresh_Click(object Sender, EventArgs e) + { + IntPtr MenuHandle = GetSystemMenu(this.Handle, false); // Note: to restore default set true + RemoveMenu(MenuHandle, 6, MF_BYPOSITION); + mToolsMenu.MenuItems.Clear(); + BuildToolsMenu(); + InsertMenu(MenuHandle, 6, MF_BYPOSITION | MF_POPUP, (int)mToolsMenu.Handle, "Tools"); + } + private void btnWinUpd_CheckedChanged(object sender, EventArgs e) { SwitchList(UpdateLists.PendingUpdates); @@ -384,9 +595,8 @@ private void btnHidden_CheckedChanged(object sender, EventArgs e) private void btnHistory_CheckedChanged(object sender, EventArgs e) { - if (!agent.IsActive()) - return; - agent.UpdateHistory(); + if (agent.IsActive()) + agent.UpdateHistory(); SwitchList(UpdateLists.UpdateHistory); } @@ -396,7 +606,7 @@ private void btnSearch_Click(object sender, EventArgs e) if (!agent.IsActive() || agent.IsBusy()) return; if (chkOffline.Checked) - agent.SearchForUpdates((int)chkDownload.CheckState, chkOld.Checked); + agent.SearchForUpdates(chkDownload.Checked, chkOld.Checked); else agent.SearchForUpdates(dlSource.Text, chkOld.Checked); } @@ -432,7 +642,12 @@ private void btnUnInstall_Click(object sender, EventArgs e) if (!agent.IsActive() || agent.IsBusy()) return; if (CurrentList == UpdateLists.InstaledUpdates) - agent.UnInstallUpdates(GetUpdates()); + { + if (chkOffline.Checked && chkManual.Checked) + agent.UnInstallUpdatesOffline(GetUpdates()); + else + agent.UnInstallUpdates(GetUpdates()); + } } private void btnHide_Click(object sender, EventArgs e) @@ -444,25 +659,15 @@ private void btnHide_Click(object sender, EventArgs e) case UpdateLists.PendingUpdates: agent.HideUpdates(GetUpdates(), true); break; case UpdateLists.HiddenUpdates: agent.HideUpdates(GetUpdates(), false); break; } - UpdateCounts(); - LoadList(); } private void btnGetLink_Click(object sender, EventArgs e) { - foreach (IUpdate update in GetUpdates()) + foreach (MsUpdate Update in GetUpdates()) { - AppLog.Line(update.Title); - foreach (IUpdate bundle in update.BundledUpdates) - { - foreach (IUpdateDownloadContent udc in bundle.DownloadContents) - { - if (String.IsNullOrEmpty(udc.DownloadUrl)) - continue; - - AppLog.Line(udc.DownloadUrl); - } - } + AppLog.Line(Update.Title); + foreach (string url in Update.Downloads) + AppLog.Line(url); AppLog.Line(""); } } @@ -511,12 +716,26 @@ void OnProgress(object sender, WuAgent.ProgressArgs args) UpdateState(); } - void OnFinished(object sender, WuAgent.FinishedArgs args) + void OnUpdates(object sender, WuAgent.UpdatesArgs args) { UpdateCounts(); - UpdateState(); - if (args.FoundUpdates) + if (args.Found) // if (agent.CurOperation() == WuAgent.AgentOperation.CheckingUpdates) + { + SetConfig("LastCheck", DateTime.Now.ToString()); SwitchList(UpdateLists.PendingUpdates); + } + else + { + LoadList(); + + if (MiscFunc.parseInt(Program.IniReadValue("Options", "Refresh", "0")) == 1 && (agent.CurOperation() == WuAgent.AgentOperation.InstallingUpdates || agent.CurOperation() == WuAgent.AgentOperation.RemoveingUpdtes)) + doUpdte = true; + } + } + + void OnFinished(object sender, WuAgent.FinishedArgs args) + { + UpdateState(); lblStatus.Text = ""; toolTip.SetToolTip(lblStatus, ""); } @@ -535,7 +754,7 @@ private void chkOffline_CheckedChanged(object sender, EventArgs e) SetConfig("Offline", chkOffline.Checked ? "1" : "0"); } - private void chkDownload_CheckStateChanged(object sender, EventArgs e) + private void chkDownload_CheckedChanged(object sender, EventArgs e) { SetConfig("Download", chkDownload.Checked ? "1" : "0"); } @@ -586,11 +805,21 @@ private void chkBlockMS_CheckedChanged(object sender, EventArgs e) private void chkAutoRun_CheckedChanged(object sender, EventArgs e) { + notifyIcon.Visible = dlAutoCheck.Enabled = chkAutoRun.Checked; + AutoUpdate = chkAutoRun.Checked ? (AutoUpdateOptions)dlAutoCheck.SelectedIndex : AutoUpdateOptions.No; if (mSuspendUpdate) return; Program.AutoStart(chkAutoRun.Checked); } + private void dlAutoCheck_SelectedIndexChanged(object sender, EventArgs e) + { + if (mSuspendUpdate) + return; + SetConfig("AutoUpdate", dlAutoCheck.SelectedIndex.ToString()); + AutoUpdate = (AutoUpdateOptions)dlAutoCheck.SelectedIndex; + } + private void chkNoUAC_CheckedChanged(object sender, EventArgs e) { if (mSuspendUpdate) @@ -609,14 +838,37 @@ private void chkMsUpd_CheckedChanged(object sender, EventArgs e) private void chkManual_CheckedChanged(object sender, EventArgs e) { + UpdateState(); SetConfig("Manual", chkManual.Checked ? "1" : "0"); } private void CheckAndHideUpdatePage() { - GPO.HideUpdatePage(chkBlockMS.Checked || dlPolMode.SelectedIndex == 1); + if(GPO.IsRespected() != 2) // 2 means some unknown windows most likely pre win 10 + GPO.HideUpdatePage(chkBlockMS.Checked || dlPolMode.SelectedIndex == 1); } + private void updateView_SelectedIndexChanged(object sender, EventArgs e) + { + UpdateItem update = (UpdateItem)updateView.SelectedObject; + if (update != null) + { + lblSupport.Links[0].LinkData = update.SupportUrl; + lblSupport.Links[0].Visited = false; + lblSupport.Visible = true; + toolTip.SetToolTip(lblSupport, update.SupportUrl); + } + else + lblSupport.Visible = false; + } + + private void lblSupport_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + string target = e.Link.LinkData as string; + System.Diagnostics.Process.Start(target); + } + + public string GetConfig(string name, string def = "") { return Program.IniReadValue("Options", name, def); @@ -632,110 +884,113 @@ public void SetConfig(string name, string value) //var subKey = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\Xanatos\Windows Update Manager", true); //subKey.SetValue(name, value); } - } + [DllImport("User32.dll")] + public static extern Int32 SetForegroundWindow(int hWnd); - class Update : INotifyPropertyChanged - { - public bool IsActive = true; - - public Update(IUpdate update) + private void notifyIcon_BalloonTipClicked(object sender, EventArgs e) { - Entry = update; - - Title = update.Title; - Category = GetCategory(update.Categories); - Description = update.Description; - Size = GetSizeStr(update); - Date = update.LastDeploymentChangeTime.ToString("dd.MM.yyyy"); - KB = GetKB(update); - - if (update.IsBeta) - State = "Beta "; - - if (update.IsInstalled) + if (!allowshowdisplay) { - State += "Installed"; - if (update.IsUninstallable) - State += " Removable"; - } - else if (update.IsHidden) - { - State += "Hidden"; - if (update.IsDownloaded) - State += " Downloaded"; - } - else - { - if (update.IsDownloaded) - State += "Downloaded"; - else - State += "Pending"; - if (update.AutoSelectOnWebSites) //update.DeploymentAction - State += " (!)"; - if (update.IsMandatory) - State += " Manatory"; + allowshowdisplay = true; + this.Show(); } + if(this.WindowState == FormWindowState.Minimized) + this.WindowState = FormWindowState.Normal; + SetForegroundWindow(this.Handle.ToInt32()); } + } + - public Update(IUpdateHistoryEntry2 update) + class UpdateItem : INotifyPropertyChanged + { + public UpdateItem(MsUpdate update) { - Title = update.Title; - Category = GetCategory(update.Categories); - Description = update.Description; - Date = update.Date.ToString("dd.MM.yyyy"); - switch (update.ResultCode) - { - case OperationResultCode.orcNotStarted: State = "Not Started"; break; - case OperationResultCode.orcInProgress: State = "In Progress"; break; - case OperationResultCode.orcSucceeded: State = "Succeeded"; break; - case OperationResultCode.orcSucceededWithErrors: State = "Succeeded with Errors"; break; - case OperationResultCode.orcFailed: State = "Failed"; break; - case OperationResultCode.orcAborted: State = "Aborted"; break; - } - State += " (0x" + String.Format("{0:X8}", update.HResult) + ")"; + Update = update; } - string GetCategory(ICategoryCollection cats) + public MsUpdate Update = null; + + public String Title { get { return Update.Title; } } + public String Category { get { return Update.Category; } } + public String KB { get { return Update.KB; } } + public String Date { get { return Update.Date.ToString("dd.MM.yyyy"); } } + public string Size { - string category = ""; - foreach (ICategory cat in cats) + get { - if (category.Length > 0) - category += "; "; - category += cat.Name; + decimal size = Update.Size; + if (size == 0) + return ""; + if (size > 1024 * 1024 * 1024) + return (size / (1024 * 1024 * 1024)).ToString("F") + " Gb"; + if (size > 1024 * 1024) + return (size / (1024 * 1024)).ToString("F") + " Mb"; + if (size > 1024) + return (size / (1024)).ToString("F") + " Kb"; + return ((Int64)size).ToString() + " b"; } - return category; - //return update.Categories.Count > 0 ? update.Categories[0].Name : "Unknown"; } - - string GetKB(IUpdate update) - { - return update.KBArticleIDs.Count > 0 ? "KB" + update.KBArticleIDs[0] : "KBUnknown"; - } - - string GetSizeStr(IUpdate update) + public String Description { get { return Update.Description; } } + public String State { - decimal size = update.MaxDownloadSize; - - if (size > 1024 * 1024 * 1024) - return (size / (1024 * 1024 * 1024)).ToString("F") + " Gb"; - if (size > 1024 * 1024) - return (size / (1024 * 1024)).ToString("F") + " Mb"; - if (size > 1024 * 1024) - return (size / (1024)).ToString("F") + " Kb"; - return ((Int64)size).ToString() + " b"; + get + { + string State = ""; + switch (Update.State) + { + case MsUpdate.UpdateState.History: + switch ((OperationResultCode)Update.ResultCode) + { + case OperationResultCode.orcNotStarted: State = "Not Started"; break; + case OperationResultCode.orcInProgress: State = "In Progress"; break; + case OperationResultCode.orcSucceeded: State = "Succeeded"; break; + case OperationResultCode.orcSucceededWithErrors: State = "Succeeded with Errors"; break; + case OperationResultCode.orcFailed: State = "Failed"; break; + case OperationResultCode.orcAborted: State = "Aborted"; break; + } + State += " (0x" + String.Format("{0:X8}", Update.HResult) + ")"; + break; + + default: + if ((Update.Attributes & (int)MsUpdate.UpdateAttr.Beta) != 0) + State = "Beta "; + + if ((Update.Attributes & (int)MsUpdate.UpdateAttr.Installed) != 0) + { + State += "Installed"; + if ((Update.Attributes & (int)MsUpdate.UpdateAttr.Uninstallable) != 0) + State += " Removable"; + } + else if ((Update.Attributes & (int)MsUpdate.UpdateAttr.Hidden) != 0) + { + State += "Hidden"; + if ((Update.Attributes & (int)MsUpdate.UpdateAttr.Downloaded) != 0) + State += " Downloaded"; + } + else + { + if ((Update.Attributes & (int)MsUpdate.UpdateAttr.Downloaded) != 0) + State += "Downloaded"; + else + State += "Pending"; + if ((Update.Attributes & (int)MsUpdate.UpdateAttr.AutoSelect) != 0) + State += " (!)"; + if ((Update.Attributes & (int)MsUpdate.UpdateAttr.Mandatory) != 0) + State += " Manatory"; + } + + if ((Update.Attributes & (int)MsUpdate.UpdateAttr.Exclusive) != 0) + State += ", Exclusive"; + + if ((Update.Attributes & (int)MsUpdate.UpdateAttr.Reboot) != 0) + State += ", Needs Reboot"; + break; + } + return State; + } } - - public String Title = ""; - public String Category = ""; - public String KB = ""; - public String Date = ""; - public String Size = ""; - public String Description = ""; - public String State = ""; - - public IUpdate Entry = null; + public String SupportUrl { get { return Update.SupportUrl; } } #region Implementation of INotifyPropertyChanged diff --git a/wumgr/WuMgr.resx b/wumgr/WuMgr.resx index d8bfaae..202ca62 100644 --- a/wumgr/WuMgr.resx +++ b/wumgr/WuMgr.resx @@ -120,14 +120,11 @@ 17, 17 - - 107, 17 - - + 274, 17 - + AAABAA8AMDAQAAEABABoBgAA9gAAACAgEAABAAQA6AIAAF4HAAAYGBAAAQAEAOgBAABGCgAAEBAQAAEA BAAoAQAALgwAAEBAAAABAAgAKBYAAFYNAAAwMAAAAQAIAKgOAAB+IwAAICAAAAEACACoCAAAJjIAABgY diff --git a/wumgr/wumgr.csproj b/wumgr/wumgr.csproj index b999f0c..d9d416b 100644 --- a/wumgr/wumgr.csproj +++ b/wumgr/wumgr.csproj @@ -12,6 +12,7 @@ 512 true + false publish\ true Disk @@ -24,7 +25,6 @@ true 0 1.0.0.%2a - false false true @@ -47,7 +47,7 @@ TRACE prompt 4 - false + true app.manifest @@ -72,14 +72,18 @@ - + + + + - - - Component - - - + + + + + + + Form