Initial commit

This commit is contained in:
Holger Boerchers 2018-03-21 20:07:40 +01:00
commit d383f0ef1c
77 changed files with 7050 additions and 0 deletions

116
.gitignore vendored Normal file
View File

@ -0,0 +1,116 @@
# ---> C Sharp
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
[Bb]in/
[Oo]bj/
# mstest test results
TestResults
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.log
*.vspscc
*.vssscc
.builds
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish
# Publish Web Output
*.Publish.xml
# NuGet Packages Directory
packages
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
[Bb]in
[Oo]bj
sql
TestResults
[Tt]est[Rr]esult*
*.Cache
ClientBin
[Ss]tyle[Cc]op.*
~$*
*.dbmdl
Generated_Code #added for RIA/Silverlight projects
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
/App.exe
/AppStub.exe
/AppStub.exe.config
.vs
/Katteker/Creator/SetupTmpl.cs
/Creator/*.DotSettings

6
AppStub/App.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
</configuration>

53
AppStub/AppStub.csproj Normal file
View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E746AE0F-BEEA-4C18-9ED8-2E708ED00A5B}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>AppStub</RootNamespace>
<AssemblyName>AppStub</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Katteker\SemanticVersion.cs">
<Link>SemanticVersion.cs</Link>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

78
AppStub/Program.cs Normal file
View File

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using Katteker;
namespace AppStub
{
internal static class Program
{
[STAThread]
private static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
try
{
var commandline = string.Join(" ", args);
var location = new FileInfo(Assembly.GetExecutingAssembly().Location);
var directory = location.Directory;
if (directory == null) return;
var files = directory.EnumerateFiles(location.Name, SearchOption.AllDirectories)
.Where(x => x.Directory?.Name.StartsWith("app-") == true);
var entries = new SortedList<SemanticVersion, FileInfo>();
foreach (var file in files)
{
var version = SemanticVersion.TryParse(file.Directory?.Name.Replace("app-", ""), out var value)
? value
: new SemanticVersion(0);
entries.Add(version, file);
}
var latestVersion = entries.LastOrDefault().Value;
if (latestVersion == null) throw new FileNotFoundException();
DeleteOldVersionDirectories(entries.Where(x => x.Value != latestVersion));
using (var process = new Process())
{
process.StartInfo =
new ProcessStartInfo(latestVersion.FullName)
{
Arguments = commandline,
WorkingDirectory = latestVersion.DirectoryName ?? Assembly.GetExecutingAssembly().Location,
UseShellExecute = false
};
process.Start();
process.WaitForExit();
}
}
catch (Exception e)
{
MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private static void DeleteOldVersionDirectories(IEnumerable<KeyValuePair<SemanticVersion, FileInfo>> dirEntries)
{
#if !DEBUG
foreach (var directoryInfo in dirEntries)
{
try
{
directoryInfo.Value.Directory?.Delete(true);
}
catch
{
// silently ignore
}
}
#endif
}
}
}

View File

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("AppStub")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AppStub")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("e746ae0f-beea-4c18-9ed8-2e708ed00a5b")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

268
AppStub/SemanticVersion.cs Normal file
View File

@ -0,0 +1,268 @@
using System;
using System.Text.RegularExpressions;
namespace AppStub
{
/// <summary>
/// A hybrid implementation of SemVer that supports semantic versioning as described at http://semver.org while not
/// strictly enforcing it to
/// allow older 4-digit versioning schemes to continue working.
/// </summary>
[Serializable]
//[TypeConverter(typeof(SemanticVersionTypeConverter))]
public sealed class SemanticVersion : IComparable, IComparable<SemanticVersion>, IEquatable<SemanticVersion>
{
private const RegexOptions Flags =
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
private static readonly Regex SemanticVersionRegex =
new Regex(@"^(?<Version>\d+(\s*\.\s*\d+){0,3})(?<Release>-[a-z][0-9a-z-]*)?$", Flags);
private static readonly Regex StrictSemanticVersionRegex =
new Regex(@"^(?<Version>\d+(\.\d+){2})(?<Release>-[a-z][0-9a-z-]*)?$", Flags);
private readonly string _originalString;
public SemanticVersion(string version)
: this(Parse(version))
{
// The constructor normalizes the version string so that it we do not need to normalize it every time we need to operate on it.
// The original string represents the original form in which the version is represented to be used when printing.
_originalString = version;
}
public SemanticVersion(int major, int minor, int build, int revision)
: this(new Version(major, minor, build, revision))
{
}
public SemanticVersion(int major, int minor, int build, string specialVersion)
: this(new Version(major, minor, build), specialVersion)
{
}
public SemanticVersion(Version version)
: this(version, string.Empty)
{
}
public SemanticVersion(Version version, string specialVersion)
: this(version, specialVersion, null)
{
}
private SemanticVersion(Version version, string specialVersion, string originalString)
{
if (version == null)
throw new ArgumentNullException(nameof(version));
Version = NormalizeVersionValue(version);
SpecialVersion = specialVersion ?? string.Empty;
_originalString = string.IsNullOrEmpty(originalString)
? version + (!string.IsNullOrEmpty(specialVersion) ? '-' + specialVersion : null)
: originalString;
}
internal SemanticVersion(SemanticVersion semVer)
{
_originalString = semVer.ToString();
Version = semVer.Version;
SpecialVersion = semVer.SpecialVersion;
}
/// <summary>
/// Gets the normalized version portion.
/// </summary>
public Version Version { get; }
/// <summary>
/// Gets the optional special version.
/// </summary>
public string SpecialVersion { get; }
public int CompareTo(object obj)
{
if (ReferenceEquals(obj, null))
return 1;
var other = obj as SemanticVersion;
if (other == null)
throw new ArgumentException("Type Must Be A Semantic Version", nameof(obj));
return CompareTo(other);
}
public int CompareTo(SemanticVersion other)
{
if (ReferenceEquals(other, null))
return 1;
var result = Version.CompareTo(other.Version);
if (result != 0)
return result;
var empty = string.IsNullOrEmpty(SpecialVersion);
var otherEmpty = string.IsNullOrEmpty(other.SpecialVersion);
if (empty && otherEmpty)
return 0;
if (empty)
return 1;
if (otherEmpty)
return -1;
return StringComparer.OrdinalIgnoreCase.Compare(SpecialVersion, other.SpecialVersion);
}
public bool Equals(SemanticVersion other)
{
return !ReferenceEquals(null, other) &&
Version.Equals(other.Version) &&
SpecialVersion.Equals(other.SpecialVersion, StringComparison.OrdinalIgnoreCase);
}
public string[] GetOriginalVersionComponents()
{
if (!string.IsNullOrEmpty(_originalString))
{
// search the start of the SpecialVersion part, if any
var dashIndex = _originalString.IndexOf('-');
var original = dashIndex != -1 ? _originalString.Substring(0, dashIndex) : _originalString;
return SplitAndPadVersionString(original);
}
return SplitAndPadVersionString(Version.ToString());
}
private static string[] SplitAndPadVersionString(string version)
{
var a = version.Split('.');
if (a.Length == 4)
{
return a;
}
// if 'a' has less than 4 elements, we pad the '0' at the end
// to make it 4.
var b = new string[4] {"0", "0", "0", "0"};
Array.Copy(a, 0, b, 0, a.Length);
return b;
}
/// <summary>
/// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an
/// optional special version.
/// </summary>
public static SemanticVersion Parse(string version)
{
if (string.IsNullOrEmpty(version))
throw new ArgumentException(nameof(version));
if (!TryParse(version, out var semVer))
throw new ArgumentException("Invalid Version String", nameof(version));
return semVer;
}
/// <summary>
/// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an
/// optional special version.
/// </summary>
public static bool TryParse(string version, out SemanticVersion value)
{
return TryParseInternal(version, SemanticVersionRegex, out value);
}
/// <summary>
/// Parses a version string using strict semantic versioning rules that allows exactly 3 components and an optional
/// special version.
/// </summary>
public static bool TryParseStrict(string version, out SemanticVersion value)
{
return TryParseInternal(version, StrictSemanticVersionRegex, out value);
}
private static bool TryParseInternal(string version, Regex regex, out SemanticVersion semVer)
{
semVer = null;
if (string.IsNullOrEmpty(version))
return false;
var match = regex.Match(version.Trim());
if (!match.Success || !Version.TryParse(match.Groups["Version"].Value, out var versionValue))
return false;
semVer = new SemanticVersion(NormalizeVersionValue(versionValue),
match.Groups["Release"].Value.TrimStart('-'), version.Replace(" ", ""));
return true;
}
/// <summary>
/// Attempts to parse the version token as a SemanticVersion.
/// </summary>
/// <returns>An instance of SemanticVersion if it parses correctly, null otherwise.</returns>
public static SemanticVersion ParseOptionalVersion(string version)
{
TryParse(version, out var semVer);
return semVer;
}
private static Version NormalizeVersionValue(Version version)
{
return new Version(version.Major,
version.Minor,
Math.Max(version.Build, 0),
Math.Max(version.Revision, 0));
}
public static bool operator ==(SemanticVersion version1, SemanticVersion version2)
{
if (ReferenceEquals(version1, null))
return ReferenceEquals(version2, null);
return version1.Equals(version2);
}
public static bool operator !=(SemanticVersion version1, SemanticVersion version2)
{
return !(version1 == version2);
}
public static bool operator <(SemanticVersion version1, SemanticVersion version2)
{
if (version1 == null)
throw new ArgumentNullException(nameof(version1));
return version1.CompareTo(version2) < 0;
}
public static bool operator <=(SemanticVersion version1, SemanticVersion version2)
{
return version1 == version2 || version1 < version2;
}
public static bool operator >(SemanticVersion version1, SemanticVersion version2)
{
if (version1 == null)
throw new ArgumentNullException(nameof(version1));
return version2 < version1;
}
public static bool operator >=(SemanticVersion version1, SemanticVersion version2)
{
return version1 == version2 || version1 > version2;
}
public override string ToString()
{
return _originalString;
}
public override bool Equals(object obj)
{
var semVer = obj as SemanticVersion;
return !ReferenceEquals(null, semVer) && Equals(semVer);
}
public override int GetHashCode()
{
var hashCode = Version.GetHashCode();
if (SpecialVersion != null)
hashCode = hashCode * 4567 + SpecialVersion.GetHashCode();
return hashCode;
}
}
}

6
AppStubEx/App.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup>
</configuration>

19
AppStubEx/AppEntry.cs Normal file
View File

@ -0,0 +1,19 @@
using System;
using System.IO;
namespace Katteker.AppStub
{
public class AppEntry : IComparable<AppEntry>
{
public AppEntry(DirectoryInfo info)
{
DirInfo = info;
Version = SemanticVersion.Parse(info.Name.Replace("app-", ""));
}
public SemanticVersion Version { get; }
public DirectoryInfo DirInfo { get; }
public int CompareTo(AppEntry other) => Version.CompareTo(other.Version);
}
}

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{AF7579CC-C0B2-4E5A-B052-00D2991DC715}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>Katteker.AppStubEx</RootNamespace>
<AssemblyName>AppStubEx</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AppEntry.cs" />
<Compile Include="IniFile.cs" />
<Compile Include="InstallerUx.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="InstallerUx.designer.cs">
<DependentUpon>InstallerUx.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SemanticVersion.cs" />
<Compile Include="Taskbar.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="InstallerUx.resx">
<DependentUpon>InstallerUx.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

50
AppStubEx/IniFile.cs Normal file
View File

@ -0,0 +1,50 @@
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
namespace Katteker.AppStub
{
public class IniFile // revision 11
{
private readonly FileInfo _path;
private readonly string _exe = Assembly.GetExecutingAssembly().GetName().Name;
[DllImport("kernel32", CharSet = CharSet.Unicode)]
private static extern long WritePrivateProfileString(string section, string key, string value, string filePath);
[DllImport("kernel32", CharSet = CharSet.Unicode)]
private static extern int GetPrivateProfileString(string section, string key, string Default, StringBuilder retVal, int size, string filePath);
public IniFile(string iniPath = null)
{
_path = new FileInfo(iniPath ?? _exe + ".ini");
}
public string Read(string key, string section = null)
{
var retVal = new StringBuilder(255);
GetPrivateProfileString(section ?? _exe, key, "", retVal, 255, _path.FullName);
return retVal.ToString();
}
public void Write(string key, string value, string section = null)
{
WritePrivateProfileString(section ?? _exe, key, value, _path.FullName);
}
public void DeleteKey(string key, string section = null)
{
Write(key, null, section ?? _exe);
}
public void DeleteSection(string section = null)
{
Write(null, null, section ?? _exe);
}
public bool Exists => _path.Exists;
public bool KeyExists(string key, string section = null) => Read(key, section).Length > 0;
}
}

73
AppStubEx/InstallerUx.cs Normal file
View File

@ -0,0 +1,73 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Katteker.AppStub
{
public sealed partial class InstallerUx : Form
{
private int _autoCloseCountdown = 10;
public InstallerUx(string appName)
{
Font = SystemFonts.MessageBoxFont;
InitializeComponent();
SetLocationAndShow();
this.appName.Text = appName;
}
private void SetLocationAndShow()
{
var taskBar = new Taskbar();
switch (taskBar.Position)
{
case TaskbarPosition.Unknown:
break;
case TaskbarPosition.Left:
Location = new Point(taskBar.Bounds.Left + taskBar.Bounds.Width, taskBar.Bounds.Bottom - Size.Height);
break;
case TaskbarPosition.Top:
Location = new Point(taskBar.Bounds.Right - Size.Width, taskBar.Bounds.Bottom);
break;
case TaskbarPosition.Right:
Location = new Point(taskBar.Bounds.Left - Size.Width, taskBar.Bounds.Bottom - Size.Height);
break;
case TaskbarPosition.Bottom:
Location = new Point(taskBar.Bounds.Right - Size.Width, taskBar.Bounds.Top - Size.Height);
break;
default:
break;
}
Show();
}
private void closeBtn_Click(object sender, EventArgs e)
{
Close();
}
private void installBtn_Click(object sender, EventArgs e)
{
_autoCloseCountdown = 0;
MessageBox.Show("Hallo Welt");
}
private void autoCloseTimer_Tick(object sender, EventArgs e)
{
switch (_autoCloseCountdown)
{
case 1:
Close();
break;
case 0:
countDownLbl.Text = string.Empty;
autoCloseTimer.Enabled = false;
break;
default:
countDownLbl.Tag = --_autoCloseCountdown;
countDownLbl.Text = _autoCloseCountdown.ToString();
break;
}
}
}
}

149
AppStubEx/InstallerUx.designer.cs generated Normal file
View File

@ -0,0 +1,149 @@
namespace Katteker.AppStub
{
sealed partial class InstallerUx
{
/// <summary>
/// Erforderliche Designervariable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Verwendete Ressourcen bereinigen.
/// </summary>
/// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Vom Windows Form-Designer generierter Code
/// <summary>
/// Erforderliche Methode für die Designerunterstützung.
/// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.closeBtn = new System.Windows.Forms.Button();
this.installBtn = new System.Windows.Forms.Button();
this.messageLbl = new System.Windows.Forms.Label();
this.appName = new System.Windows.Forms.Label();
this.countDownLbl = new System.Windows.Forms.Label();
this.autoCloseTimer = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout();
//
// closeBtn
//
this.closeBtn.AutoSize = true;
this.closeBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.closeBtn.FlatAppearance.BorderSize = 0;
this.closeBtn.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(122)))), ((int)(((byte)(204)))));
this.closeBtn.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(63)))), ((int)(((byte)(65)))));
this.closeBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.closeBtn.Font = new System.Drawing.Font("Marlett", 10F);
this.closeBtn.Location = new System.Drawing.Point(249, 0);
this.closeBtn.Margin = new System.Windows.Forms.Padding(0);
this.closeBtn.Name = "closeBtn";
this.closeBtn.Size = new System.Drawing.Size(31, 26);
this.closeBtn.TabIndex = 1;
this.closeBtn.TabStop = false;
this.closeBtn.Text = "r";
this.closeBtn.UseVisualStyleBackColor = true;
this.closeBtn.Click += new System.EventHandler(this.closeBtn_Click);
//
// installBtn
//
this.installBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.installBtn.FlatAppearance.BorderSize = 0;
this.installBtn.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(122)))), ((int)(((byte)(204)))));
this.installBtn.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(63)))), ((int)(((byte)(65)))));
this.installBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.installBtn.Location = new System.Drawing.Point(192, 59);
this.installBtn.Margin = new System.Windows.Forms.Padding(0);
this.installBtn.Name = "installBtn";
this.installBtn.Size = new System.Drawing.Size(77, 30);
this.installBtn.TabIndex = 2;
this.installBtn.TabStop = false;
this.installBtn.Text = "Install";
this.installBtn.UseVisualStyleBackColor = true;
this.installBtn.Click += new System.EventHandler(this.installBtn_Click);
//
// messageLbl
//
this.messageLbl.AutoSize = true;
this.messageLbl.Location = new System.Drawing.Point(11, 68);
this.messageLbl.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.messageLbl.Name = "messageLbl";
this.messageLbl.Size = new System.Drawing.Size(93, 13);
this.messageLbl.TabIndex = 3;
this.messageLbl.Text = "Upgrade available";
//
// appName
//
this.appName.AutoSize = true;
this.appName.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.appName.Location = new System.Drawing.Point(13, 13);
this.appName.Name = "appName";
this.appName.Size = new System.Drawing.Size(80, 20);
this.appName.TabIndex = 4;
this.appName.Text = "AppName";
//
// countDownLbl
//
this.countDownLbl.AutoSize = true;
this.countDownLbl.BackColor = System.Drawing.Color.Transparent;
this.countDownLbl.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.countDownLbl.ForeColor = System.Drawing.SystemColors.ControlDark;
this.countDownLbl.Location = new System.Drawing.Point(229, 5);
this.countDownLbl.Name = "countDownLbl";
this.countDownLbl.Size = new System.Drawing.Size(24, 17);
this.countDownLbl.TabIndex = 5;
this.countDownLbl.Tag = 10;
this.countDownLbl.Text = "10";
this.countDownLbl.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// autoCloseTimer
//
this.autoCloseTimer.Enabled = true;
this.autoCloseTimer.Interval = 1000;
this.autoCloseTimer.Tick += new System.EventHandler(this.autoCloseTimer_Tick);
//
// InstallerUx
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(45)))), ((int)(((byte)(45)))), ((int)(((byte)(48)))));
this.ClientSize = new System.Drawing.Size(278, 98);
this.ControlBox = false;
this.Controls.Add(this.countDownLbl);
this.Controls.Add(this.appName);
this.Controls.Add(this.messageLbl);
this.Controls.Add(this.installBtn);
this.Controls.Add(this.closeBtn);
this.ForeColor = System.Drawing.SystemColors.Control;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Location = new System.Drawing.Point(30, 0);
this.Name = "InstallerUx";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.TopMost = true;
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button closeBtn;
private System.Windows.Forms.Button installBtn;
private System.Windows.Forms.Label messageLbl;
private System.Windows.Forms.Label appName;
private System.Windows.Forms.Label countDownLbl;
private System.Windows.Forms.Timer autoCloseTimer;
}
}

126
AppStubEx/InstallerUx.resx Normal file
View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="autoCloseTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>167, 17</value>
</metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>87</value>
</metadata>
</root>

102
AppStubEx/Program.cs Normal file
View File

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Katteker.AppStub
{
internal class Program
{
private Program()
{
CurrentDir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);
ConfigFile = new IniFile(Path.Combine(CurrentDir.FullName, "app.ini"));
}
private DirectoryInfo CurrentDir { get; }
private IniFile ConfigFile { get; }
private Process MainProcess { get; set; }
[STAThread]
private static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var p = new Program();
p.Run(args);
}
private void Run(string[] args)
{
StartExecutable(args);
StartUpdateInfo();
}
private static void StartUpdateInfo()
{
Application.Run(new InstallerUx("TestProgramm"));
}
private void StartExecutable(string[] args)
{
var dirEntries = CurrentDir.EnumerateDirectories().Where(x => x.Name.StartsWith("app-"))
.Select(x => new AppEntry(x)).ToList();
var latestVersion = dirEntries.OrderByDescending(x => x).FirstOrDefault();
if (latestVersion != null && ConfigFile.Exists)
{
try
{
var executable = ConfigFile.Read("Executable", "Main");
var a = args.Aggregate(string.Empty, (current, s) => current + s + " ").TrimEnd(' ');
MainProcess = new Process
{
StartInfo =
new ProcessStartInfo($"{latestVersion.DirInfo.FullName}\\{executable}")
{
Arguments = a,
WorkingDirectory = latestVersion.DirInfo.FullName,
UseShellExecute = false
}
};
MainProcess.Start();
Task.Run(() =>
DeleteOldVersionDirectories(dirEntries.Where(x =>
x.DirInfo.Name != latestVersion.DirInfo.Name)));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
MessageBox.Show("Error, File not found", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private static void DeleteOldVersionDirectories(IEnumerable<AppEntry> dirEntries)
{
#if !DEBUG
foreach (var directoryInfo in dirEntries)
{
try
{
directoryInfo.DirInfo.Delete(true);
}
catch (Exception)
{
// silently ignore
}
}
#endif
}
}
}

View File

@ -0,0 +1,42 @@
using System.Reflection;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind.
//[assembly: AssemblyTitle("AppStub")]
//[assembly: AssemblyDescription("")]
//[assembly: AssemblyConfiguration("")]
//[assembly: AssemblyCompany("")]
//[assembly: AssemblyProduct("AppStub")]
//[assembly: AssemblyCopyright("Copyright © 2016")]
//[assembly: AssemblyTrademark("")]
//[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("af7579cc-c0b2-4e5a-b052-00d2991dc715")]
[assembly: AssemblyTitle("Katteker.AppStubEx")]
[assembly: AssemblyDescription("Katteker.AppStubEx")]
[assembly: AssemblyCompany("Holger Börchers")]
[assembly: AssemblyProduct("Katteker.AppStubEx")]
[assembly: AssemblyCopyright("Holger Börchers")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden.
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
//[assembly: AssemblyVersion("1.0.0.0")]
//[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,268 @@
using System;
using System.Text.RegularExpressions;
namespace Katteker.AppStub
{
/// <summary>
/// A hybrid implementation of SemVer that supports semantic versioning as described at http://semver.org while not
/// strictly enforcing it to
/// allow older 4-digit versioning schemes to continue working.
/// </summary>
[Serializable]
//[TypeConverter(typeof(SemanticVersionTypeConverter))]
public sealed class SemanticVersion : IComparable, IComparable<SemanticVersion>, IEquatable<SemanticVersion>
{
private const RegexOptions Flags =
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
private static readonly Regex SemanticVersionRegex =
new Regex(@"^(?<Version>\d+(\s*\.\s*\d+){0,3})(?<Release>-[a-z][0-9a-z-]*)?$", Flags);
private static readonly Regex StrictSemanticVersionRegex =
new Regex(@"^(?<Version>\d+(\.\d+){2})(?<Release>-[a-z][0-9a-z-]*)?$", Flags);
private readonly string _originalString;
public SemanticVersion(string version)
: this(Parse(version))
{
// The constructor normalizes the version string so that it we do not need to normalize it every time we need to operate on it.
// The original string represents the original form in which the version is represented to be used when printing.
_originalString = version;
}
public SemanticVersion(int major, int minor, int build, int revision)
: this(new Version(major, minor, build, revision))
{
}
public SemanticVersion(int major, int minor, int build, string specialVersion)
: this(new Version(major, minor, build), specialVersion)
{
}
public SemanticVersion(Version version)
: this(version, string.Empty)
{
}
public SemanticVersion(Version version, string specialVersion)
: this(version, specialVersion, null)
{
}
private SemanticVersion(Version version, string specialVersion, string originalString)
{
if (version == null)
throw new ArgumentNullException(nameof(version));
Version = NormalizeVersionValue(version);
SpecialVersion = specialVersion ?? string.Empty;
_originalString = string.IsNullOrEmpty(originalString)
? version + (!string.IsNullOrEmpty(specialVersion) ? '-' + specialVersion : null)
: originalString;
}
internal SemanticVersion(SemanticVersion semVer)
{
_originalString = semVer.ToString();
Version = semVer.Version;
SpecialVersion = semVer.SpecialVersion;
}
/// <summary>
/// Gets the normalized version portion.
/// </summary>
public Version Version { get; }
/// <summary>
/// Gets the optional special version.
/// </summary>
public string SpecialVersion { get; }
public int CompareTo(object obj)
{
if (ReferenceEquals(obj, null))
return 1;
var other = obj as SemanticVersion;
if (other == null)
throw new ArgumentException("Type Must Be A Semantic Version", nameof(obj));
return CompareTo(other);
}
public int CompareTo(SemanticVersion other)
{
if (ReferenceEquals(other, null))
return 1;
var result = Version.CompareTo(other.Version);
if (result != 0)
return result;
var empty = string.IsNullOrEmpty(SpecialVersion);
var otherEmpty = string.IsNullOrEmpty(other.SpecialVersion);
if (empty && otherEmpty)
return 0;
if (empty)
return 1;
if (otherEmpty)
return -1;
return StringComparer.OrdinalIgnoreCase.Compare(SpecialVersion, other.SpecialVersion);
}
public bool Equals(SemanticVersion other)
{
return !ReferenceEquals(null, other) &&
Version.Equals(other.Version) &&
SpecialVersion.Equals(other.SpecialVersion, StringComparison.OrdinalIgnoreCase);
}
public string[] GetOriginalVersionComponents()
{
if (!string.IsNullOrEmpty(_originalString))
{
// search the start of the SpecialVersion part, if any
var dashIndex = _originalString.IndexOf('-');
var original = dashIndex != -1 ? _originalString.Substring(0, dashIndex) : _originalString;
return SplitAndPadVersionString(original);
}
return SplitAndPadVersionString(Version.ToString());
}
private static string[] SplitAndPadVersionString(string version)
{
var a = version.Split('.');
if (a.Length == 4)
{
return a;
}
// if 'a' has less than 4 elements, we pad the '0' at the end
// to make it 4.
var b = new string[4] {"0", "0", "0", "0"};
Array.Copy(a, 0, b, 0, a.Length);
return b;
}
/// <summary>
/// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an
/// optional special version.
/// </summary>
public static SemanticVersion Parse(string version)
{
if (string.IsNullOrEmpty(version))
throw new ArgumentException(nameof(version));
if (!TryParse(version, out var semVer))
throw new ArgumentException("Invalid Version String", nameof(version));
return semVer;
}
/// <summary>
/// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an
/// optional special version.
/// </summary>
public static bool TryParse(string version, out SemanticVersion value)
{
return TryParseInternal(version, SemanticVersionRegex, out value);
}
/// <summary>
/// Parses a version string using strict semantic versioning rules that allows exactly 3 components and an optional
/// special version.
/// </summary>
public static bool TryParseStrict(string version, out SemanticVersion value)
{
return TryParseInternal(version, StrictSemanticVersionRegex, out value);
}
private static bool TryParseInternal(string version, Regex regex, out SemanticVersion semVer)
{
semVer = null;
if (string.IsNullOrEmpty(version))
return false;
var match = regex.Match(version.Trim());
if (!match.Success || !Version.TryParse(match.Groups["Version"].Value, out var versionValue))
return false;
semVer = new SemanticVersion(NormalizeVersionValue(versionValue),
match.Groups["Release"].Value.TrimStart('-'), version.Replace(" ", ""));
return true;
}
/// <summary>
/// Attempts to parse the version token as a SemanticVersion.
/// </summary>
/// <returns>An instance of SemanticVersion if it parses correctly, null otherwise.</returns>
public static SemanticVersion ParseOptionalVersion(string version)
{
TryParse(version, out var semVer);
return semVer;
}
private static Version NormalizeVersionValue(Version version)
{
return new Version(version.Major,
version.Minor,
Math.Max(version.Build, 0),
Math.Max(version.Revision, 0));
}
public static bool operator ==(SemanticVersion version1, SemanticVersion version2)
{
if (ReferenceEquals(version1, null))
return ReferenceEquals(version2, null);
return version1.Equals(version2);
}
public static bool operator !=(SemanticVersion version1, SemanticVersion version2)
{
return !(version1 == version2);
}
public static bool operator <(SemanticVersion version1, SemanticVersion version2)
{
if (version1 == null)
throw new ArgumentNullException(nameof(version1));
return version1.CompareTo(version2) < 0;
}
public static bool operator <=(SemanticVersion version1, SemanticVersion version2)
{
return version1 == version2 || version1 < version2;
}
public static bool operator >(SemanticVersion version1, SemanticVersion version2)
{
if (version1 == null)
throw new ArgumentNullException(nameof(version1));
return version2 < version1;
}
public static bool operator >=(SemanticVersion version1, SemanticVersion version2)
{
return version1 == version2 || version1 > version2;
}
public override string ToString()
{
return _originalString;
}
public override bool Equals(object obj)
{
var semVer = obj as SemanticVersion;
return !ReferenceEquals(null, semVer) && Equals(semVer);
}
public override int GetHashCode()
{
var hashCode = Version.GetHashCode();
if (SpecialVersion != null)
hashCode = hashCode * 4567 + SpecialVersion.GetHashCode();
return hashCode;
}
}
}

117
AppStubEx/Taskbar.cs Normal file
View File

@ -0,0 +1,117 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace Katteker.AppStub
{
public enum TaskbarPosition
{
Unknown = -1,
Left,
Top,
Right,
Bottom
}
public sealed class Taskbar
{
private const string ClassName = "Shell_TrayWnd";
public Taskbar()
{
var taskbarHandle = User32.FindWindow(ClassName, null);
var data = new APPBARDATA
{
cbSize = (uint) Marshal.SizeOf(typeof(APPBARDATA)),
hWnd = taskbarHandle
};
var result = Shell32.SHAppBarMessage(ABM.GetTaskbarPos, ref data);
if (result == IntPtr.Zero)
throw new InvalidOperationException();
Position = (TaskbarPosition) data.uEdge;
Bounds = Rectangle.FromLTRB(data.rc.left, data.rc.top, data.rc.right, data.rc.bottom);
data.cbSize = (uint) Marshal.SizeOf(typeof(APPBARDATA));
result = Shell32.SHAppBarMessage(ABM.GetState, ref data);
var state = result.ToInt32();
AlwaysOnTop = (state & ABS.AlwaysOnTop) == ABS.AlwaysOnTop;
AutoHide = (state & ABS.Autohide) == ABS.Autohide;
}
public Rectangle Bounds { get; }
public TaskbarPosition Position { get; private set; }
public Point Location => Bounds.Location;
public Size Size => Bounds.Size;
//Always returns false under Windows 7
public bool AlwaysOnTop { get; private set; }
public bool AutoHide { get; private set; }
}
public enum ABM : uint
{
New = 0x00000000,
Remove = 0x00000001,
QueryPos = 0x00000002,
SetPos = 0x00000003,
GetState = 0x00000004,
GetTaskbarPos = 0x00000005,
Activate = 0x00000006,
GetAutoHideBar = 0x00000007,
SetAutoHideBar = 0x00000008,
WindowPosChanged = 0x00000009,
SetState = 0x0000000A
}
public enum ABE : uint
{
Left = 0,
Top = 1,
Right = 2,
Bottom = 3
}
public static class ABS
{
public const int Autohide = 0x0000001;
public const int AlwaysOnTop = 0x0000002;
}
public static class Shell32
{
[DllImport("shell32.dll", SetLastError = true)]
public static extern IntPtr SHAppBarMessage(ABM dwMessage, [In] ref APPBARDATA pData);
}
public static class User32
{
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}
[StructLayout(LayoutKind.Sequential)]
public struct APPBARDATA
{
public uint cbSize;
public IntPtr hWnd;
public uint uCallbackMessage;
public ABE uEdge;
public RECT rc;
public int lParam;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
}

26
Creator/App.config Normal file
View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="11.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="NuGet.Frameworks" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.5.0.4" newVersion="4.5.0.4" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="NuGet.Versioning" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.5.0.4" newVersion="4.5.0.4" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="NuGet.Packaging" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.5.0.4" newVersion="4.5.0.4" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -0,0 +1,62 @@
using System.ComponentModel;
using System.IO;
namespace KattekerCreator
{
public class ApplicationArguments
{
private string _changeLog;
private string _channel;
private string _outputDir;
private string _programFile;
private string _publishDir;
[DisplayName("Program")]
[Description("Path to the program file")]
public string ProgramFile
{
get => _programFile;
set => SetPropertyIfNotDefault(ref _programFile, Path.GetFullPath(value));
}
[DisplayName("Changelog")]
[Description("Filename of the changelog file")]
public string ChangeLog
{
get => _changeLog;
set => SetPropertyIfNotDefault(ref _changeLog, value);
}
[DisplayName("Channel")]
[Description("Channel of releasing.")]
public string Channel
{
get => _channel;
set => SetPropertyIfNotDefault(ref _channel, value);
}
[DisplayName("OutDir")]
[Description("Directory for output")]
public string OutputDir
{
get => _outputDir;
set => SetPropertyIfNotDefault(ref _outputDir, value);
}
[DisplayName("PublishDir")]
[Description("Path for output from the point of view of the application.")]
public string PublishDir
{
get => _publishDir;
set => SetPropertyIfNotDefault(ref _publishDir, value);
}
private static bool SetPropertyIfNotDefault<T>(ref T field, T value)
{
if (Equals(value, field)) return false;
if (!Equals(field, default(T))) return false;
field = value;
return true;
}
}
}

View File

@ -0,0 +1,93 @@
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using Katteker;
using TsudaKageyu;
using Vestris.ResourceLib;
namespace KattekerCreator
{
public class AssemblyFileInfo
{
private readonly string _company;
private readonly string _copyright;
private readonly string _description;
private readonly string _productName;
public AssemblyFileInfo(string fileName, string tempDir)
{
FileInfo = new FileInfo(fileName);
using (var resourceInfo = new ResourceInfo())
{
resourceInfo.Load(fileName);
var versionResource = (VersionResource) resourceInfo[Kernel32.ResourceTypes.RT_VERSION].First();
var stringFileInfo = ((StringFileInfo) versionResource[nameof(StringFileInfo)]).Strings.FirstOrDefault()
.Value;
AssemblyIconPath = GetAssemblyIcon(fileName, tempDir);
if (stringFileInfo.Strings.ContainsKey("CompanyName"))
_company = stringFileInfo.Strings["CompanyName"].StringValue;
if (stringFileInfo.Strings.ContainsKey("FileDescription"))
_description = stringFileInfo.Strings["FileDescription"].StringValue;
if (stringFileInfo.Strings.ContainsKey("LegalCopyright"))
_copyright = stringFileInfo.Strings["LegalCopyright"].StringValue;
if (stringFileInfo.Strings.ContainsKey("ProductName"))
_productName = stringFileInfo.Strings["ProductName"].StringValue;
if (!stringFileInfo.Strings.ContainsKey("ProductVersion")) return;
AssemblyVersion = GetSemanticVersion(stringFileInfo.Strings["ProductVersion"].StringValue);
}
}
public string AssemblyIconPath { get; }
public SemanticVersion AssemblyVersion { get; private set; }
public string Company => _company ?? string.Empty;
public string Copyright => _copyright ?? string.Empty;
public string Description => _description ?? string.Empty;
public FileInfo FileInfo { get; }
public string ProductName => _productName ?? Description;
private static string GetAssemblyIcon(string programFile, string tempDir)
{
var applicationIcon = Path.Combine(tempDir, "application.ico");
if (string.IsNullOrWhiteSpace(programFile)) throw new ApplicationException("Program not set.");
if (!File.Exists(programFile)) throw new ApplicationException("Program not found.");
if (File.Exists(applicationIcon)) File.Delete(applicationIcon);
using (var fileStream = new FileStream(applicationIcon, FileMode.CreateNew))
{
try
{
var ie = new IconExtractor(programFile);
using (var icon = ie.GetIcon(0))
{
icon?.Save(fileStream);
}
}
catch (Exception)
{
using (var icon = Icon.ExtractAssociatedIcon(programFile))
{
icon?.Save(fileStream);
}
}
}
return applicationIcon;
}
private static SemanticVersion GetSemanticVersion(string productVersion)
{
if (!SemanticVersion.TryParse(productVersion, out var semanticVersion))
{
Version.TryParse(productVersion, out var version);
return SemanticVersion.Parse(version.ToString(3));
}
return semanticVersion?.Change(build: string.Empty);
}
}
}

51
Creator/Helper/Log.cs Normal file
View File

@ -0,0 +1,51 @@
using System;
namespace KattekerCreator.Helper
{
public static class Log
{
private const string Info = "INFO";
private const string Error = "ERROR";
public static void WriteInfoLine(string text)
{
using (var c = new ConsoleWithOtherColor())
{
c.WriteLine($"{Info} [{DateTime.Now:G}] {text}");
}
}
public static void WriteErrorLine(string text)
{
using (var c = new ConsoleWithOtherColor(ConsoleColor.DarkRed))
{
c.WriteLine($"{Error} [{DateTime.Now:G}] {text}");
}
}
}
public class ConsoleWithOtherColor : IDisposable
{
public ConsoleWithOtherColor()
{
}
private readonly ConsoleColor? _defaultColor;
public ConsoleWithOtherColor(ConsoleColor color)
{
_defaultColor = Console.ForegroundColor;
Console.ForegroundColor = color;
}
public void WriteLine(string text)
{
Console.WriteLine(text);
}
public void Dispose()
{
if (_defaultColor != null) Console.ForegroundColor = _defaultColor.Value;
}
}
}

View File

@ -0,0 +1,166 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
namespace KattekerCreator.Helper
{
public class SimpleCommandLineParser<TObject> where TObject : new()
{
private TObject _parsedObject;
public SimpleCommandLineParser(IEnumerable<string> args)
{
Parse(args);
}
private IDictionary<string, string[]> Arguments { get; } = new Dictionary<string, string[]>();
public bool Contains(string name) => Arguments.ContainsKey(name);
public TObject GetObject()
{
if (!EqualityComparer<TObject>.Default.Equals(_parsedObject, default(TObject)))
return _parsedObject;
_parsedObject = new TObject();
try
{
LoopProperties(pi =>
{
var displayName = GetDisplayName(pi);
if (displayName == null) return;
if (!Arguments.TryGetValue(displayName.ToLowerInvariant(), out var argument)) return;
if (pi.PropertyType == typeof(bool))
{
pi.SetValue(_parsedObject, true);
return;
}
if (pi.PropertyType.IsArray)
pi.SetValue(_parsedObject, argument);
else
pi.SetValue(_parsedObject, argument.FirstOrDefault());
});
}
catch (Exception e)
{
Log.WriteErrorLine("Error parsing commandline arguments: " + e.Message);
ShowHelp();
}
return _parsedObject;
}
public void ShowHelp(TextWriter textWriter = null)
{
if (textWriter == null) textWriter = Console.Out;
var executable = Assembly.GetExecutingAssembly().GetName().Name + ".exe";
var assemblyTitle = ((AssemblyTitleAttribute) Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(AssemblyTitleAttribute), false))?.Title ?? executable;
var assemblyDescription = ((AssemblyDescriptionAttribute) Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(AssemblyDescriptionAttribute), false))?.Description;
textWriter.WriteLine();
textWriter.WriteLine(assemblyTitle);
textWriter.WriteLine();
textWriter.WriteLine(assemblyDescription);
textWriter.WriteLine();
textWriter.WriteLine(executable + " <options>");
textWriter.WriteLine();
var dict = new Dictionary<string, string>();
LoopProperties(pi =>
{
var displayName = GetDisplayName(pi);
if (displayName == null) return;
var description = GetDescription(pi);
if (pi.PropertyType == typeof(bool)) description = "(Switch) " + description;
dict.Add(displayName, description);
});
var longestWord = dict.Keys.Max(x => x.Length);
foreach (var kv in dict)
{
textWriter.Write("-" + kv.Key.PadRight(longestWord + 3, ' '));
textWriter.WriteLine(kv.Value);
}
textWriter.WriteLine();
textWriter.WriteLine("A switch will be set to true, if it will called.");
textWriter.WriteLine($"Example: {executable} <Switch>");
textWriter.WriteLine();
textWriter.WriteLine("A parameter will be set.");
textWriter.WriteLine($"Example: {executable} <Key> \"Value\"");
}
/// <summary>
/// Shows the current set options.
/// </summary>
/// <param name="textWriter">If not set Console.Out will be set.</param>
public void ShowProgramArguments(TextWriter textWriter = null)
{
if (textWriter == null) textWriter = Console.Out;
LoopProperties(pi =>
{
var displayName = GetDisplayName(pi) ?? pi.Name;
var value = pi.GetValue(_parsedObject);
textWriter.Write(displayName.PadRight(20, ' '));
if (value == null)
{
textWriter.WriteLine("<NOT SET>");
}
else
{
if (value.GetType().IsArray)
{
var arrayAsString = ((IEnumerable) value)
.Cast<object>()
.Aggregate(string.Empty, (current, x) => current + x?.ToString() + ", ");
textWriter.WriteLine(arrayAsString);
}
else
{
textWriter.WriteLine(value.ToString());
}
}
});
}
private static TAttribute GetAttribute<TAttribute>(MemberInfo memberInfo) where TAttribute : Attribute =>
(TAttribute) Attribute.GetCustomAttribute(memberInfo, typeof(TAttribute));
private static string GetDescription(MemberInfo memberInfo) => GetAttribute<DescriptionAttribute>(memberInfo)?.Description;
private static string GetDisplayName(MemberInfo memberInfo) => GetAttribute<DisplayNameAttribute>(memberInfo)?.DisplayName;
private static void LoopProperties(Action<PropertyInfo> action)
{
var properties = typeof(TObject).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var propertyInfo in properties)
action.Invoke(propertyInfo);
}
private void Parse(IEnumerable<string> args)
{
var currentName = "";
var values = new List<string>();
foreach (var arg in args)
{
if (arg.StartsWith("-", StringComparison.Ordinal))
{
if (!string.IsNullOrWhiteSpace(currentName))
Arguments[currentName.ToLowerInvariant()] = values.ToArray();
values.Clear();
currentName = arg.Substring(1);
}
else if (string.IsNullOrWhiteSpace(currentName))
{
Arguments[arg] = new string[0];
}
else
{
values.Add(arg);
}
}
if (currentName != "")
Arguments[currentName.ToLowerInvariant()] = values.ToArray();
}
}
}

102
Creator/Helper/Utils.cs Normal file
View File

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Vestris.ResourceLib;
namespace KattekerCreator.Helper
{
public static class Utils
{
public static bool CheckFileExistens(string filePath)
{
if (File.Exists(filePath)) return true;
var file = filePath.Replace(Directory.GetParent(filePath) + "\\", "");
Console.WriteLine("Checking the prerequisites has failed.");
Console.WriteLine($"{file} is missing. Program exits now.");
Console.ReadKey();
return false;
}
public static string CreateTempDirectory()
{
string result;
do
{
result = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "temp", Environment.UserName,
Path.GetRandomFileName());
} while (Directory.Exists(result));
Directory.CreateDirectory(result);
return result;
}
public static bool IsFilesizeWrong(string filename)
{
var file = new FileInfo(filename);
return !file.Exists || file.Length < 524288;
}
public static void CopyResources(string inFile, string outFile)
{
using (var ri = new ResourceInfo())
{
ri.Load(inFile);
var versionResource = (VersionResource) ri[Kernel32.ResourceTypes.RT_VERSION].First();
if (ri.ResourceTypes.Any(x => x.ResourceType == Kernel32.ResourceTypes.RT_GROUP_ICON))
{
var groupIcon = ri[Kernel32.ResourceTypes.RT_GROUP_ICON];
var iconDictionary = groupIcon.FirstOrDefault() as IconDirectoryResource;
iconDictionary?.SaveTo(outFile);
}
versionResource.Language = 0;
versionResource.SaveTo(outFile);
}
}
public static void DeleteTempDir(string tempDir)
{
#if !DEBUG
try
{
Directory.Delete(tempDir, true);
}
catch (Exception)
{
//silently ignore
}
#endif
}
public static IEnumerable<FileInfo> EnumerateFiles(string programFile, IEnumerable<string> userdefinedFileExclusions = null)
{
if (string.IsNullOrWhiteSpace(programFile)) return null;
var directoryName = Path.GetDirectoryName(programFile);
if (directoryName == null) return null;
var files = new DirectoryInfo(directoryName).EnumerateFiles("*.*", SearchOption.AllDirectories);
var filter = FileExclusions(userdefinedFileExclusions).ToArray();
return files.Where(x => !filter.Any(y => x.Name.EndsWith(y, StringComparison.Ordinal)));
}
private static IEnumerable<string> FileExclusions(IEnumerable<string> userdefinedfileExclusions = null)
{
yield return ".pdb";
yield return ".tmp";
yield return ".obj";
yield return ".pch";
yield return ".vshost.exe";
yield return ".vshost.exe.config";
yield return ".vshost.exe.manifest";
yield return "squirrelHelperInfo.json";
yield return "Katteker.config";
if (userdefinedfileExclusions == null) yield break;
foreach (var fileExclusion in userdefinedfileExclusions)
{
yield return fileExclusion;
}
}
}
}

View File

@ -0,0 +1,283 @@
/*
* IconExtractor/IconUtil for .NET
* Copyright (C) 2014 Tsuda Kageyu. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
namespace TsudaKageyu
{
public class IconExtractor
{
////////////////////////////////////////////////////////////////////////
// Constants
// Flags for LoadLibraryEx().
private const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002;
// Resource types for EnumResourceNames().
private readonly static IntPtr RT_ICON = (IntPtr)3;
private readonly static IntPtr RT_GROUP_ICON = (IntPtr)14;
private const int MAX_PATH = 260;
////////////////////////////////////////////////////////////////////////
// Fields
private byte[][] iconData = null; // Binary data of each icon.
////////////////////////////////////////////////////////////////////////
// Public properties
/// <summary>
/// Gets the full path of the associated file.
/// </summary>
public string FileName
{
get;
private set;
}
/// <summary>
/// Gets the count of the icons in the associated file.
/// </summary>
public int Count
{
get { return iconData.Length; }
}
/// <summary>
/// Initializes a new instance of the IconExtractor class from the specified file name.
/// </summary>
/// <param name="fileName">The file to extract icons from.</param>
public IconExtractor(string fileName)
{
Initialize(fileName);
}
/// <summary>
/// Extracts an icon from the file.
/// </summary>
/// <param name="index">Zero based index of the icon to be extracted.</param>
/// <returns>A System.Drawing.Icon object.</returns>
/// <remarks>Always returns new copy of the Icon. It should be disposed by the user.</remarks>
public Icon GetIcon(int index)
{
if (index < 0 || Count <= index)
throw new ArgumentOutOfRangeException("index");
// Create an Icon from the .ico file in memory.
using (var ms = new MemoryStream(iconData[index]))
{
return new Icon(ms);
}
}
/// <summary>
/// Extracts all the icons from the file.
/// </summary>
/// <returns>An array of System.Drawing.Icon objects.</returns>
/// <remarks>Always returns new copies of the Icons. They should be disposed by the user.</remarks>
public Icon[] GetAllIcons()
{
var icons = new List<Icon>();
for (int i = 0; i < Count; ++i)
icons.Add(GetIcon(i));
return icons.ToArray();
}
/// <summary>
/// Save an icon to the specified output Stream.
/// </summary>
/// <param name="index">Zero based index of the icon to be saved.</param>
/// <param name="outputStream">The Stream to save to.</param>
public void Save(int index, Stream outputStream)
{
if (index < 0 || Count <= index)
throw new ArgumentOutOfRangeException("index");
if (outputStream == null)
throw new ArgumentNullException("outputStream");
var data = iconData[index];
outputStream.Write(data, 0, data.Length);
}
private void Initialize(string fileName)
{
if (fileName == null)
throw new ArgumentNullException("fileName");
IntPtr hModule = IntPtr.Zero;
try
{
hModule = NativeMethods.LoadLibraryEx(fileName, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE);
if (hModule == IntPtr.Zero)
throw new Win32Exception();
FileName = GetFileName(hModule);
// Enumerate the icon resource and build .ico files in memory.
var tmpData = new List<byte[]>();
ENUMRESNAMEPROC callback = (h, t, name, l) =>
{
// Refer to the following URL for the data structures used here:
// http://msdn.microsoft.com/en-us/library/ms997538.aspx
// RT_GROUP_ICON resource consists of a GRPICONDIR and GRPICONDIRENTRY's.
var dir = GetDataFromResource(hModule, RT_GROUP_ICON, name);
// Calculate the size of an entire .icon file.
int count = BitConverter.ToUInt16(dir, 4); // GRPICONDIR.idCount
int len = 6 + 16 * count; // sizeof(ICONDIR) + sizeof(ICONDIRENTRY) * count
for (int i = 0; i < count; ++i)
len += BitConverter.ToInt32(dir, 6 + 14 * i + 8); // GRPICONDIRENTRY.dwBytesInRes
using (var dst = new BinaryWriter(new MemoryStream(len)))
{
// Copy GRPICONDIR to ICONDIR.
dst.Write(dir, 0, 6);
int picOffset = 6 + 16 * count; // sizeof(ICONDIR) + sizeof(ICONDIRENTRY) * count
for (int i = 0; i < count; ++i)
{
// Load the picture.
ushort id = BitConverter.ToUInt16(dir, 6 + 14 * i + 12); // GRPICONDIRENTRY.nID
var pic = GetDataFromResource(hModule, RT_ICON, (IntPtr)id);
// Copy GRPICONDIRENTRY to ICONDIRENTRY.
dst.Seek(6 + 16 * i, SeekOrigin.Begin);
dst.Write(dir, 6 + 14 * i, 8); // First 8bytes are identical.
dst.Write(pic.Length); // ICONDIRENTRY.dwBytesInRes
dst.Write(picOffset); // ICONDIRENTRY.dwImageOffset
// Copy a picture.
dst.Seek(picOffset, SeekOrigin.Begin);
dst.Write(pic, 0, pic.Length);
picOffset += pic.Length;
}
tmpData.Add(((MemoryStream)dst.BaseStream).ToArray());
}
return true;
};
NativeMethods.EnumResourceNames(hModule, RT_GROUP_ICON, callback, IntPtr.Zero);
iconData = tmpData.ToArray();
}
finally
{
if (hModule != IntPtr.Zero)
NativeMethods.FreeLibrary(hModule);
}
}
private byte[] GetDataFromResource(IntPtr hModule, IntPtr type, IntPtr name)
{
// Load the binary data from the specified resource.
IntPtr hResInfo = NativeMethods.FindResource(hModule, name, type);
if (hResInfo == IntPtr.Zero)
throw new Win32Exception();
IntPtr hResData = NativeMethods.LoadResource(hModule, hResInfo);
if (hResData == IntPtr.Zero)
throw new Win32Exception();
IntPtr pResData = NativeMethods.LockResource(hResData);
if (pResData == IntPtr.Zero)
throw new Win32Exception();
uint size = NativeMethods.SizeofResource(hModule, hResInfo);
if (size == 0)
throw new Win32Exception();
byte[] buf = new byte[size];
Marshal.Copy(pResData, buf, 0, buf.Length);
return buf;
}
private string GetFileName(IntPtr hModule)
{
// Alternative to GetModuleFileName() for the module loaded with
// LOAD_LIBRARY_AS_DATAFILE option.
// Get the file name in the format like:
// "\\Device\\HarddiskVolume2\\Windows\\System32\\shell32.dll"
string fileName;
{
var buf = new StringBuilder(MAX_PATH);
int len = NativeMethods.GetMappedFileName(
NativeMethods.GetCurrentProcess(), hModule, buf, buf.Capacity);
if (len == 0)
throw new Win32Exception();
fileName = buf.ToString();
}
// Convert the device name to drive name like:
// "C:\\Windows\\System32\\shell32.dll"
for (char c = 'A'; c <= 'Z'; ++c)
{
var drive = c + ":";
var buf = new StringBuilder(MAX_PATH);
int len = NativeMethods.QueryDosDevice(drive, buf, buf.Capacity);
if (len == 0)
continue;
var devPath = buf.ToString();
if (fileName.StartsWith(devPath))
return (drive + fileName.Substring(devPath.Length));
}
return fileName;
}
}
}

View File

@ -0,0 +1,202 @@
/*
* IconExtractor/IconUtil for .NET
* Copyright (C) 2014 Tsuda Kageyu. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
namespace TsudaKageyu
{
public static class IconUtil
{
private delegate byte[] GetIconDataDelegate(Icon icon);
static GetIconDataDelegate getIconData;
static IconUtil()
{
// Create a dynamic method to access Icon.iconData private field.
var dm = new DynamicMethod(
"GetIconData", typeof(byte[]), new Type[] { typeof(Icon) }, typeof(Icon));
var fi = typeof(Icon).GetField(
"iconData", BindingFlags.Instance | BindingFlags.NonPublic);
var gen = dm.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldfld, fi);
gen.Emit(OpCodes.Ret);
getIconData = (GetIconDataDelegate)dm.CreateDelegate(typeof(GetIconDataDelegate));
}
/// <summary>
/// Split an Icon consists of multiple icons into an array of Icon each
/// consists of single icons.
/// </summary>
/// <param name="icon">A System.Drawing.Icon to be split.</param>
/// <returns>An array of System.Drawing.Icon.</returns>
public static Icon[] Split(Icon icon)
{
if (icon == null)
throw new ArgumentNullException("icon");
// Get an .ico file in memory, then split it into separate icons.
var src = GetIconData(icon);
var splitIcons = new List<Icon>();
{
int count = BitConverter.ToUInt16(src, 4);
for (int i = 0; i < count; i++)
{
int length = BitConverter.ToInt32(src, 6 + 16 * i + 8); // ICONDIRENTRY.dwBytesInRes
int offset = BitConverter.ToInt32(src, 6 + 16 * i + 12); // ICONDIRENTRY.dwImageOffset
using (var dst = new BinaryWriter(new MemoryStream(6 + 16 + length)))
{
// Copy ICONDIR and set idCount to 1.
dst.Write(src, 0, 4);
dst.Write((short)1);
// Copy ICONDIRENTRY and set dwImageOffset to 22.
dst.Write(src, 6 + 16 * i, 12); // ICONDIRENTRY except dwImageOffset
dst.Write(22); // ICONDIRENTRY.dwImageOffset
// Copy a picture.
dst.Write(src, offset, length);
// Create an icon from the in-memory file.
dst.BaseStream.Seek(0, SeekOrigin.Begin);
splitIcons.Add(new Icon(dst.BaseStream));
}
}
}
return splitIcons.ToArray();
}
/// <summary>
/// Converts an Icon to a GDI+ Bitmap preserving the transparent area.
/// </summary>
/// <param name="icon">An System.Drawing.Icon to be converted.</param>
/// <returns>A System.Drawing.Bitmap Object.</returns>
public static Bitmap ToBitmap(Icon icon)
{
if (icon == null)
throw new ArgumentNullException("icon");
// Quick workaround: Create an .ico file in memory, then load it as a Bitmap.
using (var ms = new MemoryStream())
{
icon.Save(ms);
using (var bmp = (Bitmap)Image.FromStream(ms))
{
return new Bitmap(bmp);
}
}
}
/// <summary>
/// Gets the bit depth of an Icon.
/// </summary>
/// <param name="icon">An System.Drawing.Icon object.</param>
/// <returns>Bit depth of the icon.</returns>
/// <remarks>
/// This method takes into account the PNG header.
/// If the icon has multiple variations, this method returns the bit
/// depth of the first variation.
/// </remarks>
public static int GetBitCount(Icon icon)
{
if (icon == null)
throw new ArgumentNullException("icon");
// Get an .ico file in memory, then read the header.
var data = GetIconData(icon);
if (data.Length >= 51
&& data[22] == 0x89 && data[23] == 0x50 && data[24] == 0x4e && data[25] == 0x47
&& data[26] == 0x0d && data[27] == 0x0a && data[28] == 0x1a && data[29] == 0x0a
&& data[30] == 0x00 && data[31] == 0x00 && data[32] == 0x00 && data[33] == 0x0d
&& data[34] == 0x49 && data[35] == 0x48 && data[36] == 0x44 && data[37] == 0x52)
{
// The picture is PNG. Read IHDR chunk.
switch (data[47])
{
case 0:
return data[46];
case 2:
return data[46] * 3;
case 3:
return data[46];
case 4:
return data[46] * 2;
case 6:
return data[46] * 4;
default:
// NOP
break;
}
}
else if (data.Length >= 22)
{
// The picture is not PNG. Read ICONDIRENTRY structure.
return BitConverter.ToUInt16(data, 12);
}
throw new ArgumentException("The icon is corrupt. Couldn't read the header.", "icon");
}
private static byte[] GetIconData(Icon icon)
{
var data = getIconData(icon);
if (data != null)
{
return data;
}
else
{
using (var ms = new MemoryStream())
{
icon.Save(ms);
return ms.ToArray();
}
}
}
}
}

View File

@ -0,0 +1,81 @@
/*
* IconExtractor/IconUtil for .NET
* Copyright (C) 2014 Tsuda Kageyu. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
namespace TsudaKageyu
{
internal static class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
[DllImport("kernel32.dll", SetLastError = true)]
[SuppressUnmanagedCodeSecurity]
public static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
public static extern bool EnumResourceNames(IntPtr hModule, IntPtr lpszType, ENUMRESNAMEPROC lpEnumFunc, IntPtr lParam);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
public static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType);
[DllImport("kernel32.dll", SetLastError = true)]
[SuppressUnmanagedCodeSecurity]
public static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);
[DllImport("kernel32.dll", SetLastError = true)]
[SuppressUnmanagedCodeSecurity]
public static extern IntPtr LockResource(IntPtr hResData);
[DllImport("kernel32.dll", SetLastError = true)]
[SuppressUnmanagedCodeSecurity]
public static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo);
[DllImport("kernel32.dll", SetLastError = true)]
[SuppressUnmanagedCodeSecurity]
public static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
public static extern int QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax);
[DllImport("psapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
public static extern int GetMappedFileName(IntPtr hProcess, IntPtr lpv, StringBuilder lpFilename, int nSize);
}
[UnmanagedFunctionPointer(CallingConvention.Winapi, SetLastError = true, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
internal delegate bool ENUMRESNAMEPROC(IntPtr hModule, IntPtr lpszType, IntPtr lpszName, IntPtr lParam);
}

View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{53D065EB-8818-4F60-9EBE-75705E1BB00D}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>KattekerCreator</RootNamespace>
<AssemblyName>KattekerCreator</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x64</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Xml" />
<Reference Include="Vestris.ResourceLib, Version=1.6.422.0, Culture=neutral, PublicKeyToken=ec632d8ba5e5750d, processorArchitecture=MSIL">
<HintPath>..\packages\Vestris.ResourceLib.1.6.422\lib\Vestris.ResourceLib.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="ApplicationArguments.cs" />
<Compile Include="AssemblyFileInfo.cs" />
<Compile Include="Helper\Log.cs" />
<Compile Include="IconExtractor\IconExtractor.cs" />
<Compile Include="IconExtractor\IconUtil.cs" />
<Compile Include="IconExtractor\NativeMethods.cs" />
<Compile Include="PhysicalFile.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Helper\SimpleCommandLineParser.cs" />
<Compile Include="Helper\Utils.cs" />
<Compile Include="SetupTmpl.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>SetupTmpl.tt</DependentUpon>
</Compile>
<Compile Include="SetupTmplCode.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="contrib\LoadingBar_Icon.exe">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="contrib\uninstall.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="SetupTmpl.tt">
<Generator>TextTemplatingFilePreprocessor</Generator>
<LastGenOutput>SetupTmpl.cs</LastGenOutput>
</Content>
</ItemGroup>
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Katteker\Katteker.csproj">
<Project>{a45e1c59-ba9e-452c-a5e2-50de49d53e92}</Project>
<Name>Katteker</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

14
Creator/PhysicalFile.cs Normal file
View File

@ -0,0 +1,14 @@
namespace KattekerCreator
{
public class PhysicalFile
{
public PhysicalFile(string sourcePath, string targetPath)
{
SourcePath = sourcePath;
TargetPath = targetPath;
}
public string SourcePath { get; }
public string TargetPath { get; }
}
}

213
Creator/Program.cs Normal file
View File

@ -0,0 +1,213 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Katteker;
using KattekerCreator.Helper;
namespace KattekerCreator
{
public class Program
{
private const string _executable = @"C:\Program Files (x86)\NSIS\makensis.exe";
private readonly string _baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
private ApplicationArguments _appArguments;
private AssemblyFileInfo _assemblyFileInfo;
private string _tempDir;
private Releases _releases;
private static int Main(string[] args)
{
var program = new Program();
try
{
return program.Run(args);
}
catch (Exception ex)
{
Log.WriteErrorLine(ex.Message);
if (ex.InnerException != null)
Log.WriteErrorLine(ex.InnerException.Message);
#if DEBUG
throw;
#else
return -1;
#endif
}
}
private int Run(string[] args)
{
//Parse App-Arguments
var parser = new SimpleCommandLineParser<ApplicationArguments>(args);
if (args.Length == 0)
{
parser.ShowHelp();
return 1924;
}
_appArguments = parser.GetObject();
//Create tempdir
_tempDir = Utils.CreateTempDirectory();
_releases = new Releases(_appArguments.OutputDir);
parser.ShowProgramArguments();
//Modify AppStub
var appStubFile = ModifyAppStub();
//Acquire infos from Executable.
_assemblyFileInfo = new AssemblyFileInfo(_appArguments.ProgramFile, _tempDir);
var configFile = CreateConfigFile();
//Generate NSIS-Script
var additionalFiles = new[]
{
new PhysicalFile(appStubFile, Path.GetFileName(_appArguments.ProgramFile)),
new PhysicalFile(configFile, Path.Combine($"app-{_assemblyFileInfo.AssemblyVersion}", Path.GetFileName(configFile)))
};
var templateFile = GenerateNsisTemplate(additionalFiles);
//Start makensis.exe
var setupFilePath = CompileSetupScript(templateFile);
//Copy to Output-Folder
if (CopyToOutputFolder(setupFilePath))
{
//Create/Modify RELEASE File
var releaseEntry = AddPackageToReleaseFile(setupFilePath);
//Copy installer as setup.exe
CopyAsSetup(setupFilePath, releaseEntry);
}
return 0;
}
private void CopyAsSetup(string setupFilePath, ReleaseEntry releaseEntry)
{
if (!_releases.IsLatestEntry(releaseEntry)) return;
var target = Path.Combine(_appArguments.OutputDir, Constants.SetupFile);
File.Delete(target);
File.Copy(setupFilePath, target);
}
private ReleaseEntry AddPackageToReleaseFile(string setupFilePath)
{
try
{
var result = _releases.Add(setupFilePath, _assemblyFileInfo.AssemblyVersion);
_releases.WriteReleaseFile();
return result;
}
catch (Exception e)
{
Log.WriteErrorLine(e.Message);
throw;
}
}
private bool CopyToOutputFolder(string setupFilePath)
{
try
{
if (setupFilePath == null) throw new ArgumentNullException(nameof(setupFilePath));
var setupFile = Path.GetFileName(setupFilePath);
if (string.IsNullOrEmpty(setupFile)) throw new ArgumentException();
if (!File.Exists(setupFilePath)) throw new FileNotFoundException(setupFile);
if (!string.IsNullOrEmpty(_appArguments.ChangeLog))
{
var changeLogPath = Path.Combine(Path.GetDirectoryName(_appArguments.ProgramFile), _appArguments.ChangeLog);
if (!File.Exists(changeLogPath)) throw new FileNotFoundException(changeLogPath);
File.Copy(changeLogPath, Path.Combine(_appArguments.OutputDir, Path.GetFileName(_appArguments.ChangeLog) ?? throw new InvalidOperationException()), true);
}
if (!Directory.Exists(_appArguments.OutputDir)) Directory.CreateDirectory(_appArguments.OutputDir);
File.Copy(setupFilePath, Path.Combine(_appArguments.OutputDir, setupFile), true);
return true;
}
catch (Exception e)
{
Log.WriteErrorLine(e.Message);
return false;
}
}
private static string CompileSetupScript(string templateFile)
{
if (!File.Exists(_executable)) throw new FileNotFoundException("NSIS not installed properly.");
int exitcode;
using (var p = new Process())
{
p.StartInfo = new ProcessStartInfo
{
FileName = _executable,
Arguments = templateFile,
UseShellExecute = false
};
var sw = Stopwatch.StartNew();
p.Start();
p.WaitForExit();
exitcode = p.ExitCode;
Log.WriteInfoLine($"{Path.GetFileName(_executable)} has exited with Exitcode: {exitcode} (Took: {sw.ElapsedMilliseconds}ms)");
}
if (exitcode != 0) throw new Exception($"{Path.GetFileName(_executable)} has exited with Exitcode: {exitcode}");
return Path.ChangeExtension(templateFile, "exe");
}
private string CreateConfigFile()
{
var pathToFile = Path.Combine(_tempDir, Constants.KattekerConfig);
var kattekerConfig = new KattekerConfig
{
PublishDir = _appArguments.PublishDir ?? _appArguments.OutputDir,
Changelog = _appArguments.ChangeLog
};
kattekerConfig.WriteToFile(pathToFile);
return pathToFile;
}
private string ModifyAppStub()
{
var baseFile = new FileInfo(Path.Combine(_baseDirectory, Constants.ExecutionStub));
var targetFile = baseFile.CopyTo(Path.Combine(_tempDir, Constants.ExecutionStub));
Utils.CopyResources(_appArguments.ProgramFile, targetFile.FullName);
return targetFile.FullName;
}
private static string GenerateFilename(string name, string version, string extension, bool isDelta = false) => $"{name}-{version}-{(isDelta ? "delta" : "full")}.{extension}";
private string GenerateNsisTemplate(IEnumerable<PhysicalFile> additionalFiles)
{
try
{
var outFile = Path.Combine(_tempDir, GenerateFilename(_assemblyFileInfo.ProductName, _assemblyFileInfo.AssemblyVersion.ToString(), "nsi"));
var setupTmpl = new SetupTmpl
{
Executable = _assemblyFileInfo.FileInfo.Name,
AppName = _assemblyFileInfo.ProductName,
CompanyName = _assemblyFileInfo.Company,
Description = _assemblyFileInfo.Description,
IconPath = _assemblyFileInfo.AssemblyIconPath,
Version = _assemblyFileInfo.AssemblyVersion,
HelpUrl = "http://example.org", //Not used at the moment.
UserInterface = Path.Combine(_baseDirectory, "contrib", "LoadingBar_Icon.exe"),
UninstallIcon = Path.Combine(_baseDirectory, "contrib", "uninstall.ico"),
OutFile = Path.GetFileName(Path.ChangeExtension(outFile, "exe")),
ReleaseChannel = _appArguments.Channel
};
var path = Path.GetDirectoryName(_appArguments.ProgramFile) ?? string.Empty;
setupTmpl.Files.AddRange(additionalFiles);
var files = Utils.EnumerateFiles(_appArguments.ProgramFile).ToArray();
setupTmpl.InstallSize = (long) Math.Floor(files.Sum(x => x.Length) / 1024f);
setupTmpl.Files.AddRange(files.Select(x => new PhysicalFile(x.FullName, x.FullName.Replace(path, $"app-{_assemblyFileInfo.AssemblyVersion}"))));
var transformText = setupTmpl.TransformText();
File.WriteAllText(outFile, transformText);
return outFile;
}
catch (Exception e)
{
Log.WriteErrorLine(e.Message);
throw;
}
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die mit einer Assembly verknüpft sind.
[assembly: AssemblyTitle("KattekerCreator")]
[assembly: AssemblyDescription("Creates Installation and Update packages.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Holger Börchers")]
[assembly: AssemblyProduct("KattekerCreator")]
[assembly: AssemblyCopyright("Copyright © Holger Börchers")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("ef6af08f-1e5f-44c5-9be8-973b130f8086")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

270
Creator/SemanticVersion.cs Normal file
View File

@ -0,0 +1,270 @@
using System;
using System.Text.RegularExpressions;
namespace KattekerCreator
{
/// <summary>
/// A hybrid implementation of SemVer that supports semantic versioning as described at http://semver.org while not
/// strictly enforcing it to
/// allow older 4-digit versioning schemes to continue working.
/// </summary>
[Serializable]
//[TypeConverter(typeof(SemanticVersionTypeConverter))]
public sealed class SemanticVersion : IComparable, IComparable<SemanticVersion>, IEquatable<SemanticVersion>
{
private const RegexOptions Flags =
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture;
private static readonly Regex SemanticVersionRegex =
new Regex(@"^(?<Version>\d+(\s*\.\s*\d+){0,3})(?<Release>-[a-z][0-9a-z-]*)?$", Flags);
private static readonly Regex StrictSemanticVersionRegex =
new Regex(@"^(?<Version>\d+(\.\d+){2})(?<Release>-[a-z][0-9a-z-]*)?$", Flags);
private readonly string _originalString;
public SemanticVersion(string version)
: this(Parse(version))
{
// The constructor normalizes the version string so that it we do not need to normalize it every time we need to operate on it.
// The original string represents the original form in which the version is represented to be used when printing.
_originalString = version;
}
public SemanticVersion(int major, int minor, int build, int revision)
: this(new Version(major, minor, build, revision))
{
}
public SemanticVersion(int major, int minor, int build, string specialVersion)
: this(new Version(major, minor, build), specialVersion)
{
}
public SemanticVersion(Version version)
: this(version, string.Empty)
{
}
public SemanticVersion(Version version, string specialVersion)
: this(version, specialVersion, null)
{
}
private SemanticVersion(Version version, string specialVersion, string originalString)
{
if (version == null)
throw new ArgumentNullException(nameof(version));
Version = NormalizeVersionValue(version);
SpecialVersion = specialVersion ?? string.Empty;
_originalString = string.IsNullOrEmpty(originalString)
? version + (!string.IsNullOrEmpty(specialVersion) ? '-' + specialVersion : null)
: originalString;
}
internal SemanticVersion(SemanticVersion semVer)
{
_originalString = semVer.ToString();
Version = semVer.Version;
SpecialVersion = semVer.SpecialVersion;
}
/// <summary>
/// Gets the normalized version portion.
/// </summary>
public Version Version { get; }
/// <summary>
/// Gets the optional special version.
/// </summary>
public string SpecialVersion { get; }
public int CompareTo(object obj)
{
if (ReferenceEquals(obj, null))
return 1;
var other = obj as SemanticVersion;
if (other == null)
throw new ArgumentException("Type Must Be A Semantic Version", nameof(obj));
return CompareTo(other);
}
public int CompareTo(SemanticVersion other)
{
if (ReferenceEquals(other, null))
return 1;
var result = Version.CompareTo(other.Version);
if (result != 0)
return result;
var empty = string.IsNullOrEmpty(SpecialVersion);
var otherEmpty = string.IsNullOrEmpty(other.SpecialVersion);
if (empty && otherEmpty)
return 0;
if (empty)
return 1;
if (otherEmpty)
return -1;
return StringComparer.OrdinalIgnoreCase.Compare(SpecialVersion, other.SpecialVersion);
}
public bool Equals(SemanticVersion other)
{
return !ReferenceEquals(null, other) &&
Version.Equals(other.Version) &&
SpecialVersion.Equals(other.SpecialVersion, StringComparison.OrdinalIgnoreCase);
}
public string[] GetOriginalVersionComponents()
{
if (!string.IsNullOrEmpty(_originalString))
{
// search the start of the SpecialVersion part, if any
var dashIndex = _originalString.IndexOf('-');
var original = dashIndex != -1 ? _originalString.Substring(0, dashIndex) : _originalString;
return SplitAndPadVersionString(original);
}
return SplitAndPadVersionString(Version.ToString());
}
private static string[] SplitAndPadVersionString(string version)
{
var a = version.Split('.');
if (a.Length == 4)
{
return a;
}
// if 'a' has less than 4 elements, we pad the '0' at the end
// to make it 4.
var b = new string[4] {"0", "0", "0", "0"};
Array.Copy(a, 0, b, 0, a.Length);
return b;
}
/// <summary>
/// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an
/// optional special version.
/// </summary>
public static SemanticVersion Parse(string version)
{
if (string.IsNullOrEmpty(version))
throw new ArgumentException(nameof(version));
if (!TryParse(version, out var semVer))
throw new ArgumentException("Invalid Version String", nameof(version));
return semVer;
}
/// <summary>
/// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an
/// optional special version.
/// </summary>
public static bool TryParse(string version, out SemanticVersion value)
{
return TryParseInternal(version, SemanticVersionRegex, out value);
}
/// <summary>
/// Parses a version string using strict semantic versioning rules that allows exactly 3 components and an optional
/// special version.
/// </summary>
public static bool TryParseStrict(string version, out SemanticVersion value)
{
return TryParseInternal(version, StrictSemanticVersionRegex, out value);
}
private static bool TryParseInternal(string version, Regex regex, out SemanticVersion semVer)
{
semVer = null;
if (string.IsNullOrEmpty(version))
return false;
var match = regex.Match(version.Trim());
if (!match.Success || !Version.TryParse(match.Groups["Version"].Value, out var versionValue))
return false;
semVer = new SemanticVersion(NormalizeVersionValue(versionValue),
match.Groups["Release"].Value.TrimStart('-'), version.Replace(" ", ""));
return true;
}
/// <summary>
/// Attempts to parse the version token as a SemanticVersion.
/// </summary>
/// <returns>An instance of SemanticVersion if it parses correctly, null otherwise.</returns>
public static SemanticVersion ParseOptionalVersion(string version)
{
TryParse(version, out var semVer);
return semVer;
}
private static Version NormalizeVersionValue(Version version)
{
return new Version(version.Major,
version.Minor,
Math.Max(version.Build, 0),
Math.Max(version.Revision, 0));
}
public static bool operator ==(SemanticVersion version1, SemanticVersion version2)
{
if (ReferenceEquals(version1, null))
return ReferenceEquals(version2, null);
return version1.Equals(version2);
}
public static bool operator !=(SemanticVersion version1, SemanticVersion version2)
{
return !(version1 == version2);
}
public static bool operator <(SemanticVersion version1, SemanticVersion version2)
{
if (version1 == null)
throw new ArgumentNullException(nameof(version1));
return version1.CompareTo(version2) < 0;
}
public static bool operator <=(SemanticVersion version1, SemanticVersion version2)
{
return version1 == version2 || version1 < version2;
}
public static bool operator >(SemanticVersion version1, SemanticVersion version2)
{
if (version1 == null)
throw new ArgumentNullException(nameof(version1));
return version2 < version1;
}
public static bool operator >=(SemanticVersion version1, SemanticVersion version2)
{
return version1 == version2 || version1 > version2;
}
public override string ToString()
{
return _originalString;
}
public override bool Equals(object obj)
{
var semVer = obj as SemanticVersion;
return !ReferenceEquals(null, semVer) && Equals(semVer);
}
public override int GetHashCode()
{
var hashCode = Version.GetHashCode();
if (SpecialVersion != null)
hashCode = hashCode * 4567 + SpecialVersion.GetHashCode();
return hashCode;
}
}
}

103
Creator/Setup.nsi Normal file
View File

@ -0,0 +1,103 @@
!define APPNAME "Shutdown8"
!define EXECUTABLE "Shutdown8.exe"
!define COMPANYNAME "Bandisoft.com"
!define DESCRIPTION "NoDescription"
!define VERSION 1.8.0
!define HELPURL https://srv-lsimctrl01.enercon.de
!define INSTALLSIZE 155573
!define ISMANAGED 1
; exampleCmd: makensis.exe /DVERSION=1.0.0.5 /DNAME=WpfApp1 Setup.nsi
Name "${APPNAME}"
OutFile "${APPNAME}-${VERSION}.exe"
InstallDir "$LOCALAPPDATA\${APPNAME}"
RequestExecutionLevel user
SetCompressor /SOLID lzma
SilentUnInstall silent
; Subcaption 3 " "
XPStyle on
AutoCloseWindow true
ChangeUI all "${NSISDIR}\Contrib\UIs\sdbarker_tiny.exe"
Icon "${NSISDIR}\Contrib\Graphics\Icons\nsis3-install.ico"
UninstallIcon "${NSISDIR}\Contrib\Graphics\Icons\nsis3-uninstall.ico"
ShowInstDetails nevershow
ShowUninstDetails nevershow
BrandingText "${COMPANYNAME}"
;--------------------------------
; The stuff to install
Section "install" ;No components page, name is not important
DetailPrint 'Installing ${APPNAME}. Please wait...'
SetShellVarContext current
SetDetailsPrint None
SetOutPath $INSTDIR\app-${VERSION}
; Put file there
File /r /x *.pdb /x *.obj /x *.pch /x *.vshost.exe /x *.vshost.exe.* Release\*.*
SetOutPath $INSTDIR
File "/oname=${Executable}" AppStub.exe
File Rabbit.Shared.dll
WriteINIStr $INSTDIR\app.ini Main Executable "${EXECUTABLE}"
WriteINIStr $INSTDIR\app.ini Main AppName "${APPNAME}"
WriteINIStr $INSTDIR\app.ini Main IsManaged ${ISMANAGED}
WriteUninstaller "$INSTDIR\uninstall.exe"
; Desktop
CreateShortCut "$DESKTOP\${APPNAME}.lnk" "$INSTDIR\${Executable}"
# Start Menu
CreateDirectory "$SMPROGRAMS\${COMPANYNAME}"
CreateShortCut "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk" "$INSTDIR\${Executable}"
# Update pinned Taskbar
IfFileExists "$APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\${APPNAME}.lnk" 0 +2
CreateShortCut "$APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\${APPNAME}.lnk" "$INSTDIR\${Executable}"
SetOutPath $INSTDIR\app-${VERSION}
StrCpy $0 ${EXECUTABLE} -4
IfFileExists "$APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\$0.lnk" 0 +2
CreateShortCut "$APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\$0.lnk" "$INSTDIR\app-${VERSION}\${EXECUTABLE}"
# Registry information for add/remove programs
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME}"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "InstallLocation" "$\"$INSTDIR$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayIcon" "$\"$INSTDIR\app-${VERSION}\${EXECUTABLE}$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "Publisher" "${COMPANYNAME}"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "HelpLink" "${HELPURL}"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayVersion" "${VERSION}"
# There is no option for modifying or repairing the install
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoModify" 1
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoRepair" 1
# Set the INSTALLSIZE constant (!defined at the top of this script) so Add/Remove Programs can accurately report the size
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "EstimatedSize" ${INSTALLSIZE}
SectionEnd ; end the section
Function .onInstSuccess
IfSilent +2
Exec '"$INSTDIR\${Executable}"'
FunctionEnd
Section "uninstall"
DetailPrint 'Please wait...'
SetShellVarContext current
SetDetailsPrint None
SetAutoClose true
# Remove Start Menu launcher
Delete "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk"
Delete "$DESKTOP\${APPNAME}.lnk"
StrCpy $0 ${EXECUTABLE} -4
Delete "$APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\${APPNAME}.lnk"
Delete "$APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\$0.lnk"
# Try to remove the Start Menu folder - this will only happen if it is empty
RMDir "$SMPROGRAMS\${COMPANYNAME}"
# Always delete uninstaller as the last action
delete $INSTDIR\uninstall.exe
# Try to remove the install directory - this will only happen if it is empty
RMDir /r /REBOOTOK $INSTDIR
# Remove uninstaller information from the registry
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
sectionEnd

522
Creator/SetupTmpl.cs Normal file
View File

@ -0,0 +1,522 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version: 15.0.0.0
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
namespace KattekerCreator
{
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System;
/// <summary>
/// Class to produce the template output
/// </summary>
#line 1 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")]
public partial class SetupTmpl : SetupTmplBase
{
#line hidden
/// <summary>
/// Create the template output
/// </summary>
public virtual string TransformText()
{
this.Write("Unicode true\r\nManifestDPIAware true\r\n!define APPNAME \"");
#line 8 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(AppName));
#line default
#line hidden
this.Write("\"\r\n!define EXECUTABLE \"");
#line 9 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(Executable));
#line default
#line hidden
this.Write("\"\r\n!define SOURCEPATH \"");
#line 10 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(SourcePath));
#line default
#line hidden
this.Write("\"\r\n!define COMPANYNAME \"");
#line 11 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(CompanyName));
#line default
#line hidden
this.Write("\"\r\n!define DESCRIPTION \"");
#line 12 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(Description));
#line default
#line hidden
this.Write("\"\r\n!define VERSION ");
#line 13 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(Version.ToString()));
#line default
#line hidden
this.Write("\r\n!define HELPURL ");
#line 14 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(HelpUrl));
#line default
#line hidden
this.Write("\r\n!define INSTALLSIZE ");
#line 15 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(InstallSize));
#line default
#line hidden
this.Write("\r\n!define ISMANAGED ");
#line 16 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(IsManaged ? 1 : 0));
#line default
#line hidden
this.Write("\r\n; exampleCmd: makensis.exe /DVERSION=1.0.0.5 /DNAME=WpfApp1 Setup.nsi\r\n\r\nName \"" +
"${APPNAME}\"\r\nOutFile \"");
#line 20 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(OutFile));
#line default
#line hidden
this.Write("\"\r\nInstallDir \"$LOCALAPPDATA\\${APPNAME}\"\r\nRequestExecutionLevel user\r\nSetCompress" +
"or /SOLID lzma\r\nSilentUnInstall silent\r\n; Subcaption 3 \" \"\r\nXPStyle on\r\nAutoClos" +
"eWindow true\r\nChangeUI all \"");
#line 28 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(UserInterface));
#line default
#line hidden
this.Write("\"\r\nIcon \"");
#line 29 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(IconPath));
#line default
#line hidden
this.Write("\"\r\nUninstallIcon \"");
#line 30 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(UninstallIcon));
#line default
#line hidden
this.Write("\"\r\nShowInstDetails nevershow \r\nShowUninstDetails nevershow \r\nBrandingText \"${COMP" +
"ANYNAME}\"\r\n\r\nVIProductVersion ");
#line 35 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(LegacyVersion.ToString(4)));
#line default
#line hidden
this.Write(@"
VIAddVersionKey ProductName ""${APPNAME}""
VIAddVersionKey Comments """"
VIAddVersionKey CompanyName ""${COMPANYNAME}""
VIAddVersionKey LegalCopyright ""${COMPANYNAME}""
VIAddVersionKey FileDescription ""${DESCRIPTION}""
VIAddVersionKey FileVersion ""${VERSION}""
VIAddVersionKey ProductVersion ""${VERSION}""
VIAddVersionKey OriginalFilename """);
#line 43 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture(OutFile));
#line default
#line hidden
this.Write(@"""
;--------------------------------
; The stuff to install
Section ""install"" ;No components page, name is not important
DetailPrint 'Installing ${APPNAME}. Please wait...'
SetShellVarContext current
SetDetailsPrint None
IfSilent +2
RMDir /r $INSTDIR
SetOutPath $INSTDIR
; Create sub-directories
");
#line 56 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
foreach(var directory in Directories) {
#line default
#line hidden
#line 56 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture($" CreateDirectory \"$INSTDIR\\{directory}\"{Environment.NewLine}"));
#line default
#line hidden
#line 56 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
}
#line default
#line hidden
this.Write("\r\n ; Put file there\r\n");
#line 59 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
foreach(var file in Files) {
#line default
#line hidden
#line 59 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
this.Write(this.ToStringHelper.ToStringWithCulture($" File \"/oname={file.TargetPath}\" \"{file.SourcePath}\"{Environment.NewLine}"));
#line default
#line hidden
#line 59 "C:\Strukturberechnung_del\Repos\SquirrelKiller\Katteker\Creator\SetupTmpl.tt"
}
#line default
#line hidden
this.Write("\r\n WriteUninstaller \"$INSTDIR\\uninstall.exe\"\r\n ; Desktop\r\n CreateShortCu" +
"t \"$DESKTOP\\${APPNAME}.lnk\" \"$INSTDIR\\${EXECUTABLE}\"\r\n \r\n # Start Menu\r\n " +
" CreateDirectory \"$SMPROGRAMS\\${COMPANYNAME}\"\r\n CreateShortCut \"$SMPROGRAMS\\" +
"${COMPANYNAME}\\${APPNAME}.lnk\" \"$INSTDIR\\${EXECUTABLE}\"\r\n\r\n # Update pinned T" +
"askbar\r\n IfFileExists \"$APPDATA\\Microsoft\\Internet Explorer\\Quick Launch\\User" +
" Pinned\\TaskBar\\${APPNAME}.lnk\" 0 +2\r\n CreateShortCut \"$APPDATA\\Microsoft\\Int" +
"ernet Explorer\\Quick Launch\\User Pinned\\TaskBar\\${APPNAME}.lnk\" \"$INSTDIR\\${EXEC" +
"UTABLE}\"\r\n SetOutPath $INSTDIR\\app-${VERSION}\r\n StrCpy $0 ${EXECUTABLE} -4" +
"\r\n IfFileExists \"$APPDATA\\Microsoft\\Internet Explorer\\Quick Launch\\User Pinne" +
"d\\TaskBar\\$0.lnk\" 0 +2\r\n CreateShortCut \"$APPDATA\\Microsoft\\Internet Explorer" +
"\\Quick Launch\\User Pinned\\TaskBar\\$0.lnk\" \"$INSTDIR\\app-${VERSION}\\${EXECUTABLE}" +
"\"\r\n \r\n # Registry information for add/remove programs\r\n WriteRegStr HKC" +
"U \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${APPNAME}\" \"DisplayName\"" +
" \"${APPNAME}\"\r\n WriteRegStr HKCU \"Software\\Microsoft\\Windows\\CurrentVersion\\U" +
"ninstall\\${APPNAME}\" \"UninstallString\" \"$\\\"$INSTDIR\\uninstall.exe$\\\"\"\r\n Write" +
"RegStr HKCU \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${APPNAME}\" \"Qu" +
"ietUninstallString\" \"$\\\"$INSTDIR\\uninstall.exe$\\\" /S\"\r\n WriteRegStr HKCU \"Sof" +
"tware\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${APPNAME}\" \"InstallLocation\" \"" +
"$\\\"$INSTDIR$\\\"\"\r\n WriteRegStr HKCU \"Software\\Microsoft\\Windows\\CurrentVersion" +
"\\Uninstall\\${APPNAME}\" \"DisplayIcon\" \"$\\\"$INSTDIR\\app-${VERSION}\\${EXECUTABLE}$\\" +
"\"\"\r\n WriteRegStr HKCU \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${" +
"APPNAME}\" \"Publisher\" \"${COMPANYNAME}\"\r\n ; WriteRegStr HKCU \"Software\\Microso" +
"ft\\Windows\\CurrentVersion\\Uninstall\\${APPNAME}\" \"HelpLink\" \"${HELPURL}\"\r\n Wri" +
"teRegStr HKCU \"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\${APPNAME}\" \"" +
"DisplayVersion\" \"${VERSION}\"\r\n # There is no option for modifying or repairin" +
"g the install\r\n WriteRegDWORD HKCU \"Software\\Microsoft\\Windows\\CurrentVersion" +
"\\Uninstall\\${APPNAME}\" \"NoModify\" 1\r\n WriteRegDWORD HKCU \"Software\\Microsoft\\" +
"Windows\\CurrentVersion\\Uninstall\\${APPNAME}\" \"NoRepair\" 1\r\n # Set the INSTALL" +
"SIZE constant (!defined at the top of this script) so Add/Remove Programs can ac" +
"curately report the size\r\n WriteRegDWORD HKCU \"Software\\Microsoft\\Windows\\Cur" +
"rentVersion\\Uninstall\\${APPNAME}\" \"EstimatedSize\" ${INSTALLSIZE}\r\nSectionEnd ; e" +
"nd the section\r\n\r\nFunction .onInstSuccess\r\n IfSilent +2\r\n Exec \'\"$INSTDIR\\" +
"${EXECUTABLE}\"\'\r\nFunctionEnd\r\n\r\nSection \"uninstall\"\r\n DetailPrint \'Please wai" +
"t...\'\r\n SetShellVarContext current\r\n SetDetailsPrint None\r\n SetAutoClos" +
"e true\r\n \r\n # Remove Start Menu launcher\r\n Delete \"$SMPROGRAMS\\${COMPAN" +
"YNAME}\\${APPNAME}.lnk\"\r\n Delete \"$DESKTOP\\${APPNAME}.lnk\"\r\n StrCpy $0 ${EX" +
"ECUTABLE} -4\r\n Delete \"$APPDATA\\Microsoft\\Internet Explorer\\Quick Launch\\User" +
" Pinned\\TaskBar\\${APPNAME}.lnk\"\r\n Delete \"$APPDATA\\Microsoft\\Internet Explore" +
"r\\Quick Launch\\User Pinned\\TaskBar\\$0.lnk\"\r\n # Try to remove the Start Menu f" +
"older - this will only happen if it is empty\r\n RMDir \"$SMPROGRAMS\\${COMPANYNA" +
"ME}\"\r\n\r\n # Always delete uninstaller as the last action\r\n delete $INSTDIR\\" +
"uninstall.exe\r\n\r\n # Try to remove the install directory - this will only happ" +
"en if it is empty\r\n RMDir /r /REBOOTOK $INSTDIR\r\n\r\n # Remove uninstaller i" +
"nformation from the registry\r\n DeleteRegKey HKCU \"Software\\Microsoft\\Windows\\" +
"CurrentVersion\\Uninstall\\${APPNAME}\"\r\nsectionEnd");
return this.GenerationEnvironment.ToString();
}
}
#line default
#line hidden
#region Base class
/// <summary>
/// Base class for this transformation
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "15.0.0.0")]
public class SetupTmplBase
{
#region Fields
private global::System.Text.StringBuilder generationEnvironmentField;
private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;
private global::System.Collections.Generic.List<int> indentLengthsField;
private string currentIndentField = "";
private bool endsWithNewline;
private global::System.Collections.Generic.IDictionary<string, object> sessionField;
#endregion
#region Properties
/// <summary>
/// The string builder that generation-time code is using to assemble generated output
/// </summary>
protected System.Text.StringBuilder GenerationEnvironment
{
get
{
if ((this.generationEnvironmentField == null))
{
this.generationEnvironmentField = new global::System.Text.StringBuilder();
}
return this.generationEnvironmentField;
}
set
{
this.generationEnvironmentField = value;
}
}
/// <summary>
/// The error collection for the generation process
/// </summary>
public System.CodeDom.Compiler.CompilerErrorCollection Errors
{
get
{
if ((this.errorsField == null))
{
this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();
}
return this.errorsField;
}
}
/// <summary>
/// A list of the lengths of each indent that was added with PushIndent
/// </summary>
private System.Collections.Generic.List<int> indentLengths
{
get
{
if ((this.indentLengthsField == null))
{
this.indentLengthsField = new global::System.Collections.Generic.List<int>();
}
return this.indentLengthsField;
}
}
/// <summary>
/// Gets the current indent we use when adding lines to the output
/// </summary>
public string CurrentIndent
{
get
{
return this.currentIndentField;
}
}
/// <summary>
/// Current transformation session
/// </summary>
public virtual global::System.Collections.Generic.IDictionary<string, object> Session
{
get
{
return this.sessionField;
}
set
{
this.sessionField = value;
}
}
#endregion
#region Transform-time helpers
/// <summary>
/// Write text directly into the generated output
/// </summary>
public void Write(string textToAppend)
{
if (string.IsNullOrEmpty(textToAppend))
{
return;
}
// If we're starting off, or if the previous text ended with a newline,
// we have to append the current indent first.
if (((this.GenerationEnvironment.Length == 0)
|| this.endsWithNewline))
{
this.GenerationEnvironment.Append(this.currentIndentField);
this.endsWithNewline = false;
}
// Check if the current text ends with a newline
if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))
{
this.endsWithNewline = true;
}
// This is an optimization. If the current indent is "", then we don't have to do any
// of the more complex stuff further down.
if ((this.currentIndentField.Length == 0))
{
this.GenerationEnvironment.Append(textToAppend);
return;
}
// Everywhere there is a newline in the text, add an indent after it
textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));
// If the text ends with a newline, then we should strip off the indent added at the very end
// because the appropriate indent will be added when the next time Write() is called
if (this.endsWithNewline)
{
this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));
}
else
{
this.GenerationEnvironment.Append(textToAppend);
}
}
/// <summary>
/// Write text directly into the generated output
/// </summary>
public void WriteLine(string textToAppend)
{
this.Write(textToAppend);
this.GenerationEnvironment.AppendLine();
this.endsWithNewline = true;
}
/// <summary>
/// Write formatted text directly into the generated output
/// </summary>
public void Write(string format, params object[] args)
{
this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
}
/// <summary>
/// Write formatted text directly into the generated output
/// </summary>
public void WriteLine(string format, params object[] args)
{
this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
}
/// <summary>
/// Raise an error
/// </summary>
public void Error(string message)
{
System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
error.ErrorText = message;
this.Errors.Add(error);
}
/// <summary>
/// Raise a warning
/// </summary>
public void Warning(string message)
{
System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
error.ErrorText = message;
error.IsWarning = true;
this.Errors.Add(error);
}
/// <summary>
/// Increase the indent
/// </summary>
public void PushIndent(string indent)
{
if ((indent == null))
{
throw new global::System.ArgumentNullException("indent");
}
this.currentIndentField = (this.currentIndentField + indent);
this.indentLengths.Add(indent.Length);
}
/// <summary>
/// Remove the last indent that was added with PushIndent
/// </summary>
public string PopIndent()
{
string returnValue = "";
if ((this.indentLengths.Count > 0))
{
int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];
this.indentLengths.RemoveAt((this.indentLengths.Count - 1));
if ((indentLength > 0))
{
returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));
this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));
}
}
return returnValue;
}
/// <summary>
/// Remove any indentation
/// </summary>
public void ClearIndent()
{
this.indentLengths.Clear();
this.currentIndentField = "";
}
#endregion
#region ToString Helpers
/// <summary>
/// Utility class to produce culture-oriented representation of an object as a string.
/// </summary>
public class ToStringInstanceHelper
{
private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture;
/// <summary>
/// Gets or sets format provider to be used by ToStringWithCulture method.
/// </summary>
public System.IFormatProvider FormatProvider
{
get
{
return this.formatProviderField ;
}
set
{
if ((value != null))
{
this.formatProviderField = value;
}
}
}
/// <summary>
/// This is called from the compile/run appdomain to convert objects within an expression block to a string
/// </summary>
public string ToStringWithCulture(object objectToConvert)
{
if ((objectToConvert == null))
{
throw new global::System.ArgumentNullException("objectToConvert");
}
System.Type t = objectToConvert.GetType();
System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] {
typeof(System.IFormatProvider)});
if ((method == null))
{
return objectToConvert.ToString();
}
else
{
return ((string)(method.Invoke(objectToConvert, new object[] {
this.formatProviderField })));
}
}
}
private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();
/// <summary>
/// Helper to produce culture-oriented representation of an object as a string
/// </summary>
public ToStringInstanceHelper ToStringHelper
{
get
{
return this.toStringHelperField;
}
}
#endregion
}
#endregion
}

121
Creator/SetupTmpl.tt Normal file
View File

@ -0,0 +1,121 @@
<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
Unicode true
ManifestDPIAware true
!define APPNAME "<#= AppName #>"
!define EXECUTABLE "<#= Executable #>"
!define SOURCEPATH "<#= SourcePath #>"
!define COMPANYNAME "<#= CompanyName #>"
!define DESCRIPTION "<#= Description #>"
!define VERSION <#= Version.ToString() #>
!define HELPURL <#= HelpUrl #>
!define INSTALLSIZE <#= InstallSize #>
!define ISMANAGED <#= IsManaged ? 1 : 0 #>
; exampleCmd: makensis.exe /DVERSION=1.0.0.5 /DNAME=WpfApp1 Setup.nsi
Name "${APPNAME}"
OutFile "<#= OutFile #>"
InstallDir "$LOCALAPPDATA\${APPNAME}"
RequestExecutionLevel user
SetCompressor /SOLID lzma
SilentUnInstall silent
; Subcaption 3 " "
XPStyle on
AutoCloseWindow true
ChangeUI all "<#= UserInterface #>"
Icon "<#= IconPath #>"
UninstallIcon "<#= UninstallIcon #>"
ShowInstDetails nevershow
ShowUninstDetails nevershow
BrandingText "${COMPANYNAME}"
VIProductVersion <#= LegacyVersion.ToString(4) #>
VIAddVersionKey ProductName "${APPNAME}"
VIAddVersionKey Comments ""
VIAddVersionKey CompanyName "${COMPANYNAME}"
VIAddVersionKey LegalCopyright "${COMPANYNAME}"
VIAddVersionKey FileDescription "${DESCRIPTION}"
VIAddVersionKey FileVersion "${VERSION}"
VIAddVersionKey ProductVersion "${VERSION}"
VIAddVersionKey OriginalFilename "<#= OutFile #>"
;--------------------------------
; The stuff to install
Section "install" ;No components page, name is not important
DetailPrint 'Installing ${APPNAME}. Please wait...'
SetShellVarContext current
SetDetailsPrint None
IfSilent +2
RMDir /r $INSTDIR
SetOutPath $INSTDIR
; Create sub-directories
<# foreach(var directory in Directories) { #><#= $" CreateDirectory \"$INSTDIR\\{directory}\"{Environment.NewLine}" #><# } #>
; Put file there
<#foreach(var file in Files) { #><#= $" File \"/oname={file.TargetPath}\" \"{file.SourcePath}\"{Environment.NewLine}" #><# } #>
WriteUninstaller "$INSTDIR\uninstall.exe"
; Desktop
CreateShortCut "$DESKTOP\${APPNAME}.lnk" "$INSTDIR\${EXECUTABLE}"
# Start Menu
CreateDirectory "$SMPROGRAMS\${COMPANYNAME}"
CreateShortCut "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk" "$INSTDIR\${EXECUTABLE}"
# Update pinned Taskbar
IfFileExists "$APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\${APPNAME}.lnk" 0 +2
CreateShortCut "$APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\${APPNAME}.lnk" "$INSTDIR\${EXECUTABLE}"
SetOutPath $INSTDIR\app-${VERSION}
StrCpy $0 ${EXECUTABLE} -4
IfFileExists "$APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\$0.lnk" 0 +2
CreateShortCut "$APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\$0.lnk" "$INSTDIR\app-${VERSION}\${EXECUTABLE}"
# Registry information for add/remove programs
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME}"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "InstallLocation" "$\"$INSTDIR$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayIcon" "$\"$INSTDIR\app-${VERSION}\${EXECUTABLE}$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "Publisher" "${COMPANYNAME}"
; WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "HelpLink" "${HELPURL}"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayVersion" "${VERSION}"
# There is no option for modifying or repairing the install
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoModify" 1
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoRepair" 1
# Set the INSTALLSIZE constant (!defined at the top of this script) so Add/Remove Programs can accurately report the size
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "EstimatedSize" ${INSTALLSIZE}
SectionEnd ; end the section
Function .onInstSuccess
IfSilent +2
Exec '"$INSTDIR\${EXECUTABLE}"'
FunctionEnd
Section "uninstall"
DetailPrint 'Please wait...'
SetShellVarContext current
SetDetailsPrint None
SetAutoClose true
# Remove Start Menu launcher
Delete "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk"
Delete "$DESKTOP\${APPNAME}.lnk"
StrCpy $0 ${EXECUTABLE} -4
Delete "$APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\${APPNAME}.lnk"
Delete "$APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\$0.lnk"
# Try to remove the Start Menu folder - this will only happen if it is empty
RMDir "$SMPROGRAMS\${COMPANYNAME}"
# Always delete uninstaller as the last action
delete $INSTDIR\uninstall.exe
# Try to remove the install directory - this will only happen if it is empty
RMDir /r /REBOOTOK $INSTDIR
# Remove uninstaller information from the registry
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
sectionEnd

84
Creator/SetupTmplCode.cs Normal file
View File

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Katteker;
namespace KattekerCreator
{
public partial class SetupTmpl
{
public string AppName
{
get => string.IsNullOrWhiteSpace(ReleaseChannel) ? _appName : $"{_appName}_{ReleaseChannel}";
set => _appName = value;
}
public string Executable { get; set; }
public string CompanyName { get; set; }
public string Description { get; set; }
public SemanticVersion Version { get; set; }
public Version LegacyVersion => Version.ToSystemVersion();
public string HelpUrl { get; set; }
public long InstallSize { get; set; }
public bool IsManaged { get; set; }
public string IconPath { get; set; }
public string SourcePath { get; set; } = string.Empty;
public List<PhysicalFile> Files { get; set; } = new List<PhysicalFile>();
private IEnumerable<string> Directories => DirectoriesToCreate();
public string UserInterface { get; set; }
public string OutFile { get; set; }
public string UninstallIcon { get; set; }
public string ReleaseChannel { get; set; }
private readonly List<DirSplit> _directoriesList = new List<DirSplit>();
private string _appName;
private IEnumerable<string> DirectoriesToCreate()
{
foreach (var physicalFile in Files)
{
var dirSplit = new DirSplit(physicalFile.TargetPath);
if (dirSplit.SplitCount > 1 && !_directoriesList.Contains(dirSplit))
{
_directoriesList.Add(dirSplit);
}
}
return _directoriesList.Select(x=> x.ToString());
}
private class DirSplit
{
private const char SplitChar = '\\';
private readonly string[] _splits;
public int SplitCount => _splits.Length;
public DirSplit(string path)
{
_splits = path.Split(SplitChar);
}
public override string ToString()
{
return string.Join(SplitChar.ToString(), _splits.Take(_splits.Length - 1));
}
public override bool Equals(object obj)
{
return string.Equals(ToString(), obj?.ToString());
}
protected bool Equals(DirSplit other)
{
return Equals(_splits, other._splits);
}
public override int GetHashCode()
{
return (_splits != null ? _splits.GetHashCode() : 0);
}
}
}
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

4
Creator/packages.config Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Vestris.ResourceLib" version="1.6.422" targetFramework="net45" />
</packages>

19
Creator/reference.nuspec Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>$id$</id>
<version>$version$</version>
<title>$id$</title>
<authors>$author$</authors>
<owners>$author$</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>$description$</description>
<copyright>$copyright$</copyright>
</metadata>
<files>
<file src="bin\$configuration$\*.dll" target="lib\net45" />
<file src="bin\$configuration$\*.config" target="lib\net45" exclude="bin\$configuration$\*.vshost.exe.config" />
<file src="bin\$configuration$\*.exe" target="lib\net45" exclude="bin\$configuration$\*.vshost.exe" />
<file src="..\packages\squirrel.windows.0.99.1.1\tools\Squirrel.exe" target="lib\net45" />
</files>
</package>

6
Example/App.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
</configuration>

9
Example/App.xaml Normal file
View File

@ -0,0 +1,9 @@
<Application x:Class="Example.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Example"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

11
Example/App.xaml.cs Normal file
View File

@ -0,0 +1,11 @@
using System.Windows;
namespace Example
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

120
Example/Example.csproj Normal file
View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{DE9CFDEB-40B8-447D-9959-31BA78512CED}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>Example</RootNamespace>
<AssemblyName>Example</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x64</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>x64</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>nsis3-install.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<StartupObject>Example.App</StartupObject>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="changelog.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Katteker.Gui\Katteker.Gui.csproj">
<Project>{07e2de31-80a0-43da-b307-1ca47cd930a1}</Project>
<Name>Katteker.Gui</Name>
</ProjectReference>
<ProjectReference Include="..\Katteker\Katteker.csproj">
<Project>{a45e1c59-ba9e-452c-a5e2-50de49d53e92}</Project>
<Name>Katteker</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Resource Include="nsis3-install.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

17
Example/MainWindow.xaml Normal file
View File

@ -0,0 +1,17 @@
<Window
x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Example"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
Background="DarkBlue"
ContentRendered="MainWindow_OnContentRendered"
mc:Ignorable="d">
<StackPanel>
<Button Margin="10" FontSize="24" Content="Update" Click="ButtonBase_OnClick"></Button>
</StackPanel>
</Window>

View File

@ -0,0 +1,32 @@
using System;
using System.Reflection;
using System.Windows;
using Katteker.Gui;
namespace Example
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
var version = GetType().Assembly.GetName().Version.ToString();
var a = Assembly.GetExecutingAssembly();
a.ManifestModule.GetPEKind(out var peKind, out var machine);
Title = $"{version} | {peKind} | {machine}";
}
private async void MainWindow_OnContentRendered(object sender, EventArgs e)
{
await Wrapper.CheckForUpdateAsync(true).ConfigureAwait(false);
}
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
await Wrapper.CheckForUpdateAsync().ConfigureAwait(false);
}
}
}

View File

@ -0,0 +1,53 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Example")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Enercon GmbH")]
[assembly: AssemblyProduct("Example")]
[assembly: AssemblyCopyright("Copyright © Enercon GmbH 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.36.0")]
[assembly: AssemblyFileVersion("1.0.36.0")]

63
Example/Properties/Resources.Designer.cs generated Normal file
View File

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Example.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Example.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

26
Example/Properties/Settings.Designer.cs generated Normal file
View File

@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Example.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.6.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

5
Example/changelog.md Normal file
View File

@ -0,0 +1,5 @@
# Changelog
## 1.0.0
- Hello World

BIN
Example/nsis3-install.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,61 @@
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace Katteker.Gui
{
internal static class ChangelogHelper
{
private static string GenerateHtmlifyChangelog(string text, string extension)
{
string result;
switch (extension)
{
case ".txt":
var plainText = WebUtility.HtmlEncode(text);
result = plainText.Replace(Environment.NewLine, "<br />");
break;
case ".md":
result = CommonMark.CommonMarkConverter.Convert(text);
break;
default:
result = text;
break;
}
return result;
}
internal static async Task<string> LoadChangelogAsync(string filename, string path)
{
if (!string.IsNullOrEmpty(filename) || !string.IsNullOrEmpty(path))
{
if (!path.EndsWith("/", StringComparison.Ordinal))
path += "/";
var webReq = WebRequest.Create(path + filename);
try
{
using (var response = await webReq.GetResponseAsync().ConfigureAwait(false))
using (var sr = new StreamReader(response.GetResponseStream()))
{
return GenerateHtmlifyChangelog(await sr.ReadToEndAsync().ConfigureAwait(false),
Path.GetExtension(filename));
}
}
catch (WebException)
{
var changelogFilename = Path.GetFileName(filename);
if (changelogFilename == null) return GenerateHtmlifyChangelog("Changelog not found", ".txt");
var currentChangelogPath = Path.Combine(Environment.CurrentDirectory, changelogFilename);
if (File.Exists(currentChangelogPath))
{
return GenerateHtmlifyChangelog(File.ReadAllText(currentChangelogPath), Path.GetExtension(filename));
}
}
}
return GenerateHtmlifyChangelog("Changelog not found", ".txt");
}
}
}

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{07E2DE31-80A0-43DA-B307-1CA47CD930A1}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Katteker.Gui</RootNamespace>
<AssemblyName>Katteker.Gui</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Katteker.Gui.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Katteker.Gui.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="CommonMark, Version=0.1.0.0, Culture=neutral, PublicKeyToken=001ef8810438905d, processorArchitecture=MSIL">
<HintPath>..\packages\CommonMark.NET.0.15.1\lib\net45\CommonMark.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ChangelogHelper.cs" />
<Compile Include="Wrapper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="UpdateWindow.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UpdateWindow.Designer.cs">
<DependentUpon>UpdateWindow.cs</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="UpdateWindow.resx">
<DependentUpon>UpdateWindow.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Katteker\Katteker.csproj">
<Project>{a45e1c59-ba9e-452c-a5e2-50de49d53e92}</Project>
<Name>Katteker</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>$id$</id>
<version>$version$</version>
<title>$id$</title>
<authors>$author$</authors>
<owners>$author$</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>$description$</description>
<copyright>Copyright 2017</copyright>
<dependencies>
<dependency id="squirrel.windows" version="[1.7.9]" />
</dependencies>
</metadata>
<files>
</files>
</package>

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die mit einer Assembly verknüpft sind.
[assembly: AssemblyTitle("Katteker.Gui")]
[assembly: AssemblyDescription("Helps Applications to integrate Katteker")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("WRD GmbH")]
[assembly: AssemblyProduct("Katteker.Gui")]
[assembly: AssemblyCopyright("Copyright © Enercon GmbH 2015-2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("4877208f-00bf-42b7-a56b-bf8bf4a0d3c8")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]

View File

@ -0,0 +1,144 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Katteker.Gui.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Katteker.Gui.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Installed new Version! Would you like to restart the Application?.
/// </summary>
internal static string Installed_new_Version {
get {
return ResourceManager.GetString("Installed_new_Version", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New Version.
/// </summary>
internal static string New_Version {
get {
return ResourceManager.GetString("New_Version", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No update available..
/// </summary>
internal static string No_update_available {
get {
return ResourceManager.GetString("No_update_available", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Can&apos;t find configuration File. Ask your local distributor..
/// </summary>
internal static string SquirrelWrapper_CheckForUpdate {
get {
return ResourceManager.GetString("SquirrelWrapper_CheckForUpdate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Ask your local distributor..
/// </summary>
internal static string SquirrelWrapper_CheckForUpdate_AskYourLocalDistributor {
get {
return ResourceManager.GetString("SquirrelWrapper_CheckForUpdate_AskYourLocalDistributor", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error with Configuration-File.
/// </summary>
internal static string SquirrelWrapper_CheckForUpdate_Error {
get {
return ResourceManager.GetString("SquirrelWrapper_CheckForUpdate_Error", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error with Update.
/// </summary>
internal static string SquirrelWrapper_CheckForUpdate_ErrorWithUpdate {
get {
return ResourceManager.GetString("SquirrelWrapper_CheckForUpdate_ErrorWithUpdate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You can update to Version: .
/// </summary>
internal static string You_can_update_to_Version {
get {
return ResourceManager.GetString("You_can_update_to_Version", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You&apos;re up to date..
/// </summary>
internal static string You_re_up_to_date {
get {
return ResourceManager.GetString("You_re_up_to_date", resourceCulture);
}
}
}
}

View File

@ -0,0 +1,147 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Installed_new_Version" xml:space="preserve">
<value>Installed new Version! Would you like to restart the Application?</value>
</data>
<data name="New_Version" xml:space="preserve">
<value>New Version</value>
</data>
<data name="You_re_up_to_date" xml:space="preserve">
<value>You're up to date.</value>
</data>
<data name="You_can_update_to_Version" xml:space="preserve">
<value>You can update to Version: </value>
</data>
<data name="No_update_available" xml:space="preserve">
<value>No update available.</value>
</data>
<data name="SquirrelWrapper_CheckForUpdate" xml:space="preserve">
<value>Can't find configuration File. Ask your local distributor.</value>
</data>
<data name="SquirrelWrapper_CheckForUpdate_Error" xml:space="preserve">
<value>Error with Configuration-File</value>
</data>
<data name="SquirrelWrapper_CheckForUpdate_ErrorWithUpdate" xml:space="preserve">
<value>Error with Update</value>
</data>
<data name="SquirrelWrapper_CheckForUpdate_AskYourLocalDistributor" xml:space="preserve">
<value> Ask your local distributor.</value>
</data>
</root>

314
Katteker.Gui/UpdateWindow.Designer.cs generated Normal file
View File

@ -0,0 +1,314 @@
using System;
namespace Katteker.Gui
{
/// <summary>
/// Shows the Update Window.
/// </summary>
sealed partial class UpdateWindow
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
[STAThread]
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UpdateWindow));
this.mainSplitContainer = new System.Windows.Forms.SplitContainer();
this.titleLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
this.closeWindowBtn = new System.Windows.Forms.Button();
this.maximizeBtn = new System.Windows.Forms.Button();
this.titlebar = new System.Windows.Forms.Label();
this.minimizeBtn = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.splitContainer3 = new System.Windows.Forms.SplitContainer();
this.changelogBrowser = new System.Windows.Forms.WebBrowser();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
this.closeBtn = new System.Windows.Forms.Button();
this.updBtn = new System.Windows.Forms.Button();
this.panel1 = new System.Windows.Forms.Panel();
((System.ComponentModel.ISupportInitialize)(this.mainSplitContainer)).BeginInit();
this.mainSplitContainer.Panel1.SuspendLayout();
this.mainSplitContainer.Panel2.SuspendLayout();
this.mainSplitContainer.SuspendLayout();
this.titleLayoutPanel.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).BeginInit();
this.splitContainer3.Panel1.SuspendLayout();
this.splitContainer3.Panel2.SuspendLayout();
this.splitContainer3.SuspendLayout();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// mainSplitContainer
//
this.mainSplitContainer.BackColor = System.Drawing.SystemColors.Control;
this.mainSplitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
this.mainSplitContainer.FixedPanel = System.Windows.Forms.FixedPanel.Panel1;
this.mainSplitContainer.IsSplitterFixed = true;
this.mainSplitContainer.Location = new System.Drawing.Point(1, 1);
this.mainSplitContainer.Name = "mainSplitContainer";
this.mainSplitContainer.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// mainSplitContainer.Panel1
//
this.mainSplitContainer.Panel1.Controls.Add(this.titleLayoutPanel);
this.mainSplitContainer.Panel1MinSize = 30;
//
// mainSplitContainer.Panel2
//
this.mainSplitContainer.Panel2.Controls.Add(this.splitContainer3);
this.mainSplitContainer.Size = new System.Drawing.Size(698, 438);
this.mainSplitContainer.SplitterDistance = 30;
this.mainSplitContainer.TabIndex = 7;
this.mainSplitContainer.TabStop = false;
//
// titleLayoutPanel
//
this.titleLayoutPanel.BackColor = System.Drawing.SystemColors.ControlDarkDark;
this.titleLayoutPanel.ColumnCount = 5;
this.titleLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 120F));
this.titleLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.titleLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 40F));
this.titleLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 40F));
this.titleLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 40F));
this.titleLayoutPanel.Controls.Add(this.closeWindowBtn, 4, 0);
this.titleLayoutPanel.Controls.Add(this.maximizeBtn, 3, 0);
this.titleLayoutPanel.Controls.Add(this.titlebar, 1, 0);
this.titleLayoutPanel.Controls.Add(this.minimizeBtn, 2, 0);
this.titleLayoutPanel.Controls.Add(this.label1, 0, 0);
this.titleLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.titleLayoutPanel.Location = new System.Drawing.Point(0, 0);
this.titleLayoutPanel.Name = "titleLayoutPanel";
this.titleLayoutPanel.RowCount = 1;
this.titleLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.titleLayoutPanel.Size = new System.Drawing.Size(698, 30);
this.titleLayoutPanel.TabIndex = 0;
this.titleLayoutPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Titlebar_MouseDown);
//
// closeWindowBtn
//
this.closeWindowBtn.Dock = System.Windows.Forms.DockStyle.Fill;
this.closeWindowBtn.FlatAppearance.BorderSize = 0;
this.closeWindowBtn.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(192)))), ((int)(((byte)(192)))));
this.closeWindowBtn.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Red;
this.closeWindowBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.closeWindowBtn.Font = new System.Drawing.Font("Segoe UI Symbol", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.closeWindowBtn.ForeColor = System.Drawing.SystemColors.ControlLightLight;
this.closeWindowBtn.Location = new System.Drawing.Point(658, 0);
this.closeWindowBtn.Margin = new System.Windows.Forms.Padding(0);
this.closeWindowBtn.Name = "closeWindowBtn";
this.closeWindowBtn.Size = new System.Drawing.Size(40, 30);
this.closeWindowBtn.TabIndex = 3;
this.closeWindowBtn.TabStop = false;
this.closeWindowBtn.Text = "";
this.closeWindowBtn.UseVisualStyleBackColor = true;
this.closeWindowBtn.Click += new System.EventHandler(this.CloseBtn_Click);
//
// maximizeBtn
//
this.maximizeBtn.Dock = System.Windows.Forms.DockStyle.Fill;
this.maximizeBtn.FlatAppearance.BorderSize = 0;
this.maximizeBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.maximizeBtn.Font = new System.Drawing.Font("Segoe UI Symbol", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.maximizeBtn.ForeColor = System.Drawing.SystemColors.ControlLightLight;
this.maximizeBtn.Location = new System.Drawing.Point(618, 0);
this.maximizeBtn.Margin = new System.Windows.Forms.Padding(0);
this.maximizeBtn.Name = "maximizeBtn";
this.maximizeBtn.Size = new System.Drawing.Size(40, 30);
this.maximizeBtn.TabIndex = 2;
this.maximizeBtn.TabStop = false;
this.maximizeBtn.Text = "□";
this.maximizeBtn.UseVisualStyleBackColor = true;
this.maximizeBtn.Click += new System.EventHandler(this.MaximizeBtn_Click);
//
// titlebar
//
this.titlebar.Dock = System.Windows.Forms.DockStyle.Fill;
this.titlebar.Font = new System.Drawing.Font("Segoe UI", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.titlebar.ForeColor = System.Drawing.SystemColors.ControlLightLight;
this.titlebar.Location = new System.Drawing.Point(123, 0);
this.titlebar.Name = "titlebar";
this.titlebar.Size = new System.Drawing.Size(452, 30);
this.titlebar.TabIndex = 0;
this.titlebar.Text = "titleBar";
this.titlebar.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.titlebar.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Titlebar_MouseDown);
//
// minimizeBtn
//
this.minimizeBtn.Dock = System.Windows.Forms.DockStyle.Fill;
this.minimizeBtn.FlatAppearance.BorderSize = 0;
this.minimizeBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.minimizeBtn.Font = new System.Drawing.Font("Segoe UI Symbol", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.minimizeBtn.ForeColor = System.Drawing.SystemColors.ControlLightLight;
this.minimizeBtn.Location = new System.Drawing.Point(578, 0);
this.minimizeBtn.Margin = new System.Windows.Forms.Padding(0);
this.minimizeBtn.Name = "minimizeBtn";
this.minimizeBtn.Size = new System.Drawing.Size(40, 30);
this.minimizeBtn.TabIndex = 1;
this.minimizeBtn.TabStop = false;
this.minimizeBtn.Text = "—";
this.minimizeBtn.UseVisualStyleBackColor = true;
this.minimizeBtn.Click += new System.EventHandler(this.MinimizeBtn_Click);
//
// label1
//
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
this.label1.Font = new System.Drawing.Font("Segoe UI Symbol", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label1.ForeColor = System.Drawing.SystemColors.Window;
this.label1.Location = new System.Drawing.Point(3, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(114, 30);
this.label1.TabIndex = 4;
this.label1.Text = "⛽";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// splitContainer3
//
this.splitContainer3.BackColor = System.Drawing.SystemColors.Control;
this.splitContainer3.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer3.FixedPanel = System.Windows.Forms.FixedPanel.Panel2;
this.splitContainer3.IsSplitterFixed = true;
this.splitContainer3.Location = new System.Drawing.Point(0, 0);
this.splitContainer3.Name = "splitContainer3";
this.splitContainer3.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// splitContainer3.Panel1
//
this.splitContainer3.Panel1.Controls.Add(this.changelogBrowser);
//
// splitContainer3.Panel2
//
this.splitContainer3.Panel2.Controls.Add(this.progressBar1);
this.splitContainer3.Panel2.Controls.Add(this.closeBtn);
this.splitContainer3.Panel2.Controls.Add(this.updBtn);
this.splitContainer3.Size = new System.Drawing.Size(698, 404);
this.splitContainer3.SplitterDistance = 344;
this.splitContainer3.TabIndex = 7;
this.splitContainer3.TabStop = false;
//
// changelogBrowser
//
this.changelogBrowser.AllowNavigation = false;
this.changelogBrowser.AllowWebBrowserDrop = false;
this.changelogBrowser.Dock = System.Windows.Forms.DockStyle.Fill;
this.changelogBrowser.IsWebBrowserContextMenuEnabled = false;
this.changelogBrowser.Location = new System.Drawing.Point(0, 0);
this.changelogBrowser.MinimumSize = new System.Drawing.Size(19, 18);
this.changelogBrowser.Name = "changelogBrowser";
this.changelogBrowser.ScriptErrorsSuppressed = true;
this.changelogBrowser.Size = new System.Drawing.Size(698, 344);
this.changelogBrowser.TabIndex = 7;
this.changelogBrowser.TabStop = false;
this.changelogBrowser.WebBrowserShortcutsEnabled = false;
//
// progressBar1
//
this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.progressBar1.Location = new System.Drawing.Point(12, 14);
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(487, 30);
this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
this.progressBar1.TabIndex = 4;
this.progressBar1.Visible = false;
//
// closeBtn
//
this.closeBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.closeBtn.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(253)))), ((int)(((byte)(253)))), ((int)(((byte)(253)))));
this.closeBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.closeBtn.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(171)))), ((int)(((byte)(171)))), ((int)(((byte)(171)))));
this.closeBtn.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(146)))), ((int)(((byte)(192)))), ((int)(((byte)(224)))));
this.closeBtn.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(205)))), ((int)(((byte)(230)))), ((int)(((byte)(247)))));
this.closeBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.closeBtn.ForeColor = System.Drawing.SystemColors.ControlText;
this.closeBtn.Location = new System.Drawing.Point(507, 14);
this.closeBtn.Name = "closeBtn";
this.closeBtn.Size = new System.Drawing.Size(179, 30);
this.closeBtn.TabIndex = 7;
this.closeBtn.Text = "Close";
this.closeBtn.UseVisualStyleBackColor = false;
//
// updBtn
//
this.updBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.updBtn.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(253)))), ((int)(((byte)(253)))), ((int)(((byte)(253)))));
this.updBtn.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(171)))), ((int)(((byte)(171)))), ((int)(((byte)(171)))));
this.updBtn.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(146)))), ((int)(((byte)(192)))), ((int)(((byte)(224)))));
this.updBtn.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(205)))), ((int)(((byte)(230)))), ((int)(((byte)(247)))));
this.updBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.updBtn.Location = new System.Drawing.Point(13, 14);
this.updBtn.Name = "updBtn";
this.updBtn.Size = new System.Drawing.Size(486, 30);
this.updBtn.TabIndex = 5;
this.updBtn.Text = "Update";
this.updBtn.UseVisualStyleBackColor = false;
this.updBtn.Visible = false;
this.updBtn.Click += new System.EventHandler(this.UpdBtn_Click);
//
// panel1
//
this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(131)))), ((int)(((byte)(131)))), ((int)(((byte)(131)))));
this.panel1.Controls.Add(this.mainSplitContainer);
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel1.Location = new System.Drawing.Point(0, 0);
this.panel1.Margin = new System.Windows.Forms.Padding(10);
this.panel1.Name = "panel1";
this.panel1.Padding = new System.Windows.Forms.Padding(1);
this.panel1.Size = new System.Drawing.Size(700, 440);
this.panel1.TabIndex = 8;
//
// UpdateWindow
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(700, 440);
this.ControlBox = false;
this.Controls.Add(this.panel1);
this.DoubleBuffered = true;
this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "UpdateWindow";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Load += new System.EventHandler(this.UpdateWindow_Load);
this.mainSplitContainer.Panel1.ResumeLayout(false);
this.mainSplitContainer.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.mainSplitContainer)).EndInit();
this.mainSplitContainer.ResumeLayout(false);
this.titleLayoutPanel.ResumeLayout(false);
this.splitContainer3.Panel1.ResumeLayout(false);
this.splitContainer3.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit();
this.splitContainer3.ResumeLayout(false);
this.panel1.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.SplitContainer mainSplitContainer;
private System.Windows.Forms.SplitContainer splitContainer3;
private System.Windows.Forms.WebBrowser changelogBrowser;
private System.Windows.Forms.Button closeBtn;
private System.Windows.Forms.Button updBtn;
private System.Windows.Forms.ProgressBar progressBar1;
private System.Windows.Forms.TableLayoutPanel titleLayoutPanel;
private System.Windows.Forms.Label titlebar;
private System.Windows.Forms.Button closeWindowBtn;
private System.Windows.Forms.Button maximizeBtn;
private System.Windows.Forms.Button minimizeBtn;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Label label1;
}
}

View File

@ -0,0 +1,146 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Katteker.Gui.Properties;
namespace Katteker.Gui
{
public sealed partial class UpdateWindow : Form
{
private const int HTCAPTION = 0x2;
private const int WM_NCLBUTTONDOWN = 0xA1;
/// <summary>
/// The Update Window
/// </summary>
public UpdateWindow()
{
Font = SystemFonts.MessageBoxFont;
Application.EnableVisualStyles();
if (Environment.OSVersion.Version.Major >= 6)
SetProcessDPIAware();
InitializeComponent();
}
private string Changelog => UpdateManager.ChangelogFilename;
/// <summary>
/// Last release entry.
/// </summary>
public ReleaseEntry ReleaseEntry { get; set; }
/// <summary>
/// Update manager
/// </summary>
public UpdateManager UpdateManager { get; set; }
private string PublishPath => UpdateManager.UrlOrPath;
/// <inheritdoc />
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing)
{
components?.Dispose();
}
base.Dispose(disposing);
}
[DllImport("User32.dll")]
private static extern bool ReleaseCapture();
[DllImport("User32.dll")]
private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("user32.dll")]
private static extern bool SetProcessDPIAware();
private void CloseBtn_Click(object sender, EventArgs e)
{
Close();
}
private void MaximizeBtn_Click(object sender, EventArgs e)
{
WindowState = WindowState != FormWindowState.Maximized ? FormWindowState.Maximized : FormWindowState.Normal;
}
private void MinimizeBtn_Click(object sender, EventArgs e)
{
WindowState = FormWindowState.Minimized;
}
private void Titlebar_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
ReleaseCapture();
SendMessage(Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0);
}
private async void UpdateWindow_Load(object sender, EventArgs e)
{
var changelog = await ChangelogHelper.LoadChangelogAsync(Changelog, PublishPath).ConfigureAwait(false);
Invoke(new Action(() => changelogBrowser.DocumentText = changelog));
if (ReleaseEntry == null)
{
Invoke(new Action(() => { WriteTitle(Resources.No_update_available); }));
}
else
{
var latest = ReleaseEntry.Version;
Invoke(new Action(() =>
{
WriteTitle(Resources.You_can_update_to_Version + latest);
updBtn.Visible = true;
}));
}
}
private async void UpdBtn_Click(object sender, EventArgs e)
{
Invoke(new Action(() => progressBar1.Visible = true));
try
{
var progress = new Progress<int>(x => Invoke(new Action(() => { progressBar1.Value = x; })));
await UpdateManager.UpdateAppAsync(progress).ConfigureAwait(false);
WriteTitle(Resources.You_re_up_to_date);
}
catch (Exception ex)
{
WriteTitle(ex.Message);
}
finally
{
Invoke(new Action(() =>
{
progressBar1.Visible = false;
updBtn.Visible = false;
var messResult = MessageBox.Show(
Resources.Installed_new_Version,
Resources.New_Version,
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (messResult == DialogResult.Yes)
{
DialogResult = DialogResult.OK;
Close();
}
}));
}
}
private void WriteTitle(string text)
{
Invoke(new Action(() =>
{
Text = text;
titlebar.Text = text;
}));
}
}
}

View File

@ -0,0 +1,197 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAEAICAAAAEAIACoEAAAFgAAACgAAAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYYGwFCSVQrTlloXjZEUYFIV2iiUmqFt1h2
m8o1UHHcEShHPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhgcBiQuPs5khK7/fajd/3up
4P94q+b/fa3p/0Jml/8NIkVQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEB80eUdy
qv9+rOj/eqfg/3Sk3f96p+D/SG2f/xMZKWMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAMGS45OWCR/1yOzf9Gdar/SXyr/0uBtP85bqT/IUBk3ilBU2JzhZUaAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAADZHWDwwUX3/Y5e//5vI1P+dvMX/k6+4/5avtf+Rtsb/i7jF/3ibsfJcdZmyHjpgYD5T
Wg8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAB5g5YIZYiZuqzb6P+u1s//fIud/4CBsf+Pjsn/jZHE/4KKuf+JlbX/nb7F/7vq
9P+WwNT/Wnqf126KkG+ZqqQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAOzUyC1Ntjqm56vP/ncDB/3Z4pP+snur/opvb/4mHwP+Cgq7/eniW/6Sb
3v9eXYT/U11t/7/k5//S////jsDR/16Bnc5ufItFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLX22flMfc/7HVzv97d5//m5Hh/4KGof+coqb/yc3O//Xz
7v/j4Nv/TE9g/y0uXf81KmL/Pj1n/6LLyv/i////w/L4/3mmv/txfJd7AAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjqCWVX6owf/P+fn/coCa/7qx+v+Mhrr/ra+v////
///l5+j/4OPh//////+Qkpj/KiRl/zgyfv83LG7/Ojde/6rGyv/h////0v///4y7zf9pg5R2AAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAImhuCldiqDm0P///5q4tv9WSnb/fn64/2ht
lf9ZVG3//Pv3//Lw8v+xsLT//////+jm4f9ANFX/JyJn/ycrX/8nHWL/PD5i/5m3tP+76ez/1P///3+f
rda5upINAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADD0KwKaIOVv7Dj7f/f////XHF4/yki
Yv8vK2L/LS5m/ykjVP+yr7X//////5yan/+Mk5T/mqWq/3qMlP95lKL/gZqp/32Po/9JVWn/ZG9u/837
/P/S////hay3/7i5lVEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHiOkn2YwtT/2////8fm
6v85Q1//Ni15/zYzeP8yMX7/KiNe/0pMUv+To5n/k6+2/7vc3v+339f/xPXv/9r////Z////2f///6DG
w/+22d7/2v///8X4/f9ehZvPubuYAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYnpg7apO398//
///T////p8bM/y8sX/8/MH3/NTBx/zAmXP9bZHv/psbK/8z4+f/Y////jau2/3B7ov98gqf/hZWd/7/m
4f/a////xe30/8L7/f/a////eqfG/3WEk4EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGR3
lbWw5/L/0v///9P///+Jp67/Lydl/zgubP8xOFv/kKmx/9T++//N////1v///6S8wf97d6r/trD//7Wv
//+gm+H/X3GM/7DY3v/N/Pn/1////4691/9Nboa3xMKtBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTF
mw2Ln5NQfqPD/9P////N+f//2f///3WKlP8rImH/Nz5m/6zN0P/a////yfv//8z7///N9uj/cn6d/7Wx
+v+srPj/n5ve/3t3ov9JWoD/W4i0/2aax/9chrf/UnKM1K64lRoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAmpuYUnOEmKuv3+n/z/7//8z8/P/a////b3iN/yYpS/+iw87/1////8z8/P/O/P3/2P///5i9
0P9SXoT/doOz/19rkf9IS2D/Z2ls/5GWnP91gpr/ZJDE/0hytf+KlYiDAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAABje5eGbY6k2cX9/v/P/f//yfj7/5/Awv8+SVL/bH2B/+D////J+f//0f///8Pz
+v9smLD/Pl59/2Jmdv9ecZT/ZX6f/6Ouuf////////////r49P+RoKv/UXiw/159lJwAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGF9nYltlq3cx////9D7///L+f3/v+Pm/6nDyP94nJn/vevn/9f/
//+75+7/Tnmi/1Jpiv/V2df/9vr4/9Pc4/+CiY3/5ubn/8XHx/+YnJ///////+no4/9aeJz/P2abtgAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmJyVU3yTl6u97vb/z/3//8z7/f/M////1f///8/5
+//Q/f7/uefs/0l6rv9bbY7/4Nzi/////P/q7en//////+fm5f/HxMP/fHl4/zEvMf/4+Pz/7+7s/2J/
mv9WdqDNs6yOAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC6uqAElp6UQoSqxf/Q////z/3//838
///N/v3/2f///7zm8f9HdqT/V4nE/3uFl///////kpeU/yIhJv/n4+H//////7a0s//v7u3/6enm////
//+4t7X/X4Sn/1N8s/ylraAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADI0LgCdYqWmXeh
vP656/j/yvn//8n1/v+czNr/R3Sg/1aHyP94p+L/dYKP//v7+f/IyMv/gICE//Tx8///////f4SL/7q4
uv/c4eP/qK+t/1ReZv9WdJj/Y47M/0VtlMNujZWShJiUL7O9tQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAjJOLS2mJmNFtj6XsaJCX6DpchPFAcrT/gqvi/3io4P9lhK3/r7K3/////////////////7Oz
q/9YfZ7/W4W2/0ZUcP9OXXT/Ynen/3Sj1f91ptv/WIbH/0Vwsf9EcbD+cImVtbu/ogoAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAwdKpC622sRSnv7ARpLOtJkRrltptoND/eaba/3Gj4f9ngKH/ipCW/5+n
rf93gIT/SlVz/1p1m/90qtv/ZY64/32k1P90ntD/XY3L/zpuj/9gh8L/bJ3W/3uk2/9Vicr/cIqQeZGm
kyoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiJ2VXE57tP99qd7/dKTe/3ur
6P9Taoj/UG+S/0tjfv9ojsD/d5rU/3qm2f95ruP/b53k/059sv9WcJWdqa6gO2Z2h6BMfLP/dKTS/2aT
1P9vhZawnKeWVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADGxKsHVXSbx2+e
1f91pdz/eKXe/3KbzP92pNj/d6XZ/3Wl2f97q+P/dJ7Z/1SDwP9Ca5XbfY2NVwAAAAAAAAAAw86YBYib
mYY/ZpTDSW2Pt7G1oyvOyqsEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAB9kZRiVYO8/3um4f95p9n/d6bf/3Wk2/92nM3/YIrD/0R4sP5QdpjhY4KVc6S3oBMAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAK60n0ZNd6z/fazh/0uAvP49ZpfGJ1mXwEFpmrV5j5eTcIeNM4qlsQ4AAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAoa2ZS1KAsP9vo9f/WYG0/3SFi00AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUnJZOWoi4/2CKyP9jksv/V3+cxoykrQMAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJadk0xUgLX/cp3a/3Wh2v9ReqLWcHuVBwAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtr+jJENtlehZjdH/VH22/3uU
l38AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg6KaP3KG
jb2MnJd0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA/wB///8Af///gH///4Af//+AA///AAD//gAAf/4AAD/8AAAf+AAAD/AA
AA/wAAAP4AAAH+AAAB+AAAA/gAAAf4AAAH+AAAB/gAAAP4AAAD/AAAAH8AAAA/gAAAH/gAAB/4ABgf/A
A///wA///8H////A////wP///8H////j//8=
</value>
</data>
</root>

60
Katteker.Gui/Wrapper.cs Normal file
View File

@ -0,0 +1,60 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Katteker.Gui.Properties;
namespace Katteker.Gui
{
/// <summary>
/// The wrapper to add Squirrel-capability to older Programs.
/// </summary>
public static class Wrapper
{
/// <summary>
/// Checks for Updates.
/// </summary>
/// <returns>Task</returns>
public static Task CheckForUpdateAsync() => CheckForUpdateAsync(false);
/// <summary>
/// Checks for Updates.
/// </summary>
/// <param name="isStartup">Is this Method called on Startup of the Program.</param>
/// <returns>Task</returns>
public static async Task CheckForUpdateAsync(bool isStartup)
{
using (var window = new UpdateWindow())
{
var dialogResult = DialogResult.Cancel;
if (!UpdateManager.TryCreate(out var manager) && !isStartup)
{
MessageBox.Show(Resources.SquirrelWrapper_CheckForUpdate,
Resources.SquirrelWrapper_CheckForUpdate_Error, MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
var releases = (await manager.CheckForUpdateAsync().ConfigureAwait(true)).ToArray();
window.UpdateManager = manager;
window.ReleaseEntry = releases.LastOrDefault();
if (isStartup)
{
if (releases?.Any() == true)
{
dialogResult = window.ShowDialog();
}
}
else
{
dialogResult = window.ShowDialog();
}
if (dialogResult == DialogResult.OK)
{
manager.RestartApp();
}
}
}
}
}

23
Katteker.Gui/app.config Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="NuGet.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.8.50926.602" newVersion="2.8.50926.602" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Mono.Cecil" publicKeyToken="0738eb9f132ed756" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-0.9.5.0" newVersion="0.9.5.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="DeltaCompressionDotNet.MsDelta" publicKeyToken="46b2138a390abf55" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpCompress" publicKeyToken="afb0a02973931d96" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-0.19.2.0" newVersion="0.19.2.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonMark.NET" version="0.15.1" targetFramework="net45" />
</packages>

49
Katteker.sln Normal file
View File

@ -0,0 +1,49 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2036
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KattekerCreator", "Creator\KattekerCreator.csproj", "{53D065EB-8818-4F60-9EBE-75705E1BB00D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Katteker", "Katteker\Katteker.csproj", "{A45E1C59-BA9E-452C-A5E2-50DE49D53E92}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppStub", "AppStub\AppStub.csproj", "{E746AE0F-BEEA-4C18-9ED8-2E708ED00A5B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{DE9CFDEB-40B8-447D-9959-31BA78512CED}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Katteker.Gui", "Katteker.Gui\Katteker.Gui.csproj", "{07E2DE31-80A0-43DA-B307-1CA47CD930A1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{53D065EB-8818-4F60-9EBE-75705E1BB00D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53D065EB-8818-4F60-9EBE-75705E1BB00D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{53D065EB-8818-4F60-9EBE-75705E1BB00D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{53D065EB-8818-4F60-9EBE-75705E1BB00D}.Release|Any CPU.Build.0 = Release|Any CPU
{A45E1C59-BA9E-452C-A5E2-50DE49D53E92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A45E1C59-BA9E-452C-A5E2-50DE49D53E92}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A45E1C59-BA9E-452C-A5E2-50DE49D53E92}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A45E1C59-BA9E-452C-A5E2-50DE49D53E92}.Release|Any CPU.Build.0 = Release|Any CPU
{E746AE0F-BEEA-4C18-9ED8-2E708ED00A5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E746AE0F-BEEA-4C18-9ED8-2E708ED00A5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E746AE0F-BEEA-4C18-9ED8-2E708ED00A5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E746AE0F-BEEA-4C18-9ED8-2E708ED00A5B}.Release|Any CPU.Build.0 = Release|Any CPU
{DE9CFDEB-40B8-447D-9959-31BA78512CED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DE9CFDEB-40B8-447D-9959-31BA78512CED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE9CFDEB-40B8-447D-9959-31BA78512CED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE9CFDEB-40B8-447D-9959-31BA78512CED}.Release|Any CPU.Build.0 = Release|Any CPU
{07E2DE31-80A0-43DA-B307-1CA47CD930A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{07E2DE31-80A0-43DA-B307-1CA47CD930A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07E2DE31-80A0-43DA-B307-1CA47CD930A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07E2DE31-80A0-43DA-B307-1CA47CD930A1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EAAA54E0-C857-4C5C-BD5D-14A21D462081}
EndGlobalSection
EndGlobal

30
Katteker/Constants.cs Normal file
View File

@ -0,0 +1,30 @@
namespace Katteker
{
public static class Constants
{
/// <summary>
/// Filename of the configuration-file.
/// </summary>
public const string KattekerConfig = "Katteker.config";
/// <summary>
/// Filename of the setup-file
/// </summary>
public const string SetupFile = "setup.exe";
/// <summary>
/// Filename of the execution stub.
/// </summary>
public const string ExecutionStub = "AppStub.exe";
/// <summary>
/// File of the releases.
/// </summary>
public const string RELEASE = "RELEASE";
/// <summary>
/// Name of the
/// </summary>
public const string PackageFolder = "package";
}
}

53
Katteker/Katteker.csproj Normal file
View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A45E1C59-BA9E-452C-A5E2-50DE49D53E92}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Katteker</RootNamespace>
<AssemblyName>Katteker</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Katteker.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Constants.cs" />
<Compile Include="KattekerConfig.cs" />
<Compile Include="SemanticVersion.cs" />
<Compile Include="UpdateInfo.cs" />
<Compile Include="UpdateManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ReleaseEntry.cs" />
<Compile Include="Releases.cs" />
<Compile Include="Utility.cs" />
<Compile Include="VersionExtension.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,38 @@
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
namespace Katteker
{
[DataContract]
public class KattekerConfig
{
[DataMember]
public string PublishDir { get; set; }
[DataMember]
public string Changelog { get; set; }
public static KattekerConfig ReadFromFile(string path)
{
if (!File.Exists(path)) throw new FileNotFoundException();
var dataContractJsonSerializer = new DataContractJsonSerializer(typeof(KattekerConfig));
using (var fileStream = File.OpenRead(path))
{
var obj = dataContractJsonSerializer.ReadObject(fileStream);
return (KattekerConfig) obj;
}
}
public void WriteToFile(string path)
{
if (File.Exists(path)) File.Delete(path);
using (var fileStream = File.OpenWrite(path))
{
var dataContractJsonSerializer = new DataContractJsonSerializer(typeof(KattekerConfig));
dataContractJsonSerializer.WriteObject(fileStream, this);
}
}
}
}

View File

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("Katteker")]
[assembly: AssemblyDescription("Katteker Client")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Holger Börchers")]
[assembly: AssemblyProduct("Katteker")]
[assembly: AssemblyCopyright("Copyright © Holger Börchers 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("a45e1c59-ba9e-452c-a5e2-50de49d53e92")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
// indem Sie "*" wie unten gezeigt eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

92
Katteker/ReleaseEntry.cs Normal file
View File

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Katteker
{
public class ReleaseEntry : IComparable<ReleaseEntry>, IComparable
{
public const string FilenameRegex = @"(^.*)-((?: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-]+)*)?).*-(full|delta)";
public ReleaseEntry(string filename, SemanticVersion version, long fileSize, bool isDelta, string sha1)
{
Filename = filename;
Version = version;
Filesize = fileSize;
IsDelta = isDelta;
SHA1 = sha1;
}
public ReleaseEntry(string line)
{
var elements = line?.Split(' ');
if (elements?.Length == 3)
{
SHA1 = elements[0];
Filename = elements[1];
Filesize = long.Parse(elements[2]);
var fileSegments = Regex.Match(Filename, FilenameRegex);
if (fileSegments.Groups.Count < 3) throw new ArgumentOutOfRangeException("Filename is not compilant.");
ApplicationName = fileSegments.Groups[1].Value;
Version = SemanticVersion.Parse(fileSegments.Groups[2].Value);
IsDelta = fileSegments.Groups[3].Value != "full";
}
}
public ReleaseEntry(string applicationName, SemanticVersion version)
{
ApplicationName = applicationName;
Version = version;
}
public SemanticVersion Version { get; }
public string SHA1 { get; }
public string Filename { get; }
public long Filesize { get; }
public bool IsDelta { get; }
public string ApplicationName { get; }
public string EntryAsString => $"{SHA1} {Filename} {Filesize}";
public override string ToString() => $"{ApplicationName} {Version}";
public int CompareTo(object obj) => CompareTo(obj as ReleaseEntry);
public int CompareTo(ReleaseEntry other)
{
if (ReferenceEquals(this, other)) return 0;
if (ReferenceEquals(null, other)) return 1;
var versionComparison = Comparer<SemanticVersion>.Default.Compare(Version, other.Version);
if (versionComparison != 0) return versionComparison;
return string.Compare(Filename, other.Filename, StringComparison.Ordinal);
}
public override bool Equals(object obj) => Equals(obj as ReleaseEntry);
protected bool Equals(ReleaseEntry other)
{
return Equals(Version, other.Version) && string.Equals(SHA1, other.SHA1) && string.Equals(Filename, other.Filename) && Filesize == other.Filesize && IsDelta == other.IsDelta;
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (Version != null ? Version.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (SHA1 != null ? SHA1.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (Filename != null ? Filename.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ Filesize.GetHashCode();
hashCode = (hashCode * 397) ^ IsDelta.GetHashCode();
return hashCode;
}
}
public static bool operator ==(ReleaseEntry left, ReleaseEntry right)
{
return Equals(left, right);
}
public static bool operator !=(ReleaseEntry left, ReleaseEntry right)
{
return !Equals(left, right);
}
}
}

79
Katteker/Releases.cs Normal file
View File

@ -0,0 +1,79 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Katteker
{
public class Releases : IEnumerable<ReleaseEntry>
{
private readonly string _filePath;
private SortedList<SemanticVersion, ReleaseEntry> ReleaseEntries { get; }
private Releases()
{
ReleaseEntries = new SortedList<SemanticVersion, ReleaseEntry>();
}
public Releases(string path) : this()
{
_filePath = Path.Combine(path, Constants.RELEASE);
if (!File.Exists(_filePath)) return;
AddRange(File.ReadAllLines(_filePath));
}
public Releases(string[] content) : this()
{
AddRange(content);
}
private void AddRange(IEnumerable<string> content)
{
foreach (var line in content)
{
var entry = new ReleaseEntry(line);
ReleaseEntries.Add(entry.Version, entry);
}
}
public void WriteReleaseFile()
{
File.WriteAllLines(_filePath, ReleaseEntries.Select(x => x.Value.EntryAsString));
}
/// <summary>
/// Adds new entry to file.
/// </summary>
/// <param name="setupFilePath">Path to setup-file</param>
/// <param name="version">Version of setup-file</param>
/// <returns>Returns true if newest version.</returns>
public ReleaseEntry Add(string setupFilePath, SemanticVersion version)
{
var sha1 = Utility.ComputeFileHash(setupFilePath);
var setupFile = new FileInfo(setupFilePath);
var entry = new ReleaseEntry(setupFile.Name, version, setupFile.Length, false, sha1);
if (!ReleaseEntries.ContainsValue(entry))
{
if (ReleaseEntries.ContainsKey(version))
{
ReleaseEntries.Remove(version);
}
ReleaseEntries.Add(version, entry);
}
return entry;
}
public bool IsLatestEntry(ReleaseEntry entry) => entry.Equals(ReleaseEntries.LastOrDefault().Value);
public IEnumerator<ReleaseEntry> GetEnumerator()
{
return ReleaseEntries.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

511
Katteker/SemanticVersion.cs Normal file
View File

@ -0,0 +1,511 @@
using System;
using System.Globalization;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Text.RegularExpressions;
namespace Katteker
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
/// <summary>
/// A semantic version implementation.
/// Conforms to v2.0.0 of http://semver.org/
/// </summary>
[Serializable]
public sealed class SemanticVersion : IComparable<SemanticVersion>, IComparable, ISerializable
{
private static readonly Regex parseEx =
new Regex(@"^(?<major>\d+)(\.(?<minor>\d+))?(\.(?<patch>\d+))?(\-(?<pre>[0-9A-Za-z\-\.]+))?(\+(?<build>[0-9A-Za-z\-\.]+))?$",
RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
/// <summary>
/// Initializes a new instance of the <see cref="SemanticVersion" /> class.
/// </summary>
/// <param name="info"></param>
/// <param name="context"></param>
/// <exception cref="ArgumentNullException"></exception>
private SemanticVersion(SerializationInfo info, StreamingContext context)
{
if (info == null) throw new ArgumentNullException(nameof(info));
var semVersion = Parse(info.GetString("SemanticVersion"));
Major = semVersion.Major;
Minor = semVersion.Minor;
Patch = semVersion.Patch;
Prerelease = semVersion.Prerelease;
Build = semVersion.Build;
}
/// <summary>
/// Initializes a new instance of the <see cref="SemanticVersion" /> class.
/// </summary>
/// <param name="major">The major version.</param>
/// <param name="minor">The minor version.</param>
/// <param name="patch">The patch version.</param>
/// <param name="prerelease">The prerelease version (eg. "alpha").</param>
/// <param name="build">The build eg ("nightly.232").</param>
public SemanticVersion(int major, int minor = 0, int patch = 0, string prerelease = "", string build = "")
{
Major = major;
Minor = minor;
Patch = patch;
Prerelease = prerelease ?? "";
Build = build ?? "";
}
/// <summary>
/// Initializes a new instance of the <see cref="SemanticVersion"/> class.
/// </summary>
/// <param name="version">The <see cref="System.Version"/> that is used to initialize
/// the Major, Minor, Patch and Build properties.</param>
public SemanticVersion(Version version)
{
if (version == null)
throw new ArgumentNullException(nameof(version));
Major = version.Major;
Minor = version.Minor;
if (version.Revision >= 0)
{
Patch = version.Revision;
}
Prerelease = string.Empty;
if (version.Build > 0)
{
Build = version.Build.ToString();
}
else
{
Build = string.Empty;
}
}
/// <summary>
/// Parses the specified string to a semantic version.
/// </summary>
/// <param name="version">The version string.</param>
/// <param name="strict">If set to <c>true</c> minor and patch version are required, else they default to 0.</param>
/// <returns>The SemanticVersion object.</returns>
/// <exception cref="System.InvalidOperationException">When a invalid version string is passed.</exception>
public static SemanticVersion Parse(string version, bool strict = false)
{
var match = parseEx.Match(version);
if (!match.Success)
throw new ArgumentException("Invalid version.", nameof(version));
var major = int.Parse(match.Groups["major"].Value, CultureInfo.InvariantCulture);
var minorMatch = match.Groups["minor"];
var minor = 0;
if (minorMatch.Success)
{
minor = int.Parse(minorMatch.Value, CultureInfo.InvariantCulture);
}
else if (strict)
{
throw new InvalidOperationException("Invalid version (no minor version given in strict mode)");
}
var patchMatch = match.Groups["patch"];
var patch = 0;
if (patchMatch.Success)
{
patch = int.Parse(patchMatch.Value, CultureInfo.InvariantCulture);
}
else if (strict)
{
throw new InvalidOperationException("Invalid version (no patch version given in strict mode)");
}
var prerelease = match.Groups["pre"].Value;
var build = match.Groups["build"].Value;
return new SemanticVersion(major, minor, patch, prerelease, build);
}
/// <summary>
/// Parses the specified string to a semantic version.
/// </summary>
/// <param name="version">The version string.</param>
/// <param name="semver">When the method returns, contains a SemanticVersion instance equivalent
/// to the version string passed in, if the version string was valid, or <c>null</c> if the
/// version string was not valid.</param>
/// <param name="strict">If set to <c>true</c> minor and patch version are required, else they default to 0.</param>
/// <returns><c>False</c> when a invalid version string is passed, otherwise <c>true</c>.</returns>
public static bool TryParse(string version, out SemanticVersion semver, bool strict = false)
{
try
{
semver = Parse(version, strict);
return true;
}
catch (Exception)
{
semver = null;
return false;
}
}
/// <summary>
/// Tests the specified versions for equality.
/// </summary>
/// <param name="versionA">The first version.</param>
/// <param name="versionB">The second version.</param>
/// <returns>If versionA is equal to versionB <c>true</c>, else <c>false</c>.</returns>
public static bool Equals(SemanticVersion versionA, SemanticVersion versionB)
{
if (ReferenceEquals(versionA, null))
return ReferenceEquals(versionB, null);
return versionA.Equals(versionB);
}
/// <summary>
/// Compares the specified versions.
/// </summary>
/// <param name="versionA">The version to compare to.</param>
/// <param name="versionB">The version to compare against.</param>
/// <returns>If versionA &lt; versionB <c>&lt; 0</c>, if versionA &gt; versionB <c>&gt; 0</c>,
/// if versionA is equal to versionB <c>0</c>.</returns>
public static int Compare(SemanticVersion versionA, SemanticVersion versionB)
{
if (ReferenceEquals(versionA, null))
return ReferenceEquals(versionB, null) ? 0 : -1;
return versionA.CompareTo(versionB);
}
/// <summary>
/// Make a copy of the current instance with optional altered fields.
/// </summary>
/// <param name="major">The major version.</param>
/// <param name="minor">The minor version.</param>
/// <param name="patch">The patch version.</param>
/// <param name="prerelease">The prerelease text.</param>
/// <param name="build">The build text.</param>
/// <returns>The new version object.</returns>
public SemanticVersion Change(int? major = null, int? minor = null, int? patch = null,
string prerelease = null, string build = null)
{
return new SemanticVersion(
major ?? Major,
minor ?? Minor,
patch ?? Patch,
prerelease ?? Prerelease,
build ?? Build);
}
/// <summary>
/// Gets the major version.
/// </summary>
/// <value>
/// The major version.
/// </value>
public int Major { get; private set; }
/// <summary>
/// Gets the minor version.
/// </summary>
/// <value>
/// The minor version.
/// </value>
public int Minor { get; private set; }
/// <summary>
/// Gets the patch version.
/// </summary>
/// <value>
/// The patch version.
/// </value>
public int Patch { get; private set; }
/// <summary>
/// Gets the pre-release version.
/// </summary>
/// <value>
/// The pre-release version.
/// </value>
public string Prerelease { get; private set; }
/// <summary>
/// Gets the build version.
/// </summary>
/// <value>
/// The build version.
/// </value>
public string Build { get; private set; }
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString()
{
var version = "" + Major + "." + Minor + "." + Patch;
if (!string.IsNullOrEmpty(Prerelease))
version += "-" + Prerelease;
if (!string.IsNullOrEmpty(Build))
version += "+" + Build;
return version;
}
/// <summary>
/// Compares the current instance with another object of the same type and returns an integer that indicates
/// whether the current instance precedes, follows, or occurs in the same position in the sort order as the
/// other object.
/// </summary>
/// <param name="obj">An object to compare with this instance.</param>
/// <returns>
/// A value that indicates the relative order of the objects being compared.
/// The return value has these meanings: Value Meaning Less than zero
/// This instance precedes <paramref name="obj" /> in the sort order.
/// Zero This instance occurs in the same position in the sort order as <paramref name="obj" />. i
/// Greater than zero This instance follows <paramref name="obj" /> in the sort order.
/// </returns>
public int CompareTo(object obj)
{
return CompareTo((SemanticVersion)obj);
}
/// <summary>
/// Compares the current instance with another object of the same type and returns an integer that indicates
/// whether the current instance precedes, follows, or occurs in the same position in the sort order as the
/// other object.
/// </summary>
/// <param name="other">An object to compare with this instance.</param>
/// <returns>
/// A value that indicates the relative order of the objects being compared.
/// The return value has these meanings: Value Meaning Less than zero
/// This instance precedes <paramref name="other" /> in the sort order.
/// Zero This instance occurs in the same position in the sort order as <paramref name="other" />. i
/// Greater than zero This instance follows <paramref name="other" /> in the sort order.
/// </returns>
public int CompareTo(SemanticVersion other)
{
if (ReferenceEquals(other, null))
return 1;
var r = CompareByPrecedence(other);
if (r != 0)
return r;
r = CompareComponent(Build, other.Build);
return r;
}
/// <summary>
/// Compares to semantic versions by precedence. This does the same as a Equals, but ignores the build information.
/// </summary>
/// <param name="other">The semantic version.</param>
/// <returns><c>true</c> if the version precedence matches.</returns>
public bool PrecedenceMatches(SemanticVersion other)
{
return CompareByPrecedence(other) == 0;
}
/// <summary>
/// Compares to semantic versions by precedence. This does the same as a Equals, but ignores the build information.
/// </summary>
/// <param name="other">The semantic version.</param>
/// <returns>
/// A value that indicates the relative order of the objects being compared.
/// The return value has these meanings: Value Meaning Less than zero
/// This instance precedes <paramref name="other" /> in the version precedence.
/// Zero This instance has the same precedence as <paramref name="other" />. i
/// Greater than zero This instance has creater precedence as <paramref name="other" />.
/// </returns>
public int CompareByPrecedence(SemanticVersion other)
{
if (ReferenceEquals(other, null))
return 1;
var r = Major.CompareTo(other.Major);
if (r != 0) return r;
r = Minor.CompareTo(other.Minor);
if (r != 0) return r;
r = Patch.CompareTo(other.Patch);
if (r != 0) return r;
r = CompareComponent(Prerelease, other.Prerelease, true);
return r;
}
static int CompareComponent(string a, string b, bool lower = false)
{
var aEmpty = string.IsNullOrEmpty(a);
var bEmpty = string.IsNullOrEmpty(b);
if (aEmpty && bEmpty)
return 0;
if (aEmpty)
return lower ? 1 : -1;
if (bEmpty)
return lower ? -1 : 1;
var aComps = a.Split('.');
var bComps = b.Split('.');
var minLen = Math.Min(aComps.Length, bComps.Length);
for (var i = 0; i < minLen; i++)
{
var ac = aComps[i];
var bc = bComps[i];
int anum, bnum;
var isanum = int.TryParse(ac, out anum);
var isbnum = int.TryParse(bc, out bnum);
int r;
if (isanum && isbnum)
{
r = anum.CompareTo(bnum);
if (r != 0) return anum.CompareTo(bnum);
}
else
{
if (isanum)
return -1;
if (isbnum)
return 1;
r = string.CompareOrdinal(ac, bc);
if (r != 0)
return r;
}
}
return aComps.Length.CompareTo(bComps.Length);
}
/// <summary>
/// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
if (ReferenceEquals(obj, null))
return false;
if (ReferenceEquals(this, obj))
return true;
var other = (SemanticVersion)obj;
return Major == other.Major &&
Minor == other.Minor &&
Patch == other.Patch &&
string.Equals(Prerelease, other.Prerelease, StringComparison.Ordinal) &&
string.Equals(Build, other.Build, StringComparison.Ordinal);
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override int GetHashCode()
{
unchecked
{
var result = Major.GetHashCode();
result = result * 31 + Minor.GetHashCode();
result = result * 31 + Patch.GetHashCode();
result = result * 31 + Prerelease.GetHashCode();
result = result * 31 + Build.GetHashCode();
return result;
}
}
#if !NETSTANDARD
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null) throw new ArgumentNullException(nameof(info));
info.AddValue("SemanticVersion", ToString());
}
#endif
/// <summary>
/// Implicit conversion from string to SemanticVersion.
/// </summary>
/// <param name="version">The semantic version.</param>
/// <returns>The SemanticVersion object.</returns>
public static implicit operator SemanticVersion(string version)
{
return Parse(version);
}
/// <summary>
/// The override of the equals operator.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns>If left is equal to right <c>true</c>, else <c>false</c>.</returns>
public static bool operator ==(SemanticVersion left, SemanticVersion right)
{
return Equals(left, right);
}
/// <summary>
/// The override of the un-equal operator.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns>If left is not equal to right <c>true</c>, else <c>false</c>.</returns>
public static bool operator !=(SemanticVersion left, SemanticVersion right)
{
return !Equals(left, right);
}
/// <summary>
/// The override of the greater operator.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns>If left is greater than right <c>true</c>, else <c>false</c>.</returns>
public static bool operator >(SemanticVersion left, SemanticVersion right)
{
return Compare(left, right) > 0;
}
/// <summary>
/// The override of the greater than or equal operator.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns>If left is greater than or equal to right <c>true</c>, else <c>false</c>.</returns>
public static bool operator >=(SemanticVersion left, SemanticVersion right)
{
return left == right || left > right;
}
/// <summary>
/// The override of the less operator.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns>If left is less than right <c>true</c>, else <c>false</c>.</returns>
public static bool operator <(SemanticVersion left, SemanticVersion right)
{
return Compare(left, right) < 0;
}
/// <summary>
/// The override of the less than or equal operator.
/// </summary>
/// <param name="left">The left value.</param>
/// <param name="right">The right value.</param>
/// <returns>If left is less than or equal to right <c>true</c>, else <c>false</c>.</returns>
public static bool operator <=(SemanticVersion left, SemanticVersion right)
{
return left == right || left < right;
}
}
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
}

29
Katteker/UpdateInfo.cs Normal file
View File

@ -0,0 +1,29 @@
using System.Collections.Generic;
namespace Katteker
{
public class UpdateInfo
{
private readonly Releases _releases;
public ReleaseEntry CurrentlyInstalledVersion { get; protected set; }
public ReleaseEntry FutureReleaseEntry { get; protected set; }
public List<ReleaseEntry> ReleasesToApply { get; protected set; } = new List<ReleaseEntry>();
public UpdateInfo(string applicationName, Releases releases)
{
_releases = releases;
CurrentlyInstalledVersion = new ReleaseEntry(applicationName, VersionExtension.GetCurrentVersion);
foreach (var release in releases)
{
if (applicationName.Equals(release.ApplicationName) && release.Version > CurrentlyInstalledVersion.Version)
{
ReleasesToApply.Add(release);
}
}
}
}
}

181
Katteker/UpdateManager.cs Normal file
View File

@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace Katteker
{
public class UpdateManager
{
private static readonly string _baseDir = AppDomain.CurrentDomain.BaseDirectory;
private static KattekerConfig _config;
private readonly string _applicationName;
private readonly string _packageDir;
private readonly string _rootAppDirectory;
private readonly string _urlOrPath;
private Releases _releases;
private UpdateManager(string urlOrPath, string applicationName, string rootDirectory)
{
_urlOrPath = urlOrPath;
_applicationName = applicationName;
_rootAppDirectory = rootDirectory;
var packageDir = Path.Combine(rootDirectory, Constants.PackageFolder);
if (!Directory.Exists(packageDir)) Directory.CreateDirectory(packageDir);
_packageDir = packageDir;
}
/// <summary>
/// Filename of the changelog.
/// </summary>
public string ChangelogFilename => _config.Changelog;
/// <summary>
/// Url or path where the update files are located.
/// </summary>
public string UrlOrPath => _urlOrPath;
public static UpdateManager Create(string urlOrPath = null, string applicationName = null, string rootDirectory = null)
{
_config = ReadConfigFile();
urlOrPath = urlOrPath ?? _config.PublishDir;
var appName = applicationName ?? Utility.GetApplicationName();
var rootAppDirectory = Path.Combine(rootDirectory ?? Utility.GetLocalAppDataDirectory(), appName);
return new UpdateManager(urlOrPath, appName, rootAppDirectory);
}
public static bool TryCreate(out UpdateManager manager, string urlOrPath = null, string applicationName = null, string rootDirectory = null)
{
try
{
manager = Create(urlOrPath, applicationName, rootDirectory);
return true;
}
catch (Exception)
{
manager = default(UpdateManager);
return false;
}
}
public async Task<IEnumerable<ReleaseEntry>> CheckForUpdateAsync()
{
_releases = Utility.IsWebUrl(_urlOrPath) ? await DownloadIndexAsync(_urlOrPath).ConfigureAwait(false) : GetFromFilesystem(_urlOrPath);
var updateInfo = new UpdateInfo(_applicationName, _releases);
return updateInfo.ReleasesToApply;
}
public void RestartApp(string exeToStart = null, string arguments = null)
{
exeToStart = exeToStart ?? Path.GetFileName(Assembly.GetEntryAssembly().Location);
var program = Path.Combine(_rootAppDirectory, exeToStart);
if (File.Exists(program))
{
Process.Start(program, arguments);
Thread.Sleep(500);
Environment.Exit(0);
}
}
public async Task<bool> UpdateAppAsync(IProgress<int> progress = null)
{
progress?.Report(0);
var updateEntries = (await CheckForUpdateAsync().ConfigureAwait(false)).ToArray();
var update = updateEntries.LastOrDefault();
if (update == null) return false;
progress?.Report(30);
return await UpdateAppImplAsync(update, progress).ConfigureAwait(false);
}
private static async Task<Releases> DownloadIndexAsync(string urlOrPath)
{
var url = urlOrPath.TrimEnd('/');
url += "/" + Constants.RELEASE;
var content = await new WebClient().DownloadStringTaskAsync(url).ConfigureAwait(false);
var lines = content.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
return new Releases(lines);
}
private static Releases GetFromFilesystem(string urlOrPath) => new Releases(urlOrPath);
private static KattekerConfig ReadConfigFile()
{
var configPath = Path.Combine(_baseDir, Constants.KattekerConfig);
if (!File.Exists(configPath)) throw new FileNotFoundException("Configuration file not found.", configPath);
return KattekerConfig.ReadFromFile(configPath);
}
private static bool VerifyFileChecksum(string targetFile, string lastEntrySha1)
{
var hash = Utility.ComputeFileHash(targetFile);
return lastEntrySha1.Equals(hash);
}
private Task KillAppStubAsync()
{
foreach (var process in Process.GetProcessesByName(_applicationName))
{
var path = Path.GetDirectoryName(process.MainModule.FileName);
if (_rootAppDirectory.Equals(path))
{
process.Kill();
return Task.Delay(500);
}
}
return Task.FromResult(true);
}
private async Task PutFileInPackageFolderAsync(string filename)
{
var targetFile = Path.Combine(_packageDir, filename);
File.Delete(targetFile);
if (Utility.IsWebUrl(_urlOrPath))
{
var url = _urlOrPath.TrimEnd('/');
url += "/" + filename;
await new WebClient().DownloadFileTaskAsync(new Uri(url), targetFile).ConfigureAwait(false);
}
else
{
File.Copy(Path.Combine(_urlOrPath, filename), targetFile);
}
}
private async Task<bool> UpdateAppImplAsync(ReleaseEntry lastEntry, IProgress<int> progress)
{
try
{
var targetFile = Path.Combine(_packageDir, lastEntry.Filename);
//download file.
await PutFileInPackageFolderAsync(lastEntry.Filename).ConfigureAwait(false);
progress?.Report(60);
if (!VerifyFileChecksum(targetFile, lastEntry.SHA1)) throw new FileLoadException();
progress?.Report(70);
await KillAppStubAsync().ConfigureAwait(false);
progress?.Report(80);
using (var updater = new Process())
{
updater.StartInfo = new ProcessStartInfo(targetFile, "/S");
updater.Start();
updater.WaitForExit();
}
progress?.Report(100);
return true;
}
catch (Exception e)
{
Console.WriteLine(e);
}
return false;
}
}
}

41
Katteker/Utility.cs Normal file
View File

@ -0,0 +1,41 @@
using System;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
namespace Katteker
{
public static class Utility
{
internal static string GetLocalAppDataDirectory()
{
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
}
internal static string GetApplicationName()
{
return Assembly.GetEntryAssembly().GetName().Name;
}
public static bool IsWebUrl(string urlOrPath)
{
var uri = new Uri(urlOrPath);
return uri.Scheme == "http" || uri.Scheme == "https";
}
public static string ComputeFileHash(string filename)
{
string sha1;
var fileInfo = new FileInfo(filename);
using (var fileStream = fileInfo.OpenRead())
{
var buffer = new byte[5242880];
fileStream.Read(buffer, 0, buffer.Length);
var sha1Managed = new SHA1Managed();
sha1 = Convert.ToBase64String(sha1Managed.ComputeHash(buffer));
}
return sha1;
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Reflection;
namespace Katteker
{
/// <summary>
/// Extension methods for the App-Version.
/// </summary>
public static class VersionExtension
{
/// <summary>
/// Get the current Version of Application.
/// </summary>
public static SemanticVersion GetCurrentVersion
{
get
{
var assemblyVersion = Assembly.GetEntryAssembly().GetName().Version.ToString(3);
var getCurrentVersion = SemanticVersion.Parse(assemblyVersion);
var informalVersion = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
if (informalVersion != null && SemanticVersion.TryParse(informalVersion, out var semVersion))
return semVersion;
return getCurrentVersion;
}
}
public static Version ToSystemVersion(this SemanticVersion value) => new Version(value.Major, value.Minor, value.Patch, 0);
/// <summary>
/// Get the current Version of Application.
/// </summary>
public static string GetFullVersion => GetCurrentVersion.ToString();
}
}

35
README.md Normal file
View File

@ -0,0 +1,35 @@
SquirrelKiller (aka Wombat)
=============
Ziele:
-------------
- Automatische Updates, auch für nicht verwaltete Software.
- Popup unten rechts mit Informationen über Update.
- Paket erstellt mit NSIS.
- schnellere Verteilung.
- Schnellere Paketerstellung ohne Umwege.
- AppStub übernimmt die Resourcen der Executable. (Icon, Name, Version, Copyright, Beschreibung, etc)
[ResourceLib C# File Resource Management Library](https://github.com/dblock/resourcelib)
Creator:
-------------
- Informationen der Anwendung auslesen
- Name,
- Version,
- ...
- AppStub kopieren & konfigurieren
- Installer erstellen
- RELEASE Datei updaten
AppStub:
-------------
- Anwendung starten
- Im Mutex ausführen
- Alte Versionen löschen (aufräumen)
- Updates für 'unmanaged' Anwendungen anzeigen
Library:
--------------
- Updates suchen
- Update installieren
- App neustarten