├── .build ├── build.ps1 ├── docfx.json ├── lib │ └── Newtonsoft.Json.dll └── setup.ps1 ├── .gitignore ├── .nuget ├── NuGet.Config ├── NuGet.exe └── NuGet.targets ├── Build.bat ├── LICENSE ├── README.md ├── _config.yml ├── appveyor.yml ├── docs ├── README.html ├── api │ ├── EventHook.ApplicationEventArgs.html │ ├── EventHook.ApplicationEvents.html │ ├── EventHook.ApplicationWatcher.html │ ├── EventHook.ClipboardContentTypes.html │ ├── EventHook.ClipboardEventArgs.html │ ├── EventHook.ClipboardWatcher.html │ ├── EventHook.EventHookFactory.html │ ├── EventHook.Hooks.MouseMessages.html │ ├── EventHook.Hooks.POINT.html │ ├── EventHook.Hooks.WindowEventArgs.html │ ├── EventHook.Hooks.WindowHookEx.html │ ├── EventHook.Hooks.html │ ├── EventHook.KeyData.html │ ├── EventHook.KeyEvent.html │ ├── EventHook.KeyInputEventArgs.html │ ├── EventHook.KeyboardWatcher.html │ ├── EventHook.MouseEventArgs.html │ ├── EventHook.MouseWatcher.html │ ├── EventHook.PrintEventArgs.html │ ├── EventHook.PrintEventData.html │ ├── EventHook.PrintWatcher.html │ ├── EventHook.WindowData.html │ ├── EventHook.html │ └── toc.html ├── favicon.ico ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── index.json ├── logo.svg ├── search-stopwords.json ├── styles │ ├── docfx.css │ ├── docfx.js │ ├── docfx.vendor.css │ ├── docfx.vendor.js │ ├── lunr.js │ ├── lunr.min.js │ ├── main.css │ ├── main.js │ └── search-worker.js └── xrefmap.yml ├── examples ├── EventHook.ConsoleApp.Example │ ├── Capture.PNG │ ├── EventHook.ConsoleApp.Example.csproj │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── app.config ├── EventHook.WPF.Example │ ├── App.config │ ├── App.xaml │ ├── App.xaml.cs │ ├── EventHook.WPF.Example.csproj │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ └── Properties │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings └── EventHook.WinForms.Example │ ├── App.config │ ├── EventHook.WinForms.Example.csproj │ ├── MainForm.Designer.cs │ ├── MainForm.cs │ ├── Program.cs │ └── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings └── src ├── EventHook.Docs.sln ├── EventHook.sln ├── EventHook.sln.DotSettings └── EventHook ├── ApplicationWatcher.cs ├── ClipboardWatcher.cs ├── EventHook.csproj ├── EventHook.nuspec ├── EventHookFactory.cs ├── Helpers ├── AsyncConcurrentQueue.cs ├── SyncFactory.cs └── WindowHelper.cs ├── Hooks ├── ClipBoardHook.cs ├── KeyboardHook.cs ├── Library │ ├── Enum.cs │ ├── Msgs.cs │ ├── PrintSpoolAPI.cs │ ├── Procs.cs │ ├── Structs.cs │ └── User32.cs ├── MouseHook.cs ├── PrintQueueHook.cs ├── ShellHook.cs ├── WindowHook.cs └── WindowHookEx.cs ├── KeyboardWatcher.cs ├── MouseWatcher.cs ├── PrintWatcher.cs ├── Properties └── AssemblyInfo.cs ├── StrongNameKey.snk └── app.config /.build/build.ps1: -------------------------------------------------------------------------------- 1 | $PSake.use_exit_on_error = $true 2 | 3 | $Here = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" 4 | 5 | $RepoRoot = $(Split-Path -parent $Here) 6 | $SolutionRoot = "$RepoRoot\src" 7 | 8 | $ProjectName = "EventHook" 9 | $GitHubProjectName = "Windows-User-Action-Hook" 10 | $GitHubUserName = "justcoding121" 11 | 12 | $SolutionFile = "$SolutionRoot\$ProjectName.sln" 13 | 14 | ## This comes from the build server iteration 15 | if(!$BuildNumber) { $BuildNumber = $env:APPVEYOR_BUILD_NUMBER } 16 | if(!$BuildNumber) { $BuildNumber = "0"} 17 | 18 | ## The build configuration, i.e. Debug/Release 19 | if(!$Configuration) { $Configuration = $env:Configuration } 20 | if(!$Configuration) { $Configuration = "Release" } 21 | 22 | if(!$Version) { $Version = $env:APPVEYOR_BUILD_VERSION } 23 | if(!$Version) { $Version = "0.0.$BuildNumber" } 24 | 25 | if(!$Branch) { $Branch = $env:APPVEYOR_REPO_BRANCH } 26 | if(!$Branch) { $Branch = "local" } 27 | 28 | if($Branch -eq "beta" ) { $Version = "$Version-beta" } 29 | 30 | $NuGet = Join-Path $RepoRoot ".nuget\nuget.exe" 31 | 32 | $MSBuild = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\msbuild.exe" 33 | $MSBuild -replace ' ', '` ' 34 | 35 | FormatTaskName (("-"*25) + "[{0}]" + ("-"*25)) 36 | 37 | #default task 38 | Task default -depends Clean, Build, Document, Package 39 | 40 | #cleans obj, b 41 | Task Clean { 42 | Get-ChildItem .\ -include bin,obj -Recurse | foreach ($_) { Remove-Item $_.fullname -Force -Recurse } 43 | exec { . $MSBuild $SolutionFile /t:Clean /v:quiet } 44 | } 45 | 46 | #install build tools 47 | Task Install-BuildTools -depends Clean { 48 | if(!(Test-Path $MSBuild)) 49 | { 50 | cinst microsoft-build-tools -y 51 | } 52 | } 53 | 54 | #restore nuget packages 55 | Task Restore-Packages -depends Install-BuildTools { 56 | exec { . dotnet restore "$SolutionRoot\$ProjectName.sln" } 57 | } 58 | 59 | #build 60 | Task Build -depends Restore-Packages{ 61 | exec { . $MSBuild $SolutionFile /t:Build /v:normal /p:Configuration=$Configuration /t:restore } 62 | } 63 | 64 | #publish API documentation changes for GitHub pages under master\docs directory 65 | Task Document -depends Build { 66 | 67 | if($Branch -eq "develop") 68 | { 69 | 70 | #use docfx to generate API documentation from source metadata 71 | docfx docfx.json 72 | 73 | #patch index.json so that it is always sorted 74 | #otherwise git will think file was changed 75 | $IndexJsonFile = "$RepoRoot\docs\index.json" 76 | $unsorted = Get-Content $IndexJsonFile | Out-String 77 | [Reflection.Assembly]::LoadFile("$Here\lib\Newtonsoft.Json.dll") 78 | [System.Reflection.Assembly]::LoadWithPartialName("System") 79 | $hashTable = [Newtonsoft.Json.JsonConvert]::DeserializeObject($unsorted, [System.Collections.Generic.SortedDictionary[[string],[object]]]) 80 | $obj = [Newtonsoft.Json.JsonConvert]::SerializeObject($hashTable, [Newtonsoft.Json.Formatting]::Indented) 81 | Set-Content -Path $IndexJsonFile -Value $obj 82 | 83 | #setup clone directory 84 | $TEMP_REPO_DIR =(Split-Path -parent $RepoRoot) + "\temp-repo-clone" 85 | 86 | If(test-path $TEMP_REPO_DIR) 87 | { 88 | Remove-Item $TEMP_REPO_DIR -Force -Recurse 89 | } 90 | 91 | New-Item -ItemType Directory -Force -Path $TEMP_REPO_DIR 92 | 93 | #clone 94 | git clone https://github.com/$GitHubUserName/$GitHubProjectName.git --branch develop $TEMP_REPO_DIR 95 | 96 | If(test-path "$TEMP_REPO_DIR\docs") 97 | { 98 | Remove-Item "$TEMP_REPO_DIR\docs" -Force -Recurse 99 | } 100 | New-Item -ItemType Directory -Force -Path "$TEMP_REPO_DIR\docs" 101 | 102 | #cd to docs folder 103 | cd "$TEMP_REPO_DIR\docs" 104 | 105 | #copy docs to clone directory\docs 106 | Copy-Item -Path "$RepoRoot\docs\*" -Destination "$TEMP_REPO_DIR\docs" -Recurse -Force 107 | 108 | #push changes to develop 109 | git config --global credential.helper store 110 | Add-Content "$HOME\.git-credentials" "https://$($env:github_access_token):x-oauth-basic@github.com`n" 111 | git config --global user.email $env:github_email 112 | git config --global user.name "buildbot171" 113 | git add . -A 114 | git commit -m "API documentation update by build server" 115 | git push origin develop 116 | 117 | #move cd back to current location 118 | cd $Here 119 | } 120 | } 121 | 122 | #package nuget files 123 | Task Package -depends Document { 124 | exec { . $NuGet pack "$SolutionRoot\$ProjectName\$ProjectName.nuspec" -Properties Configuration=$Configuration -OutputDirectory "$RepoRoot" -Version "$Version" } 125 | } 126 | 127 | -------------------------------------------------------------------------------- /.build/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": [ 3 | { 4 | "src": [ 5 | { 6 | "files": [ "EventHook.Docs.sln"], 7 | "src": "../src/" 8 | } 9 | ], 10 | "dest": "obj/api" 11 | } 12 | ], 13 | "build": { 14 | "content": [ 15 | { 16 | "files": [ "**/*.yml" ], 17 | "src": "obj/api", 18 | "dest": "api" 19 | }, 20 | { 21 | "files": [ "*.md" ] 22 | } 23 | ], 24 | "resource": [ 25 | { 26 | "files": [ ""] 27 | } 28 | ], 29 | "overwrite": "specs/*.md", 30 | "globalMetadata": { 31 | "_appTitle": "EventHook", 32 | "_enableSearch": true 33 | }, 34 | "dest": "../docs", 35 | "xrefService": [ "https://xref.docs.microsoft.com/query?uid={uid}" ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.build/lib/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justcoding121/windows-user-action-hook/0397d20212baaa1cefe364af58dba892265d1dc8/.build/lib/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /.build/setup.ps1: -------------------------------------------------------------------------------- 1 | param ( 2 | [string]$Action="default", 3 | [hashtable]$properties=@{}, 4 | [switch]$Help 5 | ) 6 | 7 | function Install-Chocolatey() 8 | { 9 | if(-not $env:ChocolateyInstall -or -not (Test-Path "$env:ChocolateyInstall\*")) 10 | { 11 | Write-Output "Chocolatey Not Found, Installing..." 12 | iex ((new-object net.webclient).DownloadString('http://chocolatey.org/install.ps1')) 13 | } 14 | $env:Path += ";${env:ChocolateyInstall}" 15 | } 16 | 17 | function Install-Psake() 18 | { 19 | if(!(Test-Path $env:ChocolateyInstall\lib\Psake\tools\Psake*)) 20 | { 21 | choco install psake -y 22 | } 23 | } 24 | 25 | function Install-Git() 26 | { 27 | if(!((Test-Path ${env:ProgramFiles(x86)}\Git*) -Or (Test-Path ${env:ProgramFiles}\Git*))) 28 | { 29 | choco install git.install 30 | } 31 | $env:Path += ";${env:ProgramFiles(x86)}\Git" 32 | $env:Path += ";${env:ProgramFiles}\Git" 33 | } 34 | 35 | function Install-DocFx() 36 | { 37 | if(!(Test-Path $env:ChocolateyInstall\lib\docfx\tools*)) 38 | { 39 | choco install docfx --version 2.55 40 | } 41 | $env:Path += ";$env:ChocolateyInstall\lib\docfx\tools" 42 | } 43 | 44 | #current directory 45 | $Here = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" 46 | 47 | $ErrorActionPreference = 'Stop' 48 | Set-StrictMode -Version Latest 49 | 50 | $ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Definition 51 | $SolutionRoot = Split-Path -Parent $ScriptPath 52 | $ToolsPath = Join-Path -Path $SolutionRoot -ChildPath "lib" 53 | 54 | if(-not $env:ChocolateyInstall) 55 | { 56 | $env:ChocolateyInstall = "${env:ALLUSERSPROFILE}\chocolatey"; 57 | } 58 | 59 | Install-Chocolatey 60 | 61 | Install-Psake 62 | 63 | Install-Git 64 | 65 | Install-DocFx 66 | 67 | $psakeDirectory = (Resolve-Path $env:ChocolateyInstall\lib\Psake*) 68 | 69 | #appveyor for some reason have different location for psake (it has older psake version?) 70 | if(Test-Path $psakeDirectory\tools\Psake\Psake.psm*) 71 | { 72 | Import-Module (Join-Path $psakeDirectory "tools\Psake\Psake.psm1") 73 | } 74 | else 75 | { 76 | Import-Module (Join-Path $psakeDirectory "tools\Psake.psm1") 77 | } 78 | 79 | 80 | #invoke the task 81 | Invoke-Psake -buildFile "$Here\build.ps1" -parameters $properties -tasklist $Action 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | # DNX 42 | project.lock.json 43 | artifacts/ 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding add-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | # TODO: Comment the next line if you want to checkin your web deploy settings 137 | # but database connection strings (with potential passwords) will be unencrypted 138 | *.pubxml 139 | *.publishproj 140 | 141 | # NuGet Packages 142 | *.nupkg 143 | # The packages folder can be ignored because of Package Restore 144 | **/packages/* 145 | # except build/, which is used as an MSBuild target. 146 | !**/packages/build/ 147 | # Uncomment if necessary however generally it will be regenerated when needed 148 | #!**/packages/repositories.config 149 | 150 | # Windows Azure Build Output 151 | csx/ 152 | *.build.csdef 153 | 154 | # Windows Store app package directory 155 | AppPackages/ 156 | 157 | # Visual Studio cache files 158 | # files ending in .cache can be ignored 159 | *.[Cc]ache 160 | # but keep track of directories ending in .cache 161 | !*.[Cc]ache/ 162 | 163 | # Others 164 | ClientBin/ 165 | [Ss]tyle[Cc]op.* 166 | ~$* 167 | *~ 168 | *.dbmdl 169 | *.dbproj.schemaview 170 | *.pfx 171 | *.publishsettings 172 | node_modules/ 173 | orleans.codegen.cs 174 | 175 | # RIA/Silverlight projects 176 | Generated_Code/ 177 | 178 | # Backup & report files from converting an old project file 179 | # to a newer Visual Studio version. Backup files are not needed, 180 | # because we have git ;-) 181 | _UpgradeReport_Files/ 182 | Backup*/ 183 | UpgradeLog*.XML 184 | UpgradeLog*.htm 185 | 186 | # SQL Server files 187 | *.mdf 188 | *.ldf 189 | 190 | # Business Intelligence projects 191 | *.rdl.data 192 | *.bim.layout 193 | *.bim_*.settings 194 | 195 | # Microsoft Fakes 196 | FakesAssemblies/ 197 | 198 | # Node.js Tools for Visual Studio 199 | .ntvs_analysis.dat 200 | 201 | # Visual Studio 6 build log 202 | *.plg 203 | 204 | # Visual Studio 6 workspace options file 205 | *.opt 206 | 207 | # Docfx 208 | docs/manifest.json -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justcoding121/windows-user-action-hook/0397d20212baaa1cefe364af58dba892265d1dc8/.nuget/NuGet.exe -------------------------------------------------------------------------------- /Build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | powershell -NoProfile -ExecutionPolicy bypass -Command "%~dp0.build\setup.ps1 %*; if ($psake.build_success -eq $false) { exit 1 } else { exit 0 }" 4 | exit /B %errorlevel% -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Windows User Action Hook 2 | 3 | ### Note: This Project is no longer maintained. 4 | 5 | A one stop library for global windows user actions such mouse, keyboard, clipboard, & print events 6 | 7 | ![Build Status](https://ci.appveyor.com/api/projects/status/htea647ukrgg4qcl?svg=true) 8 | 9 | Kindly report only issues/bugs here . For programming help or questions use [StackOverflow](http://stackoverflow.com/questions/tagged/windows-user-action-hook) with the tag EventHook or Windows-User-Action-Hook. 10 | 11 | * [API Documentation](https://justcoding121.github.io/windows-user-action-hook/docs/api/EventHook.html) 12 | 13 | ### Supported Events 14 | 15 | * Keyboard events 16 | * Mouse events 17 | * clipboard events 18 | * application events 19 | * print events 20 | 21 | ### Development enviroment 22 | 23 | * Visual Studio 2017 24 | 25 | ### Usage 26 | 27 | Install by [nuget](https://www.nuget.org/packages/EventHook) 28 | 29 | Install-Package EventHook 30 | 31 | ### Sample Code: 32 | 33 | ```csharp 34 | using (var eventHookFactory = new EventHookFactory()) 35 | { 36 | var keyboardWatcher = eventHookFactory.GetKeyboardWatcher(); 37 | keyboardWatcher.Start(); 38 | keyboardWatcher.OnKeyInput += (s, e) => 39 | { 40 | Console.WriteLine(string.Format("Key {0} event of key {1}", e.KeyData.EventType, e.KeyData.Keyname)); 41 | }; 42 | 43 | var mouseWatcher = eventHookFactory.GetMouseWatcher(); 44 | mouseWatcher.Start(); 45 | mouseWatcher.OnMouseInput += (s, e) => 46 | { 47 | Console.WriteLine(string.Format("Mouse event {0} at point {1},{2}", e.Message.ToString(), e.Point.x, e.Point.y)); 48 | }; 49 | 50 | var clipboardWatcher = eventHookFactory.GetClipboardWatcher(); 51 | clipboardWatcher.Start(); 52 | clipboardWatcher.OnClipboardModified += (s, e) => 53 | { 54 | Console.WriteLine(string.Format("Clipboard updated with data '{0}' of format {1}", e.Data, e.DataFormat.ToString())); 55 | }; 56 | 57 | 58 | var applicationWatcher = eventHookFactory.GetApplicationWatcher(); 59 | applicationWatcher.Start(); 60 | applicationWatcher.OnApplicationWindowChange += (s, e) => 61 | { 62 | Console.WriteLine(string.Format("Application window of '{0}' with the title '{1}' was {2}", e.ApplicationData.AppName, e.ApplicationData.AppTitle, e.Event)); 63 | }; 64 | 65 | var printWatcher = eventHookFactory.GetPrintWatcher(); 66 | printWatcher.Start(); 67 | printWatcher.OnPrintEvent += (s, e) => 68 | { 69 | Console.WriteLine(string.Format("Printer '{0}' currently printing {1} pages.", e.EventData.PrinterName, e.EventData.Pages)); 70 | }; 71 | 72 | //waiting here to keep this thread running 73 | Console.Read(); 74 | 75 | //stop watching 76 | keyboardWatcher.Stop(); 77 | mouseWatcher.Stop(); 78 | clipboardWatcher.Stop(); 79 | applicationWatcher.Stop(); 80 | printWatcher.Stop(); 81 | } 82 | ``` 83 | 84 | ![alt tag](https://raw.githubusercontent.com/justcoding121/Windows-User-Action-Hook/develop/examples/EventHook.ConsoleApp.Example/Capture.PNG) 85 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | baseurl: /Windows-User-Action-Hook -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # AppVeyor CI build file 2 | 3 | # Notes: 4 | # - Minimal appveyor.yml file is an empty file. All sections are optional. 5 | # - Indent each level of configuration with 2 spaces. Do not use tabs! 6 | # - All section names are case-sensitive. 7 | # - Section names should be unique on each level. 8 | 9 | # version format 10 | version: 1.4.{build} 11 | image: Visual Studio 2017 12 | 13 | shallow_clone: true 14 | 15 | #---------------------------------# 16 | # build configuration # 17 | #---------------------------------# 18 | 19 | # build platform, i.e. x86, x64, Any CPU. This setting is optional. 20 | platform: Any CPU 21 | 22 | # build Configuration, i.e. Debug, Release, etc. 23 | configuration: Release 24 | 25 | # to run your custom scripts instead of automatic MSBuild 26 | build_script: 27 | - cmd: build.bat Package 28 | 29 | assembly_info: 30 | patch: true 31 | file: AssemblyInfo.* 32 | assembly_version: "{version}" 33 | assembly_file_version: "{version}" 34 | assembly_informational_version: "{version}" 35 | 36 | # to disable automatic tests 37 | test: on 38 | 39 | # skip building commits that add tags (such as release tag) 40 | skip_tags: true 41 | 42 | skip_commits: 43 | author: buildbot171 44 | files: 45 | - docs/* 46 | - README.md 47 | - LICENSE 48 | #---------------------------------# 49 | # artifacts configuration # 50 | #---------------------------------# 51 | 52 | nuget: 53 | disable_publish_on_pr: true # disable publishing of .nupkg artifacts to account/project feeds for pull request builds 54 | 55 | artifacts: 56 | - path: '**\EventHook.*.nupkg' 57 | 58 | environment: 59 | github_access_token: 60 | secure: mZLeq0GTB9kb5b6+HnVpJB6hhiYMJIQ2+Zf/DwZ/LEIyxJaYB1nx36aGHXE9q1cN 61 | github_email: 62 | secure: iBJZGqxyiHVNeYI0uIW+MdGd3I3pg8brJtETNRkKe/A= 63 | nuget_access_token: 64 | secure: 65rklofkoUWJzPPja621kXlxrruHemmbREnIX7QgXK0cydW412yYMZJQsN42Js27 65 | deploy: 66 | - provider: GitHub 67 | auth_token: $(github_access_token) 68 | on: 69 | branch: /(stable|beta)/ 70 | - provider: NuGet 71 | api_key: $(nuget_access_token) 72 | on: 73 | branch: /(stable|beta)/ 74 | -------------------------------------------------------------------------------- /docs/README.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Windows User Action Hook | EventHook 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | 50 | 51 | 58 |
59 |
60 | 61 |
62 |
63 |
64 |

