diff --git a/App.config b/App.config
new file mode 100644
index 0000000..8e15646
--- /dev/null
+++ b/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/App.xaml b/App.xaml
new file mode 100644
index 0000000..4733fb5
--- /dev/null
+++ b/App.xaml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/App.xaml.cs b/App.xaml.cs
new file mode 100644
index 0000000..16bfc72
--- /dev/null
+++ b/App.xaml.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Windows;
+using System.Windows.Forms;
+using Application = System.Windows.Application;
+
+namespace WinHook
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/Controls/KeyInputBox.cs b/Controls/KeyInputBox.cs
new file mode 100644
index 0000000..e260188
--- /dev/null
+++ b/Controls/KeyInputBox.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Forms;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using WinHook.Models;
+using KeyEventArgs = System.Windows.Input.KeyEventArgs;
+using KeyEventHandler = System.Windows.Input.KeyEventHandler;
+using TextBox = System.Windows.Controls.TextBox;
+
+namespace WinHook.Controls
+{
+ ///
+ /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
+ ///
+ /// Step 1a) Using this custom control in a XAML file that exists in the current project.
+ /// Add this XmlNamespace attribute to the root element of the markup file where it is
+ /// to be used:
+ ///
+ /// xmlns:MyNamespace="clr-namespace:WinHook.Controls"
+ ///
+ ///
+ /// Step 1b) Using this custom control in a XAML file that exists in a different project.
+ /// Add this XmlNamespace attribute to the root element of the markup file where it is
+ /// to be used:
+ ///
+ /// xmlns:MyNamespace="clr-namespace:WinHook.Controls;assembly=WinHook.Controls"
+ ///
+ /// You will also need to add a project reference from the project where the XAML file lives
+ /// to this project and Rebuild to avoid compilation errors:
+ ///
+ /// Right click on the target project in the Solution Explorer and
+ /// "Add Reference"->"Projects"->[Browse to and select this project]
+ ///
+ ///
+ /// Step 2)
+ /// Go ahead and use your control in the XAML file.
+ ///
+ ///
+ ///
+ ///
+ public class KeyInputBox : TextBox
+ {
+ static KeyInputBox()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(KeyInputBox), new FrameworkPropertyMetadata(typeof(KeyInputBox)));
+ EventManager.RegisterClassHandler(typeof(KeyInputBox), Keyboard.PreviewKeyDownEvent, new KeyEventHandler(OnPreviewKeyDownEvent));
+ }
+
+ public static readonly DependencyProperty ShorcutModeProperty = DependencyProperty.Register(
+ "ShorcutMode", typeof(bool), typeof(KeyInputBox), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
+ );
+ public static readonly DependencyProperty KeysProperty = DependencyProperty.Register(
+ "Keys", typeof(Keys?), typeof(KeyInputBox), new FrameworkPropertyMetadata(new Keys(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, FillText)
+ );
+ public static readonly DependencyProperty ShorcutProperty = DependencyProperty.Register(
+ "Shorcut", typeof(ShorcutSwitch), typeof(KeyInputBox), new FrameworkPropertyMetadata(new ShorcutSwitch(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, FillText)
+ );
+
+ private static void FillText(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ var invoker = d as KeyInputBox;
+
+ var newValue = e.NewValue == null ? string.Empty : e.NewValue.ToString();
+ invoker.Text = newValue;
+ }
+
+ public bool ShorcutMode
+ {
+ get
+ {
+ return (bool)GetValue(ShorcutModeProperty);
+ }
+ set
+ {
+ SetValue(ShorcutModeProperty, value);
+ }
+ }
+
+ public Keys Keys
+ {
+ get
+ {
+ return (Keys)GetValue(KeysProperty);
+ }
+ set
+ {
+ SetValue(KeysProperty, value);
+ }
+ }
+
+ public ShorcutSwitch Shorcut
+ {
+ get
+ {
+ return (ShorcutSwitch)GetValue(ShorcutProperty);
+ }
+ set
+ {
+ SetValue(ShorcutProperty, value);
+ }
+ }
+
+
+ public static readonly RoutedEvent InvertCallEvent = EventManager.RegisterRoutedEvent(
+ "InvertCall", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(KeyInputBox)
+ );
+
+ public event RoutedEventHandler InvertCall
+ {
+ add { AddHandler(InvertCallEvent, value); }
+ remove { RemoveHandler(InvertCallEvent, value); }
+ }
+
+ private void OnInvertCall()
+ {
+ var args = new RoutedEventArgs(InvertCallEvent);
+ RaiseEvent(args);
+ }
+
+ static void OnPreviewKeyDownEvent(object sender, KeyEventArgs e)
+ {
+ var invoker = sender as KeyInputBox;
+
+ // The text box grabs all input.
+ e.Handled = true;
+
+ if (invoker.ShorcutMode)
+ {
+ // Fetch the actual shortcut key.
+ var key = (e.Key == Key.System ? e.SystemKey : e.Key);
+
+ // Ignore modifier keys.
+ if (key == Key.LeftShift || key == Key.RightShift
+ || key == Key.LeftCtrl || key == Key.RightCtrl
+ || key == Key.LeftAlt || key == Key.RightAlt
+ || key == Key.LWin || key == Key.RWin)
+ {
+ // do nothing
+ }
+ else
+ {
+ invoker.Shorcut = new ShorcutSwitch((Keys)KeyInterop.VirtualKeyFromKey(key), Keyboard.Modifiers);
+ }
+ }
+ else
+ {
+ invoker.Keys = (Keys) KeyInterop.VirtualKeyFromKey(e.Key);
+ }
+
+ invoker.OnInvertCall();
+ }
+ }
+}
diff --git a/Models/Config.cs b/Models/Config.cs
new file mode 100644
index 0000000..680e1a1
--- /dev/null
+++ b/Models/Config.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Windows.Input;
+
+namespace WinHook.Models
+{
+ [Serializable]
+ public class Config: ObservableObject
+ {
+ private GeneralConfig _generalConfig = new GeneralConfig();
+ private KeyBlockConfig _keyBlockConfig = new KeyBlockConfig();
+
+ //public Config()
+ //{
+ // HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ //}
+
+ public GeneralConfig GeneralConfig
+ {
+ get { return _generalConfig; }
+ set
+ {
+ if (_generalConfig == value) return;
+ _generalConfig = value;
+ RaisePropertyChanged();
+ }
+ }
+
+ public KeyBlockConfig KeyBlockConfig
+ {
+ get { return _keyBlockConfig; }
+ set
+ {
+ if (_keyBlockConfig == value) return;
+ _keyBlockConfig = value;
+ RaisePropertyChanged();
+ }
+ }
+
+ //private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ //{
+ // if (e.Key == EnableShortcut.Keys && e.Modifiers == EnableShortcut.ModifierKeys)
+ // {
+ // IsEnabled = !IsEnabled;
+ // }
+ //}
+
+ }
+}
diff --git a/Models/GeneralConfig.cs b/Models/GeneralConfig.cs
new file mode 100644
index 0000000..f96a996
--- /dev/null
+++ b/Models/GeneralConfig.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Diagnostics;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Input;
+using WinHook.Utils;
+
+namespace WinHook.Models
+{
+ public class GeneralConfig: ObservableObject
+ {
+ private ShorcutSwitch _enableShortcut;
+ private int? shortcutId;
+ private bool _isEnabled;
+ private bool _minimizeToTray;
+ private bool _startMinimized;
+ private bool _startWithWindows;
+
+ public ShorcutSwitch EnableShortcut
+ {
+ get
+ {
+ return _enableShortcut;
+ }
+ set
+ {
+ if (_enableShortcut == value) return;
+ _enableShortcut = value;
+ if (shortcutId != null)
+ {
+ HotKeyManager.UnregisterHotKey((int)shortcutId);
+ }
+ if (_enableShortcut != null)
+ {
+ shortcutId = HotKeyManager.RegisterHotKey(_enableShortcut.Keys, _enableShortcut.ModifierKeys);
+ }
+ RaisePropertyChanged();
+ }
+ }
+
+ public bool IsEnabled
+ {
+ get { return _isEnabled; }
+ set
+ {
+ if (_isEnabled == value) return;
+ _isEnabled = value;
+ RaisePropertyChanged();
+ }
+ }
+
+ public bool MinimizeToTray
+ {
+ get { return _minimizeToTray; }
+ set
+ {
+ if (_minimizeToTray == value) return;
+ _minimizeToTray = value;
+ RaisePropertyChanged();
+ }
+ }
+
+ public bool StartMinimized
+ {
+ get { return _startMinimized; }
+ set
+ {
+ if (_startMinimized == value) return;
+ _startMinimized = value;
+ RaisePropertyChanged();
+ }
+ }
+
+ public bool StartWithWindows
+ {
+ get { return _startWithWindows; }
+ set
+ {
+ if (_startWithWindows == value) return;
+ _startWithWindows = value;
+ RaisePropertyChanged();
+ }
+ }
+
+ public void ClearEnableShortcutSwitch()
+ {
+ EnableShortcut = null;
+ }
+
+ private void InstallUninstallOnStartUp(bool install)
+ {
+ try
+ {
+ var key = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Run", true);
+ var curAssembly = Assembly.GetExecutingAssembly();
+
+ if (install)
+ {
+ key.SetValue(curAssembly.GetName().Name, curAssembly.Location);
+ }
+ else
+ {
+ key.DeleteValue(curAssembly.GetName().Name);
+ }
+ }
+ catch (Exception e)
+ {
+ StartWithWindows = !install;
+ var message = install ? "Can't add key to registry" : "Can't remove key from registry";
+ Debug.WriteLine("{0} {1}", message, e.Message);
+ MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+
+ public ICommand ClearEnableShortcutSwitchCommand
+ {
+ get { return new RelayCommand(ClearEnableShortcutSwitch); }
+ }
+
+ public ICommand InstallUninstallOnStartUpCommand
+ {
+ get { return new RelayCommand(InstallUninstallOnStartUp); }
+ }
+ }
+}
diff --git a/Models/KeyBlockConfig.cs b/Models/KeyBlockConfig.cs
new file mode 100644
index 0000000..32bec1f
--- /dev/null
+++ b/Models/KeyBlockConfig.cs
@@ -0,0 +1,68 @@
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Windows.Forms;
+using System.Windows.Input;
+using System.Xml.Serialization;
+
+namespace WinHook.Models
+{
+ public class KeyBlockConfig: ObservableObject
+ {
+ private ObservableCollection _blockedKeys = new ObservableCollection();
+ private Keys? _tmpKey;
+
+ public ObservableCollection BlockedKeys
+ {
+ get { return _blockedKeys; }
+ set
+ {
+ if (_blockedKeys == value) return;
+ _blockedKeys = value;
+ RaisePropertyChanged();
+ }
+ }
+
+ [XmlIgnore]
+ public Keys? TmpAddKeys
+ {
+ get { return _tmpKey; }
+ set
+ {
+ if (_tmpKey == value) return;
+ _tmpKey = value;
+ RaisePropertyChanged();
+ }
+ }
+
+ public ICommand AddKeyCommand
+ {
+ get { return new RelayCommand(AddKey, CanAddKey); }
+ }
+
+ public void AddKey(Keys key)
+ {
+ BlockedKeys.Add(key);
+ TmpAddKeys = null;
+ }
+
+ public bool CanAddKey(Keys key)
+ {
+ return (key != Keys.None && !BlockedKeys.Contains(key));
+ }
+
+ public ICommand RemoveKeyCommand
+ {
+ get { return new RelayCommand(RemoveKey, CanRemoveKey); }
+ }
+
+ public void RemoveKey(Keys key)
+ {
+ BlockedKeys.Remove(key);
+ }
+
+ public bool CanRemoveKey(Keys key)
+ {
+ return (key != Keys.None && BlockedKeys.Contains(key));
+ }
+ }
+}
diff --git a/Models/ObservableObject.cs b/Models/ObservableObject.cs
new file mode 100644
index 0000000..b802877
--- /dev/null
+++ b/Models/ObservableObject.cs
@@ -0,0 +1,16 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace WinHook.Models
+{
+ public class ObservableObject: INotifyPropertyChanged
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
+ {
+ if (PropertyChanged != null)
+ PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
diff --git a/Models/RelayCommand.cs b/Models/RelayCommand.cs
new file mode 100644
index 0000000..7f04b43
--- /dev/null
+++ b/Models/RelayCommand.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Windows.Input;
+
+namespace WinHook.Models
+{
+ public class RelayCommand : ICommand
+ {
+ #region Fields
+
+ readonly Action _execute;
+ readonly Func _canExecute;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// Delegate to execute when Execute is called on the command. This can be null to just hook up a CanExecute delegate.
+ /// will always return true.
+ public RelayCommand(Action execute)
+ : this(execute, null)
+ {
+ }
+
+ ///
+ /// Creates a new command.
+ ///
+ /// The execution logic.
+ /// The execution status logic.
+ public RelayCommand(Action execute, Func canExecute)
+ {
+ if (execute == null)
+ throw new ArgumentNullException("execute");
+
+ _execute = execute;
+ _canExecute = canExecute;
+ }
+
+ #endregion
+
+ #region ICommand Members
+
+ ///
+ ///Defines the method that determines whether the command can execute in its current state.
+ ///
+ ///Data used by the command. If the command does not require data to be passed, this object can be set to null.
+ ///
+ ///true if this command can be executed; otherwise, false.
+ ///
+ public bool CanExecute(object parameter)
+ {
+ return _canExecute == null || _canExecute();
+ }
+
+ ///
+ ///Occurs when changes occur that affect whether or not the command should execute.
+ ///
+ public event EventHandler CanExecuteChanged
+ {
+ add { CommandManager.RequerySuggested += value; }
+ remove { CommandManager.RequerySuggested -= value; }
+ }
+
+ ///
+ ///Defines the method to be called when the command is invoked.
+ ///
+ ///Data used by the command. If the command does not require data to be passed, this object can be set to .
+ public void Execute(object parameter)
+ {
+ _execute();
+ }
+
+ #endregion
+ }
+
+
+ public class RelayCommand : ICommand
+ {
+ #region Fields
+
+ readonly Action _execute;
+ readonly Predicate _canExecute;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// Delegate to execute when Execute is called on the command. This can be null to just hook up a CanExecute delegate.
+ /// will always return true.
+ public RelayCommand(Action execute)
+ : this(execute, null)
+ {
+ }
+
+ ///
+ /// Creates a new command.
+ ///
+ /// The execution logic.
+ /// The execution status logic.
+ public RelayCommand(Action execute, Predicate canExecute)
+ {
+ if (execute == null)
+ throw new ArgumentNullException("execute");
+
+ _execute = execute;
+ _canExecute = canExecute;
+ }
+
+ #endregion
+
+ #region ICommand Members
+
+ ///
+ ///Defines the method that determines whether the command can execute in its current state.
+ ///
+ ///Data used by the command. If the command does not require data to be passed, this object can be set to null.
+ ///
+ ///true if this command can be executed; otherwise, false.
+ ///
+ public bool CanExecute(object parameter)
+ {
+ return _canExecute == null || (parameter != null && _canExecute((T) parameter));
+ }
+
+ ///
+ ///Occurs when changes occur that affect whether or not the command should execute.
+ ///
+ public event EventHandler CanExecuteChanged
+ {
+ add { CommandManager.RequerySuggested += value; }
+ remove { CommandManager.RequerySuggested -= value; }
+ }
+
+ ///
+ ///Defines the method to be called when the command is invoked.
+ ///
+ ///Data used by the command. If the command does not require data to be passed, this object can be set to .
+ public void Execute(object parameter)
+ {
+ _execute((T)parameter);
+ }
+
+ #endregion
+ }
+}
diff --git a/Models/ShorcutSwitch.cs b/Models/ShorcutSwitch.cs
new file mode 100644
index 0000000..f1ce742
--- /dev/null
+++ b/Models/ShorcutSwitch.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Input;
+
+namespace WinHook.Models
+{
+ [Serializable]
+ //[TypeConverter(typeof(ShorcutSwitchConverter))]
+ public class ShorcutSwitch
+ {
+ //[TypeConverter(typeof(KeysConverter))]
+ public Keys Keys { get; set; }
+
+ //[TypeConverter(typeof(ModifierKeysConverter))]
+ public ModifierKeys ModifierKeys { get; set; }
+
+ public ShorcutSwitch(Keys keys, ModifierKeys modifierKeys)
+ {
+ Keys = keys;
+ ModifierKeys = modifierKeys;
+ }
+
+ public ShorcutSwitch() { }
+
+ public override string ToString()
+ {
+ var shortcutText = new StringBuilder();
+
+ if ((ModifierKeys & ModifierKeys.Control) != 0)
+ {
+ shortcutText.Append(ModifierKeys.Control);
+ shortcutText.Append("+");
+ }
+ if ((ModifierKeys & ModifierKeys.Shift) != 0)
+ {
+ shortcutText.Append(ModifierKeys.Shift);
+ shortcutText.Append("+");
+ }
+ if ((ModifierKeys & ModifierKeys.Alt) != 0)
+ {
+ shortcutText.Append(ModifierKeys.Alt);
+ shortcutText.Append("+");
+ }
+ shortcutText.Append(Keys);
+
+ return shortcutText.ToString();
+ }
+ }
+
+ //public class ShorcutSwitchConverter : TypeConverter
+ //{
+ // public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+ // {
+ // return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
+ // }
+
+ // // Overrides the ConvertFrom method of TypeConverter.
+ // public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ // {
+ // if (!(value is string)) return base.ConvertFrom(context, culture, value);
+ // var v = ((string)value).Split('+');
+ // var keys = (Keys)TypeDescriptor.GetConverter(typeof(Keys)).ConvertFrom(v.Last());
+ // var a = string.Join("+", v.DropLast(1));
+ // var modifiersKeys = (ModifierKeys)TypeDescriptor.GetConverter(typeof(ModifierKeys)).ConvertFromInvariantString(a);
+ // return new ShorcutSwitch(keys, modifiersKeys);
+ // }
+ // // Overrides the ConvertTo method of TypeConverter.
+ // public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+ // {
+ // if (destinationType == typeof(string))
+ // {
+ // return value == null ? "" : value.ToString();
+ // }
+ // return base.ConvertTo(context, culture, value, destinationType);
+ // }
+ //}
+}
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..97fd6bb
--- /dev/null
+++ b/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+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("WinHook")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("WinHook")]
+[assembly: AssemblyCopyright("Copyright © 2013")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the 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.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..0baffc2
--- /dev/null
+++ b/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.18052
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace WinHook.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // 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", "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() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [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("WinHook.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [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/Properties/Resources.resx b/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/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/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..5618b63
--- /dev/null
+++ b/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.18052
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace WinHook.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.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/Properties/Settings.settings b/Properties/Settings.settings
new file mode 100644
index 0000000..033d7a5
--- /dev/null
+++ b/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Themes/Generic.xaml b/Themes/Generic.xaml
new file mode 100644
index 0000000..f04add4
--- /dev/null
+++ b/Themes/Generic.xaml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/Utils/DropLast.cs b/Utils/DropLast.cs
new file mode 100644
index 0000000..9b4a93a
--- /dev/null
+++ b/Utils/DropLast.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace WinHook.Utils
+{
+ public static class DropLastEx
+ {
+ public static IEnumerable DropLast(this IEnumerable source, int n)
+ {
+ if (source == null)
+ throw new ArgumentNullException("source");
+
+ if (n < 0)
+ throw new ArgumentOutOfRangeException("n", "Argument n should be non-negative.");
+
+ return InternalDropLast(source, n);
+ }
+
+ private static IEnumerable InternalDropLast(IEnumerable source, int n)
+ {
+ var buffer = new Queue(n + 1);
+
+ foreach (var x in source)
+ {
+ buffer.Enqueue(x);
+
+ if (buffer.Count == n + 1)
+ yield return buffer.Dequeue();
+ }
+ }
+ }
+}
diff --git a/Utils/HotKeyManager.cs b/Utils/HotKeyManager.cs
new file mode 100644
index 0000000..59845d4
--- /dev/null
+++ b/Utils/HotKeyManager.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Input;
+
+namespace WinHook.Utils
+{
+ public static class HotKeyManager
+ {
+ [DllImport("user32", SetLastError = true)]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
+
+ [DllImport("user32", SetLastError = true)]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ private static int _id = 0;
+ private const int WM_HOTKEY = 0x312;
+ private const int NoRepeat = 0x4000;
+
+ private static volatile MessageWindow _wnd;
+ private static volatile IntPtr _hwnd;
+ private static readonly ManualResetEvent WindowReadyEvent = new ManualResetEvent(false);
+
+ delegate void RegisterHotKeyDelegate(IntPtr hwnd, int id, uint modifiers, uint key);
+ delegate void UnRegisterHotKeyDelegate(IntPtr hwnd, int id);
+
+ public static event EventHandler HotKeyPressed;
+
+ public static int RegisterHotKey(Keys key, ModifierKeys modifiers, bool noRepeat = true)
+ {
+ WindowReadyEvent.WaitOne();
+ var id = Interlocked.Increment(ref _id);
+ var combinedModifiers = noRepeat ? (uint)modifiers | NoRepeat : (uint)modifiers;
+ _wnd.Invoke(new RegisterHotKeyDelegate(RegisterHotKeyInternal), _hwnd, id, combinedModifiers, (uint)key);
+ return id;
+ }
+
+ public static void UnregisterHotKey(int id)
+ {
+ _wnd.Invoke(new UnRegisterHotKeyDelegate(UnRegisterHotKeyInternal), _hwnd, id);
+ }
+
+ private static void RegisterHotKeyInternal(IntPtr hwnd, int id, uint modifiers, uint key)
+ {
+ RegisterHotKey(hwnd, id, modifiers, key);
+ }
+
+ private static void UnRegisterHotKeyInternal(IntPtr hwnd, int id)
+ {
+ UnregisterHotKey(_hwnd, id);
+ }
+
+ private static void OnHotKeyPressed(HotKeyEventArgs e)
+ {
+ if (HotKeyPressed != null)
+ {
+ HotKeyPressed(null, e);
+ }
+ }
+
+ static HotKeyManager()
+ {
+ var messageLoop = new Thread(delegate() { Application.Run(new MessageWindow()); })
+ {
+ Name = "MessageLoopThread",
+ IsBackground = true
+ };
+ messageLoop.Start();
+ }
+
+ private class MessageWindow : Form
+ {
+ public MessageWindow()
+ {
+ _wnd = this;
+ _hwnd = Handle;
+ WindowReadyEvent.Set();
+ }
+
+ protected override void WndProc(ref Message m)
+ {
+ if (m.Msg == WM_HOTKEY)
+ {
+ var e = new HotKeyEventArgs(m.LParam);
+ OnHotKeyPressed(e);
+ }
+
+ base.WndProc(ref m);
+ }
+
+ protected override void SetVisibleCore(bool value)
+ {
+ // Ensure the window never becomes visible
+ base.SetVisibleCore(false);
+ }
+ }
+ }
+
+
+ public class HotKeyEventArgs : EventArgs
+ {
+ public readonly Keys Key;
+ public readonly ModifierKeys Modifiers;
+
+ public HotKeyEventArgs(Keys key, ModifierKeys modifiers)
+ {
+ Key = key;
+ Modifiers = modifiers;
+ }
+
+ public HotKeyEventArgs(IntPtr hotKeyParam)
+ {
+ var param = (uint)hotKeyParam.ToInt64();
+ Key = (Keys)((param & 0xffff0000) >> 16);
+ Modifiers = (ModifierKeys)(param & 0x0000ffff);
+ }
+ }
+}
diff --git a/Utils/KeyHook.cs b/Utils/KeyHook.cs
new file mode 100644
index 0000000..4e61c38
--- /dev/null
+++ b/Utils/KeyHook.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace WinHook.Utils
+{
+ public class KeyCaptureEventArgs : EventArgs
+ {
+ public Keys Key { get; private set; }
+ public int Flags { get; private set; }
+ public IntPtr Extra { get; private set; }
+ public bool Handled { get; set; }
+
+ public KeyCaptureEventArgs(Keys key, int flags, IntPtr extra)
+ {
+ Flags = flags;
+ Extra = extra;
+ Key = key;
+ Handled = false;
+ }
+ }
+
+ public delegate void KeyCaptyreEventHandler(object sender, KeyCaptureEventArgs e);
+
+ public static class KeyHook
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ private struct Kbdllhookstruct
+ {
+ public Keys key;
+ public int scanCode;
+ public int flags;
+ public int time;
+ public IntPtr extra;
+ }
+
+ private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
+
+ public static event KeyCaptyreEventHandler KeyCapture;
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ private static extern IntPtr SetWindowsHookEx(int id, LowLevelKeyboardProc callback, IntPtr hMod,
+ uint dwThreadId);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ private static extern bool UnhookWindowsHookEx(IntPtr hook);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ private static extern IntPtr CallNextHookEx(IntPtr hook, int nCode, IntPtr wp, IntPtr lp);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ private static extern IntPtr GetModuleHandle(string name);
+
+ private static IntPtr _ptrHook;
+ private static readonly LowLevelKeyboardProc ObjKeyboardProcess;
+
+ static KeyHook()
+ {
+ ProcessModule objCurrentModule = Process.GetCurrentProcess().MainModule;
+ ObjKeyboardProcess = CaptureKey;
+ _ptrHook = SetWindowsHookEx(13, ObjKeyboardProcess, GetModuleHandle(objCurrentModule.ModuleName), 0);
+ }
+
+ public static void Clear()
+ {
+ if (_ptrHook == IntPtr.Zero) return;
+ UnhookWindowsHookEx(_ptrHook);
+ _ptrHook = IntPtr.Zero;
+ }
+
+ private static IntPtr CaptureKey(int nCode, IntPtr wp, IntPtr lp)
+ {
+ if (nCode >= 0)
+ {
+ var objKeyInfo = (Kbdllhookstruct)Marshal.PtrToStructure(lp, typeof(Kbdllhookstruct));
+ var e = new KeyCaptureEventArgs(objKeyInfo.key, objKeyInfo.flags, objKeyInfo.extra);
+ OnkeyCaptyre(e);
+ if (e.Handled)
+ {
+ return (IntPtr)1;
+ }
+ }
+ return CallNextHookEx(_ptrHook, nCode, wp, lp);
+ }
+
+ private static void OnkeyCaptyre(KeyCaptureEventArgs e)
+ {
+ if (KeyCapture != null)
+ {
+ KeyCapture(null, e);
+ }
+ }
+
+ }
+}
diff --git a/Utils/MinimizeToTray.cs b/Utils/MinimizeToTray.cs
new file mode 100644
index 0000000..f6bb858
--- /dev/null
+++ b/Utils/MinimizeToTray.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Drawing;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Forms;
+
+namespace WinHook.Utils
+{
+ ///
+ /// Class implementing support for "minimize to tray" functionality.
+ ///
+ public static class MinimizeToTray
+ {
+ ///
+ /// Enables "minimize to tray" behavior for the specified Window.
+ ///
+ /// Window to enable the behavior for.
+ public static void Enable(Window window)
+ {
+ // No need to track this instance; its event handlers will keep it alive
+ new MinimizeToTrayInstance(window);
+ }
+
+ ///
+ /// Class implementing "minimize to tray" functionality for a Window instance.
+ ///
+ private class MinimizeToTrayInstance
+ {
+ private readonly Window _window;
+ private NotifyIcon _notifyIcon;
+
+ ///
+ /// Initializes a new instance of the MinimizeToTrayInstance class.
+ ///
+ /// Window instance to attach to.
+ public MinimizeToTrayInstance(Window window)
+ {
+ _window = window;
+ _window.StateChanged += HandleStateChanged;
+ }
+
+ ///
+ /// Handles the Window's StateChanged event.
+ ///
+ /// Event source.
+ /// Event arguments.
+ private void HandleStateChanged(object sender, EventArgs e)
+ {
+ if (_notifyIcon == null)
+ {
+ // Initialize NotifyIcon instance "on demand"
+ _notifyIcon = new NotifyIcon();
+ _notifyIcon.Icon = Icon.ExtractAssociatedIcon(Assembly.GetEntryAssembly().Location);
+ _notifyIcon.MouseClick += HandleNotifyIconOrBalloonClicked;
+ _notifyIcon.BalloonTipClicked += HandleNotifyIconOrBalloonClicked;
+ }
+ // Update copy of Window Title in case it has changed
+ _notifyIcon.Text = _window.Title;
+
+ // Show/hide Window and NotifyIcon
+ var minimized = (_window.WindowState == WindowState.Minimized);
+ _window.ShowInTaskbar = !minimized;
+ _notifyIcon.Visible = minimized;
+ }
+
+ ///
+ /// Handles a click on the notify icon or its balloon.
+ ///
+ /// Event source.
+ /// Event arguments.
+ private void HandleNotifyIconOrBalloonClicked(object sender, EventArgs e)
+ {
+ // Restore the Window
+ _window.WindowState = WindowState.Normal;
+ }
+ }
+ }
+}
diff --git a/ViewModels/WinHookViewModel.cs b/ViewModels/WinHookViewModel.cs
new file mode 100644
index 0000000..0131063
--- /dev/null
+++ b/ViewModels/WinHookViewModel.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Input;
+using System.Xml.Serialization;
+using WinHook.Models;
+using WinHook.Utils;
+
+namespace WinHook.ViewModels
+{
+ public class WinHookViewModel : ObservableObject
+ {
+ private const string ConfigFileName = "config.xml";
+
+ private Config _config;
+
+ public WinHookViewModel()
+ {
+ _config = LoadConfig();
+ HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
+ KeyHook.KeyCapture += KeyHook_KeyCapture;
+ }
+ public void SaveConfig()
+ {
+ try
+ {
+ using (var fs = File.Open(ConfigFilePath, FileMode.Create))
+ (new XmlSerializer(typeof(Config))).Serialize(fs, _config);
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+ return;
+ }
+ MessageBox.Show("Config file saved!", "Saved", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+
+ public Config LoadConfig()
+ {
+ try
+ {
+ using (var fs = File.Open(ConfigFilePath, FileMode.Open))
+ return (Config)(new XmlSerializer(typeof(Config))).Deserialize(fs);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine("Can't load {0}; {1}", ConfigFilePath, e);
+ return new Config();
+ }
+ }
+
+ private static string ConfigFilePath
+ {
+ get { return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigFileName); }
+ }
+
+ public Config Config
+ {
+ get { return _config; }
+ set
+ {
+ if (_config == value) return;
+ _config = value;
+ RaisePropertyChanged();
+ }
+ }
+
+ public ICommand SaveCommand
+ {
+ get { return new RelayCommand(SaveConfig); }
+ }
+
+ private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
+ {
+ if (e.Key == Config.GeneralConfig.EnableShortcut.Keys && e.Modifiers == Config.GeneralConfig.EnableShortcut.ModifierKeys)
+ {
+ Config.GeneralConfig.IsEnabled = !Config.GeneralConfig.IsEnabled;
+ }
+ }
+
+ private void KeyHook_KeyCapture(object sender, KeyCaptureEventArgs e)
+ {
+ if (Config.KeyBlockConfig.BlockedKeys.Any(key => key == e.Key))
+ {
+ e.Handled = true;
+ }
+ }
+ }
+}
diff --git a/Views/WinHookGeneralControl.xaml b/Views/WinHookGeneralControl.xaml
new file mode 100644
index 0000000..2342137
--- /dev/null
+++ b/Views/WinHookGeneralControl.xaml
@@ -0,0 +1,32 @@
+
+
+
+
+ Enabled
+
+
+ Enabled
+
+
+ Enabled
+
+
+ Enabled
+
+
+
+
+
+
+
+
+
diff --git a/Views/WinHookGeneralControl.xaml.cs b/Views/WinHookGeneralControl.xaml.cs
new file mode 100644
index 0000000..0366329
--- /dev/null
+++ b/Views/WinHookGeneralControl.xaml.cs
@@ -0,0 +1,15 @@
+using System.Windows.Controls;
+
+namespace WinHook.Views
+{
+ ///
+ /// Interaction logic for WinHookGeneralControl.xaml
+ ///
+ public partial class WinHookGeneralControl : UserControl
+ {
+ public WinHookGeneralControl()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Views/WinHookKeyBlockerControl.xaml b/Views/WinHookKeyBlockerControl.xaml
new file mode 100644
index 0000000..d17df41
--- /dev/null
+++ b/Views/WinHookKeyBlockerControl.xaml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Views/WinHookKeyBlockerControl.xaml.cs b/Views/WinHookKeyBlockerControl.xaml.cs
new file mode 100644
index 0000000..34380cc
--- /dev/null
+++ b/Views/WinHookKeyBlockerControl.xaml.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+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.Navigation;
+using System.Windows.Shapes;
+
+namespace WinHook.Views
+{
+ ///
+ /// Interaction logic for WinHookKeyBlockerControl.xaml
+ ///
+ public partial class WinHookKeyBlockerControl : UserControl
+ {
+ public WinHookKeyBlockerControl()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Views/WinHookWindow.xaml b/Views/WinHookWindow.xaml
new file mode 100644
index 0000000..ec2ac0a
--- /dev/null
+++ b/Views/WinHookWindow.xaml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Views/WinHookWindow.xaml.cs b/Views/WinHookWindow.xaml.cs
new file mode 100644
index 0000000..cfc17ed
--- /dev/null
+++ b/Views/WinHookWindow.xaml.cs
@@ -0,0 +1,29 @@
+using System.Windows;
+using WinHook.ViewModels;
+using WinHook.Utils;
+
+namespace WinHook.Views
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+
+ private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
+ {
+ var window = (MainWindow) sender;
+ var vm = (WinHookViewModel)window.DataContext;
+
+ if (vm.Config.GeneralConfig.MinimizeToTray)
+ MinimizeToTray.Enable(this);
+
+ if (vm.Config.GeneralConfig.StartMinimized)
+ WindowState = WindowState.Minimized;
+ }
+ }
+}
diff --git a/WinHook.csproj b/WinHook.csproj
new file mode 100644
index 0000000..a3d3f04
--- /dev/null
+++ b/WinHook.csproj
@@ -0,0 +1,176 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {ABC247F3-02AF-407F-B613-33CCF1322F49}
+ WinExe
+ Properties
+ WinHook
+ WinHook
+ 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
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ WinHook.App
+
+
+ block.ico
+
+
+
+
+
+
+
+
+
+
+
+
+ 4.0
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ WinHookGeneralControl.xaml
+
+
+ WinHookKeyBlockerControl.xaml
+
+
+ WinHookWindow.xaml
+
+
+ App.xaml
+ Code
+
+
+ MSBuild:Compile
+ Designer
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+
+
+ Code
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ Settings.settings
+ True
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ 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/WinHook.sln b/WinHook.sln
new file mode 100644
index 0000000..56d0f35
--- /dev/null
+++ b/WinHook.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinHook", "WinHook.csproj", "{ABC247F3-02AF-407F-B613-33CCF1322F49}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {ABC247F3-02AF-407F-B613-33CCF1322F49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ABC247F3-02AF-407F-B613-33CCF1322F49}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ABC247F3-02AF-407F-B613-33CCF1322F49}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ABC247F3-02AF-407F-B613-33CCF1322F49}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/block.ico b/block.ico
new file mode 100644
index 0000000..8ef0559
Binary files /dev/null and b/block.ico differ