├── .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 | [](https://marketplace.visualstudio.com/items?itemName=PaulHarrington.SettingsStoreExplorer)
5 | [](https://marketplace.visualstudio.com/items?itemName=PaulHarrington.SettingsStoreExplorer)
6 | [](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 | [](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 |

21 |
22 | Or you can find it in "Quick search":
23 |

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 |

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 |

36 |
37 | A new sub-collection or values can be created by right-clicking on a collection.
38 |

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 |

10 |
11 | Or you can find it in "Quick search":
12 |

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 |

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 |

25 |
26 | A new sub-collection or values can be created by right-clicking on a collection.
27 |

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 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
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 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
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 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
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 |
--------------------------------------------------------------------------------
/src/SettingsStoreExplorer/Resources/SettingsStoreExplorer.light.xaml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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