├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── CI.yml │ └── PublishVSIX.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── marketplace ├── images │ ├── EditDWORDValueDialog.png │ ├── QuickSearch.png │ ├── SettingsStoreToolWindow.png │ ├── SubCollectionContextMenu.png │ └── ViewMenu.png ├── overview.md └── publishManifest.json └── src ├── Directory.Build.props ├── Directory.Build.targets ├── Microsoft.NET.GenerateAssemblyInfo.targets ├── SettingsStoreExplorer.sln ├── SettingsStoreExplorer ├── BinaryToHexDumpConverter.cs ├── ControlExtensions.cs ├── DependencyObjectExtensions.cs ├── DwordToStringConverter.cs ├── EditBinaryDialog.xaml ├── EditBinaryDialog.xaml.cs ├── EditIntegerDialog.xaml ├── EditIntegerDialog.xaml.cs ├── EditStringDialog.xaml ├── EditStringDialog.xaml.cs ├── GenericBooleanConverter.cs ├── HandleDoubleClickBehavior.cs ├── HandleEnterBehavior.cs ├── HexDumpAddressTextBox.cs ├── HexDumpAsciiTextBox.cs ├── HexDumpControl.xaml ├── HexDumpControl.xaml.cs ├── HexDumpControlCustomTextBox.cs ├── HexDumpControlDataContext.cs ├── HexDumpHexBytesTextBox.cs ├── IVsSettingsStoreExtensions.cs ├── IVsWritableSettingsStoreExtensions.cs ├── IntegerToStringConverter.cs ├── ItemContainerGeneratorExtensions.cs ├── KnownMonikers.cs ├── ModifyPropertyValueCommand.cs ├── ObservableCollectionExtensions.cs ├── Properties │ └── AssemblyInfo.cs ├── QwordToStringConverter.cs ├── Resources │ ├── SettingsStoreExplorer.128x128.png │ ├── SettingsStoreExplorer.16x16.png │ ├── SettingsStoreExplorer.dark.xaml │ └── SettingsStoreExplorer.light.xaml ├── RoamingSettingsStore.cs ├── SettingsStoreCommandSet.cs ├── SettingsStoreExplorer.imagemanifest ├── SettingsStoreExplorer.projitems ├── SettingsStoreExplorer.shproj ├── SettingsStoreExplorerPackage.cs ├── SettingsStoreExplorerToolWindow.cs ├── SettingsStoreExplorerToolWindowCommand.cs ├── SettingsStoreExplorerToolWindowControl.xaml ├── SettingsStoreExplorerToolWindowControl.xaml.cs ├── SettingsStoreItemExtensions.cs ├── SettingsStoreItemToImageMonikerConverter.cs ├── SettingsStorePropertyNameConverter.cs ├── SettingsStorePropertyValueConverter.cs ├── SettingsStoreTypeToImageMonikerConverter.cs ├── SettingsStoreTypeToRegTypeConverter.cs ├── SettingsStoreViewModel.cs ├── Telemetry.cs ├── TextBoxSelectionAdorner.cs ├── TextBoxWithSelectionAdorner.cs ├── VSPackage.Designer.cs ├── VSPackage.resx └── VsThemeStyles.xaml ├── VSIX ├── CustomImages.vsct ├── SettingsStoreExplorerPackage.vsct ├── VSIX.csproj └── source.extension.vsixmanifest └── VSIX_Dev17 ├── VSIX_Dev17.csproj └── source.extension.vsixmanifest /.editorconfig: -------------------------------------------------------------------------------- 1 | # To learn more about .editorconfig see https://aka.ms/editorconfigdocs 2 | ############################### 3 | # Core EditorConfig Options # 4 | ############################### 5 | # All files 6 | [*] 7 | indent_style = space 8 | # Code files 9 | [*.{cs,csx,vb,vbx}] 10 | guidelines = 132 11 | guidelines_style = 2px dashed 40C00020 12 | indent_size = 4 13 | insert_final_newline = true 14 | charset = utf-8-bom 15 | ############################### 16 | # .NET Coding Conventions # 17 | ############################### 18 | [*.{cs,vb}] 19 | # Organize usings 20 | dotnet_sort_system_directives_first = true 21 | # this. preferences 22 | dotnet_style_qualification_for_field = false:silent 23 | dotnet_style_qualification_for_property = false:silent 24 | dotnet_style_qualification_for_method = false:silent 25 | dotnet_style_qualification_for_event = false:silent 26 | # Language keywords vs BCL types preferences 27 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 28 | dotnet_style_predefined_type_for_member_access = true:suggestion 29 | # Parentheses preferences 30 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion 31 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion 32 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion 33 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion 34 | # Modifier preferences 35 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion 36 | dotnet_style_readonly_field = true:suggestion 37 | # Expression-level preferences 38 | dotnet_style_object_initializer = true:suggestion 39 | dotnet_style_collection_initializer = true:suggestion 40 | dotnet_style_explicit_tuple_names = true:suggestion 41 | dotnet_style_null_propagation = true:suggestion 42 | dotnet_style_coalesce_expression = true:suggestion 43 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 44 | dotnet_prefer_inferred_tuple_names = true:suggestion 45 | dotnet_prefer_inferred_anonymous_type_member_names = true:suggestion 46 | dotnet_style_prefer_auto_properties = true:suggestion 47 | dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion 48 | dotnet_style_prefer_conditional_expression_over_return = true:suggestion 49 | ############################### 50 | # Naming Conventions # 51 | ############################### 52 | # Style Definitions 53 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 54 | 55 | dotnet_naming_style.camel_case_with_leading_underscore.capitalization = camel_case 56 | dotnet_naming_style.camel_case_with_leading_underscore.required_prefix = _ 57 | 58 | dotnet_naming_style.camel_case_with_c_prefix_style.capitalization = camel_case 59 | dotnet_naming_style.camel_case_with_c_prefix_style.required_prefix = c_ 60 | 61 | dotnet_naming_style.camel_case_with_s_prefix.capitalization = camel_case 62 | dotnet_naming_style.camel_case_with_s_prefix.required_prefix = s_ 63 | 64 | # Symbol Definitions 65 | dotnet_naming_symbols.non_public_fields.applicable_kinds = field 66 | dotnet_naming_symbols.non_public_fields.applicable_accessibilities = private,protected,internal 67 | 68 | dotnet_naming_symbols.public_constant_fields.applicable_kinds = field 69 | dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public 70 | dotnet_naming_symbols.public_constant_fields.required_modifiers = const 71 | 72 | dotnet_naming_symbols.non_public_constant_fields.applicable_kinds = field 73 | dotnet_naming_symbols.non_public_constant_fields.applicable_accessibilities = private,protected,internal 74 | dotnet_naming_symbols.non_public_constant_fields.required_modifiers = const 75 | 76 | dotnet_naming_symbols.non_public_static_fields.applicable_kinds = field 77 | dotnet_naming_symbols.non_public_static_fields.applicable_accessibilities = private,protected,internal 78 | dotnet_naming_symbols.non_public_static_fields.required_modifiers = static 79 | 80 | # Use PascalCase for public constant fields 81 | dotnet_naming_rule.public_constant_fields_should_be_pascal_case.severity = suggestion 82 | dotnet_naming_rule.public_constant_fields_should_be_pascal_case.symbols = public_constant_fields 83 | dotnet_naming_rule.public_constant_fields_should_be_pascal_case.style = pascal_case_style 84 | 85 | # Use c_camelCase for non-public const fields 86 | dotnet_naming_rule.non_public_constant_fields_should_be_camel_case.severity = suggestion 87 | dotnet_naming_rule.non_public_constant_fields_should_be_camel_case.symbols = non_public_constant_fields 88 | dotnet_naming_rule.non_public_constant_fields_should_be_camel_case.style = camel_case_with_c_prefix_style 89 | 90 | # Use s_camelCase for non-public static fields 91 | dotnet_naming_rule.non_public_static_fields_should_be_c_camel_case.severity = suggestion 92 | dotnet_naming_rule.non_public_static_fields_should_be_c_camel_case.symbols = non_public_static_fields 93 | dotnet_naming_rule.non_public_static_fields_should_be_c_camel_case.style = camel_case_with_s_prefix 94 | 95 | # Use _camelCase for non-public fields 96 | dotnet_naming_rule.use_camel_case_with_leading_underscore_for_non_public_fields.severity = suggestion 97 | dotnet_naming_rule.use_camel_case_with_leading_underscore_for_non_public_fields.symbols = non_public_fields 98 | dotnet_naming_rule.use_camel_case_with_leading_underscore_for_non_public_fields.style = camel_case_with_leading_underscore 99 | ############################### 100 | # C# Coding Conventions # 101 | ############################### 102 | [*.cs] 103 | # var preferences 104 | csharp_style_var_for_built_in_types = true:silent 105 | csharp_style_var_when_type_is_apparent = true:suggestion 106 | csharp_style_var_elsewhere = true:silent 107 | # Expression-bodied members 108 | csharp_style_expression_bodied_methods = false:silent 109 | csharp_style_expression_bodied_constructors = false:silent 110 | csharp_style_expression_bodied_operators = false:silent 111 | csharp_style_expression_bodied_properties = true:silent 112 | csharp_style_expression_bodied_indexers = true:silent 113 | csharp_style_expression_bodied_accessors = true:silent 114 | # Pattern matching preferences 115 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 116 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 117 | # Null-checking preferences 118 | csharp_style_throw_expression = true:suggestion 119 | csharp_style_conditional_delegate_call = true:suggestion 120 | # Modifier preferences 121 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion 122 | # Expression-level preferences 123 | csharp_prefer_braces = true:silent 124 | csharp_style_deconstructed_variable_declaration = true:suggestion 125 | csharp_prefer_simple_default_expression = true:suggestion 126 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 127 | csharp_style_inlined_variable_declaration = true:suggestion 128 | ############################### 129 | # C# Formatting Rules # 130 | ############################### 131 | # New line preferences 132 | csharp_new_line_before_open_brace = all 133 | csharp_new_line_before_else = true 134 | csharp_new_line_before_catch = true 135 | csharp_new_line_before_finally = true 136 | csharp_new_line_before_members_in_object_initializers = true 137 | csharp_new_line_before_members_in_anonymous_types = true 138 | csharp_new_line_between_query_expression_clauses = true 139 | # Indentation preferences 140 | csharp_indent_case_contents = true 141 | csharp_indent_switch_labels = true 142 | csharp_indent_labels = flush_left 143 | # Space preferences 144 | csharp_space_after_cast = false 145 | csharp_space_after_keywords_in_control_flow_statements = true 146 | csharp_space_between_method_call_parameter_list_parentheses = false 147 | csharp_space_between_method_declaration_parameter_list_parentheses = false 148 | csharp_space_between_parentheses = false 149 | csharp_space_before_colon_in_inheritance_clause = true 150 | csharp_space_after_colon_in_inheritance_clause = true 151 | csharp_space_around_binary_operators = before_and_after 152 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 153 | csharp_space_between_method_call_name_and_opening_parenthesis = false 154 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 155 | # Wrapping preferences 156 | csharp_preserve_single_line_statements = true 157 | csharp_preserve_single_line_blocks = true 158 | ############################### 159 | # VB Coding Conventions # 160 | ############################### 161 | [*.vb] 162 | # Modifier preferences 163 | visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion 164 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | build: 12 | 13 | strategy: 14 | matrix: 15 | configuration: [Debug, Release] 16 | 17 | runs-on: windows-latest 18 | 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v2 22 | with: 23 | fetch-depth: 0 24 | 25 | # Install the .NET Core workload 26 | - name: Install .NET Core 27 | uses: actions/setup-dotnet@v1 28 | with: 29 | dotnet-version: 5.0.x 30 | 31 | # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild 32 | - name: Setup MSBuild.exe 33 | uses: microsoft/setup-msbuild@v1.0.2 34 | 35 | # Restore and build 36 | - name: Restore and build 37 | run: msbuild src /t:Restore,Build /p:Configuration=$env:Configuration /p:DeployExtension=false 38 | env: 39 | Configuration: ${{ matrix.configuration }} 40 | 41 | # Upload the VSIX package: https://github.com/marketplace/actions/upload-artifact 42 | - name: Upload build artifact (VSIX) 43 | uses: actions/upload-artifact@v2 44 | with: 45 | name: VSIX Package 46 | path: src/VSIX/bin/**/*.vsix 47 | 48 | - name: Upload build artifact (VSIX) 49 | uses: actions/upload-artifact@v2 50 | with: 51 | name: VSIX Package (Dev 17) 52 | path: src/VSIX_Dev17/bin/**/*.vsix 53 | -------------------------------------------------------------------------------- /.github/workflows/PublishVSIX.yml: -------------------------------------------------------------------------------- 1 | name: Publish to VS Marketplace 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | publish: 9 | runs-on: windows-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | with: 14 | fetch-depth: 0 15 | 16 | - name: Publish release to marketplace 17 | id: publish 18 | uses: mrluje/vs-marketplace-publisher@v2 19 | with: 20 | # (Required) Personal access token to perform action on the VS Marketplace 21 | pat: ${{ secrets.vs_pat }} 22 | 23 | # (Required) Path to the manifest used for the publish 24 | manifestPath: marketplace/publishManifest.json 25 | 26 | # (Optional) Fetch the latest release container a vsix package for upload to the VS Marketplace 27 | useLatestReleaseAsset: true 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Settings Store Explorer 2 | All notable changes will be documented in this file. 3 | 4 | ## Version [1.0.5] (November 15th 2021) 5 | ### Changed 6 | - Removed the Preview tag from the VS 2022 version. 7 | 8 | ## Version [1.0.4] (June 22nd 2021) 9 | ### Changed 10 | - Moved the tool window command lower down in the Other Windows sub menu. [Issue #63](https://github.com/pharring/SettingsStoreExplorer/issues/63) 11 | 12 | ### Added 13 | - Support for VS 2022 (Preview) 14 | 15 | ## Version [1.0.3] (February 2nd 2020) 16 | ### Added 17 | - Copy Key Name, Copy Property Value and Copy Property Name commands to context menus. 18 | 19 | ## Version [1.0.2] (August 28th 2019) 20 | ### Added 21 | - Support for Roaming settings. 22 | 23 | ## Version [1.0.1] (August 18th 2019) 24 | ### Added 25 | - Usage telemetry 26 | 27 | ### Fixed 28 | - Prevent delete during rename. [Issue #47](https://github.com/pharring/SettingsStoreExplorer/issues/47) 29 | 30 | ## Version [1.0.0] (August 17th 2019) 31 | ### Changed 32 | - Open sourced on https://github.com/pharring/SettingsStoreExplorer 33 | 34 | [1.0.4]: https://github.com/pharring/SettingsStoreExplorer/compare/1.0.3..1.0.4 35 | [1.0.3]: https://github.com/pharring/SettingsStoreExplorer/compare/1.0.2..1.0.3 36 | [1.0.2]: https://github.com/pharring/SettingsStoreExplorer/compare/1.0.1..1.0.2 37 | [1.0.1]: https://github.com/pharring/SettingsStoreExplorer/compare/1.0.0..1.0.1 38 | [1.0.0]: https://github.com/pharring/SettingsStoreExplorer/releases/tag/1.0.0 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Paul Harrington 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Settings Store Explorer 2 | A Visual Studio Extension with a tool window that allows you to view and edit the contents of Visual Studio's Settings Store. 3 | 4 | [![Visual Studio Marketplace](http://vsmarketplacebadge.apphb.com/version/PaulHarrington.SettingsStoreExplorer.svg)](https://marketplace.visualstudio.com/items?itemName=PaulHarrington.SettingsStoreExplorer) 5 | [![Visual Studio Marketplace Rating](http://vsmarketplacebadge.apphb.com/rating-star/PaulHarrington.SettingsStoreExplorer.svg)](https://marketplace.visualstudio.com/items?itemName=PaulHarrington.SettingsStoreExplorer) 6 | [![Visual Studio Marketplace Downloads](http://vsmarketplacebadge.apphb.com/downloads-short/PaulHarrington.SettingsStoreExplorer.svg)](https://marketplace.visualstudio.com/items?itemName=PaulHarrington.SettingsStoreExplorer) 7 | 8 | The "Settings Store" is like the registry -- in fact, behind the scenes, it's imported and managed as a registry hive. 9 | 10 | In earlier versions of Visual Studio, you could use Regedit to view/edit the settings store. However, that's no longer possible (since Dev14, I think) since Visual Studio supports side-by-side installation with independent settings. 11 | 12 | [![Build Status](https://dev.azure.com/pharring/SettingsStoreExplorer/_apis/build/status/pharring.SettingsStoreExplorer?branchName=master)](https://dev.azure.com/pharring/SettingsStoreExplorer/_build/latest?definitionId=1&branchName=master) 13 | 14 | ## Getting Started 15 | Download and install the extension from the Visual Studio Marketplace. 16 | 17 | _Note: This extension collects and transmits anonymized usage statistics to the extension author for product improvement purposes._ 18 | 19 | Access the Settings Store Explorer from the View menu. It's under "View > Other Windows >Settings Store Explorer": 20 |
![View Menu](marketplace/images/ViewMenu.png) 21 | 22 | Or you can find it in "Quick search": 23 |
![Quick Search](marketplace/images/QuickSearch.png) 24 | 25 | The Tool Window has two panels. The left hand panel shows four trees: Config, User, Remote and Roaming. Each tree is a hierarchical collection of sub-collections and properties. The properties for the selected sub-collection are shown in the right hand panel. 26 |
![Settings Store Tool Window](marketplace/images/SettingsStoreToolWindow.png) 27 | 28 | ## Modifying (editing) settings 29 | 30 | > **Important:** Modifying the Settings Store is like editing the Windows Registry. You should do it only if you know what you are doing. There is no "undo" facility if you make a mistake! 31 | 32 | If you've used the Windows Registry editor (Regedit), this should be familiar. 33 | 34 | You can edit a property of the **User** or **Remote** trees by double-clicking it in the right-hand pane. Note that you cannot edit values or collections under the **Config** or **Roaming** trees because they are read-only. 35 |
![Edit DWORD value dialog](marketplace/images/EditDWORDValueDialog.png) 36 | 37 | A new sub-collection or values can be created by right-clicking on a collection. 38 |
![Sub-collection Context Menu](marketplace/images/SubCollectionContextMenu.png) 39 | 40 | New properties can be created in a collection by right-clicking in a blank area of the right-hand panel. 41 | 42 | A collection or property can be renamed by pressing F2. 43 | 44 | A collection or property can be deleted by pressing Delete. 45 | 46 | Pressing F5 refreshes the view. 47 | -------------------------------------------------------------------------------- /marketplace/images/EditDWORDValueDialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pharring/SettingsStoreExplorer/65181ecd615bbb0df51ee6bcb28203bcefe44558/marketplace/images/EditDWORDValueDialog.png -------------------------------------------------------------------------------- /marketplace/images/QuickSearch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pharring/SettingsStoreExplorer/65181ecd615bbb0df51ee6bcb28203bcefe44558/marketplace/images/QuickSearch.png -------------------------------------------------------------------------------- /marketplace/images/SettingsStoreToolWindow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pharring/SettingsStoreExplorer/65181ecd615bbb0df51ee6bcb28203bcefe44558/marketplace/images/SettingsStoreToolWindow.png -------------------------------------------------------------------------------- /marketplace/images/SubCollectionContextMenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pharring/SettingsStoreExplorer/65181ecd615bbb0df51ee6bcb28203bcefe44558/marketplace/images/SubCollectionContextMenu.png -------------------------------------------------------------------------------- /marketplace/images/ViewMenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pharring/SettingsStoreExplorer/65181ecd615bbb0df51ee6bcb28203bcefe44558/marketplace/images/ViewMenu.png -------------------------------------------------------------------------------- /marketplace/overview.md: -------------------------------------------------------------------------------- 1 | The Settings Store Explorer extension adds a tool window for viewing and editing the Visual Studio Settings Store. It's like the Windows Registry Editor, but for Visual Studio's internal settings. 2 | 3 | Looking for a version that works with **Visual Studio 2022 Preview**? Please [go here](https://marketplace.visualstudio.com/items?itemName=PaulHarrington.SettingsStoreExplorerPreview) to download the preview version. 4 | 5 | ## Getting Started 6 | _Note: This extension collects and transmits anonymized usage statistics to the extension author for product improvement purposes._ 7 | 8 | Access the Settings Store Explorer from the View menu. It's under "View > Other Windows >Settings Store Explorer": 9 |
![View Menu](images/ViewMenu.png) 10 | 11 | Or you can find it in "Quick search": 12 |
![Quick Search](images/QuickSearch.png) 13 | 14 | The Tool Window has two panels. The left hand panel shows four trees: Config, User, Remote and Roaming. Each tree is a hierarchical collection of sub-collections and properties. The properties are shown in the right hand panel. 15 |
![Settings Store Tool Window](images/SettingsStoreToolWindow.png) 16 | 17 | ## Modifying (editing) settings 18 | 19 | > **Important:** Modifying the Settings Store is like editing the Windows Registry. You should do it only if you know what you are doing. There is no "undo" facility if you make a mistake! 20 | 21 | If you've used the Windows Registry editor (Regedit), this should be familiar. 22 | 23 | You can edit a property of the **User** or **Remote** trees by double-clicking it in the right-hand pane. Note that you cannot edit values or collections under the **Config** or **Roaming** trees because they are read-only. 24 |
![Edit DWORD value dialog](images/EditDWORDValueDialog.png) 25 | 26 | A new sub-collection or values can be created by right-clicking on a collection. 27 |
![Sub-collection Context Menu](images/SubCollectionContextMenu.png) 28 | 29 | New properties can be created in a collection by right-clicking in a blank area of the right-hand panel. 30 | 31 | A collection or property can be renamed by pressing F2. 32 | 33 | A collection or property can be deleted by pressing Delete. 34 | 35 | Pressing F5 refreshes the view. 36 | 37 | ## Support 38 | If you find a bug in this extension or have a feature request, please visit https://github.com/pharring/SettingsStoreExplorer to file an issue. 39 | -------------------------------------------------------------------------------- /marketplace/publishManifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/vsix-publish", 3 | "categories": [ "coding", "other" ], 4 | "identity": { 5 | "internalName": "SettingsStoreExplorer" 6 | }, 7 | "overview": "overview.md", 8 | "priceCategory": "free", 9 | "publisher": "PaulHarrington", 10 | "private": false, 11 | "qna": true, 12 | "repo": "https://github.com/pharring/SettingsStoreExplorer", 13 | "assetFiles": [ 14 | { 15 | "pathOnDisk": "images/EditDWORDValueDialog.png", 16 | "targetPath": "images/EditDWORDValueDialog.png" 17 | }, 18 | { 19 | "pathOnDisk": "images/QuickSearch.png", 20 | "targetPath": "images/QuickSearch.png" 21 | }, 22 | { 23 | "pathOnDisk": "images/SettingsStoreToolWindow.png", 24 | "targetPath": "images/SettingsStoreToolWindow.png" 25 | }, 26 | { 27 | "pathOnDisk": "images/SubCollectionContextMenu.png", 28 | "targetPath": "images/SubCollectionContextMenu.png" 29 | }, 30 | { 31 | "pathOnDisk": "images/ViewMenu.png", 32 | "targetPath": "images/ViewMenu.png" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Paul Harrington 6 | Copyright © 2021 Paul Harrington 7 | 1.0.5 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 0) 27 | { 28 | Version version; 29 | if (Version.TryParse(NuGetVersion, out version)) 30 | { 31 | var revision = Math.Max(version.Revision, 0); 32 | var build = Math.Max(version.Build, 0); 33 | AssemblyVersion = new Version(version.Major, version.Minor, build, revision).ToString(); 34 | break; 35 | } 36 | 37 | NuGetVersion = NuGetVersion.Substring(0, NuGetVersion.Length - 1); 38 | } 39 | ]]> 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | <_VSIXVersion>$(Version) 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/Microsoft.NET.GenerateAssemblyInfo.targets: -------------------------------------------------------------------------------- 1 | 12 | 13 | 20 | 21 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 22 | $(IntermediateOutputPath)$(MSBuildProjectName).AssemblyInfo$(DefaultLanguageSourceExtension) 23 | true 24 | 25 | 26 | 27 | true 28 | true 29 | true 30 | true 31 | true 32 | true 33 | true 34 | true 35 | true 36 | true 37 | true 38 | 39 | 40 | 47 | 51 | 52 | 55 | 56 | 57 | <_InformationalVersionContainsPlus>false 58 | <_InformationalVersionContainsPlus Condition="$(InformationalVersion.Contains('+'))">true 59 | 60 | $(InformationalVersion)+$(SourceRevisionId) 61 | $(InformationalVersion).$(SourceRevisionId) 62 | 63 | 64 | 65 | 67 | 68 | 69 | <_Parameter1>$(Company) 70 | 71 | 72 | <_Parameter1>$(Configuration) 73 | 74 | 75 | <_Parameter1>$(Copyright) 76 | 77 | 78 | <_Parameter1>$(Description) 79 | 80 | 81 | <_Parameter1>$(FileVersion) 82 | 83 | 84 | <_Parameter1>$(InformationalVersion) 85 | 86 | 87 | <_Parameter1>$(Product) 88 | 89 | 90 | <_Parameter1>$(AssemblyTitle) 91 | 92 | 93 | <_Parameter1>$(AssemblyVersion) 94 | 95 | 96 | <_Parameter1>$(NeutralLanguage) 97 | 98 | 99 | 100 | 101 | 106 | 108 | 109 | $(IntermediateOutputPath)$(MSBuildProjectName).AssemblyInfoInputs.cache 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | $(AssemblyVersion) 167 | $(Version) 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31410.414 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Marketplace", "Marketplace", "{183A5BD5-90D5-441E-8478-EA6BA967CD0A}" 7 | ProjectSection(SolutionItems) = preProject 8 | ..\marketplace\overview.md = ..\marketplace\overview.md 9 | ..\marketplace\publishManifest.json = ..\marketplace\publishManifest.json 10 | EndProjectSection 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5FCADACA-F2C1-425D-81E8-A78036581A40}" 13 | ProjectSection(SolutionItems) = preProject 14 | Directory.Build.props = Directory.Build.props 15 | Directory.Build.targets = Directory.Build.targets 16 | Microsoft.NET.GenerateAssemblyInfo.targets = Microsoft.NET.GenerateAssemblyInfo.targets 17 | EndProjectSection 18 | EndProject 19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "images", "images", "{0F4DD1ED-029B-46C7-8F27-3412207BE18E}" 20 | ProjectSection(SolutionItems) = preProject 21 | ..\marketplace\images\EditDWORDValueDialog.png = ..\marketplace\images\EditDWORDValueDialog.png 22 | ..\marketplace\images\QuickSearch.png = ..\marketplace\images\QuickSearch.png 23 | ..\marketplace\images\SettingsStoreToolWindow.png = ..\marketplace\images\SettingsStoreToolWindow.png 24 | ..\marketplace\images\SubCollectionContextMenu.png = ..\marketplace\images\SubCollectionContextMenu.png 25 | ..\marketplace\images\ViewMenu.png = ..\marketplace\images\ViewMenu.png 26 | EndProjectSection 27 | EndProject 28 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SettingsStoreExplorer", "SettingsStoreExplorer\SettingsStoreExplorer.shproj", "{566912FA-295C-430E-B8F3-1140E4490D04}" 29 | EndProject 30 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VSIX", "VSIX\VSIX.csproj", "{4ACB08BB-D2F9-4E35-BD4F-A170D62B6D71}" 31 | EndProject 32 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VSIX_Dev17", "VSIX_Dev17\VSIX_Dev17.csproj", "{58AE81DD-2560-4645-B731-6BEE46E7E3C8}" 33 | EndProject 34 | Global 35 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 36 | SettingsStoreExplorer\SettingsStoreExplorer.projitems*{4acb08bb-d2f9-4e35-bd4f-a170d62b6d71}*SharedItemsImports = 4 37 | SettingsStoreExplorer\SettingsStoreExplorer.projitems*{566912fa-295c-430e-b8f3-1140e4490d04}*SharedItemsImports = 13 38 | SettingsStoreExplorer\SettingsStoreExplorer.projitems*{58ae81dd-2560-4645-b731-6bee46e7e3c8}*SharedItemsImports = 4 39 | EndGlobalSection 40 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 41 | Debug|Any CPU = Debug|Any CPU 42 | Release|Any CPU = Release|Any CPU 43 | EndGlobalSection 44 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 45 | {4ACB08BB-D2F9-4E35-BD4F-A170D62B6D71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {4ACB08BB-D2F9-4E35-BD4F-A170D62B6D71}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {4ACB08BB-D2F9-4E35-BD4F-A170D62B6D71}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {4ACB08BB-D2F9-4E35-BD4F-A170D62B6D71}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {58AE81DD-2560-4645-B731-6BEE46E7E3C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {58AE81DD-2560-4645-B731-6BEE46E7E3C8}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {58AE81DD-2560-4645-B731-6BEE46E7E3C8}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {58AE81DD-2560-4645-B731-6BEE46E7E3C8}.Release|Any CPU.Build.0 = Release|Any CPU 53 | EndGlobalSection 54 | GlobalSection(SolutionProperties) = preSolution 55 | HideSolutionNode = FALSE 56 | EndGlobalSection 57 | GlobalSection(NestedProjects) = preSolution 58 | {0F4DD1ED-029B-46C7-8F27-3412207BE18E} = {183A5BD5-90D5-441E-8478-EA6BA967CD0A} 59 | EndGlobalSection 60 | GlobalSection(ExtensibilityGlobals) = postSolution 61 | SolutionGuid = {EC5B2F56-8820-4624-A7BF-5E89FAD6C645} 62 | EndGlobalSection 63 | EndGlobal 64 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/BinaryToHexDumpConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using Microsoft.VisualStudio.PlatformUI; 4 | using System.Globalization; 5 | using System.Text; 6 | 7 | namespace SettingsStoreExplorer 8 | { 9 | internal class BinaryToHexDumpConverter : ValueConverter 10 | { 11 | protected override string Convert(byte[] value, object parameter, CultureInfo culture) 12 | => value == null ? "" : BinaryToString(value); 13 | 14 | /// 15 | /// Render the byte array as a string in hex-dump format. 16 | /// This matches the format used by regedit for REG_BINARY. 17 | /// 18 | /// The bytes. 19 | /// A string representing a hex-dump. 20 | /// 21 | /// 0000 01 23 45 67 FF 12 30 . # E g ÿ þ . 0 22 | /// 0008 98 17 23 10 . . # . 23 | /// 24 | private static string BinaryToString(byte[] binary) 25 | { 26 | var sbMain = new StringBuilder(); 27 | var sbAscii = new StringBuilder(); 28 | 29 | int row = 0; 30 | int offset = 0; 31 | foreach (var x in binary) 32 | { 33 | if (offset == 0) 34 | { 35 | sbMain.Append((row * 8).ToString("X4")); 36 | sbMain.Append(" "); 37 | } 38 | 39 | sbMain.AppendFormat(" {0:X2} ", x); 40 | 41 | if (char.IsControl((char)x)) 42 | { 43 | sbAscii.Append('.'); 44 | } 45 | else 46 | { 47 | sbAscii.Append((char)x); 48 | } 49 | 50 | sbAscii.Append(' '); 51 | 52 | offset++; 53 | if (offset == 8) 54 | { 55 | sbMain.AppendLine(sbAscii.ToString()); 56 | sbAscii.Clear(); 57 | offset = 0; 58 | row++; 59 | } 60 | } 61 | 62 | if (offset != 0) 63 | { 64 | sbMain.Append(' ', 6 * (8 - offset)); 65 | sbMain.Append(sbAscii.ToString()); 66 | } 67 | 68 | return sbMain.ToString(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/ControlExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Input; 7 | using System.Windows.Threading; 8 | using Microsoft; 9 | 10 | namespace SettingsStoreExplorer 11 | { 12 | internal static class ControlExtensions 13 | { 14 | public static void InPlaceEdit(this Control control, string initialText, Action onEditing, Action onAcceptEdit) 15 | { 16 | if (!InPlaceEditInternal(control, initialText, onEditing, onAcceptEdit)) 17 | { 18 | // This can fail for virtualized controls. Retry after a layout/render. 19 | #pragma warning disable VSTHRD001 // Avoid legacy thread switching APIs 20 | control.Dispatcher.Invoke(() => InPlaceEditInternal(control, initialText, onEditing, onAcceptEdit), DispatcherPriority.Render); 21 | #pragma warning restore VSTHRD001 // Avoid legacy thread switching APIs 22 | } 23 | } 24 | 25 | private static bool InPlaceEditInternal(Control control, string initialText, Action onEditing, Action onAcceptEdit) 26 | { 27 | var stackPanel = control.FindVisualDescendent(); 28 | if (stackPanel == null) 29 | { 30 | return false; 31 | } 32 | 33 | var label = stackPanel.FindName("label") as TextBlock; 34 | Assumes.NotNull(label); 35 | 36 | var editor = stackPanel.FindName("editor") as TextBox; 37 | Assumes.NotNull(editor); 38 | 39 | void OnPreviewKeyDown(object sender, KeyEventArgs e) 40 | { 41 | switch (e.Key) 42 | { 43 | case Key.Escape: 44 | editor.Text = initialText; 45 | goto case Key.Enter; 46 | 47 | case Key.Enter: 48 | e.Handled = true; 49 | control.Focus(); 50 | return; 51 | 52 | case Key.Tab: 53 | // Regedit "dings" here 54 | e.Handled = true; 55 | return; 56 | } 57 | } 58 | 59 | void OnLostFocus(object sender, RoutedEventArgs e) 60 | { 61 | e.Handled = true; 62 | 63 | editor.Visibility = Visibility.Collapsed; 64 | label.Visibility = Visibility.Visible; 65 | 66 | editor.LostFocus -= OnLostFocus; 67 | editor.PreviewKeyDown -= OnPreviewKeyDown; 68 | 69 | onEditing(false); 70 | 71 | if (editor.Text != initialText) 72 | { 73 | onAcceptEdit(editor.Text); 74 | } 75 | } 76 | 77 | editor.Text = initialText; 78 | editor.PreviewKeyDown += OnPreviewKeyDown; 79 | editor.LostFocus += OnLostFocus; 80 | 81 | label.Visibility = Visibility.Collapsed; 82 | editor.Visibility = Visibility.Visible; 83 | 84 | onEditing(true); 85 | editor.SelectAll(); 86 | editor.Focus(); 87 | 88 | return true; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/DependencyObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Windows; 4 | using System.Windows.Media; 5 | 6 | namespace SettingsStoreExplorer 7 | { 8 | internal static class DependencyObjectExtensions 9 | { 10 | /// 11 | /// Searches up the visual tree for an ancestor of the given type. 12 | /// 13 | /// The type of ancestor to find. 14 | /// The starting object. 15 | /// The first ancestor of the given type or null if none was found. 16 | public static T FindVisualAncestor(this DependencyObject dependencyObject) where T : DependencyObject 17 | { 18 | switch (dependencyObject) 19 | { 20 | case Visual visual: 21 | dependencyObject = VisualTreeHelper.GetParent(visual); 22 | break; 23 | 24 | default: 25 | dependencyObject = LogicalTreeHelper.GetParent(dependencyObject); 26 | break; 27 | } 28 | 29 | while (true) 30 | { 31 | switch (dependencyObject) 32 | { 33 | case null: 34 | return null; 35 | 36 | case T found: 37 | return found; 38 | 39 | case Visual visual: 40 | dependencyObject = VisualTreeHelper.GetParent(visual); 41 | break; 42 | 43 | default: 44 | dependencyObject = LogicalTreeHelper.GetParent(dependencyObject); 45 | break; 46 | } 47 | } 48 | } 49 | 50 | /// 51 | /// Searches down the visual tree using a depth-first search to find a descendent of the given type. 52 | /// 53 | /// The type of descendent to find. 54 | /// The starting object. 55 | /// The first descendent of the given type or null if none was found. 56 | public static T FindVisualDescendent(this DependencyObject dependencyObject) where T : DependencyObject 57 | { 58 | var childrenCount = VisualTreeHelper.GetChildrenCount(dependencyObject); 59 | for (int childIndex = 0; childIndex < childrenCount; childIndex++) 60 | { 61 | var child = VisualTreeHelper.GetChild(dependencyObject, childIndex); 62 | if (child is T found) 63 | { 64 | return found; 65 | } 66 | 67 | found = FindVisualDescendent(child); 68 | if (found != null) 69 | { 70 | return found; 71 | } 72 | } 73 | 74 | return null; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/DwordToStringConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Globalization; 4 | 5 | namespace SettingsStoreExplorer 6 | { 7 | internal sealed class DwordToStringConverter : IntegerToStringConverter 8 | { 9 | public static readonly DwordToStringConverter Instance = new DwordToStringConverter(); 10 | 11 | private DwordToStringConverter() 12 | { 13 | } 14 | 15 | public override string ToString(object value, NumberStyles style, CultureInfo culture) 16 | => ((uint)value).ToString(FormatStringFromStyle(style), culture); 17 | 18 | public override bool TryParse(string text, NumberStyles style, CultureInfo culture, out object value) 19 | { 20 | if (uint.TryParse(text, style, culture, out var result)) 21 | { 22 | value = result; 23 | return true; 24 | } 25 | 26 | value = null; 27 | return false; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/EditBinaryDialog.xaml: -------------------------------------------------------------------------------- 1 |  17 | 18 | 27 | 28 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/EditBinaryDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using Microsoft.VisualStudio.PlatformUI; 4 | using System.Windows; 5 | 6 | namespace SettingsStoreExplorer 7 | { 8 | /// 9 | /// Interaction logic for EditBinaryDialog.xaml 10 | /// 11 | public partial class EditBinaryDialog : DialogWindow 12 | { 13 | private readonly SettingsStoreProperty _property; 14 | 15 | public EditBinaryDialog(SettingsStoreProperty property) 16 | { 17 | _property = property; 18 | InitializeComponent(); 19 | DataContext = new { property.Name, property.Value }; 20 | } 21 | 22 | private void OkButton_Click(object sender, RoutedEventArgs e) 23 | { 24 | DialogResult = true; 25 | _property.Value = ValueTextBox.EditedValue; 26 | Close(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/EditIntegerDialog.xaml: -------------------------------------------------------------------------------- 1 |  18 | 19 | 20 | 21 | 22 | 45 | 46 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/EditIntegerDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using Microsoft.VisualStudio.PlatformUI; 4 | using System; 5 | using System.Globalization; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | 9 | namespace SettingsStoreExplorer 10 | { 11 | /// 12 | /// Interaction logic for EditIntegerDialog.xaml 13 | /// 14 | public partial class EditIntegerDialog : DialogWindow 15 | { 16 | private readonly SettingsStoreProperty _property; 17 | 18 | public EditIntegerDialog(string title, IntegerToStringConverter converter, SettingsStoreProperty property) 19 | { 20 | Title = title; 21 | _converter = converter; 22 | _property = property; 23 | InitializeComponent(); 24 | DataContext = new { property.Name, Value = _converter.ToString(property.Value, ValueFormat, CultureInfo.CurrentUICulture) }; 25 | } 26 | 27 | /// 28 | /// The format of the number. Hexadecimal or Decimal. 29 | /// 30 | public NumberStyles ValueFormat 31 | { 32 | get => (NumberStyles)GetValue(ValueFormatProperty); 33 | set => SetValue(ValueFormatProperty, value); 34 | } 35 | 36 | // Using a DependencyProperty as the backing store for ValueFormat. This enables animation, styling, binding, etc... 37 | public static readonly DependencyProperty ValueFormatProperty = 38 | DependencyProperty.Register(nameof(ValueFormat), typeof(NumberStyles), typeof(EditIntegerDialog), new PropertyMetadata(NumberStyles.HexNumber, OnValueFormatChanged)); 39 | 40 | private static void OnValueFormatChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => ((EditIntegerDialog)d).OnValueFormatChanged((NumberStyles)e.OldValue, (NumberStyles)e.NewValue); 41 | 42 | private void OnValueFormatChanged(NumberStyles oldFormat, NumberStyles newFormat) 43 | { 44 | var text = ValueTextBox.Text; 45 | var oldValue = _converter.Parse(text, oldFormat, CultureInfo.CurrentUICulture); 46 | var newText = _converter.ToString(oldValue, newFormat, CultureInfo.CurrentUICulture); 47 | ValueTextBox.Text = newText; 48 | } 49 | 50 | protected override void OnActivated(EventArgs e) 51 | { 52 | ValueTextBox.SelectAll(); 53 | base.OnActivated(e); 54 | } 55 | 56 | private void OkButton_Click(object sender, RoutedEventArgs e) 57 | { 58 | DialogResult = true; 59 | _property.Value = _converter.Parse(ValueTextBox.Text, ValueFormat, CultureInfo.CurrentUICulture); 60 | Close(); 61 | } 62 | 63 | private void OnPreviewTextInput(object sender, System.Windows.Input.TextCompositionEventArgs e) 64 | { 65 | var textBox = sender as TextBox; 66 | var text = textBox.Text; 67 | if (textBox.SelectionLength > 0) 68 | { 69 | text.Remove(textBox.SelectionStart, textBox.SelectionLength); 70 | } 71 | 72 | text = text.Insert(textBox.SelectionStart, e.Text); 73 | 74 | // If the value doesn't parse, then swallow the edit (say we handled it). 75 | e.Handled = !_converter.TryParse(text, ValueFormat, CultureInfo.CurrentUICulture, out _); 76 | } 77 | 78 | private readonly IntegerToStringConverter _converter; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/EditStringDialog.xaml: -------------------------------------------------------------------------------- 1 |  16 | 17 | 26 | 27 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/EditStringDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using Microsoft.VisualStudio.PlatformUI; 4 | using System; 5 | using System.Windows; 6 | 7 | namespace SettingsStoreExplorer 8 | { 9 | /// 10 | /// Interaction logic for EditStringDialog.xaml 11 | /// 12 | public partial class EditStringDialog : DialogWindow 13 | { 14 | private readonly SettingsStoreProperty _property; 15 | 16 | public EditStringDialog(SettingsStoreProperty property) 17 | { 18 | _property = property; 19 | InitializeComponent(); 20 | DataContext = new { property.Name, property.Value }; 21 | } 22 | 23 | protected override void OnActivated(EventArgs e) 24 | { 25 | ValueTextBox.SelectAll(); 26 | base.OnActivated(e); 27 | } 28 | 29 | private void OkButton_Click(object sender, RoutedEventArgs e) 30 | { 31 | DialogResult = true; 32 | _property.Value = ValueTextBox.Text; 33 | Close(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/GenericBooleanConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using System.Globalization; 5 | using System.Windows.Data; 6 | 7 | namespace SettingsStoreExplorer 8 | { 9 | /// 10 | /// A boolean converter that returns true when the value equals the parameter, false otherwise. 11 | /// 12 | public sealed class GenericBooleanConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | => value?.Equals(parameter); 16 | 17 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 18 | => value?.Equals(true) == true ? parameter : Binding.DoNothing; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/HandleDoubleClickBehavior.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Input; 7 | 8 | namespace SettingsStoreExplorer 9 | { 10 | public sealed class HandleDoubleClickBehavior 11 | { 12 | public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached( 13 | "Command", typeof(ICommand), typeof(HandleDoubleClickBehavior), new PropertyMetadata(default(ICommand), OnComandChanged)); 14 | 15 | public static void SetCommand(DependencyObject element, ICommand value) => element.SetValue(CommandProperty, value); 16 | 17 | public static ICommand GetCommand(DependencyObject element) => (ICommand)element.GetValue(CommandProperty); 18 | 19 | public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached( 20 | "CommandParameter", typeof(object), typeof(HandleDoubleClickBehavior), new PropertyMetadata(default(object))); 21 | 22 | public static void SetCommandParameter(DependencyObject element, object value) => element.SetValue(CommandParameterProperty, value); 23 | 24 | public static object GetCommandParameter(DependencyObject element) => element.GetValue(CommandParameterProperty); 25 | 26 | private static void OnComandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 27 | { 28 | if (!(d is Control control)) 29 | { 30 | throw new InvalidOperationException(nameof(HandleDoubleClickBehavior) + " can be attached only to elements of type " + nameof(Control)); 31 | } 32 | 33 | control.MouseDoubleClick -= OnDoubleClick; 34 | if (GetCommand(control) != null) 35 | { 36 | control.MouseDoubleClick += OnDoubleClick; 37 | } 38 | } 39 | 40 | private static void OnDoubleClick(object sender, MouseButtonEventArgs e) 41 | { 42 | if (!(sender is DependencyObject d)) 43 | { 44 | return; 45 | } 46 | 47 | var command = GetCommand(d); 48 | if (command == null) 49 | { 50 | return; 51 | } 52 | 53 | var parameter = GetCommandParameter(d); 54 | if (command.CanExecute(parameter)) 55 | { 56 | command.Execute(parameter); 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/HandleEnterBehavior.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Input; 7 | 8 | namespace SettingsStoreExplorer 9 | { 10 | public sealed class HandleEnterBehavior 11 | { 12 | public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached( 13 | "Command", typeof(ICommand), typeof(HandleEnterBehavior), new PropertyMetadata(default(ICommand), OnComandChanged)); 14 | 15 | public static void SetCommand(DependencyObject element, ICommand value) => element.SetValue(CommandProperty, value); 16 | 17 | public static ICommand GetCommand(DependencyObject element) => (ICommand)element.GetValue(CommandProperty); 18 | 19 | public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached( 20 | "CommandParameter", typeof(object), typeof(HandleEnterBehavior), new PropertyMetadata(default(object))); 21 | 22 | public static void SetCommandParameter(DependencyObject element, object value) => element.SetValue(CommandParameterProperty, value); 23 | 24 | public static object GetCommandParameter(DependencyObject element) => element.GetValue(CommandParameterProperty); 25 | 26 | private static void OnComandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 27 | { 28 | if (!(d is Control c)) 29 | { 30 | throw new InvalidOperationException($"can only be attached to {nameof(Control)}"); 31 | } 32 | 33 | c.KeyDown -= OnKeyDown; 34 | if (GetCommand(c) != null) 35 | { 36 | c.KeyDown += OnKeyDown; 37 | } 38 | } 39 | 40 | private static void OnKeyDown(object sender, KeyEventArgs e) 41 | { 42 | if (e.Key != Key.Enter) 43 | { 44 | return; 45 | } 46 | 47 | if (!(sender is DependencyObject d)) 48 | { 49 | return; 50 | } 51 | 52 | var command = GetCommand(d); 53 | if (command == null) 54 | { 55 | return; 56 | } 57 | 58 | var parameter = GetCommandParameter(d); 59 | if (command.CanExecute(parameter)) 60 | { 61 | command.Execute(parameter); 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/HexDumpAddressTextBox.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Text; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | 9 | namespace SettingsStoreExplorer 10 | { 11 | internal sealed class HexDumpAddressTextBox : TextBox 12 | { 13 | public const int BytesPerRow = 8; 14 | 15 | public HexDumpAddressTextBox() => DataContextChanged += OnDataContextChanged; 16 | 17 | private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) 18 | { 19 | if (e.OldValue is HexDumpControlDataContext oldDataContext) 20 | { 21 | oldDataContext.PropertyChanged -= BytesChanged; 22 | } 23 | 24 | if (e.NewValue is HexDumpControlDataContext newDataContext) 25 | { 26 | newDataContext.PropertyChanged += BytesChanged; 27 | UpdateContent(newDataContext.Bytes); 28 | } 29 | } 30 | 31 | private void BytesChanged(object sender, PropertyChangedEventArgs e) 32 | { 33 | if (e.PropertyName == nameof(HexDumpControlDataContext.Bytes)) 34 | { 35 | var dataContext = (HexDumpControlDataContext)sender; 36 | UpdateContent(dataContext.Bytes); 37 | } 38 | } 39 | 40 | /// 41 | /// Refresh the content of the text box when the data context changes. 42 | /// 43 | /// The byte array. 44 | private void UpdateContent(ICollection bytes) 45 | { 46 | var text = BuildContent(bytes); 47 | Text = text; 48 | } 49 | 50 | private string BuildContent(ICollection bytes) 51 | { 52 | var sb = new StringBuilder(); 53 | 54 | // Intentionally using <= here so we get a placeholder 55 | // for otherwise blank lines. 56 | for (int x = 0; x <= bytes.Count; x += BytesPerRow) 57 | { 58 | sb.AppendLine(x.ToString("X4")); 59 | } 60 | 61 | return sb.ToString(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/HexDumpAsciiTextBox.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Text; 4 | using System.Windows.Input; 5 | 6 | namespace SettingsStoreExplorer 7 | { 8 | internal sealed class HexDumpAsciiTextBox : HexDumpControlCustomTextBox 9 | { 10 | /// 11 | /// The width of one rendered ASCII char in the ASCII text box. 12 | /// 13 | private const int AsciiWidth = 2; 14 | 15 | protected override StringBuilder AppendFormattedByte(StringBuilder sbAscii, byte x) 16 | { 17 | if (char.IsControl((char)x)) 18 | { 19 | sbAscii.Append('.'); 20 | } 21 | else 22 | { 23 | sbAscii.Append((char)x); 24 | } 25 | 26 | sbAscii.Append(' ', AsciiWidth - 1); 27 | 28 | return sbAscii; 29 | } 30 | 31 | protected override void OnPreviewKeyDown(KeyEventArgs e) 32 | { 33 | base.OnPreviewKeyDown(e); 34 | switch(e.Key) 35 | { 36 | case Key.Space: 37 | InsertOrReplaceSelection((byte)' '); 38 | e.Handled = true; 39 | break; 40 | } 41 | } 42 | 43 | protected override void OnChar(char ch) 44 | { 45 | if (ch >= 32 && ch <= 255) 46 | { 47 | InsertOrReplaceSelection((byte)ch); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/HexDumpControl.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/HexDumpControl.xaml.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Collections.Generic; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Controls.Primitives; 7 | 8 | namespace SettingsStoreExplorer 9 | { 10 | /// 11 | /// Interaction logic for HexDumpControl.xaml 12 | /// The data context is a byte array. 13 | /// The user control consists of three text boxes side-by-side with their 14 | /// scroll positions linked. 15 | /// The 1st text box shows the offset of the first byte of the row. 16 | /// The 2nd text box contains the hexadecimal digits for the row. 17 | /// The 3rd text box contains the ASCII values of the bytes. 18 | /// 19 | public partial class HexDumpControl : UserControl 20 | { 21 | private HexDumpControlDataContext _innerDataContext; 22 | 23 | /// 24 | /// The constructor. 25 | /// 26 | public HexDumpControl() 27 | { 28 | InitializeComponent(); 29 | DataContextChanged += OnDataContextChanged; 30 | } 31 | 32 | public byte[] EditedValue => _innerDataContext.Bytes; 33 | 34 | private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) 35 | { 36 | if (e.NewValue is ICollection bytes) 37 | { 38 | _innerDataContext = new HexDumpControlDataContext(bytes); 39 | asciiTextBox.DataContext = _innerDataContext; 40 | hexBytesTextBox.DataContext = _innerDataContext; 41 | addressTextBox.DataContext = _innerDataContext; 42 | } 43 | } 44 | 45 | /// 46 | /// Handler for the event on 47 | /// any of the text boxes. 48 | /// 49 | /// The scroll viewer whose scroll position has changed. 50 | /// The event arguments. 51 | private void OnScrollChanged(object sender, ScrollChangedEventArgs e) 52 | { 53 | // Link the vertical scroll positions together. 54 | var verticalOffset = e.VerticalOffset; 55 | addressTextBox.ScrollToVerticalOffset(verticalOffset); 56 | hexBytesTextBox.ScrollToVerticalOffset(verticalOffset); 57 | asciiTextBox.ScrollToVerticalOffset(verticalOffset); 58 | } 59 | 60 | /// 61 | /// Handler for event on the hex bytes text box. 62 | /// 63 | /// The sender (text box) 64 | /// The event arguments. 65 | private void HexBytesTextBox_SelectionChanged(object sender, RoutedEventArgs e) 66 | => asciiTextBox.InsertPointAndSelectionLength = hexBytesTextBox.InsertPointAndSelectionLength; 67 | 68 | /// 69 | /// Handler for event on the ASCII text box. 70 | /// 71 | /// The sender (text box) 72 | /// The event arguments. 73 | private void AsciiTextBox_SelectionChanged(object sender, RoutedEventArgs e) 74 | => hexBytesTextBox.InsertPointAndSelectionLength = asciiTextBox.InsertPointAndSelectionLength; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/HexDumpControlDataContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using System.Runtime.CompilerServices; 7 | 8 | namespace SettingsStoreExplorer 9 | { 10 | /// 11 | /// The data context object for the hex dump control. The contents are stored 12 | /// in a byte array. 13 | /// 14 | internal sealed class HexDumpControlDataContext : INotifyPropertyChanged 15 | { 16 | public event PropertyChangedEventHandler PropertyChanged; 17 | 18 | /// 19 | /// The actual bytes. 20 | /// 21 | private byte[] _bytes; 22 | 23 | public HexDumpControlDataContext(ICollection bytes) 24 | => _bytes = bytes is byte[] array ? array : bytes.ToArray(); 25 | 26 | public byte[] Bytes 27 | { 28 | get => _bytes; 29 | set 30 | { 31 | if (value != _bytes) 32 | { 33 | _bytes = value; 34 | NotifyPropertyChanged(); 35 | } 36 | } 37 | } 38 | 39 | private void NotifyPropertyChanged([CallerMemberName] string propertyName = null) 40 | => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/HexDumpHexBytesTextBox.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Text; 4 | using System.Windows.Input; 5 | using static System.Globalization.CultureInfo; 6 | 7 | namespace SettingsStoreExplorer 8 | { 9 | internal sealed class HexDumpHexBytesTextBox : HexDumpControlCustomTextBox 10 | { 11 | /// 12 | /// The format string used to format bytes as hexadecimal. 13 | /// 14 | private const string ByteFormatString = " {0:X2} "; 15 | 16 | /// 17 | /// Set to true if you've already typed the first (high) nybble of a hex value. 18 | /// 19 | private bool _highNybble; 20 | 21 | protected override StringBuilder AppendFormattedByte(StringBuilder sb, byte b) 22 | => sb.AppendFormat(InvariantCulture, ByteFormatString, b); 23 | 24 | protected override void MoveSelection(int selectionStart, int selectionLength) 25 | { 26 | if (_highNybble && selectionLength == 0) 27 | { 28 | selectionStart += 3; 29 | } 30 | 31 | base.MoveSelection(selectionStart, selectionLength); 32 | } 33 | 34 | protected override void OnPreviewKeyDown(KeyEventArgs e) 35 | { 36 | var oldHighByte = _highNybble; 37 | _highNybble = false; 38 | 39 | base.OnPreviewKeyDown(e); 40 | 41 | if (oldHighByte && !e.Handled) 42 | { 43 | _highNybble = true; 44 | } 45 | } 46 | 47 | protected override void OnChar(char ch) 48 | { 49 | if (TryParseHexDigit(ch, out var digit)) 50 | { 51 | var (insertPoint, replacementLength) = InsertPointAndSelectionLength; 52 | if (insertPoint < 0) 53 | { 54 | return; 55 | } 56 | 57 | if (_highNybble) 58 | { 59 | _highNybble = false; 60 | InsertOrReplaceByte(insertPoint, replacementLength: 1, replacementValue: (byte)(GetByteAt(insertPoint) | digit)); 61 | Select(CaretIndexFromByteOffset(insertPoint + 1), 0); 62 | } 63 | else 64 | { 65 | _highNybble = true; 66 | InsertOrReplaceByte(insertPoint, replacementLength, (byte)(digit << 4)); 67 | Select(CaretIndexFromByteOffset(insertPoint), 0); 68 | } 69 | } 70 | } 71 | 72 | /// 73 | /// Try to parse the given character as a hexadecimal digit. 74 | /// 75 | /// The character to parse. 76 | /// The parsed hexadecimal digit. 77 | /// True if is a valid hexadecimal digit. 78 | private static bool TryParseHexDigit(char ch, out byte digit) 79 | { 80 | if (ch >= '0' && ch <= '9') 81 | { 82 | digit = (byte)(ch - '0'); 83 | return true; 84 | } 85 | 86 | if (ch >= 'A' && ch <= 'F') 87 | { 88 | digit = (byte)(10 + ch - 'A'); 89 | return true; 90 | } 91 | 92 | if (ch >= 'a' && ch <= 'f') 93 | { 94 | digit = (byte)(10 + ch - 'a'); 95 | return true; 96 | } 97 | 98 | digit = 0; 99 | return false; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/IVsSettingsStoreExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using Microsoft.VisualStudio; 6 | using Microsoft.VisualStudio.Shell.Interop; 7 | 8 | // I believe it's safe to suppress the analyzer warning about accessing the settings 9 | // store only from the main thread. 10 | #pragma warning disable VSTHRD010 // Invoke single-threaded types on Main thread 11 | 12 | namespace SettingsStoreExplorer 13 | { 14 | internal static class IVsSettingsStoreExtensions 15 | { 16 | public static string GetString(this IVsSettingsStore store, string collectionPath, string propertyName) 17 | { 18 | ErrorHandler.ThrowOnFailure(store.GetString(collectionPath, propertyName, out var value)); 19 | return value; 20 | } 21 | 22 | public static uint GetUint32(this IVsSettingsStore store, string collectionPath, string propertyName) 23 | { 24 | ErrorHandler.ThrowOnFailure(store.GetUnsignedInt(collectionPath, propertyName, out var value)); 25 | return value; 26 | } 27 | 28 | public static ulong GetUint64(this IVsSettingsStore store, string collectionPath, string propertyName) 29 | { 30 | ErrorHandler.ThrowOnFailure(store.GetUnsignedInt64(collectionPath, propertyName, out var value)); 31 | return value; 32 | } 33 | 34 | public static byte[] GetByteArray(this IVsSettingsStore store, string collectionPath, string propertyName) 35 | { 36 | uint[] actualByteLength = { 0 }; 37 | ErrorHandler.ThrowOnFailure(store.GetBinary(collectionPath, propertyName, 0, null, actualByteLength)); 38 | byte[] binaryValue = new byte[actualByteLength[0]]; 39 | ErrorHandler.ThrowOnFailure(store.GetBinary(collectionPath, propertyName, actualByteLength[0], binaryValue, actualByteLength)); 40 | return binaryValue; 41 | } 42 | 43 | public static IEnumerable GetSubCollectionNames(this IVsSettingsStore store, string collectionPath) 44 | { 45 | // Don't get the count up-front. It's essentially an enumeration which is as expensive 46 | // as just looping until we get an error. 47 | 48 | for (uint index = 0; ; index++) 49 | { 50 | string subCollectionName; 51 | 52 | try 53 | { 54 | if (ErrorHandler.Failed(store.GetSubCollectionName(collectionPath, index, out subCollectionName))) 55 | { 56 | break; 57 | } 58 | 59 | } 60 | catch (IndexOutOfRangeException) 61 | { 62 | break; 63 | } 64 | 65 | yield return subCollectionName; 66 | } 67 | } 68 | 69 | public static bool CollectionExists(this IVsSettingsStore store, string collectionPath) 70 | { 71 | ErrorHandler.ThrowOnFailure(store.CollectionExists(collectionPath, out int exists)); 72 | return exists != 0; 73 | } 74 | 75 | public static IEnumerable GetPropertyNames(this IVsSettingsStore store, string collectionPath) 76 | { 77 | for (uint index = 0; ; index++) 78 | { 79 | string name; 80 | try 81 | { 82 | if (ErrorHandler.Failed(store.GetPropertyName(collectionPath, index, out name))) 83 | { 84 | break; 85 | 86 | } 87 | } 88 | catch (IndexOutOfRangeException) 89 | { 90 | break; 91 | } 92 | 93 | yield return name; 94 | } 95 | } 96 | 97 | public static __VsSettingsType GetPropertyType(this IVsSettingsStore store, string collectionPath, string propertyName) 98 | { 99 | ErrorHandler.ThrowOnFailure(store.GetPropertyType(collectionPath, propertyName, out var type)); 100 | return (__VsSettingsType)type; 101 | } 102 | 103 | public static bool PropertyExists(this IVsSettingsStore store, string collectionPath, string propertyName) 104 | { 105 | ErrorHandler.ThrowOnFailure(store.PropertyExists(collectionPath, propertyName, out int exists)); 106 | return exists != 0; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/IVsWritableSettingsStoreExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using Microsoft.VisualStudio; 4 | using Microsoft.VisualStudio.Shell; 5 | using Microsoft.VisualStudio.Shell.Interop; 6 | 7 | namespace SettingsStoreExplorer 8 | { 9 | internal static class IVsWritableSettingsStoreExtensions 10 | { 11 | public static void CopyTree(this IVsWritableSettingsStore writableSettingsStore, SettingsStoreSubCollection from, SettingsStoreSubCollection to) 12 | { 13 | ThreadHelper.ThrowIfNotOnUIThread(); 14 | 15 | writableSettingsStore.CopyProperties(from, to); 16 | 17 | var fromStore = from.Root.SettingsStore; 18 | var fromPath = from.Path; 19 | 20 | for (uint index = 0; ; index++) 21 | { 22 | if (ErrorHandler.Failed(fromStore.GetSubCollectionName(fromPath, index, out var name))) 23 | { 24 | break; 25 | } 26 | 27 | var newSubCollection = new SettingsStoreSubCollection(to, name); 28 | ErrorHandler.ThrowOnFailure(writableSettingsStore.CreateCollection(newSubCollection.Name)); 29 | 30 | writableSettingsStore.CopyTree(new SettingsStoreSubCollection(from, name), newSubCollection); 31 | } 32 | } 33 | 34 | public static void CopyProperties(this IVsWritableSettingsStore writableSettingsStore, SettingsStoreSubCollection from, SettingsStoreSubCollection to) 35 | { 36 | ThreadHelper.ThrowIfNotOnUIThread(); 37 | 38 | var fromStore = from.Root.SettingsStore; 39 | var fromPath = from.Path; 40 | var toPath = to.Path; 41 | 42 | for (uint index = 0; ; index++) 43 | { 44 | if (ErrorHandler.Failed(fromStore.GetPropertyName(fromPath, index, out var name))) 45 | { 46 | break; 47 | } 48 | 49 | if (ErrorHandler.Failed(fromStore.GetPropertyType(fromPath, name, out var type))) 50 | { 51 | break; 52 | } 53 | 54 | switch ((__VsSettingsType)type) 55 | { 56 | case __VsSettingsType.SettingsType_String: 57 | ErrorHandler.ThrowOnFailure(fromStore.GetString(fromPath, name, out var stringValue)); 58 | ErrorHandler.ThrowOnFailure(writableSettingsStore.SetString(toPath, name, stringValue)); 59 | break; 60 | 61 | case __VsSettingsType.SettingsType_Int: 62 | ErrorHandler.ThrowOnFailure(fromStore.GetInt(fromPath, name, out var intValue)); 63 | ErrorHandler.ThrowOnFailure(writableSettingsStore.SetInt(toPath, name, intValue)); 64 | break; 65 | 66 | case __VsSettingsType.SettingsType_Int64: 67 | ErrorHandler.ThrowOnFailure(fromStore.GetInt64(fromPath, name, out var longValue)); 68 | ErrorHandler.ThrowOnFailure(writableSettingsStore.SetInt64(toPath, name, longValue)); 69 | break; 70 | 71 | case __VsSettingsType.SettingsType_Binary: 72 | uint[] actualByteLength = { 0 }; 73 | ErrorHandler.ThrowOnFailure(fromStore.GetBinary(fromPath, name, 0, null, actualByteLength)); 74 | byte[] bytes = new byte[actualByteLength[0]]; 75 | ErrorHandler.ThrowOnFailure(fromStore.GetBinary(fromPath, name, actualByteLength[0], bytes, actualByteLength)); 76 | ErrorHandler.ThrowOnFailure(writableSettingsStore.SetBinary(toPath, name, actualByteLength[0], bytes)); 77 | break; 78 | } 79 | } 80 | } 81 | 82 | public static void CopyProperty(this IVsWritableSettingsStore store, SettingsStoreProperty from, string toName) 83 | { 84 | ThreadHelper.ThrowIfNotOnUIThread(); 85 | var fromName = from.Name; 86 | var collectionPath = from.CollectionPath; 87 | 88 | switch (from.Type) 89 | { 90 | case __VsSettingsType.SettingsType_String: 91 | ErrorHandler.ThrowOnFailure(store.GetString(collectionPath, fromName, out var stringValue)); 92 | ErrorHandler.ThrowOnFailure(store.SetString(collectionPath, toName, stringValue)); 93 | break; 94 | 95 | case __VsSettingsType.SettingsType_Int: 96 | ErrorHandler.ThrowOnFailure(store.GetInt(collectionPath, fromName, out var intValue)); 97 | ErrorHandler.ThrowOnFailure(store.SetInt(collectionPath, toName, intValue)); 98 | break; 99 | 100 | case __VsSettingsType.SettingsType_Int64: 101 | ErrorHandler.ThrowOnFailure(store.GetInt64(collectionPath, fromName, out var longValue)); 102 | ErrorHandler.ThrowOnFailure(store.SetInt64(collectionPath, toName, longValue)); 103 | break; 104 | 105 | case __VsSettingsType.SettingsType_Binary: 106 | uint[] actualByteLength = { 0 }; 107 | ErrorHandler.ThrowOnFailure(store.GetBinary(collectionPath, fromName, 0, null, actualByteLength)); 108 | byte[] bytes = new byte[actualByteLength[0]]; 109 | ErrorHandler.ThrowOnFailure(store.GetBinary(collectionPath, fromName, actualByteLength[0], bytes, actualByteLength)); 110 | ErrorHandler.ThrowOnFailure(store.SetBinary(collectionPath, toName, actualByteLength[0], bytes)); 111 | break; 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/IntegerToStringConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using System.Globalization; 5 | 6 | namespace SettingsStoreExplorer 7 | { 8 | public abstract class IntegerToStringConverter 9 | { 10 | public abstract string ToString(object value, NumberStyles style, CultureInfo culture); 11 | public abstract bool TryParse(string text, NumberStyles style, CultureInfo culture, out object value); 12 | public object Parse(string text, NumberStyles style, CultureInfo culture) 13 | { 14 | if (TryParse(text, style, culture, out var value)) 15 | { 16 | return value; 17 | } 18 | 19 | throw new FormatException("Could not parse the given text."); 20 | } 21 | 22 | protected static string FormatStringFromStyle(NumberStyles style) => style == NumberStyles.HexNumber ? "x" : "d"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/ItemContainerGeneratorExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Windows.Controls; 4 | 5 | namespace SettingsStoreExplorer 6 | { 7 | internal static class ItemContainerGeneratorExtensions 8 | { 9 | /// 10 | /// A version of ContainerFromItem that works with HierarchicalDataTemplate 11 | /// 12 | /// The container generator. 13 | /// The item you're looking for. 14 | /// The type of items control (usually a TreeViewItem). 15 | /// The container object. 16 | public static T ContainerFromItemRecursive(this ItemContainerGenerator containerGenerator, object item) where T : ItemsControl 17 | { 18 | if (containerGenerator.ContainerFromItem(item) is T container) 19 | { 20 | return container; 21 | } 22 | 23 | foreach (var subItem in containerGenerator.Items) 24 | { 25 | if (containerGenerator.ContainerFromItem(subItem) is ItemsControl itemsControl) 26 | { 27 | container = itemsControl.ItemContainerGenerator.ContainerFromItemRecursive(item); 28 | if (container != null) 29 | { 30 | return container; 31 | } 32 | } 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/KnownMonikers.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using Microsoft.VisualStudio.Imaging.Interop; 4 | using System; 5 | 6 | namespace SettingsStoreExplorer 7 | { 8 | internal static class KnownMonikers 9 | { 10 | public static readonly Guid CustomImages = new Guid("3da9ddb5-b35b-4ed6-9d52-73aa4c30127e"); 11 | public static readonly ImageMoniker SettingsStoreExplorer = new ImageMoniker { Guid = CustomImages, Id = 1 }; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/ModifyPropertyValueCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Windows.Input; 6 | using Microsoft.VisualStudio; 7 | using Microsoft.VisualStudio.Shell; 8 | using Microsoft.VisualStudio.Shell.Interop; 9 | 10 | namespace SettingsStoreExplorer 11 | { 12 | internal class ModifyPropertyValueCommand : ICommand 13 | { 14 | public event EventHandler CanExecuteChanged 15 | { 16 | add { } 17 | remove { } 18 | } 19 | 20 | public bool CanExecute(object parameter) => parameter is SettingsStoreProperty; 21 | 22 | public void Execute(object parameter) 23 | { 24 | ThreadHelper.ThrowIfNotOnUIThread(); 25 | Telemetry.Client.TrackEvent(nameof(ModifyPropertyValueCommand) + "." + nameof(Execute)); 26 | 27 | if (!(parameter is SettingsStoreProperty property)) 28 | { 29 | return; 30 | } 31 | 32 | if (!property.TryGetWritableSettingsStore(out var writableStore)) 33 | { 34 | // Cannot get a writable setting store. The usual case is trying to modify a value under Config 35 | // TODO: Show a message? Run as admin? 36 | Telemetry.Client.TrackEvent("No writable store"); 37 | return; 38 | } 39 | 40 | ErrorHandler.ThrowOnFailure(writableStore.PropertyExists(property.CollectionPath, property.Name, out var exists)); 41 | if (exists == 0) 42 | { 43 | // Property has been deleted 44 | // TODO: Show a message 45 | Telemetry.Client.TrackEvent("Property deleted"); 46 | return; 47 | } 48 | 49 | ShowModifyPropertyDialog(property, writableStore); 50 | } 51 | 52 | public static void ShowModifyPropertyDialog(SettingsStoreProperty property, IVsWritableSettingsStore writableStore) 53 | { 54 | ThreadHelper.ThrowIfNotOnUIThread(); 55 | switch (property.Type) 56 | { 57 | case __VsSettingsType.SettingsType_String: 58 | { 59 | Telemetry.Client.TrackPageView(nameof(EditStringDialog)); 60 | var dialog = new EditStringDialog(property); 61 | if (dialog.ShowModal() == true) 62 | { 63 | ErrorHandler.ThrowOnFailure(writableStore.SetString(property.CollectionPath, property.Name, (string)property.Value)); 64 | Telemetry.Client.TrackEvent("PropertyModified", new Dictionary { ["Type"] = property.Type.ToString() }); 65 | } 66 | } 67 | break; 68 | 69 | case __VsSettingsType.SettingsType_Int: 70 | { 71 | Telemetry.Client.TrackPageView(nameof(EditIntegerDialog) + "(32)"); 72 | var dialog = new EditIntegerDialog("Edit DWORD (32-bit) Value", DwordToStringConverter.Instance, property); 73 | if (dialog.ShowModal() == true) 74 | { 75 | ErrorHandler.ThrowOnFailure(writableStore.SetUnsignedInt(property.CollectionPath, property.Name, (uint)property.Value)); 76 | Telemetry.Client.TrackEvent("PropertyModified", new Dictionary { ["Type"] = property.Type.ToString() }); 77 | } 78 | } 79 | break; 80 | 81 | case __VsSettingsType.SettingsType_Int64: 82 | { 83 | Telemetry.Client.TrackPageView(nameof(EditIntegerDialog) + "(64)"); 84 | var dialog = new EditIntegerDialog("Edit QWORD (64-bit) Value", QwordToStringConverter.Instance, property); 85 | if (dialog.ShowModal() == true) 86 | { 87 | ErrorHandler.ThrowOnFailure(writableStore.SetUnsignedInt64(property.CollectionPath, property.Name, (ulong)property.Value)); 88 | Telemetry.Client.TrackEvent("PropertyModified", new Dictionary { ["Type"] = property.Type.ToString() }); 89 | } 90 | } 91 | break; 92 | 93 | case __VsSettingsType.SettingsType_Binary: 94 | { 95 | Telemetry.Client.TrackPageView(nameof(EditBinaryDialog)); 96 | var dialog = new EditBinaryDialog(property); 97 | if (dialog.ShowModal() == true) 98 | { 99 | var binary = (byte[])property.Value; 100 | ErrorHandler.ThrowOnFailure(writableStore.SetBinary(property.CollectionPath, property.Name, (uint)binary.Length, binary)); 101 | Telemetry.Client.TrackEvent("PropertyModified", new Dictionary { ["Type"] = property.Type.ToString() }); 102 | } 103 | } 104 | break; 105 | 106 | default: 107 | break; 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/ObservableCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | 6 | namespace SettingsStoreExplorer 7 | { 8 | internal static class ObservableCollectionExtensions 9 | { 10 | /// 11 | /// doesn't have bulk operations. So we do it naïvely. 12 | /// 13 | /// Type of the items in the collection. 14 | /// The observable collection to augment. 15 | /// The items to add. 16 | public static void AddRange(this ObservableCollection collection, IEnumerable items) 17 | { 18 | foreach (var item in items) 19 | { 20 | collection.Add(item); 21 | } 22 | } 23 | 24 | /// 25 | /// doesn't have bulk operations. So we do it naïvely. 26 | /// 27 | /// Type of the items in the collection. 28 | /// The observable collection to modify. 29 | /// The new items to add. Existing items will be removed. 30 | public static void ReplaceAll(this ObservableCollection collection, IEnumerable newItems) 31 | { 32 | collection.Clear(); 33 | collection.AddRange(newItems); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Settings Store Explorer")] 8 | [assembly: AssemblyDescription("A Visual Studio Extension with a tool window for viewing and editing the Visual Studio settings store.")] 9 | [assembly: AssemblyProduct("SettingsStoreExplorer")] 10 | [assembly: AssemblyTrademark("")] 11 | [assembly: AssemblyCulture("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(false)] 17 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/QwordToStringConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Globalization; 4 | 5 | namespace SettingsStoreExplorer 6 | { 7 | internal sealed class QwordToStringConverter : IntegerToStringConverter 8 | { 9 | public static readonly QwordToStringConverter Instance = new QwordToStringConverter(); 10 | 11 | private QwordToStringConverter() 12 | { 13 | } 14 | 15 | public override string ToString(object value, NumberStyles style, CultureInfo culture) 16 | => ((ulong)value).ToString(FormatStringFromStyle(style), culture); 17 | 18 | public override bool TryParse(string text, NumberStyles style, CultureInfo culture, out object value) 19 | { 20 | if (ulong.TryParse(text, style, culture, out var result)) 21 | { 22 | value = result; 23 | return true; 24 | } 25 | 26 | value = null; 27 | return false; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/Resources/SettingsStoreExplorer.128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pharring/SettingsStoreExplorer/65181ecd615bbb0df51ee6bcb28203bcefe44558/src/SettingsStoreExplorer/Resources/SettingsStoreExplorer.128x128.png -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/Resources/SettingsStoreExplorer.16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pharring/SettingsStoreExplorer/65181ecd615bbb0df51ee6bcb28203bcefe44558/src/SettingsStoreExplorer/Resources/SettingsStoreExplorer.16x16.png -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/Resources/SettingsStoreExplorer.dark.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/Resources/SettingsStoreExplorer.light.xaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/RoamingSettingsStore.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.Linq; 7 | using Microsoft.VisualStudio; 8 | using Microsoft.VisualStudio.Settings; 9 | using Microsoft.VisualStudio.Shell.Interop; 10 | 11 | namespace SettingsStoreExplorer 12 | { 13 | internal class RoamingSettingsStore : IVsSettingsStore 14 | { 15 | private readonly ISettingsManager _settingsManager; 16 | private string[] _names; 17 | 18 | public RoamingSettingsStore(ISettingsManager settingsManager) 19 | { 20 | _settingsManager = settingsManager ?? throw new ArgumentNullException(nameof(settingsManager)); 21 | } 22 | 23 | /// 24 | /// Compute the insert point for the given search term. 25 | /// 26 | /// The sorted array of terms. 27 | /// The term to search for. 28 | /// The index where should be inserted into to maintain the ordering. 29 | private static int LowerBound(T[] corpus, T searchTerm, IComparer comparer) 30 | { 31 | int lo = 0, hi = corpus.Length; 32 | 33 | while (lo != hi) 34 | { 35 | var mid = lo + (hi - lo) / 2; 36 | 37 | var midValue = corpus[mid]; 38 | 39 | if (comparer.Compare(midValue, searchTerm) < 0) 40 | { 41 | lo = mid + 1; 42 | } 43 | else 44 | { 45 | hi = mid; 46 | } 47 | } 48 | 49 | return lo; 50 | } 51 | 52 | private string[] AllNames => _names ?? (_names = InitializeNames()); 53 | 54 | /// 55 | /// Retrieve all settings from the settings manager, sort them and filter out _metadata 56 | /// 57 | /// The initial set of settings. 58 | private string[] InitializeNames() 59 | { 60 | var names = _settingsManager.NamesStartingWith(""); 61 | Array.Sort(names, SettingsComparer.Instance); 62 | 63 | // Remove "_metadata." 64 | var metadataStart = LowerBound(names, "_metadata.", SettingsComparer.Instance); 65 | int metadataLength = 0; 66 | 67 | for (int i = metadataStart; i < names.Length; i++, metadataLength++) 68 | { 69 | if (!names[i].StartsWith("_metadata.", StringComparison.Ordinal)) 70 | { 71 | break; 72 | } 73 | } 74 | 75 | if (metadataLength > 0) 76 | { 77 | int metadataEnd = metadataStart + metadataLength; 78 | Array.Copy(names, metadataEnd, names, metadataStart, names.Length - metadataEnd); 79 | Array.Resize(ref names, names.Length - metadataLength); 80 | } 81 | 82 | return names; 83 | } 84 | 85 | private IEnumerable NamesStartingWith(string prefix) 86 | { 87 | var names = AllNames; 88 | for (var index = LowerBound(AllNames, prefix, SettingsComparer.Instance); index < names.Length; index++) 89 | { 90 | var name = names[index]; 91 | if (!name.StartsWith(prefix, StringComparison.Ordinal)) 92 | { 93 | break; 94 | } 95 | 96 | yield return name; 97 | } 98 | } 99 | 100 | private IEnumerable GetSubCollections(string collectionPath) 101 | { 102 | if (collectionPath.Length > 0) 103 | { 104 | collectionPath += "."; 105 | } 106 | 107 | string lastYield = null; 108 | var suffixStart = collectionPath.Length; 109 | 110 | foreach (var name in NamesStartingWith(collectionPath)) 111 | { 112 | var separatorIndex = name.IndexOf('.', suffixStart); 113 | if (separatorIndex < 0) 114 | { 115 | // Found a property, not a subcollection 116 | continue; 117 | } 118 | 119 | var candidateLength = separatorIndex - suffixStart; 120 | if (lastYield == null || string.CompareOrdinal(lastYield, 0, name, suffixStart, candidateLength) != 0) 121 | { 122 | yield return lastYield = name.Substring(suffixStart, candidateLength); 123 | } 124 | } 125 | } 126 | 127 | private IEnumerable GetProperties(string collectionPath) 128 | { 129 | if (collectionPath.Length == 0) 130 | { 131 | // Properties of the root node are at the beginning of the collection. 132 | foreach (var name in _names) 133 | { 134 | if (name.Contains('.')) 135 | { 136 | // Reached the first subcollection. 137 | break; 138 | } 139 | 140 | yield return name; 141 | } 142 | } 143 | else 144 | { 145 | collectionPath += "."; 146 | var prefixLength = collectionPath.Length; 147 | 148 | foreach (var name in NamesStartingWith(collectionPath)) 149 | { 150 | if (name.IndexOf('.', prefixLength) >= 0) 151 | { 152 | // There is a subcollection here. 153 | continue; 154 | } 155 | 156 | yield return name.Substring(prefixLength); 157 | } 158 | } 159 | } 160 | 161 | private static string MakeFullName(string collectionPath, string propertyName) 162 | => string.IsNullOrEmpty(collectionPath) ? propertyName : (collectionPath + "." + propertyName); 163 | 164 | public int GetBool(string collectionPath, string propertyName, out int value) => throw new NotImplementedException(); 165 | public int GetInt(string collectionPath, string propertyName, out int value) => throw new NotImplementedException(); 166 | 167 | public int GetUnsignedInt(string collectionPath, string propertyName, out uint value) 168 | { 169 | if (_settingsManager.TryGetValue(MakeFullName(collectionPath, propertyName), out object obj) == GetValueResult.Success) 170 | { 171 | value = Convert.ToUInt32(obj); 172 | return VSConstants.S_OK; 173 | } 174 | 175 | value = 0; 176 | return VSConstants.E_FAIL; 177 | } 178 | 179 | public int GetInt64(string collectionPath, string propertyName, out long value) => throw new NotImplementedException(); 180 | 181 | public int GetUnsignedInt64(string collectionPath, string propertyName, out ulong value) 182 | { 183 | if (_settingsManager.TryGetValue(MakeFullName(collectionPath, propertyName), out object obj) == GetValueResult.Success) 184 | { 185 | value = Convert.ToUInt64(obj); 186 | return VSConstants.S_OK; 187 | } 188 | 189 | value = 0; 190 | return VSConstants.E_FAIL; 191 | } 192 | 193 | public int GetString(string collectionPath, string propertyName, out string value) 194 | { 195 | if (_settingsManager.TryGetValue(MakeFullName(collectionPath, propertyName), out object obj) == GetValueResult.Success) 196 | { 197 | value = Convert.ToString(obj, CultureInfo.InvariantCulture); 198 | return VSConstants.S_OK; 199 | } 200 | 201 | value = null; 202 | return VSConstants.E_FAIL; 203 | } 204 | 205 | public int GetBinary(string collectionPath, string propertyName, uint byteLength, byte[] pBytes, uint[] actualByteLength) => throw new NotImplementedException(); 206 | public int GetBoolOrDefault(string collectionPath, string propertyName, int defaultValue, out int value) => throw new NotImplementedException(); 207 | public int GetIntOrDefault(string collectionPath, string propertyName, int defaultValue, out int value) => throw new NotImplementedException(); 208 | public int GetUnsignedIntOrDefault(string collectionPath, string propertyName, uint defaultValue, out uint value) => throw new NotImplementedException(); 209 | public int GetInt64OrDefault(string collectionPath, string propertyName, long defaultValue, out long value) => throw new NotImplementedException(); 210 | public int GetUnsignedInt64OrDefault(string collectionPath, string propertyName, ulong defaultValue, out ulong value) => throw new NotImplementedException(); 211 | public int GetStringOrDefault(string collectionPath, string propertyName, string defaultValue, out string value) => throw new NotImplementedException(); 212 | public int GetPropertyType(string collectionPath, string propertyName, out uint type) 213 | { 214 | var fullName = string.IsNullOrEmpty(collectionPath) ? propertyName : (collectionPath + "." + propertyName); 215 | var result = _settingsManager.TryGetValue(fullName, out object value); 216 | if (result == GetValueResult.Success) 217 | { 218 | switch (value) 219 | { 220 | case string _: 221 | type = (uint)__VsSettingsType.SettingsType_String; 222 | return VSConstants.S_OK; 223 | 224 | case uint _: 225 | case int _: 226 | case bool _: 227 | type = (uint)__VsSettingsType.SettingsType_Int; 228 | return VSConstants.S_OK; 229 | 230 | case ulong _: 231 | case long _: 232 | type = (uint)__VsSettingsType.SettingsType_Int64; 233 | return VSConstants.S_OK; 234 | 235 | default: 236 | // Treat it like a string 237 | type = (uint)__VsSettingsType.SettingsType_String; 238 | return VSConstants.S_OK; 239 | 240 | } 241 | } 242 | 243 | type = 0; 244 | return VSConstants.E_FAIL; 245 | } 246 | 247 | public int PropertyExists(string collectionPath, string propertyName, out int pfExists) => throw new NotImplementedException(); 248 | public int CollectionExists(string collectionPath, out int pfExists) => throw new NotImplementedException(); 249 | public int GetSubCollectionCount(string collectionPath, out uint subCollectionCount) => throw new NotImplementedException(); 250 | public int GetPropertyCount(string collectionPath, out uint propertyCount) => throw new NotImplementedException(); 251 | public int GetLastWriteTime(string collectionPath, SYSTEMTIME[] lastWriteTime) => throw new NotImplementedException(); 252 | 253 | public int GetSubCollectionName(string collectionPath, uint index, out string subCollectionName) 254 | { 255 | foreach (var name in GetSubCollections(collectionPath)) 256 | { 257 | if (index-- == 0) 258 | { 259 | subCollectionName = name; 260 | return VSConstants.S_OK; 261 | } 262 | } 263 | 264 | subCollectionName = null; 265 | return VSConstants.E_INVALIDARG; 266 | } 267 | 268 | public int GetPropertyName(string collectionPath, uint index, out string propertyName) 269 | { 270 | foreach (var name in GetProperties(collectionPath)) 271 | { 272 | if (index-- == 0) 273 | { 274 | propertyName = name; 275 | return VSConstants.S_OK; 276 | } 277 | } 278 | 279 | propertyName = null; 280 | return VSConstants.E_INVALIDARG; 281 | } 282 | 283 | /// 284 | /// Custom comparer for sorting setting names. 285 | /// The period char is a separator, so it should sort ahead of any other char. 286 | /// A special case is for names that have no dots; they are properties 287 | /// of the root node. 288 | /// 289 | private class SettingsComparer : IComparer 290 | { 291 | public static readonly SettingsComparer Instance = new SettingsComparer(); 292 | 293 | private SettingsComparer() { } 294 | 295 | public int Compare(string x, string y) 296 | { 297 | if (x == null) 298 | { 299 | // Null is less than everything except null 300 | return y == null ? 0 : -1; 301 | } 302 | 303 | if (y == null) 304 | { 305 | // Everything is greater than null 306 | return 1; 307 | } 308 | 309 | // Special case: Root properties (with no dot) sort first. 310 | if (x.Contains('.')) 311 | { 312 | if (!y.Contains('.')) 313 | { 314 | // y is a root property 315 | return 1; 316 | } 317 | } 318 | else 319 | { 320 | // x is a root property 321 | if (y.Contains('.')) 322 | { 323 | return -1; 324 | } 325 | } 326 | 327 | for (int i = 0; i < x.Length; i++) 328 | { 329 | if (i >= y.Length) 330 | { 331 | // x is longer than y 332 | return 1; 333 | } 334 | 335 | char cx = x[i]; 336 | char cy = y[i]; 337 | if (cx != cy) 338 | { 339 | // dot sorts first 340 | if (cx == '.') 341 | { 342 | return -1; 343 | } 344 | 345 | if (cy == '.') 346 | { 347 | return 1; 348 | } 349 | 350 | return cx.CompareTo(cy); 351 | } 352 | } 353 | 354 | // x is equal to or a prefix of y 355 | return y.Length == x.Length ? 0 : -1; 356 | } 357 | } 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/SettingsStoreCommandSet.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using System.ComponentModel.Design; 5 | 6 | namespace SettingsStoreExplorer 7 | { 8 | internal static class SettingsStoreCommandSet 9 | { 10 | /// 11 | /// Command menu group (command set GUID). 12 | /// 13 | public static readonly Guid CommandSet = new Guid("9fc9f69d-174d-4876-b28b-dc1e4fac89dc"); 14 | 15 | private static CommandID MakeCommandID(int dword) => new CommandID(CommandSet, dword); 16 | 17 | // Commands (buttons) 18 | public static readonly CommandID SettingsStoreExplorerToolWindowCommandId = MakeCommandID(0x0100); 19 | public static readonly CommandID AddNewSubCollectionCommandId = MakeCommandID(0x0101); 20 | public static readonly CommandID AddNewStringValueCommandId = MakeCommandID(0x0102); 21 | public static readonly CommandID AddNewDWORDValueCommandId = MakeCommandID(0x0103); 22 | public static readonly CommandID AddNewQWORDValueCommandId = MakeCommandID(0x0104); 23 | public static readonly CommandID AddNewBinaryValueCommandId = MakeCommandID(0x0105); 24 | public static readonly CommandID RenameCommandId = MakeCommandID(0x0106); 25 | public static readonly CommandID DeleteCommandId = MakeCommandID(0x0107); 26 | public static readonly CommandID ModifyCommandId = MakeCommandID(0x0108); 27 | public static readonly CommandID RefreshCommandId = MakeCommandID(0x0109); 28 | public static readonly CommandID CopyKeyNameCommandId = MakeCommandID(0x010A); 29 | public static readonly CommandID CopyPropertyNameCommandId = MakeCommandID(0x010B); 30 | public static readonly CommandID CopyPropertyValueCommandId = MakeCommandID(0x010C); 31 | 32 | // Menus 33 | public static readonly CommandID SubCollectionNewContextMenu = MakeCommandID(0x300); 34 | public static readonly CommandID TreeViewItemContextMenu = MakeCommandID(0x301); 35 | public static readonly CommandID ListViewItemContextMenu = MakeCommandID(0x302); 36 | public static readonly CommandID ListViewContextMenu = MakeCommandID(0x303); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/SettingsStoreExplorer.imagemanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/SettingsStoreExplorer.projitems: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | 566912fa-295c-430e-b8f3-1140e4490d04 7 | 8 | 9 | SettingsStoreExplorer 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | EditBinaryDialog.xaml 18 | 19 | 20 | EditIntegerDialog.xaml 21 | 22 | 23 | EditStringDialog.xaml 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | HexDumpControl.xaml 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | SettingsStoreExplorerToolWindowControl.xaml 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | VSPackage.resx 65 | True 66 | True 67 | 68 | 69 | 70 | 71 | PreserveNewest 72 | 73 | 74 | true 75 | 76 | 77 | 78 | 79 | Designer 80 | MSBuild:Compile 81 | 82 | 83 | Designer 84 | MSBuild:Compile 85 | 86 | 87 | Designer 88 | MSBuild:Compile 89 | 90 | 91 | Designer 92 | MSBuild:Compile 93 | 94 | 95 | Designer 96 | MSBuild:Compile 97 | 98 | 99 | Designer 100 | MSBuild:Compile 101 | 102 | 103 | Designer 104 | MSBuild:Compile 105 | 106 | 107 | Designer 108 | MSBuild:Compile 109 | 110 | 111 | 112 | 113 | true 114 | SettingsStoreExplorer.VSPackage 115 | Designer 116 | ResXFileCodeGenerator 117 | VSPackage.Designer.cs 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/SettingsStoreExplorer.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 566912fa-295c-430e-b8f3-1140e4490d04 5 | 14.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/SettingsStoreExplorerPackage.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Runtime.InteropServices; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using Microsoft.VisualStudio; 9 | using Microsoft.VisualStudio.Shell; 10 | using Microsoft.VisualStudio.Shell.Interop; 11 | using Task = System.Threading.Tasks.Task; 12 | 13 | namespace SettingsStoreExplorer 14 | { 15 | /// 16 | /// This is the class that implements the package exposed by this assembly. 17 | /// 18 | /// 19 | /// 20 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 21 | /// is to implement the IVsPackage interface and register itself with the shell. 22 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 23 | /// to do it: it derives from the Package class that provides the implementation of the 24 | /// IVsPackage interface and uses the registration attributes defined in the framework to 25 | /// register itself and its components with the shell. These attributes tell the pkgdef creation 26 | /// utility what data to put into .pkgdef file. 27 | /// 28 | /// 29 | /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file. 30 | /// 31 | /// 32 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 33 | [Guid(c_packageGuidString)] 34 | [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")] 35 | [ProvideMenuResource("Menus.ctmenu", 1)] 36 | [ProvideToolWindow(typeof(SettingsStoreExplorerToolWindow), Style = VsDockStyle.Tabbed, DockedWidth = 600, Window = "DocumentWell", Orientation = ToolWindowOrientation.Left)] 37 | [ProvideKeyBindingTable(SettingsStoreExplorerToolWindow.c_toolWindowGuidString, 113)] 38 | public sealed class SettingsStoreExplorerPackage : AsyncPackage 39 | { 40 | /// 41 | /// SettingsStoreExplorerPackage GUID string. 42 | /// 43 | private const string c_packageGuidString = "e8762000-5824-4411-bc19-417b39b309f5"; 44 | 45 | /// 46 | /// Initializes a new instance of the class. 47 | /// 48 | public SettingsStoreExplorerPackage() 49 | { 50 | // Inside this method you can place any initialization code that does not require 51 | // any Visual Studio service because at this point the package object is created but 52 | // not sited yet inside Visual Studio environment. The place to do all the other 53 | // initialization is the Initialize method. 54 | } 55 | 56 | #region AsyncPackage Members 57 | 58 | /// 59 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 60 | /// where you can put all the initialization code that rely on services provided by VisualStudio. 61 | /// 62 | /// A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down. 63 | /// A provider for progress updates. 64 | /// A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method. 65 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 66 | { 67 | var shellVersion = await GetShellVersionAsync(cancellationToken); 68 | var initializeTelemetry = Telemetry.CreateInitializeTelemetryItem(nameof(SettingsStoreExplorerPackage) + "." + nameof(InitializeAsync)); 69 | initializeTelemetry.Properties.Add("VSVersion", shellVersion); 70 | Telemetry.Client.TrackEvent(initializeTelemetry); 71 | 72 | await SettingsStoreExplorerToolWindowCommand.InitializeAsync(this); 73 | } 74 | 75 | private async Task GetShellVersionAsync(CancellationToken cancellationToken) 76 | { 77 | await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); 78 | if (await GetServiceAsync(typeof(SVsShell)) is IVsShell shell) 79 | { 80 | if (ErrorHandler.Succeeded(shell.GetProperty((int)__VSSPROPID5.VSSPROPID_ReleaseVersion, out var obj)) && obj != null) 81 | { 82 | return obj.ToString(); 83 | } 84 | } 85 | 86 | return "Unknown"; 87 | } 88 | 89 | #endregion 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/SettingsStoreExplorerToolWindowCommand.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using System.ComponentModel.Design; 5 | using Microsoft.VisualStudio.Shell; 6 | using Microsoft.VisualStudio.Shell.Interop; 7 | using Task = System.Threading.Tasks.Task; 8 | using static SettingsStoreExplorer.SettingsStoreCommandSet; 9 | 10 | namespace SettingsStoreExplorer 11 | { 12 | /// 13 | /// Command handler 14 | /// 15 | internal sealed class SettingsStoreExplorerToolWindowCommand 16 | { 17 | /// 18 | /// VS Package that provides this command, not null. 19 | /// 20 | private readonly AsyncPackage _package; 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// Adds our command handlers for menu (commands must exist in the command table file) 25 | /// 26 | /// Owner package, not null. 27 | /// Command service to add command to, not null. 28 | private SettingsStoreExplorerToolWindowCommand(AsyncPackage package, OleMenuCommandService commandService) 29 | { 30 | ThreadHelper.ThrowIfNotOnUIThread(); 31 | 32 | _package = package ?? throw new ArgumentNullException(nameof(package)); 33 | commandService = commandService ?? throw new ArgumentNullException(nameof(commandService)); 34 | 35 | var menuItem = new MenuCommand(Execute, SettingsStoreExplorerToolWindowCommandId); 36 | commandService.AddCommand(menuItem); 37 | } 38 | 39 | /// 40 | /// Gets the instance of the command. 41 | /// 42 | public static SettingsStoreExplorerToolWindowCommand Instance { get; private set; } 43 | 44 | /// 45 | /// Initializes the singleton instance of the command. 46 | /// 47 | /// Owner package, not null. 48 | public static async Task InitializeAsync(AsyncPackage package) 49 | { 50 | // Switch to the main thread - the call to AddCommand in SettingsStoreExplorerToolWindowCommand's constructor requires 51 | // the UI thread. 52 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 53 | 54 | var commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; 55 | Instance = new SettingsStoreExplorerToolWindowCommand(package, commandService); 56 | } 57 | 58 | /// 59 | /// Shows the tool window when the menu item is clicked. 60 | /// 61 | /// The event sender. 62 | /// The event args. 63 | private void Execute(object sender, EventArgs e) 64 | { 65 | ThreadHelper.ThrowIfNotOnUIThread(); 66 | 67 | // Get the instance number 0 of this tool window. This window is single instance so this instance 68 | // is actually the only one. 69 | // The last flag is set to true so that if the tool window does not exists it will be created. 70 | var window = _package.FindToolWindow(typeof(SettingsStoreExplorerToolWindow), 0, true); 71 | if (window is null || window.Frame is null) 72 | { 73 | throw new NotSupportedException("Cannot create tool window"); 74 | } 75 | 76 | var windowFrame = (IVsWindowFrame)window.Frame; 77 | Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show()); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/SettingsStoreExplorerToolWindowControl.xaml: -------------------------------------------------------------------------------- 1 |  14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/SettingsStoreItemExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using Microsoft.VisualStudio; 5 | using Microsoft.VisualStudio.Shell; 6 | using Microsoft.VisualStudio.Shell.Interop; 7 | using static System.FormattableString; 8 | 9 | namespace SettingsStoreExplorer 10 | { 11 | internal static class SettingsStoreItemExtensions 12 | { 13 | public static bool TryGetWritableSettingsStore(this SettingsStoreItem settingsStoreItem, out IVsWritableSettingsStore writableSettingsStore) 14 | { 15 | ThreadHelper.ThrowIfNotOnUIThread(); 16 | 17 | if (!(ServiceProvider.GlobalProvider.GetService(typeof(SVsSettingsManager)) is IVsSettingsManager settingsManager)) 18 | { 19 | writableSettingsStore = null; 20 | return false; 21 | } 22 | 23 | return ErrorHandler.Succeeded(settingsManager.GetWritableSettingsStore((uint)settingsStoreItem.Root.EnclosingScope, out writableSettingsStore)); 24 | } 25 | 26 | public static SettingsStoreSubCollection GenerateNewSubCollection(this SettingsStoreSubCollection subCollection) 27 | { 28 | ThreadHelper.ThrowIfNotOnUIThread(); 29 | var settingsStore = subCollection.Root.SettingsStore; 30 | 31 | for (int i = 1; i < 100; i++) 32 | { 33 | var name = Invariant($"New Collection #{i}"); 34 | var newCollection = new SettingsStoreSubCollection(subCollection, name); 35 | 36 | ErrorHandler.ThrowOnFailure(settingsStore.CollectionExists(newCollection.Path, out int exists)); 37 | if (exists == 0) 38 | { 39 | return newCollection; 40 | } 41 | } 42 | 43 | throw new InvalidOperationException("Could not find a unique name for the new subcollection."); 44 | } 45 | 46 | public static string GenerateNewPropertyName(this SettingsStoreSubCollection subCollection) 47 | { 48 | ThreadHelper.ThrowIfNotOnUIThread(); 49 | var settingsStore = subCollection.Root.SettingsStore; 50 | 51 | for (int i = 1; i < 100; i++) 52 | { 53 | var name = Invariant($"New Value #{i}"); 54 | 55 | ErrorHandler.ThrowOnFailure(settingsStore.PropertyExists(subCollection.Path, name, out int exists)); 56 | if (exists == 0) 57 | { 58 | return name; 59 | } 60 | } 61 | 62 | throw new InvalidOperationException("Could not find a unique name for the new value."); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/SettingsStoreItemToImageMonikerConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Globalization; 4 | using Microsoft.VisualStudio.Imaging.Interop; 5 | using Microsoft.VisualStudio.PlatformUI; 6 | using VsKnownMonikers = Microsoft.VisualStudio.Imaging.KnownMonikers; 7 | 8 | namespace SettingsStoreExplorer 9 | { 10 | internal class SettingsStoreItemToImageMonikerConverter : ValueConverter 11 | { 12 | protected override ImageMoniker Convert(SettingsStoreItem value, object parameter, CultureInfo culture) 13 | { 14 | switch(value) 15 | { 16 | case RootSettingsStore rootItem: 17 | switch (rootItem.EnclosingScope) 18 | { 19 | case Scope.User: 20 | return VsKnownMonikers.User; 21 | 22 | case Scope.Remote: 23 | return VsKnownMonikers.ServerSettings; 24 | 25 | case Scope.Roaming: 26 | return VsKnownMonikers.SyncServer; 27 | 28 | default: 29 | return VsKnownMonikers.Registry; 30 | } 31 | 32 | case SettingsStoreSubCollection _: 33 | return VsKnownMonikers.FolderClosed; 34 | 35 | default: 36 | return base.Convert(value, parameter, culture); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/SettingsStorePropertyNameConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Globalization; 4 | using Microsoft.VisualStudio.PlatformUI; 5 | 6 | namespace SettingsStoreExplorer 7 | { 8 | internal class SettingsStorePropertyNameConverter : ValueConverter 9 | { 10 | protected override string Convert(string value, object parameter, CultureInfo culture) 11 | => string.IsNullOrEmpty(value) ? "(Default)" : value; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/SettingsStorePropertyValueConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Globalization; 4 | using System.Linq; 5 | using Microsoft.VisualStudio.PlatformUI; 6 | 7 | namespace SettingsStoreExplorer 8 | { 9 | internal class SettingsStorePropertyValueConverter : ValueConverter 10 | { 11 | protected override string Convert(object value, object parameter, CultureInfo culture) 12 | { 13 | if (value is string) 14 | { 15 | return (string)value; 16 | } 17 | 18 | if (value is byte[] arr) 19 | { 20 | if (arr.Length == 0) 21 | { 22 | return "(zero-length binary value)"; 23 | } 24 | 25 | var query = arr.Select(b => b.ToString("X2")); 26 | return string.Join(" ", query); 27 | } 28 | 29 | return value is uint 30 | ? string.Format(culture, "0x{0:x8} ({0})", value) 31 | : value is ulong ? string.Format(culture, "0x{0:x16} ({0})", value) : ""; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/SettingsStoreTypeToImageMonikerConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Globalization; 4 | using Microsoft.VisualStudio.Imaging.Interop; 5 | using Microsoft.VisualStudio.PlatformUI; 6 | using Microsoft.VisualStudio.Shell.Interop; 7 | using VsKnownMonikers = Microsoft.VisualStudio.Imaging.KnownMonikers; 8 | 9 | namespace SettingsStoreExplorer 10 | { 11 | internal class SettingsStoreTypeToImageMonikerConverter : ValueConverter<__VsSettingsType, ImageMoniker> 12 | { 13 | protected override ImageMoniker Convert(__VsSettingsType value, object parameter, CultureInfo culture) 14 | { 15 | switch (value) 16 | { 17 | case __VsSettingsType.SettingsType_Int: 18 | case __VsSettingsType.SettingsType_Int64: 19 | return VsKnownMonikers.Numeric; 20 | 21 | case __VsSettingsType.SettingsType_String: 22 | return VsKnownMonikers.StringRegistryValue; 23 | 24 | case __VsSettingsType.SettingsType_Binary: 25 | return VsKnownMonikers.BinaryRegistryValue; 26 | 27 | default: 28 | return default; 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/SettingsStoreTypeToRegTypeConverter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System.Globalization; 4 | using Microsoft.VisualStudio.PlatformUI; 5 | using Microsoft.VisualStudio.Shell.Interop; 6 | 7 | namespace SettingsStoreExplorer 8 | { 9 | internal class SettingsStoreTypeToRegTypeConverter : ValueConverter<__VsSettingsType, string> 10 | { 11 | protected override string Convert(__VsSettingsType value, object parameter, CultureInfo culture) 12 | { 13 | switch(value) 14 | { 15 | case __VsSettingsType.SettingsType_Binary: 16 | return "REG_BINARY"; 17 | 18 | case __VsSettingsType.SettingsType_Int: 19 | return "REG_DWORD"; 20 | 21 | case __VsSettingsType.SettingsType_String: 22 | return "REG_SZ"; 23 | 24 | case __VsSettingsType.SettingsType_Int64: 25 | return "REG_QWORD"; 26 | 27 | default: 28 | return "Unknown"; 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/Telemetry.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using Microsoft.ApplicationInsights; 4 | using Microsoft.ApplicationInsights.Channel; 5 | using Microsoft.ApplicationInsights.DataContracts; 6 | using Microsoft.ApplicationInsights.Extensibility; 7 | using System; 8 | using System.Security.Cryptography; 9 | 10 | namespace SettingsStoreExplorer 11 | { 12 | internal static class Telemetry 13 | { 14 | private const string c_instrumentationKey = "959aadb2-1af1-4ec5-8ac8-80775b0310b8"; 15 | 16 | public static TelemetryClient Client { get; } = CreateClient(); 17 | 18 | /// 19 | /// Create a telemetry item for the 'initialize' event with additional properties 20 | /// that only need to be sent once. 21 | /// 22 | /// The name of the initialize event. 23 | /// A custom event telemetry with additional context for OS and component version. 24 | public static EventTelemetry CreateInitializeTelemetryItem(string name) 25 | { 26 | var eventTelemetry = new EventTelemetry(name); 27 | eventTelemetry.Context.Device.OperatingSystem = Environment.OSVersion.ToString(); 28 | eventTelemetry.Context.Component.Version = typeof(Telemetry).Assembly.GetName().Version.ToString(); 29 | return eventTelemetry; 30 | } 31 | 32 | private static TelemetryClient CreateClient() 33 | { 34 | var configuration = new TelemetryConfiguration 35 | { 36 | InstrumentationKey = c_instrumentationKey, 37 | TelemetryChannel = new InMemoryChannel 38 | { 39 | #if DEBUG 40 | DeveloperMode = true 41 | #else 42 | DeveloperMode = false 43 | #endif 44 | } 45 | }; 46 | 47 | // Keep this context as small as possible since it's sent with every event. 48 | var client = new TelemetryClient(configuration); 49 | client.Context.User.Id = Anonymize(Environment.UserDomainName + "\\" + Environment.UserName); 50 | client.Context.Session.Id = Convert.ToBase64String(GetRandomBytes(length:6)); 51 | return client; 52 | } 53 | 54 | private static byte[] GetRandomBytes(int length) 55 | { 56 | var buff = new byte[length]; 57 | RandomNumberGenerator.Create().GetBytes(buff); 58 | return buff; 59 | } 60 | 61 | private static string Anonymize(string str) 62 | { 63 | #pragma warning disable CA5350 // Do Not Use Weak Cryptographic Algorithms 64 | using (var sha1 = SHA1.Create()) 65 | #pragma warning restore CA5350 // Do Not Use Weak Cryptographic Algorithms 66 | { 67 | var inputBytes = System.Text.Encoding.Unicode.GetBytes(str); 68 | var hash = sha1.ComputeHash(inputBytes); 69 | var base64 = Convert.ToBase64String(hash, 0, 6); 70 | return base64; 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/TextBoxSelectionAdorner.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Documents; 7 | using System.Windows.Media; 8 | 9 | namespace SettingsStoreExplorer 10 | { 11 | /// 12 | /// An adorner for text boxes that paints the selection even when it doesn't have focus. 13 | /// 14 | public class TextBoxSelectionAdorner : Adorner 15 | { 16 | public TextBoxSelectionAdorner(TextBox textBox) : base(textBox) 17 | { 18 | } 19 | 20 | protected override void OnRender(DrawingContext drawingContext) 21 | { 22 | var textBox = (TextBox)AdornedElement; 23 | 24 | if (textBox.SelectionLength == 0) 25 | { 26 | // No selection. 27 | return; 28 | } 29 | 30 | if (textBox.IsKeyboardFocused) 31 | { 32 | // Let the text box draw its own selection adorner. 33 | return; 34 | } 35 | 36 | // We use ViewportWidth/Height to account for scroll bars. 37 | // TODO: The +4 is necessary because ViewportWidth ignores the margin between the right edge of text and the vertical scroll bar. 38 | // TODO: Figure out where this comes from and if it can be calculated. 39 | drawingContext.PushClip(new RectangleGeometry(new Rect(0, 0, textBox.ViewportWidth + 4, textBox.ViewportHeight))); 40 | 41 | int firstCharIndex = textBox.SelectionStart; 42 | int lastCharIndex = firstCharIndex + textBox.SelectionLength; 43 | var firstCharRect = textBox.GetRectFromCharacterIndex(firstCharIndex); 44 | var lastCharRect = textBox.GetRectFromCharacterIndex(lastCharIndex); 45 | 46 | // CONSIDER: Cache the geometry and invalidate it only when the selection changes. 47 | 48 | var highlightGeometry = new GeometryGroup(); 49 | if (firstCharRect.Top == lastCharRect.Top) 50 | { 51 | // single line selection 52 | highlightGeometry.Children.Add(new RectangleGeometry(new Rect(firstCharRect.TopLeft, lastCharRect.BottomRight))); 53 | } 54 | else 55 | { 56 | int firstVisibleLine = textBox.GetFirstVisibleLineIndex(); 57 | int lastVisibleLine = textBox.GetLastVisibleLineIndex(); 58 | if (textBox.GetLineIndexFromCharacterIndex(firstCharIndex) < firstVisibleLine) 59 | { 60 | firstCharIndex = textBox.GetCharacterIndexFromLineIndex(firstVisibleLine - 1); 61 | firstCharRect = textBox.GetRectFromCharacterIndex(firstCharIndex); 62 | } 63 | if (textBox.GetLineIndexFromCharacterIndex(lastCharIndex) > lastVisibleLine) 64 | { 65 | lastCharIndex = textBox.GetCharacterIndexFromLineIndex(lastVisibleLine + 1); 66 | lastCharRect = textBox.GetRectFromCharacterIndex(lastCharIndex); 67 | } 68 | 69 | var lineHeight = firstCharRect.Height; 70 | var lineCount = (int)Math.Round((lastCharRect.Top - firstCharRect.Top) / lineHeight); 71 | var lineLeft = firstCharRect.Left; 72 | var lineTop = firstCharRect.Top; 73 | var currentCharIndex = firstCharIndex; 74 | for (int i = 0; i <= lineCount; i++) 75 | { 76 | var lineIndex = textBox.GetLineIndexFromCharacterIndex(currentCharIndex); 77 | var startOfLineCharIndex = textBox.GetCharacterIndexFromLineIndex(lineIndex); 78 | var lineLength = textBox.GetLineLength(lineIndex); 79 | 80 | var endOfLineCharIndex = startOfLineCharIndex + lineLength - 1; 81 | if (endOfLineCharIndex > lastCharIndex) 82 | { 83 | endOfLineCharIndex = lastCharIndex; 84 | } 85 | 86 | var endOfLineCharRect = textBox.GetRectFromCharacterIndex(endOfLineCharIndex); 87 | var lineWidth = endOfLineCharRect.Right - lineLeft; 88 | 89 | if (i < lineCount) 90 | { 91 | // There's an adjustment (for padding?) for selection that extends over multiple lines 92 | // TODO: I came up with this by emprical observation, but it would be nice to figure 93 | // out where this comes from in the sources. 94 | lineWidth += textBox.FontSize / 2; 95 | } 96 | 97 | if (Math.Round(lineWidth) <= 0) 98 | { 99 | lineWidth = 5; 100 | } 101 | 102 | highlightGeometry.Children.Add(new RectangleGeometry(new Rect(lineLeft, lineTop, lineWidth, lineHeight))); 103 | currentCharIndex = startOfLineCharIndex + lineLength; 104 | var nextLineFirstCharRect = textBox.GetRectFromCharacterIndex(currentCharIndex); 105 | lineLeft = nextLineFirstCharRect.Left; 106 | lineTop = nextLineFirstCharRect.Top; 107 | } 108 | } 109 | 110 | // The underlying text box uses an opacity of 0.4 to draw the selection highlight. 111 | // We use something slightly lower as a visual indication that we don't have keyboard 112 | // focus. 113 | drawingContext.PushOpacity(0.25); 114 | 115 | // TODO: Use a themed brush 116 | drawingContext.DrawGeometry(SystemColors.HighlightBrush, null, highlightGeometry); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/TextBoxWithSelectionAdorner.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Paul Harrington. All Rights Reserved. Licensed under the MIT License. See LICENSE in the project root for license information. 2 | 3 | using System; 4 | using System.Windows.Controls; 5 | using System.Windows.Documents; 6 | using System.Windows.Input; 7 | 8 | namespace SettingsStoreExplorer 9 | { 10 | /// 11 | /// A text box with a custom adorner to show selection even when not in focus. 12 | /// 13 | internal class TextBoxWithSelectionAdorner : TextBox 14 | { 15 | /// 16 | /// The selection adorner object. 17 | /// 18 | private readonly Adorner _adorner; 19 | 20 | public TextBoxWithSelectionAdorner() => _adorner = new TextBoxSelectionAdorner(this); 21 | 22 | protected override void OnInitialized(EventArgs e) 23 | { 24 | AdornerLayer.GetAdornerLayer(this)?.Add(_adorner); 25 | base.OnInitialized(e); 26 | } 27 | 28 | protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) 29 | { 30 | base.OnLostKeyboardFocus(e); 31 | _adorner.InvalidateVisual(); 32 | } 33 | 34 | protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) 35 | { 36 | base.OnGotKeyboardFocus(e); 37 | _adorner.InvalidateVisual(); 38 | } 39 | 40 | protected override void OnSelectionChanged(System.Windows.RoutedEventArgs e) 41 | { 42 | base.OnSelectionChanged(e); 43 | _adorner.InvalidateVisual(); 44 | } 45 | 46 | public new void ScrollToVerticalOffset(double offset) 47 | { 48 | base.ScrollToVerticalOffset(offset); 49 | _adorner.InvalidateVisual(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/VSPackage.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 SettingsStoreExplorer { 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", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class VSPackage { 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 VSPackage() { 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("SettingsStoreExplorer.VSPackage", typeof(VSPackage).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 Settings Store Explorer Extension. 65 | /// 66 | internal static string _110 { 67 | get { 68 | return ResourceManager.GetString("110", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to A tool window for viewing and editing the contents of the VS Settings Store.. 74 | /// 75 | internal static string _112 { 76 | get { 77 | return ResourceManager.GetString("112", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Looks up a localized string similar to Settings Store Explorer. 83 | /// 84 | internal static string _113 { 85 | get { 86 | return ResourceManager.GetString("113", resourceCulture); 87 | } 88 | } 89 | 90 | /// 91 | /// Looks up a localized string similar to Settings Store Explorer. 92 | /// 93 | internal static string ToolWindowCaption { 94 | get { 95 | return ResourceManager.GetString("ToolWindowCaption", resourceCulture); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/VSPackage.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 | Settings Store Explorer Extension 122 | 123 | 124 | A tool window for viewing and editing the contents of the VS Settings Store. 125 | 126 | 127 | Settings Store Explorer 128 | Keybinding editor name 129 | 130 | 131 | Settings Store Explorer 132 | 133 | -------------------------------------------------------------------------------- /src/SettingsStoreExplorer/VsThemeStyles.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 16 | 17 | 21 | 22 | 23 | M 0 0 L 6 0 L 0 6 Z 24 | 25 | 26 | 57 | 106 | 107 | 112 | 113 | 146 | 147 | 162 | 163 | 207 | 208 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /src/VSIX/CustomImages.vsct: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/VSIX/VSIX.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 15.0 6 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 7 | true 8 | 9 | 10 | 11 | false 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | Debug 20 | AnyCPU 21 | 2.0 22 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 23 | {4ACB08BB-D2F9-4E35-BD4F-A170D62B6D71} 24 | Library 25 | Properties 26 | SettingsStoreExplorer 27 | SettingsStoreExplorer 28 | v4.7.2 29 | true 30 | true 31 | true 32 | true 33 | true 34 | false 35 | Program 36 | $(DevEnvDir)devenv.exe 37 | /rootsuffix Exp 38 | 39 | 40 | true 41 | full 42 | false 43 | bin\Debug\ 44 | DEBUG;TRACE 45 | prompt 46 | 4 47 | 7.3 48 | 49 | 50 | pdbonly 51 | true 52 | bin\Release\ 53 | TRACE 54 | prompt 55 | 4 56 | 7.3 57 | 58 | 59 | 60 | LICENSE 61 | true 62 | 63 | 64 | 65 | SettingsStoreExplorer.128x128.png 66 | true 67 | 68 | 69 | Menus.ctmenu 70 | 71 | 72 | Designer 73 | 74 | 75 | 76 | 77 | 2.1.0 78 | 79 | 80 | 15.0.1 81 | 82 | 83 | 16.10.1055 84 | 85 | 86 | 4.5.0 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 112 | -------------------------------------------------------------------------------- /src/VSIX/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Settings Store Explorer 7 | A tool window for viewing and editing the contents of the VS Settings Store. 8 | https://github.com/pharring/SettingsStoreExplorer 9 | LICENSE 10 | https://github.com/pharring/SettingsStoreExplorer#getting-started 11 | https://github.com/pharring/SettingsStoreExplorer/blob/master/CHANGELOG.md 12 | SettingsStoreExplorer.128x128.png 13 | Settings;Store;Explorer;Registry;Hive 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/VSIX_Dev17/VSIX_Dev17.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 17.0 6 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 7 | true 8 | 9 | 10 | 11 | Debug 12 | AnyCPU 13 | 2.0 14 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | {58AE81DD-2560-4645-B731-6BEE46E7E3C8} 16 | Library 17 | Properties 18 | SettingsStoreExplorer 19 | SettingsStoreExplorer 20 | v4.7.2 21 | true 22 | true 23 | true 24 | true 25 | true 26 | false 27 | Program 28 | $(DevEnvDir)devenv.exe 29 | /rootsuffix Exp 30 | 31 | 32 | true 33 | full 34 | false 35 | bin\Debug\ 36 | DEBUG;TRACE 37 | prompt 38 | 4 39 | 40 | 41 | pdbonly 42 | true 43 | bin\Release\ 44 | TRACE 45 | prompt 46 | 4 47 | 48 | 49 | 50 | LICENSE 51 | true 52 | 53 | 54 | CustomImages.vsct 55 | 56 | 57 | SettingsStoreExplorer.128x128.png 58 | true 59 | 60 | 61 | SettingsStoreExplorerPackage.vsct 62 | Menus.ctmenu 63 | 64 | 65 | Designer 66 | 67 | 68 | 69 | 70 | 2.1.0 71 | 72 | 73 | compile; build; native; contentfiles; analyzers; buildtransitive 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 100 | -------------------------------------------------------------------------------- /src/VSIX_Dev17/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Settings Store Explorer 7 | A tool window for viewing and editing the contents of the VS Settings Store. 8 | https://github.com/pharring/SettingsStoreExplorer 9 | LICENSE 10 | https://github.com/pharring/SettingsStoreExplorer#getting-started 11 | https://github.com/pharring/SettingsStoreExplorer/blob/master/CHANGELOG.md 12 | SettingsStoreExplorer.128x128.png 13 | Settings;Store;Explorer;Registry;Hive 14 | 15 | 16 | 17 | amd64 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | --------------------------------------------------------------------------------