├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── .gitmodules ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── Winook.sln ├── doc └── Winook.svg ├── src ├── Winook.Common │ ├── DebugHelper.h │ ├── MainWindowFinder.h │ ├── TimestampLogger.h │ └── Winook.h ├── Winook.Lib.Host │ ├── Host.cpp │ ├── Makefile │ ├── Resource.h │ ├── Winook.Lib.Host.ico │ ├── Winook.Lib.Host.rc │ ├── Winook.Lib.Host.vcxproj │ ├── Winook.Lib.Host.vcxproj.filters │ └── application.manifest ├── Winook.Lib │ ├── Lib.cpp │ ├── Lib.def │ ├── Lib.h │ ├── Makefile │ ├── MessageSender.h │ ├── Winook.Lib.rc │ ├── Winook.Lib.vcxproj │ └── Winook.Lib.vcxproj.filters └── Winook │ ├── Helper.cs │ ├── HookBase.cs │ ├── HookType.cs │ ├── KeyCode.cs │ ├── KeyDirection.cs │ ├── KeyboardHook.cs │ ├── KeyboardMessageEventArgs.cs │ ├── MessageEventArgs.cs │ ├── MessageReceiver.cs │ ├── Modifiers.cs │ ├── MouseHook.cs │ ├── MouseMessageCode.cs │ ├── MouseMessageEventArgs.cs │ ├── MouseMessageTypes.cs │ ├── MouseXButtons.cs │ ├── Properties │ ├── Resources.Designer.cs │ ├── Resources.resx │ └── Winook.props │ ├── Winook.csproj │ ├── WinookException.cs │ └── winook.support │ ├── Winook.Lib.Host.x64.exe │ ├── Winook.Lib.Host.x86.exe │ ├── Winook.Lib.Keyboard.x64.dll │ ├── Winook.Lib.Keyboard.x86.dll │ ├── Winook.Lib.Mouse.x64.dll │ └── Winook.Lib.Mouse.x86.dll └── test ├── Winook.Desktop.Core.Test ├── App.xaml ├── App.xaml.cs ├── AssemblyInfo.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs └── Winook.Desktop.Core.Test.csproj └── Winook.Desktop.Test ├── App.config ├── Form1.Designer.cs ├── Form1.cs ├── Form1.resx ├── Program.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings └── Winook.Desktop.Test.csproj /.editorconfig: -------------------------------------------------------------------------------- 1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories 2 | root = true 3 | 4 | # C# files 5 | [*.cs] 6 | 7 | #### Core EditorConfig Options #### 8 | 9 | # Indentation and spacing 10 | indent_size = 4 11 | indent_style = space 12 | 13 | # New line preferences 14 | end_of_line = crlf 15 | insert_final_newline = false 16 | 17 | #### .NET Coding Conventions #### 18 | 19 | # Organize usings 20 | dotnet_separate_import_directive_groups = false 21 | dotnet_sort_system_directives_first = false 22 | 23 | # this. and Me. preferences 24 | dotnet_style_qualification_for_event = false:silent 25 | dotnet_style_qualification_for_field = false:silent 26 | dotnet_style_qualification_for_method = false:silent 27 | dotnet_style_qualification_for_property = false:silent 28 | 29 | # Language keywords vs BCL types preferences 30 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent 31 | dotnet_style_predefined_type_for_member_access = true:silent 32 | 33 | # Parentheses preferences 34 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent 35 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent 36 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent 37 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent 38 | 39 | # Modifier preferences 40 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent 41 | 42 | # Expression-level preferences 43 | dotnet_style_coalesce_expression = true:suggestion 44 | dotnet_style_collection_initializer = true:suggestion 45 | dotnet_style_explicit_tuple_names = true:suggestion 46 | dotnet_style_null_propagation = true:suggestion 47 | dotnet_style_object_initializer = true:suggestion 48 | dotnet_style_prefer_auto_properties = true:silent 49 | dotnet_style_prefer_compound_assignment = true:suggestion 50 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent 51 | dotnet_style_prefer_conditional_expression_over_return = true:silent 52 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 53 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 54 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 55 | dotnet_style_prefer_simplified_interpolation = true:suggestion 56 | 57 | # Field preferences 58 | dotnet_style_readonly_field = true:suggestion 59 | 60 | # Parameter preferences 61 | dotnet_code_quality_unused_parameters = all:suggestion 62 | 63 | #### C# Coding Conventions #### 64 | 65 | # var preferences 66 | csharp_style_var_elsewhere = false:silent 67 | csharp_style_var_for_built_in_types = false:silent 68 | csharp_style_var_when_type_is_apparent = false:silent 69 | 70 | # Expression-bodied members 71 | csharp_style_expression_bodied_accessors = true:silent 72 | csharp_style_expression_bodied_constructors = false:silent 73 | csharp_style_expression_bodied_indexers = true:silent 74 | csharp_style_expression_bodied_lambdas = true:silent 75 | csharp_style_expression_bodied_local_functions = false:silent 76 | csharp_style_expression_bodied_methods = false:silent 77 | csharp_style_expression_bodied_operators = false:silent 78 | csharp_style_expression_bodied_properties = true:silent 79 | 80 | # Pattern matching preferences 81 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 82 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 83 | csharp_style_prefer_switch_expression = true:suggestion 84 | 85 | # Null-checking preferences 86 | csharp_style_conditional_delegate_call = true:suggestion 87 | 88 | # Modifier preferences 89 | csharp_prefer_static_local_function = true:suggestion 90 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent 91 | 92 | # Code-block preferences 93 | csharp_prefer_braces = true:silent 94 | csharp_prefer_simple_using_statement = true:suggestion 95 | 96 | # Expression-level preferences 97 | csharp_prefer_simple_default_expression = true:suggestion 98 | csharp_style_deconstructed_variable_declaration = true:suggestion 99 | csharp_style_inlined_variable_declaration = true:suggestion 100 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 101 | csharp_style_prefer_index_operator = true:suggestion 102 | csharp_style_prefer_range_operator = true:suggestion 103 | csharp_style_throw_expression = true:suggestion 104 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion 105 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent 106 | 107 | # 'using' directive preferences 108 | csharp_using_directive_placement = inside_namespace:silent 109 | 110 | #### C# Formatting Rules #### 111 | 112 | # New line preferences 113 | csharp_new_line_before_catch = true 114 | csharp_new_line_before_else = true 115 | csharp_new_line_before_finally = true 116 | csharp_new_line_before_members_in_anonymous_types = true 117 | csharp_new_line_before_members_in_object_initializers = true 118 | csharp_new_line_before_open_brace = all 119 | csharp_new_line_between_query_expression_clauses = true 120 | 121 | # Indentation preferences 122 | csharp_indent_block_contents = true 123 | csharp_indent_braces = false 124 | csharp_indent_case_contents = true 125 | csharp_indent_case_contents_when_block = true 126 | csharp_indent_labels = one_less_than_current 127 | csharp_indent_switch_labels = true 128 | 129 | # Space preferences 130 | csharp_space_after_cast = false 131 | csharp_space_after_colon_in_inheritance_clause = true 132 | csharp_space_after_comma = true 133 | csharp_space_after_dot = false 134 | csharp_space_after_keywords_in_control_flow_statements = true 135 | csharp_space_after_semicolon_in_for_statement = true 136 | csharp_space_around_binary_operators = before_and_after 137 | csharp_space_around_declaration_statements = false 138 | csharp_space_before_colon_in_inheritance_clause = true 139 | csharp_space_before_comma = false 140 | csharp_space_before_dot = false 141 | csharp_space_before_open_square_brackets = false 142 | csharp_space_before_semicolon_in_for_statement = false 143 | csharp_space_between_empty_square_brackets = false 144 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 145 | csharp_space_between_method_call_name_and_opening_parenthesis = false 146 | csharp_space_between_method_call_parameter_list_parentheses = false 147 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 148 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 149 | csharp_space_between_method_declaration_parameter_list_parentheses = false 150 | csharp_space_between_parentheses = false 151 | csharp_space_between_square_brackets = false 152 | 153 | # Wrapping preferences 154 | csharp_preserve_single_line_blocks = true 155 | csharp_preserve_single_line_statements = true 156 | 157 | #### Naming styles #### 158 | 159 | # Naming rules 160 | 161 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion 162 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface 163 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i 164 | 165 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion 166 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types 167 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case 168 | 169 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion 170 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members 171 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case 172 | 173 | # Symbol specifications 174 | 175 | dotnet_naming_symbols.interface.applicable_kinds = interface 176 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 177 | dotnet_naming_symbols.interface.required_modifiers = 178 | 179 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum 180 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 181 | dotnet_naming_symbols.types.required_modifiers = 182 | 183 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 184 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 185 | dotnet_naming_symbols.non_field_members.required_modifiers = 186 | 187 | # Naming styles 188 | 189 | dotnet_naming_style.pascal_case.required_prefix = 190 | dotnet_naming_style.pascal_case.required_suffix = 191 | dotnet_naming_style.pascal_case.word_separator = 192 | dotnet_naming_style.pascal_case.capitalization = pascal_case 193 | 194 | dotnet_naming_style.begins_with_i.required_prefix = I 195 | dotnet_naming_style.begins_with_i.required_suffix = 196 | dotnet_naming_style.begins_with_i.word_separator = 197 | dotnet_naming_style.begins_with_i.capitalization = pascal_case 198 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | custom: ['https://paypal.me/mancote'] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rd/asio"] 2 | path = 3rd/asio 3 | url = https://github.com/chriskohlhoff/asio 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/test/Winook.Desktop.Core.Test/bin/Debug/netcoreapp3.1/Winook.Desktop.Core.Test.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/test/Winook.Desktop.Core.Test", 16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "iomanip": "cpp" 4 | } 5 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/test/Winook.Desktop.Core.Test/Winook.Desktop.Core.Test.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/test/Winook.Desktop.Core.Test/Winook.Desktop.Core.Test.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/test/Winook.Desktop.Core.Test/Winook.Desktop.Core.Test.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.3.2 (2021-05-08) 4 | 5 | ### Fixed 6 | 7 | - #31: Fix Dispose() issue in MouseHook 8 | 9 | ### Added 10 | 11 | - #30: Add RemoveAllHandlers() 12 | 13 | ## 1.3.1 (2021-04-30) 14 | 15 | ### Fixed 16 | 17 | - #28: Fix Dispose() issue in KeyboardHook 18 | 19 | ## 1.3.0 (2021-04-23) 20 | 21 | ### Added 22 | 23 | - #26: Add Shift, Control, Alt state to mouse messages 24 | 25 | ## 1.2.1 (2021-04-15) 26 | 27 | ### Fixed 28 | 29 | - #24: Fix Shift, Control and Alt handling 30 | 31 | ## 1.2.0 (2021-03-02) 32 | 33 | ### Added 34 | 35 | - #22: Add code to identify XButtons 36 | 37 | ## 1.1.1 (2021-02-09) 38 | 39 | ### Fixed 40 | 41 | - #21: Restore asio code 42 | 43 | ## 1.1.0 (2020-06-24) 44 | 45 | ### Changed 46 | 47 | - #20: Remove asio dependency 48 | 49 | ## 1.0.1 (2020-06-24) 50 | 51 | ### Added 52 | 53 | - #17: Support .NET Standard 2.0 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](doc/Winook.svg) 2 | 3 | [![nuget][nuget-badge]][nuget-url] 4 | 5 | [nuget-badge]: https://img.shields.io/badge/nuget-v1.3.2-blue.svg 6 | [nuget-url]: https://www.nuget.org/packages/Winook 7 | 8 | Winook is a Windows library that let you install thread-level hooks inside processes. This library offers an alternative to solutions that use global hooks. With thread-level hooks, performance and management issues can be avoided. 9 | 10 | # Information 11 | Winook uses host processes and dll injection to setup hooks. Both 32-bit and 64-bit support host applications and dlls are included. 12 | 13 | ## Features 14 | - Works in .NET Framework and .NET Core applications 15 | - 32-bit and 64-bit dynamic support 16 | - Mouse and keyboard specific message handlers 17 | - Extensive keyboard modifiers support 18 | - Mouse message filtering at dll level 19 | - Ability to ignore mouse move messages to improve performance 20 | 21 | ## Application 22 | - Game Helpers 23 | - Winook can be used in game helpers. In-game keyboard and mouse events can be tied to event handlers in your application. For example, Winook is used in [Poe Lurker](https://github.com/C1rdec/Poe-Lurker) to provide additional trading functionality. 24 | - Application Inspection 25 | - Winook can be used as a telemetry inspection tool. 26 | - Other Uses 27 | - Please share how you use Winook! 28 | 29 | # Installation 30 | 31 | ## NuGet 32 | ``` 33 | Install-Package Winook 34 | ``` 35 | 36 | # Usage 37 | ``` csharp 38 | _process = Process.Start(@"c:\windows\notepad.exe"); 39 | //_process = Process.Start(@"c:\windows\syswow64\notepad.exe"); // works also with 32-bit 40 | 41 | _mouseHook = new MouseHook(_process.Id); 42 | //_mouseHook = new MouseHook(_process.Id, MouseMessageTypes.IgnoreMove); 43 | _mouseHook.MessageReceived += MouseHook_MessageReceived; 44 | //_mouseHook.LeftButtonUp += MouseHook_LeftButtonUp; 45 | //_mouseHook.AddHandler(MouseMessageCode.NCLeftButtonUp, MouseHook_NCLButtonUp); 46 | _mouseHook.InstallAsync(); 47 | 48 | _keyboardHook = new KeyboardHook(_process.Id); 49 | _keyboardHook.MessageReceived += KeyboardHook_MessageReceived; 50 | //_keyboardHook.AddHandler(KeyCode.Y, Modifiers.ControlShift, KeyboardHook_ControlShiftY); 51 | _keyboardHook.InstallAsync(); 52 | 53 | ... 54 | 55 | private void MouseHook_MessageReceived(object sender, MouseMessageEventArgs e) 56 | { 57 | Debug.Write($"Code: {e.MessageCode}; X: {e.X}; Y: {e.Y}; Modifiers: {e.Modifiers:x}; "); 58 | Debug.WriteLine($"Delta: {e.Delta}; XButtons: {e.XButtons}"); 59 | } 60 | 61 | private void KeyboardHook_MessageReceived(object sender, KeyboardMessageEventArgs e) 62 | { 63 | Debug.Write($"Code: {e.KeyValue}; Modifiers: {e.Modifiers:x}; Flags: {e.Flags:x}; "); 64 | Debug.WriteLine($"Shift: {e.Shift}; Control: {e.Control}; Alt: {e.Alt}; Direction: {e.Direction}"); 65 | } 66 | 67 | ``` 68 | 69 | # License 70 | MIT 71 | 72 | # Acknowledgements 73 | Thanks to [C1rdec](https://github.com/C1rdec) for the inspiration and motivation. I created this initially to help him with his [project](https://github.com/C1rdec/Poe-Lurker). 74 | -------------------------------------------------------------------------------- /Winook.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30011.22 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1464C5EF-06A7-45E9-948E-AF7F17D5C397}" 7 | ProjectSection(SolutionItems) = preProject 8 | .editorconfig = .editorconfig 9 | .gitignore = .gitignore 10 | .gitmodules = .gitmodules 11 | CHANGELOG.md = CHANGELOG.md 12 | LICENSE = LICENSE 13 | README.md = README.md 14 | EndProjectSection 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Winook", "src\Winook\Winook.csproj", "{800700A9-E0BD-47DD-9095-318E2F4C7ADA}" 17 | EndProject 18 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Winook.Lib", "src\Winook.Lib\Winook.Lib.vcxproj", "{5FB052A8-82FA-46E9-A9DF-078BF725A548}" 19 | EndProject 20 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Winook.Lib.Host", "src\Winook.Lib.Host\Winook.Lib.Host.vcxproj", "{69306344-BD77-4AF6-8890-BF001D8EA7EE}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Winook.Desktop.Test", "test\Winook.Desktop.Test\Winook.Desktop.Test.csproj", "{BFE61D8C-E947-4F75-98E2-17A276F72175}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Winook.Desktop.Core.Test", "test\Winook.Desktop.Core.Test\Winook.Desktop.Core.Test.csproj", "{26BEFC8F-B812-4637-95DE-1AF365DAEBD5}" 25 | EndProject 26 | Global 27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 28 | Debug|Any CPU = Debug|Any CPU 29 | Debug|x64 = Debug|x64 30 | Debug|x86 = Debug|x86 31 | Release|Any CPU = Release|Any CPU 32 | Release|x64 = Release|x64 33 | Release|x86 = Release|x86 34 | EndGlobalSection 35 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 36 | {800700A9-E0BD-47DD-9095-318E2F4C7ADA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {800700A9-E0BD-47DD-9095-318E2F4C7ADA}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {800700A9-E0BD-47DD-9095-318E2F4C7ADA}.Debug|x64.ActiveCfg = Debug|Any CPU 39 | {800700A9-E0BD-47DD-9095-318E2F4C7ADA}.Debug|x64.Build.0 = Debug|Any CPU 40 | {800700A9-E0BD-47DD-9095-318E2F4C7ADA}.Debug|x86.ActiveCfg = Debug|Any CPU 41 | {800700A9-E0BD-47DD-9095-318E2F4C7ADA}.Debug|x86.Build.0 = Debug|Any CPU 42 | {800700A9-E0BD-47DD-9095-318E2F4C7ADA}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {800700A9-E0BD-47DD-9095-318E2F4C7ADA}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {800700A9-E0BD-47DD-9095-318E2F4C7ADA}.Release|x64.ActiveCfg = Release|Any CPU 45 | {800700A9-E0BD-47DD-9095-318E2F4C7ADA}.Release|x64.Build.0 = Release|Any CPU 46 | {800700A9-E0BD-47DD-9095-318E2F4C7ADA}.Release|x86.ActiveCfg = Release|Any CPU 47 | {800700A9-E0BD-47DD-9095-318E2F4C7ADA}.Release|x86.Build.0 = Release|Any CPU 48 | {5FB052A8-82FA-46E9-A9DF-078BF725A548}.Debug|Any CPU.ActiveCfg = Debug|Win32 49 | {5FB052A8-82FA-46E9-A9DF-078BF725A548}.Debug|x64.ActiveCfg = Debug|x64 50 | {5FB052A8-82FA-46E9-A9DF-078BF725A548}.Debug|x64.Build.0 = Debug|x64 51 | {5FB052A8-82FA-46E9-A9DF-078BF725A548}.Debug|x86.ActiveCfg = Debug|Win32 52 | {5FB052A8-82FA-46E9-A9DF-078BF725A548}.Debug|x86.Build.0 = Debug|Win32 53 | {5FB052A8-82FA-46E9-A9DF-078BF725A548}.Release|Any CPU.ActiveCfg = Release|Win32 54 | {5FB052A8-82FA-46E9-A9DF-078BF725A548}.Release|x64.ActiveCfg = Release|x64 55 | {5FB052A8-82FA-46E9-A9DF-078BF725A548}.Release|x64.Build.0 = Release|x64 56 | {5FB052A8-82FA-46E9-A9DF-078BF725A548}.Release|x86.ActiveCfg = Release|Win32 57 | {5FB052A8-82FA-46E9-A9DF-078BF725A548}.Release|x86.Build.0 = Release|Win32 58 | {69306344-BD77-4AF6-8890-BF001D8EA7EE}.Debug|Any CPU.ActiveCfg = Debug|Win32 59 | {69306344-BD77-4AF6-8890-BF001D8EA7EE}.Debug|x64.ActiveCfg = Debug|x64 60 | {69306344-BD77-4AF6-8890-BF001D8EA7EE}.Debug|x64.Build.0 = Debug|x64 61 | {69306344-BD77-4AF6-8890-BF001D8EA7EE}.Debug|x86.ActiveCfg = Debug|Win32 62 | {69306344-BD77-4AF6-8890-BF001D8EA7EE}.Debug|x86.Build.0 = Debug|Win32 63 | {69306344-BD77-4AF6-8890-BF001D8EA7EE}.Release|Any CPU.ActiveCfg = Release|Win32 64 | {69306344-BD77-4AF6-8890-BF001D8EA7EE}.Release|x64.ActiveCfg = Release|x64 65 | {69306344-BD77-4AF6-8890-BF001D8EA7EE}.Release|x64.Build.0 = Release|x64 66 | {69306344-BD77-4AF6-8890-BF001D8EA7EE}.Release|x86.ActiveCfg = Release|Win32 67 | {69306344-BD77-4AF6-8890-BF001D8EA7EE}.Release|x86.Build.0 = Release|Win32 68 | {BFE61D8C-E947-4F75-98E2-17A276F72175}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 | {BFE61D8C-E947-4F75-98E2-17A276F72175}.Debug|Any CPU.Build.0 = Debug|Any CPU 70 | {BFE61D8C-E947-4F75-98E2-17A276F72175}.Debug|x64.ActiveCfg = Debug|Any CPU 71 | {BFE61D8C-E947-4F75-98E2-17A276F72175}.Debug|x64.Build.0 = Debug|Any CPU 72 | {BFE61D8C-E947-4F75-98E2-17A276F72175}.Debug|x86.ActiveCfg = Debug|Any CPU 73 | {BFE61D8C-E947-4F75-98E2-17A276F72175}.Debug|x86.Build.0 = Debug|Any CPU 74 | {BFE61D8C-E947-4F75-98E2-17A276F72175}.Release|Any CPU.ActiveCfg = Release|Any CPU 75 | {BFE61D8C-E947-4F75-98E2-17A276F72175}.Release|Any CPU.Build.0 = Release|Any CPU 76 | {BFE61D8C-E947-4F75-98E2-17A276F72175}.Release|x64.ActiveCfg = Release|Any CPU 77 | {BFE61D8C-E947-4F75-98E2-17A276F72175}.Release|x64.Build.0 = Release|Any CPU 78 | {BFE61D8C-E947-4F75-98E2-17A276F72175}.Release|x86.ActiveCfg = Release|Any CPU 79 | {BFE61D8C-E947-4F75-98E2-17A276F72175}.Release|x86.Build.0 = Release|Any CPU 80 | {26BEFC8F-B812-4637-95DE-1AF365DAEBD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 81 | {26BEFC8F-B812-4637-95DE-1AF365DAEBD5}.Debug|Any CPU.Build.0 = Debug|Any CPU 82 | {26BEFC8F-B812-4637-95DE-1AF365DAEBD5}.Debug|x64.ActiveCfg = Debug|Any CPU 83 | {26BEFC8F-B812-4637-95DE-1AF365DAEBD5}.Debug|x64.Build.0 = Debug|Any CPU 84 | {26BEFC8F-B812-4637-95DE-1AF365DAEBD5}.Debug|x86.ActiveCfg = Debug|Any CPU 85 | {26BEFC8F-B812-4637-95DE-1AF365DAEBD5}.Debug|x86.Build.0 = Debug|Any CPU 86 | {26BEFC8F-B812-4637-95DE-1AF365DAEBD5}.Release|Any CPU.ActiveCfg = Release|Any CPU 87 | {26BEFC8F-B812-4637-95DE-1AF365DAEBD5}.Release|Any CPU.Build.0 = Release|Any CPU 88 | {26BEFC8F-B812-4637-95DE-1AF365DAEBD5}.Release|x64.ActiveCfg = Release|Any CPU 89 | {26BEFC8F-B812-4637-95DE-1AF365DAEBD5}.Release|x64.Build.0 = Release|Any CPU 90 | {26BEFC8F-B812-4637-95DE-1AF365DAEBD5}.Release|x86.ActiveCfg = Release|Any CPU 91 | {26BEFC8F-B812-4637-95DE-1AF365DAEBD5}.Release|x86.Build.0 = Release|Any CPU 92 | EndGlobalSection 93 | GlobalSection(SolutionProperties) = preSolution 94 | HideSolutionNode = FALSE 95 | EndGlobalSection 96 | GlobalSection(ExtensibilityGlobals) = postSolution 97 | SolutionGuid = {40FB57A0-76CF-4B66-ACBA-6B945B119F11} 98 | EndGlobalSection 99 | EndGlobal 100 | -------------------------------------------------------------------------------- /doc/Winook.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Winook.Common/DebugHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class DebugHelper 11 | { 12 | public: 13 | static std::wstring FormatWindowMessage(MSG msg); 14 | static std::wstring FormatWindowMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); 15 | static std::wstring FormatMouseHookMessage(int code, WPARAM wParam, LPARAM lParam); 16 | static std::wstring FormatKeyboardHookMessage(int code, WPARAM wParam, LPARAM lParam); 17 | private: 18 | DebugHelper() { } 19 | }; 20 | 21 | const std::map kWindowMessageCodeMap = 22 | { 23 | { WM_NULL, TEXT("WM_NULL") }, 24 | { WM_CREATE, TEXT("WM_CREATE") }, 25 | { WM_DESTROY, TEXT("WM_DESTROY") }, 26 | { WM_MOVE, TEXT("WM_MOVE") }, 27 | { WM_SIZE, TEXT("WM_SIZE") }, 28 | { WM_ACTIVATE, TEXT("WM_ACTIVATE") }, 29 | { WM_SETFOCUS, TEXT("WM_SETFOCUS") }, 30 | { WM_KILLFOCUS, TEXT("WM_KILLFOCUS") }, 31 | { WM_ENABLE, TEXT("WM_ENABLE") }, 32 | { WM_SETREDRAW, TEXT("WM_SETREDRAW") }, 33 | { WM_SETTEXT, TEXT("WM_SETTEXT") }, 34 | { WM_GETTEXT, TEXT("WM_GETTEXT") }, 35 | { WM_GETTEXTLENGTH, TEXT("WM_GETTEXTLENGTH") }, 36 | { WM_PAINT, TEXT("WM_PAINT") }, 37 | { WM_CLOSE, TEXT("WM_CLOSE") }, 38 | { WM_QUERYENDSESSION, TEXT("WM_QUERYENDSESSION") }, 39 | { WM_QUERYOPEN, TEXT("WM_QUERYOPEN") }, 40 | { WM_ENDSESSION, TEXT("WM_ENDSESSION") }, 41 | { WM_QUIT, TEXT("WM_QUIT") }, 42 | { WM_ERASEBKGND, TEXT("WM_ERASEBKGND") }, 43 | { WM_SYSCOLORCHANGE, TEXT("WM_SYSCOLORCHANGE") }, 44 | { WM_SHOWWINDOW, TEXT("WM_SHOWWINDOW") }, 45 | { WM_WININICHANGE, TEXT("WM_WININICHANGE") }, 46 | { WM_DEVMODECHANGE, TEXT("WM_DEVMODECHANGE") }, 47 | { WM_ACTIVATEAPP, TEXT("WM_ACTIVATEAPP") }, 48 | { WM_FONTCHANGE, TEXT("WM_FONTCHANGE") }, 49 | { WM_TIMECHANGE, TEXT("WM_TIMECHANGE") }, 50 | { WM_CANCELMODE, TEXT("WM_CANCELMODE") }, 51 | { WM_SETCURSOR, TEXT("WM_SETCURSOR") }, 52 | { WM_MOUSEACTIVATE, TEXT("WM_MOUSEACTIVATE") }, 53 | { WM_CHILDACTIVATE, TEXT("WM_CHILDACTIVATE") }, 54 | { WM_QUEUESYNC, TEXT("WM_QUEUESYNC") }, 55 | { WM_GETMINMAXINFO, TEXT("WM_GETMINMAXINFO") }, 56 | { WM_PAINTICON, TEXT("WM_PAINTICON") }, 57 | { WM_ICONERASEBKGND, TEXT("WM_ICONERASEBKGND") }, 58 | { WM_NEXTDLGCTL, TEXT("WM_NEXTDLGCTL") }, 59 | { WM_SPOOLERSTATUS, TEXT("WM_SPOOLERSTATUS") }, 60 | { WM_DRAWITEM, TEXT("WM_DRAWITEM") }, 61 | { WM_MEASUREITEM, TEXT("WM_MEASUREITEM") }, 62 | { WM_DELETEITEM, TEXT("WM_DELETEITEM") }, 63 | { WM_VKEYTOITEM, TEXT("WM_VKEYTOITEM") }, 64 | { WM_CHARTOITEM, TEXT("WM_CHARTOITEM") }, 65 | { WM_SETFONT, TEXT("WM_SETFONT") }, 66 | { WM_GETFONT, TEXT("WM_GETFONT") }, 67 | { WM_SETHOTKEY, TEXT("WM_SETHOTKEY") }, 68 | { WM_GETHOTKEY, TEXT("WM_GETHOTKEY") }, 69 | { WM_QUERYDRAGICON, TEXT("WM_QUERYDRAGICON") }, 70 | { WM_COMPAREITEM, TEXT("WM_COMPAREITEM") }, 71 | { WM_GETOBJECT, TEXT("WM_GETOBJECT") }, 72 | { WM_COMPACTING, TEXT("WM_COMPACTING") }, 73 | { WM_COMMNOTIFY, TEXT("WM_COMMNOTIFY") }, 74 | { WM_WINDOWPOSCHANGING, TEXT("WM_WINDOWPOSCHANGING") }, 75 | { WM_WINDOWPOSCHANGED, TEXT("WM_WINDOWPOSCHANGED") }, 76 | { WM_POWER, TEXT("WM_POWER") }, 77 | { WM_COPYDATA, TEXT("WM_COPYDATA") }, 78 | { WM_CANCELJOURNAL, TEXT("WM_CANCELJOURNAL") }, 79 | { WM_NOTIFY, TEXT("WM_NOTIFY") }, 80 | { WM_INPUTLANGCHANGEREQUEST, TEXT("WM_INPUTLANGCHANGEREQUEST") }, 81 | { WM_INPUTLANGCHANGE, TEXT("WM_INPUTLANGCHANGE") }, 82 | { WM_TCARD, TEXT("WM_TCARD") }, 83 | { WM_HELP, TEXT("WM_HELP") }, 84 | { WM_USERCHANGED, TEXT("WM_USERCHANGED") }, 85 | { WM_NOTIFYFORMAT, TEXT("WM_NOTIFYFORMAT") }, 86 | { WM_CONTEXTMENU, TEXT("WM_CONTEXTMENU") }, 87 | { WM_STYLECHANGING, TEXT("WM_STYLECHANGING") }, 88 | { WM_STYLECHANGED, TEXT("WM_STYLECHANGED") }, 89 | { WM_DISPLAYCHANGE, TEXT("WM_DISPLAYCHANGE") }, 90 | { WM_GETICON, TEXT("WM_GETICON") }, 91 | { WM_SETICON, TEXT("WM_SETICON") }, 92 | { WM_NCCREATE, TEXT("WM_NCCREATE") }, 93 | { WM_NCDESTROY, TEXT("WM_NCDESTROY") }, 94 | { WM_NCCALCSIZE, TEXT("WM_NCCALCSIZE") }, 95 | { WM_NCHITTEST, TEXT("WM_NCHITTEST") }, 96 | { WM_NCPAINT, TEXT("WM_NCPAINT") }, 97 | { WM_NCACTIVATE, TEXT("WM_NCACTIVATE") }, 98 | { WM_GETDLGCODE, TEXT("WM_GETDLGCODE") }, 99 | { WM_SYNCPAINT, TEXT("WM_SYNCPAINT") }, 100 | { WM_NCMOUSEMOVE, TEXT("WM_NCMOUSEMOVE") }, 101 | { WM_NCLBUTTONDOWN, TEXT("WM_NCLBUTTONDOWN") }, 102 | { WM_NCLBUTTONUP, TEXT("WM_NCLBUTTONUP") }, 103 | { WM_NCLBUTTONDBLCLK, TEXT("WM_NCLBUTTONDBLCLK") }, 104 | { WM_NCRBUTTONDOWN, TEXT("WM_NCRBUTTONDOWN") }, 105 | { WM_NCRBUTTONUP, TEXT("WM_NCRBUTTONUP") }, 106 | { WM_NCRBUTTONDBLCLK, TEXT("WM_NCRBUTTONDBLCLK") }, 107 | { WM_NCMBUTTONDOWN, TEXT("WM_NCMBUTTONDOWN") }, 108 | { WM_NCMBUTTONUP, TEXT("WM_NCMBUTTONUP") }, 109 | { WM_NCMBUTTONDBLCLK, TEXT("WM_NCMBUTTONDBLCLK") }, 110 | { WM_NCXBUTTONDOWN, TEXT("WM_NCXBUTTONDOWN") }, 111 | { WM_NCXBUTTONUP, TEXT("WM_NCXBUTTONUP") }, 112 | { WM_NCXBUTTONDBLCLK, TEXT("WM_NCXBUTTONDBLCLK") }, 113 | { WM_INPUT_DEVICE_CHANGE, TEXT("WM_INPUT_DEVICE_CHANGE") }, 114 | { WM_INPUT, TEXT("WM_INPUT") }, 115 | { WM_KEYFIRST, TEXT("WM_KEYFIRST") }, 116 | { WM_KEYDOWN, TEXT("WM_KEYDOWN") }, 117 | { WM_KEYUP, TEXT("WM_KEYUP") }, 118 | { WM_CHAR, TEXT("WM_CHAR") }, 119 | { WM_DEADCHAR, TEXT("WM_DEADCHAR") }, 120 | { WM_SYSKEYDOWN, TEXT("WM_SYSKEYDOWN") }, 121 | { WM_SYSKEYUP, TEXT("WM_SYSKEYUP") }, 122 | { WM_SYSCHAR, TEXT("WM_SYSCHAR") }, 123 | { WM_SYSDEADCHAR, TEXT("WM_SYSDEADCHAR") }, 124 | { WM_KEYLAST, TEXT("WM_KEYLAST") }, 125 | { WM_KEYLAST, TEXT("WM_KEYLAST") }, 126 | { WM_IME_STARTCOMPOSITION, TEXT("WM_IME_STARTCOMPOSITION") }, 127 | { WM_IME_ENDCOMPOSITION, TEXT("WM_IME_ENDCOMPOSITION") }, 128 | { WM_IME_COMPOSITION, TEXT("WM_IME_COMPOSITION") }, 129 | { WM_IME_KEYLAST, TEXT("WM_IME_KEYLAST") }, 130 | { WM_INITDIALOG, TEXT("WM_INITDIALOG") }, 131 | { WM_COMMAND, TEXT("WM_COMMAND") }, 132 | { WM_SYSCOMMAND, TEXT("WM_SYSCOMMAND") }, 133 | { WM_TIMER, TEXT("WM_TIMER") }, 134 | { WM_HSCROLL, TEXT("WM_HSCROLL") }, 135 | { WM_VSCROLL, TEXT("WM_VSCROLL") }, 136 | { WM_INITMENU, TEXT("WM_INITMENU") }, 137 | { WM_INITMENUPOPUP, TEXT("WM_INITMENUPOPUP") }, 138 | { WM_GESTURE, TEXT("WM_GESTURE") }, 139 | { WM_GESTURENOTIFY, TEXT("WM_GESTURENOTIFY") }, 140 | { WM_MENUSELECT, TEXT("WM_MENUSELECT") }, 141 | { WM_MENUCHAR, TEXT("WM_MENUCHAR") }, 142 | { WM_ENTERIDLE, TEXT("WM_ENTERIDLE") }, 143 | { WM_MENURBUTTONUP, TEXT("WM_MENURBUTTONUP") }, 144 | { WM_MENUDRAG, TEXT("WM_MENUDRAG") }, 145 | { WM_MENUGETOBJECT, TEXT("WM_MENUGETOBJECT") }, 146 | { WM_UNINITMENUPOPUP, TEXT("WM_UNINITMENUPOPUP") }, 147 | { WM_MENUCOMMAND, TEXT("WM_MENUCOMMAND") }, 148 | { WM_CHANGEUISTATE, TEXT("WM_CHANGEUISTATE") }, 149 | { WM_UPDATEUISTATE, TEXT("WM_UPDATEUISTATE") }, 150 | { WM_QUERYUISTATE, TEXT("WM_QUERYUISTATE") }, 151 | { WM_CTLCOLORMSGBOX, TEXT("WM_CTLCOLORMSGBOX") }, 152 | { WM_CTLCOLOREDIT, TEXT("WM_CTLCOLOREDIT") }, 153 | { WM_CTLCOLORLISTBOX, TEXT("WM_CTLCOLORLISTBOX") }, 154 | { WM_CTLCOLORBTN, TEXT("WM_CTLCOLORBTN") }, 155 | { WM_CTLCOLORDLG, TEXT("WM_CTLCOLORDLG") }, 156 | { WM_CTLCOLORSCROLLBAR, TEXT("WM_CTLCOLORSCROLLBAR") }, 157 | { WM_CTLCOLORSTATIC, TEXT("WM_CTLCOLORSTATIC") }, 158 | { MN_GETHMENU, TEXT("MN_GETHMENU") }, 159 | { WM_MOUSEMOVE, TEXT("WM_MOUSEMOVE") }, 160 | { WM_LBUTTONDOWN, TEXT("WM_LBUTTONDOWN") }, 161 | { WM_LBUTTONUP, TEXT("WM_LBUTTONUP") }, 162 | { WM_LBUTTONDBLCLK, TEXT("WM_LBUTTONDBLCLK") }, 163 | { WM_RBUTTONDOWN, TEXT("WM_RBUTTONDOWN") }, 164 | { WM_RBUTTONUP, TEXT("WM_RBUTTONUP") }, 165 | { WM_RBUTTONDBLCLK, TEXT("WM_RBUTTONDBLCLK") }, 166 | { WM_MBUTTONDOWN, TEXT("WM_MBUTTONDOWN") }, 167 | { WM_MBUTTONUP, TEXT("WM_MBUTTONUP") }, 168 | { WM_MBUTTONDBLCLK, TEXT("WM_MBUTTONDBLCLK") }, 169 | { WM_MOUSEWHEEL, TEXT("WM_MOUSEWHEEL") }, 170 | { WM_XBUTTONDOWN, TEXT("WM_XBUTTONDOWN") }, 171 | { WM_XBUTTONUP, TEXT("WM_XBUTTONUP") }, 172 | { WM_XBUTTONDBLCLK, TEXT("WM_XBUTTONDBLCLK") }, 173 | { WM_MOUSEHWHEEL, TEXT("WM_MOUSEHWHEEL") }, 174 | { WM_PARENTNOTIFY, TEXT("WM_PARENTNOTIFY") }, 175 | { WM_ENTERMENULOOP, TEXT("WM_ENTERMENULOOP") }, 176 | { WM_EXITMENULOOP, TEXT("WM_EXITMENULOOP") }, 177 | { WM_NEXTMENU, TEXT("WM_NEXTMENU") }, 178 | { WM_SIZING, TEXT("WM_SIZING") }, 179 | { WM_CAPTURECHANGED, TEXT("WM_CAPTURECHANGED") }, 180 | { WM_MOVING, TEXT("WM_MOVING") }, 181 | { WM_POWERBROADCAST, TEXT("WM_POWERBROADCAST") }, 182 | { WM_DEVICECHANGE, TEXT("WM_DEVICECHANGE") }, 183 | { WM_MDICREATE, TEXT("WM_MDICREATE") }, 184 | { WM_MDIDESTROY, TEXT("WM_MDIDESTROY") }, 185 | { WM_MDIACTIVATE, TEXT("WM_MDIACTIVATE") }, 186 | { WM_MDIRESTORE, TEXT("WM_MDIRESTORE") }, 187 | { WM_MDINEXT, TEXT("WM_MDINEXT") }, 188 | { WM_MDIMAXIMIZE, TEXT("WM_MDIMAXIMIZE") }, 189 | { WM_MDITILE, TEXT("WM_MDITILE") }, 190 | { WM_MDICASCADE, TEXT("WM_MDICASCADE") }, 191 | { WM_MDIICONARRANGE, TEXT("WM_MDIICONARRANGE") }, 192 | { WM_MDIGETACTIVE, TEXT("WM_MDIGETACTIVE") }, 193 | { WM_MDISETMENU, TEXT("WM_MDISETMENU") }, 194 | { WM_ENTERSIZEMOVE, TEXT("WM_ENTERSIZEMOVE") }, 195 | { WM_EXITSIZEMOVE, TEXT("WM_EXITSIZEMOVE") }, 196 | { WM_DROPFILES, TEXT("WM_DROPFILES") }, 197 | { WM_MDIREFRESHMENU, TEXT("WM_MDIREFRESHMENU") }, 198 | { WM_POINTERDEVICECHANGE, TEXT("WM_POINTERDEVICECHANGE") }, 199 | { WM_POINTERDEVICEINRANGE, TEXT("WM_POINTERDEVICEINRANGE") }, 200 | { WM_POINTERDEVICEOUTOFRANGE, TEXT("WM_POINTERDEVICEOUTOFRANGE") }, 201 | { WM_TOUCH, TEXT("WM_TOUCH") }, 202 | { WM_NCPOINTERUPDATE, TEXT("WM_NCPOINTERUPDATE") }, 203 | { WM_NCPOINTERDOWN, TEXT("WM_NCPOINTERDOWN") }, 204 | { WM_NCPOINTERUP, TEXT("WM_NCPOINTERUP") }, 205 | { WM_POINTERUPDATE, TEXT("WM_POINTERUPDATE") }, 206 | { WM_POINTERDOWN, TEXT("WM_POINTERDOWN") }, 207 | { WM_POINTERUP, TEXT("WM_POINTERUP") }, 208 | { WM_POINTERENTER, TEXT("WM_POINTERENTER") }, 209 | { WM_POINTERLEAVE, TEXT("WM_POINTERLEAVE") }, 210 | { WM_POINTERACTIVATE, TEXT("WM_POINTERACTIVATE") }, 211 | { WM_POINTERCAPTURECHANGED, TEXT("WM_POINTERCAPTURECHANGED") }, 212 | { WM_TOUCHHITTESTING, TEXT("WM_TOUCHHITTESTING") }, 213 | { WM_POINTERWHEEL, TEXT("WM_POINTERWHEEL") }, 214 | { WM_POINTERHWHEEL, TEXT("WM_POINTERHWHEEL") }, 215 | #ifdef __MINGW32__ 216 | { 0x0250, TEXT("DM_POINTERHITTEST") }, 217 | #else 218 | { DM_POINTERHITTEST, TEXT("DM_POINTERHITTEST") }, 219 | #endif 220 | { WM_IME_SETCONTEXT, TEXT("WM_IME_SETCONTEXT") }, 221 | { WM_IME_NOTIFY, TEXT("WM_IME_NOTIFY") }, 222 | { WM_IME_CONTROL, TEXT("WM_IME_CONTROL") }, 223 | { WM_IME_COMPOSITIONFULL, TEXT("WM_IME_COMPOSITIONFULL") }, 224 | { WM_IME_SELECT, TEXT("WM_IME_SELECT") }, 225 | { WM_IME_CHAR, TEXT("WM_IME_CHAR") }, 226 | { WM_IME_REQUEST, TEXT("WM_IME_REQUEST") }, 227 | { WM_IME_KEYDOWN, TEXT("WM_IME_KEYDOWN") }, 228 | { WM_IME_KEYUP, TEXT("WM_IME_KEYUP") }, 229 | { WM_MOUSEHOVER, TEXT("WM_MOUSEHOVER") }, 230 | { WM_MOUSELEAVE, TEXT("WM_MOUSELEAVE") }, 231 | { WM_NCMOUSEHOVER, TEXT("WM_NCMOUSEHOVER") }, 232 | { WM_NCMOUSELEAVE, TEXT("WM_NCMOUSELEAVE") }, 233 | { WM_WTSSESSION_CHANGE, TEXT("WM_WTSSESSION_CHANGE") }, 234 | { WM_TABLET_FIRST, TEXT("WM_TABLET_FIRST") }, 235 | { WM_TABLET_LAST, TEXT("WM_TABLET_LAST") }, 236 | { WM_DPICHANGED, TEXT("WM_DPICHANGED") }, 237 | { WM_CUT, TEXT("WM_CUT") }, 238 | { WM_COPY, TEXT("WM_COPY") }, 239 | { WM_PASTE, TEXT("WM_PASTE") }, 240 | { WM_CLEAR, TEXT("WM_CLEAR") }, 241 | { WM_UNDO, TEXT("WM_UNDO") }, 242 | { WM_RENDERFORMAT, TEXT("WM_RENDERFORMAT") }, 243 | { WM_RENDERALLFORMATS, TEXT("WM_RENDERALLFORMATS") }, 244 | { WM_DESTROYCLIPBOARD, TEXT("WM_DESTROYCLIPBOARD") }, 245 | { WM_DRAWCLIPBOARD, TEXT("WM_DRAWCLIPBOARD") }, 246 | { WM_PAINTCLIPBOARD, TEXT("WM_PAINTCLIPBOARD") }, 247 | { WM_VSCROLLCLIPBOARD, TEXT("WM_VSCROLLCLIPBOARD") }, 248 | { WM_SIZECLIPBOARD, TEXT("WM_SIZECLIPBOARD") }, 249 | { WM_ASKCBFORMATNAME, TEXT("WM_ASKCBFORMATNAME") }, 250 | { WM_CHANGECBCHAIN, TEXT("WM_CHANGECBCHAIN") }, 251 | { WM_HSCROLLCLIPBOARD, TEXT("WM_HSCROLLCLIPBOARD") }, 252 | { WM_QUERYNEWPALETTE, TEXT("WM_QUERYNEWPALETTE") }, 253 | { WM_PALETTEISCHANGING, TEXT("WM_PALETTEISCHANGING") }, 254 | { WM_PALETTECHANGED, TEXT("WM_PALETTECHANGED") }, 255 | { WM_HOTKEY, TEXT("WM_HOTKEY") }, 256 | { WM_PRINT, TEXT("WM_PRINT") }, 257 | { WM_PRINTCLIENT, TEXT("WM_PRINTCLIENT") }, 258 | { WM_APPCOMMAND, TEXT("WM_APPCOMMAND") }, 259 | { WM_THEMECHANGED, TEXT("WM_THEMECHANGED") }, 260 | { WM_CLIPBOARDUPDATE, TEXT("WM_CLIPBOARDUPDATE") }, 261 | { WM_DWMCOMPOSITIONCHANGED, TEXT("WM_DWMCOMPOSITIONCHANGED") }, 262 | { WM_DWMNCRENDERINGCHANGED, TEXT("WM_DWMNCRENDERINGCHANGED") }, 263 | { WM_DWMCOLORIZATIONCOLORCHANGED, TEXT("WM_DWMCOLORIZATIONCOLORCHANGED") }, 264 | { WM_DWMWINDOWMAXIMIZEDCHANGE, TEXT("WM_DWMWINDOWMAXIMIZEDCHANGE") }, 265 | { WM_DWMSENDICONICTHUMBNAIL, TEXT("WM_DWMSENDICONICTHUMBNAIL") }, 266 | { WM_DWMSENDICONICLIVEPREVIEWBITMAP, TEXT("WM_DWMSENDICONICLIVEPREVIEWBITMAP") }, 267 | { WM_GETTITLEBARINFOEX, TEXT("WM_GETTITLEBARINFOEX") }, 268 | { WM_HANDHELDFIRST, TEXT("WM_HANDHELDFIRST") }, 269 | { WM_HANDHELDLAST, TEXT("WM_HANDHELDLAST") }, 270 | { WM_AFXFIRST, TEXT("WM_AFXFIRST") }, 271 | { WM_AFXLAST, TEXT("WM_AFXLAST") }, 272 | { WM_PENWINFIRST, TEXT("WM_PENWINFIRST") }, 273 | { WM_PENWINLAST, TEXT("WM_PENWINLAST") }, 274 | { WM_APP, TEXT("WM_APP") } 275 | }; 276 | 277 | inline std::wstring DebugHelper::FormatWindowMessage(MSG msg) 278 | { 279 | std::wstringstream wss; 280 | wss << TEXT("hwnd: ") << std::setw(8) << std::setfill(TEXT('0')) << std::hex << msg.hwnd << TEXT("; "); 281 | auto messagecode = kWindowMessageCodeMap.find(msg.message) == kWindowMessageCodeMap.end() ? TEXT("") : TEXT(" (") + kWindowMessageCodeMap.at(msg.message) + TEXT(")"); 282 | wss << TEXT("message: ") << std::setw(8) << std::setfill(TEXT('0')) << std::hex << msg.message << messagecode << TEXT("; "); 283 | wss << TEXT("wParam: ") << std::setw(8) << std::setfill(TEXT('0')) << std::hex << msg.wParam << TEXT("; "); 284 | wss << TEXT("lParam: ") << std::setw(8) << std::setfill(TEXT('0')) << std::hex << msg.lParam << TEXT("; "); 285 | wss << TEXT("time: ") << msg.time << TEXT("; "); 286 | wss << TEXT("pt: (") << msg.pt.x << TEXT(", ") << msg.pt.y << ")"; 287 | 288 | return wss.str(); 289 | } 290 | 291 | inline std::wstring DebugHelper::FormatWindowMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) 292 | { 293 | std::wstringstream wss; 294 | auto messagecode = kWindowMessageCodeMap.find(uMsg) == kWindowMessageCodeMap.end() ? TEXT("") : TEXT(" (") + kWindowMessageCodeMap.at(uMsg) + TEXT(")"); 295 | wss << TEXT("message: ") << std::setw(8) << std::setfill(TEXT('0')) << std::hex << uMsg << messagecode << TEXT("; "); 296 | wss << TEXT("wParam: ") << std::setw(8) << std::setfill(TEXT('0')) << std::hex << wParam << TEXT("; "); 297 | wss << TEXT("lParam: ") << std::setw(8) << std::setfill(TEXT('0')) << std::hex << lParam << TEXT("; "); 298 | 299 | return wss.str(); 300 | } 301 | 302 | inline std::wstring DebugHelper::FormatMouseHookMessage(int code, WPARAM wParam, LPARAM lParam) 303 | { 304 | std::wstringstream wss; 305 | wss << TEXT("code: ") << code << TEXT("; "); 306 | auto messagecode = (UINT)wParam; 307 | auto messagecodestring = kWindowMessageCodeMap.find(messagecode) == kWindowMessageCodeMap.end() ? TEXT("") : TEXT(" (") + kWindowMessageCodeMap.at(messagecode) + TEXT(")"); 308 | wss << TEXT("message code: 0x") << std::setw(8) << std::setfill(TEXT('0')) << std::hex << messagecode << messagecodestring << TEXT("; "); 309 | auto mousehookstructpointer = (PMOUSEHOOKSTRUCT)lParam; 310 | wss << TEXT("pt(") << std::dec << mousehookstructpointer->pt.x << TEXT(", ") << mousehookstructpointer->pt.y << TEXT("); "); 311 | wss << TEXT("hwnd: 0x") << std::setw(8) << std::setfill(TEXT('0')) << std::hex << PtrToInt(mousehookstructpointer->hwnd) << TEXT("; "); 312 | wss << TEXT("HitTestCode: ") << std::dec << mousehookstructpointer->wHitTestCode << TEXT("; "); 313 | 314 | return wss.str(); 315 | } 316 | 317 | inline std::wstring DebugHelper::FormatKeyboardHookMessage(int code, WPARAM wParam, LPARAM lParam) 318 | { 319 | std::wstringstream wss; 320 | wss << TEXT("code: ") << code << TEXT("; "); 321 | auto virtualkeycode = (UINT)wParam; 322 | wss << TEXT("virtual-key code: 0x") << std::setw(8) << std::setfill(TEXT('0')) << std::hex << virtualkeycode << TEXT("; "); 323 | auto flags = (UINT)lParam; 324 | wss << TEXT("flags: 0x") << std::setw(8) << std::setfill(TEXT('0')) << std::hex << flags << TEXT("; "); 325 | 326 | return wss.str(); 327 | } -------------------------------------------------------------------------------- /src/Winook.Common/MainWindowFinder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class MainWindowFinder 6 | { 7 | public: 8 | MainWindowFinder(DWORD processid) : processid_(processid) { } 9 | HWND FindMainWindow() 10 | { 11 | if (!EnumWindows(EnumWindowsCallback, (LONG_PTR)this)) 12 | { 13 | lasterror_ = GetLastError(); 14 | } 15 | 16 | return besthandle_; 17 | } 18 | DWORD lasterror() const { return lasterror_; } 19 | private: 20 | static BOOL CALLBACK EnumWindowsCallback(HWND handle, LONG_PTR mwf) 21 | { 22 | auto self = reinterpret_cast(mwf); 23 | DWORD processid{}; 24 | GetWindowThreadProcessId(handle, &processid); 25 | if (processid == self->processid_ && MainWindowFinder::IsMainWindow(handle)) 26 | { 27 | self->besthandle_ = handle; 28 | 29 | return FALSE; 30 | } 31 | 32 | return TRUE; 33 | } 34 | static BOOL IsMainWindow(HWND handle) 35 | { 36 | return GetWindow(handle, GW_OWNER) == (HWND)0 && IsWindowVisible(handle); 37 | } 38 | private: 39 | DWORD processid_{}; 40 | HWND besthandle_{ NULL }; 41 | DWORD lasterror_{}; 42 | }; -------------------------------------------------------------------------------- /src/Winook.Common/TimestampLogger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class TimestampLogger 11 | { 12 | public: 13 | TimestampLogger(const std::wstring& filepath) 14 | : TimestampLogger(filepath, FALSE) { } 15 | TimestampLogger(const std::wstring& filepath, BOOL autoflush) 16 | : logfile_(std::wofstream(filepath.c_str())) 17 | { 18 | pwritecriticalsection_ = new CRITICAL_SECTION(); 19 | if (!InitializeCriticalSectionAndSpinCount(pwritecriticalsection_, 0x00000400)) 20 | { 21 | // TODO: handle error 22 | } 23 | } 24 | ~TimestampLogger() 25 | { 26 | if (pwritecriticalsection_ != nullptr) 27 | { 28 | DeleteCriticalSection(pwritecriticalsection_); 29 | delete pwritecriticalsection_; 30 | } 31 | 32 | Close(); 33 | } 34 | void WriteLine(const std::string& line); 35 | void WriteLine(const std::wstring& line); 36 | void Close() 37 | { 38 | logfile_.close(); 39 | } 40 | static std::wstring GetTimestampString(); 41 | static std::wstring GetTimestampString(BOOL asvalidfilename); 42 | private: 43 | std::wofstream logfile_; 44 | PCRITICAL_SECTION pwritecriticalsection_{ nullptr }; 45 | }; 46 | 47 | inline void TimestampLogger::WriteLine(const std::string& line) 48 | { 49 | std::wstring wline(line.begin(), line.end()); 50 | WriteLine(wline); 51 | } 52 | 53 | inline void TimestampLogger::WriteLine(const std::wstring& line) 54 | { 55 | EnterCriticalSection(pwritecriticalsection_); 56 | 57 | SYSTEMTIME filetime; 58 | GetLocalTime(&filetime); 59 | std::wstringstream wss; 60 | wss << TEXT("[") << GetTimestampString() << TEXT("] "); 61 | logfile_ << wss.str() << line << std::endl; 62 | 63 | LeaveCriticalSection(pwritecriticalsection_); 64 | } 65 | 66 | inline std::wstring TimestampLogger::GetTimestampString() 67 | { 68 | return GetTimestampString(FALSE); 69 | } 70 | 71 | inline std::wstring TimestampLogger::GetTimestampString(BOOL asvalidfilename) 72 | { 73 | SYSTEMTIME filetime; 74 | GetLocalTime(&filetime); 75 | std::wstringstream wss; 76 | const auto timeseparator = asvalidfilename ? TEXT(".") : TEXT(":"); 77 | wss << filetime.wYear << TEXT("-"); 78 | wss << std::setw(2) << std::setfill(TEXT('0')) << filetime.wMonth << TEXT("-"); 79 | wss << std::setw(2) << std::setfill(TEXT('0')) << filetime.wDay << TEXT("T"); 80 | wss << std::setw(2) << std::setfill(TEXT('0')) << filetime.wHour << timeseparator; 81 | wss << std::setw(2) << std::setfill(TEXT('0')) << filetime.wMinute << timeseparator; 82 | wss << std::setw(2) << std::setfill(TEXT('0')) << filetime.wSecond << TEXT("."); 83 | wss << std::setw(3) << std::setfill(TEXT('0')) << filetime.wMilliseconds; 84 | 85 | return wss.str(); 86 | } -------------------------------------------------------------------------------- /src/Winook.Common/Winook.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "MainWindowFinder.h" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #if defined(__MINGW32__) && !defined(PROCESSOR_ARCHITECTURE_ARM64) 13 | #define PROCESSOR_ARCHITECTURE_ARM64 12 14 | #endif 15 | 16 | constexpr auto kProcessWaitForInputIdleTimeoutIntervalInMilliseconds = 2000; 17 | constexpr auto kPathBufferSize = 1024; 18 | 19 | #if defined(WINOOK64) 20 | const std::wstring kKeyboardHookLibName = std::wstring(TEXT("Winook.Lib.Keyboard.x64.dll")); 21 | const std::wstring kMouseHookLibName = std::wstring(TEXT("Winook.Lib.Mouse.x64.dll")); 22 | #else 23 | const std::wstring kKeyboardHookLibName = std::wstring(TEXT("Winook.Lib.Keyboard.x86.dll")); 24 | const std::wstring kMouseHookLibName = std::wstring(TEXT("Winook.Lib.Mouse.x86.dll")); 25 | #endif 26 | 27 | const std::string kKeyboardHookProcName = std::string("KeyboardHookProc"); 28 | const std::string kMouseHookProcName = std::string("MouseHookProc"); 29 | 30 | #define LOGWINOOK 1 31 | #if _DEBUG && LOGWINOOK 32 | #define LOGWINOOKPATH L"C:\\Temp\\Winook_" 33 | #include "DebugHelper.h" 34 | #include "TimestampLogger.h" 35 | #endif 36 | 37 | class Winook 38 | { 39 | public: 40 | static std::wstring GetConfigFilePath(LPCTSTR libfullpath, DWORD processid, DWORD threadid, INT hooktype); 41 | static std::wstring FindConfigFilePath(int hooktype); 42 | public: 43 | Winook(INT hooktype, INT processid, std::wstring port, std::wstring mutexguid) 44 | : hooktype_(hooktype), processid_(processid), port_(port), mutexguid_(mutexguid) 45 | #if _DEBUG && LOGWINOOK 46 | , logger_(TimestampLogger(LOGWINOOKPATH + TimestampLogger::GetTimestampString(TRUE) + L".log", TRUE)) 47 | #endif 48 | { 49 | } 50 | virtual ~Winook() 51 | { 52 | CleanUp(); 53 | } 54 | public: 55 | BOOL IsBitnessMatch(); 56 | void Hook(); 57 | void AddConfigArgument(std::wstring argument); 58 | private: 59 | void GetProcessFullPath(); 60 | void WaitForProcess(); 61 | void FindProcessMainWindowThreadId(); 62 | void GetLibPath(); 63 | void WriteConfigurationFile(); 64 | void SetupHook(); 65 | void WaitOnHostMutex(); 66 | void LogError(std::string errormessage); 67 | void HandleError(std::string errormessage); 68 | void HandleError(std::string errormessage, DWORD errorcode); 69 | void CleanUp(); 70 | private: 71 | std::vector additionalconfigargs_; 72 | INT hooktype_; 73 | INT processid_; 74 | std::wstring port_; 75 | std::wstring mutexguid_; 76 | HANDLE process_{ NULL }; 77 | std::wstring processfullpath_; 78 | DWORD threadid_{}; 79 | std::wstring hooklibpath_; 80 | HHOOK hook_{ NULL }; 81 | HANDLE mutex_{ NULL }; 82 | #if _DEBUG && LOGWINOOK 83 | TimestampLogger logger_; 84 | #endif 85 | }; 86 | 87 | inline std::wstring Winook::GetConfigFilePath(LPCTSTR libfullpath, DWORD processid, DWORD threadid, INT hooktype) 88 | { 89 | TCHAR temppath[kPathBufferSize]; 90 | GetTempPath(kPathBufferSize, temppath); 91 | TCHAR configfilepath[kPathBufferSize]; 92 | swprintf(configfilepath, sizeof(configfilepath), TEXT("%ls%ls%d%d%d"), 93 | temppath, 94 | std::regex_replace(libfullpath, std::wregex(TEXT("[\\\\]|[/]|[:]|[ ]")), TEXT("")).c_str(), 95 | processid, 96 | threadid, 97 | hooktype); 98 | 99 | return std::wstring(configfilepath); 100 | } 101 | 102 | inline std::wstring Winook::FindConfigFilePath(int hooktype) 103 | { 104 | TCHAR modulepath[kPathBufferSize]; 105 | GetModuleFileName(NULL, modulepath, kPathBufferSize); 106 | const auto configfilepath = GetConfigFilePath(modulepath, GetCurrentProcessId(), GetThreadId(GetCurrentThread()), hooktype); 107 | WIN32_FIND_DATA findfiledata; 108 | HANDLE find = FindFirstFile(configfilepath.c_str(), &findfiledata); 109 | if (find != INVALID_HANDLE_VALUE) 110 | { 111 | FindClose(find); 112 | return configfilepath; 113 | } 114 | 115 | return std::wstring(); 116 | } 117 | 118 | inline void Winook::LogError(std::string errormessage) 119 | { 120 | TCHAR temppath[kPathBufferSize]; 121 | GetTempPath(kPathBufferSize, temppath); 122 | TCHAR errorfilepath[kPathBufferSize]; 123 | swprintf(errorfilepath, sizeof(errorfilepath), TEXT("%ls%ls"), temppath, mutexguid_.c_str()); 124 | 125 | std::ofstream errorfile(errorfilepath); 126 | errorfile << errormessage; 127 | } 128 | 129 | inline BOOL Winook::IsBitnessMatch() 130 | { 131 | process_ = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processid_); 132 | if (process_ == NULL) 133 | { 134 | HandleError("OpenProcess() failed", GetLastError()); 135 | } 136 | 137 | BOOL isx64os{}; 138 | SYSTEM_INFO systeminfo; 139 | GetNativeSystemInfo(&systeminfo); 140 | switch (systeminfo.wProcessorArchitecture) 141 | { 142 | case PROCESSOR_ARCHITECTURE_AMD64: 143 | case PROCESSOR_ARCHITECTURE_ARM64: 144 | isx64os = TRUE; 145 | break; 146 | case PROCESSOR_ARCHITECTURE_ARM: 147 | case PROCESSOR_ARCHITECTURE_INTEL: 148 | break; 149 | default: 150 | HandleError("Unsupported processor architecture"); 151 | break; 152 | } 153 | 154 | BOOL iswow64process; 155 | if (!IsWow64Process(process_, &iswow64process)) 156 | { 157 | HandleError("IsWow64Process() failed", GetLastError()); 158 | } 159 | 160 | #if defined WINOOK64 161 | const auto isx64binary = TRUE; 162 | #else 163 | const auto isx64binary = FALSE; 164 | #endif 165 | 166 | if (isx64binary) 167 | { 168 | if (iswow64process) 169 | { 170 | // Let the 32-bit version handle the hook 171 | return FALSE; 172 | } 173 | } 174 | else if (isx64os && !iswow64process) 175 | { 176 | // Let the 64-bit version handle the hook 177 | return FALSE; 178 | } 179 | 180 | return TRUE; 181 | } 182 | 183 | inline void Winook::Hook() 184 | { 185 | GetProcessFullPath(); 186 | WaitForProcess(); 187 | FindProcessMainWindowThreadId(); 188 | GetLibPath(); 189 | WriteConfigurationFile(); 190 | SetupHook(); 191 | WaitOnHostMutex(); 192 | } 193 | 194 | inline void Winook::AddConfigArgument(std::wstring argument) 195 | { 196 | additionalconfigargs_.push_back(argument); 197 | } 198 | 199 | inline void Winook::GetProcessFullPath() 200 | { 201 | TCHAR processfullpath[kPathBufferSize]; 202 | DWORD processfullpathsize = sizeof(processfullpath); 203 | if (!QueryFullProcessImageName(process_, 0, processfullpath, &processfullpathsize)) 204 | { 205 | HandleError("QueryFullProcessImageName() failed", GetLastError()); 206 | } 207 | 208 | processfullpath_ = processfullpath; 209 | } 210 | 211 | inline void Winook::WaitForProcess() 212 | { 213 | // Ensure target process' main window is ready 214 | const auto waitresult = WaitForInputIdle(process_, kProcessWaitForInputIdleTimeoutIntervalInMilliseconds); 215 | CloseHandle(process_); 216 | process_ = NULL; 217 | 218 | if (waitresult == WAIT_FAILED) 219 | { 220 | HandleError("WaitForInputIdle() failed", GetLastError()); 221 | } 222 | } 223 | 224 | inline void Winook::FindProcessMainWindowThreadId() 225 | { 226 | MainWindowFinder mainwindowfinder(processid_); 227 | const auto mainwindowhandle = mainwindowfinder.FindMainWindow(); 228 | if (mainwindowhandle == NULL) 229 | { 230 | HandleError("FindMainWindow() failed", mainwindowfinder.lasterror()); 231 | } 232 | 233 | threadid_ = GetWindowThreadProcessId(mainwindowhandle, NULL); 234 | if (threadid_ == 0) 235 | { 236 | HandleError("GetWindowThreadProcessId() failed", GetLastError()); 237 | } 238 | } 239 | 240 | inline void Winook::GetLibPath() 241 | { 242 | TCHAR modulepath[kPathBufferSize]; 243 | if (!GetModuleFileName(NULL, modulepath, kPathBufferSize)) 244 | { 245 | HandleError("GetModuleFileName() failed", GetLastError()); 246 | } 247 | 248 | const auto modulepathtmp = std::wstring(modulepath); 249 | const auto modulefolder = modulepathtmp.substr(0, modulepathtmp.find_last_of(TEXT("\\")) + 1); 250 | TCHAR libpath[kPathBufferSize]; 251 | std::wstring libname; 252 | if (hooktype_ == WH_KEYBOARD) 253 | { 254 | libname = kKeyboardHookLibName; 255 | } 256 | else if (hooktype_ == WH_MOUSE) 257 | { 258 | libname = kMouseHookLibName; 259 | } 260 | else 261 | { 262 | HandleError("Invalid hook type"); 263 | } 264 | 265 | swprintf(libpath, sizeof(libpath), TEXT("%ls%ls"), modulefolder.c_str(), libname.c_str()); 266 | 267 | hooklibpath_ = libpath; 268 | } 269 | 270 | inline void Winook::WriteConfigurationFile() 271 | { 272 | const auto configfilepath = Winook::GetConfigFilePath(processfullpath_.c_str(), processid_, threadid_, hooktype_); 273 | std::wofstream configfile(configfilepath.c_str()); 274 | configfile << port_ << std::endl; 275 | for (size_t i = 0; i < additionalconfigargs_.size(); i++) 276 | { 277 | configfile << additionalconfigargs_[i] << std::endl; 278 | } 279 | } 280 | 281 | inline void Winook::SetupHook() 282 | { 283 | std::string hookprocname; 284 | if (hooktype_ == WH_KEYBOARD) 285 | { 286 | hookprocname = kKeyboardHookProcName; 287 | } 288 | else if (hooktype_ == WH_MOUSE) 289 | { 290 | hookprocname = kMouseHookProcName; 291 | } 292 | else 293 | { 294 | HandleError("Unsupported hook type"); 295 | } 296 | 297 | const auto hooklib = LoadLibrary(hooklibpath_.c_str()); 298 | if (hooklib == NULL) 299 | { 300 | HandleError("LoadLibrary() failed", GetLastError()); 301 | } 302 | 303 | 304 | const auto hookproc = (HOOKPROC)GetProcAddress(hooklib, hookprocname.c_str()); 305 | if (hookproc == NULL) 306 | { 307 | HandleError("GetProcAddress() failed", GetLastError()); 308 | } 309 | 310 | hook_ = SetWindowsHookEx(hooktype_, hookproc, hooklib, threadid_); 311 | if (hook_ == NULL) 312 | { 313 | HandleError("SetWindowsHookEx() failed", GetLastError()); 314 | } 315 | 316 | if (!FreeLibrary(hooklib)) 317 | { 318 | HandleError("FreeLibrary() failed", GetLastError()); 319 | } 320 | } 321 | 322 | inline void Winook::WaitOnHostMutex() 323 | { 324 | TCHAR mutexname[256]; 325 | swprintf(mutexname, sizeof(mutexname), TEXT("Global\\%ls"), mutexguid_.c_str()); 326 | mutex_ = OpenMutex(SYNCHRONIZE, FALSE, mutexname); 327 | if (mutex_ == NULL) 328 | { 329 | HandleError("OpenMutex() failed", GetLastError()); 330 | } 331 | 332 | auto event = WaitForSingleObject(mutex_, INFINITE); 333 | if (event == WAIT_FAILED) 334 | { 335 | HandleError("WaitForSingleObject() failed", GetLastError()); 336 | } 337 | } 338 | 339 | inline void Winook::HandleError(std::string errormessage) 340 | { 341 | HandleError(errormessage, 0); 342 | } 343 | 344 | inline void Winook::HandleError(std::string errormessage, DWORD errorcode) 345 | { 346 | std::stringstream ss; 347 | ss << errormessage; 348 | if (errorcode > 0) 349 | { 350 | ss << " (0x" << std::hex << std::uppercase << errorcode << ")"; 351 | } 352 | 353 | const auto formattederrormessage = ss.str(); 354 | LogError(formattederrormessage); 355 | 356 | throw std::runtime_error(formattederrormessage); 357 | } 358 | 359 | inline void Winook::CleanUp() 360 | { 361 | DWORD exitCode{}; 362 | if (process_ != NULL) 363 | { 364 | if (!GetExitCodeProcess(process_, &exitCode)) 365 | { 366 | HandleError("GetExitCodeProcess() failed", GetLastError()); 367 | } 368 | 369 | CloseHandle(process_); 370 | } 371 | 372 | if (mutex_ != NULL) 373 | { 374 | CloseHandle(mutex_); 375 | } 376 | 377 | if (hook_ != NULL && exitCode == STILL_ACTIVE) 378 | { 379 | if (!UnhookWindowsHookEx(hook_)) 380 | { 381 | HandleError("UnhookWindowsHookEx() failed", GetLastError()); 382 | } 383 | } 384 | } -------------------------------------------------------------------------------- /src/Winook.Lib.Host/Host.cpp: -------------------------------------------------------------------------------- 1 | #define STRICT 2 | #define WIN32_LEAN_AND_MEAN 3 | 4 | #include "Winook.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #if !defined(__MINGW32__) 12 | #pragma comment (lib, "shlwapi.lib") 13 | #endif 14 | 15 | #define LOGWINOOKLIBHOST 0 16 | #if _DEBUG && LOGWINOOKLIBHOST 17 | #define LOGWINOOKLIBHOSTPATH TEXT("C:\\Temp\\WinookLibHost_") 18 | #include "DebugHelper.h" 19 | #include "TimestampLogger.h" 20 | TimestampLogger Logger(LOGWINOOKLIBHOSTPATH + TimestampLogger::GetTimestampString(TRUE) + TEXT(".log"), TRUE); 21 | #endif 22 | 23 | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) 24 | { 25 | try 26 | { 27 | INT argscount; 28 | const auto args = CommandLineToArgvW(GetCommandLine(), &argscount); 29 | 30 | if (argscount < 5) 31 | { 32 | return EXIT_FAILURE; 33 | } 34 | 35 | const auto hooktype = std::stoi(args[1]); 36 | const auto port = std::wstring(args[2]); 37 | const auto processid = std::stoi(args[3]); 38 | const auto mutexguid = std::wstring(args[4]); 39 | 40 | Winook winook(hooktype, processid, port, mutexguid); 41 | 42 | if (argscount > 5) 43 | { 44 | for (INT i = 5; i < argscount; i++) 45 | { 46 | winook.AddConfigArgument(args[i]); 47 | } 48 | } 49 | 50 | LocalFree(args); 51 | 52 | if (winook.IsBitnessMatch()) 53 | { 54 | winook.Hook(); 55 | } 56 | 57 | return EXIT_SUCCESS; 58 | } 59 | catch (const std::exception&) 60 | { 61 | return EXIT_FAILURE; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Winook.Lib.Host/Makefile: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | 3 | SYS = $(MSYSTEM) 4 | DUMPMACHINE = $(shell $(CC) -dumpmachine) 5 | 6 | WARNS = -Wall 7 | 8 | ifeq ($(DUMPMACHINE),x86_64-w64-mingw32) 9 | EXE = Winook.Lib.Host.x64.exe 10 | else 11 | EXE = Winook.Lib.Host.x86.exe 12 | endif 13 | 14 | WINDRES = windres 15 | 16 | OBJECTS = obj/Host.o obj/Winook.Lib.Host.o 17 | 18 | ifeq ($(DUMPMACHINE),x86_64-w64-mingw32) 19 | CFLAGS = -m64 -std=c++17 -I../Winook.Common -DWINVER=0x0603 -D_WIN32_WINNT=0x0603 -DUNICODE -D_UNICODE -DWINOOK64 -D_GLIBCXX_ASSERTIONS -O2 $(WARNS) -fmessage-length=0 -fasynchronous-unwind-tables 20 | else 21 | CFLAGS = -std=c++17 -I../Winook.Common -DWINVER=0x0603 -D_WIN32_WINNT=0x0603 -DUNICODE -D_UNICODE -D_GLIBCXX_ASSERTIONS -O2 $(WARNS) -fmessage-length=0 -fasynchronous-unwind-tables 22 | endif 23 | 24 | ifneq ($(config),release) 25 | CFLAGS += -D_DEBUG -g 26 | LDFLAGS = -static -mwindows 27 | else 28 | LDFLAGS = -s -static -mwindows 29 | endif 30 | 31 | .PHONY: all clean 32 | 33 | all: bin/$(EXE) 34 | 35 | clean: 36 | ifneq (,$(findstring MINGW,$(SYS))) 37 | @if [ -d "bin" ]; then rm -r bin; fi 38 | @if [ -d "obj" ]; then rm -r obj; fi 39 | else 40 | @if exist bin\* del /f /s /q bin 1>nul & rd /s /q bin 41 | @if exist obj\* del /f /s /q obj 1>nul & rd /s /q obj 42 | endif 43 | 44 | bin obj: 45 | ifneq (,$(findstring MINGW,$(SYS))) 46 | @if [ ! -d "bin" ]; then mkdir bin; fi 47 | @if [ ! -d "obj" ]; then mkdir obj; fi 48 | else 49 | @if not exist "$@" mkdir "$@" 50 | endif 51 | 52 | obj/Winook.Lib.Host.o: Winook.Lib.Host.rc 53 | $(WINDRES) --language 0x0409 Winook.Lib.Host.rc -o obj/Winook.Lib.Host.o 54 | 55 | obj/%.o: %.cpp | obj 56 | $(CC) $(CFLAGS) -c "$<" -o "$@" 57 | 58 | bin/$(EXE): $(OBJECTS) | bin 59 | $(CC) -o "$@" $(OBJECTS) $(LDFLAGS) 60 | -------------------------------------------------------------------------------- /src/Winook.Lib.Host/Resource.h: -------------------------------------------------------------------------------- 1 | #define IDI_ICON 101 2 | -------------------------------------------------------------------------------- /src/Winook.Lib.Host/Winook.Lib.Host.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macote/Winook/4704fa9676b0d98c881c759af72fe88585c74287/src/Winook.Lib.Host/Winook.Lib.Host.ico -------------------------------------------------------------------------------- /src/Winook.Lib.Host/Winook.Lib.Host.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macote/Winook/4704fa9676b0d98c881c759af72fe88585c74287/src/Winook.Lib.Host/Winook.Lib.Host.rc -------------------------------------------------------------------------------- /src/Winook.Lib.Host/Winook.Lib.Host.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Debug 10 | x64 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {69306344-BD77-4AF6-8890-BF001D8EA7EE} 23 | Win32Proj 24 | 25 | 26 | Winook.Lib.Host 27 | 10.0 28 | 29 | 30 | 31 | Application 32 | true 33 | v142 34 | Unicode 35 | 36 | 37 | Application 38 | true 39 | v142 40 | Unicode 41 | 42 | 43 | Application 44 | false 45 | v142 46 | true 47 | Unicode 48 | 49 | 50 | Application 51 | false 52 | v142 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | false 75 | bin\$(Platform)\$(Configuration)\ 76 | obj\$(Platform)\$(Configuration)\ 77 | $(ProjectName).x86 78 | 79 | 80 | true 81 | false 82 | bin\$(Platform)\$(Configuration)\ 83 | obj\$(Platform)\$(Configuration)\ 84 | $(ProjectName).x64 85 | 86 | 87 | false 88 | false 89 | bin\$(Platform)\$(Configuration)\ 90 | obj\$(Platform)\$(Configuration)\ 91 | $(ProjectName).x86 92 | 93 | 94 | false 95 | false 96 | bin\$(Platform)\$(Configuration)\ 97 | obj\$(Platform)\$(Configuration)\ 98 | $(ProjectName).x64 99 | 100 | 101 | 102 | 103 | 104 | Level3 105 | Disabled 106 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 107 | $(SolutionDir)src\Winook.Common 108 | stdcpp17 109 | 110 | 111 | Windows 112 | true 113 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;%(AdditionalDependencies) 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | Level3 123 | Disabled 124 | WIN32;WINOOK64;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 125 | $(SolutionDir)src\Winook.Common 126 | stdcpp17 127 | 128 | 129 | Windows 130 | true 131 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;%(AdditionalDependencies) 132 | 133 | 134 | 135 | 136 | 137 | 138 | Level3 139 | 140 | 141 | MaxSpeed 142 | true 143 | true 144 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 145 | MultiThreaded 146 | $(SolutionDir)src\Winook.Common 147 | stdcpp17 148 | 149 | 150 | Windows 151 | true 152 | true 153 | true 154 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;shlwapi.lib;%(AdditionalDependencies) 155 | 156 | 157 | 158 | 159 | Level3 160 | 161 | 162 | MaxSpeed 163 | true 164 | true 165 | WIN32;WINOOK64;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 166 | MultiThreaded 167 | $(SolutionDir)src\Winook.Common 168 | stdcpp17 169 | 170 | 171 | Windows 172 | true 173 | true 174 | true 175 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;shlwapi.lib;%(AdditionalDependencies) 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | -------------------------------------------------------------------------------- /src/Winook.Lib.Host/Winook.Lib.Host.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | 35 | 36 | Source Files 37 | 38 | 39 | 40 | 41 | Resource Files 42 | 43 | 44 | 45 | 46 | Resource Files 47 | 48 | 49 | 50 | 51 | Resource Files 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/Winook.Lib.Host/application.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | Host application for Winook.Lib 6 | -------------------------------------------------------------------------------- /src/Winook.Lib/Lib.cpp: -------------------------------------------------------------------------------- 1 | #include "Lib.h" 2 | #include "Winook.h" 3 | #include "MessageSender.h" 4 | 5 | #if _DEBUG 6 | #include 7 | #endif 8 | #include 9 | #include 10 | #include 11 | 12 | #if !defined(__MINGW32__) 13 | #pragma comment (lib, "shlwapi.lib") 14 | #endif 15 | 16 | #define LOGWINOOKLIB 1 17 | #if _DEBUG && LOGWINOOKLIB 18 | #define LOGWINOOKLIBPATH TEXT("C:\\Temp\\WinookLibHookProc_") 19 | #include "DebugHelper.h" 20 | #include "TimestampLogger.h" 21 | TimestampLogger Logger = TimestampLogger(LOGWINOOKLIBPATH + TimestampLogger::GetTimestampString(TRUE) + TEXT(".log"), TRUE); 22 | #endif 23 | 24 | asio::io_context io_context; 25 | MessageSender messagesender(io_context); 26 | WORD mousemessagetypes; 27 | 28 | BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID lpReserved) 29 | { 30 | switch (fdwReason) 31 | { 32 | case DLL_PROCESS_ATTACH: 33 | #if _DEBUG 34 | LogDllMain(hinst, TEXT("DLL_PROCESS_ATTACH")); 35 | #endif 36 | if (!Initialize(hinst)) 37 | { 38 | return FALSE; 39 | } 40 | 41 | break; 42 | 43 | case DLL_THREAD_ATTACH: 44 | #if _DEBUG 45 | LogDllMain(hinst, TEXT("DLL_THREAD_ATTACH")); 46 | #endif 47 | break; 48 | 49 | case DLL_THREAD_DETACH: 50 | #if _DEBUG 51 | LogDllMain(hinst, TEXT("DLL_THREAD_DETACH")); 52 | #endif 53 | break; 54 | 55 | case DLL_PROCESS_DETACH: 56 | #if _DEBUG 57 | LogDllMain(hinst, TEXT("DLL_PROCESS_DETACH")); 58 | #endif 59 | break; 60 | } 61 | 62 | return TRUE; 63 | } 64 | 65 | BOOL Initialize(HINSTANCE hinst) 66 | { 67 | // Look for initialization file stored in %TEMP% 68 | 69 | HMODULE module; 70 | if (!GetModuleHandleEx( 71 | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, 72 | (LPCTSTR)Initialize, 73 | &module)) 74 | { 75 | return FALSE; 76 | } 77 | 78 | TCHAR dllfilepath[kPathBufferSize]; 79 | GetModuleFileName(module, dllfilepath, kPathBufferSize); 80 | int hooktype{}; 81 | if (StrStrI(dllfilepath, kKeyboardHookLibName.c_str()) != NULL) 82 | { 83 | hooktype = WH_KEYBOARD; 84 | } 85 | else if (StrStrI(dllfilepath, kMouseHookLibName.c_str()) != NULL) 86 | { 87 | hooktype = WH_MOUSE; 88 | } 89 | else 90 | { 91 | return FALSE; // Unsupported hook type 92 | } 93 | 94 | const auto configfilepath = Winook::FindConfigFilePath(hooktype); 95 | if (configfilepath.empty()) 96 | { 97 | return TRUE; // Assume out-of-context initialization 98 | } 99 | 100 | std::ifstream configfile(configfilepath.c_str()); 101 | std::string port; 102 | configfile >> port; 103 | if (hooktype == WH_MOUSE) 104 | { 105 | configfile >> mousemessagetypes; 106 | } 107 | 108 | configfile.close(); 109 | DeleteFile(configfilepath.c_str()); 110 | messagesender.Connect(port); 111 | 112 | return TRUE; 113 | } 114 | 115 | WORD GetShiftCtrlAltState() 116 | { 117 | const auto lshift = GetKeyState(VK_LSHIFT); 118 | const auto lcontrol = GetKeyState(VK_LCONTROL); 119 | const auto lalt = GetKeyState(VK_LMENU); 120 | const auto rshift = GetKeyState(VK_RSHIFT); 121 | const auto rcontrol = GetKeyState(VK_RCONTROL); 122 | const auto ralt = GetKeyState(VK_RMENU); 123 | return (lshift < 0) << kLeftShiftBitIndex 124 | | (lcontrol < 0) << kLeftControlBitIndex 125 | | (lalt < 0) << kLeftAltBitIndex 126 | | (rshift < 0) << kRightShiftBitIndex 127 | | (rcontrol < 0) << kRightControlBitIndex 128 | | (ralt < 0) << kRightAltBitIndex 129 | | (lshift < 0 || rshift < 0) << kShiftBitIndex 130 | | (lcontrol < 0 || rcontrol < 0) << kControlBitIndex 131 | | (lalt < 0 || ralt < 0) << kAltBitIndex; 132 | } 133 | 134 | LRESULT CALLBACK KeyboardHookProc(int code, WPARAM wParam, LPARAM lParam) 135 | { 136 | #if _DEBUG 137 | LogDll(DebugHelper::FormatKeyboardHookMessage(code, wParam, lParam)); 138 | #endif 139 | if (code == HC_ACTION) 140 | { 141 | HookKeyboardMessage hkm{}; 142 | hkm.keyCode = (WORD)wParam; 143 | hkm.modifiers = GetShiftCtrlAltState(); 144 | hkm.flags = (DWORD)lParam; 145 | messagesender.SendMessage(&hkm, sizeof(HookKeyboardMessage)); 146 | } 147 | 148 | return CallNextHookEx(NULL, code, wParam, lParam); 149 | } 150 | 151 | LRESULT CALLBACK MouseHookProc(int code, WPARAM wParam, LPARAM lParam) 152 | { 153 | #if _DEBUG 154 | LogDll(DebugHelper::FormatMouseHookMessage(code, wParam, lParam)); 155 | #endif 156 | if (code == HC_ACTION) 157 | { 158 | DWORD messagecode = (DWORD)wParam; 159 | WORD messagetype = (messagecode == WM_NCHITTEST 160 | || messagecode == WM_NCMOUSEHOVER 161 | || messagecode == WM_NCMOUSELEAVE) << 5 162 | | (messagecode == WM_NCMOUSEMOVE) << 4 163 | | (messagecode == WM_NCLBUTTONDOWN 164 | || messagecode == WM_NCLBUTTONUP 165 | || messagecode == WM_NCLBUTTONDBLCLK 166 | || messagecode == WM_NCRBUTTONDOWN 167 | || messagecode == WM_NCRBUTTONUP 168 | || messagecode == WM_NCRBUTTONDBLCLK 169 | || messagecode == WM_NCMBUTTONDOWN 170 | || messagecode == WM_NCMBUTTONUP 171 | || messagecode == WM_NCMBUTTONDBLCLK 172 | || messagecode == WM_NCXBUTTONDOWN 173 | || messagecode == WM_NCXBUTTONUP 174 | || messagecode == WM_NCXBUTTONDBLCLK) << 3 175 | | (messagecode == WM_MOUSEACTIVATE 176 | || messagecode == WM_MOUSEWHEEL 177 | || messagecode == WM_MOUSEHWHEEL 178 | || messagecode == WM_CAPTURECHANGED 179 | || messagecode == WM_MOUSEHOVER 180 | || messagecode == WM_MOUSELEAVE) << 2 181 | | (messagecode == WM_MOUSEMOVE) << 1 182 | | (messagecode == WM_LBUTTONDOWN 183 | || messagecode == WM_LBUTTONUP 184 | || messagecode == WM_LBUTTONDBLCLK 185 | || messagecode == WM_RBUTTONDOWN 186 | || messagecode == WM_RBUTTONUP 187 | || messagecode == WM_RBUTTONDBLCLK 188 | || messagecode == WM_MBUTTONDOWN 189 | || messagecode == WM_MBUTTONUP 190 | || messagecode == WM_MBUTTONDBLCLK 191 | || messagecode == WM_XBUTTONDOWN 192 | || messagecode == WM_XBUTTONUP 193 | || messagecode == WM_XBUTTONDBLCLK); 194 | #if _DEBUG 195 | std::bitset<16> mousemessagetypesbits(mousemessagetypes); 196 | std::bitset<16> messagetypebits(messagetype); 197 | LogDll(std::string("mousemessagetypesbits: ") + mousemessagetypesbits.to_string()); 198 | LogDll(std::string("messagetypebits: ") + messagetypebits.to_string()); 199 | #endif 200 | if (mousemessagetypes == 0 || (mousemessagetypes & messagetype) > 0) 201 | { 202 | HookMouseMessage hmm{}; 203 | hmm.messageCode = messagecode; 204 | hmm.modifiers = GetShiftCtrlAltState(); 205 | if (wParam == WM_MOUSEWHEEL 206 | || wParam == WM_XBUTTONDOWN 207 | || wParam == WM_XBUTTONUP 208 | || wParam == WM_XBUTTONDBLCLK 209 | || wParam == WM_NCXBUTTONDOWN 210 | || wParam == WM_NCXBUTTONUP 211 | || wParam == WM_NCXBUTTONDBLCLK) 212 | { 213 | auto pmhsx = (PMOUSEHOOKSTRUCTEX)lParam; 214 | hmm.pointX = pmhsx->pt.x; 215 | hmm.pointY = pmhsx->pt.y; 216 | hmm.hwnd = (DWORD)PtrToInt(pmhsx->hwnd); 217 | hmm.hitTestCode = (DWORD)pmhsx->wHitTestCode; 218 | hmm.extra = HIWORD(pmhsx->mouseData); 219 | } 220 | else 221 | { 222 | auto pmhs = (PMOUSEHOOKSTRUCT)lParam; 223 | hmm.pointX = pmhs->pt.x; 224 | hmm.pointY = pmhs->pt.y; 225 | hmm.hwnd = (DWORD)PtrToInt(pmhs->hwnd); 226 | hmm.hitTestCode = (DWORD)pmhs->wHitTestCode; 227 | } 228 | 229 | messagesender.SendMessage(&hmm, sizeof(HookMouseMessage)); 230 | } 231 | } 232 | 233 | return CallNextHookEx(NULL, code, wParam, lParam); 234 | } 235 | 236 | #if _DEBUG 237 | void LogDll(std::string message) 238 | { 239 | #if LOGWINOOKLIB 240 | Logger.WriteLine(message); 241 | #endif 242 | } 243 | 244 | void LogDll(std::wstring message) 245 | { 246 | #if LOGWINOOKLIB 247 | Logger.WriteLine(message); 248 | #endif 249 | } 250 | 251 | void LogDllMain(HINSTANCE hinst, std::wstring reason) 252 | { 253 | #if LOGWINOOKLIB 254 | std::wstringstream wss; 255 | wss << std::setw(16) << std::setfill(L'0') << std::hex << hinst; 256 | TCHAR procInfo[256]; 257 | swprintf(procInfo, sizeof(procInfo), TEXT("Instance: %lx; Reason: %ls; ProcessId: %d; ThreadId: %d"), 258 | PtrToLong(hinst), reason.c_str(), GetCurrentProcessId(), GetThreadId(GetCurrentThread())); 259 | Logger.WriteLine(procInfo); 260 | #endif 261 | } 262 | #endif -------------------------------------------------------------------------------- /src/Winook.Lib/Lib.def: -------------------------------------------------------------------------------- 1 | LIBRARY 2 | EXPORTS 3 | KeyboardHookProc @1 4 | MouseHookProc @2 -------------------------------------------------------------------------------- /src/Winook.Lib/Lib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define STRICT 4 | #define WIN32_LEAN_AND_MEAN 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | constexpr auto kLeftShiftBitIndex = 8; 12 | constexpr auto kLeftControlBitIndex = 7; 13 | constexpr auto kLeftAltBitIndex = 6; 14 | constexpr auto kRightShiftBitIndex = 5; 15 | constexpr auto kRightControlBitIndex = 4; 16 | constexpr auto kRightAltBitIndex = 3; 17 | constexpr auto kShiftBitIndex = 2; 18 | constexpr auto kControlBitIndex = 1; 19 | constexpr auto kAltBitIndex = 0; 20 | 21 | struct HookMouseMessage 22 | { 23 | DWORD messageCode; 24 | DWORD pointX; 25 | DWORD pointY; 26 | DWORD hwnd; 27 | DWORD hitTestCode; 28 | DWORD extra; 29 | DWORD modifiers; 30 | }; 31 | 32 | struct HookKeyboardMessage 33 | { 34 | WORD keyCode; 35 | WORD modifiers; 36 | DWORD flags; 37 | }; 38 | 39 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); 40 | 41 | BOOL Initialize(HINSTANCE hinst); 42 | WORD GetShiftCtrlAltState(); 43 | 44 | #if _DEBUG 45 | void LogDll(std::string message); 46 | void LogDll(std::wstring message); 47 | void LogDllMain(HINSTANCE hinst, std::wstring reason); 48 | #endif 49 | 50 | extern "C" __declspec(dllexport) LRESULT CALLBACK KeyboardHookProc(int code, WPARAM wParam, LPARAM lParam); 51 | 52 | extern "C" __declspec(dllexport) LRESULT CALLBACK MouseHookProc(int code, WPARAM wParam, LPARAM lParam); -------------------------------------------------------------------------------- /src/Winook.Lib/Makefile: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | 3 | SYS = $(MSYSTEM) 4 | DUMPMACHINE = $(shell $(CC) -dumpmachine) 5 | 6 | WARNS = -Wall 7 | 8 | ifeq ($(DUMPMACHINE),x86_64-w64-mingw32) 9 | DLL = Winook.Lib.Keyboard.x64.dll bin/Winook.Lib.Mouse.x64.dll 10 | else 11 | DLL = Winook.Lib.Keyboard.x86.dll bin/Winook.Lib.Mouse.x86.dll 12 | endif 13 | 14 | WINDRES = windres 15 | 16 | OBJECTS = obj/Lib.o obj/Winook.Lib.o 17 | 18 | ifeq ($(DUMPMACHINE),x86_64-w64-mingw32) 19 | CFLAGS = -m64 -std=c++17 -I../Winook.Common -I../../3rd/asio/asio/include -DWINVER=0x0603 -D_WIN32_WINNT=0x0603 -DUNICODE -D_UNICODE -DWINOOK64 -D_GLIBCXX_ASSERTIONS -DASIO_STANDALONE -O2 $(WARNS) -fmessage-length=0 -fasynchronous-unwind-tables 20 | else 21 | CFLAGS = -std=c++17 -I../Winook.Common -I../../3rd/asio/asio/include -DWINVER=0x0603 -D_WIN32_WINNT=0x0603 -DUNICODE -D_UNICODE -D_GLIBCXX_ASSERTIONS -DASIO_STANDALONE -O2 $(WARNS) -fmessage-length=0 -fasynchronous-unwind-tables 22 | endif 23 | 24 | ifneq ($(config),release) 25 | CFLAGS += -D_DEBUG -g 26 | LDFLAGS = -shared -static -Wl,--kill-at,--subsystem,windows -lws2_32 27 | else 28 | LDFLAGS = -s -shared -static -Wl,--kill-at,--subsystem,windows -lws2_32 29 | endif 30 | 31 | .PHONY: all clean 32 | 33 | all: bin/$(DLL) 34 | 35 | clean: 36 | ifneq (,$(findstring MINGW,$(SYS))) 37 | @if [ -d "bin" ]; then rm -r bin; fi 38 | @if [ -d "obj" ]; then rm -r obj; fi 39 | else 40 | @if exist bin\* del /f /s /q bin 1>nul & rd /s /q bin 41 | @if exist obj\* del /f /s /q obj 1>nul & rd /s /q obj 42 | endif 43 | 44 | bin obj: 45 | ifneq (,$(findstring MINGW,$(SYS))) 46 | @if [ ! -d "bin" ]; then mkdir bin; fi 47 | @if [ ! -d "obj" ]; then mkdir obj; fi 48 | else 49 | @if not exist "$@" mkdir "$@" 50 | endif 51 | 52 | obj/Winook.Lib.o: Winook.Lib.rc 53 | $(WINDRES) --language 0x0409 Winook.Lib.rc -o obj/Winook.Lib.o 54 | 55 | obj/%.o: %.cpp | obj 56 | $(CC) $(CFLAGS) -c "$<" -o "$@" 57 | 58 | bin/$(DLL): $(OBJECTS) | bin 59 | $(CC) -o "$@" $(OBJECTS) $(LDFLAGS) 60 | -------------------------------------------------------------------------------- /src/Winook.Lib/MessageSender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "asio.hpp" 6 | 7 | #define LOGWINOOKMESSAGESENDER 1 8 | #if _DEBUG && LOGWINOOKMESSAGESENDER 9 | #define LOGWINOOKMESSAGESENDERPATH TEXT("C:\\Temp\\WinookMessageSender_") 10 | #include "DebugHelper.h" 11 | #include "TimestampLogger.h" 12 | TimestampLogger MessageSenderLogger = TimestampLogger(LOGWINOOKMESSAGESENDERPATH + TimestampLogger::GetTimestampString(TRUE) + TEXT(".log"), TRUE); 13 | #endif 14 | 15 | using asio::ip::tcp; 16 | 17 | class MessageSender 18 | { 19 | public: 20 | MessageSender(asio::io_context& io_context) 21 | : io_context_(io_context), socket_(tcp::socket(io_context)) 22 | { 23 | } 24 | ~MessageSender() 25 | { 26 | io_context_.run(); 27 | } 28 | void Connect(std::string port); 29 | void SendMessage(void* data, size_t bytecount); 30 | private: 31 | asio::io_context& io_context_; 32 | tcp::socket socket_; 33 | asio::error_code ignorederror_; 34 | }; 35 | 36 | inline void MessageSender::Connect(std::string port) 37 | { 38 | tcp::resolver resolver(io_context_); 39 | auto endpoints = resolver.resolve("127.0.0.1", port); 40 | std::error_code error; 41 | asio::connect( 42 | socket_, 43 | endpoints, 44 | error); 45 | if (error) 46 | { 47 | // TODO: handle error 48 | } 49 | } 50 | 51 | inline void MessageSender::SendMessage(void* data, size_t bytecount) 52 | { 53 | asio::async_write( 54 | socket_, 55 | asio::buffer(data, bytecount), 56 | [this](std::error_code ec, std::size_t) 57 | { 58 | if (ec) 59 | { 60 | // TODO: handle error 61 | 62 | socket_.close(); 63 | } 64 | }); 65 | } -------------------------------------------------------------------------------- /src/Winook.Lib/Winook.Lib.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macote/Winook/4704fa9676b0d98c881c759af72fe88585c74287/src/Winook.Lib/Winook.Lib.rc -------------------------------------------------------------------------------- /src/Winook.Lib/Winook.Lib.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {5FB052A8-82FA-46E9-A9DF-078BF725A548} 24 | 25 | 26 | 10.0 27 | Winook.Lib 28 | 29 | 30 | 31 | DynamicLibrary 32 | true 33 | v142 34 | Unicode 35 | 36 | 37 | DynamicLibrary 38 | false 39 | v142 40 | true 41 | Unicode 42 | 43 | 44 | DynamicLibrary 45 | true 46 | v142 47 | Unicode 48 | 49 | 50 | DynamicLibrary 51 | false 52 | v142 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | bin\$(Platform)\$(Configuration)\ 77 | obj\$(Platform)\$(Configuration)\ 78 | $(ProjectName).x86 79 | 80 | 81 | true 82 | bin\$(Platform)\$(Configuration)\ 83 | obj\$(Platform)\$(Configuration)\ 84 | $(ProjectName).x64 85 | 86 | 87 | false 88 | bin\$(Platform)\$(Configuration)\ 89 | obj\$(Platform)\$(Configuration)\ 90 | $(ProjectName).x86 91 | 92 | 93 | false 94 | bin\$(Platform)\$(Configuration)\ 95 | obj\$(Platform)\$(Configuration)\ 96 | $(ProjectName).x64 97 | 98 | 99 | 100 | Level3 101 | true 102 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 103 | true 104 | $(SolutionDir)3rd\asio\asio\include;$(SolutionDir)src\Winook.Common;%(AdditionalIncludeDirectories) 105 | stdcpp17 106 | 107 | 108 | Windows 109 | true 110 | Lib.def 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;WINOOK64;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | $(SolutionDir)3rd\asio\asio\include;$(SolutionDir)src\Winook.Common;%(AdditionalIncludeDirectories) 120 | stdcpp17 121 | 122 | 123 | Windows 124 | true 125 | Lib.def 126 | 127 | 128 | 129 | 130 | Level3 131 | true 132 | true 133 | true 134 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 135 | true 136 | $(SolutionDir)3rd\asio\asio\include;$(SolutionDir)src\Winook.Common;%(AdditionalIncludeDirectories) 137 | stdcpp17 138 | 139 | 140 | Windows 141 | true 142 | true 143 | true 144 | Lib.def 145 | 146 | 147 | 148 | 149 | Level3 150 | true 151 | true 152 | true 153 | NDEBUG;WINOOK64;_CONSOLE;%(PreprocessorDefinitions) 154 | true 155 | $(SolutionDir)3rd\asio\asio\include;$(SolutionDir)src\Winook.Common;%(AdditionalIncludeDirectories) 156 | stdcpp17 157 | 158 | 159 | Windows 160 | true 161 | true 162 | true 163 | Lib.def 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /src/Winook.Lib/Winook.Lib.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | Header Files 25 | 26 | 27 | Header Files 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | 40 | 41 | Source Files 42 | 43 | 44 | 45 | 46 | 47 | Resource Files 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/Winook/Helper.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | using System; 4 | using System.IO; 5 | using System.Reflection; 6 | 7 | internal class Helper 8 | { 9 | #region Methods 10 | 11 | internal static string GetExecutingAssemblyDirectory() 12 | { 13 | var codeBase = Assembly.GetExecutingAssembly().CodeBase; 14 | var uri = new UriBuilder(codeBase); 15 | var path = Uri.UnescapeDataString(uri.Path); 16 | 17 | return Path.GetDirectoryName(path); 18 | } 19 | 20 | #endregion 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Winook/HookBase.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Globalization; 7 | using System.IO; 8 | using System.Resources; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | public abstract class HookBase : IDisposable 13 | { 14 | #region Fields 15 | 16 | private const string LibHostBaseName = "winook.support\\Winook.Lib.Host"; 17 | private const int InitializationTimeout1InMilliseconds = 222; 18 | private const int InitializationTimeout2InMilliseconds = 1111; 19 | private const int InitializationTimeout3InMilliseconds = 3333; 20 | 21 | private readonly int _processId; 22 | private readonly MessageReceiver _messageReceiver; 23 | private readonly HookType _hookType; 24 | private readonly Guid _libHostMutexGuid = Guid.NewGuid(); 25 | private readonly List _additionalHostArguments = new List(); 26 | private readonly ResourceManager _resourceManager = new ResourceManager(typeof(Properties.Resources)); 27 | 28 | private Process _libHostProcess; 29 | private Process _libHostProcess64; 30 | private ManualResetEventSlim _libHostMutexReleaseEvent; 31 | private bool _disposed = false; 32 | 33 | #endregion 34 | 35 | #region Constructors 36 | 37 | public HookBase(int processId, HookType hookType, int messageSizeInBytes) 38 | { 39 | _processId = processId; 40 | _hookType = hookType; 41 | _messageReceiver = new MessageReceiver(messageSizeInBytes); 42 | _messageReceiver.MessageReceived += OnMessageReceived; 43 | } 44 | 45 | #endregion 46 | 47 | #region Methods 48 | 49 | public async Task InstallAsync() 50 | { 51 | _messageReceiver.StartListening(); 52 | 53 | var libHostMutexName = $"Global\\{_libHostMutexGuid}"; 54 | using (var acquireEvent = new ManualResetEventSlim(false)) 55 | { 56 | _libHostMutexReleaseEvent = new ManualResetEventSlim(false); 57 | _ = Task.Run(() => 58 | { 59 | using (var mutex = new Mutex(true, libHostMutexName, out _)) 60 | { 61 | acquireEvent.Set(); 62 | _libHostMutexReleaseEvent.Wait(); 63 | mutex.ReleaseMutex(); // Let host process unhook and terminate 64 | } 65 | }); 66 | acquireEvent.Wait(); 67 | } 68 | 69 | var libHostName = $"{LibHostBaseName}.x86.exe"; 70 | var libHostPath = Path.Combine(Helper.GetExecutingAssemblyDirectory(), libHostName); 71 | _libHostProcess = Process.Start(libHostPath, $"{GetHostArguments()}"); 72 | 73 | Debug.WriteLine($"{libHostName} args: {GetHostArguments()}"); 74 | 75 | if (Environment.Is64BitOperatingSystem) 76 | { 77 | var libHost64Name = $"{LibHostBaseName}.x64.exe"; 78 | var libHost64Path = Path.Combine(Helper.GetExecutingAssemblyDirectory(), libHost64Name); 79 | _libHostProcess64 = Process.Start(libHost64Path, $"{GetHostArguments()}"); 80 | } 81 | 82 | await Task.Delay(InitializationTimeout1InMilliseconds).ConfigureAwait(false); 83 | CheckLibHostsStatus(out bool hostRunning, out bool host64Running, out int exitCode, out int exitCode64); 84 | if (hostRunning && host64Running) 85 | { 86 | await Task.Delay(InitializationTimeout2InMilliseconds).ConfigureAwait(false); 87 | CheckLibHostsStatus(out hostRunning, out host64Running, out exitCode, out exitCode64); 88 | } 89 | 90 | if (hostRunning && host64Running) 91 | { 92 | await Task.Delay(InitializationTimeout3InMilliseconds).ConfigureAwait(false); 93 | CheckLibHostsStatus(out hostRunning, out host64Running, out exitCode, out exitCode64); 94 | } 95 | 96 | if (hostRunning && host64Running) 97 | { 98 | throw new WinookException(_resourceManager.GetString("HostApplicationsTimedOut", CultureInfo.CurrentCulture)); 99 | } 100 | 101 | var errorFile = Path.Combine(Path.GetTempPath(), _libHostMutexGuid.ToString()); 102 | var errorFileExists = File.Exists(errorFile); 103 | if (exitCode != 0 || exitCode64 != 0 || errorFileExists) 104 | { 105 | if (errorFileExists) 106 | { 107 | throw new WinookException(File.ReadAllText(errorFile)); 108 | } 109 | else if (!(exitCode != 0 && exitCode64 != 0)) 110 | { 111 | throw new WinookException(_resourceManager.GetString("HostApplicationFailed", CultureInfo.CurrentCulture)); 112 | } 113 | else 114 | { 115 | throw new WinookException(_resourceManager.GetString("HostApplicationsFailed", CultureInfo.CurrentCulture)); 116 | } 117 | } 118 | } 119 | 120 | public void Uninstall() 121 | { 122 | ReleaseAndDisposeMutex(); 123 | _messageReceiver.Stop(); 124 | } 125 | 126 | public void Dispose() 127 | { 128 | Dispose(true); 129 | GC.SuppressFinalize(this); 130 | } 131 | 132 | protected virtual void Dispose(bool disposing) 133 | { 134 | if (!_disposed) 135 | { 136 | if (disposing) 137 | { 138 | ReleaseAndDisposeMutex(); 139 | _libHostProcess?.Dispose(); 140 | _libHostProcess64?.Dispose(); 141 | _messageReceiver.Dispose(); 142 | } 143 | 144 | _disposed = true; 145 | } 146 | } 147 | 148 | protected abstract void OnMessageReceived(object sender, MessageEventArgs e); 149 | 150 | protected void AddHostArguments(params string[] arguments) 151 | =>_additionalHostArguments.AddRange(arguments); 152 | 153 | private string GetBaseHostArguments() 154 | => $"{(int)_hookType} {_messageReceiver.Port} {_processId} {_libHostMutexGuid}"; 155 | 156 | private string GetHostArguments() 157 | => GetBaseHostArguments() + " " + string.Join(" ", _additionalHostArguments); 158 | 159 | private void CheckLibHostsStatus(out bool hostRunning, out bool host64Running, out int exitCode, out int exitCode64) 160 | { 161 | exitCode = 0; 162 | exitCode64 = 0; 163 | host64Running = false; 164 | if (Environment.Is64BitOperatingSystem) 165 | { 166 | host64Running = !_libHostProcess64.HasExited; 167 | if (!host64Running) 168 | { 169 | exitCode64 = _libHostProcess64.ExitCode; 170 | } 171 | } 172 | 173 | hostRunning = !_libHostProcess.HasExited; 174 | if (!hostRunning) 175 | { 176 | exitCode = _libHostProcess.ExitCode; 177 | } 178 | } 179 | 180 | private void ReleaseAndDisposeMutex() 181 | { 182 | if (_libHostMutexReleaseEvent != null) 183 | { 184 | _libHostMutexReleaseEvent.Set(); 185 | _libHostMutexReleaseEvent.Dispose(); 186 | } 187 | } 188 | 189 | #endregion 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/Winook/HookType.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | public enum HookType 4 | { 5 | Keyboard = 2, 6 | Mouse = 7 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Winook/KeyDirection.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | public enum KeyDirection 4 | { 5 | Any, 6 | Up, 7 | Down 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Winook/KeyboardHook.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Diagnostics.Contracts; 7 | using System.Linq; 8 | 9 | public class KeyboardHook : HookBase 10 | { 11 | #region Fields 12 | 13 | private const int HookMessageSizeInBytes = 8; 14 | private const HookType KeyboardHookType = HookType.Keyboard; // WH_KEYBOARD 15 | 16 | private readonly Dictionary _messageHandlers = new Dictionary(); 17 | 18 | private bool _disposed = false; 19 | 20 | #endregion 21 | 22 | #region Events 23 | 24 | public event EventHandler MessageReceived; 25 | 26 | public delegate void KeyboardEventHandler(object sender, KeyboardMessageEventArgs e); 27 | 28 | #endregion 29 | 30 | #region Constructors 31 | 32 | public KeyboardHook(int processId) 33 | : base(processId, KeyboardHookType, HookMessageSizeInBytes) 34 | { 35 | } 36 | 37 | #endregion 38 | 39 | #region Methods 40 | 41 | public void AddHandler(KeyCode keyCode, KeyboardEventHandler handler) 42 | => AddHandler((ushort)keyCode, Modifiers.None, KeyDirection.Up, handler); 43 | 44 | public void AddHandler(ushort keyValue, KeyboardEventHandler handler) 45 | => AddHandler(keyValue, Modifiers.None, KeyDirection.Up, handler); 46 | 47 | public void AddHandler(KeyCode keyCode, Modifiers modifiers, KeyboardEventHandler handler) 48 | => AddHandler((ushort)keyCode, modifiers, KeyDirection.Up, handler); 49 | public void AddHandler(ushort keyValue, Modifiers modifiers, KeyboardEventHandler handler) 50 | => AddHandler(keyValue, modifiers, KeyDirection.Up, handler); 51 | 52 | public void AddHandler(KeyCode keyCode, Modifiers modifiers, KeyDirection direction, KeyboardEventHandler handler) 53 | => AddHandler((ushort)keyCode, modifiers, direction, handler); 54 | 55 | public void AddHandler(ushort keyValue, Modifiers modifiers, KeyDirection direction, KeyboardEventHandler handler) 56 | { 57 | foreach (var key in GetHandlerKeys(keyValue, modifiers, direction)) 58 | { 59 | AddHandlerInternal(key, handler ?? throw new ArgumentNullException(nameof(handler))); 60 | } 61 | } 62 | 63 | public void RemoveHandler(KeyCode keyCode, KeyboardEventHandler handler) 64 | => RemoveHandler((ushort)keyCode, Modifiers.None, KeyDirection.Any, handler); 65 | 66 | public void RemoveHandler(ushort keyValue, KeyboardEventHandler handler) 67 | => RemoveHandler(keyValue, Modifiers.None, KeyDirection.Any, handler); 68 | 69 | public void RemoveHandler(KeyCode keyCode, Modifiers modifiers, KeyboardEventHandler handler) 70 | => RemoveHandler((ushort)keyCode, modifiers, KeyDirection.Any, handler); 71 | 72 | public void RemoveHandler(ushort keyValue, Modifiers modifiers, KeyboardEventHandler handler) 73 | => RemoveHandler(keyValue, modifiers, KeyDirection.Any, handler); 74 | 75 | public void RemoveHandler(KeyCode keyCode, Modifiers modifiers, KeyDirection direction, KeyboardEventHandler handler) 76 | => RemoveHandler((ushort)keyCode, modifiers, direction, handler); 77 | 78 | public void RemoveHandler(ushort keyValue, Modifiers modifiers, KeyDirection direction, KeyboardEventHandler handler) 79 | { 80 | foreach (var key in GetHandlerKeys(keyValue, modifiers, direction)) 81 | { 82 | RemoveHandlerInternal(key, handler ?? throw new ArgumentNullException(nameof(handler))); 83 | } 84 | } 85 | 86 | public void RemoveAllHandlers() 87 | { 88 | MessageReceived = null; 89 | foreach (var key in _messageHandlers.Keys.ToArray()) 90 | { 91 | _messageHandlers[key] = null; 92 | } 93 | } 94 | 95 | protected override void OnMessageReceived(object sender, MessageEventArgs e) 96 | { 97 | Contract.Requires(e != null); 98 | 99 | var keyValue = BitConverter.ToUInt16(e.Bytes, 0); 100 | var modifiers = BitConverter.ToUInt16(e.Bytes, 2); 101 | var flags = BitConverter.ToUInt32(e.Bytes, 4); 102 | var pressed = (flags & 0x80000000) == 0; 103 | var eventArgs = new KeyboardMessageEventArgs 104 | { 105 | KeyValue = keyValue, 106 | Modifiers = modifiers, 107 | Flags = flags, 108 | Shift = (modifiers & 0b100) > 0, 109 | Control = (modifiers & 0b10) > 0, 110 | Alt = (modifiers & 0b1) > 0, 111 | Direction = pressed ? KeyDirection.Down : KeyDirection.Up, 112 | }; 113 | 114 | Debug.Write($"Code: {eventArgs.KeyValue}; Modifiers: {eventArgs.Modifiers:x}; Flags: {eventArgs.Flags:x}; "); 115 | Debug.WriteLine($"Shift: {eventArgs.Shift}; Control: {eventArgs.Control}; Alt: {eventArgs.Alt}; Direction: {eventArgs.Direction}"); 116 | 117 | MessageReceived?.Invoke(this, eventArgs); 118 | 119 | uint handlerKey = GetHandlerKey(keyValue, modifiers, pressed); 120 | if (_messageHandlers.ContainsKey(handlerKey)) 121 | { 122 | _messageHandlers[handlerKey]?.Invoke(this, eventArgs); 123 | } 124 | } 125 | 126 | protected override void Dispose(bool disposing) 127 | { 128 | if (_disposed) 129 | { 130 | return; 131 | } 132 | 133 | if (disposing) 134 | { 135 | RemoveAllHandlers(); 136 | } 137 | 138 | _disposed = true; 139 | 140 | base.Dispose(disposing); 141 | } 142 | 143 | private void AddHandlerInternal(uint key, KeyboardEventHandler handler) 144 | { 145 | if (!_messageHandlers.ContainsKey(key)) 146 | { 147 | _messageHandlers.Add(key, null); 148 | } 149 | 150 | _messageHandlers[key] += handler; 151 | } 152 | 153 | private void RemoveHandlerInternal(uint key, KeyboardEventHandler handler) 154 | { 155 | if (_messageHandlers.ContainsKey(key)) 156 | { 157 | _messageHandlers[key] -= handler; 158 | } 159 | } 160 | 161 | /// 162 | /// Returns a key that can match an entry in the handlers dictionary. 163 | /// Bits format: 164 | /// [31..26]: unused 165 | /// [25]: 1 when key is being pressed 166 | /// [24..22]: left shift, control and alt (1 = pressed) 167 | /// [21..19]: right shift, control and alt (1 = pressed) 168 | /// [18..16]: shift, control and alt (left or right, 1 = pressed) 169 | /// [15..0]: virtual key code value 170 | /// Modifiers that are not side sensitive are optionally applied. 171 | /// 172 | /// 173 | /// 174 | /// 175 | /// 176 | /// 177 | private static uint GetHandlerKey(ushort keyValue, ushort modifiers, bool pressed, ushort insensitiveModifiers = 0) 178 | => keyValue | (((uint)modifiers & 0b111_111_000 | insensitiveModifiers | (pressed ? (uint)1 : 0) << 9) << 16); 179 | 180 | private static IEnumerable GetHandlerKeys(ushort keyValue, Modifiers modifiers, KeyDirection direction) 181 | => GetHandlerKeys(keyValue, (ushort)modifiers, direction); 182 | 183 | private static IEnumerable GetHandlerKeys(ushort keyValue, ushort modifiers, KeyDirection direction) 184 | { 185 | var keys = new HashSet(); 186 | if ((modifiers & 0b111) > 0) 187 | { 188 | // Add handlers for side insensitive modifiers 189 | var left = (ushort)((modifiers & 0b111) << 6); 190 | var right = (ushort)((modifiers & 0b111) << 3); 191 | var both = (ushort)(left | right); 192 | if (direction == KeyDirection.Any | direction == KeyDirection.Up) 193 | { 194 | keys.Add(GetHandlerKey(keyValue, modifiers, false, left)); 195 | keys.Add(GetHandlerKey(keyValue, modifiers, false, right)); 196 | keys.Add(GetHandlerKey(keyValue, modifiers, false, both)); 197 | } 198 | 199 | if (direction == KeyDirection.Any | direction == KeyDirection.Down) 200 | { 201 | keys.Add(GetHandlerKey(keyValue, modifiers, true, left)); 202 | keys.Add(GetHandlerKey(keyValue, modifiers, true, right)); 203 | keys.Add(GetHandlerKey(keyValue, modifiers, true, both)); 204 | } 205 | } 206 | else 207 | { 208 | // Add handlers for side sensitive modifiers and unmodified (single) keys 209 | if (direction == KeyDirection.Any | direction == KeyDirection.Up) 210 | { 211 | keys.Add(GetHandlerKey(keyValue, modifiers, false)); 212 | } 213 | 214 | if (direction == KeyDirection.Any | direction == KeyDirection.Down) 215 | { 216 | keys.Add(GetHandlerKey(keyValue, modifiers, true)); 217 | } 218 | } 219 | 220 | return keys; 221 | } 222 | 223 | #endregion 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/Winook/KeyboardMessageEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | using System; 4 | 5 | #pragma warning disable CA1051 // Do not declare visible instance fields 6 | public class KeyboardMessageEventArgs : EventArgs 7 | { 8 | public ushort KeyValue; 9 | public ushort Modifiers; 10 | public uint Flags; 11 | public bool Shift; 12 | public bool Control; 13 | public bool Alt; 14 | public KeyDirection Direction; 15 | } 16 | #pragma warning restore CA1051 // Do not declare visible instance fields 17 | } 18 | -------------------------------------------------------------------------------- /src/Winook/MessageEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | using System; 4 | 5 | #pragma warning disable CA1051 // Do not declare visible instance fields 6 | public class MessageEventArgs : EventArgs 7 | { 8 | public byte[] Bytes; 9 | } 10 | #pragma warning restore CA1051 // Do not declare visible instance fields 11 | } 12 | -------------------------------------------------------------------------------- /src/Winook/MessageReceiver.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | using System; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | public class MessageReceiver : IDisposable 10 | { 11 | #region Fields 12 | 13 | private readonly int _messageByteSize; 14 | 15 | private TcpListener _tcpListener; 16 | private CancellationTokenSource _cancellationTokenSource; 17 | private CancellationToken _cancellationToken; 18 | private Task _listeningTask; 19 | private ManualResetEventSlim _portSetEvent; 20 | 21 | private bool _disposed = false; 22 | 23 | #endregion 24 | 25 | #region Constructors 26 | 27 | public MessageReceiver(int messageByteSize) 28 | { 29 | _messageByteSize = messageByteSize; 30 | } 31 | 32 | #endregion 33 | 34 | #region Events 35 | 36 | public event EventHandler MessageReceived; 37 | 38 | #endregion 39 | 40 | #region Properties 41 | 42 | public int Port { get; private set; } 43 | 44 | public bool IsListening => _listeningTask != null && !_listeningTask.IsCompleted; 45 | 46 | #endregion 47 | 48 | #region Methods 49 | 50 | public void StartListening() 51 | { 52 | _cancellationTokenSource = new CancellationTokenSource(); 53 | _cancellationToken = _cancellationTokenSource.Token; 54 | _portSetEvent = new ManualResetEventSlim(false); 55 | _listeningTask = Task.Run(() => 56 | { 57 | _tcpListener = new TcpListener(IPAddress.Loopback, 0); 58 | _tcpListener.Start(1); 59 | Port = ((IPEndPoint)_tcpListener.LocalEndpoint).Port; 60 | _portSetEvent.Set(); 61 | 62 | try 63 | { 64 | var bytes = new byte[_messageByteSize]; 65 | using (var client = _tcpListener.AcceptTcpClient()) 66 | { 67 | using (var stream = client.GetStream()) 68 | { 69 | int bytecount, offset = 0; 70 | while ((bytecount = stream.Read(bytes, offset, bytes.Length - offset)) != 0) 71 | { 72 | if (bytecount + offset == _messageByteSize) 73 | { 74 | offset = 0; 75 | MessageReceived(this, new MessageEventArgs { Bytes = bytes }); 76 | } 77 | else 78 | { 79 | offset = bytecount; 80 | } 81 | 82 | if (_cancellationToken.IsCancellationRequested) 83 | { 84 | break; 85 | } 86 | } 87 | } 88 | } 89 | } 90 | catch (System.IO.IOException exception) 91 | { 92 | if (exception.InnerException is SocketException socketException) 93 | { 94 | // Connection gets reset when the hooked client terminates 95 | if (socketException.SocketErrorCode != SocketError.ConnectionReset) 96 | { 97 | throw; 98 | } 99 | } 100 | } 101 | catch (SocketException exception) 102 | { 103 | if (exception.ErrorCode != 10004) // WSACancelBlockingCall 104 | { 105 | throw; 106 | } 107 | } 108 | }); 109 | _portSetEvent.Wait(); 110 | } 111 | 112 | public void Stop() 113 | { 114 | if (_tcpListener != null) 115 | { 116 | _cancellationTokenSource.Cancel(); 117 | _tcpListener.Stop(); 118 | _portSetEvent.Reset(); 119 | } 120 | 121 | Port = 0; 122 | } 123 | 124 | public void Dispose() 125 | { 126 | Dispose(true); 127 | GC.SuppressFinalize(this); 128 | } 129 | 130 | protected virtual void Dispose(bool disposing) 131 | { 132 | if (!_disposed) 133 | { 134 | if (disposing) 135 | { 136 | if (IsListening) 137 | { 138 | Stop(); 139 | _listeningTask.Wait(); 140 | } 141 | 142 | _listeningTask?.Dispose(); 143 | _portSetEvent?.Dispose(); 144 | _cancellationTokenSource?.Dispose(); 145 | } 146 | 147 | _disposed = true; 148 | } 149 | } 150 | 151 | #endregion 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/Winook/Modifiers.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | using System; 4 | 5 | [Flags] 6 | public enum Modifiers 7 | { 8 | None = 0, 9 | Alt = 1, 10 | Control = 2, 11 | Shift = 4, 12 | RightAlt = 8, 13 | RightControl = 16, 14 | RightShift = 32, 15 | LeftAlt = 64, 16 | LeftControl = 128, 17 | LeftShift = 256, 18 | AltControl = Alt | Control, 19 | AltShift = Alt | Shift, 20 | AltControlShift = Alt | Control | Shift, 21 | ControlShift = Control | Shift, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Winook/MouseHook.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Diagnostics.Contracts; 7 | using System.Globalization; 8 | using System.Linq; 9 | 10 | public class MouseHook : HookBase 11 | { 12 | #region Fields 13 | 14 | private const int HookMessageSizeInBytes = 28; 15 | private const HookType MouseHookType = HookType.Mouse; // WH_MOUSE 16 | 17 | private readonly Dictionary _messageHandlers = new Dictionary(); 18 | 19 | private bool _disposed = false; 20 | 21 | #endregion 22 | 23 | #region Events 24 | 25 | public event EventHandler MessageReceived; 26 | public event EventHandler MouseMove; 27 | public event EventHandler LeftButtonDown; 28 | public event EventHandler LeftButtonUp; 29 | public event EventHandler LeftButtonDblClk; 30 | public event EventHandler RightButtonDown; 31 | public event EventHandler RightButtonUp; 32 | public event EventHandler RightButtonDblClk; 33 | public event EventHandler MiddleButtonDown; 34 | public event EventHandler MiddleButtonUp; 35 | public event EventHandler MiddleButtonDblClk; 36 | public event EventHandler MouseWheel; 37 | public event EventHandler XButtonDown; 38 | public event EventHandler XButtonUp; 39 | public event EventHandler XButtonDblClk; 40 | public event EventHandler MouseHWheel; 41 | 42 | public delegate void MouseEventHandler(object sender, MouseMessageEventArgs e); 43 | 44 | #endregion 45 | 46 | #region Constructors 47 | 48 | public MouseHook(int processId) 49 | : this(processId, MouseMessageTypes.All) 50 | { 51 | } 52 | 53 | public MouseHook(int processId, MouseMessageTypes messageTypes) 54 | : base(processId, MouseHookType, HookMessageSizeInBytes) 55 | { 56 | AddHostArguments(((int)messageTypes).ToString(CultureInfo.InvariantCulture)); 57 | } 58 | 59 | #endregion 60 | 61 | #region Methods 62 | 63 | public void AddHandler(MouseMessageCode mouseMessageCode, MouseEventHandler handler) 64 | => AddHandler((int)mouseMessageCode, handler); 65 | 66 | public void AddHandler(int mouseMessageCode, MouseEventHandler handler) 67 | { 68 | if (handler == null) 69 | { 70 | #pragma warning disable IDE0016 // Use 'throw' expression 71 | throw new ArgumentNullException(nameof(handler)); 72 | #pragma warning restore IDE0016 // Use 'throw' expression 73 | } 74 | 75 | if (!_messageHandlers.ContainsKey(mouseMessageCode)) 76 | { 77 | _messageHandlers.Add(mouseMessageCode, null); 78 | } 79 | 80 | _messageHandlers[mouseMessageCode] += handler; 81 | } 82 | 83 | public void RemoveHandler(MouseMessageCode mouseMessageCode, MouseEventHandler handler) 84 | => RemoveHandler((int)mouseMessageCode, handler); 85 | 86 | public void RemoveHandler(int mouseMessageCode, MouseEventHandler handler) 87 | { 88 | if (_messageHandlers.ContainsKey(mouseMessageCode)) 89 | { 90 | _messageHandlers[mouseMessageCode] -= handler ?? throw new ArgumentNullException(nameof(handler)); 91 | } 92 | } 93 | 94 | public void RemoveAllHandlers() 95 | { 96 | MessageReceived = null; 97 | MouseMove = null; 98 | LeftButtonDown = null; 99 | LeftButtonUp = null; 100 | LeftButtonDblClk = null; 101 | RightButtonDown = null; 102 | RightButtonUp = null; 103 | RightButtonDblClk = null; 104 | MiddleButtonDown = null; 105 | MiddleButtonUp = null; 106 | MiddleButtonDblClk = null; 107 | MouseWheel = null; 108 | XButtonDown = null; 109 | XButtonUp = null; 110 | XButtonDblClk = null; 111 | MouseHWheel = null; 112 | foreach (var key in _messageHandlers.Keys.ToArray()) 113 | { 114 | _messageHandlers[key] = null; 115 | } 116 | } 117 | 118 | protected override void OnMessageReceived(object sender, MessageEventArgs e) 119 | { 120 | Contract.Requires(e != null); 121 | 122 | var modifiers = BitConverter.ToUInt16(e.Bytes, 24); 123 | var eventArgs = new MouseMessageEventArgs 124 | { 125 | MessageCode = BitConverter.ToInt32(e.Bytes, 0), 126 | X = BitConverter.ToInt32(e.Bytes, 4), 127 | Y = BitConverter.ToInt32(e.Bytes, 8), 128 | Handle = BitConverter.ToInt32(e.Bytes, 12), 129 | HitTestCode = BitConverter.ToInt32(e.Bytes, 16), 130 | Modifiers = modifiers, 131 | Shift = (modifiers & 0b100) > 0, 132 | Control = (modifiers & 0b10) > 0, 133 | Alt = (modifiers & 0b1) > 0, 134 | }; 135 | 136 | var messageCode = GetMessageCode(eventArgs.MessageCode); 137 | if (messageCode == MouseMessageCode.MouseWheel) 138 | { 139 | eventArgs.Delta = BitConverter.ToInt16(e.Bytes, 20); 140 | } 141 | else if (messageCode == MouseMessageCode.NCXButtonDown 142 | || messageCode == MouseMessageCode.NCXButtonUp 143 | || messageCode == MouseMessageCode.NCXButtonDblClk 144 | || messageCode == MouseMessageCode.XButtonDown 145 | || messageCode == MouseMessageCode.XButtonUp 146 | || messageCode == MouseMessageCode.XButtonDblClk) 147 | { 148 | eventArgs.XButtons = BitConverter.ToInt16(e.Bytes, 20); 149 | } 150 | 151 | Debug.Write($"Code: {eventArgs.MessageCode}; X: {eventArgs.X}; Y: {eventArgs.Y}; Modifiers: {eventArgs.Modifiers:x}; "); 152 | Debug.WriteLine($"Delta: {eventArgs.Delta}; XButtons: {eventArgs.XButtons}"); 153 | 154 | MessageReceived?.Invoke(this, eventArgs); 155 | 156 | switch (messageCode) 157 | { 158 | case MouseMessageCode.MouseMove: 159 | MouseMove?.Invoke(this, eventArgs); 160 | break; 161 | case MouseMessageCode.LeftButtonDown: 162 | LeftButtonDown?.Invoke(this, eventArgs); 163 | break; 164 | case MouseMessageCode.LeftButtonUp: 165 | LeftButtonUp?.Invoke(this, eventArgs); 166 | break; 167 | case MouseMessageCode.LeftButtonDblClk: 168 | LeftButtonDblClk?.Invoke(this, eventArgs); 169 | break; 170 | case MouseMessageCode.RightButtonDown: 171 | RightButtonDown?.Invoke(this, eventArgs); 172 | break; 173 | case MouseMessageCode.RightButtonUp: 174 | RightButtonUp?.Invoke(this, eventArgs); 175 | break; 176 | case MouseMessageCode.RightButtonDblClk: 177 | RightButtonDblClk?.Invoke(this, eventArgs); 178 | break; 179 | case MouseMessageCode.MiddleButtonDown: 180 | MiddleButtonDown?.Invoke(this, eventArgs); 181 | break; 182 | case MouseMessageCode.MiddleButtonUp: 183 | MiddleButtonUp?.Invoke(this, eventArgs); 184 | break; 185 | case MouseMessageCode.MiddleButtonDblClk: 186 | MiddleButtonDblClk?.Invoke(this, eventArgs); 187 | break; 188 | case MouseMessageCode.MouseWheel: 189 | MouseWheel?.Invoke(this, eventArgs); 190 | break; 191 | case MouseMessageCode.XButtonDown: 192 | XButtonDown?.Invoke(this, eventArgs); 193 | break; 194 | case MouseMessageCode.XButtonUp: 195 | XButtonUp?.Invoke(this, eventArgs); 196 | break; 197 | case MouseMessageCode.XButtonDblClk: 198 | XButtonDblClk?.Invoke(this, eventArgs); 199 | break; 200 | case MouseMessageCode.MouseHWheel: 201 | MouseHWheel?.Invoke(this, eventArgs); 202 | break; 203 | default: 204 | break; 205 | } 206 | 207 | if (_messageHandlers.ContainsKey(eventArgs.MessageCode)) 208 | { 209 | _messageHandlers[eventArgs.MessageCode]?.Invoke(this, eventArgs); 210 | } 211 | } 212 | 213 | protected override void Dispose(bool disposing) 214 | { 215 | if (_disposed) 216 | { 217 | return; 218 | } 219 | 220 | if (disposing) 221 | { 222 | RemoveAllHandlers(); 223 | } 224 | 225 | _disposed = true; 226 | 227 | base.Dispose(disposing); 228 | } 229 | 230 | private MouseMessageCode? GetMessageCode(int messageCode) 231 | { 232 | if (Enum.IsDefined(typeof(MouseMessageCode), messageCode)) 233 | { 234 | return (MouseMessageCode)messageCode; 235 | } 236 | 237 | return null; 238 | } 239 | 240 | #endregion 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/Winook/MouseMessageCode.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | public enum MouseMessageCode 4 | { 5 | MouseActivate = 0x0021, // WM_MOUSEACTIVATE 6 | NCHitTest = 0x0084, // WM_NCHITTEST 7 | NCMouseMove = 0x00A0, // WM_NCMOUSEMOVE 8 | NCLeftButtonDown = 0x00A1, // WM_NCLBUTTONDOWN 9 | NCLeftButtonUp = 0x00A2, // WM_NCLBUTTONUP 10 | NCLeftButtonDblClk = 0x00A3, // WM_NCLBUTTONDBLCLK 11 | NCRightButtonDown = 0x00A4, // WM_NCRBUTTONDOWN 12 | NCRightButtonUp = 0x00A5, // WM_NCRBUTTONUP 13 | NCRightButtonDblClk = 0x00A6, // WM_NCRBUTTONDBLCLK 14 | NCMiddleButtonDown = 0x00A7, // WM_NCMBUTTONDOWN 15 | NCMiddleButtonUp = 0x00A8, // WM_NCMBUTTONUP 16 | NCMiddleButtonDblClk = 0x00A9, // WM_NCMBUTTONDBLCLK 17 | NCXButtonDown = 0x00AB, // WM_NCXBUTTONDOWN 18 | NCXButtonUp = 0x00AC, // WM_NCXBUTTONUP 19 | NCXButtonDblClk = 0x00AD, // WM_NCXBUTTONDBLCLK 20 | MouseMove = 0x0200, // WM_MOUSEMOVE 21 | LeftButtonDown = 0x0201, // WM_LBUTTONDOWN 22 | LeftButtonUp = 0x0202, // WM_LBUTTONUP 23 | LeftButtonDblClk = 0x0203, // WM_LBUTTONDBLCLK 24 | RightButtonDown = 0x0204, // WM_RBUTTONDOWN 25 | RightButtonUp = 0x0205, // WM_RBUTTONUP 26 | RightButtonDblClk = 0x0206, // WM_RBUTTONDBLCLK 27 | MiddleButtonDown = 0x0207, // WM_MBUTTONDOWN 28 | MiddleButtonUp = 0x0208, // WM_MBUTTONUP 29 | MiddleButtonDblClk = 0x0209, // WM_MBUTTONDBLCLK 30 | MouseWheel = 0x020A, // WM_MOUSEWHEEL 31 | XButtonDown = 0x020B, // WM_XBUTTONDOWN 32 | XButtonUp = 0x020C, // WM_XBUTTONUP 33 | XButtonDblClk = 0x020D, // WM_XBUTTONDBLCLK 34 | MouseHWheel = 0x020E, // WM_MOUSEHWHEEL 35 | CaptureChanged = 0x0215, // WM_CAPTURECHANGED 36 | NCMouseHover = 0x02A0, // WM_NCMOUSEHOVER 37 | MouseHover = 0x02A1, // WM_MOUSEHOVER 38 | NCMouseLeave = 0x02A2, // WM_NCMOUSELEAVE 39 | MouseLeave = 0x02A3, // WM_MOUSELEAVE 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Winook/MouseMessageEventArgs.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | using System; 4 | 5 | #pragma warning disable CA1051 // Do not declare visible instance fields 6 | public class MouseMessageEventArgs : EventArgs 7 | { 8 | public int MessageCode; 9 | public int X; 10 | public int Y; 11 | public int Handle; 12 | public int HitTestCode; 13 | public short Delta; 14 | public short XButtons; 15 | public ushort Modifiers; 16 | public bool Shift; 17 | public bool Control; 18 | public bool Alt; 19 | } 20 | #pragma warning restore CA1051 // Do not declare visible instance fields 21 | } 22 | -------------------------------------------------------------------------------- /src/Winook/MouseMessageTypes.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | using System; 4 | 5 | [Flags] 6 | public enum MouseMessageTypes 7 | { 8 | All = 0, 9 | Click = 1, 10 | Move = 2, 11 | Other = 4, 12 | NCClick = 8, 13 | NCMove = 16, 14 | NCOther = 32, 15 | IgnoreMove = Click | Other | NCClick | NCOther, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Winook/MouseXButtons.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | using System; 4 | 5 | [Flags] 6 | public enum MouseXButtons 7 | { 8 | Button1 = 1, 9 | Button2 = 2, 10 | AllButtons = Button1 | Button2, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Winook/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 Winook.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", "16.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("Winook.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 | /// Looks up a localized string similar to One of the Winook library host applications has failed.. 65 | /// 66 | internal static string HostApplicationFailed { 67 | get { 68 | return ResourceManager.GetString("HostApplicationFailed", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to The Winook library host applications have failed.. 74 | /// 75 | internal static string HostApplicationsFailed { 76 | get { 77 | return ResourceManager.GetString("HostApplicationsFailed", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Looks up a localized string similar to The Winook library host applications have timed out.. 83 | /// 84 | internal static string HostApplicationsTimedOut { 85 | get { 86 | return ResourceManager.GetString("HostApplicationsTimedOut", resourceCulture); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Winook/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 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | One of the Winook library host applications has failed. 122 | 123 | 124 | The Winook library host applications have failed. 125 | 126 | 127 | The Winook library host applications have timed out. 128 | 129 | -------------------------------------------------------------------------------- /src/Winook/Properties/Winook.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | winook.support\Winook.Lib.Host.x64.exe 6 | PreserveNewest 7 | 8 | 9 | winook.support\Winook.Lib.Host.x86.exe 10 | PreserveNewest 11 | 12 | 13 | winook.support\Winook.Lib.Keyboard.x64.dll 14 | PreserveNewest 15 | 16 | 17 | winook.support\Winook.Lib.Keyboard.x86.dll 18 | PreserveNewest 19 | 20 | 21 | winook.support\Winook.Lib.Mouse.x64.dll 22 | PreserveNewest 23 | 24 | 25 | winook.support\Winook.Lib.Mouse.x86.dll 26 | PreserveNewest 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Winook/Winook.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;net45 5 | Winook Library 6 | Winook is a .NET library to install thread-level hooks. 7 | Winook Library 8 | Marc-André Côté 9 | Copyright © Marc-André Côté 10 | 1.3.2.0 11 | 1.3.2.0 12 | 1.3.2 13 | Winook 14 | 1.3.2 15 | MIT 16 | https://github.com/macote/Winook 17 | windows api .net dll hook message event thread keyboard mouse 18 | en-US 19 | 20 | 21 | 22 | 23 | PreserveNewest 24 | 25 | 26 | PreserveNewest 27 | 28 | 29 | PreserveNewest 30 | 31 | 32 | PreserveNewest 33 | 34 | 35 | PreserveNewest 36 | 37 | 38 | PreserveNewest 39 | 40 | 41 | 42 | 43 | 44 | True 45 | True 46 | Resources.resx 47 | 48 | 49 | ResXFileCodeGenerator 50 | Resources.Designer.cs 51 | 52 | 53 | 54 | 55 | 56 | true 57 | contentFiles\any\netstandard2.0\winook.support\ 58 | None 59 | true 60 | 61 | 62 | 63 | 64 | 65 | true 66 | build\net45\ 67 | 68 | 69 | true 70 | build\net45\winook.support\ 71 | 72 | 73 | 74 | 75 | 76 | all 77 | runtime; build; native; contentfiles; analyzers; buildtransitive 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/Winook/WinookException.cs: -------------------------------------------------------------------------------- 1 | namespace Winook 2 | { 3 | using System; 4 | using System.Runtime.Serialization; 5 | 6 | [Serializable] 7 | public class WinookException : Exception 8 | { 9 | public WinookException() 10 | { 11 | } 12 | 13 | public WinookException(string message) 14 | : base(message) 15 | { 16 | } 17 | 18 | public WinookException(string message, Exception inner) 19 | : base(message, inner) 20 | { 21 | } 22 | 23 | protected WinookException(SerializationInfo serializationInfo, StreamingContext streamingContext) 24 | : base(serializationInfo, streamingContext) 25 | { 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Winook/winook.support/Winook.Lib.Host.x64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macote/Winook/4704fa9676b0d98c881c759af72fe88585c74287/src/Winook/winook.support/Winook.Lib.Host.x64.exe -------------------------------------------------------------------------------- /src/Winook/winook.support/Winook.Lib.Host.x86.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macote/Winook/4704fa9676b0d98c881c759af72fe88585c74287/src/Winook/winook.support/Winook.Lib.Host.x86.exe -------------------------------------------------------------------------------- /src/Winook/winook.support/Winook.Lib.Keyboard.x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macote/Winook/4704fa9676b0d98c881c759af72fe88585c74287/src/Winook/winook.support/Winook.Lib.Keyboard.x64.dll -------------------------------------------------------------------------------- /src/Winook/winook.support/Winook.Lib.Keyboard.x86.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macote/Winook/4704fa9676b0d98c881c759af72fe88585c74287/src/Winook/winook.support/Winook.Lib.Keyboard.x86.dll -------------------------------------------------------------------------------- /src/Winook/winook.support/Winook.Lib.Mouse.x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macote/Winook/4704fa9676b0d98c881c759af72fe88585c74287/src/Winook/winook.support/Winook.Lib.Mouse.x64.dll -------------------------------------------------------------------------------- /src/Winook/winook.support/Winook.Lib.Mouse.x86.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/macote/Winook/4704fa9676b0d98c881c759af72fe88585c74287/src/Winook/winook.support/Winook.Lib.Mouse.x86.dll -------------------------------------------------------------------------------- /test/Winook.Desktop.Core.Test/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/Winook.Desktop.Core.Test/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace Winook.Desktop.Core.Test 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/Winook.Desktop.Core.Test/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | [assembly: ThemeInfo( 4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 5 | //(used if a resource is not found in the page, 6 | // or application resource dictionaries) 7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 8 | //(used if a resource is not found in the page, 9 | // app, or any theme specific resource dictionaries) 10 | )] 11 | -------------------------------------------------------------------------------- /test/Winook.Desktop.Core.Test/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 |