65 |
66 |
    67 |
    68 |
    69 | 160 | 161 | 173 |
    174 | 175 | 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /docs/api/EventHook.ApplicationEvents.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Enum ApplicationEvents 9 | | EventHook 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
    27 |
    28 | 29 | 52 | 53 | 60 |
    61 |
    62 | 63 |
    64 |
    65 |
    66 |

    67 |
    68 |
      69 |
      70 |
      71 | 135 | 136 | 148 |
      149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /docs/api/EventHook.ClipboardContentTypes.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Enum ClipboardContentTypes 9 | | EventHook 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
      27 |
      28 | 29 | 52 | 53 | 60 |
      61 |
      62 | 63 |
      64 |
      65 |
      66 |

      67 |
      68 |
        69 |
        70 |
        71 | 143 | 144 | 156 |
        157 | 158 | 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /docs/api/EventHook.Hooks.MouseMessages.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Enum MouseMessages 9 | | EventHook 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
        27 |
        28 | 29 | 52 | 53 | 60 |
        61 |
        62 | 63 |
        64 |
        65 |
        66 |

        67 |
        68 |
          69 |
          70 |
          71 | 163 | 164 | 176 |
          177 | 178 | 179 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /docs/api/EventHook.Hooks.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Namespace EventHook.Hooks 9 | | EventHook 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
          27 |
          28 | 29 | 52 | 53 | 60 |
          61 |
          62 | 63 |
          64 |
          65 |
          66 |

          67 |
          68 |
            69 |
            70 |
            71 | 122 | 123 | 135 |
            136 | 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /docs/api/EventHook.KeyEvent.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Enum KeyEvent 9 | | EventHook 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
            27 |
            28 | 29 | 52 | 53 | 60 |
            61 |
            62 | 63 |
            64 |
            65 |
            66 |

            67 |
            68 |
              69 |
              70 |
              71 | 131 | 132 | 144 |
              145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /docs/api/toc.html: -------------------------------------------------------------------------------- 1 |  2 |
              3 |
              4 |
              5 |
              6 | 7 | 8 | 9 |
              10 |
              11 |
              12 |
              13 | 14 | 93 |
              94 |
              95 |
              96 |
              -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justcoding121/windows-user-action-hook/0397d20212baaa1cefe364af58dba892265d1dc8/docs/favicon.ico -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justcoding121/windows-user-action-hook/0397d20212baaa1cefe364af58dba892265d1dc8/docs/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justcoding121/windows-user-action-hook/0397d20212baaa1cefe364af58dba892265d1dc8/docs/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justcoding121/windows-user-action-hook/0397d20212baaa1cefe364af58dba892265d1dc8/docs/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /docs/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justcoding121/windows-user-action-hook/0397d20212baaa1cefe364af58dba892265d1dc8/docs/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /docs/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by Docfx 9 | 10 | 12 | 15 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /docs/search-stopwords.json: -------------------------------------------------------------------------------- 1 | [ 2 | "a", 3 | "able", 4 | "about", 5 | "across", 6 | "after", 7 | "all", 8 | "almost", 9 | "also", 10 | "am", 11 | "among", 12 | "an", 13 | "and", 14 | "any", 15 | "are", 16 | "as", 17 | "at", 18 | "be", 19 | "because", 20 | "been", 21 | "but", 22 | "by", 23 | "can", 24 | "cannot", 25 | "could", 26 | "dear", 27 | "did", 28 | "do", 29 | "does", 30 | "either", 31 | "else", 32 | "ever", 33 | "every", 34 | "for", 35 | "from", 36 | "get", 37 | "got", 38 | "had", 39 | "has", 40 | "have", 41 | "he", 42 | "her", 43 | "hers", 44 | "him", 45 | "his", 46 | "how", 47 | "however", 48 | "i", 49 | "if", 50 | "in", 51 | "into", 52 | "is", 53 | "it", 54 | "its", 55 | "just", 56 | "least", 57 | "let", 58 | "like", 59 | "likely", 60 | "may", 61 | "me", 62 | "might", 63 | "most", 64 | "must", 65 | "my", 66 | "neither", 67 | "no", 68 | "nor", 69 | "not", 70 | "of", 71 | "off", 72 | "often", 73 | "on", 74 | "only", 75 | "or", 76 | "other", 77 | "our", 78 | "own", 79 | "rather", 80 | "said", 81 | "say", 82 | "says", 83 | "she", 84 | "should", 85 | "since", 86 | "so", 87 | "some", 88 | "than", 89 | "that", 90 | "the", 91 | "their", 92 | "them", 93 | "then", 94 | "there", 95 | "these", 96 | "they", 97 | "this", 98 | "tis", 99 | "to", 100 | "too", 101 | "twas", 102 | "us", 103 | "wants", 104 | "was", 105 | "we", 106 | "were", 107 | "what", 108 | "when", 109 | "where", 110 | "which", 111 | "while", 112 | "who", 113 | "whom", 114 | "why", 115 | "will", 116 | "with", 117 | "would", 118 | "yet", 119 | "you", 120 | "your" 121 | ] 122 | -------------------------------------------------------------------------------- /docs/styles/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justcoding121/windows-user-action-hook/0397d20212baaa1cefe364af58dba892265d1dc8/docs/styles/main.css -------------------------------------------------------------------------------- /docs/styles/main.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information. 2 | -------------------------------------------------------------------------------- /docs/styles/search-worker.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | importScripts('lunr.min.js'); 3 | 4 | var lunrIndex; 5 | 6 | var stopWords = null; 7 | var searchData = {}; 8 | 9 | lunr.tokenizer.separator = /[\s\-\.\(\)]+/; 10 | 11 | var stopWordsRequest = new XMLHttpRequest(); 12 | stopWordsRequest.open('GET', '../search-stopwords.json'); 13 | stopWordsRequest.onload = function () { 14 | if (this.status != 200) { 15 | return; 16 | } 17 | stopWords = JSON.parse(this.responseText); 18 | buildIndex(); 19 | } 20 | stopWordsRequest.send(); 21 | 22 | var searchDataRequest = new XMLHttpRequest(); 23 | 24 | searchDataRequest.open('GET', '../index.json'); 25 | searchDataRequest.onload = function () { 26 | if (this.status != 200) { 27 | return; 28 | } 29 | searchData = JSON.parse(this.responseText); 30 | 31 | buildIndex(); 32 | 33 | postMessage({ e: 'index-ready' }); 34 | } 35 | searchDataRequest.send(); 36 | 37 | onmessage = function (oEvent) { 38 | var q = oEvent.data.q; 39 | var hits = lunrIndex.search(q); 40 | var results = []; 41 | hits.forEach(function (hit) { 42 | var item = searchData[hit.ref]; 43 | results.push({ 'href': item.href, 'title': item.title, 'keywords': item.keywords }); 44 | }); 45 | postMessage({ e: 'query-ready', q: q, d: results }); 46 | } 47 | 48 | function buildIndex() { 49 | if (stopWords !== null && !isEmpty(searchData)) { 50 | lunrIndex = lunr(function () { 51 | this.pipeline.remove(lunr.stopWordFilter); 52 | this.ref('href'); 53 | this.field('title', { boost: 50 }); 54 | this.field('keywords', { boost: 20 }); 55 | 56 | for (var prop in searchData) { 57 | if (searchData.hasOwnProperty(prop)) { 58 | this.add(searchData[prop]); 59 | } 60 | } 61 | 62 | var docfxStopWordFilter = lunr.generateStopWordFilter(stopWords); 63 | lunr.Pipeline.registerFunction(docfxStopWordFilter, 'docfxStopWordFilter'); 64 | this.pipeline.add(docfxStopWordFilter); 65 | this.searchPipeline.add(docfxStopWordFilter); 66 | }); 67 | } 68 | } 69 | 70 | function isEmpty(obj) { 71 | if(!obj) return true; 72 | 73 | for (var prop in obj) { 74 | if (obj.hasOwnProperty(prop)) 75 | return false; 76 | } 77 | 78 | return true; 79 | } 80 | })(); 81 | -------------------------------------------------------------------------------- /examples/EventHook.ConsoleApp.Example/Capture.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justcoding121/windows-user-action-hook/0397d20212baaa1cefe364af58dba892265d1dc8/examples/EventHook.ConsoleApp.Example/Capture.PNG -------------------------------------------------------------------------------- /examples/EventHook.ConsoleApp.Example/EventHook.ConsoleApp.Example.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {0075830E-89B6-4E6B-9C49-CA377746AB4C} 8 | Exe 9 | Properties 10 | EventHook.ConsoleApp.Example 11 | EventHook.ConsoleApp.Example 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | v4.5 26 | false 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | v4.5 37 | false 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | {3847a7f4-3f6b-4626-af4c-8fdfba6165f2} 59 | EventHook 60 | 61 | 62 | 63 | 70 | -------------------------------------------------------------------------------- /examples/EventHook.ConsoleApp.Example/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EventHook.ConsoleApp.Example 4 | { 5 | internal class Program 6 | { 7 | private static void Main(string[] args) 8 | { 9 | var eventHookFactory = new EventHookFactory(); 10 | 11 | var keyboardWatcher = eventHookFactory.GetKeyboardWatcher(); 12 | keyboardWatcher.Start(); 13 | keyboardWatcher.OnKeyInput += (s, e) => 14 | { 15 | Console.WriteLine("Key {0} event of key {1}", e.KeyData.EventType, e.KeyData.Keyname); 16 | }; 17 | 18 | var mouseWatcher = eventHookFactory.GetMouseWatcher(); 19 | mouseWatcher.Start(); 20 | mouseWatcher.OnMouseInput += (s, e) => 21 | { 22 | Console.WriteLine("Mouse event {0} at point {1},{2}", e.Message.ToString(), e.Point.x, e.Point.y); 23 | }; 24 | 25 | var clipboardWatcher = eventHookFactory.GetClipboardWatcher(); 26 | clipboardWatcher.Start(); 27 | clipboardWatcher.OnClipboardModified += (s, e) => 28 | { 29 | Console.WriteLine("Clipboard updated with data '{0}' of format {1}", e.Data, 30 | e.DataFormat.ToString()); 31 | }; 32 | 33 | 34 | var applicationWatcher = eventHookFactory.GetApplicationWatcher(); 35 | applicationWatcher.Start(); 36 | applicationWatcher.OnApplicationWindowChange += (s, e) => 37 | { 38 | Console.WriteLine("Application window of '{0}' with the title '{1}' was {2}", 39 | e.ApplicationData.AppName, e.ApplicationData.AppTitle, e.Event); 40 | }; 41 | 42 | var printWatcher = eventHookFactory.GetPrintWatcher(); 43 | printWatcher.Start(); 44 | printWatcher.OnPrintEvent += (s, e) => 45 | { 46 | Console.WriteLine("Printer '{0}' currently printing {1} pages.", e.EventData.PrinterName, 47 | e.EventData.Pages); 48 | }; 49 | 50 | 51 | Console.Read(); 52 | 53 | keyboardWatcher.Stop(); 54 | mouseWatcher.Stop(); 55 | clipboardWatcher.Stop(); 56 | applicationWatcher.Stop(); 57 | printWatcher.Stop(); 58 | 59 | eventHookFactory.Dispose(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/EventHook.ConsoleApp.Example/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("EventHook.Tests")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("EventHook.Tests")] 12 | [assembly: AssemblyCopyright("Copyright © 2015")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("3d0265b1-1af4-442c-8536-2815400e0b95")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /examples/EventHook.ConsoleApp.Example/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/EventHook.WPF.Example/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples/EventHook.WPF.Example/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/EventHook.WPF.Example/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace EventHook.WPF.Example 4 | { 5 | /// 6 | /// Interaction logic for App.xaml 7 | /// 8 | public partial class App : Application 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/EventHook.WPF.Example/EventHook.WPF.Example.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {41F4FF6F-0E65-439C-9091-BA2C8E25BAE0} 8 | WinExe 9 | Properties 10 | EventHook.WPF.Example 11 | EventHook.WPF.Example 12 | v4.5.1 13 | 512 14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 4 16 | true 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 4.0 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | MSBuild:Compile 55 | Designer 56 | 57 | 58 | MSBuild:Compile 59 | Designer 60 | 61 | 62 | App.xaml 63 | Code 64 | 65 | 66 | MainWindow.xaml 67 | Code 68 | 69 | 70 | 71 | 72 | Code 73 | 74 | 75 | True 76 | True 77 | Resources.resx 78 | 79 | 80 | True 81 | Settings.settings 82 | True 83 | 84 | 85 | ResXFileCodeGenerator 86 | Resources.Designer.cs 87 | 88 | 89 | SettingsSingleFileGenerator 90 | Settings.Designer.cs 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | {3847a7f4-3f6b-4626-af4c-8fdfba6165f2} 100 | EventHook 101 | 102 | 103 | 104 | 111 | -------------------------------------------------------------------------------- /examples/EventHook.WPF.Example/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | -------------------------------------------------------------------------------- /examples/EventHook.WPF.Example/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | 4 | namespace EventHook.WPF.Example 5 | { 6 | /// 7 | /// Interaction logic for MainWindow.xaml 8 | /// 9 | public partial class MainWindow : Window 10 | { 11 | private readonly ApplicationWatcher applicationWatcher; 12 | private readonly ClipboardWatcher clipboardWatcher; 13 | private readonly EventHookFactory eventHookFactory = new EventHookFactory(); 14 | private readonly KeyboardWatcher keyboardWatcher; 15 | private readonly MouseWatcher mouseWatcher; 16 | private readonly PrintWatcher printWatcher; 17 | 18 | public MainWindow() 19 | { 20 | InitializeComponent(); 21 | 22 | Application.Current.Exit += OnApplicationExit; 23 | 24 | keyboardWatcher = eventHookFactory.GetKeyboardWatcher(); 25 | keyboardWatcher.Start(); 26 | keyboardWatcher.OnKeyInput += (s, e) => 27 | { 28 | Console.WriteLine("Key {0} event of key {1}", e.KeyData.EventType, e.KeyData.Keyname); 29 | }; 30 | 31 | mouseWatcher = eventHookFactory.GetMouseWatcher(); 32 | mouseWatcher.Start(); 33 | mouseWatcher.OnMouseInput += (s, e) => 34 | { 35 | Console.WriteLine("Mouse event {0} at point {1},{2}", e.Message.ToString(), e.Point.x, e.Point.y); 36 | }; 37 | 38 | clipboardWatcher = eventHookFactory.GetClipboardWatcher(); 39 | clipboardWatcher.Start(); 40 | clipboardWatcher.OnClipboardModified += (s, e) => 41 | { 42 | Console.WriteLine("Clipboard updated with data '{0}' of format {1}", e.Data, 43 | e.DataFormat.ToString()); 44 | }; 45 | 46 | 47 | applicationWatcher = eventHookFactory.GetApplicationWatcher(); 48 | applicationWatcher.Start(); 49 | applicationWatcher.OnApplicationWindowChange += (s, e) => 50 | { 51 | Console.WriteLine("Application window of '{0}' with the title '{1}' was {2}", 52 | e.ApplicationData.AppName, e.ApplicationData.AppTitle, e.Event); 53 | }; 54 | 55 | printWatcher = eventHookFactory.GetPrintWatcher(); 56 | printWatcher.Start(); 57 | printWatcher.OnPrintEvent += (s, e) => 58 | { 59 | Console.WriteLine("Printer '{0}' currently printing {1} pages.", e.EventData.PrinterName, 60 | e.EventData.Pages); 61 | }; 62 | eventHookFactory.Dispose(); 63 | } 64 | 65 | private void OnApplicationExit(object sender, EventArgs e) 66 | { 67 | keyboardWatcher.Stop(); 68 | mouseWatcher.Stop(); 69 | clipboardWatcher.Stop(); 70 | applicationWatcher.Stop(); 71 | printWatcher.Stop(); 72 | 73 | eventHookFactory.Dispose(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /examples/EventHook.WPF.Example/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Windows; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("EventHook.WPF.Example")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("EventHook.WPF.Example")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | //In order to begin building localizable applications, set 23 | //CultureYouAreCodingWith in your .csproj file 24 | //inside a . For example, if you are using US english 25 | //in your source files, set the to en-US. Then uncomment 26 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 27 | //the line below to match the UICulture setting in the project file. 28 | 29 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 30 | 31 | 32 | [assembly: ThemeInfo( 33 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 34 | //(used if a resource is not found in the page, 35 | // or application resource dictionaries) 36 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 37 | //(used if a resource is not found in the page, 38 | // app, or any theme specific resource dictionaries) 39 | )] 40 | 41 | 42 | // Version information for an assembly consists of the following four values: 43 | // 44 | // Major Version 45 | // Minor Version 46 | // Build Number 47 | // Revision 48 | // 49 | // You can specify all the values or you can default the Build and Revision Numbers 50 | // by using the '*' as shown below: 51 | // [assembly: AssemblyVersion("1.0.*")] 52 | [assembly: AssemblyVersion("1.0.0.0")] 53 | [assembly: AssemblyFileVersion("1.0.0.0")] 54 | -------------------------------------------------------------------------------- /examples/EventHook.WPF.Example/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace EventHook.WPF.Example.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("EventHook.WPF.Example.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/EventHook.WPF.Example/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /examples/EventHook.WPF.Example/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace EventHook.WPF.Example.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/EventHook.WPF.Example/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/EventHook.WinForms.Example/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /examples/EventHook.WinForms.Example/EventHook.WinForms.Example.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {17264D8C-CF5A-4032-9E70-090525D574F2} 8 | WinExe 9 | Properties 10 | EventHook.WinForms.Example 11 | EventHook.WinForms.Example 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Form 51 | 52 | 53 | MainForm.cs 54 | 55 | 56 | 57 | 58 | ResXFileCodeGenerator 59 | Resources.Designer.cs 60 | Designer 61 | 62 | 63 | True 64 | Resources.resx 65 | True 66 | 67 | 68 | SettingsSingleFileGenerator 69 | Settings.Designer.cs 70 | 71 | 72 | True 73 | Settings.settings 74 | True 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | {3847a7f4-3f6b-4626-af4c-8fdfba6165f2} 83 | EventHook 84 | 85 | 86 | 87 | 94 | -------------------------------------------------------------------------------- /examples/EventHook.WinForms.Example/MainForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace EventHook.WinForms.Example 2 | { 3 | partial class MainForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.components = new System.ComponentModel.Container(); 32 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 33 | this.Text = "Form1"; 34 | } 35 | 36 | #endregion 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /examples/EventHook.WinForms.Example/MainForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace EventHook.WinForms.Example 5 | { 6 | public partial class MainForm : Form 7 | { 8 | private readonly ApplicationWatcher applicationWatcher; 9 | private readonly ClipboardWatcher clipboardWatcher; 10 | private readonly EventHookFactory eventHookFactory = new EventHookFactory(); 11 | private readonly KeyboardWatcher keyboardWatcher; 12 | private readonly MouseWatcher mouseWatcher; 13 | private readonly PrintWatcher printWatcher; 14 | 15 | public MainForm() 16 | { 17 | Application.ApplicationExit += OnApplicationExit; 18 | 19 | InitializeComponent(); 20 | 21 | keyboardWatcher = eventHookFactory.GetKeyboardWatcher(); 22 | keyboardWatcher.Start(); 23 | keyboardWatcher.OnKeyInput += (s, e) => 24 | { 25 | Console.WriteLine("Key {0} event of key {1}", e.KeyData.EventType, e.KeyData.Keyname); 26 | }; 27 | 28 | mouseWatcher = eventHookFactory.GetMouseWatcher(); 29 | mouseWatcher.Start(); 30 | mouseWatcher.OnMouseInput += (s, e) => 31 | { 32 | Console.WriteLine("Mouse event {0} at point {1},{2}", e.Message.ToString(), e.Point.x, e.Point.y); 33 | }; 34 | 35 | clipboardWatcher = eventHookFactory.GetClipboardWatcher(); 36 | clipboardWatcher.Start(); 37 | clipboardWatcher.OnClipboardModified += (s, e) => 38 | { 39 | Console.WriteLine("Clipboard updated with data '{0}' of format {1}", e.Data, 40 | e.DataFormat.ToString()); 41 | }; 42 | 43 | 44 | applicationWatcher = eventHookFactory.GetApplicationWatcher(); 45 | applicationWatcher.Start(); 46 | applicationWatcher.OnApplicationWindowChange += (s, e) => 47 | { 48 | Console.WriteLine("Application window of '{0}' with the title '{1}' was {2}", 49 | e.ApplicationData.AppName, e.ApplicationData.AppTitle, e.Event); 50 | }; 51 | 52 | printWatcher = eventHookFactory.GetPrintWatcher(); 53 | printWatcher.Start(); 54 | printWatcher.OnPrintEvent += (s, e) => 55 | { 56 | Console.WriteLine("Printer '{0}' currently printing {1} pages.", e.EventData.PrinterName, 57 | e.EventData.Pages); 58 | }; 59 | } 60 | 61 | private void OnApplicationExit(object sender, EventArgs e) 62 | { 63 | keyboardWatcher.Stop(); 64 | mouseWatcher.Stop(); 65 | clipboardWatcher.Stop(); 66 | applicationWatcher.Stop(); 67 | printWatcher.Stop(); 68 | 69 | eventHookFactory.Dispose(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /examples/EventHook.WinForms.Example/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace EventHook.WinForms.Example 5 | { 6 | internal static class Program 7 | { 8 | /// 9 | /// The main entry point for the application. 10 | /// 11 | [STAThread] 12 | private static void Main() 13 | { 14 | Application.EnableVisualStyles(); 15 | Application.SetCompatibleTextRenderingDefault(false); 16 | Application.Run(new MainForm()); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/EventHook.WinForms.Example/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("EventHook.Forms.Tests")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("EventHook.Forms.Tests")] 12 | [assembly: AssemblyCopyright("Copyright © 2016")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("17264d8c-cf5a-4032-9e70-090525d574f2")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /examples/EventHook.WinForms.Example/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace EventHook.WinForms.Example.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("EventHook.WinForms.Example.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /examples/EventHook.WinForms.Example/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /examples/EventHook.WinForms.Example/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace EventHook.WinForms.Example.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/EventHook.WinForms.Example/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/EventHook.Docs.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30621.155 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHook", "EventHook\EventHook.csproj", "{3847A7F4-3F6B-4626-AF4C-8FDFBA6165F2}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {3847A7F4-3F6B-4626-AF4C-8FDFBA6165F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {3847A7F4-3F6B-4626-AF4C-8FDFBA6165F2}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {3847A7F4-3F6B-4626-AF4C-8FDFBA6165F2}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {3847A7F4-3F6B-4626-AF4C-8FDFBA6165F2}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {A250E1E5-3ABA-4FED-9A0E-6C63EB0261E0} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /src/EventHook.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26730.16 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{C622ACD2-76FB-4B6D-844D-C0C406C73214}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{44D5BA9F-DF7B-4208-BCD0-A909ECAADE1C}" 9 | ProjectSection(SolutionItems) = preProject 10 | ..\.nuget\NuGet.Config = ..\.nuget\NuGet.Config 11 | ..\.nuget\NuGet.exe = ..\.nuget\NuGet.exe 12 | ..\.nuget\NuGet.targets = ..\.nuget\NuGet.targets 13 | EndProjectSection 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHook", "EventHook\EventHook.csproj", "{3847A7F4-3F6B-4626-AF4C-8FDFBA6165F2}" 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentation", "{FC2A6986-DC6C-4ABF-8BA5-FAF29CD15507}" 18 | ProjectSection(SolutionItems) = preProject 19 | ..\LICENSE = ..\LICENSE 20 | ..\README.md = ..\README.md 21 | EndProjectSection 22 | EndProject 23 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHook.ConsoleApp.Example", "..\examples\EventHook.ConsoleApp.Example\EventHook.ConsoleApp.Example.csproj", "{0075830E-89B6-4E6B-9C49-CA377746AB4C}" 24 | EndProject 25 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHook.WinForms.Example", "..\examples\EventHook.WinForms.Example\EventHook.WinForms.Example.csproj", "{17264D8C-CF5A-4032-9E70-090525D574F2}" 26 | EndProject 27 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHook.WPF.Example", "..\examples\EventHook.WPF.Example\EventHook.WPF.Example.csproj", "{41F4FF6F-0E65-439C-9091-BA2C8E25BAE0}" 28 | EndProject 29 | Global 30 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 31 | Debug|Any CPU = Debug|Any CPU 32 | Release|Any CPU = Release|Any CPU 33 | EndGlobalSection 34 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 | {3847A7F4-3F6B-4626-AF4C-8FDFBA6165F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {3847A7F4-3F6B-4626-AF4C-8FDFBA6165F2}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {3847A7F4-3F6B-4626-AF4C-8FDFBA6165F2}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {3847A7F4-3F6B-4626-AF4C-8FDFBA6165F2}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {0075830E-89B6-4E6B-9C49-CA377746AB4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {0075830E-89B6-4E6B-9C49-CA377746AB4C}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {0075830E-89B6-4E6B-9C49-CA377746AB4C}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {0075830E-89B6-4E6B-9C49-CA377746AB4C}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {17264D8C-CF5A-4032-9E70-090525D574F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 44 | {17264D8C-CF5A-4032-9E70-090525D574F2}.Debug|Any CPU.Build.0 = Debug|Any CPU 45 | {17264D8C-CF5A-4032-9E70-090525D574F2}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {17264D8C-CF5A-4032-9E70-090525D574F2}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {41F4FF6F-0E65-439C-9091-BA2C8E25BAE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {41F4FF6F-0E65-439C-9091-BA2C8E25BAE0}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {41F4FF6F-0E65-439C-9091-BA2C8E25BAE0}.Release|Any CPU.ActiveCfg = Release|Any CPU 50 | {41F4FF6F-0E65-439C-9091-BA2C8E25BAE0}.Release|Any CPU.Build.0 = Release|Any CPU 51 | EndGlobalSection 52 | GlobalSection(SolutionProperties) = preSolution 53 | HideSolutionNode = FALSE 54 | EndGlobalSection 55 | GlobalSection(NestedProjects) = preSolution 56 | {0075830E-89B6-4E6B-9C49-CA377746AB4C} = {C622ACD2-76FB-4B6D-844D-C0C406C73214} 57 | {17264D8C-CF5A-4032-9E70-090525D574F2} = {C622ACD2-76FB-4B6D-844D-C0C406C73214} 58 | {41F4FF6F-0E65-439C-9091-BA2C8E25BAE0} = {C622ACD2-76FB-4B6D-844D-C0C406C73214} 59 | EndGlobalSection 60 | GlobalSection(ExtensibilityGlobals) = postSolution 61 | SolutionGuid = {E257FE38-E63D-4F7E-A2BD-C6DD64CF3412} 62 | EndGlobalSection 63 | EndGlobal 64 | -------------------------------------------------------------------------------- /src/EventHook.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | False 3 | True 4 | NEVER 5 | NEVER 6 | LINE_BREAK 7 | LINE_BREAK 8 | False 9 | True 10 | 120 11 | UseExplicitType 12 | UseVar 13 | BC 14 | CN 15 | DN 16 | EKU 17 | KU 18 | MTA 19 | OID 20 | OIDS 21 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> 22 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> 23 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> 24 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> 25 | <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="Property (private)"><ElementKinds><Kind Name="PROPERTY" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> 26 | True 27 | True 28 | True 29 | True 30 | True 31 | True 32 | True 33 | True -------------------------------------------------------------------------------- /src/EventHook/ClipboardWatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using System.Windows.Forms; 5 | using EventHook.Helpers; 6 | using EventHook.Hooks; 7 | 8 | namespace EventHook 9 | { 10 | /// 11 | /// Type of clipboard content. 12 | /// 13 | public enum ClipboardContentTypes 14 | { 15 | PlainText = 0, 16 | RichText = 1, 17 | Html = 2, 18 | Csv = 3, 19 | UnicodeText = 4 20 | } 21 | 22 | /// 23 | /// An argument send to user. 24 | /// 25 | public class ClipboardEventArgs : EventArgs 26 | { 27 | public object Data { get; set; } 28 | public ClipboardContentTypes DataFormat { get; set; } 29 | } 30 | 31 | /// 32 | /// Wraps around clipboardHook. 33 | /// Uses a producer-consumer pattern to improve performance and to avoid operating system forcing unhook on delayed 34 | /// user callbacks. 35 | /// 36 | public class ClipboardWatcher 37 | { 38 | private readonly object accesslock = new object(); 39 | 40 | private readonly SyncFactory factory; 41 | 42 | private ClipBoardHook clip; 43 | private AsyncConcurrentQueue clipQueue; 44 | public bool isRunning; 45 | private CancellationTokenSource taskCancellationTokenSource; 46 | 47 | internal ClipboardWatcher(SyncFactory factory) 48 | { 49 | this.factory = factory; 50 | } 51 | 52 | public event EventHandler OnClipboardModified; 53 | 54 | /// 55 | /// Start watching 56 | /// 57 | public void Start() 58 | { 59 | lock (accesslock) 60 | { 61 | if (!isRunning) 62 | { 63 | taskCancellationTokenSource = new CancellationTokenSource(); 64 | clipQueue = new AsyncConcurrentQueue(taskCancellationTokenSource.Token); 65 | 66 | //This needs to run on UI thread context 67 | //So use task factory with the shared UI message pump thread 68 | Task.Factory.StartNew(() => 69 | { 70 | clip = new ClipBoardHook(); 71 | clip.RegisterClipboardViewer(); 72 | clip.ClipBoardChanged += ClipboardHandler; 73 | }, 74 | CancellationToken.None, 75 | TaskCreationOptions.None, 76 | factory.GetTaskScheduler()).Wait(); 77 | 78 | Task.Factory.StartNew(() => ClipConsumerAsync()); 79 | 80 | isRunning = true; 81 | } 82 | } 83 | } 84 | 85 | /// 86 | /// Stop watching 87 | /// 88 | public void Stop() 89 | { 90 | lock (accesslock) 91 | { 92 | if (isRunning) 93 | { 94 | if (clip != null) 95 | { 96 | //This needs to run on UI thread context 97 | //So use task factory with the shared UI message pump thread 98 | Task.Factory.StartNew(() => 99 | { 100 | clip.ClipBoardChanged -= ClipboardHandler; 101 | clip.UnregisterClipboardViewer(); 102 | clip.Dispose(); 103 | }, 104 | CancellationToken.None, 105 | TaskCreationOptions.None, 106 | factory.GetTaskScheduler()); 107 | } 108 | 109 | isRunning = false; 110 | clipQueue.Enqueue(false); 111 | taskCancellationTokenSource.Cancel(); 112 | } 113 | } 114 | } 115 | 116 | /// 117 | /// Add event to producer queue 118 | /// 119 | /// 120 | /// 121 | private void ClipboardHandler(object sender, EventArgs e) 122 | { 123 | clipQueue.Enqueue(sender); 124 | } 125 | 126 | /// 127 | /// Consume event from producer queue asynchronously 128 | /// 129 | /// 130 | private async Task ClipConsumerAsync() 131 | { 132 | while (isRunning) 133 | { 134 | var item = await clipQueue.DequeueAsync(); 135 | 136 | if (item is null) 137 | { 138 | continue; 139 | } 140 | 141 | if (item is bool) 142 | { 143 | break; 144 | } 145 | 146 | ClipboardHandler(item); 147 | } 148 | } 149 | 150 | /// 151 | /// Actual handler to invoke user call backs 152 | /// 153 | /// 154 | private void ClipboardHandler(object sender) 155 | { 156 | IDataObject iData = (DataObject)sender; 157 | 158 | var format = default(ClipboardContentTypes); 159 | 160 | object data = null; 161 | 162 | bool validDataType = false; 163 | if (iData.GetDataPresent(DataFormats.Text)) 164 | { 165 | format = ClipboardContentTypes.PlainText; 166 | data = iData.GetData(DataFormats.Text); 167 | validDataType = true; 168 | } 169 | else if (iData.GetDataPresent(DataFormats.Rtf)) 170 | { 171 | format = ClipboardContentTypes.RichText; 172 | data = iData.GetData(DataFormats.Rtf); 173 | validDataType = true; 174 | } 175 | else if (iData.GetDataPresent(DataFormats.CommaSeparatedValue)) 176 | { 177 | format = ClipboardContentTypes.Csv; 178 | data = iData.GetData(DataFormats.CommaSeparatedValue); 179 | validDataType = true; 180 | } 181 | else if (iData.GetDataPresent(DataFormats.Html)) 182 | { 183 | format = ClipboardContentTypes.Html; 184 | data = iData.GetData(DataFormats.Html); 185 | validDataType = true; 186 | } 187 | 188 | else if (iData.GetDataPresent(DataFormats.StringFormat)) 189 | { 190 | format = ClipboardContentTypes.PlainText; 191 | data = iData.GetData(DataFormats.StringFormat); 192 | validDataType = true; 193 | } 194 | else if (iData.GetDataPresent(DataFormats.UnicodeText)) 195 | { 196 | format = ClipboardContentTypes.UnicodeText; 197 | data = iData.GetData(DataFormats.UnicodeText); 198 | validDataType = true; 199 | } 200 | 201 | if (!validDataType) 202 | { 203 | return; 204 | } 205 | 206 | OnClipboardModified?.Invoke(null, new ClipboardEventArgs { Data = data, DataFormat = format }); 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/EventHook/EventHook.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {3847A7F4-3F6B-4626-AF4C-8FDFBA6165F2} 8 | Library 9 | Properties 10 | EventHook 11 | EventHook 12 | v4.5 13 | 512 14 | ..\ 15 | true 16 | 17 | ..\..\.nuget 18 | .. 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | TRACE;DEBUG;NET45 26 | prompt 27 | 4 28 | true 29 | v4.5 30 | false 31 | 32 | 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE;NET45 37 | prompt 38 | 4 39 | AnyCPU 40 | v4.5 41 | false 42 | 43 | 44 | true 45 | 46 | 47 | StrongNameKey.snk 48 | 49 | 50 | 51 | True 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | Form 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 108 | 109 | 110 | 111 | 118 | -------------------------------------------------------------------------------- /src/EventHook/EventHook.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | EventHook 5 | 1.0.0 6 | EventHook 7 | 8 | This library is intended to hook global windows user events 9 | https://github.com/titanium007/Windows-User-Action-Hook 10 | https://github.com/titanium007/Windows-User-Action-Hook/blob/master/LICENSE 11 | titanium007 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/EventHook/EventHookFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EventHook.Helpers; 3 | 4 | namespace EventHook 5 | { 6 | /// 7 | /// A factory class core to the management of various watchers 8 | /// that all shares the same synchronization objects. 9 | /// Use this class to get instances of differant watchers. 10 | /// This factory instance should be disposed only after all watchers it have been unsubscribed. 11 | /// 12 | public class EventHookFactory : IDisposable 13 | { 14 | private readonly SyncFactory syncFactory = new SyncFactory(); 15 | 16 | public void Dispose() 17 | { 18 | syncFactory.Dispose(); 19 | } 20 | 21 | /// 22 | /// Get an instance of application watcher. 23 | /// 24 | /// 25 | public ApplicationWatcher GetApplicationWatcher() 26 | { 27 | return new ApplicationWatcher(syncFactory); 28 | } 29 | 30 | /// 31 | /// Get an instance of keystroke watcher. 32 | /// 33 | /// 34 | public KeyboardWatcher GetKeyboardWatcher() 35 | { 36 | return new KeyboardWatcher(syncFactory); 37 | } 38 | 39 | /// 40 | /// Get an instance of mouse watcher. 41 | /// 42 | /// 43 | public MouseWatcher GetMouseWatcher() 44 | { 45 | return new MouseWatcher(syncFactory); 46 | } 47 | 48 | /// 49 | /// Get an instance of clipboard watcher. 50 | /// 51 | /// 52 | public ClipboardWatcher GetClipboardWatcher() 53 | { 54 | return new ClipboardWatcher(syncFactory); 55 | } 56 | 57 | /// 58 | /// Get an instance of print watcher. 59 | /// 60 | /// 61 | public PrintWatcher GetPrintWatcher() 62 | { 63 | return new PrintWatcher(syncFactory); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/EventHook/Helpers/AsyncConcurrentQueue.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace EventHook.Helpers 6 | { 7 | /// 8 | /// A concurrent queue facilitating async dequeue with minimal locking 9 | /// Assumes single/multi-threaded producer and a single-threaded consumer 10 | /// 11 | /// 12 | internal class AsyncConcurrentQueue 13 | { 14 | /// 15 | /// Backing queue 16 | /// 17 | private readonly ConcurrentQueue queue = new ConcurrentQueue(); 18 | 19 | /// 20 | /// Wake up any pending dequeue task 21 | /// 22 | private TaskCompletionSource dequeueTask; 23 | private SemaphoreSlim @dequeueTaskLock = new SemaphoreSlim(1); 24 | private CancellationToken taskCancellationToken; 25 | 26 | internal AsyncConcurrentQueue(CancellationToken taskCancellationToken) 27 | { 28 | this.taskCancellationToken = taskCancellationToken; 29 | } 30 | 31 | /// 32 | /// Supports multi-threaded producers 33 | /// 34 | /// 35 | internal void Enqueue(T value) 36 | { 37 | queue.Enqueue(value); 38 | 39 | //signal 40 | dequeueTaskLock.Wait(); 41 | dequeueTask?.TrySetResult(true); 42 | dequeueTaskLock.Release(); 43 | 44 | } 45 | 46 | /// 47 | /// Assumes a single-threaded consumer! 48 | /// 49 | /// 50 | internal async Task DequeueAsync() 51 | { 52 | T result; 53 | queue.TryDequeue(out result); 54 | 55 | if (result != null) 56 | { 57 | return result; 58 | } 59 | 60 | await dequeueTaskLock.WaitAsync(); 61 | dequeueTask = new TaskCompletionSource(); 62 | dequeueTaskLock.Release(); 63 | 64 | taskCancellationToken.Register(() => dequeueTask.TrySetCanceled()); 65 | await dequeueTask.Task; 66 | 67 | queue.TryDequeue(out result); 68 | return result; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/EventHook/Helpers/SyncFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | using System.Windows.Threading; 7 | 8 | namespace EventHook.Helpers 9 | { 10 | /// 11 | /// A class to create a dummy message pump if we don't have one 12 | /// A message pump is required for most of our hooks to succeed 13 | /// 14 | internal class SyncFactory : IDisposable 15 | { 16 | private readonly Lazy messageHandler; 17 | 18 | private readonly Lazy scheduler; 19 | private bool hasUIThread; 20 | 21 | internal SyncFactory() 22 | { 23 | scheduler = new Lazy(() => 24 | { 25 | //if the calling thread is a UI thread then return its synchronization context 26 | //no need to create a message pump 27 | var dispatcher = Dispatcher.FromThread(Thread.CurrentThread); 28 | if (dispatcher != null) 29 | { 30 | if (SynchronizationContext.Current != null) 31 | { 32 | hasUIThread = true; 33 | return TaskScheduler.FromCurrentSynchronizationContext(); 34 | } 35 | } 36 | 37 | TaskScheduler current = null; 38 | 39 | //if current task scheduler is null, create a message pump 40 | //http://stackoverflow.com/questions/2443867/message-pump-in-net-windows-service 41 | //use async for performance gain! 42 | new Task(() => 43 | { 44 | Dispatcher.CurrentDispatcher.BeginInvoke( 45 | new Action(() => 46 | { 47 | Volatile.Write(ref current, TaskScheduler.FromCurrentSynchronizationContext()); 48 | }), DispatcherPriority.Normal); 49 | Dispatcher.Run(); 50 | }).Start(); 51 | 52 | //we called dispatcher begin invoke to get the Message Pump Sync Context 53 | //we check every 10ms until synchronization context is copied 54 | while (Volatile.Read(ref current) == null) 55 | { 56 | Thread.Sleep(10); 57 | } 58 | 59 | return Volatile.Read(ref current); 60 | }); 61 | 62 | messageHandler = new Lazy(() => 63 | { 64 | MessageHandler msgHandler = null; 65 | //get the mesage handler dummy window created using the UI sync context 66 | new Task(e => { Volatile.Write(ref msgHandler, new MessageHandler()); }, GetTaskScheduler()).Start(); 67 | 68 | //wait here until the window is created on UI thread 69 | while (Volatile.Read(ref msgHandler) == null) 70 | { 71 | Thread.Sleep(10); 72 | } 73 | 74 | ; 75 | 76 | return Volatile.Read(ref msgHandler); 77 | }); 78 | 79 | Initialize(); 80 | } 81 | 82 | public void Dispose() 83 | { 84 | if (messageHandler?.Value != null) 85 | { 86 | messageHandler.Value.DestroyHandle(); 87 | } 88 | } 89 | 90 | /// 91 | /// Initialize the required message pump for all the hooks 92 | /// 93 | private void Initialize() 94 | { 95 | GetTaskScheduler(); 96 | GetHandle(); 97 | } 98 | 99 | /// 100 | /// Get the UI task scheduler 101 | /// 102 | /// 103 | internal TaskScheduler GetTaskScheduler() 104 | { 105 | return scheduler.Value; 106 | } 107 | 108 | /// 109 | /// Get the handle of the window we created on the UI thread 110 | /// 111 | /// 112 | internal IntPtr GetHandle() 113 | { 114 | var handle = IntPtr.Zero; 115 | 116 | if (hasUIThread) 117 | { 118 | try 119 | { 120 | handle = Process.GetCurrentProcess().MainWindowHandle; 121 | 122 | if (handle != IntPtr.Zero) 123 | { 124 | return handle; 125 | } 126 | } 127 | catch 128 | { 129 | } 130 | } 131 | 132 | return messageHandler.Value.Handle; 133 | } 134 | } 135 | 136 | /// 137 | /// A dummy class to create a dummy invisible window object 138 | /// 139 | internal class MessageHandler : NativeWindow 140 | { 141 | internal MessageHandler() 142 | { 143 | CreateHandle(new CreateParams()); 144 | } 145 | 146 | protected override void WndProc(ref Message msg) 147 | { 148 | base.WndProc(ref msg); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/EventHook/Helpers/WindowHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Text; 4 | using EventHook.Hooks.Library; 5 | 6 | namespace EventHook.Helpers 7 | { 8 | /// 9 | /// A helper class to get window names/handles etc 10 | /// 11 | internal class WindowHelper 12 | { 13 | /// 14 | /// Get the handle of current acitive window on screen if any 15 | /// 16 | /// 17 | internal static IntPtr GetActiveWindowHandle() 18 | { 19 | try 20 | { 21 | return (IntPtr)User32.GetForegroundWindow(); 22 | } 23 | catch (Exception) 24 | { 25 | // ignored 26 | } 27 | 28 | return IntPtr.Zero; 29 | } 30 | 31 | /// 32 | /// The the application exe path of this window 33 | /// 34 | /// window handle 35 | /// 36 | internal static string GetAppPath(IntPtr hWnd) 37 | { 38 | if (hWnd == IntPtr.Zero) 39 | { 40 | return null; 41 | } 42 | 43 | try 44 | { 45 | uint pid; 46 | User32.GetWindowThreadProcessId(hWnd, out pid); 47 | var proc = Process.GetProcessById((int)pid); 48 | return proc.MainModule.FileName; 49 | } 50 | catch 51 | { 52 | return null; 53 | } 54 | } 55 | 56 | /// 57 | /// Get the title text of this window 58 | /// 59 | /// widow handle 60 | /// 61 | internal static string GetWindowText(IntPtr hWnd) 62 | { 63 | try 64 | { 65 | int length = User32.GetWindowTextLength(hWnd); 66 | var sb = new StringBuilder(length + 1); 67 | User32.GetWindowText(hWnd, sb, sb.Capacity); 68 | return sb.ToString(); 69 | } 70 | catch (Exception) 71 | { 72 | return null; 73 | } 74 | } 75 | 76 | /// 77 | /// Get the application description file attribute from path of an executable file 78 | /// 79 | /// 80 | /// 81 | internal static string GetAppDescription(string appPath) 82 | { 83 | if (appPath == null) 84 | { 85 | return null; 86 | } 87 | 88 | try 89 | { 90 | return FileVersionInfo.GetVersionInfo(appPath).FileDescription; 91 | } 92 | catch 93 | { 94 | return null; 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/EventHook/Hooks/ClipBoardHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Windows.Forms; 4 | using EventHook.Hooks.Library; 5 | 6 | /// 7 | /// https://github.com/MrksKwsnck/Wlipper 8 | /// 9 | namespace EventHook.Hooks 10 | { 11 | internal class ClipBoardHook : Form 12 | { 13 | private IntPtr _clipboardViewerNext; 14 | 15 | internal event EventHandler ClipBoardChanged = delegate { }; 16 | 17 | /// 18 | /// Register this form as a Clipboard Viewer application 19 | /// 20 | internal void RegisterClipboardViewer() 21 | { 22 | _clipboardViewerNext = User32.SetClipboardViewer(Handle); 23 | } 24 | 25 | /// 26 | /// Remove this form from the Clipboard Viewer list 27 | /// 28 | internal void UnregisterClipboardViewer() 29 | { 30 | User32.ChangeClipboardChain(Handle, _clipboardViewerNext); 31 | } 32 | 33 | 34 | /// 35 | /// Show the clipboard contents in the window 36 | /// and show the notification balloon if a link is found 37 | /// 38 | private void GetClipboardData() 39 | { 40 | // 41 | // Data on the clipboard uses the 42 | // IDataObject interface 43 | // 44 | Exception threadEx = null; 45 | var staThread = new Thread( 46 | delegate() 47 | { 48 | try 49 | { 50 | var iData = Clipboard.GetDataObject(); 51 | ClipBoardChanged(iData, new EventArgs()); 52 | } 53 | 54 | catch (Exception ex) 55 | { 56 | threadEx = ex; 57 | } 58 | }); 59 | 60 | staThread.SetApartmentState(ApartmentState.STA); 61 | staThread.Start(); 62 | staThread.Join(); 63 | } 64 | 65 | 66 | protected override void WndProc(ref Message m) 67 | { 68 | switch ((Msgs)m.Msg) 69 | { 70 | // 71 | // The WM_DRAWCLIPBOARD message is sent to the first window 72 | // in the clipboard viewer chain when the content of the 73 | // clipboard changes. This enables a clipboard viewer 74 | // window to display the new content of the clipboard. 75 | // 76 | case Msgs.WM_DRAWCLIPBOARD: 77 | 78 | 79 | GetClipboardData(); 80 | 81 | // 82 | // Each window that receives the WM_DRAWCLIPBOARD message 83 | // must call the SendMessage function to pass the message 84 | // on to the next window in the clipboard viewer chain. 85 | // 86 | User32.SendMessage(_clipboardViewerNext, m.Msg, m.WParam, m.LParam); 87 | break; 88 | 89 | 90 | // 91 | // The WM_CHANGECBCHAIN message is sent to the first window 92 | // in the clipboard viewer chain when a window is being 93 | // removed from the chain. 94 | // 95 | case Msgs.WM_CHANGECBCHAIN: 96 | 97 | 98 | // When a clipboard viewer window receives the WM_CHANGECBCHAIN message, 99 | // it should call the SendMessage function to pass the message to the 100 | // next window in the chain, unless the next window is the window 101 | // being removed. In this case, the clipboard viewer should save 102 | // the handle specified by the lParam parameter as the next window in the chain. 103 | 104 | // 105 | // wParam is the Handle to the window being removed from 106 | // the clipboard viewer chain 107 | // lParam is the Handle to the next window in the chain 108 | // following the window being removed. 109 | if (m.WParam == _clipboardViewerNext) 110 | { 111 | // 112 | // If wParam is the next clipboard viewer then it 113 | // is being removed so update pointer to the next 114 | // window in the clipboard chain 115 | // 116 | _clipboardViewerNext = m.LParam; 117 | } 118 | else 119 | { 120 | User32.SendMessage(_clipboardViewerNext, m.Msg, m.WParam, m.LParam); 121 | } 122 | 123 | break; 124 | 125 | default: 126 | // 127 | // Let the form process the messages that we are 128 | // not interested in 129 | // 130 | base.WndProc(ref m); 131 | break; 132 | } 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/EventHook/Hooks/Library/Enum.cs: -------------------------------------------------------------------------------- 1 | namespace EventHook.Hooks.Library 2 | { 3 | internal enum ShellEvents 4 | { 5 | HSHELL_WINDOWCREATED = 1, 6 | HSHELL_WINDOWDESTROYED = 2, 7 | HSHELL_ACTIVATESHELLWINDOW = 3, 8 | HSHELL_WINDOWACTIVATED = 4, 9 | HSHELL_GETMINRECT = 5, 10 | HSHELL_REDRAW = 6, 11 | HSHELL_TASKMAN = 7, 12 | HSHELL_LANGUAGE = 8, 13 | HSHELL_SYSMENU = 9, 14 | HSHELL_ENDTASK = 10, 15 | HSHELL_ACCESSIBILITYSTATE = 11, 16 | HSHELL_APPCOMMAND = 12, 17 | HSHELL_WINDOWREPLACED = 13, 18 | HSHELL_WINDOWREPLACING = 14, 19 | HSHELL_HIGHBIT = 0x8000, 20 | HSHELL_FLASH = HSHELL_REDRAW | HSHELL_HIGHBIT, 21 | HSHELL_RUDEAPPACTIVATED = HSHELL_WINDOWACTIVATED | HSHELL_HIGHBIT 22 | } 23 | 24 | internal enum WindowStyle 25 | { 26 | WS_OVERLAPPED = 0x00000000, 27 | WS_POPUP = -2147483648, 28 | WS_CHILD = 0x40000000, 29 | WS_MINIMIZE = 0x20000000, 30 | WS_VISIBLE = 0x10000000, 31 | WS_DISABLED = 0x08000000, 32 | WS_CLIPSIBLINGS = 0x04000000, 33 | WS_CLIPCHILDREN = 0x02000000, 34 | WS_MAXIMIZE = 0x01000000, 35 | WS_CAPTION = 0x00C00000, 36 | WS_BORDER = 0x00800000, 37 | WS_DLGFRAME = 0x00400000, 38 | WS_VSCROLL = 0x00200000, 39 | WS_HSCROLL = 0x00100000, 40 | WS_SYSMENU = 0x00080000, 41 | WS_THICKFRAME = 0x00040000, 42 | WS_GROUP = 0x00020000, 43 | WS_TABSTOP = 0x00010000, 44 | WS_MINIMIZEBOX = 0x00020000, 45 | WS_MAXIMIZEBOX = 0x00010000, 46 | WS_TILED = WS_OVERLAPPED, 47 | WS_ICONIC = WS_MINIMIZE, 48 | WS_SIZEBOX = WS_THICKFRAME, 49 | WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW, 50 | 51 | WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | 52 | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, 53 | WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU, 54 | WS_CHILDWINDOW = WS_CHILD 55 | } 56 | 57 | internal enum WindowStyleEx 58 | { 59 | WS_EX_DLGMODALFRAME = 0x00000001, 60 | WS_EX_NOPARENTNOTIFY = 0x00000004, 61 | WS_EX_TOPMOST = 0x00000008, 62 | WS_EX_ACCEPTFILES = 0x00000010, 63 | WS_EX_TRANSPARENT = 0x00000020, 64 | WS_EX_MDICHILD = 0x00000040, 65 | WS_EX_TOOLWINDOW = 0x00000080, 66 | WS_EX_WINDOWEDGE = 0x00000100, 67 | WS_EX_CLIENTEDGE = 0x00000200, 68 | WS_EX_CONTEXTHELP = 0x00000400, 69 | WS_EX_RIGHT = 0x00001000, 70 | WS_EX_LEFT = 0x00000000, 71 | WS_EX_RTLREADING = 0x00002000, 72 | WS_EX_LTRREADING = 0x00000000, 73 | WS_EX_LEFTSCROLLBAR = 0x00004000, 74 | WS_EX_RIGHTSCROLLBAR = 0x00000000, 75 | WS_EX_CONTROLPARENT = 0x00010000, 76 | WS_EX_STATICEDGE = 0x00020000, 77 | WS_EX_APPWINDOW = 0x00040000, 78 | WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, 79 | WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, 80 | WS_EX_LAYERED = 0x00080000, 81 | WS_EX_NOINHERITLAYOUT = 0x00100000, // Disable inheritence of mirroring by children 82 | WS_EX_LAYOUTRTL = 0x00400000, // Right to left mirroring 83 | WS_EX_COMPOSITED = 0x02000000, 84 | WS_EX_NOACTIVATE = 0x08000000 85 | } 86 | 87 | internal enum GWLIndex 88 | { 89 | GWL_WNDPROC = -4, 90 | GWL_HINSTANCE = -6, 91 | GWL_HWNDPARENT = -8, 92 | GWL_STYLE = -16, 93 | GWL_EXSTYLE = -20, 94 | GWL_USERDATA = -21, 95 | GWL_ID = -12 96 | } 97 | 98 | internal enum GetWindowContstants 99 | { 100 | GW_HWNDFIRST = 0, 101 | GW_HWNDLAST = 1, 102 | GW_HWNDNEXT = 2, 103 | GW_HWNDPREV = 3, 104 | GW_OWNER = 4, 105 | GW_CHILD = 5, 106 | 107 | GW_ENABLEDPOPUP = 6, 108 | GW_MAX = 6 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/EventHook/Hooks/Library/Msgs.cs: -------------------------------------------------------------------------------- 1 | namespace EventHook.Hooks.Library 2 | { 3 | /// 4 | /// Windows Event Messages sent to the WindowProc 5 | /// 6 | internal enum Msgs 7 | { 8 | WM_NULL = 0x0000, 9 | WM_CREATE = 0x0001, 10 | WM_DESTROY = 0x0002, 11 | WM_MOVE = 0x0003, 12 | WM_SIZE = 0x0005, 13 | WM_ACTIVATE = 0x0006, 14 | WM_SETFOCUS = 0x0007, 15 | WM_KILLFOCUS = 0x0008, 16 | WM_ENABLE = 0x000A, 17 | WM_SETREDRAW = 0x000B, 18 | WM_SETTEXT = 0x000C, 19 | WM_GETTEXT = 0x000D, 20 | WM_GETTEXTLENGTH = 0x000E, 21 | WM_PAINT = 0x000F, 22 | WM_CLOSE = 0x0010, 23 | WM_QUERYENDSESSION = 0x0011, 24 | WM_QUIT = 0x0012, 25 | WM_QUERYOPEN = 0x0013, 26 | WM_ERASEBKGND = 0x0014, 27 | WM_SYSCOLORCHANGE = 0x0015, 28 | WM_ENDSESSION = 0x0016, 29 | WM_SHOWWINDOW = 0x0018, 30 | WM_WININICHANGE = 0x001A, 31 | WM_SETTINGCHANGE = 0x001A, 32 | WM_DEVMODECHANGE = 0x001B, 33 | WM_ACTIVATEAPP = 0x001C, 34 | WM_FONTCHANGE = 0x001D, 35 | WM_TIMECHANGE = 0x001E, 36 | WM_CANCELMODE = 0x001F, 37 | WM_SETCURSOR = 0x0020, 38 | WM_MOUSEACTIVATE = 0x0021, 39 | WM_CHILDACTIVATE = 0x0022, 40 | WM_QUEUESYNC = 0x0023, 41 | WM_GETMINMAXINFO = 0x0024, 42 | WM_PAINTICON = 0x0026, 43 | WM_ICONERASEBKGND = 0x0027, 44 | WM_NEXTDLGCTL = 0x0028, 45 | WM_SPOOLERSTATUS = 0x002A, 46 | WM_DRAWITEM = 0x002B, 47 | WM_MEASUREITEM = 0x002C, 48 | WM_DELETEITEM = 0x002D, 49 | WM_VKEYTOITEM = 0x002E, 50 | WM_CHARTOITEM = 0x002F, 51 | WM_SETFONT = 0x0030, 52 | WM_GETFONT = 0x0031, 53 | WM_SETHOTKEY = 0x0032, 54 | WM_GETHOTKEY = 0x0033, 55 | WM_QUERYDRAGICON = 0x0037, 56 | WM_COMPAREITEM = 0x0039, 57 | WM_GETOBJECT = 0x003D, 58 | WM_COMPACTING = 0x0041, 59 | WM_COMMNOTIFY = 0x0044, 60 | WM_WINDOWPOSCHANGING = 0x0046, 61 | WM_WINDOWPOSCHANGED = 0x0047, 62 | WM_POWER = 0x0048, 63 | WM_COPYDATA = 0x004A, 64 | WM_CANCELJOURNAL = 0x004B, 65 | WM_NOTIFY = 0x004E, 66 | WM_INPUTLANGCHANGEREQUEST = 0x0050, 67 | WM_INPUTLANGCHANGE = 0x0051, 68 | WM_TCARD = 0x0052, 69 | WM_HELP = 0x0053, 70 | WM_USERCHANGED = 0x0054, 71 | WM_NOTIFYFORMAT = 0x0055, 72 | WM_CONTEXTMENU = 0x007B, 73 | WM_STYLECHANGING = 0x007C, 74 | WM_STYLECHANGED = 0x007D, 75 | WM_DISPLAYCHANGE = 0x007E, 76 | WM_GETICON = 0x007F, 77 | WM_SETICON = 0x0080, 78 | WM_NCCREATE = 0x0081, 79 | WM_NCDESTROY = 0x0082, 80 | WM_NCCALCSIZE = 0x0083, 81 | WM_NCHITTEST = 0x0084, 82 | WM_NCPAINT = 0x0085, 83 | WM_NCACTIVATE = 0x0086, 84 | WM_GETDLGCODE = 0x0087, 85 | WM_SYNCPAINT = 0x0088, 86 | WM_NCMOUSEMOVE = 0x00A0, 87 | WM_NCLBUTTONDOWN = 0x00A1, 88 | WM_NCLBUTTONUP = 0x00A2, 89 | WM_NCLBUTTONDBLCLK = 0x00A3, 90 | WM_NCRBUTTONDOWN = 0x00A4, 91 | WM_NCRBUTTONUP = 0x00A5, 92 | WM_NCRBUTTONDBLCLK = 0x00A6, 93 | WM_NCMBUTTONDOWN = 0x00A7, 94 | WM_NCMBUTTONUP = 0x00A8, 95 | WM_NCMBUTTONDBLCLK = 0x00A9, 96 | WM_NCXBUTTONDOWN = 0x00AB, 97 | WM_NCXBUTTONUP = 0x00AC, 98 | WM_KEYDOWN = 0x0100, 99 | WM_KEYUP = 0x0101, 100 | WM_CHAR = 0x0102, 101 | WM_DEADCHAR = 0x0103, 102 | WM_SYSKEYDOWN = 0x0104, 103 | WM_SYSKEYUP = 0x0105, 104 | WM_SYSCHAR = 0x0106, 105 | WM_SYSDEADCHAR = 0x0107, 106 | WM_KEYLAST = 0x0108, 107 | WM_IME_STARTCOMPOSITION = 0x010D, 108 | WM_IME_ENDCOMPOSITION = 0x010E, 109 | WM_IME_COMPOSITION = 0x010F, 110 | WM_IME_KEYLAST = 0x010F, 111 | WM_INITDIALOG = 0x0110, 112 | WM_COMMAND = 0x0111, 113 | WM_SYSCOMMAND = 0x0112, 114 | WM_TIMER = 0x0113, 115 | WM_HSCROLL = 0x0114, 116 | WM_VSCROLL = 0x0115, 117 | WM_INITMENU = 0x0116, 118 | WM_INITMENUPOPUP = 0x0117, 119 | WM_MENUSELECT = 0x011F, 120 | WM_MENUCHAR = 0x0120, 121 | WM_ENTERIDLE = 0x0121, 122 | WM_MENURBUTTONUP = 0x0122, 123 | WM_MENUDRAG = 0x0123, 124 | WM_MENUGETOBJECT = 0x0124, 125 | WM_UNINITMENUPOPUP = 0x0125, 126 | WM_MENUCOMMAND = 0x0126, 127 | WM_CTLCOLORMSGBOX = 0x0132, 128 | WM_CTLCOLOREDIT = 0x0133, 129 | WM_CTLCOLORLISTBOX = 0x0134, 130 | WM_CTLCOLORBTN = 0x0135, 131 | WM_CTLCOLORDLG = 0x0136, 132 | WM_CTLCOLORSCROLLBAR = 0x0137, 133 | WM_CTLCOLORSTATIC = 0x0138, 134 | WM_MOUSEMOVE = 0x0200, 135 | WM_LBUTTONDOWN = 0x0201, 136 | WM_LBUTTONUP = 0x0202, 137 | WM_LBUTTONDBLCLK = 0x0203, 138 | WM_RBUTTONDOWN = 0x0204, 139 | WM_RBUTTONUP = 0x0205, 140 | WM_RBUTTONDBLCLK = 0x0206, 141 | WM_MBUTTONDOWN = 0x0207, 142 | WM_MBUTTONUP = 0x0208, 143 | WM_MBUTTONDBLCLK = 0x0209, 144 | WM_MOUSEWHEEL = 0x020A, 145 | WM_XBUTTONDOWN = 0x020B, 146 | WM_XBUTTONUP = 0x020C, 147 | WM_XBUTTONDBLCLK = 0x020D, 148 | WM_PARENTNOTIFY = 0x0210, 149 | WM_ENTERMENULOOP = 0x0211, 150 | WM_EXITMENULOOP = 0x0212, 151 | WM_NEXTMENU = 0x0213, 152 | WM_SIZING = 0x0214, 153 | WM_CAPTURECHANGED = 0x0215, 154 | WM_MOVING = 0x0216, 155 | WM_DEVICECHANGE = 0x0219, 156 | WM_MDICREATE = 0x0220, 157 | WM_MDIDESTROY = 0x0221, 158 | WM_MDIACTIVATE = 0x0222, 159 | WM_MDIRESTORE = 0x0223, 160 | WM_MDINEXT = 0x0224, 161 | WM_MDIMAXIMIZE = 0x0225, 162 | WM_MDITILE = 0x0226, 163 | WM_MDICASCADE = 0x0227, 164 | WM_MDIICONARRANGE = 0x0228, 165 | WM_MDIGETACTIVE = 0x0229, 166 | WM_MDISETMENU = 0x0230, 167 | WM_ENTERSIZEMOVE = 0x0231, 168 | WM_EXITSIZEMOVE = 0x0232, 169 | WM_DROPFILES = 0x0233, 170 | WM_MDIREFRESHMENU = 0x0234, 171 | WM_IME_SETCONTEXT = 0x0281, 172 | WM_IME_NOTIFY = 0x0282, 173 | WM_IME_CONTROL = 0x0283, 174 | WM_IME_COMPOSITIONFULL = 0x0284, 175 | WM_IME_SELECT = 0x0285, 176 | WM_IME_CHAR = 0x0286, 177 | WM_IME_REQUEST = 0x0288, 178 | WM_IME_KEYDOWN = 0x0290, 179 | WM_IME_KEYUP = 0x0291, 180 | WM_MOUSEHOVER = 0x02A1, 181 | WM_MOUSELEAVE = 0x02A3, 182 | WM_CUT = 0x0300, 183 | WM_COPY = 0x0301, 184 | WM_PASTE = 0x0302, 185 | WM_CLEAR = 0x0303, 186 | WM_UNDO = 0x0304, 187 | WM_RENDERFORMAT = 0x0305, 188 | WM_RENDERALLFORMATS = 0x0306, 189 | WM_DESTROYCLIPBOARD = 0x0307, 190 | WM_DRAWCLIPBOARD = 0x0308, 191 | WM_PAINTCLIPBOARD = 0x0309, 192 | WM_VSCROLLCLIPBOARD = 0x030A, 193 | WM_SIZECLIPBOARD = 0x030B, 194 | WM_ASKCBFORMATNAME = 0x030C, 195 | WM_CHANGECBCHAIN = 0x030D, 196 | WM_HSCROLLCLIPBOARD = 0x030E, 197 | WM_QUERYNEWPALETTE = 0x030F, 198 | WM_PALETTEISCHANGING = 0x0310, 199 | WM_PALETTECHANGED = 0x0311, 200 | WM_HOTKEY = 0x0312, 201 | WM_PRINT = 0x0317, 202 | WM_PRINTCLIENT = 0x0318, 203 | WM_HANDHELDFIRST = 0x0358, 204 | WM_HANDHELDLAST = 0x035F, 205 | WM_AFXFIRST = 0x0360, 206 | WM_AFXLAST = 0x037F, 207 | WM_PENWINFIRST = 0x0380, 208 | WM_PENWINLAST = 0x038F, 209 | WM_APP = 0x8000, 210 | WM_USER = 0x0400, 211 | 212 | // For Windows XP Balloon messages from the System Notification Area 213 | NIN_BALLOONSHOW = 0x0402, 214 | NIN_BALLOONHIDE = 0x0403, 215 | NIN_BALLOONTIMEOUT = 0x0404, 216 | NIN_BALLOONUSERCLICK = 0x0405 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/EventHook/Hooks/Library/Procs.cs: -------------------------------------------------------------------------------- 1 | #region License block 2 | 3 | /* 4 | Copyright (c) 2009 Khaprov Ilya (http://dead-trickster.com) 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | */ 26 | 27 | #endregion 28 | 29 | using System; 30 | 31 | namespace EventHook.Hooks.Library 32 | { 33 | internal delegate IntPtr WndProc(IntPtr hWnd, uint message, IntPtr wParam, IntPtr lParam); 34 | 35 | internal delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); 36 | } 37 | -------------------------------------------------------------------------------- /src/EventHook/Hooks/Library/Structs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace EventHook.Hooks.Library 6 | { 7 | #region RECT struct 8 | 9 | [StructLayout(LayoutKind.Sequential)] 10 | internal struct RECT 11 | { 12 | internal int left; 13 | internal int top; 14 | internal int right; 15 | internal int bottom; 16 | 17 | internal RECT(int left, int top, int right, int bottom) 18 | { 19 | this.left = left; 20 | this.top = top; 21 | this.right = right; 22 | this.bottom = bottom; 23 | } 24 | 25 | internal Rectangle Rect => new Rectangle(left, top, right - left, bottom - top); 26 | 27 | internal static RECT FromXywh(int x, int y, int width, int height) 28 | { 29 | return new RECT(x, 30 | y, 31 | x + width, 32 | y + height); 33 | } 34 | 35 | internal static RECT FromRectangle(Rectangle rect) 36 | { 37 | return new RECT(rect.Left, 38 | rect.Top, 39 | rect.Right, 40 | rect.Bottom); 41 | } 42 | 43 | public override string ToString() 44 | { 45 | return "{left=" + left + ", " + "top=" + top + ", " + 46 | "right=" + right + ", " + "bottom=" + bottom + "}"; 47 | } 48 | } 49 | 50 | #endregion //RECT 51 | 52 | #region SHELLHOOKINFO 53 | 54 | internal struct SHELLHOOKINFO 55 | { 56 | internal IntPtr Hwnd; 57 | internal RECT Rc; 58 | } 59 | 60 | #endregion 61 | } 62 | -------------------------------------------------------------------------------- /src/EventHook/Hooks/Library/User32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace EventHook.Hooks.Library 6 | { 7 | internal class User32 8 | { 9 | [DllImport("user32.dll")] 10 | internal static extern int GetForegroundWindow(); 11 | 12 | [DllImport("user32.dll", SetLastError = true)] 13 | internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); 14 | 15 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] 16 | internal static extern int GetWindowTextLength(IntPtr hWnd); 17 | 18 | [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] 19 | internal static extern int GetWindowText(IntPtr hWnd, [Out] StringBuilder lpString, int nMaxCount); 20 | 21 | [DllImport("User32.dll", CharSet = CharSet.Auto)] 22 | internal static extern IntPtr SetClipboardViewer(IntPtr hWnd); 23 | 24 | [DllImport("User32.dll", CharSet = CharSet.Auto)] 25 | internal static extern bool ChangeClipboardChain( 26 | IntPtr hWndRemove, // handle to window to remove 27 | IntPtr hWndNewNext // handle to next window 28 | ); 29 | 30 | [DllImport("user32.dll", CharSet = CharSet.Auto)] 31 | internal static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam); 32 | 33 | [DllImport("User32.dll", CharSet = CharSet.Auto)] 34 | internal static extern bool EnumWindows(EnumWindowsProc numFunc, IntPtr lParam); 35 | 36 | [DllImport("User32.dll", CharSet = CharSet.Auto)] 37 | internal static extern IntPtr GetParent(IntPtr hWnd); 38 | 39 | [DllImport("User32.dll", CharSet = CharSet.Auto)] 40 | internal static extern IntPtr GetWindow(IntPtr hwnd, int uCmd); 41 | 42 | [DllImport("user32.dll", EntryPoint = "GetWindowLong", SetLastError = true, CharSet = CharSet.Auto)] 43 | internal static extern int GetWindowLong(IntPtr hWnd, int nIndex); 44 | 45 | [DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", SetLastError = true, CharSet = CharSet.Auto)] 46 | internal static extern int GetWindowLongPtr(IntPtr hWnd, int nIndex); 47 | 48 | [DllImport("User32.dll", CharSet = CharSet.Auto)] 49 | internal static extern bool IsWindowVisible(IntPtr hwnd); 50 | 51 | [DllImport("User32.dll", CharSet = CharSet.Auto)] 52 | internal static extern bool RegisterShellHook(IntPtr hWnd, int flags); 53 | 54 | /// 55 | /// Registers a specified Shell window to receive certain messages for events or notifications that are useful to 56 | /// Shell applications. The event messages received are only those sent to the Shell window associated with the 57 | /// specified window's desktop. Many of the messages are the same as those that can be received after calling 58 | /// the SetWindowsHookEx function and specifying WH_SHELL for the hook type. The difference with 59 | /// RegisterShellHookWindow is that the messages are received through the specified window's WindowProc 60 | /// and not through a call back procedure. 61 | /// 62 | /// [in] Handle to the window to register for Shell hook messages. 63 | /// TRUE if the function succeeds; FALSE if the function fails. 64 | /// 65 | /// As with normal window messages, the second parameter of the window procedure identifies the 66 | /// message as a "WM_SHELLHOOKMESSAGE". However, for these Shell hook messages, the 67 | /// message value is not a pre-defined constant like other message identifiers (IDs) such as 68 | /// WM_COMMAND. The value must be obtained dynamically using a call to 69 | /// RegisterWindowMessage(TEXT("SHELLHOOK"));. This precludes handling these messages using 70 | /// a traditional switch statement which requires ID values that are known at compile time. 71 | /// For handling Shell hook messages, the normal practice is to code an If statement in the default 72 | /// section of your switch statement and then handle the message if the value of the message ID 73 | /// is the same as the value obtained from the RegisterWindowMessage call. 74 | /// for more see MSDN 75 | /// 76 | [DllImport("User32.dll", CharSet = CharSet.Auto)] 77 | internal static extern bool RegisterShellHookWindow(IntPtr hWnd); 78 | 79 | [DllImport("User32.dll", CharSet = CharSet.Auto)] 80 | internal static extern uint RegisterWindowMessage(string Message); 81 | 82 | [DllImport("user32.dll")] 83 | internal static extern void SetTaskmanWindow(IntPtr hwnd); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/EventHook/Hooks/MouseHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Runtime.InteropServices; 4 | 5 | /// 6 | /// http://stackoverflow.com/questions/11607133/global-mouse-event-handler 7 | /// 8 | namespace EventHook.Hooks 9 | { 10 | internal class RawMouseEventArgs : EventArgs 11 | { 12 | internal MouseMessages Message { get; set; } 13 | internal Point Point { get; set; } 14 | internal uint MouseData { get; set; } 15 | } 16 | 17 | /// 18 | /// The mouse messages. 19 | /// 20 | public enum MouseMessages 21 | { 22 | WM_LBUTTONDOWN = 0x0201, 23 | WM_LBUTTONUP = 0x0202, 24 | WM_MOUSEMOVE = 0x0200, 25 | WM_MOUSEWHEEL = 0x020A, 26 | WM_RBUTTONDOWN = 0x0204, 27 | WM_RBUTTONUP = 0x0205, 28 | WM_WHEELBUTTONDOWN = 0x207, 29 | WM_WHEELBUTTONUP = 0x208, 30 | WM_XBUTTONDOWN = 0x020B, 31 | WM_XBUTTONUP = 0x020C 32 | } 33 | 34 | /// 35 | /// The point co-ordinate. 36 | /// 37 | [StructLayout(LayoutKind.Sequential)] 38 | public struct Point 39 | { 40 | public readonly int x; 41 | public readonly int y; 42 | } 43 | 44 | [StructLayout(LayoutKind.Sequential)] 45 | internal struct MSLLHOOKSTRUCT 46 | { 47 | internal Point pt; 48 | internal readonly uint mouseData; 49 | internal readonly uint flags; 50 | internal readonly uint time; 51 | internal readonly IntPtr dwExtraInfo; 52 | } 53 | 54 | internal class MouseHook 55 | { 56 | private const int WH_MOUSE_LL = 14; 57 | private static IntPtr _hookId = IntPtr.Zero; 58 | 59 | private readonly LowLevelMouseProc Proc; 60 | 61 | internal MouseHook() 62 | { 63 | Proc = HookCallback; 64 | } 65 | 66 | internal event EventHandler MouseAction = delegate { }; 67 | 68 | internal void Start() 69 | { 70 | _hookId = SetHook(Proc); 71 | } 72 | 73 | internal void Stop() 74 | { 75 | UnhookWindowsHookEx(_hookId); 76 | } 77 | 78 | private static IntPtr SetHook(LowLevelMouseProc proc) 79 | { 80 | var hook = SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle("user32"), 0); 81 | if (hook == IntPtr.Zero) 82 | { 83 | throw new Win32Exception(); 84 | } 85 | 86 | return hook; 87 | } 88 | 89 | private IntPtr HookCallback( 90 | int nCode, IntPtr wParam, IntPtr lParam) 91 | { 92 | MSLLHOOKSTRUCT hookStruct; 93 | if (nCode < 0) 94 | { 95 | return CallNextHookEx(_hookId, nCode, wParam, lParam); 96 | } 97 | 98 | hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT)); 99 | 100 | MouseAction(null, new RawMouseEventArgs { Message = (MouseMessages)wParam, Point = hookStruct.pt, MouseData = hookStruct.mouseData }); 101 | 102 | return CallNextHookEx(_hookId, nCode, wParam, lParam); 103 | } 104 | 105 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 106 | private static extern IntPtr SetWindowsHookEx(int idHook, 107 | LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId); 108 | 109 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 110 | [return: MarshalAs(UnmanagedType.Bool)] 111 | private static extern bool UnhookWindowsHookEx(IntPtr hhk); 112 | 113 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 114 | private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, 115 | IntPtr wParam, IntPtr lParam); 116 | 117 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 118 | private static extern IntPtr GetModuleHandle(string lpModuleName); 119 | 120 | private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/EventHook/Hooks/ShellHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using EventHook.Hooks.Library; 4 | 5 | namespace EventHook.Hooks 6 | { 7 | /// 8 | /// //https://github.com/lemkepf/ClipHub/blob/master/ClipHub/ClipHub/Code/Helpers/ShellHook.cs 9 | /// 10 | internal delegate void GeneralShellHookEventHandler(ShellHook sender, IntPtr hWnd); 11 | 12 | internal sealed class ShellHook : NativeWindow 13 | { 14 | private readonly uint _wmShellHook; 15 | 16 | internal ShellHook(IntPtr hWnd) 17 | { 18 | var cp = new CreateParams(); 19 | 20 | // Create the actual window 21 | CreateHandle(cp); 22 | 23 | User32.SetTaskmanWindow(hWnd); 24 | 25 | if (User32.RegisterShellHookWindow(Handle)) 26 | { 27 | _wmShellHook = User32.RegisterWindowMessage("SHELLHOOK"); 28 | } 29 | } 30 | 31 | internal void DeRegister() 32 | { 33 | User32.RegisterShellHook(Handle, 0); 34 | } 35 | 36 | #region Shell events 37 | 38 | /// 39 | /// A top-level, unowned window has been created. The window exists when the system calls this hook. 40 | /// 41 | internal event GeneralShellHookEventHandler WindowCreated; 42 | 43 | /// 44 | /// A top-level, unowned window is about to be destroyed. The window still exists when the system calls this hook. 45 | /// 46 | internal event GeneralShellHookEventHandler WindowDestroyed; 47 | 48 | /// 49 | /// The activation has changed to a different top-level, unowned window. 50 | /// 51 | internal event GeneralShellHookEventHandler WindowActivated; 52 | 53 | 54 | protected override void WndProc(ref Message m) 55 | { 56 | if (m.Msg == _wmShellHook) 57 | { 58 | switch ((ShellEvents)m.WParam) 59 | { 60 | case ShellEvents.HSHELL_WINDOWCREATED: 61 | if (IsAppWindow(m.LParam)) 62 | { 63 | OnWindowCreated(m.LParam); 64 | } 65 | 66 | break; 67 | case ShellEvents.HSHELL_WINDOWDESTROYED: 68 | WindowDestroyed?.Invoke(this, m.LParam); 69 | break; 70 | 71 | case ShellEvents.HSHELL_WINDOWACTIVATED: 72 | WindowActivated?.Invoke(this, m.LParam); 73 | break; 74 | } 75 | } 76 | 77 | base.WndProc(ref m); 78 | } 79 | 80 | #endregion 81 | 82 | #region Windows enumeration 83 | 84 | internal void EnumWindows() 85 | { 86 | User32.EnumWindows(EnumWindowsProc, IntPtr.Zero); 87 | } 88 | 89 | private bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam) 90 | { 91 | if (IsAppWindow(hWnd)) 92 | { 93 | OnWindowCreated(hWnd); 94 | } 95 | 96 | return true; 97 | } 98 | 99 | private void OnWindowCreated(IntPtr hWnd) 100 | { 101 | if (WindowCreated != null) 102 | { 103 | WindowCreated(this, hWnd); 104 | } 105 | } 106 | 107 | private static bool IsAppWindow(IntPtr hWnd) 108 | { 109 | if ((GetWindowLong(hWnd, (int)GWLIndex.GWL_STYLE) & (int)WindowStyle.WS_SYSMENU) == 0) 110 | { 111 | return false; 112 | } 113 | 114 | if (User32.IsWindowVisible(hWnd)) 115 | { 116 | if ((GetWindowLong(hWnd, (int)GWLIndex.GWL_EXSTYLE) & (int)WindowStyleEx.WS_EX_TOOLWINDOW) != 0) 117 | { 118 | return false; 119 | } 120 | 121 | var hwndOwner = User32.GetWindow(hWnd, (int)GetWindowContstants.GW_OWNER); 122 | return (GetWindowLong(hwndOwner, (int)GWLIndex.GWL_STYLE) & 123 | ((int)WindowStyle.WS_VISIBLE | (int)WindowStyle.WS_CLIPCHILDREN)) != 124 | ((int)WindowStyle.WS_VISIBLE | (int)WindowStyle.WS_CLIPCHILDREN) || 125 | (GetWindowLong(hwndOwner, (int)GWLIndex.GWL_EXSTYLE) & (int)WindowStyleEx.WS_EX_TOOLWINDOW) != 126 | 0; 127 | } 128 | 129 | return false; 130 | } 131 | 132 | private static int GetWindowLong(IntPtr hWnd, int nIndex) 133 | { 134 | if (IntPtr.Size == 4) 135 | { 136 | return User32.GetWindowLong(hWnd, nIndex); 137 | } 138 | 139 | return User32.GetWindowLongPtr(hWnd, nIndex); 140 | } 141 | 142 | #endregion 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/EventHook/Hooks/WindowHook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EventHook.Helpers; 3 | 4 | namespace EventHook.Hooks 5 | { 6 | ///One window event to many application wide listeners 7 | internal class WindowHook 8 | { 9 | private static ShellHook sh; 10 | 11 | internal WindowHook(SyncFactory factory) 12 | { 13 | if (sh == null) 14 | { 15 | sh = new ShellHook(factory.GetHandle()); 16 | 17 | sh.WindowCreated += WindowCreatedEvent; 18 | sh.WindowDestroyed += WindowDestroyedEvent; 19 | sh.WindowActivated += WindowActivatedEvent; 20 | } 21 | } 22 | 23 | /// 24 | /// A top-level, unowned window has been created. The window exists when the system calls this hook. 25 | /// 26 | internal event GeneralShellHookEventHandler WindowCreated; 27 | 28 | /// 29 | /// A top-level, unowned window is about to be destroyed. The window still exists when the system calls this hook. 30 | /// 31 | internal event GeneralShellHookEventHandler WindowDestroyed; 32 | 33 | /// 34 | /// The activation has changed to a different top-level, unowned window. 35 | /// 36 | internal event GeneralShellHookEventHandler WindowActivated; 37 | 38 | private void WindowCreatedEvent(ShellHook ShellObject, IntPtr hWnd) 39 | { 40 | WindowCreated?.Invoke(ShellObject, hWnd); 41 | } 42 | 43 | private void WindowDestroyedEvent(ShellHook ShellObject, IntPtr hWnd) 44 | { 45 | WindowDestroyed?.Invoke(ShellObject, hWnd); 46 | } 47 | 48 | private void WindowActivatedEvent(ShellHook ShellObject, IntPtr hWnd) 49 | { 50 | WindowActivated?.Invoke(ShellObject, hWnd); 51 | } 52 | 53 | internal void Destroy() 54 | { 55 | sh = null; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/EventHook/Hooks/WindowHookEx.cs: -------------------------------------------------------------------------------- 1 | namespace EventHook.Hooks 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel; 6 | using System.Diagnostics; 7 | using System.Runtime.InteropServices; 8 | 9 | /// 10 | /// Track events across all windows 11 | /// 12 | public sealed class WindowHookEx: IDisposable 13 | { 14 | readonly WinEventProc proc; 15 | readonly SortedDictionary hooks = new SortedDictionary(); 16 | 17 | /// 18 | /// Must be called from UI thread 19 | /// 20 | public WindowHookEx() { 21 | this.proc = this.Hook; 22 | } 23 | 24 | EventHandler activated; 25 | /// 26 | /// Occurs when a window is about to be activated 27 | /// 28 | public event EventHandler Activated { 29 | add => this.EventAdd(ref this.activated, value, WindowEvent.ForegroundChanged); 30 | remove => this.EventRemove(ref this.activated, value, WindowEvent.ForegroundChanged); 31 | } 32 | 33 | EventHandler minimized; 34 | /// 35 | /// Occurs when a window is about to be minimized 36 | /// 37 | public event EventHandler Minimized { 38 | add => this.EventAdd(ref this.minimized, value, WindowEvent.Minimized); 39 | remove => this.EventRemove(ref this.minimized, value, WindowEvent.Minimized); 40 | } 41 | 42 | EventHandler unminimized; 43 | /// 44 | /// Occurs when a window is about to be restored from minimized state 45 | /// 46 | public event EventHandler Unminimized { 47 | add => this.EventAdd(ref this.unminimized, value, WindowEvent.Unmiminized); 48 | remove => this.EventRemove(ref this.unminimized, value, WindowEvent.Unmiminized); 49 | } 50 | 51 | EventHandler textChanged; 52 | /// 53 | /// Occurs when window's text is changed 54 | /// 55 | public event EventHandler TextChanged { 56 | add => this.EventAdd(ref this.textChanged, value, WindowEvent.NameChanged); 57 | remove => this.EventRemove(ref this.textChanged, value, WindowEvent.NameChanged); 58 | } 59 | 60 | void EventAdd(ref EventHandler handler, EventHandler user, 61 | WindowEvent @event) { 62 | lock (this.hooks) { 63 | handler += user; 64 | this.EnsureHook(@event); 65 | } 66 | } 67 | 68 | void EventRemove(ref EventHandler handler, EventHandler user, 69 | WindowEvent @event) { 70 | lock (this.hooks) { 71 | if (handler != null && handler - user == null) { 72 | if (!UnhookWinEvent(this.hooks[@event])) 73 | throw new Win32Exception(); 74 | bool existed = this.hooks.Remove(@event); 75 | Debug.Assert(existed); 76 | } 77 | 78 | handler -= user; 79 | } 80 | } 81 | 82 | void Hook(IntPtr hookHandle, WindowEvent @event, 83 | IntPtr hwnd, 84 | int @object, int child, 85 | int threadID, int timestampMs) { 86 | EventHandler handler; 87 | switch (@event) { 88 | case WindowEvent.ForegroundChanged: handler = this.activated; break; 89 | case WindowEvent.NameChanged: handler = this.textChanged; break; 90 | case WindowEvent.Minimized: handler = this.minimized; break; 91 | case WindowEvent.Unmiminized: handler = this.unminimized; break; 92 | default: Debug.Write($"Unexpected event {@event}"); return; 93 | } 94 | handler?.Invoke(this, new WindowEventArgs(hwnd)); 95 | } 96 | 97 | void EnsureHook(WindowEvent @event) { 98 | if (!this.hooks.TryGetValue(@event, out var hookID)) { 99 | hookID = this.SetHook(@event); 100 | this.hooks.Add(@event, hookID); 101 | } 102 | } 103 | 104 | IntPtr SetHook(WindowEvent @event) { 105 | IntPtr hookID = SetWinEventHook( 106 | hookMin: @event, hookMax: @event, 107 | moduleHandle: IntPtr.Zero, callback: this.proc, 108 | processID: 0, threadID: 0, 109 | flags: HookFlags.OutOfContext); 110 | if (hookID == IntPtr.Zero) 111 | throw new Win32Exception(); 112 | return hookID; 113 | } 114 | 115 | // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local 116 | void ReleaseUnmanagedResources(bool disposing) { 117 | lock (this.hooks) { 118 | Debug.Assert(!Environment.HasShutdownStarted && (this.hooks.Count == 0 || disposing), "Somebody forgot to dispose the hook. " 119 | + "That will cause heap corruption, because the hook handler " 120 | + "will be disposed before hooking is disabled by the code below."); 121 | foreach (IntPtr hook in this.hooks.Values) { 122 | if (!UnhookWinEvent(hook)) { 123 | var error = new Win32Exception(); 124 | // handle is invalid 125 | if (error.NativeErrorCode != 0x6) 126 | throw error; 127 | else 128 | Debug.WriteLine("Can't dispose hook: the handle is invalid"); 129 | } 130 | } 131 | 132 | this.hooks.Clear(); 133 | } 134 | 135 | GC.KeepAlive(this.proc); 136 | } 137 | 138 | /// 139 | public void Dispose() { 140 | this.ReleaseUnmanagedResources(disposing: true); 141 | this.activated = this.minimized = this.unminimized = this.textChanged = null; 142 | GC.SuppressFinalize(this); 143 | } 144 | 145 | ~WindowHookEx() { 146 | this.ReleaseUnmanagedResources(disposing: false); 147 | } 148 | 149 | #region Native API 150 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 151 | static extern IntPtr SetWinEventHook(WindowEvent hookMin, WindowEvent hookMax, 152 | IntPtr moduleHandle, 153 | WinEventProc callback, int processID, int threadID, HookFlags flags); 154 | 155 | [Flags] 156 | enum HookFlags: int 157 | { 158 | OutOfContext = 0, 159 | } 160 | 161 | enum WindowEvent 162 | { 163 | ForegroundChanged = 0x03, 164 | NameChanged = 0x800C, 165 | Minimized = 0x0016, 166 | Unmiminized = 0x0017, 167 | } 168 | 169 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 170 | [return: MarshalAs(UnmanagedType.Bool)] 171 | static extern bool UnhookWinEvent(IntPtr hhk); 172 | 173 | delegate void WinEventProc(IntPtr hookHandle, WindowEvent @event, 174 | IntPtr hwnd, 175 | int @object, int child, 176 | int threadID, int timestampMs); 177 | #endregion 178 | } 179 | 180 | /// 181 | /// The window event arguments. 182 | /// 183 | public class WindowEventArgs 184 | { 185 | public WindowEventArgs(IntPtr handle) { 186 | this.Handle = handle; 187 | } 188 | public IntPtr Handle { get; } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/EventHook/KeyboardWatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using EventHook.Helpers; 5 | using EventHook.Hooks; 6 | 7 | namespace EventHook 8 | { 9 | /// 10 | /// Key press data. 11 | /// 12 | public class KeyInputEventArgs : EventArgs 13 | { 14 | public KeyData KeyData { get; set; } 15 | } 16 | 17 | /// 18 | /// Key data. 19 | /// 20 | public class KeyData 21 | { 22 | public KeyEvent EventType; 23 | public string Keyname; 24 | public string UnicodeCharacter; 25 | } 26 | 27 | /// 28 | /// Key press event type. 29 | /// 30 | public enum KeyEvent 31 | { 32 | down = 0, 33 | up = 1 34 | } 35 | 36 | /// 37 | /// Wraps low level keyboard hook. 38 | /// Uses a producer-consumer pattern to improve performance and to avoid operating system forcing unhook on delayed 39 | /// user callbacks. 40 | /// 41 | public class KeyboardWatcher 42 | { 43 | private readonly object accesslock = new object(); 44 | 45 | private readonly SyncFactory factory; 46 | 47 | private KeyboardHook keyboardHook; 48 | private AsyncConcurrentQueue keyQueue; 49 | private CancellationTokenSource taskCancellationTokenSource; 50 | 51 | internal KeyboardWatcher(SyncFactory factory) 52 | { 53 | this.factory = factory; 54 | } 55 | 56 | private bool isRunning { get; set; } 57 | public event EventHandler OnKeyInput; 58 | 59 | /// 60 | /// Start watching 61 | /// 62 | public void Start() 63 | { 64 | lock (accesslock) 65 | { 66 | if (!isRunning) 67 | { 68 | taskCancellationTokenSource = new CancellationTokenSource(); 69 | keyQueue = new AsyncConcurrentQueue(taskCancellationTokenSource.Token); 70 | 71 | //This needs to run on UI thread context 72 | //So use task factory with the shared UI message pump thread 73 | Task.Factory.StartNew(() => 74 | { 75 | keyboardHook = new KeyboardHook(); 76 | keyboardHook.KeyDown += KListener; 77 | keyboardHook.KeyUp += KListener; 78 | keyboardHook.Start(); 79 | }, 80 | CancellationToken.None, 81 | TaskCreationOptions.None, 82 | factory.GetTaskScheduler()).Wait(); 83 | 84 | Task.Factory.StartNew(() => ConsumeKeyAsync()); 85 | 86 | isRunning = true; 87 | } 88 | } 89 | } 90 | 91 | /// 92 | /// Stop watching 93 | /// 94 | public void Stop() 95 | { 96 | lock (accesslock) 97 | { 98 | if (isRunning) 99 | { 100 | if (keyboardHook != null) 101 | { 102 | //This needs to run on UI thread context 103 | //So use task factory with the shared UI message pump thread 104 | Task.Factory.StartNew(() => 105 | { 106 | keyboardHook.KeyDown -= KListener; 107 | keyboardHook.Stop(); 108 | keyboardHook = null; 109 | }, 110 | CancellationToken.None, 111 | TaskCreationOptions.None, 112 | factory.GetTaskScheduler()); 113 | } 114 | 115 | keyQueue.Enqueue(false); 116 | isRunning = false; 117 | taskCancellationTokenSource.Cancel(); 118 | } 119 | } 120 | } 121 | 122 | /// 123 | /// Add key event to the producer queue 124 | /// 125 | /// 126 | /// 127 | private void KListener(object sender, RawKeyEventArgs e) 128 | { 129 | keyQueue.Enqueue(new KeyData 130 | { 131 | UnicodeCharacter = e.Character, 132 | Keyname = e.Key.ToString(), 133 | EventType = (KeyEvent)e.EventType 134 | }); 135 | } 136 | 137 | /// 138 | /// Consume events from the producer queue asynchronously 139 | /// 140 | /// 141 | private async Task ConsumeKeyAsync() 142 | { 143 | while (isRunning) 144 | { 145 | //blocking here until a key is added to the queue 146 | var item = await keyQueue.DequeueAsync(); 147 | 148 | if (item is null) 149 | { 150 | continue; 151 | } 152 | 153 | if (item is bool) 154 | { 155 | break; 156 | } 157 | 158 | KListener_KeyDown((KeyData)item); 159 | } 160 | } 161 | 162 | /// 163 | /// Invoke user call backs 164 | /// 165 | /// 166 | private void KListener_KeyDown(KeyData kd) 167 | { 168 | OnKeyInput?.Invoke(null, new KeyInputEventArgs { KeyData = kd }); 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/EventHook/MouseWatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using EventHook.Helpers; 5 | using EventHook.Hooks; 6 | 7 | namespace EventHook 8 | { 9 | /// 10 | /// Event argument to pass data through user callbacks. 11 | /// 12 | public class MouseEventArgs : EventArgs 13 | { 14 | public MouseMessages Message { get; set; } 15 | public Point Point { get; set; } 16 | public uint MouseData { get; set; } 17 | } 18 | 19 | /// 20 | /// Wraps low level mouse hook. 21 | /// Uses a producer-consumer pattern to improve performance and to avoid operating system forcing unhook on delayed 22 | /// user callbacks. 23 | /// 24 | public class MouseWatcher 25 | { 26 | private readonly object accesslock = new object(); 27 | 28 | private readonly SyncFactory factory; 29 | 30 | private MouseHook mouseHook; 31 | private AsyncConcurrentQueue mouseQueue; 32 | private CancellationTokenSource taskCancellationTokenSource; 33 | 34 | internal MouseWatcher(SyncFactory factory) 35 | { 36 | this.factory = factory; 37 | } 38 | 39 | private bool isRunning { get; set; } 40 | public event EventHandler OnMouseInput; 41 | 42 | /// 43 | /// Start watching mouse events 44 | /// 45 | public void Start() 46 | { 47 | lock (accesslock) 48 | { 49 | if (!isRunning) 50 | { 51 | taskCancellationTokenSource = new CancellationTokenSource(); 52 | mouseQueue = new AsyncConcurrentQueue(taskCancellationTokenSource.Token); 53 | //This needs to run on UI thread context 54 | //So use task factory with the shared UI message pump thread 55 | Task.Factory.StartNew(() => 56 | { 57 | mouseHook = new MouseHook(); 58 | mouseHook.MouseAction += MListener; 59 | mouseHook.Start(); 60 | }, 61 | CancellationToken.None, 62 | TaskCreationOptions.None, 63 | factory.GetTaskScheduler()).Wait(); 64 | 65 | Task.Factory.StartNew(() => ConsumeKeyAsync()); 66 | 67 | isRunning = true; 68 | } 69 | } 70 | } 71 | 72 | /// 73 | /// Stop watching mouse events 74 | /// 75 | public void Stop() 76 | { 77 | lock (accesslock) 78 | { 79 | if (isRunning) 80 | { 81 | if (mouseHook != null) 82 | { 83 | //This needs to run on UI thread context 84 | //So use task factory with the shared UI message pump thread 85 | Task.Factory.StartNew(() => 86 | { 87 | mouseHook.MouseAction -= MListener; 88 | mouseHook.Stop(); 89 | mouseHook = null; 90 | }, 91 | CancellationToken.None, 92 | TaskCreationOptions.None, 93 | factory.GetTaskScheduler()); 94 | } 95 | 96 | mouseQueue.Enqueue(false); 97 | isRunning = false; 98 | taskCancellationTokenSource.Cancel(); 99 | } 100 | } 101 | } 102 | 103 | /// 104 | /// Add mouse event to our producer queue 105 | /// 106 | /// 107 | /// 108 | private void MListener(object sender, RawMouseEventArgs e) 109 | { 110 | mouseQueue.Enqueue(e); 111 | } 112 | 113 | /// 114 | /// Consume mouse events in our producer queue asynchronously 115 | /// 116 | /// 117 | private async Task ConsumeKeyAsync() 118 | { 119 | while (isRunning) 120 | { 121 | //blocking here until a key is added to the queue 122 | var item = await mouseQueue.DequeueAsync(); 123 | 124 | if (item is null) 125 | { 126 | continue; 127 | } 128 | 129 | if (item is bool) 130 | { 131 | break; 132 | } 133 | 134 | KListener_KeyDown(item as RawMouseEventArgs); 135 | } 136 | } 137 | 138 | /// 139 | /// Invoke user callbacks with the argument 140 | /// 141 | /// 142 | private void KListener_KeyDown(RawMouseEventArgs kd) 143 | { 144 | OnMouseInput?.Invoke(null, new MouseEventArgs { Message = kd.Message, Point = kd.Point, MouseData = kd.MouseData }); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/EventHook/PrintWatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Printing; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using EventHook.Helpers; 7 | using EventHook.Hooks; 8 | using EventHook.Hooks.Library; 9 | 10 | namespace EventHook 11 | { 12 | /// 13 | /// An object holding key information on a particular print event. 14 | /// 15 | public class PrintEventData 16 | { 17 | public DateTime EventDateTime { get; set; } 18 | public string PrinterName { get; set; } 19 | public string JobName { get; set; } 20 | public int? Pages { get; set; } 21 | public int? JobSize { get; set; } 22 | } 23 | 24 | /// 25 | /// An argument passed along user call backs. 26 | /// 27 | public class PrintEventArgs : EventArgs 28 | { 29 | public PrintEventData EventData { get; set; } 30 | } 31 | 32 | /// 33 | /// A class that wraps around printServer object. 34 | /// 35 | public class PrintWatcher 36 | { 37 | private readonly object accesslock = new object(); 38 | 39 | private readonly SyncFactory factory; 40 | 41 | private ArrayList printers; 42 | private PrintServer printServer; 43 | 44 | internal PrintWatcher(SyncFactory factory) 45 | { 46 | this.factory = factory; 47 | } 48 | 49 | private bool isRunning { get; set; } 50 | public event EventHandler OnPrintEvent; 51 | 52 | /// 53 | /// Start watching print events 54 | /// 55 | public void Start() 56 | { 57 | lock (accesslock) 58 | { 59 | if (!isRunning) 60 | { 61 | Task.Factory.StartNew(() => 62 | { 63 | isRunning = true; 64 | printers = new ArrayList(); 65 | printServer = new PrintServer(); 66 | foreach (var pq in printServer.GetPrintQueues()) 67 | { 68 | var pqm = new PrintQueueHook(pq.Name); 69 | pqm.OnJobStatusChange += pqm_OnJobStatusChange; 70 | pqm.Start(); 71 | printers.Add(pqm); 72 | } 73 | }, 74 | CancellationToken.None, 75 | TaskCreationOptions.None, 76 | factory.GetTaskScheduler()).Wait(); 77 | } 78 | } 79 | } 80 | 81 | /// 82 | /// Stop watching print events 83 | /// 84 | public void Stop() 85 | { 86 | lock (accesslock) 87 | { 88 | if (isRunning) 89 | { 90 | Task.Factory.StartNew(() => 91 | { 92 | if (printers != null) 93 | { 94 | foreach (PrintQueueHook pqm in printers) 95 | { 96 | pqm.OnJobStatusChange -= pqm_OnJobStatusChange; 97 | 98 | try 99 | { 100 | pqm.Stop(); 101 | } 102 | catch 103 | { 104 | //ignored intentionally 105 | //Not sure why but it throws error 106 | //not a bug deal since we a stopping it anyway 107 | } 108 | } 109 | 110 | printers.Clear(); 111 | } 112 | 113 | printers = null; 114 | isRunning = false; 115 | }, 116 | CancellationToken.None, 117 | TaskCreationOptions.None, 118 | factory.GetTaskScheduler()); 119 | } 120 | } 121 | } 122 | 123 | /// 124 | /// Invoke user callback as soon as a relevent event is fired 125 | /// 126 | /// 127 | /// 128 | private void pqm_OnJobStatusChange(object sender, PrintJobChangeEventArgs e) 129 | { 130 | if ((e.JobStatus & JOBSTATUS.JOB_STATUS_SPOOLING) == JOBSTATUS.JOB_STATUS_SPOOLING 131 | && e.JobInfo != null) 132 | { 133 | var hWnd = WindowHelper.GetActiveWindowHandle(); 134 | string appTitle = WindowHelper.GetWindowText(hWnd); 135 | string appName = WindowHelper.GetAppDescription(WindowHelper.GetAppPath(hWnd)); 136 | 137 | var printEvent = new PrintEventData 138 | { 139 | JobName = e.JobInfo.JobName, 140 | JobSize = e.JobInfo.JobSize, 141 | EventDateTime = DateTime.Now, 142 | Pages = e.JobInfo.NumberOfPages, 143 | PrinterName = ((PrintQueueHook)sender).SpoolerName 144 | }; 145 | 146 | Task.Run(() => OnPrintEvent?.Invoke(null, new PrintEventArgs { EventData = printEvent })); 147 | } 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/EventHook/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("EventHook")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("EventHook")] 12 | [assembly: AssemblyCopyright("Copyright © 2015")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("d91de6c7-ea92-4ffe-80d2-db27918d3ee3")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /src/EventHook/StrongNameKey.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justcoding121/windows-user-action-hook/0397d20212baaa1cefe364af58dba892265d1dc8/src/EventHook/StrongNameKey.snk -------------------------------------------------------------------------------- /src/EventHook/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | --------------------------------------------------------------------------------