diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1ff0c42
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7964536
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,189 @@
+## 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/
+[Dd]ebugPublic/
+[Rr]elease/
+x64/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+
+# Roslyn cache directories
+*.ide/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+#NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding addin-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+## TODO: Comment the next line if you want to checkin your
+## web deploy settings but do note that will include unencrypted
+## passwords
+#*.pubxml
+
+# NuGet Packages Directory
+packages/*
+## TODO: If the tool you use requires repositories.config
+## uncomment the next line
+#!packages/repositories.config
+
+# Enable "build/" folder in the NuGet Packages folder since
+# NuGet packages use it for MSBuild targets.
+# This line needs to be after the ignore of the build folder
+# (and the packages folder if the line above has been uncommented)
+!packages/build/
+
+# Windows Azure Build Output
+csx/
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# LightSwitch generated files
+GeneratedArtifacts/
+_Pvt_Extensions/
+ModelManifest.xml
\ No newline at end of file
diff --git a/WSUSOnlineDescriptions.sln b/WSUSOnlineDescriptions.sln
new file mode 100644
index 0000000..d71ae31
--- /dev/null
+++ b/WSUSOnlineDescriptions.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WSUSOnlineDescriptions", "WSUSOnlineDescriptions\WSUSOnlineDescriptions.csproj", "{D14767F4-E1E4-4413-A122-44FB18FFBFBE}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D14767F4-E1E4-4413-A122-44FB18FFBFBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D14767F4-E1E4-4413-A122-44FB18FFBFBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D14767F4-E1E4-4413-A122-44FB18FFBFBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D14767F4-E1E4-4413-A122-44FB18FFBFBE}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/WSUSOnlineDescriptions/App.xaml b/WSUSOnlineDescriptions/App.xaml
new file mode 100644
index 0000000..26e7c90
--- /dev/null
+++ b/WSUSOnlineDescriptions/App.xaml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/WSUSOnlineDescriptions/App.xaml.cs b/WSUSOnlineDescriptions/App.xaml.cs
new file mode 100644
index 0000000..c6e7b47
--- /dev/null
+++ b/WSUSOnlineDescriptions/App.xaml.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Windows;
+
+namespace WSUSDescriptionHelper
+{
+ ///
+ /// Interaktionslogik für "App.xaml"
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/WSUSOnlineDescriptions/MainWindow.xaml b/WSUSOnlineDescriptions/MainWindow.xaml
new file mode 100644
index 0000000..10e42ee
--- /dev/null
+++ b/WSUSOnlineDescriptions/MainWindow.xaml
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Select the server version your WSUS is running on. This affects the SQL connection string.
+
+
+ Click the 'Get updates' button to query the KB article numbers of all unapproved updates in the WSUS database. The descriptions are added from the Microsoft Support servers. This may take a while.
+
+
+ If you have more than one update language installed, select the language whose descriptions should be overwritten with the new text.
+
+
+ If neccessary, edit the update descriptions in the table below. Click the 'Apply descriptions' button to save the values to the WSUS database.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WSUSOnlineDescriptions/MainWindow.xaml.cs b/WSUSOnlineDescriptions/MainWindow.xaml.cs
new file mode 100644
index 0000000..3a7bb0f
--- /dev/null
+++ b/WSUSOnlineDescriptions/MainWindow.xaml.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace WSUSOnlineDescriptions
+{
+ ///
+ /// Interaktionslogik für MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/WSUSOnlineDescriptions/MainWindowViewModel.cs b/WSUSOnlineDescriptions/MainWindowViewModel.cs
new file mode 100644
index 0000000..156863f
--- /dev/null
+++ b/WSUSOnlineDescriptions/MainWindowViewModel.cs
@@ -0,0 +1,267 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Data.SqlClient;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Threading;
+
+namespace WSUSOnlineDescriptions
+{
+ public class MainWindowViewModel : INotifyPropertyChanged
+ {
+ #region Fields
+ RelayCommand queryUpdatesCommand = new RelayCommand(true);
+ RelayCommand saveDescriptionsCommand = new RelayCommand(false);
+
+ ObservableCollection unapprovedUpdates = new ObservableCollection();
+ ObservableCollection availableLanguages = new ObservableCollection();
+ List serverVersions = new List()
+ {
+ new WSUSServerVersion("Windows Server 2012", @"\\.\pipe\MICROSOFT##WID\tsql\query"),
+ new WSUSServerVersion("Windows Server 2003-2008", @"\\.\pipe\MSSQL$MICROSOFT##SSEE\sql\query")
+ };
+
+ WSUSServerVersion selectedServerVersion;
+ WSUSLanguage selectedLanguage;
+ double progressValue;
+ double progressMax = 100;
+ bool isProgressBarIndeterminate;
+ #endregion
+
+ #region Properties
+ private string dbConnectionString
+ {
+ get
+ {
+ return new SqlConnectionStringBuilder()
+ {
+ DataSource = this.SelectedServerVersion.DatabaseName,
+ InitialCatalog = "SUSDB",
+ IntegratedSecurity = true
+ }.ConnectionString;
+ }
+ }
+
+ public RelayCommand QueryUpdatesCommand
+ {
+ get { return queryUpdatesCommand; }
+ }
+
+ public RelayCommand SaveDescriptionsCommand
+ {
+ get { return saveDescriptionsCommand; }
+ }
+
+ public ReadOnlyObservableCollection UnapprovedUpdates
+ {
+ get { return new ReadOnlyObservableCollection(unapprovedUpdates); }
+ }
+
+ public ReadOnlyObservableCollection AvailableLanguages
+ {
+ get { return new ReadOnlyObservableCollection(availableLanguages); }
+ }
+
+ public ReadOnlyCollection ServerVersions
+ {
+ get { return serverVersions.AsReadOnly(); }
+ }
+
+ public WSUSServerVersion SelectedServerVersion
+ {
+ get { return selectedServerVersion; }
+ set
+ {
+ selectedServerVersion = value;
+ NotifyPropertyChanged("SelectedServerVersion");
+ }
+ }
+
+ public WSUSLanguage SelectedLanguage
+ {
+ get { return selectedLanguage; }
+ set
+ {
+ selectedLanguage = value;
+ NotifyPropertyChanged("SelectedLanguage");
+ }
+ }
+
+ public double ProgressValue
+ {
+ get { return progressValue; }
+ private set
+ {
+ progressValue = value;
+ NotifyPropertyChanged("ProgressValue");
+ }
+ }
+
+ public double ProgressMax
+ {
+ get { return progressMax; }
+ private set
+ {
+ progressMax = value;
+ NotifyPropertyChanged("ProgressMax");
+ }
+ }
+
+ public bool IsProgressBarIndeterminate
+ {
+ get { return isProgressBarIndeterminate; }
+ private set
+ {
+ isProgressBarIndeterminate = value;
+ NotifyPropertyChanged("IsProgressBarIndeterminate");
+ }
+ }
+ #endregion
+
+ public MainWindowViewModel()
+ {
+ this.queryUpdatesCommand.CommandExecuted += queryKBCommand_CommandExecuted;
+ this.saveDescriptionsCommand.CommandExecuted += saveDescriptionsCommand_CommandExecuted;
+ this.SelectedServerVersion = this.serverVersions[0];
+ }
+
+ #region Commands
+ private async void queryKBCommand_CommandExecuted(object sender, EventArgs e)
+ {
+ this.queryUpdatesCommand.SetCanExecute(false);
+ this.unapprovedUpdates.Clear();
+ this.IsProgressBarIndeterminate = true;
+
+ try
+ {
+ using (SqlConnection con = new SqlConnection(this.dbConnectionString))
+ {
+ con.Open();
+
+ using (SqlCommand command = new SqlCommand(
+ "SELECT l.ShortLanguage, l.LongLanguage " +
+ "FROM tbLanguage l " +
+ "WHERE l.Enabled = 1 " +
+ "ORDER BY l.LongLanguage",
+ con))
+ {
+ using (SqlDataReader reader = command.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ this.availableLanguages.Add(new WSUSLanguage(reader.GetString(0), reader.GetString(1)));
+ }
+ }
+ }
+
+ using (SqlCommand command = new SqlCommand(
+ "SELECT DISTINCT kb.KBArticleID " +
+ "FROM tbPreComputedLocalizedProperty p " +
+ "JOIN tbUpdate u ON p.UpdateID = u.UpdateID " +
+ "JOIN tbRevision r ON u.LocalUpdateID = r.LocalUpdateID " +
+ "JOIN tbKBArticleForRevision kb ON r.RevisionID = kb.RevisionID " +
+ "WHERE p.ShortLanguage = 'de' AND u.IsHidden = 0 AND r.State = 3 AND r.IsLatestRevision = 1 " +
+ "ORDER BY kb.KBArticleID",
+ con))
+ {
+ using (SqlDataReader reader = command.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ this.unapprovedUpdates.Add(new WSUSUpdate(reader.GetString(0)));
+ }
+ }
+ }
+ }
+
+ this.SelectedLanguage = this.availableLanguages[0];
+ await Task.WhenAll(from update in this.unapprovedUpdates
+ select update.QueryKBDetailsAsync());
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(String.Format("The following error occurred:{0}{1}", Environment.NewLine, ex.Message));
+ this.unapprovedUpdates.Clear();
+ }
+ finally
+ {
+ this.IsProgressBarIndeterminate = false;
+
+ if (this.unapprovedUpdates.Count > 0)
+ this.saveDescriptionsCommand.SetCanExecute(true);
+ else
+ this.queryUpdatesCommand.SetCanExecute(true);
+ }
+ }
+
+ private async void saveDescriptionsCommand_CommandExecuted(object sender, EventArgs e)
+ {
+ this.saveDescriptionsCommand.SetCanExecute(false);
+
+ Task dbTask = new Task(SaveDescriptions);
+ dbTask.Start();
+ await dbTask;
+
+ this.saveDescriptionsCommand.SetCanExecute(true);
+ }
+
+ private void SaveDescriptions()
+ {
+ try
+ {
+ this.ProgressValue = 0;
+ this.ProgressMax = this.unapprovedUpdates.Count;
+
+ using (SqlConnection con = new SqlConnection(this.dbConnectionString))
+ {
+ con.Open();
+
+ foreach (WSUSUpdate update in unapprovedUpdates)
+ {
+ using (SqlCommand command = new SqlCommand(
+ "UPDATE tbPreComputedLocalizedProperty " +
+ "SET Description = @Description " +
+ "FROM tbPreComputedLocalizedProperty p " +
+ "JOIN tbUpdate u ON p.UpdateID = u.UpdateID " +
+ "JOIN tbRevision r ON u.LocalUpdateID = r.LocalUpdateID " +
+ "JOIN tbKBArticleForRevision kb ON r.RevisionID = kb.RevisionID " +
+ "WHERE kb.KBArticleID = @KB AND p.ShortLanguage = @Language",
+ con))
+ {
+ // Description field is limited to 1500 chars.
+ string shortDescription = new String(update.Description.Take(1500).ToArray());
+
+ command.Parameters.Add(new SqlParameter("@Description", shortDescription));
+ command.Parameters.Add(new SqlParameter("@KB", update.KB));
+ command.Parameters.Add(new SqlParameter("@Language", this.selectedLanguage.ShortLanguage));
+
+ command.ExecuteNonQuery();
+ }
+ this.ProgressValue++;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Dispatcher.CurrentDispatcher.Invoke(new ThreadStart(() =>
+ {
+ MessageBox.Show(String.Format("The following error occurred:{0}{1}", Environment.NewLine, ex.Message));
+ }));
+ }
+ }
+ #endregion
+
+ #region Notify
+ public event PropertyChangedEventHandler PropertyChanged;
+ protected void NotifyPropertyChanged(String propertyName)
+ {
+ if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/WSUSOnlineDescriptions/Properties/AssemblyInfo.cs b/WSUSOnlineDescriptions/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..d187c41
--- /dev/null
+++ b/WSUSOnlineDescriptions/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// 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("WSUS Online Descriptions")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("WSUS Online Descriptions")]
+[assembly: AssemblyCopyright("Copyright © 2015")]
+[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)]
+
+//Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie
+//ImCodeVerwendeteKultur in der .csproj-Datei
+//in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch
+//(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung
+//des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile,
+//sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher
+ //(wird verwendet, wenn eine Ressource auf der Seite
+ // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.)
+ ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs
+ //(wird verwendet, wenn eine Ressource auf der Seite, in der Anwendung oder einem
+ // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.)
+)]
+
+
+// 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.1.0")]
+[assembly: AssemblyFileVersion("1.0.1.0")]
diff --git a/WSUSOnlineDescriptions/Properties/Resources.Designer.cs b/WSUSOnlineDescriptions/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..327f834
--- /dev/null
+++ b/WSUSOnlineDescriptions/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.18444
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace WSUSOnlineDescriptions.Properties {
+ using System;
+
+
+ ///
+ /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+ ///
+ // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+ // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+ // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+ // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.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() {
+ }
+
+ ///
+ /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ ///
+ [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("WSUSOnlineDescriptions.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/WSUSOnlineDescriptions/Properties/Resources.resx b/WSUSOnlineDescriptions/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/WSUSOnlineDescriptions/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/WSUSOnlineDescriptions/Properties/Settings.Designer.cs b/WSUSOnlineDescriptions/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..762cfe1
--- /dev/null
+++ b/WSUSOnlineDescriptions/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// Dieser Code wurde von einem Tool generiert.
+// Laufzeitversion:4.0.30319.18444
+//
+// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+// der Code erneut generiert wird.
+//
+//------------------------------------------------------------------------------
+
+namespace WSUSOnlineDescriptions.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.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;
+ }
+ }
+ }
+}
diff --git a/WSUSOnlineDescriptions/Properties/Settings.settings b/WSUSOnlineDescriptions/Properties/Settings.settings
new file mode 100644
index 0000000..033d7a5
--- /dev/null
+++ b/WSUSOnlineDescriptions/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/WSUSOnlineDescriptions/RelayCommand.cs b/WSUSOnlineDescriptions/RelayCommand.cs
new file mode 100644
index 0000000..51fc84a
--- /dev/null
+++ b/WSUSOnlineDescriptions/RelayCommand.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Input;
+
+namespace WSUSOnlineDescriptions
+{
+ public class RelayCommand : ICommand
+ {
+ bool canExecute;
+ public event EventHandler CanExecuteChanged;
+ public event EventHandler CommandExecuted;
+
+ public RelayCommand(bool canExecute)
+ {
+ this.SetCanExecute(canExecute);
+ }
+
+ public void SetCanExecute(bool newValue)
+ {
+ this.canExecute = newValue;
+
+ if (this.CanExecuteChanged != null)
+ this.CanExecuteChanged(this, new EventArgs());
+ }
+
+ public bool CanExecute(object parameter)
+ {
+ return this.canExecute;
+ }
+
+ public void Execute(object parameter)
+ {
+ if (this.CommandExecuted != null)
+ this.CommandExecuted(this, new EventArgs());
+ }
+ }
+}
\ No newline at end of file
diff --git a/WSUSOnlineDescriptions/WSUSLanguage.cs b/WSUSOnlineDescriptions/WSUSLanguage.cs
new file mode 100644
index 0000000..9609202
--- /dev/null
+++ b/WSUSOnlineDescriptions/WSUSLanguage.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace WSUSOnlineDescriptions
+{
+ public class WSUSLanguage
+ {
+ string shortLanguage;
+ string longLanguage;
+
+ public string ShortLanguage
+ {
+ get
+ {
+ return this.shortLanguage;
+ }
+ }
+
+ public string LongLanguage
+ {
+ get
+ {
+ return this.longLanguage;
+ }
+ }
+
+ public WSUSLanguage(string shortLanguage, string longLanguage)
+ {
+ this.shortLanguage = shortLanguage;
+ this.longLanguage = longLanguage;
+ }
+
+ public override string ToString()
+ {
+ return String.Format("{0} ({1})", this.longLanguage, this.shortLanguage);
+ }
+ }
+}
\ No newline at end of file
diff --git a/WSUSOnlineDescriptions/WSUSOnlineDescriptions.csproj b/WSUSOnlineDescriptions/WSUSOnlineDescriptions.csproj
new file mode 100644
index 0000000..098530e
--- /dev/null
+++ b/WSUSOnlineDescriptions/WSUSOnlineDescriptions.csproj
@@ -0,0 +1,149 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {D14767F4-E1E4-4413-A122-44FB18FFBFBE}
+ WinExe
+ Properties
+ WSUSOnlineDescriptions
+ WSUSOnlineDescriptions
+ v4.5
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+
+
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+ ..\packages\HtmlAgilityPack.1.4.9\lib\Net45\HtmlAgilityPack.dll
+ True
+
+
+
+
+
+
+
+
+
+ 4.0
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ App.xaml
+ Code
+
+
+ MainWindow.xaml
+ Code
+
+
+
+
+ Code
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ Settings.settings
+ True
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+ False
+ Microsoft .NET Framework 4.5 %28x86 and x64%29
+ true
+
+
+ False
+ .NET Framework 3.5 SP1 Client Profile
+ false
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+
+
+
\ No newline at end of file
diff --git a/WSUSOnlineDescriptions/WSUSServerVersion.cs b/WSUSOnlineDescriptions/WSUSServerVersion.cs
new file mode 100644
index 0000000..3dad570
--- /dev/null
+++ b/WSUSOnlineDescriptions/WSUSServerVersion.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace WSUSOnlineDescriptions
+{
+ public class WSUSServerVersion
+ {
+ public string Name { get; set; }
+
+ public string DatabaseName { get; set; }
+
+ public WSUSServerVersion(string name, string databaseName)
+ {
+ this.Name = name;
+ this.DatabaseName = databaseName;
+ }
+
+ public override string ToString()
+ {
+ return this.Name;
+ }
+ }
+}
\ No newline at end of file
diff --git a/WSUSOnlineDescriptions/WSUSUpdate.cs b/WSUSOnlineDescriptions/WSUSUpdate.cs
new file mode 100644
index 0000000..3e4847d
--- /dev/null
+++ b/WSUSOnlineDescriptions/WSUSUpdate.cs
@@ -0,0 +1,131 @@
+using HtmlAgilityPack;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Threading;
+
+namespace WSUSOnlineDescriptions
+{
+ public class WSUSUpdate : INotifyPropertyChanged
+ {
+ string kb;
+ string description;
+
+ public string KB
+ {
+ get { return this.kb; }
+ }
+
+ public string Description
+ {
+ get { return this.description; }
+ set
+ {
+ this.description = value;
+ NotifyPropertyChanged("Description");
+ }
+ }
+
+ public WSUSUpdate(string kb)
+ {
+ this.kb = kb;
+ }
+
+ public async Task QueryKBDetailsAsync()
+ {
+ Task task = new Task(() =>
+ {
+ try
+ {
+ //-------------------------
+ // Querying the Microsoft Support API needs some cookies to be set, otherwise 404 errors are thrown.
+ // Thanks to gunr2171 @ http://stackoverflow.com/a/31349305/3414957 for finding a solution.
+ //-------------------------
+
+ //this holds all the cookies we need to add
+ CookieContainer cookieJar = new CookieContainer();
+ cookieJar.Add(new Cookie("SMCsiteDir", "ltr", "/", ".support.microsoft.com"));
+ cookieJar.Add(new Cookie("SMCsiteLang", "en-US", "/", ".support.microsoft.com"));
+ cookieJar.Add(new Cookie("smc_f", "upr", "/", ".support.microsoft.com"));
+ cookieJar.Add(new Cookie("smcexpsessionticket", "100", "/", ".microsoft.com"));
+ cookieJar.Add(new Cookie("smcexpticket", "100", "/", ".microsoft.com"));
+ cookieJar.Add(new Cookie("smcflighting", "wwp", "/", ".microsoft.com"));
+
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://support.microsoft.com/api/content/kb/" + this.kb);
+ //attach the cookie container
+ request.CookieContainer = cookieJar;
+
+ //and now go to the internet, fetching back the contents
+ HttpWebResponse response = (HttpWebResponse)request.GetResponse();
+ using (StreamReader sr = new StreamReader(response.GetResponseStream()))
+ {
+ string site = sr.ReadToEnd();
+ HtmlDocument doc = new HtmlDocument();
+ doc.LoadHtml(site);
+
+ // Get update title
+ string result = doc.DocumentNode.SelectSingleNode("//h1[1]").Attributes["title"].Value;
+
+
+ // Get detailed update description
+ HtmlNode descriptionNode =
+ doc.DocumentNode.SelectSingleNode("//div[@class='kb-symptoms-section section']") ??
+ doc.DocumentNode.SelectSingleNode("//div[@class='kb-summary-section section']");
+
+ if (descriptionNode != null)
+ {
+ string details = String.Empty;
+
+ foreach (HtmlNode node in descriptionNode.ChildNodes)
+ {
+ HtmlAttribute attr = node.Attributes["class"];
+ if (attr != null && attr.Value.Contains("header"))
+ continue;
+
+ details += node.InnerText;
+ }
+
+ result += " - " + details.Trim();
+ }
+
+ result = Regex.Replace(result, " ", " ");
+ result = Regex.Replace(result, "'", "'");
+ result = Regex.Replace(result, """, "\"");
+ result = Regex.Replace(result, "<.*?>", "");
+ result = Regex.Replace(result, "[ ]{2,}", " ");
+ result = Regex.Replace(result, "<", "<");
+ result = Regex.Replace(result, ">", ">");
+
+ this.Description = result;
+ }
+ }
+ catch (Exception ex)
+ {
+ this.Description = "ERROR: " + ex.Message;
+ }
+ });
+ task.Start();
+ await task;
+ }
+
+ public override string ToString()
+ {
+ return "KB" + this.kb;
+ }
+
+ #region Notify
+ public event PropertyChangedEventHandler PropertyChanged;
+ protected void NotifyPropertyChanged(String propertyName)
+ {
+ if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/WSUSOnlineDescriptions/app.config b/WSUSOnlineDescriptions/app.config
new file mode 100644
index 0000000..51278a4
--- /dev/null
+++ b/WSUSOnlineDescriptions/app.config
@@ -0,0 +1,3 @@
+
+
+
diff --git a/WSUSOnlineDescriptions/packages.config b/WSUSOnlineDescriptions/packages.config
new file mode 100644
index 0000000..87c301a
--- /dev/null
+++ b/WSUSOnlineDescriptions/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file