├── assets
├── icon32x32.ico
└── SharpUpdater-logo.png
├── src
├── CLI
│ ├── icon32x32.ico
│ ├── Properties
│ │ └── launchSettings.json
│ ├── ConsoleExtensions.cs
│ ├── CommandHandlers
│ │ ├── InitManifestFileCommandHandler.cs
│ │ ├── PushCommandHandler.cs
│ │ ├── InitIgnoreFileCommandHandler.cs
│ │ ├── GlobalSourceCommandHandler.cs
│ │ └── PackCommandHandler.cs
│ ├── README.md
│ ├── SharpUpdater.CLI.csproj
│ ├── ProjectHolder.cs
│ └── Program.cs
├── VSIX
│ ├── Resources
│ │ ├── folder.png
│ │ ├── Command2Package.ico
│ │ └── SharpUpdater-logo16x16.ico
│ ├── SharpUpdater-logo.png
│ ├── SharpUpdater-VsTool-Menus.png
│ ├── SharpUpdater-VsTool-Dialog.png
│ ├── release_notes.txt
│ ├── FileListItem.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── README.md
│ ├── Util
│ │ ├── CacheHelper.cs
│ │ ├── SolutionDataCache.cs
│ │ └── ManifestGatherer.cs
│ ├── SharpUpdaterPackage1.cs
│ ├── source.extension.vsixmanifest
│ ├── SharpUpdaterPackage.vsct
│ ├── Resource.Designer.cs
│ ├── Commands
│ │ ├── AddIgnoreFileCommand.cs
│ │ ├── AddManifestFileCommand.cs
│ │ └── SharpPackCommand.cs
│ ├── Wizard
│ │ ├── ManifestGrid.Designer.cs
│ │ ├── ProductInfoControl.cs
│ │ ├── ProductInfoControl.resx
│ │ └── ManifestGrid.resx
│ ├── Resource.resx
│ ├── Common.cs
│ └── SharpUpdaterPackage.cs
├── Clients
│ ├── WinForms-DotNet8
│ │ ├── update.ico
│ │ ├── Program.cs
│ │ ├── Properties
│ │ │ ├── PublishProfiles
│ │ │ │ └── FolderProfile.pubxml
│ │ │ ├── Resources.Designer.cs
│ │ │ └── Resources.resx
│ │ ├── Common.cs
│ │ ├── ZipHelper.cs
│ │ ├── Bootstrapper.cs
│ │ ├── WinForms-DotNet8.csproj
│ │ ├── BaseForm.cs
│ │ ├── ConnectionForm.Designer.cs
│ │ ├── Installer.cs
│ │ ├── UpdateForm.cs
│ │ ├── ConnectionForm.cs
│ │ └── UpdateForm.designer.cs
│ └── WinForms-DotNet4
│ │ ├── FodyWeavers.xml
│ │ ├── App.config
│ │ ├── Properties
│ │ ├── Settings.settings
│ │ ├── Settings.Designer.cs
│ │ ├── AssemblyInfo.cs
│ │ ├── Resources.Designer.cs
│ │ └── Resources.resx
│ │ ├── packages.config
│ │ └── Program.cs
└── Core
│ ├── ReleaseFile.cs
│ ├── UpdateInfo.cs
│ ├── Packaging
│ ├── PackageFeedSummary.cs
│ ├── PackageFeedResolver.cs
│ └── PackageBuilder.cs
│ ├── Templates.cs
│ ├── UpdateLog.cs
│ ├── Settings.cs
│ ├── Constants.cs
│ ├── Util
│ ├── HttpUtil.cs
│ ├── Monitor.cs
│ ├── PackageUploader.cs
│ ├── CmdHelper.cs
│ ├── Logger.cs
│ ├── UriUtility.cs
│ ├── IgnoreFileParser.cs
│ ├── XmlSerializerHelper.cs
│ ├── ProjectHelper.cs
│ ├── ManifestGatherer.cs
│ ├── VersionUtil.cs
│ ├── Downloader.cs
│ └── FileUtil.cs
│ └── SharpUpdater.Core.csproj
├── README.md
├── SharpUpdater.sln
└── .gitignore
/assets/icon32x32.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cnsharp/SharpUpdater/HEAD/assets/icon32x32.ico
--------------------------------------------------------------------------------
/src/CLI/icon32x32.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cnsharp/SharpUpdater/HEAD/src/CLI/icon32x32.ico
--------------------------------------------------------------------------------
/assets/SharpUpdater-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cnsharp/SharpUpdater/HEAD/assets/SharpUpdater-logo.png
--------------------------------------------------------------------------------
/src/VSIX/Resources/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cnsharp/SharpUpdater/HEAD/src/VSIX/Resources/folder.png
--------------------------------------------------------------------------------
/src/VSIX/SharpUpdater-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cnsharp/SharpUpdater/HEAD/src/VSIX/SharpUpdater-logo.png
--------------------------------------------------------------------------------
/src/VSIX/Resources/Command2Package.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cnsharp/SharpUpdater/HEAD/src/VSIX/Resources/Command2Package.ico
--------------------------------------------------------------------------------
/src/VSIX/SharpUpdater-VsTool-Menus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cnsharp/SharpUpdater/HEAD/src/VSIX/SharpUpdater-VsTool-Menus.png
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/update.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cnsharp/SharpUpdater/HEAD/src/Clients/WinForms-DotNet8/update.ico
--------------------------------------------------------------------------------
/src/VSIX/SharpUpdater-VsTool-Dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cnsharp/SharpUpdater/HEAD/src/VSIX/SharpUpdater-VsTool-Dialog.png
--------------------------------------------------------------------------------
/src/CLI/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "DotNetTool": {
4 | "commandName": "Project"
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/src/VSIX/Resources/SharpUpdater-logo16x16.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cnsharp/SharpUpdater/HEAD/src/VSIX/Resources/SharpUpdater-logo16x16.ico
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet4/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet4/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/VSIX/release_notes.txt:
--------------------------------------------------------------------------------
1 | V17.0.3 2025/4/25
2 | - Output .sp/.manifest files only other than all files.
3 |
4 | V17.0 2025/3/2
5 | - Support Visual Studio 2022 (VS17)
6 |
7 | V4.0 2016/6/24 (Has been offline)
8 | - A bran-new version to make package similar to NuGet package
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet4/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/VSIX/FileListItem.cs:
--------------------------------------------------------------------------------
1 | namespace CnSharp.VisualStudio.SharpUpdater
2 | {
3 | public class FileListItem
4 | {
5 | public string Dir { get; set; }
6 | public bool IsFile { get; set; }
7 | public bool Selected { get; set; }
8 | public string RelativeFileName { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Core/ReleaseFile.cs:
--------------------------------------------------------------------------------
1 | namespace CnSharp.Updater
2 | {
3 | public class ReleaseFile
4 | {
5 | public string FileName { get; set; }
6 |
7 | ///
8 | /// File size in Byte
9 | ///
10 | public long FileSize { get; set; }
11 |
12 | public string Version { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/CLI/ConsoleExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace CnSharp.Updater.CLI
2 | {
3 | public static class ConsoleExtensions
4 | {
5 | public static void WriteError(string message)
6 | {
7 | Console.ForegroundColor = ConsoleColor.Red;
8 | Console.WriteLine(message);
9 | Console.ResetColor();
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet4/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/Core/UpdateInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CnSharp.Updater
4 | {
5 | public struct UpdateInfo
6 | {
7 | public string Version { get; set; }
8 | public string UpdateLog { get; set; }
9 | public string PackUrl { get; set; }
10 | public bool NewVersionFound { get; set; }
11 | public bool Success { get; set; }
12 | public Exception Exception { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet4/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace CnSharp.Windows.Updater
5 | {
6 | static class Program
7 | {
8 | ///
9 | /// The main entry point for the application.
10 | ///
11 | [STAThread]
12 | static void Main()
13 | {
14 | Application.EnableVisualStyles();
15 | Application.SetCompatibleTextRenderingDefault(false);
16 | Bootstrapper.Start();
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/VSIX/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: AssemblyTitle("SharpUpdater")]
5 | [assembly: AssemblyDescription("")]
6 | [assembly: AssemblyConfiguration("")]
7 | [assembly: AssemblyCompany("")]
8 | [assembly: AssemblyProduct("SharpUpdater")]
9 | [assembly: AssemblyCopyright("")]
10 | [assembly: AssemblyTrademark("")]
11 | [assembly: AssemblyCulture("")]
12 |
13 | [assembly: ComVisible(false)]
14 |
15 |
16 | [assembly: AssemblyVersion("17.0.3.0")]
17 | [assembly: AssemblyFileVersion("17.0.3.0")]
--------------------------------------------------------------------------------
/src/Core/Packaging/PackageFeedSummary.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CnSharp.Updater.Packaging
4 | {
5 | public class PackageFeedSummary
6 | {
7 | public string Id { get; set; }
8 | public string Version { get; set; }
9 | public DateTimeOffset Updated { get; set; }
10 | public string ReleaseNotes { get; set; }
11 | public string PackageUrl { get; set; }
12 | public long PackageSize { get; set; }
13 |
14 | public bool IsNew(Manifest manifest)
15 | {
16 | return manifest.Version != Version;
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/Program.cs:
--------------------------------------------------------------------------------
1 | namespace CnSharp.Windows.Updater
2 | {
3 | internal static class Program
4 | {
5 |
6 | ///
7 | /// The main entry point for the application.
8 | ///
9 | [STAThread]
10 | static void Main()
11 | {
12 | // To customize application configuration such as set high DPI settings or default font,
13 | // see https://aka.ms/applicationconfiguration.
14 | ApplicationConfiguration.Initialize();
15 | Bootstrapper.Start();
16 | }
17 |
18 | }
19 | }
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/Properties/PublishProfiles/FolderProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Release
6 | Any CPU
7 | bin\Release\net8.0-windows\publish\win-x64\
8 | FileSystem
9 | <_TargetId>Folder
10 | net8.0-windows
11 | win-x64
12 | false
13 | false
14 |
15 |
--------------------------------------------------------------------------------
/src/VSIX/README.md:
--------------------------------------------------------------------------------
1 | # SharpUpdater VSIX
2 |
3 | This is a Visual Studio extension to build/deploy SharpUpdater package (.sp) for your desktop applications.
4 |
5 | ## Features
6 |
7 | - Commands to add ignore files and manifest files.
8 | - Commands to pack and push the SharpUpdater package (.sp) to the server.
9 |
10 | ## Screenshots
11 |
12 | 
13 |
14 | 
15 |
16 | ## Fork on GitHub
17 | [https://github.com/cnsharp/SharpUpdater](https://github.com/cnsharp/SharpUpdater)
18 |
19 | ## Feedback
20 | [https://github.com/cnsharp/SharpUpdater/issues](https://github.com/cnsharp/SharpUpdater/issues)
--------------------------------------------------------------------------------
/src/Core/Templates.cs:
--------------------------------------------------------------------------------
1 | namespace CnSharp.Updater
2 | {
3 | public sealed class Templates
4 | {
5 | public const string ManifestXml =
6 | @"
7 | $id$
8 |
9 | $owner$
10 |
11 | $copyright$
12 | $version$
13 |
14 | $version$
15 |
16 |
17 |
18 |
19 |
20 | ";
21 |
22 | public const string IgnoreFiles = @".log
23 | .pdb
24 | *.vshost.
25 | updater.exe
26 | ";
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Core/UpdateLog.cs:
--------------------------------------------------------------------------------
1 | using System.Xml;
2 | using System.Xml.Serialization;
3 |
4 | namespace CnSharp.Updater
5 | {
6 | public class UpdateLog
7 | {
8 | public string Version { get; set; }
9 | public string ReleaseDate { get; set; }
10 | private string _description;
11 | [XmlIgnore]
12 | public string Description
13 | {
14 | get { return _description; }
15 | set { _description = value; }
16 | }
17 |
18 | [XmlElement("Description")] //注意生成的节点名要同XmlIgnore的名称一致
19 | public XmlNode TextCData //不要使用XmlCDataSection,否则反序列化会出异常
20 | {
21 | get
22 | {
23 | return new XmlDocument().CreateCDataSection(_description);
24 | //注意此处不能写成 this.Description,否则将会得到null
25 | }
26 | set { _description = value.Value; }
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/VSIX/Util/CacheHelper.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using CnSharp.Updater.Util;
4 |
5 | namespace CnSharp.VisualStudio.SharpUpdater.Util
6 | {
7 | public class CacheHelper where T : new()
8 | {
9 |
10 | public T Get(string dir)
11 | {
12 | if (!File.Exists(dir))
13 | {
14 | throw new FileNotFoundException("cache file not found.", dir);
15 | }
16 | var obj = XmlSerializerHelper.LoadObjectFromXml(dir);
17 | return obj;
18 | }
19 |
20 | public void Save(T project, string dir)
21 | {
22 | SaveXml(project, dir);
23 | }
24 |
25 |
26 | private void SaveXml(T cache, string dir)
27 | {
28 | var xml = XmlSerializerHelper.GetXmlStringFromObject(cache);
29 | File.WriteAllText(dir, xml, Encoding.UTF8);
30 | }
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/src/Core/Settings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using CnSharp.Updater.Util;
4 |
5 | namespace CnSharp.Updater
6 | {
7 | public class Settings
8 | {
9 | public const string FileName = "settings.xml";
10 | static readonly string FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Constants.ProductName, FileName);
11 |
12 | public static Settings Load()
13 | {
14 | if (!File.Exists(FilePath)) return null;
15 | return XmlSerializerHelper.LoadObjectFromXml(FilePath);
16 | }
17 |
18 | public string GlobalSource { get; set; }
19 |
20 |
21 | public void Save()
22 | {
23 | var dir = Path.GetDirectoryName(FilePath);
24 | if (!Directory.Exists(dir))
25 | Directory.CreateDirectory(dir);
26 | XmlSerializerHelper.SerializeToXmlFile(this, FilePath);
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/src/CLI/CommandHandlers/InitManifestFileCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 | using System.CommandLine.Invocation;
3 | using System.Text;
4 |
5 | namespace CnSharp.Updater.CLI.CommandHandlers;
6 |
7 | internal class InitManifestFileCommandHandler : ICommandHandler
8 | {
9 |
10 | public int Invoke(InvocationContext context)
11 | {
12 | var dir = Directory.GetCurrentDirectory();
13 | string filePath = Path.Combine(dir, Constants.ManifestFileName);
14 | if (File.Exists(filePath))
15 | {
16 | ConsoleExtensions.WriteError($"File '{filePath}' is already exists.");
17 | return -1;
18 | }
19 | File.WriteAllText(filePath, Templates.ManifestXml, Encoding.UTF8);
20 | context.Console.WriteLine($"File '{filePath}' created.");
21 | return 0;
22 | }
23 |
24 | public Task InvokeAsync(InvocationContext context)
25 | {
26 | Invoke(context);
27 | return Task.FromResult(0);
28 | }
29 | }
--------------------------------------------------------------------------------
/src/CLI/CommandHandlers/PushCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using CnSharp.Updater;
2 | using CnSharp.Updater.Util;
3 |
4 | namespace CnSharp.Updater.CLI.CommandHandlers;
5 |
6 | internal class PushCommandHandler
7 | {
8 | public static async Task Invoke(string packageFile, string source, string apiKey)
9 | {
10 | var globalSource = Settings.Load()?.GlobalSource;
11 | var currentSource = !string.IsNullOrWhiteSpace(source) ? source : globalSource;
12 | if (string.IsNullOrWhiteSpace(currentSource))
13 | {
14 | ConsoleExtensions.WriteError("--source is required.");
15 | return;
16 | }
17 | Console.WriteLine("Start...");
18 | try
19 | {
20 | await PackageUploader.Upload(packageFile, currentSource, apiKey);
21 | }
22 | catch(Exception e)
23 | {
24 | ConsoleExtensions.WriteError(e.Message);
25 | return;
26 | }
27 |
28 | Console.WriteLine("Push successfully.");
29 | }
30 | }
--------------------------------------------------------------------------------
/src/CLI/CommandHandlers/InitIgnoreFileCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using System.CommandLine;
2 | using System.CommandLine.Invocation;
3 | using System.Text;
4 |
5 | namespace CnSharp.Updater.CLI.CommandHandlers;
6 |
7 | internal class InitIgnoreFileCommandHandler : ICommandHandler
8 | {
9 |
10 | public int Invoke(InvocationContext context)
11 | {
12 | var dir = Directory.GetCurrentDirectory();
13 | string ignoreFilePath = Path.Combine(dir, Constants.IgnoreFileName);
14 | if (File.Exists(ignoreFilePath))
15 | {
16 | ConsoleExtensions.WriteError($"File '{ignoreFilePath}' is already exists.");
17 | return -1;
18 | }
19 | File.WriteAllText(ignoreFilePath, Templates.IgnoreFiles, Encoding.UTF8);
20 | context.Console.WriteLine($"File '{ignoreFilePath}' created.");
21 | return 0;
22 | }
23 |
24 | public Task InvokeAsync(InvocationContext context)
25 | {
26 | Invoke(context);
27 | return Task.FromResult(0);
28 | }
29 | }
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/Common.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using CnSharp.Updater;
4 |
5 | namespace CnSharp.Windows.Updater
6 | {
7 | public class Common
8 | {
9 | public const string AppName = "SharpUpdater";
10 |
11 | public static string[] IgnoreFiles =
12 | {
13 | "Updater.exe",
14 | "[Content_Types].xml"
15 | };
16 |
17 | public static string[] IgnoreFolders = {"_rels"};
18 |
19 | public static string GetLocalText(string key)
20 | {
21 | return Properties.Resources.ResourceManager.GetString(key);
22 | }
23 |
24 |
25 | public static void Start(Manifest manifest,Exception e = null)
26 | {
27 | if (e != null)
28 | {
29 | Console.WriteLine(e.Message);
30 | Console.WriteLine(e.StackTrace);
31 | }
32 | Process.Start(manifest.EntryPoint, e != null ? "exception" : "ok");
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/src/Core/Constants.cs:
--------------------------------------------------------------------------------
1 | namespace CnSharp.Updater
2 | {
3 | public static class Constants
4 | {
5 | public const string ApiKeyHeader = "X-SHARPUPDATER-APIKEY";
6 | public const string HashAlgorithm = "SHA512";
7 | public const string ProductName = "SharpUpdater";
8 | public const string ManifestRelationType = "manifest";
9 | public const string ManifestExtension = Manifest.ManifestExt;
10 | public const string ManifestFileName = Manifest.ManifestFileName;
11 | public const string IgnoreFileName = ProductName+".ignore";
12 | public const string PackageRelationshipNamespace = "http://schemas.cnsharp.com/sharpupdater/2016/06/";
13 | public const string UriId = "sp";
14 | public const string UpdaterFileName = "updater.exe";
15 |
16 | ///
17 | /// Represents the ".sp" extension.
18 | ///
19 | public const string PackageExtension = Manifest.PackageFileExt;
20 |
21 | ///
22 | /// Represents the ".sp.sha512" extension.
23 | ///
24 | public const string HashFileExtension = PackageExtension + ".sha512";
25 | }
26 | }
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet4/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace CnSharp.Windows.Updater.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.13.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/CLI/CommandHandlers/GlobalSourceCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using CnSharp.Updater.Util;
2 |
3 | namespace CnSharp.Updater.CLI.CommandHandlers
4 | {
5 | internal class GlobalSourceCommandHandler
6 | {
7 | public static async Task Set(string source)
8 | {
9 | try
10 | {
11 | await HttpUtil.CheckUrl(source);
12 | }
13 | catch (Exception e)
14 | {
15 | ConsoleExtensions.WriteError(e.Message);
16 | return;
17 | }
18 |
19 | var settings = Settings.Load() ?? new Settings();
20 | settings.GlobalSource = source;
21 | settings.Save();
22 | Console.WriteLine("Global source set successfully.");
23 | }
24 |
25 | public static async Task Remove()
26 | {
27 | var settings = Settings.Load();
28 | if (settings == null)
29 | {
30 | Console.WriteLine("Global source is not set.");
31 | return;
32 | }
33 | settings.GlobalSource = null;
34 | settings.Save();
35 | Console.WriteLine("Global source removed.");
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/src/Core/Util/HttpUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using System.Text.RegularExpressions;
4 | using System.Threading.Tasks;
5 |
6 | namespace CnSharp.Updater.Util
7 | {
8 | public class HttpUtil
9 | {
10 | public static async Task CheckUrl(string url)
11 | {
12 | if (!IsValidUrl(url))
13 | throw new ArgumentException("The provided URL is not a valid URL.");
14 | if (!await IsUrlAccessible(url))
15 | throw new HttpRequestException("The provided URL is not accessible.");
16 | }
17 |
18 | public static bool IsValidUrl(string url)
19 | {
20 | var pattern = @"^(http|https)://";
21 | return Regex.IsMatch(url, pattern);
22 | }
23 |
24 | public static async Task IsUrlAccessible(string url)
25 | {
26 | using (var client = new HttpClient())
27 | {
28 | try
29 | {
30 | var response = await client.GetAsync(url);
31 | return response.IsSuccessStatusCode;
32 | }
33 | catch
34 | {
35 | return false;
36 | }
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet4/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("WinForms-DotNet4")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("WinForms-DotNet4")]
13 | [assembly: AssemblyCopyright("Copyright © 2025")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("e27df114-21a2-4eb5-ade9-bf6559950b8e")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | [assembly: AssemblyVersion("1.0.0.0")]
33 | [assembly: AssemblyFileVersion("1.0.0.0")]
34 |
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/ZipHelper.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.IO.Packaging;
3 | using CnSharp.Updater.Util;
4 |
5 | namespace CnSharp.Windows.Updater
6 | {
7 | public class ZipHelper
8 | {
9 | public static void Unzip(string compressedFileName, string folderName, bool overrideExisting)
10 | {
11 | var directoryInfo = new DirectoryInfo(folderName);
12 | if (!directoryInfo.Exists)
13 | directoryInfo.Create();
14 |
15 | using (var package = Package.Open(compressedFileName, FileMode.Open, FileAccess.Read))
16 | {
17 | foreach (var packagePart in package.GetParts())
18 | {
19 | ExtractPart(packagePart, folderName, overrideExisting);
20 | }
21 | }
22 | }
23 |
24 | private static void ExtractPart(PackagePart packagePart, string targetDirectory, bool overrideExisting)
25 | {
26 | var stringPart = targetDirectory + packagePart.Uri.ToString().Replace('\\', '/');
27 |
28 | if (!Directory.Exists(Path.GetDirectoryName(stringPart)))
29 | Directory.CreateDirectory(Path.GetDirectoryName(stringPart));
30 |
31 | if (!overrideExisting && File.Exists(stringPart))
32 | return;
33 | using (var fileStream = new FileStream(stringPart, FileMode.Create))
34 | {
35 | packagePart.GetStream().CopyTo(fileStream);
36 | }
37 | }
38 |
39 |
40 | }
41 | }
--------------------------------------------------------------------------------
/src/Core/Util/Monitor.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.IO;
3 |
4 | namespace CnSharp.Updater.Util
5 | {
6 | public class Monitor : BackgroundWorker
7 | {
8 | private readonly string _manifestPath;
9 | private readonly string _tempDir;
10 |
11 | public Monitor(string manifestPath, string tempDir)
12 | {
13 | _manifestPath = manifestPath;
14 | _tempDir = tempDir;
15 | }
16 |
17 | public Manifest LocalManifest { get; private set; }
18 | public Manifest RemoteManifest { get; private set; }
19 | public bool HasNewVersion { get; private set; }
20 |
21 | protected override void OnDoWork(DoWorkEventArgs e)
22 | {
23 | base.OnDoWork(e);
24 | LocalManifest = FileUtil.ReadManifest(_manifestPath);
25 | RemoteManifest = FileUtil.ReadManifest(LocalManifest.ReleaseUrl + "/sp/FindPackagesById()?id="+LocalManifest.Id);
26 |
27 |
28 | if (LocalManifest.CompareTo(RemoteManifest) != 0)
29 | {
30 | HasNewVersion = true;
31 | if (!string.IsNullOrEmpty(_tempDir))
32 | {
33 | if (!Directory.Exists(_tempDir))
34 | Directory.CreateDirectory(_tempDir);
35 | RemoteManifest.Save(Path.Combine(_tempDir, Manifest.ManifestFileName));
36 | //download update files silently
37 | var loader = new Downloader(LocalManifest, RemoteManifest, _tempDir);
38 | loader.RunWorkerAsync();
39 | }
40 | }
41 | }
42 |
43 | }
44 | }
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/Bootstrapper.cs:
--------------------------------------------------------------------------------
1 | using CnSharp.Updater;
2 | using System;
3 | using System.IO;
4 | using System.Threading;
5 | using System.Windows.Forms;
6 |
7 | namespace CnSharp.Windows.Updater
8 | {
9 | public class Bootstrapper
10 | {
11 | private static StreamWriter sw;
12 | public static void Start()
13 | {
14 | var logFile = Path.Combine(Application.StartupPath, "_logs",
15 | $"updater_{DateTime.Today:yyyyMMdd}.log");
16 | var dir = Path.GetDirectoryName(logFile);
17 | if (!Directory.Exists(dir))
18 | Directory.CreateDirectory(dir);
19 | sw = new StreamWriter(logFile);
20 | Console.SetOut(sw);
21 | var manifestFile = Path.Combine(Application.StartupPath, Manifest.ManifestFileName);
22 | if (!File.Exists(manifestFile))
23 | {
24 | Console.WriteLine("manifest not found.");
25 | return;
26 | }
27 | Application.ThreadException += Application_ThreadException;
28 | Application.ApplicationExit += ApplicationOnApplicationExit;
29 | var connForm = new ConnectionForm();
30 | Application.Run(connForm);
31 | }
32 |
33 | private static void ApplicationOnApplicationExit(object sender, EventArgs e)
34 | {
35 | sw.Flush();
36 | sw.Close();
37 | }
38 |
39 | static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
40 | {
41 | Console.WriteLine(e.Exception.Message);
42 | Console.WriteLine(e.Exception.StackTrace);
43 | }
44 |
45 | }
46 | }
--------------------------------------------------------------------------------
/src/Core/SharpUpdater.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netstandard2.0
4 | CnSharp.Updater
5 | 5.0.0.0
6 | 5.0.0.0
7 | True
8 | sn.snk
9 | False
10 | False
11 | False
12 | CnSharp Studio
13 | Copyright © CnSharp Studio 2012-2025
14 | Core library of SharpUpdater.
15 | SharpUpdater.Core
16 | Apache-2.0
17 | https://github.com/cnsharp/SharpUpdater
18 | Add settings
19 | git
20 | https://github.com/cnsharp/SharpUpdater.git
21 | 5.0.1
22 | CnSharp Studio
23 | SharpUpdater-logo.png
24 | Winforms,WPF,Desktop,Updater,AutoUpdate
25 |
26 |
27 |
28 | True
29 | \
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/Core/Util/PackageUploader.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Net.Http.Headers;
3 | using System.Net.Http;
4 | using System.Threading.Tasks;
5 |
6 | namespace CnSharp.Updater.Util
7 | {
8 | public class PackageUploader
9 | {
10 | public static async Task Upload(string packageFile, string source, string apiKey)
11 | {
12 | if (!packageFile.EndsWith(Constants.PackageExtension))
13 | throw new InvalidDataException("Invalid package format.");
14 | using (var httpClient = new HttpClient())
15 | {
16 | httpClient.DefaultRequestHeaders.Add(Constants.ApiKeyHeader, apiKey);
17 |
18 | using (var content = new MultipartFormDataContent())
19 | {
20 | var fileContent = new ByteArrayContent(await ReadAllBytesAsync(packageFile));
21 | fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
22 | content.Add(fileContent, "file", Path.GetFileName(packageFile));
23 |
24 | var response = await httpClient.PutAsync(source, content);
25 | response.EnsureSuccessStatusCode();
26 | }
27 | }
28 | }
29 |
30 | public static async Task ReadAllBytesAsync(string filePath)
31 | {
32 | using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true))
33 | using (var memoryStream = new MemoryStream())
34 | {
35 | await fileStream.CopyToAsync(memoryStream);
36 | return memoryStream.ToArray();
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/CLI/README.md:
--------------------------------------------------------------------------------
1 | # CnSharp.Updater.CLI
2 |
3 | This is a packaging & deployment CLI of `SharpUpdater`.
4 |
5 | ## Installation
6 | ```
7 | dotnet tool install --global SharpUpdater.CLI
8 | ```
9 |
10 | ## Root Command
11 | ```
12 | su
13 | ```
14 |
15 | ## Commands
16 | ### `global`
17 |
18 | Sets the global settings.
19 |
20 | **Options:**
21 | - `--source`, `-s` : Set the global SharpUpdater.Server source URL (Required)
22 |
23 | ```
24 | su global -s http://your.server
25 | ```
26 |
27 | ### `RemoveSource`
28 |
29 | Removes the global source.
30 |
31 | ```
32 | su RemoveSource
33 | ```
34 |
35 | ### `init`
36 |
37 | Generates a manifest file in the current directory.
38 |
39 | ```
40 | su init
41 | ```
42 |
43 | ### `ignore`
44 |
45 | Generates an ignore file in the current directory.
46 |
47 | ```
48 | su ignore
49 | ```
50 |
51 | ### `pack`
52 |
53 | Packs the project.
54 |
55 | **Options:**
56 | - `--source`, `-s` : Specify the SharpUpdater.Server source URL (Optional, follows global source if not inputed)
57 | - `--project`, `-p` : Specify the project directory
58 | - `--output`, `-o` : Specify the output directory (Default: `bin\SharpUpdater\`)
59 | - `--version`, `-v` : Specify the package version
60 | - `--MinimumVersion`, `-mv` : Specify the minimum version that must be updated
61 | - `--ReleaseNotes`, `-rn` : Input release notes
62 | - `--no-build` : Skip build
63 |
64 | ```
65 | su pack -v 1.0.0
66 | ```
67 |
68 | ### `push`
69 |
70 | Pushes the .sp package to SharpUpdater.Server.
71 |
72 | **Options:**
73 | - `--package`, `-p` : Specify the .sp file path (Required)
74 | - `--source`, `-s` : Specify the SharpUpdater.Server source URL (Optional, follows global source if not inputed)
75 | - `--apikey`, `-k` : Specify the ApiKey of SharpUpdater.Server (Required)
76 |
77 | ```
78 | su push -p some.sp -k YOUR_KEY
79 | ```
--------------------------------------------------------------------------------
/src/VSIX/SharpUpdaterPackage1.cs:
--------------------------------------------------------------------------------
1 | // ------------------------------------------------------------------------------
2 | //
3 | // This file was generated by VSIX Synchronizer
4 | //
5 | // ------------------------------------------------------------------------------
6 | namespace CnSharp.VisualStudio.SharpUpdater
7 | {
8 | using System;
9 |
10 | ///
11 | /// Helper class that exposes all GUIDs used across VS Package.
12 | ///
13 | internal sealed partial class PackageGuids
14 | {
15 | public const string guidSharpUpdaterPackageString = "85ecc0d6-9c3a-432f-ac95-7a9583195cf5";
16 | public static Guid guidSharpUpdaterPackage = new Guid(guidSharpUpdaterPackageString);
17 |
18 | public const string guidProjectCmdSetString = "9065ee1d-60e4-44a1-9303-2679b24fa055";
19 | public static Guid guidProjectCmdSet = new Guid(guidProjectCmdSetString);
20 |
21 | public const string guidProjectAddCmdSetString = "307e403c-3431-45b9-86d6-3dff0810d838";
22 | public static Guid guidProjectAddCmdSet = new Guid(guidProjectAddCmdSetString);
23 |
24 | public const string guidImagesString = "d524bb98-8a0c-4aa9-95ea-87ece9af81f8";
25 | public static Guid guidImages = new Guid(guidImagesString);
26 | }
27 | ///
28 | /// Helper class that encapsulates all CommandIDs uses across VS Package.
29 | ///
30 | internal sealed partial class PackageIds
31 | {
32 | public const int ProjectMenuGroup = 0x1020;
33 | public const int SharpPackCommandId = 0x0100;
34 | public const int ProjectAddMenuGroup = 0x1020;
35 | public const int AddIgnoreFileCommandId = 0x0100;
36 | public const int AddManifestFileCommandId = 0x0101;
37 | public const int icon1 = 0x0001;
38 | }
39 | }
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/WinForms-DotNet8.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net8.0-windows
6 | CnSharp.Windows.Updater
7 | enable
8 | true
9 | enable
10 | Updater
11 | true
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | Form
35 |
36 |
37 | Form
38 |
39 |
40 | True
41 | True
42 | Resources.resx
43 |
44 |
45 | Form
46 |
47 |
48 |
49 |
50 |
51 | Designer
52 | Resources.Designer.cs
53 | ResXFileCodeGenerator
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/src/Core/Packaging/PackageFeedResolver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Xml;
3 |
4 | namespace CnSharp.Updater.Packaging
5 | {
6 | public class PackageFeedResolver
7 | {
8 | private readonly XmlDocument _xmlDoc;
9 | private XmlNamespaceManager _namespaceManager;
10 |
11 | private void AddNamespaceManager()
12 | {
13 | _namespaceManager = new XmlNamespaceManager(_xmlDoc.NameTable);
14 | _namespaceManager.AddNamespace("ns", "http://www.w3.org/2005/Atom");
15 | _namespaceManager.AddNamespace("m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata");
16 | _namespaceManager.AddNamespace("d", "http://schemas.microsoft.com/ado/2007/08/dataservices");
17 | }
18 |
19 | public PackageFeedResolver(string xml)
20 | {
21 | _xmlDoc = new XmlDocument();
22 | _xmlDoc.LoadXml(xml);
23 | AddNamespaceManager();
24 | }
25 |
26 | public PackageFeedResolver(XmlDocument doc)
27 | {
28 | _xmlDoc = doc;
29 | AddNamespaceManager();
30 | }
31 |
32 | public PackageFeedSummary GetSummary()
33 | {
34 | var root = _xmlDoc.ChildNodes[1];
35 | return new PackageFeedSummary
36 | {
37 | Id = root.SelectSingleNode("//ns:entry/ns:title",_namespaceManager).InnerText,
38 | Version = root.SelectSingleNode("//ns:entry/m:properties/d:Version", _namespaceManager).InnerText,
39 | Updated = DateTimeOffset.Parse(root.SelectSingleNode("//ns:entry/ns:updated", _namespaceManager).InnerText),
40 | ReleaseNotes = root.SelectSingleNode("//ns:entry/m:properties/d:ReleaseNotes", _namespaceManager).InnerText,
41 | PackageUrl = root.SelectSingleNode("//ns:entry/ns:content", _namespaceManager).Attributes["src"].Value,
42 | PackageSize = long.Parse(root.SelectSingleNode("//ns:entry/m:properties/d:PackageSize", _namespaceManager).InnerText)
43 | };
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/src/Core/Util/CmdHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading;
4 |
5 | namespace CnSharp.Updater.Util
6 | {
7 | public class CmdHelper
8 | {
9 | public static bool Run(string fileName, string arguments, Action outputMessageHandler = null, Action errorMessageHandler = null)
10 | {
11 | var startInfo = new ProcessStartInfo
12 | {
13 | FileName = fileName,
14 | Arguments = arguments,
15 | RedirectStandardOutput = true,
16 | RedirectStandardError = true,
17 | UseShellExecute = false,
18 | CreateNoWindow = true
19 | };
20 |
21 | using (var process = new Process { StartInfo = startInfo })
22 | {
23 | using (var outputWaitHandle = new AutoResetEvent(false))
24 | using (var errorWaitHandle = new AutoResetEvent(false))
25 | {
26 | process.OutputDataReceived += (sender, e) => {
27 | if (e.Data == null)
28 | {
29 | outputWaitHandle.Set();
30 | }
31 | else
32 | {
33 | outputMessageHandler?.Invoke(e.Data + Environment.NewLine);
34 | }
35 | };
36 | process.ErrorDataReceived += (sender, e) =>
37 | {
38 | if (e.Data == null)
39 | {
40 | errorWaitHandle.Set();
41 | }
42 | else
43 | {
44 | errorMessageHandler?.Invoke(e.Data + Environment.NewLine);
45 | }
46 | };
47 |
48 | process.Start();
49 | process.BeginOutputReadLine();
50 | process.BeginErrorReadLine();
51 | process.WaitForExit();
52 | return process.ExitCode == 0;
53 | }
54 | }
55 | }
56 |
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/CLI/SharpUpdater.CLI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net8.0
5 | enable
6 | enable
7 | SharpUpdater.CLI
8 | CnSharp.Updater.CLI
9 | icon32x32.ico
10 | True
11 | su
12 | 1.0.0
13 | False
14 | False
15 | False
16 | CnSharp Studio
17 | CnSharp Studio
18 | Copyright © CnSharp Studio 2025
19 | SharpUpdater CLI to build/deploy packages.
20 | SharpUpdater.CLI
21 | Apache-2.0
22 | https://github.com/cnsharp/SharpUpdater
23 | initial version
24 | git
25 | https://github.com/cnsharp/SharpUpdater.git
26 | SharpUpdater-logo.png
27 | Winforms,WPF,Desktop,Updater,AutoUpdate
28 | README.md
29 |
30 |
31 |
32 | True
33 | \
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | True
47 | \
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/CLI/ProjectHolder.cs:
--------------------------------------------------------------------------------
1 | using CnSharp.Updater.Util;
2 |
3 | namespace CnSharp.Updater.CLI
4 | {
5 | public class ProjectHolder
6 | {
7 | static readonly List ProjectFileExtensions = [".csproj", ".vbproj"];
8 | static readonly List ProjectFilePatterns = ProjectFileExtensions.Select(e => "*" + e).ToList();
9 | private static readonly Dictionary ProjectFileDict = new();
10 | private static readonly Dictionary ProjectHelperDict = new();
11 |
12 | public static bool CheckExistsIfProject(string file)
13 | {
14 | if (ProjectFileExtensions.Any(file.EndsWith))
15 | {
16 | if (!File.Exists(file))
17 | {
18 | throw new FileNotFoundException($"File {file} does not exist.");
19 | }
20 | return true;
21 | }
22 | if (!Directory.Exists(file))
23 | {
24 | throw new DirectoryNotFoundException($"Directory {file} does not exist.");
25 | }
26 | return false;
27 | }
28 |
29 | public static string? GetProjectFile(string dir)
30 | {
31 | if (!ProjectFileDict.ContainsKey(dir))
32 | {
33 | foreach (var pattern in ProjectFilePatterns)
34 | {
35 | var projectFiles = Directory.GetFiles(dir, pattern, SearchOption.TopDirectoryOnly);
36 | if (projectFiles.Length > 0)
37 | {
38 | ProjectFileDict.Add(dir, projectFiles[0]);
39 | return projectFiles[0];
40 | }
41 | }
42 |
43 | ProjectFileDict.Add(dir, null);
44 | return null;
45 | }
46 | return ProjectFileDict[dir];
47 | }
48 |
49 | public static ProjectHelper GetProjectHelper(string projectFile)
50 | {
51 | if (!ProjectHelperDict.ContainsKey(projectFile))
52 | {
53 | var helper = new ProjectHelper(projectFile);
54 | ProjectHelperDict.Add(projectFile, helper);
55 | return helper;
56 | }
57 | return ProjectHelperDict[projectFile];
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/BaseForm.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Diagnostics;
3 | using System.Windows.Forms;
4 |
5 | namespace CnSharp.Windows.Updater
6 | {
7 | public class BaseForm : Form
8 | {
9 | #region Constants and Fields
10 |
11 | protected bool isEnableCloseButton;
12 |
13 | #endregion
14 |
15 | public BaseForm()
16 | {
17 | InitializeComponent();
18 | }
19 |
20 | #region Properties
21 |
22 | protected override CreateParams CreateParams
23 | {
24 | get
25 | {
26 | if (isEnableCloseButton)
27 | {
28 | CreateParams parameters = base.CreateParams;
29 | return parameters;
30 | }
31 | else
32 | {
33 | int CS_NOCLOSE = 0x200;
34 | CreateParams parameters = base.CreateParams;
35 | parameters.ClassStyle |= CS_NOCLOSE;
36 | return parameters;
37 | }
38 | }
39 | }
40 |
41 | #endregion
42 |
43 | #region Public Methods
44 |
45 | public DialogResult CheckProcessing(string exeName)
46 | {
47 | if (Process.GetProcessesByName(exeName).Length > 0)
48 | {
49 | DialogResult rs = MessageBox.Show(
50 | string.Format(Common.GetLocalText("CloseRunning"), exeName),
51 | Common.GetLocalText("Warning"),
52 | MessageBoxButtons.RetryCancel,
53 | MessageBoxIcon.Warning,
54 | MessageBoxDefaultButton.Button1);
55 | if (rs == DialogResult.Retry)
56 | {
57 | return CheckProcessing(exeName);
58 | }
59 | return rs;
60 | }
61 | return DialogResult.OK;
62 | }
63 |
64 | #endregion
65 |
66 |
67 | private void InitializeComponent()
68 | {
69 | ComponentResourceManager resources = new ComponentResourceManager(typeof(BaseForm));
70 | this.SuspendLayout();
71 | //
72 | // BaseForm
73 | //
74 | resources.ApplyResources(this, "$this");
75 | this.Name = "BaseForm";
76 | this.ResumeLayout(false);
77 |
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/src/Core/Util/Logger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace CnSharp.Updater.Util
5 | {
6 | public class Logger
7 | {
8 | ///
9 | /// 多线程锁
10 | ///
11 | private readonly object _locker = new object();
12 |
13 | private readonly string _logFolder;
14 |
15 | public Logger(string logFolder)
16 | {
17 | this._logFolder = logFolder;
18 | }
19 |
20 | public string LogFolder
21 | {
22 | get { return _logFolder; }
23 | }
24 |
25 |
26 | private void Write(string fileName, string type, string msg)
27 | {
28 | EnsureLogFolder();
29 | string file = Path.Combine(_logFolder, fileName);
30 | lock (_locker)
31 | {
32 | using (var sw = new StreamWriter(file, true))
33 | {
34 | sw.WriteLine("{0} - [{2}]: {1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), msg, type);
35 | }
36 | }
37 | }
38 |
39 | ///
40 | /// 写调试日志
41 | ///
42 | /// 日志消息
43 | public void WriteDebugLog(string msg)
44 | {
45 | Write("Debug.log", "DEBUG", msg);
46 | }
47 |
48 | ///
49 | /// 写异常日志
50 | ///
51 | /// 日志消息
52 | public void WriteExceptionLog(string msg)
53 | {
54 | Write("Exception.log", "Exception", msg);
55 | }
56 |
57 | ///
58 | /// 写异常日志
59 | ///
60 | /// 异常
61 | public void WriteExceptionLog(Exception exception)
62 | {
63 | Write("Exception.log", "Exception", exception.Message + Environment.NewLine + exception.StackTrace);
64 | }
65 |
66 | ///
67 | /// 写日志
68 | ///
69 | /// 日志消息
70 | public void WriteLog(string msg)
71 | {
72 | Write("Log.log", "Log", msg);
73 | }
74 |
75 | ///
76 | /// 创建日志存储文件夹
77 | ///
78 | private void EnsureLogFolder()
79 | {
80 | if (Directory.Exists(_logFolder) != true)
81 | Directory.CreateDirectory(_logFolder);
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/src/Core/Util/UriUtility.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.Packaging;
4 | using System.Linq;
5 |
6 | namespace CnSharp.Updater.Util
7 | {
8 | ///
9 | /// Copy from https://github.com/NuGet/NuGet2/blob/main/src/Core/Utility/UriUtility.cs
10 | ///
11 | public static class UriUtility
12 | {
13 | ///
14 | /// Converts a uri to a path. Only used for local paths.
15 | ///
16 | public static string GetPath(Uri uri)
17 | {
18 | string path = uri.OriginalString;
19 | if (path.StartsWith("/", StringComparison.Ordinal))
20 | {
21 | path = path.Substring(1);
22 | }
23 |
24 | // We need the unescaped uri string to ensure that all characters are valid for a path.
25 | // Change the direction of the slashes to match the filesystem.
26 | return Uri.UnescapeDataString(path.Replace('/', Path.DirectorySeparatorChar));
27 | }
28 |
29 | public static Uri CreatePartUri(string path)
30 | {
31 | // Only the segments between the path separators should be escaped
32 | var segments = path.Split(new[] { '/', Path.DirectorySeparatorChar }, StringSplitOptions.None)
33 | .Select(Uri.EscapeDataString);
34 | var escapedPath = string.Join("/", segments.ToArray());
35 | return PackUriHelper.CreatePartUri(new Uri(escapedPath, UriKind.Relative));
36 | }
37 |
38 | // Bug 2379: SettingsCredentialProvider does not work
39 | private static Uri CreateODataAgnosticUri(string uri)
40 | {
41 | if (uri.EndsWith("$metadata", StringComparison.OrdinalIgnoreCase))
42 | {
43 | uri = uri.Substring(0, uri.Length - 9).TrimEnd('/');
44 | }
45 | return new Uri(uri);
46 | }
47 |
48 | ///
49 | /// Determines if the scheme, server and path of two Uris are identical.
50 | ///
51 | public static bool UriEquals(Uri uri1, Uri uri2)
52 | {
53 | uri1 = CreateODataAgnosticUri(uri1.OriginalString.TrimEnd('/'));
54 | uri2 = CreateODataAgnosticUri(uri2.OriginalString.TrimEnd('/'));
55 |
56 | return Uri.Compare(uri1, uri2, UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) == 0;
57 | }
58 | }
59 |
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/ConnectionForm.Designer.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace CnSharp.Windows.Updater
4 | {
5 | partial class ConnectionForm
6 | {
7 | ///
8 | /// Required designer variable.
9 | ///
10 | private System.ComponentModel.IContainer components = null;
11 |
12 | ///
13 | /// Clean up any resources being used.
14 | ///
15 | /// true if managed resources should be disposed; otherwise, false.
16 | protected override void Dispose(bool disposing)
17 | {
18 | if (disposing && (components != null))
19 | {
20 | components.Dispose();
21 | }
22 | base.Dispose(disposing);
23 | }
24 |
25 | #region Windows Form Designer generated code
26 |
27 | ///
28 | /// Required method for Designer support - do not modify
29 | /// the contents of this method with the code editor.
30 | ///
31 | private void InitializeComponent()
32 | {
33 | ComponentResourceManager resources = new ComponentResourceManager(typeof(ConnectionForm));
34 | this.lblStatus = new System.Windows.Forms.Label();
35 | this.progressBar1 = new System.Windows.Forms.ProgressBar();
36 | this.SuspendLayout();
37 | //
38 | // lblStatus
39 | //
40 | resources.ApplyResources(this.lblStatus, "lblStatus");
41 | this.lblStatus.Name = "lblStatus";
42 | //
43 | // progressBar1
44 | //
45 | resources.ApplyResources(this.progressBar1, "progressBar1");
46 | this.progressBar1.Name = "progressBar1";
47 | this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
48 | //
49 | // ConnectionForm
50 | //
51 | resources.ApplyResources(this, "$this");
52 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
53 | this.Controls.Add(this.progressBar1);
54 | this.Controls.Add(this.lblStatus);
55 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
56 | this.MaximizeBox = false;
57 | this.MinimizeBox = false;
58 | this.Name = "ConnectionForm";
59 | this.Load += new System.EventHandler(this.ConnectionFormLoad);
60 | this.Shown += new System.EventHandler(this.ConnectionForm_Shown);
61 | this.ResumeLayout(false);
62 | this.PerformLayout();
63 |
64 | }
65 |
66 | #endregion
67 |
68 | private System.Windows.Forms.Label lblStatus;
69 | private System.Windows.Forms.ProgressBar progressBar1;
70 | }
71 | }
--------------------------------------------------------------------------------
/src/Core/Util/IgnoreFileParser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text.RegularExpressions;
6 |
7 | namespace CnSharp.Updater.Util
8 | {
9 | public class IgnoreFileParser
10 | {
11 | private readonly List _files = new List { "updater.exe", Manifest.ManifestFileName, ".pdb" };
12 | private readonly List _regex = new List();
13 |
14 | public IgnoreFileParser(string ignoreFile)
15 | {
16 | if (string.IsNullOrWhiteSpace(ignoreFile))
17 | return;
18 |
19 | if (!File.Exists(ignoreFile))
20 | throw new FileNotFoundException(".ignore file not found.", ignoreFile);
21 |
22 | using (var sr = new StreamReader(ignoreFile))
23 | {
24 | while (!sr.EndOfStream)
25 | {
26 | var line = sr.ReadLine();
27 | if (string.IsNullOrWhiteSpace(line))
28 | continue;
29 | if (WildCardRegex.IsMatch(line))
30 | {
31 | var regex = GetWildcardRegexString(line);
32 | _regex.Add(regex);
33 | }
34 | else
35 | {
36 | _files.Add(line);
37 | }
38 | }
39 | }
40 | }
41 |
42 | public bool IsExcluded(string dir)
43 | {
44 | if (_files.Any(f => dir.EndsWith(f, StringComparison.CurrentCultureIgnoreCase)))
45 | return true;
46 | if (_regex.Any(r => Regex.IsMatch(dir, r, RegexOptions.Compiled | RegexOptions.IgnoreCase)))
47 | return true;
48 | return false;
49 | }
50 |
51 | static readonly Regex WildCardRegex = new Regex("[.$^{\\[(|)*+?\\\\]");
52 |
53 | ///
54 | /// 将通配符字符串转换成等价的正则表达式
55 | /// 这可以用正则表达式来实现通配符匹配
56 | ///
57 | static string GetWildcardRegexString(string wildcardStr)
58 | {
59 |
60 | return WildCardRegex.Replace(wildcardStr,
61 | delegate (Match m)
62 | {
63 | switch (m.Value)
64 | {
65 |
66 | case "?":
67 | return ".?";
68 |
69 | case "*":
70 | return ".*";
71 |
72 | default:
73 | return "\\" + m.Value;
74 |
75 | }
76 | });
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Core/Util/XmlSerializerHelper.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Xml.Serialization;
3 |
4 | namespace CnSharp.Updater.Util
5 | {
6 | ///
7 | /// serialize/deserialize object by xml formatter
8 | ///
9 | public class XmlSerializerHelper
10 | {
11 | ///
12 | /// copy a instance
13 | ///
14 | ///
15 | ///
16 | ///
17 | public static T Copy(T t)
18 | {
19 | string xml = GetXmlStringFromObject(t);
20 | return LoadObjectFromXmlString(xml);
21 | }
22 |
23 | ///
24 | /// serialize object to xml string
25 | ///
26 | /// object
27 | /// XML string
28 | public static string GetXmlStringFromObject(T obj)
29 | {
30 | var serializer = new XmlSerializer(typeof(T));
31 | using (var writer = new StringWriter())
32 | {
33 | serializer.Serialize(writer, obj);
34 | return writer.ToString();
35 | }
36 | }
37 |
38 | ///
39 | /// Deserialize object from xml file
40 | ///
41 | ///
42 | /// xml file name
43 | ///
44 | public static T LoadObjectFromXml(string filename)
45 | {
46 | var serializer = new XmlSerializer(typeof(T));
47 | using (var reader = new StreamReader(filename))
48 | {
49 | return (T)serializer.Deserialize(reader);
50 | }
51 | }
52 |
53 | ///
54 | /// Deserialize object from xml string
55 | ///
56 | ///
57 | /// xml string
58 | ///
59 | public static T LoadObjectFromXmlString(string xml)
60 | {
61 | var serializer = new XmlSerializer(typeof(T));
62 | using (var reader = new StringReader(xml))
63 | {
64 | return (T)serializer.Deserialize(reader);
65 | }
66 | }
67 |
68 | public static void SerializeToXmlFile(T obj, string filePath)
69 | {
70 | var serializer = new XmlSerializer(typeof(T));
71 | using (var writer = new StreamWriter(filePath))
72 | {
73 | serializer.Serialize(writer, obj);
74 | }
75 | }
76 |
77 | }
78 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SharpUpdater
2 | A windows application automatic update solution,includes updater clients,update pack repository,CLI and VSIX packaging tool.
3 |
4 | ## Quick start
5 |
6 | ### Build your desktop application
7 | Take winforms for example.
8 |
9 | Fork this repo and clone it to your local machine.Then you can find the clients source code in the location [\src\Clients](src/Clients).
10 |
11 | The clients are not pulished as NuGet package so far, so you can customize the updater as you need.
12 |
13 | Build your updater.exe,publish the client in a single file.
14 |
15 | Create a new winforms project, and integrate the updater.exe by adding it to the main project and set its property 'Copy to Output Directory' as 'Copy Always'.
16 |
17 | Start the updater.exe in the main program.
18 | ```csharp
19 | static void Main(string[] args)
20 | {
21 | ApplicationConfiguration.Initialize();
22 |
23 | // Check if the application is running from the updater, if not, run the updater.
24 | // The updater.exe will pass the args to the application after updating to avoid checking again.
25 | if (args.Length == 0)
26 | {
27 | var updaterPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Updater.exe");
28 | if (File.Exists(updaterPath))
29 | {
30 | var processStartInfo = new ProcessStartInfo
31 | {
32 | FileName = "Updater.exe"
33 | };
34 | var proc = Process.Start(processStartInfo);
35 | proc?.WaitForExit();
36 | return;
37 | }
38 | }
39 |
40 | Application.Run(new MainForm());
41 | }
42 | ```
43 |
44 | ### Publish your application
45 | This solution focuses on On-premises deployment, so you can publish your application as a pure exe file or make a installer using Windows Application Packaging Project,
46 | Visual Studio Install Project,Inno Setup, etc.
47 |
48 | ### Create a update pack
49 | There are two approaches to create a update pack.
50 | 1. Use the [VSIX packaging tool](https://marketplace.visualstudio.com/items?itemName=CnSharpStudio.SharpUpdater) of SharpUpdater.
51 | 2. Use [SharpUpdater.CLI](https://www.nuget.org/packages/SharpUpdater.CLI).
52 | ```
53 | dotnet tool install --global SharpUpdater.CLI
54 |
55 | su pack -p C:\path\to\your.csproj -s http://YOUR_SERVER_HOST/sp -v 1.0.0
56 | ```
57 |
58 | ### Deploy the update pack
59 | First, you need to deploy a [SharpUpdater.Server](https://github.com/cnsharp/SharpUpdater.Server) to host the update packs.
60 | Then, you can deploy the update pack to the server by using the SharpUpdater.CLI or the VSIX.
61 | The command like below:
62 | ```
63 | su push -p C:\path\to\your\updatepack.sp -s http://YOUR_SERVER_HOST/sp -k YOUR_KEY
64 | ```
--------------------------------------------------------------------------------
/src/VSIX/source.extension.vsixmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SharpUpdater
6 | A wizard tool to build/deploy SharpUpdater packages. SharpUpdater is a full featured auto-update solution for desktop applications.
7 | https://github.com/cnsharp/SharpUpdater
8 | LICENSE.txt
9 | release_notes.txt
10 | SharpUpdater-logo.png
11 | SharpUpdater-VsTool-Dialog.png
12 | AutoUpdate,SharpUpdater,Winforms,WPF
13 |
14 |
15 |
18 |
19 | amd64
20 |
21 |
22 | amd64
23 |
24 |
27 |
28 | arm64
29 |
30 |
31 | arm64
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet4/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace CnSharp.Windows.Updater.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CnSharp.Windows.Updater.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace CnSharp.Windows.Updater.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CnSharp.Windows.Updater.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Core/Util/ProjectHelper.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Linq;
3 | using System.Xml.Linq;
4 |
5 | namespace CnSharp.Updater.Util
6 | {
7 | public class ProjectHelper
8 | {
9 | private readonly XDocument _doc;
10 | private readonly string _projectFile;
11 |
12 | public ProjectHelper(string projectFile)
13 | {
14 | _doc = XDocument.Load(projectFile);
15 | _projectFile = projectFile;
16 | }
17 |
18 | public string ProjectFile => _projectFile;
19 |
20 | public bool IsDotnetFramework
21 | {
22 | get
23 | {
24 | var ns = _doc.Root.GetDefaultNamespace();
25 | return _doc.Descendants(ns + "TargetFrameworkVersion").FirstOrDefault() != null;
26 | }
27 | }
28 |
29 | public string GetTargetFramework()
30 | {
31 | return _doc.Element("Project")
32 | ?.Element("PropertyGroup")
33 | ?.Element("TargetFramework")
34 | ?.Value;
35 | }
36 |
37 | public string GetAssemblyName()
38 | {
39 | return _doc.Element("Project")
40 | ?.Element("PropertyGroup")
41 | ?.Element("AssemblyName")
42 | ?.Value;
43 | }
44 |
45 | public string GetReleaseDir()
46 | {
47 | var dir = Path.GetDirectoryName(_projectFile);
48 | var path = Path.Combine(dir, "bin", "Release");
49 | var targetFramework = GetTargetFramework();
50 | if (!string.IsNullOrEmpty(targetFramework))
51 | path = Path.Combine(path, targetFramework);
52 | return path;
53 | }
54 |
55 | public string GetBinDir()
56 | {
57 | var dir = Path.GetDirectoryName(_projectFile);
58 | return Path.Combine(dir, "bin");
59 | }
60 |
61 | public string GetExeFileReleased()
62 | {
63 | var dir = GetReleaseDir();
64 | if (!Directory.Exists(dir))
65 | throw new DirectoryNotFoundException("Release directory not found.");
66 | string exe;
67 | var assemblyName = GetAssemblyName();
68 | if (assemblyName != null)
69 | {
70 | exe = Path.Combine(dir, assemblyName, ".exe");
71 | if (!File.Exists(exe))
72 | throw new FileNotFoundException("None of .exe file found in Release directory.");
73 | return exe;
74 | }
75 | var files = Directory.GetFiles(dir, "*.exe", SearchOption.TopDirectoryOnly);
76 | if (files.Length == 0)
77 | throw new FileNotFoundException("None of .exe file found in Release directory.");
78 | exe = files.Where(f => !Path.GetFileName(f).Equals(Constants.UpdaterFileName, System.StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
79 | if (exe == null)
80 | throw new FileNotFoundException("None of valid .exe file found in Release directory.");
81 | return exe;
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/VSIX/Util/SolutionDataCache.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using CnSharp.VisualStudio.Extensions;
6 | using EnvDTE;
7 |
8 | namespace CnSharp.VisualStudio.SharpUpdater.Util
9 | {
10 | public class SolutionDataCache : ConcurrentDictionary
11 | {
12 | private static SolutionDataCache instance;
13 | protected SolutionDataCache()
14 | {
15 |
16 | }
17 |
18 | public static SolutionDataCache Instance => instance ?? (instance = new SolutionDataCache());
19 |
20 | public SolutionProperties GetSolutionProperties(string solutionFile,int retryTimes = 3)
21 | {
22 | int i = 0;
23 | SolutionProperties sp;
24 | while (!TryGetValue(solutionFile,out sp))
25 | {
26 | System.Threading.Thread.Sleep(500);
27 | i++;
28 | if(i == retryTimes)
29 | {
30 | throw new ApplicationException("Load projects failed.");
31 | }
32 | }
33 | return sp;
34 | }
35 | }
36 |
37 | public class SolutionProperties
38 | {
39 | private List _projects;
40 |
41 | public List Projects
42 | {
43 | get { return _projects; }
44 | set
45 | {
46 | _projects = value;
47 | if (_projects != null)
48 | {
49 | ClassicProjects.AddRange(_projects?.Where(p => p.IsNetFrameworkProject()));
50 | SdkBasedProjects.AddRange(_projects?.Where(p => p.IsSdkBased()));
51 | }
52 | else
53 | {
54 | ClassicProjects.Clear();
55 | SdkBasedProjects.Clear();
56 | }
57 | }
58 | }
59 |
60 | public List ClassicProjects { get; private set; } = new List();
61 | public List SdkBasedProjects { get; private set; } = new List();
62 | public bool HasClassicProjects => ClassicProjects?.Any() == true;
63 | public bool HasSdkBasedProjects => SdkBasedProjects?.Any() == true;
64 |
65 | public void AddProject(Project project)
66 | {
67 | if(_projects == null) _projects = new List();
68 | _projects.Add(project);
69 | if (project.IsNetFrameworkProject())
70 | {
71 | ClassicProjects.Add(project);
72 | }
73 | else if (project.IsSdkBased())
74 | {
75 | SdkBasedProjects.Add(project);
76 | }
77 | }
78 |
79 | public void RemoveProject(Project project)
80 | {
81 | _projects.Remove(project);
82 | if (project.IsNetFrameworkProject())
83 | {
84 | ClassicProjects.Remove(project);
85 | }
86 | else if (project.IsSdkBased())
87 | {
88 | SdkBasedProjects.Remove(project);
89 | }
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/Core/Util/ManifestGatherer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 |
6 | namespace CnSharp.Updater.Util
7 | {
8 | public class ManifestGatherer
9 | {
10 | private readonly string _releaseDir;
11 | private readonly string _projectDir;
12 |
13 | public ManifestGatherer(string releaseDir, string projectDir)
14 | {
15 | _releaseDir = releaseDir;
16 | _projectDir = projectDir;
17 | }
18 |
19 | public List GatherFiles(bool deleteExclusions)
20 | {
21 | return GatherFilesInFolder(_releaseDir, deleteExclusions);
22 | }
23 |
24 | private List GatherFilesInFolder(string dir, bool deleteExclusions)
25 | {
26 | var list = new List();
27 |
28 | if (IsExcluded(dir))
29 | {
30 | if (deleteExclusions)
31 | {
32 | try
33 | {
34 | Directory.Delete(dir, true);
35 | }
36 | catch(Exception ex)
37 | {
38 | Console.WriteLine($"Delete directory {dir} failed: {ex.Message}");
39 | }
40 | }
41 | return list;
42 | }
43 |
44 | string[] files = Directory.GetFiles(dir);
45 | foreach (string file in files)
46 | {
47 | if (IsExcluded(file))
48 | {
49 | if (deleteExclusions)
50 | {
51 | try
52 | {
53 | File.Delete(file);
54 | }
55 | catch (Exception ex)
56 | {
57 | Console.WriteLine($"Delete file {file} failed: {ex.Message}");
58 | }
59 | }
60 | continue;
61 | }
62 | list.Add(new ReleaseFile
63 | {
64 | FileName = file.Substring(dir.Length),
65 | FileSize = new FileInfo(file).Length,
66 | Version = FileVersionInfo.GetVersionInfo(file).FileVersion ?? "-"
67 | });
68 | }
69 |
70 | string[] folders = Directory.GetDirectories(dir);
71 | foreach (string folder in folders)
72 | {
73 | list.AddRange(GatherFilesInFolder(folder, deleteExclusions).ToArray());
74 | }
75 | return list;
76 | }
77 |
78 | private IgnoreFileParser _ignoreFileParser;
79 |
80 | private void GetIgnoreFileParser()
81 | {
82 | if (_ignoreFileParser != null)
83 | return;
84 | var ignoreFile = Path.Combine(_projectDir, Constants.IgnoreFileName);
85 | _ignoreFileParser = new IgnoreFileParser(File.Exists(ignoreFile) ? ignoreFile : null);
86 | }
87 |
88 | protected virtual bool IsExcluded(string file)
89 | {
90 | GetIgnoreFileParser();
91 | return _ignoreFileParser.IsExcluded(file);
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/Installer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.IO;
4 | using System.Net.Http;
5 | using System.Threading.Tasks;
6 | using CnSharp.Updater.Util;
7 |
8 | namespace CnSharp.Windows.Updater
9 | {
10 | public class Installer
11 | {
12 | private readonly string _packagePath;
13 | private readonly string _appDir;
14 | private readonly string[] _ignoreFiles;
15 | private string _tempDir;
16 | private string _tempFile;
17 |
18 | public event ProgressChangedEventHandler DownloadProgressChanged;
19 | public event AsyncCompletedEventHandler DownloadCompleted;
20 |
21 | public Installer(string packagePath, string appDir, string[] ignoreFiles)
22 | {
23 | _packagePath = packagePath;
24 | _appDir = appDir;
25 | _ignoreFiles = ignoreFiles;
26 | }
27 |
28 | public async Task InstallAsync()
29 | {
30 |
31 | var client = new HttpClient();
32 | _tempDir = Path.Combine(Path.GetTempPath(), Common.AppName);
33 | _tempFile = Path.Combine(_tempDir, Guid.NewGuid() + ".zip");
34 |
35 | var response = await client.GetAsync(_packagePath, HttpCompletionOption.ResponseHeadersRead);
36 | response.EnsureSuccessStatusCode();
37 |
38 | var totalBytes = response.Content.Headers.ContentLength ?? -1L;
39 | var canReportProgress = totalBytes != -1;
40 |
41 | using (var stream = await response.Content.ReadAsStreamAsync())
42 | using (var fileStream = new FileStream(_tempFile, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
43 | {
44 | var buffer = new byte[8192];
45 | long totalRead = 0;
46 | int bytesRead;
47 | while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) != 0)
48 | {
49 | await fileStream.WriteAsync(buffer, 0, bytesRead);
50 | totalRead += bytesRead;
51 | if (canReportProgress)
52 | {
53 | DownloadProgressChanged?.Invoke(this, new ProgressChangedEventArgs((int)(totalRead * 100 / totalBytes), null));
54 | }
55 | }
56 | }
57 |
58 | Unzip();
59 | DownloadCompleted.Invoke(this, new AsyncCompletedEventArgs(null, false, null));
60 |
61 | }
62 |
63 | private void Unzip()
64 | {
65 | var dir = Path.GetDirectoryName(_tempFile);
66 | dir += "\\" + Guid.NewGuid();
67 | ZipHelper.Unzip(_tempFile, dir, true);
68 | ClearIgnoreFiles(dir, _ignoreFiles);
69 | FileUtil.CopyFiles(dir, _appDir);
70 | Directory.Delete(dir, true);
71 | }
72 |
73 | private void ClearIgnoreFiles(string dir, string[] ignoreFiles)
74 | {
75 | if (ignoreFiles == null) return;
76 | foreach (var ignoreFile in ignoreFiles)
77 | {
78 | var delFile = Path.Combine(dir, ignoreFile);
79 | if (File.Exists(delFile))
80 | File.Delete(delFile);
81 | }
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/src/VSIX/Util/ManifestGatherer.cs:
--------------------------------------------------------------------------------
1 | using CnSharp.Updater.Util;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace CnSharp.VisualStudio.SharpUpdater.Util
6 | {
7 | public class ManifestGatherer
8 | {
9 |
10 | private int _rootLength;
11 | private readonly string _dir;
12 | private string _projectDir;
13 | private readonly NuPackSettings _settings;
14 |
15 | public ManifestGatherer(string dir, string projectDir, NuPackSettings settings)
16 | {
17 | _rootLength = dir.Length;
18 | _dir = dir;
19 | _projectDir = projectDir;
20 | _settings = settings;
21 | }
22 |
23 | public List GatherFiles()
24 | {
25 |
26 | return GatherFilesInFolder(_dir, true,_settings.UnselectedFolders,_settings.UnselectedFiles);
27 | }
28 |
29 | private List GatherFilesInFolder(string dir, bool isFirst,IList unselectedFolders,IList unselectedFiles )
30 | {
31 | var list = new List();
32 |
33 | if (IsExcluded(dir))
34 | return list;
35 |
36 | string dirShortName = dir.Substring(_rootLength);
37 | bool folderUnselected = unselectedFolders?.Contains(dirShortName) ?? false;
38 | if (!isFirst)
39 | {
40 | var folderItem = new FileListItem { Dir = dir, Selected = !folderUnselected };
41 | list.Add(folderItem);
42 | }
43 |
44 | string[] files = Directory.GetFiles(dir);
45 | foreach (string file in files)
46 | {
47 | if (IsExcluded(file))
48 | continue;
49 | string shortName = dir.Substring(_rootLength);
50 | bool selected = !folderUnselected &&
51 | (unselectedFiles == null || !unselectedFiles.Contains(shortName))
52 | && !IsFileUnselected(file);
53 | list.Add(new FileListItem
54 | {
55 | Dir = file,
56 | IsFile = true,
57 | Selected = selected
58 | });
59 | }
60 |
61 | string[] folders = Directory.GetDirectories(dir);
62 | foreach (string folder in folders)
63 | {
64 | list.AddRange(GatherFilesInFolder(folder, false,unselectedFolders,unselectedFiles).ToArray());
65 | }
66 | return list;
67 | }
68 |
69 | private IgnoreFileParser _filter;
70 |
71 | private void GetManifestFilter()
72 | {
73 | if (_filter != null)
74 | return;
75 | var ignoreFile = Path.Combine(_projectDir,Common.IgnoreFileName);
76 | _filter = new IgnoreFileParser(File.Exists(ignoreFile) ? ignoreFile : null);
77 | }
78 |
79 | protected virtual bool IsExcluded(string file)
80 | {
81 | GetManifestFilter();
82 | return _filter.IsExcluded(file);
83 | }
84 |
85 | protected virtual bool IsFileUnselected(string file)
86 | {
87 | var ext = Path.GetExtension(file).ToLower();
88 | return (ext == ".xml" && File.Exists(file.Substring(0, file.Length - 4) + ".dll"));
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/src/Core/Util/VersionUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.RegularExpressions;
3 |
4 | namespace CnSharp.Updater.Util
5 | {
6 | public static class VersionUtil
7 | {
8 |
9 | public const string SemanticVersionPattern =
10 | "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$";
11 |
12 | private static readonly Regex SemanticVersionRegex = new Regex(SemanticVersionPattern, RegexOptions.Compiled);
13 | public static int CompareVersion(this string version, string otherVersion)
14 | {
15 | return CompareVersions(version, otherVersion);
16 | }
17 |
18 | public static int CompareVersions(string version1, string version2)
19 | {
20 | // Split version number and pre-release identifier
21 | var v1Parts = version1.Split('-');
22 | var v2Parts = version2.Split('-');
23 |
24 | // Parse the main version number
25 | Version v1 = Version.Parse(v1Parts[0]);
26 | Version v2 = Version.Parse(v2Parts[0]);
27 |
28 | // Compare the main version numbers
29 | int mainComparison = v1.CompareTo(v2);
30 | if (mainComparison != 0)
31 | return mainComparison;
32 |
33 | // If the main version numbers are the same, compare the pre-release identifiers
34 | string preRelease1 = v1Parts.Length > 1 ? v1Parts[1] : string.Empty;
35 | string preRelease2 = v2Parts.Length > 1 ? v2Parts[1] : string.Empty;
36 |
37 | return ComparePreRelease(preRelease1, preRelease2);
38 | }
39 |
40 | private static int ComparePreRelease(string preRelease1, string preRelease2)
41 | {
42 | // If neither has a pre-release identifier, they are equal
43 | if (string.IsNullOrEmpty(preRelease1) && string.IsNullOrEmpty(preRelease2))
44 | return 0;
45 |
46 | // If one version has a pre-release identifier, it is considered smaller
47 | if (string.IsNullOrEmpty(preRelease1))
48 | return 1; // version1 is a stable release, greater than version2
49 | if (string.IsNullOrEmpty(preRelease2))
50 | return -1; // version2 is a stable release, greater than version1
51 |
52 | // Compare pre-release identifiers
53 | return string.Compare(preRelease1, preRelease2, StringComparison.OrdinalIgnoreCase);
54 | }
55 |
56 | public static string ToSemanticVersion(this string versionNumber)
57 | {
58 | if (versionNumber.IsSemanticVersion())
59 | return versionNumber;
60 | // Split the version number
61 | string[] parts = versionNumber.Split('.');
62 |
63 | // Ensure at least MAJOR.MINOR.PATCH
64 | if (parts.Length < 3)
65 | {
66 | throw new ArgumentException("Version number must have at least 3 parts (MAJOR.MINOR.PATCH).");
67 | }
68 |
69 | // Extract MAJOR, MINOR, PATCH
70 | string major = parts[0];
71 | string minor = parts[1];
72 | string patch = parts[2];
73 |
74 | // Build the Semantic Version
75 | return $"{major}.{minor}.{patch}";
76 | }
77 |
78 | public static bool IsSemanticVersion(this string version)
79 | {
80 | return SemanticVersionRegex.IsMatch(version);
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/VSIX/SharpUpdaterPackage.vsct:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
12 |
13 |
14 |
17 |
20 |
21 |
22 |
23 |
24 |
26 |
27 |
36 |
45 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/src/VSIX/Resource.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace CnSharp.VisualStudio.SharpUpdater {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resource {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resource() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CnSharp.VisualStudio.SharpUpdater.Resource", typeof(Resource).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized resource of type System.Drawing.Bitmap.
65 | ///
66 | internal static System.Drawing.Bitmap folder {
67 | get {
68 | object obj = ResourceManager.GetObject("folder", resourceCulture);
69 | return ((System.Drawing.Bitmap)(obj));
70 | }
71 | }
72 |
73 | ///
74 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
75 | ///
76 | internal static System.Drawing.Icon logo {
77 | get {
78 | object obj = ResourceManager.GetObject("logo", resourceCulture);
79 | return ((System.Drawing.Icon)(obj));
80 | }
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/Core/Util/Downloader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.IO;
4 | using System.Net;
5 |
6 | namespace CnSharp.Updater.Util
7 | {
8 | public class Downloader : BackgroundWorker
9 | {
10 | private readonly Manifest _remoteManifest;
11 | private readonly string _tempDir;
12 | private readonly long _totalSize;
13 | private readonly ReleaseFile[] _diff;
14 |
15 | public long TotalBytes
16 | {
17 | get { return _totalSize; }
18 | }
19 |
20 | public long ReceivedBytes { get; private set; }
21 |
22 |
23 | public Downloader(Manifest localManifest, Manifest remoteManifest, string tempDir)
24 | {
25 | _remoteManifest = remoteManifest;
26 | _tempDir = tempDir;
27 | _diff = localManifest.GetDifferences(_remoteManifest, out _totalSize);
28 | if (!Directory.Exists(_tempDir))
29 | Directory.CreateDirectory(_tempDir);
30 | }
31 |
32 |
33 | public Downloader(Manifest remoteManifest,ReleaseFile[] files,long totalSize, string tempDir)
34 | {
35 | _remoteManifest = remoteManifest;
36 | _tempDir = tempDir;
37 | _diff = files;
38 | _totalSize = totalSize;
39 | if (!Directory.Exists(_tempDir))
40 | Directory.CreateDirectory(_tempDir);
41 | }
42 |
43 | public new void RunWorkerAsync()
44 | {
45 | if (_diff == null || _diff.Length == 0)
46 | return;
47 | var di = new DownloadInfo
48 | {
49 | WebRoot = _remoteManifest.VersionRoot,//todo
50 | TempDir = _tempDir,
51 | Files = _diff
52 | };
53 |
54 | base.RunWorkerAsync(di);
55 | }
56 |
57 |
58 |
59 | //public event DownloadProgressChangedEventHandler DownloadProgressChanged;
60 |
61 |
62 | protected override void OnDoWork(DoWorkEventArgs e)
63 | {
64 | base.OnDoWork(e);
65 | var info = e.Argument as DownloadInfo;
66 | if (info == null)
67 | return;
68 | foreach (var file in info.Files)
69 | {
70 | try
71 | {
72 | using (var wc = new WebClient())
73 | {
74 | var fileName = Path.Combine(info.TempDir, file.FileName);
75 | var dir = Path.GetDirectoryName(fileName);
76 | if (!Directory.Exists(dir))
77 | Directory.CreateDirectory(dir);
78 |
79 | wc.DownloadFile(new Uri(info.WebRoot + "/" + file.FileName),
80 | fileName);
81 | if (base.WorkerReportsProgress)
82 | {
83 | SendProgress(file.FileSize);
84 | }
85 | }
86 | }
87 | catch
88 | {
89 | e.Result = file.FileName;
90 | throw;
91 | }
92 | }
93 | }
94 |
95 | private void SendProgress(long bytes)
96 | {
97 | ReceivedBytes += bytes;
98 |
99 | var progress = Math.Min(ReceivedBytes, _totalSize);
100 | var percentage = (int)((Math.Round(Convert.ToDecimal(progress) / Convert.ToDecimal(_totalSize) * 1.0M, 2)) * 100);
101 | base.ReportProgress(percentage);
102 | }
103 |
104 |
105 |
106 | public class DownloadInfo
107 | {
108 | public string WebRoot { get; set; }
109 | public string TempDir { get; set; }
110 | public ReleaseFile[] Files { get; set; }
111 | }
112 | }
113 | }
--------------------------------------------------------------------------------
/src/Core/Packaging/PackageBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.IO.Packaging;
5 | using CnSharp.Updater.Util;
6 |
7 | namespace CnSharp.Updater.Packaging
8 | {
9 | public class PackageBuilder
10 | {
11 | private readonly Manifest _manifest;
12 | private readonly string _baseDir;
13 | private const string DefaultContentType = "application/octet";
14 |
15 | public PackageBuilder(Manifest manifest,string baseDir)
16 | {
17 | _manifest = manifest;
18 | _baseDir = baseDir;
19 | }
20 |
21 | public Package CreatePackage(string targetPath)
22 | {
23 | using (Package package = Package.Open(targetPath, FileMode.Create))
24 | {
25 | WriteManifest(package,1);
26 | WriteFiles(package);
27 |
28 | package.PackageProperties.Creator = _manifest.Owner;
29 | package.PackageProperties.Description = _manifest.Description;
30 | package.PackageProperties.Identifier = _manifest.Id;
31 | package.PackageProperties.Version = _manifest.Version;
32 | package.PackageProperties.Language = _manifest.Language;
33 | package.PackageProperties.Keywords = _manifest.Tags;
34 | package.PackageProperties.Title = _manifest.AppName;
35 | package.PackageProperties.LastModifiedBy = CreatorInfo();
36 |
37 | return package;
38 | }
39 | }
40 |
41 |
42 | private static string CreatorInfo()
43 | {
44 | List creatorInfo = new List();
45 | var assembly = typeof(PackageBuilder).Assembly;
46 | creatorInfo.Add(assembly.FullName);
47 | creatorInfo.Add(Environment.OSVersion.ToString());
48 |
49 | return string.Join(";", creatorInfo.ToArray());
50 | }
51 |
52 |
53 |
54 | private void WriteManifest(Package package, int minimumManifestVersion)
55 | {
56 | Uri uri = UriUtility.CreatePartUri(Manifest.ManifestFileName);
57 |
58 | // Create the manifest relationship
59 | package.CreateRelationship(uri, TargetMode.Internal, Constants.PackageRelationshipNamespace + Constants.ManifestRelationType);
60 |
61 | // Create the part
62 | PackagePart packagePart = package.CreatePart(uri, DefaultContentType, CompressionOption.Maximum);
63 |
64 | using (Stream stream = packagePart.GetStream())
65 | {
66 | _manifest.Save(stream, minimumManifestVersion);
67 | }
68 | }
69 |
70 | private void WriteFiles(Package package)
71 | {
72 | var files = Directory.GetFiles(_baseDir, @"*.*", SearchOption.AllDirectories);
73 | var dirLen = _baseDir.Length;
74 | foreach (var each in files)
75 | {
76 |
77 | var relPath = each.Substring(dirLen).Replace("\\", "/");
78 | if (relPath.StartsWith("/"))
79 | relPath = relPath.TrimStart('/');
80 | string destFilename = "/" + relPath;
81 | Uri uri = PackUriHelper.CreatePartUri(new Uri(destFilename, UriKind.Relative));
82 | if (package.PartExists(uri))
83 | {
84 | package.DeletePart(uri);
85 | }
86 | PackagePart part = package.CreatePart(uri, "", CompressionOption.Normal);
87 | using (FileStream fileStream = new FileStream(each, FileMode.Open, FileAccess.Read))
88 | {
89 | using (Stream dest = part.GetStream())
90 | {
91 | fileStream.CopyTo(dest);
92 | }
93 | }
94 | }
95 | }
96 | }
97 |
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/UpdateForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using System.Windows.Forms;
4 | using CnSharp.Updater;
5 | using CnSharp.Updater.Packaging;
6 | using CnSharp.Updater.Util;
7 |
8 | namespace CnSharp.Windows.Updater
9 | {
10 | public partial class UpdateForm : BaseForm
11 | {
12 | #region Constants and Fields
13 |
14 | private static readonly string FinishText = Common.GetLocalText("Completed");
15 | private static readonly string RetryText = Common.GetLocalText("Retry");
16 | private readonly PackageFeedSummary _packageFeedSummary;
17 | private Manifest _localManifest;
18 | private bool _downloaded;
19 | private readonly Installer _installer;
20 |
21 | #endregion
22 |
23 | #region Constructors and Destructors
24 |
25 | public UpdateForm(Manifest localManifest, PackageFeedSummary packageFeedSummary)
26 | {
27 | InitializeComponent();
28 |
29 | btnUpgrade.Focus();
30 |
31 | _localManifest = localManifest;
32 | _packageFeedSummary = packageFeedSummary;
33 |
34 | _installer = new Installer(packageFeedSummary.PackageUrl, Application.StartupPath, Common.IgnoreFiles);
35 | _installer.DownloadProgressChanged += (sender, e) => { progressBar.Value = e.ProgressPercentage; };
36 | _installer.DownloadCompleted += (sender, args) => { Finish(); };
37 | }
38 |
39 |
40 | private void Finish()
41 | {
42 | btnUpgrade.Enabled = true;
43 | btnUpgrade.Text = FinishText;
44 | _downloaded = true;
45 |
46 | Application.Exit();
47 | Common.Start(_localManifest);
48 | }
49 |
50 | #endregion
51 |
52 | #region Properties
53 |
54 | private bool OptionalUpdate
55 | {
56 | set => isEnableCloseButton = btnUpgrade.Enabled = btnCancel.Enabled = value;
57 | }
58 |
59 | #endregion
60 |
61 | #region Methods
62 |
63 | private async Task DoUpgrade()
64 | {
65 | _downloaded = false;
66 | progressBar.Value = 0;
67 | await _installer.InstallAsync();
68 | }
69 |
70 | private void FormUpdate_FormClosing(object sender, FormClosingEventArgs e)
71 | {
72 | }
73 |
74 | private async void FormUpdate_Load(object sender, EventArgs e)
75 | {
76 | lblSize.Text = string.Empty;
77 | await Init();
78 | }
79 |
80 | private async Task Init()
81 | {
82 | OptionalUpdate = _localManifest.CompareTo(_packageFeedSummary.Version) >= 0;
83 | lblTitle.Text = string.Format(
84 | lblTitle.Text,
85 | _localManifest.Version,
86 | _packageFeedSummary.Version,
87 | _packageFeedSummary.Updated);
88 |
89 | boxDes.Text = _packageFeedSummary.ReleaseNotes;
90 |
91 | progressBar.Maximum = 100;
92 | if (!btnUpgrade.Enabled)
93 | {
94 | await DoUpgrade();
95 | }
96 | }
97 |
98 | private void btnCancel_Click(object sender, EventArgs e)
99 | {
100 | //Directory.Delete(tempPath, true);
101 | Application.Exit();
102 | Common.Start(_localManifest);
103 | }
104 |
105 | private async void btnUpgrade_Click(object sender, EventArgs e)
106 | {
107 | btnUpgrade.Enabled = false;
108 | btnCancel.Enabled = false;
109 | if (!_downloaded)
110 | {
111 | await DoUpgrade();
112 | }
113 | else
114 | {
115 | Application.Exit();
116 | Common.Start(_localManifest);
117 | }
118 | }
119 |
120 | #endregion
121 | }
122 | }
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/ConnectionForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Windows.Forms;
6 | using System.Xml;
7 | using CnSharp.Updater;
8 | using CnSharp.Updater.Packaging;
9 | using CnSharp.Updater.Util;
10 |
11 | namespace CnSharp.Windows.Updater
12 | {
13 | public partial class ConnectionForm : BaseForm
14 | {
15 | #region Constants and Fields
16 |
17 | private readonly BackgroundWorker _worker;
18 |
19 | #endregion
20 |
21 | #region Constructors and Destructors
22 |
23 | public ConnectionForm()
24 | {
25 | InitializeComponent();
26 |
27 | var manifestFileName = Path.Combine(Application.StartupPath, Manifest.ManifestFileName);
28 | if (!File.Exists(manifestFileName))
29 | {
30 | return;
31 | }
32 |
33 | LocalManifest = FileUtil.ReadManifest(manifestFileName);
34 | _worker = new BackgroundWorker();
35 | _worker.DoWork += DoWork;
36 | _worker.RunWorkerCompleted += WorkerOnRunWorkerCompleted;
37 | }
38 |
39 | private void DoWork(object sender, DoWorkEventArgs e)
40 | {
41 | //var feedUrl = $"{LocalManifest.ReleaseUrl}/GetUpdates()?packageIds='{LocalManifest.Id}'&versions='{LocalManifest.Version}'&includePrerelease=false";
42 | var feedUrl = $"{LocalManifest.ReleaseUrl}/Search()?$filter=IsLatestVersion&searchTerm='{LocalManifest.Id}'&includePrerelease=false";
43 | var doc = new XmlDocument();
44 | doc.Load(feedUrl);
45 | var feedResolver = new PackageFeedResolver(doc);
46 | var sum = feedResolver.GetSummary();
47 | e.Result = sum;
48 | }
49 |
50 | private void WorkerOnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
51 | {
52 | if (e.Error != null)
53 | {
54 | Application.Exit();
55 | Common.Start(LocalManifest, e.Error);
56 | return;
57 | }
58 |
59 | var sum = e.Result as PackageFeedSummary;
60 |
61 | if(!sum.IsNew(LocalManifest))
62 | {
63 | Application.Exit();
64 | Common.Start(LocalManifest);
65 | return;
66 | }
67 | //Hide();
68 |
69 | if (CheckProcessing() != DialogResult.OK)
70 | {
71 | Application.Exit();
72 | return;
73 | }
74 | var form = new UpdateForm(LocalManifest, sum);
75 | form.ShowDialog();
76 | }
77 |
78 | #endregion
79 |
80 | public Manifest LocalManifest { get; }
81 |
82 | #region Methods
83 |
84 | private DialogResult CheckProcessing()
85 | {
86 | string exeName = LocalManifest.EntryPoint.Substring(0, LocalManifest.EntryPoint.Length - 4);
87 | if (Process.GetProcessesByName(exeName).Length > 0)
88 | {
89 | DialogResult rs = MessageBox.Show(
90 | string.Format(Common.GetLocalText("CloseRunning"), exeName),
91 | Common.GetLocalText("Warning"),
92 | MessageBoxButtons.RetryCancel,
93 | MessageBoxIcon.Warning,
94 | MessageBoxDefaultButton.Button1);
95 | if (rs == DialogResult.Retry)
96 | {
97 | return CheckProcessing();
98 | }
99 | return rs;
100 | }
101 | return DialogResult.OK;
102 | }
103 |
104 | private void ConnectionFormLoad(object sender, EventArgs e)
105 | {
106 | _worker.RunWorkerAsync();
107 | }
108 |
109 | #endregion
110 |
111 | private void ConnectionForm_Shown(object sender, EventArgs e)
112 | {
113 |
114 | }
115 |
116 | }
117 | }
--------------------------------------------------------------------------------
/SharpUpdater.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.12.35707.178
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpUpdater.Core", "src\Core\SharpUpdater.Core.csproj", "{1599F1D6-AA67-43AB-B544-0B73720564A5}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "clients", "clients", "{9B68B470-2460-43EF-9956-EB1B2C718363}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VSIX", "VSIX", "{6E2DEA9D-B2A7-46D7-8D33-9EE8E9013D95}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinForms-DotNet8", "src\Clients\WinForms-DotNet8\WinForms-DotNet8.csproj", "{B1EDD1A8-5AA7-4E12-89B7-97D63EF78555}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CLI", "CLI", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinForms-DotNet4", "src\Clients\WinForms-DotNet4\WinForms-DotNet4.csproj", "{E27DF114-21A2-4EB5-ADE9-BF6559950B8E}"
17 | EndProject
18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpUpdater.CLI", "src\CLI\SharpUpdater.CLI.csproj", "{798C7344-36E3-4DB8-510E-96A2449418DA}"
19 | EndProject
20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpUpdater.VsTool", "src\VSIX\SharpUpdater.VsTool.csproj", "{B322241D-ACFC-48C2-AF34-849E5DF8401D}"
21 | EndProject
22 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{5108B42C-B72C-4F01-9857-8D9862AD6000}"
23 | EndProject
24 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Solution Items", "_Solution Items", "{7D804308-3112-479E-B82B-C411B334D08F}"
25 | ProjectSection(SolutionItems) = preProject
26 | .gitignore = .gitignore
27 | LICENSE = LICENSE
28 | README.md = README.md
29 | EndProjectSection
30 | EndProject
31 | Global
32 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
33 | Debug|Any CPU = Debug|Any CPU
34 | Release|Any CPU = Release|Any CPU
35 | EndGlobalSection
36 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
37 | {1599F1D6-AA67-43AB-B544-0B73720564A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 | {1599F1D6-AA67-43AB-B544-0B73720564A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 | {1599F1D6-AA67-43AB-B544-0B73720564A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
40 | {1599F1D6-AA67-43AB-B544-0B73720564A5}.Release|Any CPU.Build.0 = Release|Any CPU
41 | {B1EDD1A8-5AA7-4E12-89B7-97D63EF78555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42 | {B1EDD1A8-5AA7-4E12-89B7-97D63EF78555}.Debug|Any CPU.Build.0 = Debug|Any CPU
43 | {B1EDD1A8-5AA7-4E12-89B7-97D63EF78555}.Release|Any CPU.ActiveCfg = Release|Any CPU
44 | {B1EDD1A8-5AA7-4E12-89B7-97D63EF78555}.Release|Any CPU.Build.0 = Release|Any CPU
45 | {E27DF114-21A2-4EB5-ADE9-BF6559950B8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46 | {E27DF114-21A2-4EB5-ADE9-BF6559950B8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
47 | {E27DF114-21A2-4EB5-ADE9-BF6559950B8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
48 | {E27DF114-21A2-4EB5-ADE9-BF6559950B8E}.Release|Any CPU.Build.0 = Release|Any CPU
49 | {798C7344-36E3-4DB8-510E-96A2449418DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 | {798C7344-36E3-4DB8-510E-96A2449418DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 | {798C7344-36E3-4DB8-510E-96A2449418DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
52 | {798C7344-36E3-4DB8-510E-96A2449418DA}.Release|Any CPU.Build.0 = Release|Any CPU
53 | {B322241D-ACFC-48C2-AF34-849E5DF8401D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54 | {B322241D-ACFC-48C2-AF34-849E5DF8401D}.Debug|Any CPU.Build.0 = Debug|Any CPU
55 | {B322241D-ACFC-48C2-AF34-849E5DF8401D}.Release|Any CPU.ActiveCfg = Release|Any CPU
56 | {B322241D-ACFC-48C2-AF34-849E5DF8401D}.Release|Any CPU.Build.0 = Release|Any CPU
57 | EndGlobalSection
58 | GlobalSection(SolutionProperties) = preSolution
59 | HideSolutionNode = FALSE
60 | EndGlobalSection
61 | GlobalSection(NestedProjects) = preSolution
62 | {1599F1D6-AA67-43AB-B544-0B73720564A5} = {5108B42C-B72C-4F01-9857-8D9862AD6000}
63 | {B1EDD1A8-5AA7-4E12-89B7-97D63EF78555} = {9B68B470-2460-43EF-9956-EB1B2C718363}
64 | {E27DF114-21A2-4EB5-ADE9-BF6559950B8E} = {9B68B470-2460-43EF-9956-EB1B2C718363}
65 | {798C7344-36E3-4DB8-510E-96A2449418DA} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
66 | {B322241D-ACFC-48C2-AF34-849E5DF8401D} = {6E2DEA9D-B2A7-46D7-8D33-9EE8E9013D95}
67 | EndGlobalSection
68 | GlobalSection(ExtensibilityGlobals) = postSolution
69 | SolutionGuid = {27D99C64-A781-4606-9D8C-0633934CDEF9}
70 | EndGlobalSection
71 | EndGlobal
72 |
--------------------------------------------------------------------------------
/src/VSIX/Commands/AddIgnoreFileCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.Design;
3 | using System.IO;
4 | using System.Text;
5 | using CnSharp.Updater;
6 | using CnSharp.VisualStudio.Extensions;
7 | using Microsoft.VisualStudio.Shell;
8 | using Task = System.Threading.Tasks.Task;
9 |
10 | namespace CnSharp.VisualStudio.SharpUpdater.Commands
11 | {
12 | ///
13 | /// Command handler
14 | ///
15 | internal sealed class AddIgnoreFileCommand
16 | {
17 | ///
18 | /// Command ID.
19 | ///
20 | public const int CommandId = PackageIds.AddIgnoreFileCommandId;
21 |
22 | ///
23 | /// Command menu group (command set GUID).
24 | ///
25 | public static readonly Guid CommandSet = PackageGuids.guidProjectAddCmdSet;
26 |
27 | ///
28 | /// VS Package that provides this command, not null.
29 | ///
30 | private readonly AsyncPackage package;
31 |
32 | ///
33 | /// Initializes a new instance of the class.
34 | /// Adds our command handlers for menu (commands must exist in the command table file)
35 | ///
36 | /// Owner package, not null.
37 | /// Command service to add command to, not null.
38 | private AddIgnoreFileCommand(AsyncPackage package, OleMenuCommandService commandService)
39 | {
40 | this.package = package ?? throw new ArgumentNullException(nameof(package));
41 | commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));
42 |
43 | var menuCommandID = new CommandID(CommandSet, CommandId);
44 | var menuItem = new MenuCommand(this.Execute, menuCommandID);
45 | commandService.AddCommand(menuItem);
46 | }
47 |
48 | ///
49 | /// Gets the instance of the command.
50 | ///
51 | public static AddIgnoreFileCommand Instance
52 | {
53 | get;
54 | private set;
55 | }
56 |
57 | ///
58 | /// Gets the service provider from the owner package.
59 | ///
60 | private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider
61 | {
62 | get
63 | {
64 | return this.package;
65 | }
66 | }
67 |
68 | ///
69 | /// Initializes the singleton instance of the command.
70 | ///
71 | /// Owner package, not null.
72 | public static async Task InitializeAsync(AsyncPackage package)
73 | {
74 | // Switch to the main thread - the call to AddCommand in AddIgnoreFileCommand's constructor requires
75 | // the UI thread.
76 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
77 |
78 | OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
79 | Instance = new AddIgnoreFileCommand(package, commandService);
80 | }
81 |
82 | ///
83 | /// This function is the callback used to execute the command when the menu item is clicked.
84 | /// See the constructor to see how the menu item is associated with this function using
85 | /// OleMenuCommandService service and MenuCommand class.
86 | ///
87 | /// Event sender.
88 | /// Event args.
89 | private void Execute(object sender, EventArgs e)
90 | {
91 | ThreadHelper.ThrowIfNotOnUIThread();
92 |
93 | var dte = Host.Instance.Dte2;
94 | var project = dte.GetActiveProject();
95 | var file = Path.Combine(project.GetDirectory(), Common.IgnoreFileName);
96 | if (File.Exists(file))
97 | {
98 | Common.ShowError($"File {Common.IgnoreFileName} already exists.");
99 | return;
100 | }
101 |
102 | File.WriteAllText(file, Templates.IgnoreFiles, Encoding.UTF8);
103 |
104 | project.ProjectItems.AddFromFile(file);
105 | dte.ItemOperations.OpenFile(file);
106 | }
107 | }
108 | }
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/UpdateForm.designer.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using System.Drawing;
3 | using System.Windows.Forms;
4 |
5 | namespace CnSharp.Windows.Updater
6 | {
7 | partial class UpdateForm
8 | {
9 | ///
10 | /// Required designer variable.
11 | ///
12 | private System.ComponentModel.IContainer components = null;
13 |
14 | ///
15 | /// Clean up any resources being used.
16 | ///
17 | /// true if managed resources should be disposed; otherwise, false.
18 | protected override void Dispose(bool disposing)
19 | {
20 | if (disposing && (this.components != null))
21 | {
22 | this.components.Dispose();
23 | }
24 | base.Dispose(disposing);
25 | }
26 |
27 | #region Windows Form Designer generated code
28 |
29 | ///
30 | /// Required method for Designer support - do not modify
31 | /// the contents of this method with the code editor.
32 | ///
33 | private void InitializeComponent()
34 | {
35 | ComponentResourceManager resources = new ComponentResourceManager(typeof(UpdateForm));
36 | panel1 = new Panel();
37 | boxDes = new RichTextBox();
38 | btnCancel = new Button();
39 | btnUpgrade = new Button();
40 | lblSize = new Label();
41 | progressBar = new ProgressBar();
42 | lblTitle = new Label();
43 | panel1.SuspendLayout();
44 | SuspendLayout();
45 | //
46 | // panel1
47 | //
48 | panel1.Controls.Add(boxDes);
49 | panel1.Controls.Add(btnCancel);
50 | panel1.Controls.Add(btnUpgrade);
51 | panel1.Controls.Add(lblSize);
52 | panel1.Controls.Add(progressBar);
53 | panel1.Controls.Add(lblTitle);
54 | resources.ApplyResources(panel1, "panel1");
55 | panel1.Name = "panel1";
56 | //
57 | // boxDes
58 | //
59 | boxDes.BackColor = SystemColors.Window;
60 | boxDes.BorderStyle = BorderStyle.FixedSingle;
61 | resources.ApplyResources(boxDes, "boxDes");
62 | boxDes.Name = "boxDes";
63 | boxDes.ReadOnly = true;
64 | //
65 | // btnCancel
66 | //
67 | resources.ApplyResources(btnCancel, "btnCancel");
68 | btnCancel.Name = "btnCancel";
69 | btnCancel.UseVisualStyleBackColor = true;
70 | btnCancel.Click += btnCancel_Click;
71 | //
72 | // btnUpgrade
73 | //
74 | resources.ApplyResources(btnUpgrade, "btnUpgrade");
75 | btnUpgrade.Name = "btnUpgrade";
76 | btnUpgrade.UseVisualStyleBackColor = true;
77 | btnUpgrade.Click += btnUpgrade_Click;
78 | //
79 | // lblSize
80 | //
81 | resources.ApplyResources(lblSize, "lblSize");
82 | lblSize.Name = "lblSize";
83 | //
84 | // progressBar
85 | //
86 | resources.ApplyResources(progressBar, "progressBar");
87 | progressBar.Name = "progressBar";
88 | //
89 | // lblTitle
90 | //
91 | resources.ApplyResources(lblTitle, "lblTitle");
92 | lblTitle.Name = "lblTitle";
93 | //
94 | // UpdateForm
95 | //
96 | resources.ApplyResources(this, "$this");
97 | AutoScaleMode = AutoScaleMode.Font;
98 | Controls.Add(panel1);
99 | FormBorderStyle = FormBorderStyle.FixedDialog;
100 | MaximizeBox = false;
101 | MinimizeBox = false;
102 | Name = "UpdateForm";
103 | FormClosing += FormUpdate_FormClosing;
104 | Load += FormUpdate_Load;
105 | panel1.ResumeLayout(false);
106 | panel1.PerformLayout();
107 | ResumeLayout(false);
108 |
109 | }
110 |
111 | #endregion
112 |
113 | private System.Windows.Forms.Label lblTitle;
114 | private System.Windows.Forms.ProgressBar progressBar;
115 | private System.Windows.Forms.Button btnUpgrade;
116 | private System.Windows.Forms.Button btnCancel;
117 | private System.Windows.Forms.Label lblSize;
118 | private System.Windows.Forms.RichTextBox boxDes;
119 | private System.Windows.Forms.Panel panel1;
120 | }
121 | }
--------------------------------------------------------------------------------
/src/VSIX/Commands/AddManifestFileCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.Design;
3 | using System.IO;
4 | using System.Text;
5 | using CnSharp.Updater;
6 | using CnSharp.VisualStudio.Extensions;
7 | using Microsoft.VisualStudio.Shell;
8 | using Task = System.Threading.Tasks.Task;
9 |
10 | namespace CnSharp.VisualStudio.SharpUpdater.Commands
11 | {
12 | ///
13 | /// Command handler
14 | ///
15 | internal sealed class AddManifestFileCommand
16 | {
17 | ///
18 | /// Command ID.
19 | ///
20 | public const int CommandId = PackageIds.AddManifestFileCommandId;
21 |
22 | ///
23 | /// Command menu group (command set GUID).
24 | ///
25 | public static readonly Guid CommandSet = PackageGuids.guidProjectAddCmdSet;
26 |
27 | ///
28 | /// VS Package that provides this command, not null.
29 | ///
30 | private readonly AsyncPackage package;
31 |
32 | ///
33 | /// Initializes a new instance of the class.
34 | /// Adds our command handlers for menu (commands must exist in the command table file)
35 | ///
36 | /// Owner package, not null.
37 | /// Command service to add command to, not null.
38 | private AddManifestFileCommand(AsyncPackage package, OleMenuCommandService commandService)
39 | {
40 | this.package = package ?? throw new ArgumentNullException(nameof(package));
41 | commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));
42 |
43 | var menuCommandID = new CommandID(CommandSet, CommandId);
44 | var menuItem = new MenuCommand(this.Execute, menuCommandID);
45 | commandService.AddCommand(menuItem);
46 | }
47 |
48 | ///
49 | /// Gets the instance of the command.
50 | ///
51 | public static AddManifestFileCommand Instance
52 | {
53 | get;
54 | private set;
55 | }
56 |
57 | ///
58 | /// Gets the service provider from the owner package.
59 | ///
60 | private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider
61 | {
62 | get
63 | {
64 | return this.package;
65 | }
66 | }
67 |
68 | ///
69 | /// Initializes the singleton instance of the command.
70 | ///
71 | /// Owner package, not null.
72 | public static async Task InitializeAsync(AsyncPackage package)
73 | {
74 | // Switch to the main thread - the call to AddCommand in AddIgnoreFileCommand's constructor requires
75 | // the UI thread.
76 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
77 |
78 | OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
79 | Instance = new AddManifestFileCommand(package, commandService);
80 | }
81 |
82 | ///
83 | /// This function is the callback used to execute the command when the menu item is clicked.
84 | /// See the constructor to see how the menu item is associated with this function using
85 | /// OleMenuCommandService service and MenuCommand class.
86 | ///
87 | /// Event sender.
88 | /// Event args.
89 | private void Execute(object sender, EventArgs e)
90 | {
91 | ThreadHelper.ThrowIfNotOnUIThread();
92 |
93 | var dte = Host.Instance.Dte2;
94 | var project = dte.GetActiveProject();
95 | var file = Path.Combine(project.GetDirectory(), Manifest.ManifestFileName);
96 | if (File.Exists(file))
97 | {
98 | Common.ShowError($"File {Manifest.ManifestFileName} already exists.");
99 | return;
100 | }
101 |
102 | File.WriteAllText(file, Templates.ManifestXml, Encoding.UTF8);
103 |
104 | project.ProjectItems.AddFromFile(file);
105 | dte.ItemOperations.OpenFile(file);
106 | }
107 | }
108 | }
--------------------------------------------------------------------------------
/src/VSIX/Commands/SharpPackCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel.Design;
3 | using System.Windows.Forms;
4 | using CnSharp.Updater;
5 | using CnSharp.VisualStudio.Extensions;
6 | using CnSharp.VisualStudio.SharpUpdater.Wizard;
7 | using Microsoft.VisualStudio.Shell;
8 | using Task = System.Threading.Tasks.Task;
9 |
10 | namespace CnSharp.VisualStudio.SharpUpdater.Commands
11 | {
12 | ///
13 | /// Command handler
14 | ///
15 | internal sealed class SharpPackCommand
16 | {
17 | ///
18 | /// Command ID.
19 | ///
20 | public const int CommandId = PackageIds.SharpPackCommandId;
21 |
22 | ///
23 | /// Command menu group (command set GUID).
24 | ///
25 | public static readonly Guid CommandSet = PackageGuids.guidProjectCmdSet;
26 |
27 | ///
28 | /// VS Package that provides this command, not null.
29 | ///
30 | private readonly AsyncPackage package;
31 |
32 | ///
33 | /// Initializes a new instance of the class.
34 | /// Adds our command handlers for menu (commands must exist in the command table file)
35 | ///
36 | /// Owner package, not null.
37 | /// Command service to add command to, not null.
38 | private SharpPackCommand(AsyncPackage package, OleMenuCommandService commandService)
39 | {
40 | this.package = package ?? throw new ArgumentNullException(nameof(package));
41 | commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));
42 |
43 | var menuCommandID = new CommandID(CommandSet, CommandId);
44 | var menuItem = new MenuCommand(this.Execute, menuCommandID);
45 | commandService.AddCommand(menuItem);
46 | }
47 |
48 | ///
49 | /// Gets the instance of the command.
50 | ///
51 | public static SharpPackCommand Instance
52 | {
53 | get;
54 | private set;
55 | }
56 |
57 | ///
58 | /// Gets the service provider from the owner package.
59 | ///
60 | private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider
61 | {
62 | get
63 | {
64 | return this.package;
65 | }
66 | }
67 |
68 | ///
69 | /// Initializes the singleton instance of the command.
70 | ///
71 | /// Owner package, not null.
72 | public static async Task InitializeAsync(AsyncPackage package)
73 | {
74 | // Switch to the main thread - the call to AddCommand in SharpPackCommand's constructor requires
75 | // the UI thread.
76 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
77 |
78 | OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService;
79 | Instance = new SharpPackCommand(package, commandService);
80 | }
81 |
82 | ///
83 | /// This function is the callback used to execute the command when the menu item is clicked.
84 | /// See the constructor to see how the menu item is associated with this function using
85 | /// OleMenuCommandService service and MenuCommand class.
86 | ///
87 | /// Event sender.
88 | /// Event args.
89 | private void Execute(object sender, EventArgs e)
90 | {
91 | ThreadHelper.ThrowIfNotOnUIThread();
92 |
93 | var wizard = new PackingWizard();
94 | if (wizard.ShowDialog() == DialogResult.OK)
95 | {
96 | Task.Run(() => wizard.PackingAsync().ContinueWith(task =>
97 | {
98 | if (task.Exception != null)
99 | {
100 | Host.Instance.Dte2.OutputMessage(Constants.ProductName,
101 | task.Exception.InnerExceptions.Count > 0 ?
102 | task.Exception.InnerExceptions[0].Message :
103 | task.Exception.Message);
104 | }
105 | }));
106 | }
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/VSIX/Wizard/ManifestGrid.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace CnSharp.VisualStudio.SharpUpdater.Wizard
2 | {
3 | partial class ManifestGrid
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Component Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.gridFileList = new System.Windows.Forms.DataGridView();
32 | this.ColSelect = new System.Windows.Forms.DataGridViewCheckBoxColumn();
33 | this.ColFileName = new System.Windows.Forms.DataGridViewTextBoxColumn();
34 | this.ColSize = new System.Windows.Forms.DataGridViewTextBoxColumn();
35 | this.ColFileVersion = new System.Windows.Forms.DataGridViewTextBoxColumn();
36 | ((System.ComponentModel.ISupportInitialize)(this.gridFileList)).BeginInit();
37 | this.SuspendLayout();
38 | //
39 | // gridFileList
40 | //
41 | this.gridFileList.AllowUserToAddRows = false;
42 | this.gridFileList.AllowUserToDeleteRows = false;
43 | this.gridFileList.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill;
44 | this.gridFileList.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
45 | this.gridFileList.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
46 | this.ColSelect,
47 | this.ColFileName,
48 | this.ColSize,
49 | this.ColFileVersion});
50 | this.gridFileList.Dock = System.Windows.Forms.DockStyle.Fill;
51 | this.gridFileList.Location = new System.Drawing.Point(0, 0);
52 | this.gridFileList.Name = "gridFileList";
53 | this.gridFileList.RowHeadersVisible = false;
54 | this.gridFileList.RowTemplate.Height = 23;
55 | this.gridFileList.Size = new System.Drawing.Size(734, 494);
56 | this.gridFileList.TabIndex = 17;
57 | this.gridFileList.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridFileList_CellClick);
58 | //
59 | // ColSelect
60 | //
61 | this.ColSelect.FillWeight = 10F;
62 | this.ColSelect.HeaderText = "Select";
63 | this.ColSelect.Name = "ColSelect";
64 | this.ColSelect.Resizable = System.Windows.Forms.DataGridViewTriState.True;
65 | this.ColSelect.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
66 | //
67 | // ColFileName
68 | //
69 | this.ColFileName.FillWeight = 50F;
70 | this.ColFileName.HeaderText = "Name";
71 | this.ColFileName.Name = "ColFileName";
72 | this.ColFileName.ReadOnly = true;
73 | //
74 | // ColSize
75 | //
76 | this.ColSize.FillWeight = 20F;
77 | this.ColSize.HeaderText = "File Size(K)";
78 | this.ColSize.Name = "ColSize";
79 | this.ColSize.ReadOnly = true;
80 | //
81 | // ColFileVersion
82 | //
83 | this.ColFileVersion.FillWeight = 20F;
84 | this.ColFileVersion.HeaderText = "Version";
85 | this.ColFileVersion.Name = "ColFileVersion";
86 | //
87 | // ManifestGrid
88 | //
89 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
90 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
91 | this.Controls.Add(this.gridFileList);
92 | this.Name = "ManifestGrid";
93 | this.Size = new System.Drawing.Size(734, 494);
94 | ((System.ComponentModel.ISupportInitialize)(this.gridFileList)).EndInit();
95 | this.ResumeLayout(false);
96 |
97 | }
98 |
99 | #endregion
100 |
101 | private System.Windows.Forms.DataGridView gridFileList;
102 | private System.Windows.Forms.DataGridViewCheckBoxColumn ColSelect;
103 | private System.Windows.Forms.DataGridViewTextBoxColumn ColFileName;
104 | private System.Windows.Forms.DataGridViewTextBoxColumn ColSize;
105 | private System.Windows.Forms.DataGridViewTextBoxColumn ColFileVersion;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | [Xx]64/
19 | [Xx]86/
20 | [Bb]uild/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 |
85 | # Visual Studio profiler
86 | *.psess
87 | *.vsp
88 | *.vspx
89 | *.sap
90 |
91 | # TFS 2012 Local Workspace
92 | $tf/
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 | *.DotSettings.user
101 |
102 | # JustCode is a .NET coding add-in
103 | .JustCode
104 |
105 | # TeamCity is a build add-in
106 | _TeamCity*
107 |
108 | # DotCover is a Code Coverage Tool
109 | *.dotCover
110 |
111 | # NCrunch
112 | _NCrunch_*
113 | .*crunch*.local.xml
114 | nCrunchTemp_*
115 |
116 | # MightyMoose
117 | *.mm.*
118 | AutoTest.Net/
119 |
120 | # Web workbench (sass)
121 | .sass-cache/
122 |
123 | # Installshield output folder
124 | [Ee]xpress/
125 |
126 | # DocProject is a documentation generator add-in
127 | DocProject/buildhelp/
128 | DocProject/Help/*.HxT
129 | DocProject/Help/*.HxC
130 | DocProject/Help/*.hhc
131 | DocProject/Help/*.hhk
132 | DocProject/Help/*.hhp
133 | DocProject/Help/Html2
134 | DocProject/Help/html
135 |
136 | # Click-Once directory
137 | publish/
138 |
139 | # Publish Web Output
140 | *.[Pp]ublish.xml
141 | *.azurePubxml
142 | *.pubxml
143 |
144 | # TODO: Un-comment the next line if you do not want to checkin
145 | # your web deploy settings because they may include unencrypted
146 | # passwords
147 | #*.pubxml
148 | *.publishproj
149 |
150 | # NuGet Packages
151 | *.nupkg
152 | # The packages folder can be ignored because of Package Restore
153 | **/packages/*
154 | # except build/, which is used as an MSBuild target.
155 | !**/packages/build/
156 | # Uncomment if necessary however generally it will be regenerated when needed
157 | #!**/packages/repositories.config
158 | # NuGet v3's project.json files produces more ignoreable files
159 | *.nuget.props
160 | *.nuget.targets
161 |
162 | # Microsoft Azure Build Output
163 | csx/
164 | *.build.csdef
165 |
166 | # Microsoft Azure Emulator
167 | ecf/
168 | rcf/
169 |
170 | # Microsoft Azure ApplicationInsights config file
171 | ApplicationInsights.config
172 |
173 | # Windows Store app package directory
174 | AppPackages/
175 | BundleArtifacts/
176 |
177 | # Visual Studio cache files
178 | # files ending in .cache can be ignored
179 | *.[Cc]ache
180 | # but keep track of directories ending in .cache
181 | !*.[Cc]ache/
182 |
183 | # Others
184 | ClientBin/
185 | [Ss]tyle[Cc]op.*
186 | ~$*
187 | *~
188 | *.dbmdl
189 | *.dbproj.schemaview
190 | *.pfx
191 | *.publishsettings
192 | node_modules/
193 | orleans.codegen.cs
194 |
195 | # RIA/Silverlight projects
196 | Generated_Code/
197 |
198 | # Backup & report files from converting an old project file
199 | # to a newer Visual Studio version. Backup files are not needed,
200 | # because we have git ;-)
201 | _UpgradeReport_Files/
202 | Backup*/
203 | UpgradeLog*.XML
204 | UpgradeLog*.htm
205 |
206 | # SQL Server files
207 | *.mdf
208 | *.ldf
209 |
210 | # Business Intelligence projects
211 | *.rdl.data
212 | *.bim.layout
213 | *.bim_*.settings
214 |
215 | # Microsoft Fakes
216 | FakesAssemblies/
217 |
218 | # GhostDoc plugin setting file
219 | *.GhostDoc.xml
220 |
221 | # Node.js Tools for Visual Studio
222 | .ntvs_analysis.dat
223 |
224 | # Visual Studio 6 build log
225 | *.plg
226 |
227 | # Visual Studio 6 workspace options file
228 | *.opt
229 |
230 | # Visual Studio LightSwitch build output
231 | **/*.HTMLClient/GeneratedArtifacts
232 | **/*.DesktopClient/GeneratedArtifacts
233 | **/*.DesktopClient/ModelManifest.xml
234 | **/*.Server/GeneratedArtifacts
235 | **/*.Server/ModelManifest.xml
236 | _Pvt_Extensions
237 |
238 | # LightSwitch generated files
239 | GeneratedArtifacts/
240 | ModelManifest.xml
241 |
242 | # Paket dependency manager
243 | .paket/paket.exe
244 |
245 | # FAKE - F# Make
246 | .fake/
247 |
248 | # keys
249 | *.snk
250 |
251 | # CnSharp Tools
252 | .nupack/
253 | .[Ss]harp[Uu]pdater/
--------------------------------------------------------------------------------
/src/CLI/Program.cs:
--------------------------------------------------------------------------------
1 | using CnSharp.Updater.CLI.CommandHandlers;
2 | using System.CommandLine;
3 |
4 | namespace CnSharp.Updater.CLI
5 | {
6 | internal class Program
7 | {
8 | static async Task Main(string[] args)
9 | {
10 | var rootCommand = new RootCommand($"This is a packaging & deployment tool of {Constants.ProductName}.");
11 | rootCommand.AddCommand(NewSetGlobalSourceCommand());
12 | rootCommand.AddCommand(NewRemoveGlobalSourceCommand());
13 | rootCommand.AddCommand(NewInitManifestFileCommand());
14 | rootCommand.AddCommand(NewInitIgnoreFileCommand());
15 | rootCommand.AddCommand(NewPackCommand());
16 | rootCommand.AddCommand(NewPushCommand());
17 | return await rootCommand.InvokeAsync(args);
18 | }
19 |
20 | private static Command NewInitManifestFileCommand()
21 | {
22 | return new Command("init", $"Generate a {Constants.ManifestFileName} file in the current directory")
23 | {
24 | Handler = new InitManifestFileCommandHandler()
25 | };
26 | }
27 |
28 | private static Command NewInitIgnoreFileCommand()
29 | {
30 | return new Command("ignore", $"Generate a {Constants.IgnoreFileName} file in the current directory")
31 | {
32 | Handler = new InitIgnoreFileCommandHandler()
33 | };
34 | }
35 |
36 | private static Command NewSetGlobalSourceCommand()
37 | {
38 | var sourceOption = new Option("--source", "Set the global SharpUpdater.Server source URL")
39 | {
40 | IsRequired = true
41 | };
42 | sourceOption.AddAlias("-s");
43 |
44 | var command = new Command("global", "Set the global settings")
45 | {
46 | sourceOption
47 | };
48 | command.SetHandler(GlobalSourceCommandHandler.Set, sourceOption);
49 | return command;
50 | }
51 |
52 | private static Command NewRemoveGlobalSourceCommand()
53 | {
54 | var command = new Command("RemoveSource", "Remove the global source");
55 | command.SetHandler(GlobalSourceCommandHandler.Remove);
56 | return command;
57 | }
58 |
59 | private static Command NewPackCommand()
60 | {
61 | var sourceOption = new Option("--source", "Specify the SharpUpdater.Server source URL");
62 | sourceOption.AddAlias("-s");
63 |
64 | var projectDirOption = new Option("--project", "Specify the project directory");
65 | projectDirOption.AddAlias("-p");
66 |
67 | var outputDirOption = new Option("--output", () => $"bin\\{Constants.ProductName}\\", "Specify the output directory");
68 | outputDirOption.AddAlias("-o");
69 |
70 | var versionOption = new Option("--version", "Specify the package version");
71 | versionOption.AddAlias("-v");
72 |
73 | var minVersionOption = new Option("--MinimumVersion", "Specify the minimum version must be updated");
74 | minVersionOption.AddAlias("-mv");
75 |
76 | var releaseNotesOption = new Option("--ReleaseNotes", "Input release notes");
77 | releaseNotesOption.AddAlias("-rn");
78 |
79 | var noBuildOption = new Option("--no-build", "Skip build");
80 |
81 | var command = new Command("pack", "Pack the project")
82 | {
83 | sourceOption,
84 | projectDirOption,
85 | outputDirOption,
86 | versionOption,
87 | minVersionOption,
88 | releaseNotesOption,
89 | noBuildOption
90 | };
91 | command.SetHandler(PackCommandHandler.Invoke,
92 | sourceOption,
93 | projectDirOption,
94 | outputDirOption,
95 | versionOption,
96 | minVersionOption,
97 | releaseNotesOption,
98 | noBuildOption);
99 | return command;
100 | }
101 |
102 | private static Command NewPushCommand()
103 | {
104 | var packageOption = new Option("--package", "Specify the .sp file path")
105 | {
106 | IsRequired = true
107 | };
108 | packageOption.AddAlias("-p");
109 |
110 | var sourceOption = new Option("--source", "Specify the SharpUpdater.Server source URL");
111 | sourceOption.AddAlias("-s");
112 |
113 | var keyOption = new Option("--apikey", "Specify the ApiKey of SharpUpdater.Server")
114 | {
115 | IsRequired = true
116 | };
117 | keyOption.AddAlias("-k");
118 |
119 | var pushCommand = new Command("push", "Push .sp package to SharpUpdater.Server")
120 | {
121 | packageOption,
122 | sourceOption,
123 | keyOption
124 | };
125 | pushCommand.SetHandler(PushCommandHandler.Invoke,
126 | packageOption,
127 | sourceOption,
128 | keyOption);
129 | return pushCommand;
130 | }
131 | }
132 |
133 | }
--------------------------------------------------------------------------------
/src/Core/Util/FileUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Security.Cryptography;
6 | using System.Text;
7 | using System.Xml;
8 |
9 | namespace CnSharp.Updater.Util
10 | {
11 | public static class FileUtil
12 | {
13 |
14 | public static Manifest ReadManifest(string fileName)
15 | {
16 | return XmlSerializerHelper.LoadObjectFromXml(fileName);
17 | }
18 |
19 | public static void Merge(this Manifest manifest, string fileName)
20 | {
21 | if (!File.Exists(fileName))
22 | {
23 | throw new FileNotFoundException($"{fileName} not found.");
24 | }
25 |
26 | var current = XmlSerializerHelper.LoadObjectFromXml(fileName);
27 | var props = typeof(Manifest).GetProperties()
28 | .Where(p => p.CanRead && p.CanWrite && p.GetType() != typeof(XmlNode))
29 | .ToList();
30 | foreach (var prop in props)
31 | {
32 | var currentValue = prop.GetValue(current)?.ToString();
33 | var newValue = prop.GetValue(manifest);
34 | if (currentValue != null && currentValue.StartsWith("$") && currentValue.EndsWith("$"))
35 | continue;
36 | if (newValue != null)
37 | {
38 | prop.SetValue(current, newValue);
39 | }
40 | }
41 | XmlSerializerHelper.SerializeToXmlFile(current, fileName);
42 | }
43 |
44 | public static void Save(this Manifest manifest, string fileName)
45 | {
46 | XmlSerializerHelper.SerializeToXmlFile(manifest, fileName);
47 | }
48 |
49 |
50 | public static void CopyFiles(string sourceDirectory, string targetDirectory)
51 | {
52 | if (!Directory.Exists(sourceDirectory)) return;
53 | if (!Directory.Exists(targetDirectory))
54 | Directory.CreateDirectory(targetDirectory);
55 |
56 | string[] directories = Directory.GetDirectories(sourceDirectory);
57 | if (directories.Length > 0)
58 | {
59 | foreach (string d in directories)
60 | {
61 | CopyFiles(d, targetDirectory + d.Substring(d.LastIndexOf("\\")));
62 | }
63 | }
64 |
65 | string[] files = Directory.GetFiles(sourceDirectory);
66 | if (files.Length > 0)
67 | {
68 | foreach (string s in files)
69 | {
70 | File.Copy(s, targetDirectory + s.Substring(s.LastIndexOf("\\")), true);
71 | }
72 | }
73 | }
74 |
75 | public static void DeleteFolder(string path)
76 | {
77 | DeleteDirectory(new DirectoryInfo(path));
78 | }
79 |
80 | public static void DeleteDirectory(DirectoryInfo dir)
81 | {
82 | if (dir.Exists)
83 | {
84 | DirectoryInfo[] childs = dir.GetDirectories();
85 | foreach (DirectoryInfo child in childs)
86 | {
87 | DeleteDirectory(child);
88 | }
89 | dir.Delete(true);
90 | }
91 | }
92 |
93 | ///
94 | /// 加密
95 | ///
96 | ///
97 | ///
98 | public static string MD5(this string input)
99 | {
100 | var md5 = new MD5CryptoServiceProvider();
101 |
102 | byte[] inBytes = Encoding.Default.GetBytes(input);
103 |
104 | byte[] outBytes = md5.ComputeHash(inBytes);
105 |
106 | var output = new StringBuilder();
107 |
108 | for (int i = 0; i < outBytes.Length; i++)
109 | {
110 | output.Append(outBytes[i].ToString("x2"));
111 | }
112 |
113 | return output.ToString();
114 | }
115 |
116 | public static string FormatFileSizeDescription(long bytes)
117 | {
118 | if (bytes > 1024 * 1024)
119 | return string.Format("{0}M", Math.Round((double)bytes / (1024 * 1024), 2, MidpointRounding.AwayFromZero));
120 | if (bytes > 1024)
121 | return string.Format("{0}K", Math.Round((double)bytes / 1024, 2, MidpointRounding.AwayFromZero));
122 | return string.Format("{0}B", bytes);
123 | }
124 |
125 |
126 | public static long CopyTo(this Stream source, Stream target)
127 | {
128 | const int bufSize = 0x1000;
129 |
130 | var buf = new byte[bufSize];
131 |
132 | long totalBytes = 0;
133 |
134 | var bytesRead = 0;
135 |
136 | while ((bytesRead = source.Read(buf, 0, bufSize)) > 0)
137 | {
138 | target.Write(buf, 0, bytesRead);
139 | totalBytes += bytesRead;
140 | }
141 |
142 | return totalBytes;
143 | }
144 |
145 | public static string JoinFilePath(string baseDir, string targetDir)
146 | {
147 | var dir = targetDir.Trim().Replace('/', Path.DirectorySeparatorChar);
148 | if (!dir.Contains(":" + Path.DirectorySeparatorChar))
149 | {
150 | dir = Path.Combine(baseDir, dir);
151 | }
152 | return dir;
153 | }
154 | }
155 | }
--------------------------------------------------------------------------------
/src/CLI/CommandHandlers/PackCommandHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using CnSharp.Updater.Packaging;
3 | using CnSharp.Updater.Util;
4 | using NuGet.Versioning;
5 |
6 | namespace CnSharp.Updater.CLI.CommandHandlers;
7 |
8 | internal class PackCommandHandler
9 | {
10 | public static async Task Invoke(string source, string? project, string output, string? version, string? minimumVersion, string releaseNotes, bool noBuild)
11 | {
12 | SemanticVersion? v = null;
13 | if (!string.IsNullOrWhiteSpace(version) && !SemanticVersion.TryParse(version, out v))
14 | {
15 | ConsoleExtensions.WriteError("Version number is not semantic.");
16 | return;
17 | }
18 |
19 | SemanticVersion? mv = null;
20 | if (!string.IsNullOrWhiteSpace(minimumVersion) && !SemanticVersion.TryParse(minimumVersion, out mv))
21 | {
22 | ConsoleExtensions.WriteError("Minimum version number is not semantic.");
23 | return;
24 | }
25 | var dir = project ?? Directory.GetCurrentDirectory();
26 | string projectFile;
27 | if (ProjectHolder.CheckExistsIfProject(dir))
28 | {
29 | projectFile = dir;
30 | dir = Path.GetDirectoryName(dir);
31 | }
32 | else
33 | {
34 | projectFile = ProjectHolder.GetProjectFile(dir);
35 | if (projectFile == null)
36 | {
37 | ConsoleExtensions.WriteError("Project file not found.");
38 | return;
39 | }
40 | }
41 |
42 | var manifest = GetManifest(dir);
43 | var globalSource = Settings.Load()?.GlobalSource;
44 | var currentSource = !string.IsNullOrWhiteSpace(source) ? source : globalSource;
45 | if (string.IsNullOrWhiteSpace(manifest.ReleaseUrl) && string.IsNullOrWhiteSpace(currentSource))
46 | {
47 | ConsoleExtensions.WriteError("--source is required.");
48 | return;
49 | }
50 |
51 | var projectHelper = ProjectHolder.GetProjectHelper(projectFile);
52 | if (!noBuild)
53 | {
54 | Console.WriteLine("Building...");
55 | if (!Build(projectHelper))
56 | {
57 | return;
58 | }
59 | }
60 |
61 | var releaseDir = projectHelper.GetReleaseDir();
62 | var exeFile = projectHelper.GetExeFileReleased();
63 | UpdateManifest(manifest, exeFile, releaseDir, dir, currentSource, v, mv, releaseNotes);
64 | var outputPath = output;
65 | if (string.IsNullOrWhiteSpace(outputPath))
66 | outputPath = $"bin\\{Constants.ProductName}";
67 | var outputDir = EnsureOutputDir(dir, outputPath);
68 | var zipName = $"{manifest.AppName}_{manifest.Version}{Manifest.PackageFileExt}";
69 | var zipPath = $"{outputDir}\\{zipName}";
70 | var pb = new PackageBuilder(manifest, releaseDir);
71 | pb.CreatePackage(zipPath);
72 | Console.WriteLine($"Package file generated in {zipPath}.");
73 | }
74 |
75 | protected static string EnsureOutputDir(string baseDir, string outputDir)
76 | {
77 | var dir = outputDir.Trim().Replace('/', Path.DirectorySeparatorChar);
78 | dir = FileUtil.JoinFilePath(baseDir, dir);
79 | if (!Directory.Exists(dir))
80 | Directory.CreateDirectory(dir);
81 | return dir;
82 | }
83 |
84 | private static Manifest GetManifest(string projectDir)
85 | {
86 | var manifestFilePath = Path.Combine(projectDir, Constants.ManifestFileName);
87 | return File.Exists(manifestFilePath) ? XmlSerializerHelper.LoadObjectFromXml(manifestFilePath) : new Manifest();
88 | }
89 |
90 | private static void UpdateManifest(Manifest manifest, string exeFile, string releaseDir, string projectDir, string? source, SemanticVersion? version, SemanticVersion? minVersion, string releaseNotes)
91 | {
92 | var fileVersionInfo = FileVersionInfo.GetVersionInfo(exeFile);
93 | var fileVersion = fileVersionInfo.FileVersion ?? fileVersionInfo.ProductVersion;
94 | var packageVersion = version?.ToString() ?? fileVersion.ToSemanticVersion();
95 | var fileName = Path.GetFileNameWithoutExtension(fileVersionInfo.FileName);
96 | manifest.SetIdIfBlank(fileName);
97 | manifest.SetAppNameIfBlank(!string.IsNullOrWhiteSpace(fileVersionInfo.ProductName) ? fileVersionInfo.ProductName : fileName);
98 | manifest.SetCopyrightIfBlank(fileVersionInfo.LegalCopyright);
99 | manifest.SetDescriptionIfBlank(fileVersionInfo.FileDescription);
100 | manifest.SetEntryPointIfBlank(Path.GetFileName(exeFile));
101 | manifest.SetIdIfBlank(fileVersionInfo.CompanyName);
102 | manifest.SetVersionIfBlank(packageVersion);
103 | if (!string.IsNullOrWhiteSpace(source))
104 | manifest.ReleaseUrl = source;
105 | if (!string.IsNullOrWhiteSpace(releaseNotes))
106 | manifest.ReleaseNotes = releaseNotes;
107 | manifest.MinVersion = minVersion?.ToString() ?? manifest.Version;
108 | manifest.Files = new ManifestGatherer(releaseDir, projectDir).GatherFiles(true);
109 | var manifestFilePath = Path.Combine(releaseDir, Manifest.ManifestFileName);
110 | manifest.Save(manifestFilePath);
111 | }
112 |
113 | private static bool Build(ProjectHelper projectHelper)
114 | {
115 | if (projectHelper.IsDotnetFramework)
116 | return CmdHelper.Run("msbuild", $"\"{projectHelper.ProjectFile}\" /p:Configuration=Release", Console.WriteLine, Console.WriteLine);
117 | return CmdHelper.Run("dotnet", $" build \"{projectHelper.ProjectFile}\" --configuration Release", Console.WriteLine, Console.WriteLine);
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/VSIX/Wizard/ProductInfoControl.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Windows.Forms;
4 | using CnSharp.Updater;
5 | using CnSharp.Updater.Util;
6 | using CnSharp.VisualStudio.Extensions.Projects;
7 |
8 | namespace CnSharp.VisualStudio.SharpUpdater.Wizard
9 | {
10 | public partial class ProductInfoControl : UserControl
11 | {
12 | public ProductInfoControl()
13 | {
14 | InitializeComponent();
15 | }
16 |
17 | private Manifest _manifest;
18 |
19 | public Manifest Manifest
20 | {
21 | set
22 | {
23 | _manifest = value;
24 | if(_manifest != null)
25 | BindManifest();
26 | }
27 | get
28 | {
29 | if(_manifest != null)
30 | SyncManifest();
31 | return _manifest;
32 | }
33 | }
34 |
35 | private ProjectAssemblyInfo _projectAssemblyInfo;
36 | public ProjectAssemblyInfo ProjectAssemblyInfo
37 | {
38 | set
39 | {
40 | _projectAssemblyInfo = value;
41 | if (_projectAssemblyInfo != null)
42 | {
43 | var ver = _projectAssemblyInfo.Version.ToSemanticVersion();
44 | txtReleaseVer.Text = ver;
45 | txtMinVer.Text = ver;
46 | }
47 | else
48 | {
49 | txtReleaseVer.Clear();
50 | txtMinVer.Clear();
51 | }
52 | }
53 | }
54 |
55 |
56 | private void SyncManifest()
57 | {
58 | _manifest.Id = txtId.Text.Trim();
59 | _manifest.AppName = txtAppName.Text.Trim();
60 | _manifest.Owner = txtOwner.Text.Trim();
61 | _manifest.WebSite = txtWebsite.Text.Trim();
62 | _manifest.Version = txtReleaseVer.Text.Trim();
63 | _manifest.MinVersion = txtMinVer.Text.Trim();
64 | _manifest.ReleaseNotes = txtNote.Text;
65 | _manifest.Language = txtLang.Text.Trim();
66 | _manifest.Copyright = txtCopyright.Text.Trim();
67 | _manifest.Tags = txtTags.Text.Trim();
68 | }
69 |
70 |
71 | private void BindManifest()
72 | {
73 | txtId.Text = _manifest.Id;
74 | txtAppName.Text = _manifest.AppName;
75 | txtOwner.Text = _manifest.Owner;
76 | txtWebsite.Text = _manifest.WebSite;
77 | txtReleaseVer.Text = _manifest.Version;
78 | txtMinVer.Text = _manifest.MinVersion;
79 | var desc = _manifest.ReleaseNotes;
80 | if (!string.IsNullOrEmpty(desc))
81 | {
82 | desc = desc.Replace("\n", Environment.NewLine);
83 | }
84 | txtNote.Text = desc;
85 | txtLang.Text = _manifest.Language;
86 | txtCopyright.Text = _manifest.Copyright;
87 | txtTags.Text = _manifest.Tags;
88 | }
89 |
90 | private void AddTextBoxEvents()
91 | {
92 | txtId.Validating += TextBoxRequiredValidating;
93 | txtId.Validated += TextBoxRequiredValidated;
94 | txtAppName.Validating += TextBoxRequiredValidating;
95 | txtAppName.Validated += TextBoxRequiredValidated;
96 | txtOwner.Validating += TextBoxRequiredValidating;
97 | txtOwner.Validated += TextBoxRequiredValidated;
98 | txtNote.Validating += TextBoxRequiredValidating;
99 | txtNote.Validated += TextBoxRequiredValidated;
100 |
101 | txtReleaseVer.Validating += TextBoxRequiredValidating;
102 | txtReleaseVer.Validating += VersionTextBoxValidating;
103 | txtReleaseVer.Validated += TextBoxRequiredValidated;
104 |
105 | txtMinVer.Validating += VersionTextBoxValidating;
106 | txtMinVer.Validating += TextBoxRequiredValidating;
107 | txtMinVer.Validated += TextBoxRequiredValidated;
108 | }
109 |
110 | private void VersionTextBoxValidating(object sender, CancelEventArgs e)
111 | {
112 | var box = sender as TextBox;
113 | if (box == null)
114 | return;
115 | if (!string.IsNullOrWhiteSpace(box.Text) && !box.Text.IsSemanticVersion())
116 | {
117 | errorProvider.SetError(box, "invalid version number");
118 | e.Cancel = true;
119 | return;
120 | }
121 | if (!string.IsNullOrWhiteSpace(txtReleaseVer.Text) && !string.IsNullOrWhiteSpace(txtMinVer.Text) &&
122 | txtMinVer.Text.CompareVersion(txtReleaseVer.Text) > 0)
123 | {
124 | errorProvider.SetError(box, "Minimum Version > Release Version ?");
125 | e.Cancel = true;
126 | }
127 | }
128 |
129 | private void TextBoxRequiredValidating(object sender, CancelEventArgs e)
130 | {
131 | var box = sender as TextBox;
132 | if (box == null)
133 | return;
134 | if (string.IsNullOrWhiteSpace(box.Text))
135 | {
136 | errorProvider.SetError(box, "*");
137 | e.Cancel = true;
138 | }
139 | }
140 |
141 |
142 | private void TextBoxRequiredValidated(object sender, EventArgs e)
143 | {
144 | var box = sender as TextBox;
145 | if (box == null)
146 | return;
147 | errorProvider.SetError(box, null);
148 | }
149 |
150 | private void ProductInfoForm_Load(object sender, EventArgs e)
151 | {
152 | ActiveControl = txtAppName;
153 | AddTextBoxEvents();
154 | }
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet4/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/src/Clients/WinForms-DotNet8/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/src/VSIX/Wizard/ProductInfoControl.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
--------------------------------------------------------------------------------
/src/VSIX/Resource.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | Resources\folder.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
123 |
124 |
--------------------------------------------------------------------------------
/src/VSIX/Common.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.IO;
5 | using System.Reflection;
6 | using System.Windows.Forms;
7 | using CnSharp.Updater.Util;
8 | using CnSharp.VisualStudio.Extensions;
9 | using EnvDTE;
10 | using Constants = CnSharp.Updater.Constants;
11 |
12 | namespace CnSharp.VisualStudio.SharpUpdater
13 | {
14 | public class Common
15 | {
16 | public static readonly string IgnoreFileName = $"{Constants.ProductName}.ignore";
17 | public static readonly string[] SupportedProjectTypes = { ".csproj", ".vbproj", ".fsproj" };
18 | public static readonly string[] SupportedFileTypes = { ".exe"};
19 |
20 | public static bool Contains(Enum keys, Enum flag)
21 | {
22 | ulong keysVal = Convert.ToUInt64(keys);
23 | ulong flagVal = Convert.ToUInt64(flag);
24 |
25 | return (keysVal & flagVal) == flagVal;
26 | }
27 |
28 | public static DialogResult ShowError(string message)
29 | {
30 | return MessageBox.Show(message, Constants.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
31 | }
32 |
33 | public static DialogResult ShowWarning(string message)
34 | {
35 | return MessageBox.Show(message, Constants.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
36 | }
37 | }
38 |
39 | public class ProductInfo
40 | {
41 | public string Name { get; set; }
42 |
43 | public string CompanyName { get; set; }
44 |
45 | public string Version { get; set; }
46 | }
47 |
48 | public class Paths
49 | {
50 | public static string AddinRoot =
51 | Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace("file:\\", "");
52 |
53 | public static string GetSettingsFileLocation(Project project)
54 | {
55 | var dir = Path.GetDirectoryName(Host.Instance.DTE.Solution.FileName);
56 | dir = Path.Combine(dir, $".{Constants.ProductName}");
57 |
58 | if (!Directory.Exists(dir))
59 | {
60 | DirectoryInfo di = Directory.CreateDirectory(dir);
61 | di.Attributes = FileAttributes.Directory | FileAttributes.Hidden;
62 | }
63 |
64 | return Path.Combine(dir, "settings.xml");
65 | }
66 | }
67 |
68 | public class NuPackSettings
69 | {
70 | public NuPackSettings()
71 | {
72 | UnselectedFiles = new List();
73 | UnselectedFolders = new List();
74 | }
75 | public List UnselectedFolders { get; set; }
76 | public List UnselectedFiles { get; set; }
77 |
78 | public string PackageOutputDirectory { get; set; } = "bin\\{version}\\";
79 | public bool OpenPackageOutputDirectoryAfterBuild { get; set; }
80 | public string DeployServer { get; set; }
81 | public string DeployKey { get; set; }
82 |
83 | }
84 |
85 | class Validation
86 | {
87 | public static bool HasValidationErrors(System.Windows.Forms.Control.ControlCollection controls)
88 | {
89 | bool hasError = false;
90 |
91 | // Now we need to loop through the controls and deterime if any of them have errors
92 | foreach (Control control in controls)
93 | {
94 | // check the control and see what it returns
95 | bool validControl = IsValid(control);
96 | // If it's not valid then set the flag and keep going. We want to get through all
97 | // the validators so they will display on the screen if errorProviders were used.
98 | if (!validControl)
99 | hasError = true;
100 |
101 | // If its a container control then it may have children that need to be checked
102 | if (control.HasChildren)
103 | {
104 | if (HasValidationErrors(control.Controls))
105 | hasError = true;
106 | }
107 | }
108 | return hasError;
109 | }
110 |
111 | // Here, let's determine if the control has a validating method attached to it
112 | // and if it does, let's execute it and return the result
113 | private static bool IsValid(object eventSource)
114 | {
115 | string name = "EventValidating";
116 |
117 | Type targetType = eventSource.GetType();
118 |
119 | do
120 | {
121 | FieldInfo[] fields = targetType.GetFields(
122 | BindingFlags.Static |
123 | BindingFlags.Instance |
124 | BindingFlags.NonPublic);
125 |
126 | foreach (FieldInfo field in fields)
127 | {
128 | if (field.Name == name)
129 | {
130 | EventHandlerList eventHandlers = ((EventHandlerList)(eventSource.GetType().GetProperty("Events",
131 | (BindingFlags.FlattenHierarchy |
132 | (BindingFlags.NonPublic | BindingFlags.Instance))).GetValue(eventSource, null)));
133 |
134 | Delegate d = eventHandlers[field.GetValue(eventSource)];
135 |
136 | if (d != null)
137 | {
138 | Delegate[] subscribers = d.GetInvocationList();
139 |
140 | // ok we found the validation event, let's get the event method and call it
141 | foreach (Delegate d1 in subscribers)
142 | {
143 | // create the parameters
144 | object sender = eventSource;
145 | CancelEventArgs eventArgs = new CancelEventArgs();
146 | eventArgs.Cancel = false;
147 | object[] parameters = new object[2];
148 | parameters[0] = sender;
149 | parameters[1] = eventArgs;
150 | // call the method
151 | d1.DynamicInvoke(parameters);
152 | // if the validation failed we need to return that failure
153 | return !eventArgs.Cancel;
154 | }
155 | }
156 | }
157 | }
158 |
159 | targetType = targetType.BaseType;
160 |
161 | } while (targetType != null);
162 |
163 | return true;
164 | }
165 |
166 | }
167 | }
--------------------------------------------------------------------------------
/src/VSIX/Wizard/ManifestGrid.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | True
122 |
123 |
124 | True
125 |
126 |
127 | True
128 |
129 |
130 | True
131 |
132 |
--------------------------------------------------------------------------------
/src/VSIX/SharpUpdaterPackage.cs:
--------------------------------------------------------------------------------
1 | using CnSharp.VisualStudio.Extensions;
2 | using CnSharp.VisualStudio.SharpUpdater.Util;
3 | using EnvDTE;
4 | using EnvDTE80;
5 | using Microsoft.VisualStudio;
6 | using Microsoft.VisualStudio.Shell;
7 | using Microsoft.VisualStudio.Shell.Interop;
8 | using System;
9 | using System.Diagnostics;
10 | using System.Globalization;
11 | using System.Linq;
12 | using System.Reflection;
13 | using System.Runtime.InteropServices;
14 | using System.Threading;
15 | using System.Threading.Tasks;
16 | using CnSharp.VisualStudio.SharpUpdater.Commands;
17 | using Task = System.Threading.Tasks.Task;
18 |
19 | namespace CnSharp.VisualStudio.SharpUpdater
20 | {
21 | ///
22 | /// This is the class that implements the package exposed by this assembly.
23 | ///
24 | ///
25 | ///
26 | /// The minimum requirement for a class to be considered a valid package for Visual Studio
27 | /// is to implement the IVsPackage interface and register itself with the shell.
28 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
29 | /// to do it: it derives from the Package class that provides the implementation of the
30 | /// IVsPackage interface and uses the registration attributes defined in the framework to
31 | /// register itself and its components with the shell. These attributes tell the pkgdef creation
32 | /// utility what data to put into .pkgdef file.
33 | ///
34 | ///
35 | /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file.
36 | ///
37 | ///
38 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
39 | [Guid(SharpUpdaterPackage.PackageGuidString)]
40 | [ProvideMenuResource("Menus.ctmenu", 1)]
41 | public sealed class SharpUpdaterPackage : AsyncPackage
42 | {
43 | ///
44 | /// SharpUpdaterPackage GUID string.
45 | ///
46 | public const string PackageGuidString = PackageGuids.guidSharpUpdaterPackageString;
47 |
48 | public SharpUpdaterPackage()
49 | {
50 | RedirectAssembly("System.IO.Packaging", new Version("9.0.0.2"), "b03f5f7f11d50a3a");
51 | }
52 |
53 | #region Package Members
54 |
55 | ///
56 | /// Initialization of the package; this method is called right after the package is sited, so this is the place
57 | /// where you can put all the initialization code that rely on services provided by VisualStudio.
58 | ///
59 | /// A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down.
60 | /// A provider for progress updates.
61 | /// A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method.
62 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
63 | {
64 | // When initialized asynchronously, the current thread may be a background thread at this point.
65 | // Do any initialization that requires the UI thread after switching to the UI thread.
66 | await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
67 |
68 | var dte = GetGlobalService(typeof(DTE)) as DTE2;
69 | Host.Instance.DTE = dte;
70 |
71 | bool isSolutionLoaded = await IsSolutionLoadedAsync();
72 |
73 | if (isSolutionLoaded)
74 | {
75 | HandleOpenSolution();
76 | }
77 |
78 | // Listen for subsequent solution events
79 | Microsoft.VisualStudio.Shell.Events.SolutionEvents.OnAfterOpenSolution += HandleOpenSolution;
80 |
81 |
82 | dte.Events.SolutionEvents.ProjectAdded += p =>
83 | {
84 | if (string.IsNullOrWhiteSpace(p.FileName) ||
85 | !Common.SupportedProjectTypes.Any(t => p.FileName.EndsWith(t, StringComparison.OrdinalIgnoreCase)))
86 | return;
87 | var sln = Host.Instance.Solution2;
88 | SolutionDataCache.Instance.TryGetValue(sln.FileName, out var sp);
89 | sp?.AddProject(p);
90 | };
91 | dte.Events.SolutionEvents.ProjectRemoved += p =>
92 | {
93 | var sln = Host.Instance.Solution2;
94 | SolutionDataCache.Instance.TryGetValue(sln.FileName, out var sp);
95 | sp?.RemoveProject(p);
96 | };
97 |
98 |
99 |
100 | await AddIgnoreFileCommand.InitializeAsync(this);
101 | await AddManifestFileCommand.InitializeAsync(this);
102 | await SharpPackCommand.InitializeAsync(this);
103 |
104 | }
105 |
106 | private async Task IsSolutionLoadedAsync()
107 | {
108 | await JoinableTaskFactory.SwitchToMainThreadAsync();
109 | var solService = await GetServiceAsync(typeof(SVsSolution)) as IVsSolution;
110 |
111 | ErrorHandler.ThrowOnFailure(solService.GetProperty((int)__VSPROPID.VSPROPID_IsSolutionOpen, out object value));
112 |
113 | return value is bool isSolOpen && isSolOpen;
114 | }
115 |
116 | private void HandleOpenSolution(object sender = null, EventArgs e = null)
117 | {
118 | var sln = Host.Instance.Solution2;
119 | var projects = Host.Instance.DTE.GetSolutionProjects()
120 | .Where(
121 | p =>
122 | !string.IsNullOrWhiteSpace(p.FileName) &&
123 | Common.SupportedProjectTypes.Any(
124 | t => p.FileName.EndsWith(t, StringComparison.OrdinalIgnoreCase)))
125 | .ToList();
126 | var sp = new SolutionProperties
127 | {
128 | Projects = projects
129 | };
130 | SolutionDataCache.Instance.AddOrUpdate(sln.FileName, sp, (k, v) =>
131 | {
132 | v = sp;
133 | return v;
134 | });
135 | }
136 |
137 |
138 | #endregion
139 |
140 | public static void RedirectAssembly(string shortName, Version targetVersion, string publicKeyToken)
141 | {
142 | ResolveEventHandler handler = null;
143 |
144 | handler = (sender, args) => {
145 | // Use latest strong name & version when trying to load SDK assemblies
146 | var requestedAssembly = new AssemblyName(args.Name);
147 | if (requestedAssembly.Name != shortName)
148 | return null;
149 |
150 | Debug.WriteLine("Redirecting assembly load of " + args.Name
151 | + ",\tloaded by " + (args.RequestingAssembly == null ? "(unknown)" : args.RequestingAssembly.FullName));
152 |
153 | requestedAssembly.Version = targetVersion;
154 | requestedAssembly.SetPublicKeyToken(new AssemblyName(shortName + ", PublicKeyToken=" + publicKeyToken).GetPublicKeyToken());
155 | requestedAssembly.CultureInfo = CultureInfo.InvariantCulture;
156 |
157 | AppDomain.CurrentDomain.AssemblyResolve -= handler;
158 |
159 | return Assembly.Load(requestedAssembly);
160 | };
161 | AppDomain.CurrentDomain.AssemblyResolve += handler;
162 | }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------