├── .github
├── dependabot.yml
└── workflows
│ ├── build-msix-sideload.yml
│ ├── release.yml
│ └── wiki.yml
├── .gitignore
├── LICENSE
├── README.md
├── docs
├── images
│ ├── AppLogo-512.png
│ ├── github-internals-viewer-repository-open-graph.png
│ ├── logo.svg
│ └── readme
│ │ └── allocation-map-pages-and-extents.png
└── internalsviewer.com
│ ├── .vitepress
│ ├── cache
│ │ └── deps
│ │ │ ├── _metadata.json
│ │ │ ├── package.json
│ │ │ ├── vitepress___@vue_devtools-api.js
│ │ │ ├── vitepress___@vueuse_core.js
│ │ │ └── vue.js
│ ├── config.mts
│ └── theme
│ │ ├── index.ts
│ │ └── style.css
│ ├── .vscode
│ └── settings.json
│ ├── docs
│ ├── components
│ │ └── MarkerKey.vue
│ ├── concepts
│ │ └── page-header.md
│ ├── introduction
│ │ ├── background.md
│ │ ├── connecting.md
│ │ ├── database-view.md
│ │ ├── getting-started.md
│ │ ├── installation.md
│ │ ├── page-viewer.md
│ │ └── permissions.md
│ ├── logo.svg
│ ├── reference
│ │ ├── compression.md
│ │ ├── data-records.md
│ │ ├── index-records.md
│ │ └── page-header.md
│ ├── technical-details
│ │ ├── general-internals.md
│ │ └── index.md
│ └── tutorial
│ │ ├── 0-introduction.md
│ │ ├── 1-connecting-and-allocations.md
│ │ ├── 2-viewing-pages.md
│ │ ├── 3-indexes.md
│ │ └── images
│ │ ├── 01-after-initial-insert.png
│ │ ├── 01-allocated-extent.png
│ │ ├── 01-connect-to-database.png
│ │ ├── 01-database-refresh-button.png
│ │ ├── 01-decoded-data.png
│ │ ├── 01-entry-points.png
│ │ ├── 01-initial-view.png
│ │ ├── 01-open-page.png
│ │ ├── 01-page-row.png
│ │ ├── 01-page-slots-more-rows.png
│ │ ├── 01-page-viewer.pdn
│ │ ├── 01-page-viewer.png
│ │ └── 01-tooltip.png
│ ├── index.md
│ ├── package-lock.json
│ └── package.json
└── src
├── .editorconfig
├── InternalsViewer.Internals.Metadata.SourceGenerators
├── AttributeGenerator.cs
├── InternalsViewer.Internals.Metadata.SourceGenerators.csproj
├── MetadataLoaderSourceGenerator.cs
└── TypeFinder.cs
├── InternalsViewer.Internals.Tests.VerificationTool
├── Helpers
│ └── ConnectionStringHelper.cs
├── Indexes
│ └── IndexVerification.cs
├── InternalsViewer.Internals.Tests.VerificationTool.csproj
├── Models
│ └── DatabaseIndexRow.cs
├── Program.cs
└── Services
│ ├── IndexVerificationService.cs
│ ├── ObjectPageListService.cs
│ ├── ObjectService.cs
│ └── TableVerificationService.cs
├── InternalsViewer.Internals.Tests
├── GlobalUsings.cs
├── Helpers
│ ├── ConnectionStringHelper.cs
│ ├── DatabaseDumper.cs
│ ├── FilePageReader.cs
│ ├── ServiceHelper.cs
│ ├── TestHelpers.cs
│ ├── TestLogger.cs
│ └── TestServiceHost.cs
├── IntegrationTests
│ ├── Providers
│ │ ├── Metadata
│ │ │ ├── AllocationUnitProviderTests.cs
│ │ │ ├── FileProviderTests.cs
│ │ │ ├── IndexStructureProviderTests.cs
│ │ │ ├── ProviderTestBase.cs
│ │ │ └── TableStructureProviderTests.cs
│ │ └── Server
│ │ │ └── BufferPoolInfoProviderTests.cs
│ ├── Readers
│ │ ├── BackupReaderTests.cs
│ │ ├── QueryPageReaderTests.cs
│ │ └── RecordReaderTests.cs
│ ├── Services
│ │ ├── Indexes
│ │ │ └── IndexServiceTests.cs
│ │ └── Loaders
│ │ │ └── Engine
│ │ │ └── MetadataLoaderTests.cs
│ └── Test Data
│ │ └── TestDatabase
│ │ ├── CreateDatabase.sql
│ │ └── TestDatabase.mdf
├── InternalsViewer.Internals.Tests.csproj
└── UnitTests
│ ├── Converters
│ ├── DataConverterTests.cs
│ ├── DateTimeConverterTests.cs
│ ├── NumberDataConverterTests.cs
│ └── StringDataConverterTests.cs
│ ├── Engine
│ └── Parsers
│ │ ├── LogSequenceNumberParserTests.cs
│ │ ├── PageAddressParserTests.cs
│ │ └── PfsByteParserTests.cs
│ ├── Helpers
│ └── IdHelperTests.cs
│ ├── Loaders
│ ├── CdDataRecordLoaderTests.cs
│ ├── CompressionInfoLoaderTests.cs
│ └── DictionaryLoaderTests.cs
│ ├── Pages
│ └── PfsByteTests.cs
│ ├── Readers
│ ├── Headers
│ │ └── PageHeaderParserTests.cs
│ └── RecordReaderTests.cs
│ ├── Services
│ ├── BufferPoolServiceTests.cs
│ ├── Loaders
│ │ └── Records
│ │ │ ├── Fields
│ │ │ ├── LobOverflowFieldLoaderTests.cs
│ │ │ ├── LobPointerFieldLoaderTests.cs
│ │ │ └── LobRootFieldLoaderTests.cs
│ │ │ ├── FixedVarDataRecordLoaderTests.cs
│ │ │ └── FixedVarIndexRecordLoaderTests.cs
│ └── Pages
│ │ ├── Loaders
│ │ ├── DatabaseServiceTests.cs
│ │ └── TestPageHeader.cs
│ │ └── Parsers
│ │ ├── AllocationPageParserTests.cs
│ │ ├── BootPageParserTests.cs
│ │ ├── DataPageParserTests.cs
│ │ ├── IamPageParserTests.cs
│ │ ├── IndexPageParserTests.cs
│ │ ├── LobPageParserTests.cs
│ │ ├── PageParserTestsBase.cs
│ │ └── PfsPageParserTests.cs
│ └── Test Data
│ └── Test Pages
│ ├── TestDatabase_1_100_IamPage.page
│ ├── TestDatabase_1_1_PfsPage.page
│ ├── TestDatabase_1_25520_DataPage.page
│ ├── TestDatabase_1_2_GamPage.page
│ ├── TestDatabase_1_3_SGamPage.page
│ ├── TestDatabase_1_6_DcmPage.page
│ ├── TestDatabase_1_7_BcmPage.page
│ └── TestDatabase_1_9_BootPage.page
├── InternalsViewer.Internals
├── Annotations
│ ├── DataStructure.cs
│ ├── DataStructureItem.cs
│ ├── DataStructureItemAttribute.cs
│ └── ItemType.cs
├── Connections
│ ├── Backup
│ │ ├── BackupConnectionFactory.cs
│ │ ├── BackupConnectionType.cs
│ │ └── BackupConnectionTypeConfig.cs
│ ├── ConnectionTypeConfig.cs
│ ├── File
│ │ ├── FileConnectionFactory.cs
│ │ ├── FileConnectionType.cs
│ │ └── FileConnectionTypeConfig.cs
│ └── Server
│ │ ├── ServerConnectionConfig.cs
│ │ ├── ServerConnectionFactory.cs
│ │ └── ServerConnectionType.cs
├── Converters
│ ├── CompressedDataConverter.cs
│ ├── DataConverter.cs
│ ├── DataEncoders.cs
│ ├── DateTimeConverters.cs
│ └── Decoder
│ │ ├── DataDecoder.cs
│ │ └── DecodeResult.cs
├── Engine
│ ├── Address
│ │ ├── LogSequenceNumber.cs
│ │ ├── PageAddress.cs
│ │ └── RowIdentifier.cs
│ ├── Allocation
│ │ ├── AllocationChain.cs
│ │ ├── Enums
│ │ │ ├── ChainType.cs
│ │ │ ├── PfsByte.cs
│ │ │ └── SpaceFree.cs
│ │ ├── IamChain.cs
│ │ └── PfsChain.cs
│ ├── Database
│ │ ├── AllocationUnit.cs
│ │ ├── BufferPool.cs
│ │ ├── DatabaseFile.cs
│ │ ├── DatabaseSource.cs
│ │ ├── DatabaseSummary.cs
│ │ ├── Enums
│ │ │ ├── AllocationUnitType.cs
│ │ │ ├── DatabaseState.cs
│ │ │ └── IndexType.cs
│ │ └── FileType.cs
│ ├── Indexes
│ │ └── IndexNode.cs
│ ├── Pages
│ │ ├── AllocationPage.cs
│ │ ├── AllocationUnitPage.cs
│ │ ├── BootPage.cs
│ │ ├── EmptyPage.cs
│ │ ├── Enums
│ │ │ └── PageType.cs
│ │ ├── FileHeaderPage.cs
│ │ ├── IamPage.cs
│ │ ├── LobPage.cs
│ │ ├── Page.cs
│ │ ├── PageData.cs
│ │ ├── PageHeader.cs
│ │ └── PfsPage.cs
│ ├── Parsers
│ │ ├── LogSequenceNumberParser.cs
│ │ ├── PageAddressParser.cs
│ │ └── PfsByteParser.cs
│ └── Records
│ │ ├── Blob
│ │ ├── BlobPointers
│ │ │ ├── BlobChildLink.cs
│ │ │ ├── BlobField.cs
│ │ │ ├── BlobFieldType.cs
│ │ │ ├── OverflowField.cs
│ │ │ ├── PointerField.cs
│ │ │ └── RootField.cs
│ │ ├── BlobRecord.cs
│ │ └── BlobType.cs
│ │ ├── CdRecordType
│ │ ├── ColumnDescriptor.cs
│ │ ├── ColumnDescriptorFlag.cs
│ │ ├── CompressedDataRecord.cs
│ │ ├── CompressedRecordField.cs
│ │ ├── CompressedRecordType.cs
│ │ ├── CompressionInfo.cs
│ │ ├── CompressionInfoStructure.cs
│ │ ├── CompressionType.cs
│ │ ├── Dictionary.cs
│ │ └── DictionaryEntry.cs
│ │ ├── Data
│ │ ├── DataRecord.cs
│ │ └── SparseVector.cs
│ │ ├── Field.cs
│ │ ├── FixedVarRecordType
│ │ ├── FixedVarRecord.cs
│ │ └── FixedVarRecordField.cs
│ │ ├── Index
│ │ ├── IndexRecord.cs
│ │ ├── IndexTypes.cs
│ │ └── NodeType.cs
│ │ ├── Record.cs
│ │ ├── RecordField.cs
│ │ ├── RecordHelpers.cs
│ │ └── RecordType.cs
├── Extensions
│ ├── ByteExtensions.cs
│ └── EnumerableExtensions.cs
├── GlobalUsings.cs
├── Helpers
│ ├── History.cs
│ ├── IdHelpers.cs
│ ├── PageHelpers.cs
│ ├── SqlTypeHelpers.cs
│ └── StringHelpers.cs
├── Interfaces
│ ├── Connections
│ │ ├── IConnectionProvider.cs
│ │ ├── IConnectionType.cs
│ │ └── IConnectionTypeFactory.cs
│ ├── Engine
│ │ └── IAllocationChain.cs
│ ├── MetadataProviders
│ │ ├── IBufferPoolInfoProvider.cs
│ │ └── ITransactionLogProvider.cs
│ ├── Readers
│ │ ├── IPageReader.cs
│ │ └── Internals
│ │ │ └── IRecordReader.cs
│ └── Services
│ │ ├── Loaders
│ │ ├── Chains
│ │ │ ├── IAllocationChainService.cs
│ │ │ ├── IIamChainService.cs
│ │ │ └── IPfsChainService.cs
│ │ ├── Engine
│ │ │ ├── IDatabaseService.cs
│ │ │ └── IMetadataLoader.cs
│ │ └── Pages
│ │ │ ├── IPageLoader.cs
│ │ │ ├── IPageParser.cs
│ │ │ └── IPageService.cs
│ │ └── Records
│ │ └── IRecordService.cs
├── InternalsViewer.Internals.csproj
├── Metadata
│ ├── ColumnAttribute.cs
│ ├── Helpers
│ │ └── StructureExtensions.cs
│ ├── HobtEntryPoint.cs
│ ├── Internals
│ │ ├── InternalMetadata.cs
│ │ ├── Tables
│ │ │ ├── InternalAllocationUnit.cs
│ │ │ ├── InternalColumn.cs
│ │ │ ├── InternalColumnLayout.cs
│ │ │ ├── InternalEntityObject.cs
│ │ │ ├── InternalFile.cs
│ │ │ ├── InternalIndex.cs
│ │ │ ├── InternalIndexColumn.cs
│ │ │ ├── InternalObject.cs
│ │ │ └── InternalRowSet.cs
│ │ ├── TypeInfo.cs
│ │ └── TypeInfoExtensions.cs
│ ├── Structures
│ │ ├── ColumnStructure.cs
│ │ ├── IndexColumnStructure.cs
│ │ ├── IndexStructure.cs
│ │ ├── Structure.cs
│ │ ├── StructureType.cs
│ │ └── TableStructure.cs
│ └── TransactionLogEntry.cs
├── Providers
│ ├── Metadata
│ │ ├── AllocationUnitProvider.cs
│ │ ├── FileProvider.cs
│ │ ├── IndexStructureProvider.cs
│ │ └── TableStructureProvider.cs
│ └── Server
│ │ └── BufferPoolInfoProvider.cs
├── Readers
│ ├── Internals
│ │ └── RecordReader.cs
│ └── Pages
│ │ ├── DataFilePageReader.cs
│ │ ├── PageReader.cs
│ │ └── QueryPageReader.cs
├── ServiceRegistration.cs
├── Services
│ ├── BufferPoolService.cs
│ ├── Indexes
│ │ └── IndexService.cs
│ ├── Loaders
│ │ ├── Chains
│ │ │ ├── AllocationChainService.cs
│ │ │ ├── IamChainService.cs
│ │ │ └── PfsChainService.cs
│ │ ├── Compression
│ │ │ ├── CompressionInfoLoader.cs
│ │ │ └── DictionaryLoader.cs
│ │ ├── Engine
│ │ │ ├── DatabaseService.cs
│ │ │ ├── InternalTableConstants.cs
│ │ │ └── MetadataLoader.cs
│ │ └── Records
│ │ │ ├── BlobFixedVarRecordLoader.cs
│ │ │ ├── Cd
│ │ │ └── CdDataRecordLoader.cs
│ │ │ ├── Fields
│ │ │ ├── LobOverflowFieldLoader.cs
│ │ │ ├── LobPointerFieldLoader.cs
│ │ │ └── LobRootFieldLoader.cs
│ │ │ ├── FixedVar
│ │ │ ├── FixedVarDataRecordLoader.cs
│ │ │ ├── FixedVarIndexRecordLoader.cs
│ │ │ └── FixedVarRecordLoader.cs
│ │ │ └── SparseVectorLoader.cs
│ ├── Pages
│ │ ├── Loaders
│ │ │ └── PageLoader.cs
│ │ ├── PageService.cs
│ │ └── Parsers
│ │ │ ├── AllocationPageParser.cs
│ │ │ ├── BootPageParser.cs
│ │ │ ├── DataPageParser.cs
│ │ │ ├── EmptyPageParser.cs
│ │ │ ├── FileHeaderPageParser.cs
│ │ │ ├── IamPageParser.cs
│ │ │ ├── IndexPageParser.cs
│ │ │ ├── LobPageParser.cs
│ │ │ ├── PageHeaderParser.cs
│ │ │ ├── PageParser.cs
│ │ │ └── PfsPageParser.cs
│ └── Records
│ │ └── RecordService.cs
├── SqlCommands.cs
└── TransactionLog
│ ├── LogData.cs
│ └── LogMonitor.cs
├── InternalsViewer.UI.App
├── Activation
│ ├── ActivationHandler.cs
│ ├── DefaultActivationHandler.cs
│ └── IActivationHandler.cs
├── App.xaml
├── App.xaml.cs
├── Assets
│ ├── AppIcon16.bmp
│ ├── AppIcon16.png
│ ├── AppLogo-16.svg
│ ├── AppLogo-512.png
│ ├── AppLogo.png
│ ├── AppLogo.svg
│ ├── Bookmark.svg
│ ├── Database.svg
│ ├── DatabaseFile.svg
│ ├── Index32.png
│ ├── InternalsViewer.ico
│ ├── TabIcons
│ │ ├── Database.png
│ │ ├── Database16.png
│ │ ├── Database32.png
│ │ ├── DatabaseTabIcon.svg
│ │ ├── IndexTabIcon.svg
│ │ ├── Page.png
│ │ ├── Page16.png
│ │ ├── Page32.png
│ │ └── PageTabIcon.svg
│ └── Untitled.png
├── Controls
│ ├── Allocation
│ │ ├── AllocationControl.xaml
│ │ ├── AllocationControl.xaml.cs
│ │ ├── AllocationLayerGrid.xaml
│ │ ├── AllocationLayerGrid.xaml.cs
│ │ ├── AllocationRenderer.cs
│ │ └── PfsRenderer.cs
│ ├── Connections
│ │ ├── BackupFileConnectionControl.xaml
│ │ ├── BackupFileConnectionControl.xaml.cs
│ │ ├── ConnectTile.xaml
│ │ ├── ConnectTile.xaml.cs
│ │ ├── HeaderTile.xaml
│ │ └── HeaderTile.xaml.cs
│ ├── CopyButton.xaml
│ ├── CopyButton.xaml.cs
│ ├── CopyTextBlock.xaml
│ ├── CopyTextBlock.xaml.cs
│ ├── CursorUserControl.cs
│ ├── ExceptionDialog.xaml
│ ├── ExceptionDialog.xaml.cs
│ ├── Index
│ │ ├── IndexControl.xaml
│ │ ├── IndexControl.xaml.cs
│ │ ├── RecordGrid.xaml
│ │ └── RecordGrid.xaml.cs
│ ├── Page
│ │ ├── HexViewControl.xaml
│ │ ├── HexViewControl.xaml.cs
│ │ ├── LabelTextBox.xaml
│ │ ├── LabelTextBox.xaml.cs
│ │ ├── MarkerDataTable.xaml
│ │ ├── MarkerDataTable.xaml.cs
│ │ ├── MarkerTreeView.xaml
│ │ └── MarkerTreeView.xaml.cs
│ ├── PageAddressTextBox.xaml
│ ├── PageAddressTextBox.xaml.cs
│ ├── PasswordDialog.xaml
│ └── PasswordDialog.xaml.cs
├── GlobalUsings.cs
├── Helpers
│ ├── CollectionExtensions.cs
│ ├── ColourHelpers.cs
│ ├── ConnectionHelper.cs
│ ├── Converters
│ │ ├── ColorToSolidColorBrushValueConverter.cs
│ │ ├── PageAddressToStringConverter.cs
│ │ └── RecordValueConverter.cs
│ ├── FileHelpers.cs
│ ├── LayoutHelpers.cs
│ ├── RuntimeHelper.cs
│ └── Selectors
│ │ ├── MarkerTemplateSelector.cs
│ │ └── SlotTemplateSelector.cs
├── Images
│ ├── LargeTile.scale-100.png
│ ├── LargeTile.scale-125.png
│ ├── LargeTile.scale-150.png
│ ├── LargeTile.scale-200.png
│ ├── LargeTile.scale-400.png
│ ├── SmallTile.scale-100.png
│ ├── SmallTile.scale-125.png
│ ├── SmallTile.scale-150.png
│ ├── SmallTile.scale-200.png
│ ├── SmallTile.scale-400.png
│ ├── SplashScreen.scale-100.png
│ ├── SplashScreen.scale-125.png
│ ├── SplashScreen.scale-150.png
│ ├── SplashScreen.scale-200.png
│ ├── SplashScreen.scale-400.png
│ ├── Square150x150Logo.scale-100.png
│ ├── Square150x150Logo.scale-125.png
│ ├── Square150x150Logo.scale-150.png
│ ├── Square150x150Logo.scale-200.png
│ ├── Square150x150Logo.scale-400.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-16.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-24.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-256.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-32.png
│ ├── Square44x44Logo.altform-lightunplated_targetsize-48.png
│ ├── Square44x44Logo.altform-unplated_targetsize-16.png
│ ├── Square44x44Logo.altform-unplated_targetsize-24.png
│ ├── Square44x44Logo.altform-unplated_targetsize-256.png
│ ├── Square44x44Logo.altform-unplated_targetsize-32.png
│ ├── Square44x44Logo.altform-unplated_targetsize-48.png
│ ├── Square44x44Logo.scale-100.png
│ ├── Square44x44Logo.scale-125.png
│ ├── Square44x44Logo.scale-150.png
│ ├── Square44x44Logo.scale-200.png
│ ├── Square44x44Logo.scale-400.png
│ ├── Square44x44Logo.targetsize-16.png
│ ├── Square44x44Logo.targetsize-24.png
│ ├── Square44x44Logo.targetsize-256.png
│ ├── Square44x44Logo.targetsize-32.png
│ ├── Square44x44Logo.targetsize-48.png
│ ├── StoreLogo.scale-100.png
│ ├── StoreLogo.scale-125.png
│ ├── StoreLogo.scale-150.png
│ ├── StoreLogo.scale-200.png
│ ├── StoreLogo.scale-400.png
│ ├── Wide310x150Logo.scale-100.png
│ ├── Wide310x150Logo.scale-125.png
│ ├── Wide310x150Logo.scale-150.png
│ ├── Wide310x150Logo.scale-200.png
│ └── Wide310x150Logo.scale-400.png
├── InternalsViewer.UI.App.csproj
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Messages
│ ├── ConnectMessages.cs
│ ├── ExceptionMessage.cs
│ ├── NavigateMessage.cs
│ └── OpenPageMessage.cs
├── Models
│ ├── AllocationLayer.cs
│ ├── AllocationUnit.cs
│ ├── Connections
│ │ ├── RecentConnection.cs
│ │ └── ServerConnectionSettings.cs
│ ├── DatabaseFile.cs
│ ├── ExtentAllocation.cs
│ ├── Index
│ │ ├── IndexPageModel.cs
│ │ ├── IndexRecordFieldModel.cs
│ │ └── IndexRecordModel.cs
│ ├── MarkStyle.cs
│ ├── Marker.cs
│ ├── Page
│ │ └── DecodePair.cs
│ ├── PageBookmark.cs
│ ├── PageSlot.cs
│ └── SettingsOptions.cs
├── Package.StoreAssociation.xml
├── Package.appxmanifest
├── Properties
│ ├── PublishProfiles
│ │ ├── win-x64.pubxml
│ │ └── win-x86.pubxml
│ └── launchSettings.json
├── Services
│ ├── Markers
│ │ ├── MarkStyleProvider.cs
│ │ └── MarkerBuilder.cs
│ └── SettingsService.cs
├── Styles
│ ├── CommonStyles.xaml
│ └── Markers
│ │ ├── MarkerStylesDark.xaml
│ │ └── MarkerStylesLight.xaml
├── ViewModels
│ ├── Allocation
│ │ ├── AllocationLayerBuilder.cs
│ │ ├── AllocationLayerGridViewModel.cs
│ │ ├── AllocationOverViewModel.cs
│ │ └── CalibrationBuilder.cs
│ ├── Connections
│ │ ├── BackupFileConnectionViewModel.cs
│ │ ├── ConnectFileViewModel.cs
│ │ └── ConnectServerViewModel.cs
│ ├── Database
│ │ └── DatabaseTabViewModel.cs
│ ├── Index
│ │ └── IndexTabViewModel.cs
│ ├── MainViewModel.cs
│ ├── Page
│ │ ├── HexControlViewModel.cs
│ │ └── PageTabViewModel.cs
│ └── Tabs
│ │ ├── TabType.cs
│ │ └── TabViewModel.cs
├── Views
│ ├── Connect
│ │ ├── ConnectBackupPage.xaml
│ │ ├── ConnectBackupPage.xaml.cs
│ │ ├── ConnectFilePage.xaml
│ │ ├── ConnectFilePage.xaml.cs
│ │ ├── ConnectServerPage.xaml
│ │ ├── ConnectServerPage.xaml.cs
│ │ ├── ConnectStartPage.xaml
│ │ ├── ConnectStartPage.xaml.cs
│ │ ├── ConnectView.xaml
│ │ └── ConnectView.xaml.cs
│ ├── DatabaseView.xaml
│ ├── DatabaseView.xaml.cs
│ ├── IndexView.xaml
│ ├── IndexView.xaml.cs
│ ├── PageView.xaml
│ ├── PageView.xaml.cs
│ ├── SettingsPage.xaml
│ └── SettingsPage.xaml.cs
├── app.manifest
└── appsettings.json
├── InternalsViewer.ndproj
├── InternalsViewer.sln
├── InternalsViewer.sln.DotSettings
└── nuget.config
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "nuget"
4 | directory: "/" # Location of package manifests
5 | schedule:
6 | interval: "daily"
7 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Build application and create GitHub release
2 | on:
3 | push:
4 | tags: [ 'v*.*.*' ]
5 | workflow_dispatch:
6 |
7 | jobs:
8 | build:
9 | uses: ./.github/workflows/build-msix-sideload.yml
10 | secrets: inherit
11 |
12 | release:
13 | needs: build
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - name: Checkout
18 | uses: actions/checkout@v2
19 | with:
20 | fetch-depth: 0
21 |
22 | - name: Download artifacts
23 | uses: actions/download-artifact@v4
24 | with:
25 | path: artifacts
26 |
27 | - name: List downloaded files
28 | run: ls -R artifacts
29 |
30 | - name: Zip x86 package
31 | run: |
32 | zip -r artifacts/internals-viewer-msix-x86.zip artifacts/msix-package-x86/
33 |
34 | - name: Zip x64 package
35 | run: |
36 | zip -r artifacts/internals-viewer-msix-x64.zip artifacts/msix-package-x64/
37 |
38 | - name: Create release
39 | run: |
40 | gh release create ${{ github.ref }} artifacts/internals-viewer-*.zip
41 | env:
42 | GITHUB_TOKEN: ${{ github.TOKEN }}
43 | shell: bash
44 |
--------------------------------------------------------------------------------
/.github/workflows/wiki.yml:
--------------------------------------------------------------------------------
1 | name: Deploy static content to Pages
2 |
3 | on:
4 | push:
5 | branches: ['main']
6 |
7 | workflow_dispatch:
8 |
9 | permissions:
10 | contents: read
11 | pages: write
12 | id-token: write
13 |
14 | concurrency:
15 | group: 'pages'
16 | cancel-in-progress: true
17 |
18 | jobs:
19 | deploy:
20 | environment:
21 | name: github-pages
22 | url: ${{ steps.deployment.outputs.page_url }}
23 | runs-on: ubuntu-latest
24 | steps:
25 | - name: Checkout
26 | uses: actions/checkout@v4
27 | - name: Set up Node
28 | uses: actions/setup-node@v3
29 | with:
30 | node-version: 18
31 | cache: 'npm'
32 | cache-dependency-path: ./docs/internalsviewer.com/package-lock.json
33 |
34 | - name: Install dependencies
35 | run: npm install
36 | working-directory: ./docs/internalsviewer.com/
37 |
38 | - name: Build
39 | run: npm run docs:build
40 | working-directory: ./docs/internalsviewer.com/
41 |
42 | - name: Setup Pages
43 | uses: actions/configure-pages@v3
44 | - name: Upload artifact
45 | uses: actions/upload-pages-artifact@v2
46 | with:
47 | # Upload dist repository
48 | path: './docs/internalsviewer.com/.vitepress/dist'
49 | - name: Deploy to GitHub Pages
50 | id: deployment
51 | uses: actions/deploy-pages@v2
52 |
--------------------------------------------------------------------------------
/docs/images/AppLogo-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/images/AppLogo-512.png
--------------------------------------------------------------------------------
/docs/images/github-internals-viewer-repository-open-graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/images/github-internals-viewer-repository-open-graph.png
--------------------------------------------------------------------------------
/docs/images/readme/allocation-map-pages-and-extents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/images/readme/allocation-map-pages-and-extents.png
--------------------------------------------------------------------------------
/docs/internalsviewer.com/.vitepress/cache/deps/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module"
3 | }
4 |
--------------------------------------------------------------------------------
/docs/internalsviewer.com/.vitepress/theme/index.ts:
--------------------------------------------------------------------------------
1 | // https://vitepress.dev/guide/custom-theme
2 | import { h } from 'vue'
3 | import type { Theme } from 'vitepress'
4 | import DefaultTheme from 'vitepress/theme'
5 | import './style.css'
6 |
7 | export default {
8 | extends: DefaultTheme,
9 | Layout: () => {
10 | return h(DefaultTheme.Layout, null, {
11 | // https://vitepress.dev/guide/extending-default-theme#layout-slots
12 | })
13 | },
14 | enhanceApp({ app, router, siteData }) {
15 | // ...
16 | }
17 | } satisfies Theme
18 |
--------------------------------------------------------------------------------
/docs/internalsviewer.com/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "colour",
4 | "VARCHAR"
5 | ]
6 | }
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/components/MarkerKey.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
21 |
22 |
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/concepts/page-header.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/concepts/page-header.md
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/introduction/background.md:
--------------------------------------------------------------------------------
1 | # Background
2 |
3 | In 2006 I started looking into SQL Server internals to get a better understanding of what was going on inside of a database. SQL Server isn't a black box where what happens inside is a mystery. There's a huge amount of information on what it is doing but to understand it requires knowledge about the internal architecture. What is a clustered index? What is a heap? What is the difference between a DATETIME and SMALLDATETIME? What is the effect on my table structure if a field is NULL vs NOT NULL, or CHAR vs VARCHAR?
4 |
5 | To get answers to these questions and to learn about internals if you want to see it and experiment you have to start digging around using system views and undocumented commands.
6 |
7 |
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/introduction/connecting.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/introduction/connecting.md
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/introduction/getting-started.md:
--------------------------------------------------------------------------------
1 | ---
2 | outline: deep
3 | ---
4 |
5 | # Getting started
6 |
7 | ## Requirements
8 |
9 | Internals Viewer will run on Windows 10/11 (version 17763.0 or higher).
10 |
11 | SQL Server 2022 or 2019 is required when connecting to a SQL Server database or file.
12 |
13 | `sysadmin` permissions are required for a SQL Server connection, see [Permissions](/docs/introduction/permissions).
14 |
15 | ## Installation
16 |
17 | The easiest way to install Internals Viewer is to get it from the Microsoft Store.
18 |
19 |
21 |
22 |
23 |
24 | To install manually see [Installation](/docs/introduction/installation.md)
25 |
26 | ## Connecting
27 |
28 | When you open the application the Start page will bring up the different options to connect.
29 |
30 | - Click on Connect to SQL Server
31 | - Set Instance Name to the name of the SQL Server
32 | - Choose either Active Directory Integrated or SQL Password for the Authentication type
33 | - For Database either type in the name of the database or expand the drop down list to see a list of databases on the server
34 | - Click Connect
35 |
36 |
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/introduction/installation.md:
--------------------------------------------------------------------------------
1 | # Manual Installation
2 |
3 | The releases on GitHub are built from its source code.
4 |
5 | The application is packaged as an .msix file. Windows will only install a package that has been signed. The version published in the Microsoft Store is signed with a Microsoft certificate as it has been through a verification process.
6 |
7 | The version on Github uses a self-signing certificate that needs to be installed first before the application is installed.
8 |
9 | The script Install.ps1 installs the certificate and then installs the .msix package.
10 |
11 | Steps:
12 |
13 | 1. Download the latest release artifacts from the [Releases](https://github.com/danny-sg/internals-viewer/releases) page
14 | 2. Extract the files to a folder and navigate to `\internals-viewer-msix-platform\artifacts\msix-package-platform\InternalsViewer.UI.App_version\`
15 | 3. Run `powershell -ExecutionPolicy Bypass -File Install.ps1`
16 | - You will be prompted to install the certificate. Accept prompts to continue.
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/introduction/page-viewer.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/introduction/page-viewer.md
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/introduction/permissions.md:
--------------------------------------------------------------------------------
1 | # Permissions
2 |
3 | `sysadmin` permissions are required for a SQL Server connection.
4 |
5 | Permissions are not required to read .mdf or .bak files other than file read permissions.
6 |
7 | Internals Viewer uses the `DBCC PAGE` command to connect to online databases. This is the only command used as everything is done from the page data. `DBCC PAGE` requires `syadmin` permissions due to the nature of the command - it can read anything in the database regardless of permissions.
8 |
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/reference/compression.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Compression
6 |
7 | ## Compression Info
8 |
9 | |Key|Name|Description|
10 | |---|----|-----------|
11 | |00|Header
12 | |00|Page Modification Count
13 | |00|Size
14 | |00|Length
15 | |00|Anchor Record
16 | |00|Dictionary
17 |
18 | ## Dictionary
19 |
20 | |Key|Name|Description|
21 | |---|----|-----------|
22 | |00|Entry Count
23 | |00|Dictionary Entry Offset Array
24 | |00|Dictionary Value
25 |
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/reference/index-records.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Data Records
6 |
7 | ## FixedVar Format
8 |
9 | |Key|Name|Description|
10 | |---|----|-----------|
11 | |00|Status Bits A
12 | |00|Null Bitmap
13 | |00|Column Count
14 | |00|Variable Length Column Count
15 | |00|Variable Length Column Offset Array
16 | |00|Fixed Length Value
17 | |00|Variable Length Value
18 | |00|Uniquifier
19 | |00|RID
20 | |00|Down Page Pointer
21 |
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/technical-details/general-internals.md:
--------------------------------------------------------------------------------
1 | # Undocumented DBCC Commands
2 |
3 | List all DBCC commands:
4 |
5 | ```sql
6 | DBCC TRACEON(2588)
7 | DBCC HELP ('?')
8 |
9 | -- Then DBCC HELP for the command syntax, e.g.
10 | DBCC HELP ('READPAGE')
11 | ```
12 |
13 | Reference - https://www.sqlskills.com/blogs/paul/dbcc-writepage/
14 |
15 | # System Base Tables
16 |
17 | See - https://learn.microsoft.com/en-us/sql/relational-databases/system-tables/system-base-tables
18 |
19 | If you connect to SQL Server using a [Dedicated Administrator Connection (DAC)](https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/diagnostic-connection-for-database-administrators) you can query these tables.
20 |
21 | To connect on a DAC connection prefix the server name with `admin:`.
22 |
23 | Only one DAC connection is allowed at a time.
24 |
25 | # sp_helptext
26 |
27 | The system stored procedures, views, DMVs etc. have underlying SQL that can be viewed using `sp_helptext`.
28 |
29 | For example `sys.objects` is a view based on a hidden base table, `sys.objects$`.
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/technical-details/index.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/technical-details/index.md
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/0-introduction.md:
--------------------------------------------------------------------------------
1 | # Tutorial
2 |
3 | In this tutorial we'll create a database from scratch. We'll run DDL (Data Definition Language) SQL to create objects, then run DML (Data Manipulation Language) SQL to insert and modify data to see how we can view it and the effects on the database storage internals.
4 |
5 | This tutorial will cover:
6 |
7 | - Part 1
8 | - Connecting to a database
9 | - Viewing object allocations in the database
10 | - How to find object entry points
11 |
12 | - Part 2
13 | - Viewing pages and records
14 | - How to view records
15 | - How pages are linked together
16 |
17 | - Part 3
18 | - Navigating indexes
19 | - Heaps, clustered indexes, and non-clustered indexes
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/2-viewing-pages.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/2-viewing-pages.md
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/3-indexes.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/3-indexes.md
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/images/01-after-initial-insert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-after-initial-insert.png
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/images/01-allocated-extent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-allocated-extent.png
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/images/01-connect-to-database.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-connect-to-database.png
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/images/01-database-refresh-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-database-refresh-button.png
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/images/01-decoded-data.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-decoded-data.png
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/images/01-entry-points.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-entry-points.png
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/images/01-initial-view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-initial-view.png
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/images/01-open-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-open-page.png
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/images/01-page-row.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-page-row.png
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/images/01-page-slots-more-rows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-page-slots-more-rows.png
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/images/01-page-viewer.pdn:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-page-viewer.pdn
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/images/01-page-viewer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-page-viewer.png
--------------------------------------------------------------------------------
/docs/internalsviewer.com/docs/tutorial/images/01-tooltip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/docs/internalsviewer.com/docs/tutorial/images/01-tooltip.png
--------------------------------------------------------------------------------
/docs/internalsviewer.com/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home
3 |
4 | hero:
5 | name: "Internals Viewer"
6 | text: "Tutorials and documentation"
7 | # tagline: My great project tagline
8 | actions:
9 | - theme: brand
10 | text: Getting started
11 | link: /docs/introduction/getting-started
12 | - theme: alt
13 | text: Tutorial
14 | link: /docs/tutorial/0-introduction
15 |
16 | ---
17 |
18 |
--------------------------------------------------------------------------------
/docs/internalsviewer.com/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "docs:dev": "vitepress dev",
4 | "docs:build": "vitepress build",
5 | "docs:preview": "vitepress preview"
6 | },
7 | "devDependencies": {
8 | "vitepress": "^1.0.0-rc.41"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Metadata.SourceGenerators/AttributeGenerator.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 |
3 | namespace InternalsViewer.Internals.Metadata.SourceGenerators;
4 |
5 | [Generator]
6 | public class AttributeGenerator : IIncrementalGenerator
7 | {
8 | ///
9 | public void Initialize(IncrementalGeneratorInitializationContext context)
10 | {
11 | context.RegisterPostInitializationOutput(i =>
12 | {
13 | var attributeSource = @"//
14 | #nullable enable
15 |
16 | using System;
17 | using System.Data;
18 | using System.Diagnostics.CodeAnalysis;
19 |
20 | #pragma warning disable CS9113
21 |
22 | namespace InternalsViewer.Internals.Generators;
23 |
24 | [ExcludeFromCodeCoverage]
25 | [AttributeUsage(AttributeTargets.Class)]
26 | public class InternalsMetadataAttribute : Attribute;
27 |
28 | [ExcludeFromCodeCoverage]
29 | [AttributeUsage(AttributeTargets.Property)]
30 | public class InternalsMetadataColumnAttribute(string Name, int ColumnId, SqlDbType DataType, int DataLength, short LeafOffset, short NullBit)
31 | : Attribute;
32 | ";
33 |
34 | i.AddSource("InternalsMetadataAttribute.g.cs", attributeSource);
35 | });
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Metadata.SourceGenerators/InternalsViewer.Internals.Metadata.SourceGenerators.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 | Latest
7 | Debug;Release;Installer
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Metadata.SourceGenerators/TypeFinder.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Microsoft.CodeAnalysis;
4 | using Microsoft.CodeAnalysis.CSharp.Syntax;
5 |
6 | namespace InternalsViewer.Internals.Metadata.SourceGenerators;
7 |
8 | public class TypeFinder : ISyntaxReceiver
9 | {
10 | public List LoadTypes { get; } = new();
11 |
12 | public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
13 | {
14 | if (syntaxNode is RecordDeclarationSyntax type)
15 | {
16 | if (type.AttributeLists
17 | .SelectMany(s=>s.Attributes)
18 | .Any(a => a.GetText().ToString().EndsWith("InternalsMetadata")))
19 | {
20 | LoadTypes.Add(type);
21 | }
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests.VerificationTool/Helpers/ConnectionStringHelper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 |
3 | namespace InternalsViewer.Internals.Tests.VerificationTool.Helpers;
4 |
5 | internal class ConnectionStringHelper
6 | {
7 | public static string GetConnectionString(string name)
8 | {
9 | var builder = new ConfigurationBuilder().AddUserSecrets();
10 |
11 | var configuration = builder.Build();
12 |
13 | return configuration.GetConnectionString(name) ?? string.Empty;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests.VerificationTool/Indexes/IndexVerification.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace InternalsViewer.Internals.Tests.VerificationTool.Indexes;
8 |
9 | internal class IndexVerification
10 | {
11 | }
12 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests.VerificationTool/InternalsViewer.Internals.Tests.VerificationTool.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 | enable
7 | enable
8 | ec4f112d-d125-4a6d-83ad-34a84213b825
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | global using InternalsViewer.Internals.Engine.Address;
2 | global using InternalsViewer.Internals.Engine.Allocation.Enums;
3 | global using InternalsViewer.Internals.Engine.Pages;
4 | global using InternalsViewer.Internals.Metadata.Structures;
5 | global using Xunit;
6 | global using Xunit.Abstractions;
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/Helpers/ConnectionStringHelper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 |
3 | namespace InternalsViewer.Internals.Tests.Helpers;
4 | internal class ConnectionStringHelper
5 | {
6 | public static string GetConnectionString(string name)
7 | {
8 | var builder = new ConfigurationBuilder().AddUserSecrets();
9 |
10 | var configuration = builder.Build();
11 |
12 | return configuration.GetConnectionString(name) ?? string.Empty;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/Helpers/TestHelpers.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Tests.Helpers;
2 |
3 | public static class TestHelpers
4 | {
5 | }
6 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/Helpers/TestServiceHost.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Hosting;
2 |
3 | namespace InternalsViewer.Internals.Tests.Helpers;
4 |
5 | internal class TestServiceHost
6 | {
7 | private readonly IHost host;
8 |
9 | public TestServiceHost()
10 | {
11 | host = GetHost();
12 | }
13 |
14 | private IHost GetHost()
15 | {
16 | return
17 | Host
18 | .CreateDefaultBuilder()
19 | .UseContentRoot(AppContext.BaseDirectory)
20 | .ConfigureServices((context, services) =>
21 | {
22 | services.RegisterServices();
23 | }).Build();
24 | }
25 |
26 | internal T GetService() where T : class
27 | {
28 | var service = host.Services.GetService(typeof(T));
29 |
30 | return service as T;
31 | }
32 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/IntegrationTests/Providers/Metadata/AllocationUnitProviderTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Providers.Metadata;
2 |
3 | namespace InternalsViewer.Internals.Tests.IntegrationTests.Providers.Metadata;
4 |
5 | public class AllocationUnitProviderTests(ITestOutputHelper testOutput) : ProviderTestBase(testOutput)
6 | {
7 | [Fact]
8 | public async Task Can_Get_AllocationUnits()
9 | {
10 | var metadata = await GetMetadata();
11 |
12 | var allocationUnits = AllocationUnitProvider.GetAllocationUnits(metadata);
13 |
14 | Assert.NotEmpty(allocationUnits);
15 | }
16 |
17 | [Theory]
18 | [InlineData(72057594060734464)]
19 | public async Task Can_Get_AllocationUnit(long allocationUnitId)
20 | {
21 | var metadata = await GetMetadata();
22 |
23 | var source = metadata.AllocationUnits.First(a=> a.AllocationUnitId == allocationUnitId);
24 | var result = AllocationUnitProvider.GetAllocationUnit(metadata, source);
25 |
26 | Assert.NotNull(result);
27 | }
28 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/IntegrationTests/Providers/Metadata/FileProviderTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Providers.Metadata;
2 |
3 | namespace InternalsViewer.Internals.Tests.IntegrationTests.Providers.Metadata;
4 |
5 | public class FileProviderTests(ITestOutputHelper testOutput) : ProviderTestBase(testOutput)
6 | {
7 | [Fact]
8 | public async Task Can_Load_And_Parse_Metadata()
9 | {
10 | var metadata = await GetMetadata();
11 |
12 | var files = FileProvider.GetFiles(metadata);
13 |
14 | Assert.NotEmpty(files);
15 | }
16 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/IntegrationTests/Providers/Metadata/IndexStructureProviderTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Metadata.Helpers;
2 | using InternalsViewer.Internals.Providers.Metadata;
3 |
4 | namespace InternalsViewer.Internals.Tests.IntegrationTests.Providers.Metadata;
5 |
6 | public class IndexStructureProviderTests(ITestOutputHelper testOutput) : ProviderTestBase(testOutput)
7 | {
8 | [Theory]
9 | //[InlineData(72057594054049792)]
10 | //[InlineData(72057594054115328)]
11 | [InlineData(72057594054049792)]
12 | public async Task Can_Get_IndexStructure(long allocationUnitId)
13 | {
14 | var metadata = await GetMetadata();
15 |
16 | var structure = IndexStructureProvider.GetIndexStructure(metadata, allocationUnitId);
17 |
18 | TestOutput.WriteLine(structure.ToDetailString());
19 |
20 | Assert.NotEmpty(structure.Columns);
21 | }
22 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/IntegrationTests/Providers/Server/BufferPoolInfoProviderTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Providers.Server;
2 | using InternalsViewer.Internals.Tests.Helpers;
3 |
4 | namespace InternalsViewer.Internals.Tests.IntegrationTests.Providers.Server;
5 |
6 | public class BufferPoolInfoProviderTests
7 | {
8 | private static BufferPoolInfoProvider GetProvider()
9 | {
10 | var connectionString = ConnectionStringHelper.GetConnectionString("local");
11 |
12 | var provider = new BufferPoolInfoProvider(connectionString);
13 |
14 | return provider;
15 | }
16 |
17 | [Fact]
18 | public async Task Can_Get_Buffer_Pool_Entries()
19 | {
20 | var provider = GetProvider();
21 |
22 | var bufferPoolEntries = await provider.GetBufferPoolEntries("AdventureWorks2022");
23 |
24 | Assert.NotNull(bufferPoolEntries.Clean);
25 | Assert.NotNull(bufferPoolEntries.Dirty);
26 | }
27 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/IntegrationTests/Readers/QueryPageReaderTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Connections.File;
2 | using InternalsViewer.Internals.Engine.Database;
3 | using InternalsViewer.Internals.Engine.Pages.Enums;
4 | using InternalsViewer.Internals.Readers.Pages;
5 | using InternalsViewer.Internals.Tests.Helpers;
6 |
7 | namespace InternalsViewer.Internals.Tests.IntegrationTests.Readers;
8 |
9 | public class QueryPageReaderTests(ITestOutputHelper testOutputHelper)
10 | {
11 | public ITestOutputHelper TestOutputHelper { get; set; } = testOutputHelper;
12 |
13 | [Fact]
14 | public async Task Can_Read_Database_Page()
15 | {
16 | var connectionString = ConnectionStringHelper.GetConnectionString("local");
17 |
18 | var reader = new QueryPageReader(connectionString);
19 |
20 | var result = await reader.Read("TestDatabase", new PageAddress(1, 1));
21 |
22 | Assert.NotNull(result);
23 | }
24 |
25 | [Fact]
26 | public async Task Can_Read_Mdf_Page()
27 | {
28 | var service = ServiceHelper.CreatePageService(TestOutputHelper);
29 |
30 | var connection = FileConnectionFactory.Create(c => c.Filename = "./IntegrationTests/Test Data/TestDatabase.mdf");
31 |
32 | var result = await service.GetPage(new DatabaseSource(connection),
33 | new PageAddress(1, 9));
34 |
35 | Assert.Equal(PageType.Boot, result.PageHeader.PageType);
36 | }
37 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/IntegrationTests/Services/Indexes/IndexServiceTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Services.Indexes;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using InternalsViewer.Internals.Connections.File;
8 | using InternalsViewer.Internals.Engine.Database;
9 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Engine;
10 | using InternalsViewer.Internals.Services.Records;
11 | using InternalsViewer.Internals.Tests.Helpers;
12 | using InternalsViewer.Internals.Readers.Internals;
13 | using InternalsViewer.Internals.Services.Loaders.Engine;
14 | using Microsoft.Extensions.Logging;
15 |
16 | namespace InternalsViewer.Internals.Tests.IntegrationTests.Services.Indexes;
17 |
18 | public class IndexServiceTests(ITestOutputHelper testOutput)
19 | {
20 | public ITestOutputHelper TestOutput { get; } = testOutput;
21 |
22 | [Fact]
23 | public async Task Can_Get_Index_Nodes()
24 | {
25 | var serviceHost = new TestServiceHost();
26 |
27 | var service = serviceHost.GetService();
28 |
29 | var connection = FileConnectionFactory.Create(c => c.Filename = "./IntegrationTests/Test Data/TestDatabase/TestDatabase.mdf");
30 |
31 | var databaseService = serviceHost.GetService();
32 |
33 | var database = await databaseService.LoadAsync("TestDatabase", connection);
34 |
35 | var result = await service.GetNodes(database, new PageAddress(1,1688));
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/IntegrationTests/Test Data/TestDatabase/TestDatabase.mdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.Internals.Tests/IntegrationTests/Test Data/TestDatabase/TestDatabase.mdf
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Engine/Parsers/LogSequenceNumberParserTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Parsers;
2 |
3 | namespace InternalsViewer.Internals.Tests.UnitTests.Engine.Parsers;
4 |
5 | public class LogSequenceNumberParserTests
6 | {
7 | [Theory]
8 | [InlineData("0:0:0", 0, 0, 0)]
9 | [InlineData("(0:0:0)", 0, 0, 0)]
10 | [InlineData("1:2:3", 1, 2, 3)]
11 | [InlineData("(1:2:3)", 1, 2, 3)]
12 | [InlineData("(53:24384:559)", 53, 24384, 559)]
13 | [InlineData("00000035:000075B8:0002", 53, 30136, 2)]
14 | public void Can_Parse_Log_Sequence_Number(string value, int virtualLogFile, int fileOffset, int recordSequence)
15 | {
16 | var logSequenceNumber = LogSequenceNumberParser.Parse(value);
17 |
18 | Assert.Equal(virtualLogFile, logSequenceNumber.VirtualLogFile);
19 | Assert.Equal(fileOffset, logSequenceNumber.FileOffset);
20 | Assert.Equal(recordSequence, logSequenceNumber.RecordSequence);
21 | }
22 |
23 | [Theory]
24 | [InlineData(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0, 0, 0)]
25 | public void Can_Parse_Binary_Log_Sequence_Number(byte[] value, int virtualLogFile, int fileOffset, int recordSequence)
26 | {
27 | var logSequenceNumber = LogSequenceNumberParser.Parse(value);
28 |
29 | Assert.Equal(virtualLogFile, logSequenceNumber.VirtualLogFile);
30 | Assert.Equal(fileOffset, logSequenceNumber.FileOffset);
31 | Assert.Equal(recordSequence, logSequenceNumber.RecordSequence);
32 | }
33 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Engine/Parsers/PageAddressParserTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Parsers;
2 |
3 | namespace InternalsViewer.Internals.Tests.UnitTests.Engine.Parsers;
4 |
5 | public class PageAddressParserTests
6 | {
7 | [Theory]
8 | [InlineData("1:9", 1, 9)]
9 | [InlineData("(1:9)", 1, 9)]
10 | [InlineData("(01:09)", 1, 9)]
11 | [InlineData("2:1000", 2, 1000)]
12 | [InlineData("0x000000000000", 0, 0)]
13 | [InlineData("0x521A00000100", 1, 6738)]
14 | [InlineData("0x140000000100", 1, 20)]
15 | [InlineData("0x111111111111", 4369, 286331153)]
16 | [InlineData("0xDF0200000100", 1, 735)]
17 | public void Can_Parse_Page_Address(string value, int expectedFileId, int expectedPageId)
18 | {
19 | var pageAddress = PageAddressParser.Parse(value);
20 |
21 | Assert.Equal(expectedFileId, pageAddress.FileId);
22 | Assert.Equal(expectedPageId, pageAddress.PageId);
23 | }
24 |
25 | [Theory]
26 | [InlineData("1")]
27 | [InlineData("1-10")]
28 | [InlineData("1:ABC")]
29 | [InlineData("0x00")]
30 | public void Invalid_Format_Throws_Exception(string value)
31 | {
32 | Assert.Throws(() => PageAddressParser.Parse(value));
33 | }
34 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Helpers/IdHelperTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Helpers;
2 |
3 | namespace InternalsViewer.Internals.Tests.UnitTests.Helpers;
4 |
5 | public class IdHelperTests
6 | {
7 | [Theory]
8 | [InlineData(97, 256, 72057594044284928)]
9 | [InlineData(7, 2, 562949953880064)]
10 | [InlineData(7, 0, 458752)]
11 | public void Can_Get_AllocationUnitId_From_ObjectId_IndexId(int objectId, int indexId, long expected)
12 | {
13 | var result = IdHelpers.GetAllocationUnitId(objectId, indexId);
14 |
15 | Assert.Equal(expected, result);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Readers/RecordReaderTests.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Tests.UnitTests.Readers;
2 | public class RecordReaderTests
3 | {
4 | //[Fact]
5 | //public Task Can_Read_Table()
6 | //{
7 |
8 | //}
9 | }
10 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Services/BufferPoolServiceTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Interfaces.MetadataProviders;
2 | using InternalsViewer.Internals.Services;
3 | using Moq;
4 |
5 | namespace InternalsViewer.Internals.Tests.UnitTests.Services;
6 |
7 | public class BufferPoolServiceTests
8 | {
9 | [Fact]
10 | public async Task Can_Get_Buffer_Pool()
11 | {
12 | var provider = new Mock();
13 |
14 | var results =
15 | (
16 | Clean: new List { new(1, 100), new(1, 102) },
17 | Dirty: new List { new(1, 200), new(1, 202) }
18 | );
19 |
20 | provider.Setup(p => p.GetBufferPoolEntries("TestDatabase"))
21 | .ReturnsAsync(results);
22 |
23 | var service = new BufferPoolService(provider.Object);
24 |
25 | var bufferPool = await service.GetBufferPool("TestDatabase");
26 |
27 | Assert.Equivalent(results.Clean, bufferPool.CleanPages);
28 | Assert.Equivalent(results.Dirty, bufferPool.DirtyPages);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Services/Loaders/Records/Fields/LobOverflowFieldLoaderTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Records.Blob.BlobPointers;
2 | using InternalsViewer.Internals.Helpers;
3 | using InternalsViewer.Internals.Services.Loaders.Records.Fields;
4 |
5 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Loaders.Records.Fields;
6 |
7 | public class LobOverflowFieldLoaderTests
8 | {
9 | private const string TestData = "02 00 00 00 01 00 00 00 23 48 00 00 40 1F 00 00 4D 03 00 00 01 00 00 00";
10 |
11 | ///
12 | ///
13 | ///
14 | ///
15 | /// DBCC Page output:
16 | ///
17 | /// Column3 = [BLOB Inline Root] Slot 0 Column 3 Offset 0x1f51 Length 24 Length (physical) 24
18 | ///
19 | /// Level = 0 Unused = 0 UpdateSeq = 1
20 | /// TimeStamp = 1210253312 Type = 2
21 | /// Link 0
22 | ///
23 | /// Size = 8000 RowId = (1:845:0)
24 | ///
25 | [Fact]
26 | public void Can_Load_Overflow_Field()
27 | {
28 | var data = TestData.ToByteArray();
29 |
30 | var field = LobOverflowFieldLoader.Load(data, 0);
31 |
32 | Assert.Equal(BlobFieldType.RowOverflow, field.PointerType);
33 | Assert.Equal(0, field.Level);
34 | Assert.Equal((uint)1210253312, field.Timestamp);
35 | Assert.Equal(1, field.UpdateSeq);
36 | Assert.Equal(8000, field.Length);
37 | Assert.Equal(new RowIdentifier(1, 845, 0), field.Links[0].RowIdentifier);
38 | }
39 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Services/Loaders/Records/Fields/LobPointerFieldLoaderTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Records.Blob.BlobPointers;
2 | using InternalsViewer.Internals.Helpers;
3 | using InternalsViewer.Internals.Services.Loaders.Records.Fields;
4 |
5 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Loaders.Records.Fields;
6 |
7 | public class LobPointerFieldLoaderTests
8 | {
9 | private const string TestData = "00 00 35 F2 00 00 00 00 10 23 00 00 01 00 01 00";
10 |
11 | ///
12 | ///
13 | ///
14 | ///
15 | /// DBCC Page output:
16 | ///
17 | /// Col3 = [Textpointer] Slot 0 Column 3 Offset 0x15 Length 16 Length (physical) 16
18 | ///
19 | /// TextTimeStamp = 4063559680 RowId = (1:8976:1)
20 | ///
21 | ///
22 | [Fact]
23 | public void Can_Load_Pointer_Field()
24 | {
25 | var data = TestData.ToByteArray();
26 |
27 | var field = LobPointerFieldLoader.Load(data, 0);
28 |
29 | Assert.Equal(BlobFieldType.LobPointer, field.PointerType);
30 | Assert.Equal(4063559680, field.Timestamp);
31 | Assert.Equal(new RowIdentifier(1, 8976, 1), field.Links[0].RowIdentifier);
32 | }
33 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Services/Loaders/Records/Fields/LobRootFieldLoaderTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Records.Blob.BlobPointers;
2 | using InternalsViewer.Internals.Helpers;
3 | using InternalsViewer.Internals.Services.Loaders.Records.Fields;
4 |
5 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Loaders.Records.Fields;
6 |
7 | public class LobRootFieldLoaderTests
8 | {
9 | private const string TestData = "04 00 00 00 01 00 00 00 29 00 00 00 40 1F 00 00 58 03 00 00 01 00 00 00";
10 |
11 | ///
12 | ///
13 | ///
14 | ///
15 | /// DBCC Page output:
16 | ///
17 | /// Column4 = [BLOB Inline Root] Slot 0 Column 4 Offset 0x43 Length 24 Length (physical) 24
18 | ///
19 | /// Level = 0 Unused = 0 UpdateSeq = 1
20 | /// TimeStamp = 2686976 Type = 4
21 | /// Link 0
22 | ///
23 | /// Size = 8000 RowId = (1:856:0)
24 | ///
25 | [Fact]
26 | public void Can_Load_Root_Field()
27 | {
28 | var data = TestData.ToByteArray();
29 |
30 | var field = LobRootFieldLoader.Load(data, 0);
31 |
32 | Assert.Equal(BlobFieldType.LobRoot, field.PointerType);
33 | Assert.Equal(0, field.Level);
34 | Assert.Equal((uint)2686976, field.Timestamp);
35 | Assert.Equal(1, field.UpdateSeq);
36 |
37 | Assert.Equal(new RowIdentifier(1, 856, 0), field.Links[0].RowIdentifier);
38 | Assert.Equal(8000, field.Links[0].Length);
39 | }
40 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Loaders/TestPageHeader.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Loaders;
2 |
3 | public class TestPageHeader
4 | {
5 | public static Dictionary HeaderValues { get; set; } = new()
6 | {
7 | {"m_slotCnt", "1"},
8 | {"m_freeCnt", "2"},
9 | {"m_freeData", "3"},
10 | {"m_type", "8"},
11 | {"m_level", "0"},
12 | {"pminlen", "1"},
13 | {"Metadata: IndexId", "1"},
14 | {"Metadata: AllocUnitId", "6488064"},
15 | {"Metadata: ObjectId", "99"},
16 | {"Metadata: PartitionId", "0"},
17 | {"m_reservedCnt", "1"},
18 | {"m_xactReserved", "2"},
19 | {"m_tornBits", "-273531820"},
20 | {"m_pageId", "(1:2)"},
21 | {"m_lsn", "(53:29745:7)"},
22 | {"m_flagBits", "0x200"},
23 | {"m_prevPage", "(0:0)"},
24 | {"m_nextPage", "(0:0)"}
25 | };
26 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Parsers/AllocationPageParserTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Pages.Enums;
2 | using InternalsViewer.Internals.Services.Pages.Parsers;
3 |
4 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Parsers;
5 |
6 | public class AllocationPageParserTests(ITestOutputHelper testOutput)
7 | : PageParserTestsBase(testOutput)
8 | {
9 | [Theory]
10 | [InlineData(AllocationPage.FirstGamPage, PageType.Gam)]
11 | [InlineData(AllocationPage.FirstSgamPage, PageType.Sgam)]
12 | [InlineData(AllocationPage.FirstDcmPage, PageType.Dcm)]
13 | [InlineData(AllocationPage.FirstBcmPage, PageType.Bcm)]
14 | public async Task Can_Parse_Allocation_Page(int pageId, PageType pageType)
15 | {
16 | var pageData = await GetPageData(new PageAddress(1, pageId));
17 |
18 | var parser = new AllocationPageParser();
19 |
20 | var allocationPage = parser.Parse(pageData);
21 |
22 | Assert.True(allocationPage.PageHeader.PageType == pageType);
23 | }
24 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Parsers/IamPageParserTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Pages.Enums;
2 | using InternalsViewer.Internals.Services.Pages.Parsers;
3 |
4 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Parsers;
5 |
6 | public class IamPageParserTests(ITestOutputHelper testOutput)
7 | : PageParserTestsBase(testOutput)
8 | {
9 | [Theory]
10 | [InlineData(100)]
11 | public async Task Can_Parse_Iam_Page(int pageId)
12 | {
13 | var pageData = await GetPageData(new PageAddress(1, pageId));
14 |
15 | var parser = new IamPageParser();
16 |
17 | var page = parser.Parse(pageData);
18 |
19 | Assert.Equal(PageType.Iam, page.PageHeader.PageType);
20 | Assert.Equal(new PageAddress(1, 0), page.StartPage);
21 |
22 | Assert.Equal(new PageAddress(1, 99), page.SinglePageSlots[0]);
23 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[1]);
24 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[2]);
25 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[3]);
26 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[4]);
27 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[5]);
28 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[6]);
29 | Assert.Equal(new PageAddress(0, 0), page.SinglePageSlots[7]);
30 | }
31 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Parsers/IndexPageParserTests.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Parsers;
2 |
3 | public class IndexPageParserTests(ITestOutputHelper testOutput)
4 | : PageParserTestsBase(testOutput)
5 | {
6 | public async Task Can_Parse_Index_Page()
7 | {
8 |
9 | }
10 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Parsers/LobPageParserTests.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Parsers;
2 |
3 | public class LobPageParserTests(ITestOutputHelper testOutput)
4 | : PageParserTestsBase(testOutput)
5 | {
6 | public async Task Can_Parse_Lob_Page()
7 | {
8 |
9 | }
10 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Parsers/PageParserTestsBase.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Database;
2 | using InternalsViewer.Internals.Services.Pages.Loaders;
3 | using InternalsViewer.Internals.Tests.Helpers;
4 |
5 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Parsers;
6 |
7 | public abstract class PageParserTestsBase(ITestOutputHelper testOutput)
8 | {
9 | public ITestOutputHelper TestOutput { get; } = testOutput;
10 |
11 | protected DatabaseSource Database { get; set; }
12 |
13 | protected async Task GetPageData(PageAddress pageAddress)
14 | {
15 | var filePath = "./UnitTests/Test Data/Test Pages/";
16 |
17 | var reader = new FilePageReader(filePath);
18 |
19 | var pageLoader = new PageLoader();
20 |
21 | return await pageLoader.Load(Database, pageAddress);
22 | }
23 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals.Tests/UnitTests/Services/Pages/Parsers/PfsPageParserTests.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Services.Pages.Parsers;
2 |
3 | namespace InternalsViewer.Internals.Tests.UnitTests.Services.Pages.Parsers;
4 |
5 | public class PfsPageParserTests(ITestOutputHelper testOutput)
6 | : PageParserTestsBase(testOutput)
7 | {
8 | [Fact]
9 | public async Task Can_Parse_Pfs_Page()
10 | {
11 | var pageData = await GetPageData(new PageAddress(1, 1));
12 |
13 | var parser = new PfsPageParser();
14 |
15 | var page = parser.Parse(pageData);
16 |
17 | Assert.Equal(new PfsByte { IsAllocated = true, PageSpaceFree = SpaceFree.OneHundredPercent, IsIam = false, IsMixed = false },
18 | page.PfsBytes[0]);
19 |
20 | Assert.Equal(new PfsByte { IsAllocated = true, PageSpaceFree = SpaceFree.OneHundredPercent, IsIam = false, IsMixed = false },
21 | page.PfsBytes[1]);
22 |
23 | Assert.Equal(new PfsByte { IsAllocated = true, PageSpaceFree = SpaceFree.Empty, IsIam = true, IsMixed = true },
24 | page.PfsBytes[100]);
25 | }
26 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Annotations/DataStructureItem.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Annotations;
2 |
3 | ///
4 | /// Data Structure annotations that can be made against a Data Structure object
5 | ///
6 | public class DataStructureItem
7 | {
8 | public ItemType ItemType { get; set; }
9 |
10 | public int Offset { get; set; } = -1;
11 |
12 | public int Length { get; set; } = -1;
13 |
14 | public string Name { get; set; } = string.Empty;
15 |
16 | public int Index { get; set; } = -1;
17 |
18 | public string Prefix { get; set; } = string.Empty;
19 |
20 | public List Tags { get; set; } = new();
21 |
22 | public bool IsVisible { get; set; } = true;
23 | }
24 |
25 | public class PropertyItem : DataStructureItem
26 | {
27 | public string PropertyName { get; set; } = string.Empty;
28 | }
29 |
30 | public class ValueItem : DataStructureItem
31 | {
32 | public object? Value { get; set; }
33 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Annotations/DataStructureItemAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Annotations;
2 |
3 | ///
4 | /// Custom attribute to store mark properties
5 | ///
6 | [AttributeUsage(AttributeTargets.Property)]
7 | public class DataStructureItemAttribute(ItemType itemType, string description)
8 | : Attribute
9 | {
10 | public string Name { get; set; } = description;
11 |
12 | public ItemType ItemType { get; set; } = itemType;
13 |
14 | public DataStructureItemAttribute(ItemType itemType)
15 | : this(itemType, string.Empty)
16 | {
17 | }
18 |
19 | public bool IsVisible { get; set; } = true;
20 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Connections/Backup/BackupConnectionFactory.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Interfaces.Connections;
2 |
3 | namespace InternalsViewer.Internals.Connections.Backup;
4 |
5 | public class BackupConnectionFactory : IConnectionTypeFactory
6 | {
7 | public string Identifier => "Backup";
8 |
9 | public static IConnectionType Create(Action configDelegate)
10 | {
11 | var config = new BackupConnectionTypeConfig();
12 |
13 | configDelegate(config);
14 |
15 | throw new NotImplementedException();
16 | }
17 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Connections/Backup/BackupConnectionType.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Interfaces.Connections;
2 | using InternalsViewer.Internals.Interfaces.Readers;
3 |
4 | namespace InternalsViewer.Internals.Connections.Backup;
5 |
6 | public class BackupConnectionType(IPageReader pageReader, string name): IConnectionType
7 | {
8 | public string Identifier => "Backup";
9 |
10 | public string Name { get; set; } = name;
11 |
12 | public IPageReader PageReader { get; } = pageReader;
13 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Connections/Backup/BackupConnectionTypeConfig.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Connections.Backup;
2 |
3 | public class BackupConnectionTypeConfig() : ConnectionTypeConfig
4 | {
5 | public string Filename { get; set; } = string.Empty;
6 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Connections/ConnectionTypeConfig.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Connections;
2 |
3 | public abstract class ConnectionTypeConfig;
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Connections/File/FileConnectionFactory.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Interfaces.Connections;
2 | using InternalsViewer.Internals.Readers.Pages;
3 |
4 | namespace InternalsViewer.Internals.Connections.File;
5 |
6 | public class FileConnectionFactory : IConnectionTypeFactory
7 | {
8 | public string Identifier => "File";
9 |
10 | public static IConnectionType Create(Action configDelegate)
11 | {
12 | var config = new FileConnectionTypeConfig();
13 |
14 | configDelegate(config);
15 |
16 | var name = System.IO.Path.GetFileNameWithoutExtension(config.Filename);
17 |
18 | return new FileConnectionType(new DataFilePageReader(config.Filename), name);
19 | }
20 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Connections/File/FileConnectionType.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Interfaces.Connections;
2 | using InternalsViewer.Internals.Interfaces.Readers;
3 |
4 | namespace InternalsViewer.Internals.Connections.File;
5 |
6 | public class FileConnectionType(IPageReader pageReader, string name) : IConnectionType
7 | {
8 | public IPageReader PageReader { get; } = pageReader;
9 |
10 | public string Name { get; set; } = name;
11 |
12 | public string Identifier => "File";
13 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Connections/File/FileConnectionTypeConfig.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Connections.File;
2 |
3 | public class FileConnectionTypeConfig: ConnectionTypeConfig
4 | {
5 | public string Filename { get; set; } = string.Empty;
6 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Connections/Server/ServerConnectionConfig.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Connections.Server;
2 |
3 | public class ServerConnectionConfig : ConnectionTypeConfig
4 | {
5 | public string ConnectionString { get; set; } = string.Empty;
6 |
7 | public string Name { get; set; } = string.Empty;
8 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Connections/Server/ServerConnectionFactory.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Interfaces.Connections;
2 | using InternalsViewer.Internals.Readers.Pages;
3 | using Microsoft.Data.SqlClient;
4 |
5 | namespace InternalsViewer.Internals.Connections.Server;
6 |
7 | public class ServerConnectionFactory : IConnectionTypeFactory
8 | {
9 | public string Identifier => "Server";
10 |
11 | public static IConnectionType Create(Action config)
12 | {
13 | var c = new ServerConnectionConfig();
14 |
15 | config.Invoke(c);
16 | var connectionStringBuilder = new SqlConnectionStringBuilder(c.ConnectionString);
17 |
18 | var name = connectionStringBuilder.InitialCatalog ?? c.Name;
19 |
20 | return new ServerConnectionType(new QueryPageReader(c.ConnectionString), name);
21 | }
22 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Connections/Server/ServerConnectionType.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Interfaces.Connections;
2 | using InternalsViewer.Internals.Interfaces.Readers;
3 |
4 | namespace InternalsViewer.Internals.Connections.Server;
5 |
6 | public class ServerConnectionType(IPageReader pageReader, string name) : IConnectionType
7 | {
8 | public string Identifier => "Server";
9 |
10 | public string Name { get; set; } = name;
11 |
12 | public IPageReader PageReader { get; } = pageReader;
13 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Converters/Decoder/DecodeResult.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Converters.Decoder;
2 |
3 | public record DecodeResult(string DataType, string Value)
4 | {
5 | public string DataType { get; set; } = DataType;
6 |
7 | public string Value { get; set; } = Value;
8 | }
9 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Address/LogSequenceNumber.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Address;
2 |
3 | public readonly record struct LogSequenceNumber(int VirtualLogFile, int FileOffset, short RecordSequence)
4 | {
5 | public const int Size = sizeof(int) + sizeof(int) + sizeof(short);
6 |
7 | public override string ToString() => $"({VirtualLogFile}:{FileOffset}:{RecordSequence})";
8 |
9 | public string ToBinaryString()
10 | {
11 | return $"{VirtualLogFile:X8}:{FileOffset:X8}:{RecordSequence:X4}";
12 | }
13 |
14 | public decimal ToDecimal()
15 | {
16 | return decimal.Parse($"{VirtualLogFile}{FileOffset:0000000000}{RecordSequence:00000}");
17 | }
18 |
19 | public decimal ToDecimalFileOffsetOnly()
20 | {
21 | return decimal.Parse($"{VirtualLogFile}{FileOffset:0000000000}");
22 | }
23 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Address/PageAddress.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 |
3 | namespace InternalsViewer.Internals.Engine.Address;
4 |
5 | ///
6 | /// Page Address that gives a unique address for a page in a database
7 | ///
8 | public record struct PageAddress(short FileId, int PageId)
9 | {
10 | public static readonly PageAddress Empty = new();
11 |
12 | ///
13 | /// Size of a Page Address is 6 bytes (2 byte File Id + 4 byte Page Id)
14 | ///
15 | public const int Size = sizeof(short) + sizeof(int);
16 |
17 | ///
18 | /// File Id for the Page Address
19 | ///
20 | public short FileId { get; } = FileId;
21 |
22 | ///
23 | /// Page Id for the Page Address
24 | ///
25 | public int PageId { get; } = PageId;
26 |
27 | ///
28 | /// Extent (block of 8 pages) for the Page Address
29 | ///
30 | public readonly int Extent => (PageId - 1) / 8;
31 |
32 | public override string ToString()
33 | {
34 | return string.Format(CultureInfo.CurrentCulture, "({0}:{1})", FileId, PageId);
35 | }
36 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Allocation/Enums/ChainType.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Allocation.Enums;
2 |
3 | ///
4 | /// Allocation chain type
5 | ///
6 | public enum ChainType
7 | {
8 | ///
9 | /// If the next page in the chain is based on a bitmap interval
10 | ///
11 | Interval,
12 | ///
13 | /// If the next page in the chain is based on a linked list in the Page Header
14 | ///
15 | Linked
16 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Allocation/Enums/SpaceFree.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Allocation.Enums;
2 |
3 | public enum SpaceFree : byte
4 | {
5 | Empty = 0x00,
6 | FiftyPercent = 0x01, // 1%-50%
7 | EightyPercent = 0x02, // 51%-80%
8 | NinetyFivePercent = 0x03, // 81%-95%
9 | OneHundredPercent = 0x04 // 96-100%
10 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Allocation/PfsChain.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Allocation.Enums;
2 | using InternalsViewer.Internals.Engine.Pages;
3 |
4 | namespace InternalsViewer.Internals.Engine.Allocation;
5 |
6 | ///
7 | /// PFS (Page Free Space) Page Chain
8 | ///
9 | ///
10 | /// Collection of the PFS Pages in a database.
11 | ///
12 | /// Each PFS page covers 8088 pages = 64MB meaning the number of PFS pages per file = (file size / 64MB)
13 | ///
14 | ///
15 | ///
16 | public class PfsChain
17 | {
18 | public List PfsPages { get; } = new();
19 |
20 | ///
21 | /// Gets the PFS status for a given page
22 | ///
23 | public PfsByte GetPageStatus(int page)
24 | {
25 | // How many pages into the PFS chain is the page
26 | var pfsPageIndex = page / PfsPage.PfsInterval;
27 |
28 | // Where in the byte map is the page
29 | var pfsByteIndex = page % PfsPage.PfsInterval;
30 |
31 | // Check the PFS byte exists
32 | var result = PfsPages.Count > pfsPageIndex && PfsPages[pfsPageIndex].PfsBytes.Count > pfsByteIndex
33 | ? PfsPages[pfsPageIndex].PfsBytes[pfsByteIndex]
34 | : PfsByte.Unknown;
35 |
36 | return result;
37 | }
38 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Database/BufferPool.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 |
3 | namespace InternalsViewer.Internals.Engine.Database;
4 |
5 | ///
6 | /// Set of pages in the server's buffer bool
7 | ///
8 | public record BufferPool(List CleanPages, List DirtyPages)
9 | {
10 | ///
11 | /// Gets the clean page addresses.
12 | ///
13 | public List CleanPages { get; } = CleanPages;
14 |
15 | ///
16 | /// Gets the dirty page addresses.
17 | ///
18 | public List DirtyPages { get; } = DirtyPages;
19 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Database/DatabaseFile.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace InternalsViewer.Internals.Engine.Database;
3 |
4 | ///
5 | /// SQL Server Database File
6 | ///
7 | ///
8 | public record DatabaseFile(short FileId)
9 | {
10 | public short FileId { get; set; } = FileId;
11 |
12 | public short FileGroupId { get; set; }
13 |
14 | public string Name { get; set; } = string.Empty;
15 |
16 | public FileType FileType { get; set; }
17 |
18 | public string PhysicalName { get; set; } = string.Empty;
19 |
20 | public long TotalPages { get; set; }
21 |
22 | public long UsedPages { get; set; }
23 |
24 | public float TotalMb => TotalPages * 512 / 1024F;
25 |
26 | public float UsedMb => UsedPages * 512 / 1024F;
27 |
28 | public string FileName => PhysicalName[(PhysicalName.LastIndexOf('\\') + 1)..];
29 |
30 | public int Size { get; set; }
31 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Database/DatabaseSummary.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Database.Enums;
2 |
3 | namespace InternalsViewer.Internals.Engine.Database;
4 |
5 | public class DatabaseSummary
6 | {
7 | public short DatabaseId { get; set; }
8 |
9 | public string Name { get; set; } = string.Empty;
10 |
11 | public DatabaseState State { get; set; }
12 |
13 | public byte CompatibilityLevel { get; set; }
14 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Database/Enums/AllocationUnitType.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Database.Enums;
2 |
3 | ///
4 | /// Allocation Unit Types
5 | ///
6 | ///
7 | public enum AllocationUnitType : byte
8 | {
9 | Dropped = 0,
10 |
11 | ///
12 | /// In-row data (all data types, except LOB data types)
13 | ///
14 | InRowData = 1,
15 |
16 | ///
17 | /// Large object (LOB) data (text, ntext, image, xml, large value types, and CLR user-defined types)
18 | ///
19 | LargeObjectData = 2,
20 |
21 | RowOverflowData = 3
22 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Database/Enums/DatabaseState.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Database.Enums;
2 |
3 | ///
4 | /// Database States
5 | ///
6 | ///
7 | public enum DatabaseState : byte
8 | {
9 | Online = 0,
10 | Restoring = 1,
11 | Recovering = 2,
12 | RecoveryPending = 3,
13 | Suspect = 4,
14 | Emergency = 5,
15 | Offline = 6,
16 | Copying = 7,
17 | OfflineSecondary = 10
18 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Database/Enums/IndexType.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Database.Enums;
2 |
3 | ///
4 | /// Index Types
5 | ///
6 | ///
7 | public enum IndexType : byte
8 | {
9 | Heap = 0,
10 | Clustered = 1,
11 | NonClustered = 2,
12 | Xml = 3,
13 | Spatial = 4,
14 | ClusteredColumnStore = 5,
15 | NonClusteredColumnStore = 6,
16 | NonClusteredHash = 7
17 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Database/FileType.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Database;
2 |
3 | ///
4 | /// File Types
5 | ///
6 | ///
7 | /// Source: SELECT * FROM sys.syspalvalues ft WHERE ft.class = 'DBFT'
8 | ///
9 | public enum FileType : byte
10 | {
11 | Rows = 0,
12 | Log = 1,
13 | Filestream = 2,
14 | FilestreamLog = 3,
15 | FullText = 4
16 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Indexes/IndexNode.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 | using InternalsViewer.Internals.Engine.Pages.Enums;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace InternalsViewer.Internals.Engine.Indexes;
10 |
11 | public class IndexNode(PageAddress pageAddress)
12 | {
13 | public PageAddress PageAddress { get; set; } = pageAddress;
14 |
15 | public PageType PageType { get; set; }
16 |
17 | public List Parents { get; set; } = new();
18 |
19 | public List Children { get; set; } = new();
20 |
21 | public PageAddress NextPage { get; set; }
22 |
23 | public PageAddress PreviousPage { get; set; }
24 |
25 | public int Level { get; set; }
26 |
27 | public int Ordinal { get; set; }
28 |
29 | public int IndexLevel { get; set; }
30 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Pages/AllocationPage.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Pages;
2 |
3 | ///
4 | /// Allocation Page containing an allocation bitmap (IAM, GAM, SGAM, DCM, BCM)
5 | ///
6 | ///
7 | /// Allocation Pages are used to track the allocation of pages and extents within a database file
8 | ///
9 | public class AllocationPage : Page
10 | {
11 | ///
12 | /// Interval between allocation pages
13 | ///
14 | ///
15 | /// 1 bit per extent representing allocation status of the extent
16 | ///
17 | /// 63,904 bits = 7,988 bytes = 1 page (8,192 bytes) less page header/overhead.
18 | ///
19 | public const int AllocationInterval = 63904;
20 |
21 | public const int AllocationArrayOffset = 194;
22 |
23 | public const int SinglePageSlotOffset = 142;
24 |
25 | public const int SlotCount = 8;
26 |
27 | public const int FirstGamPage = 2;
28 |
29 | public const int FirstSgamPage = 3;
30 |
31 | public const int FirstDcmPage = 6;
32 |
33 | public const int FirstBcmPage = 7;
34 |
35 | ///
36 | /// Allocation bitmap
37 | ///
38 | public bool[] AllocationMap { get; } = new bool[AllocationInterval];
39 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Pages/AllocationUnitPage.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Database;
2 | using InternalsViewer.Internals.Engine.Records.CdRecordType;
3 |
4 | namespace InternalsViewer.Internals.Engine.Pages;
5 |
6 | public abstract class AllocationUnitPage
7 | : Page
8 | {
9 | ///
10 | /// If page compression is active
11 | ///
12 | ///
13 | /// Page Compression can be turned on against the object, but not all pages will necessarily be compressed, for example if the page
14 | /// is not full it will use row compression only.
15 | ///
16 | /// The type flag bit 0x80 (128 / bit 8) determines if the page is compressed.
17 | ///
18 | public bool IsPageCompressed => (PageHeader.TypeFlagBits & 0x80) != 0;
19 |
20 | ///
21 | /// CI (Compression Information) structure for compressed data/index pages
22 | ///
23 | public CompressionInfo? CompressionInfo { get; set; }
24 |
25 | public AllocationUnit AllocationUnit { get; set; } = AllocationUnit.Unknown;
26 | }
27 |
28 | public class IndexPage
29 | : AllocationUnitPage;
30 |
31 | public class DataPage
32 | : AllocationUnitPage;
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Pages/BootPage.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 |
3 | namespace InternalsViewer.Internals.Engine.Pages;
4 |
5 | ///
6 | /// Boot Page
7 | ///
8 | public class BootPage : Page
9 | {
10 | public static PageAddress BootPageAddress = new(1, 9);
11 |
12 | public string DatabaseName { get; set; } = string.Empty;
13 |
14 | public int DatabaseId { get; set; }
15 |
16 | public short CreatedVersion { get; set; }
17 |
18 | public short CurrentVersion { get; set; }
19 |
20 | ///
21 | /// First page for the Allocation Units (sys.sysallocunits) table
22 | ///
23 | ///
24 | /// dbi_firstSysIndexes in the DBCC PAGE output
25 | ///
26 | public PageAddress FirstAllocationUnitsPage { get; set; }
27 |
28 | public int Status { get; set; }
29 |
30 | public DateTime CreatedDateTime { get; set; }
31 |
32 | public int CompatibilityLevel { get; set; }
33 |
34 | ///
35 | /// Last checkpoint LSN.
36 | ///
37 | public LogSequenceNumber CheckpointLsn { get; set; }
38 |
39 | public long MaxLogSpaceUsed { get; set; }
40 |
41 | public int Collation { get; set; }
42 |
43 | public long NextAllocationUnitId { get; set; }
44 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Pages/EmptyPage.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Pages.Enums;
2 |
3 | namespace InternalsViewer.Internals.Engine.Pages;
4 |
5 | public class EmptyPage : Page
6 | {
7 | public EmptyPage()
8 | {
9 | PageHeader.PageType = PageType.None;
10 | }
11 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Pages/Enums/PageType.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Pages.Enums;
2 |
3 | ///
4 | /// Page Type
5 | ///
6 | public enum PageType
7 | {
8 | None = 0,
9 | FileHeader = 15,
10 | Data = 1,
11 | Index = 2,
12 | Lob3 = 3,
13 | Lob4 = 4,
14 | Sort = 7,
15 | Gam = 8,
16 | Sgam = 9,
17 | Iam = 10,
18 | Pfs = 11,
19 | Boot = 13,
20 | Dcm = 16,
21 | Bcm = 17
22 | }
23 |
24 | public enum AllocationUnitPageFlags
25 | {
26 | AllocNonLogged = 0x20,
27 | HasChecksum = 0x200,
28 | VersionInfo = 0x2000,
29 | AddBeg = 0x4000,
30 | AddEnd = 0x8000,
31 | HasFreeSlot = 0x10000,
32 | PgAligned4 = 0x2,
33 | FixedLengthRow = 0x4
34 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Pages/FileHeaderPage.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Pages;
2 |
3 | public class FileHeaderPage : DataPage;
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Pages/LobPage.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Pages;
2 |
3 | public class LobPage : Page
4 | {
5 |
6 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Pages/Page.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Pages;
2 |
3 | public abstract class Page : PageData;
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Pages/PageData.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Annotations;
2 | using InternalsViewer.Internals.Engine.Address;
3 | using InternalsViewer.Internals.Engine.Database;
4 |
5 | namespace InternalsViewer.Internals.Engine.Pages;
6 |
7 | ///
8 | /// Page Data
9 | ///
10 | public class PageData : DataStructure
11 | {
12 | ///
13 | /// Page size is 8192 bytes/8KB
14 | ///
15 | public const int Size = 8192;
16 |
17 | ///
18 | /// Database the page belongs to
19 | ///
20 | public DatabaseSource Database { get; init; } = null!;
21 |
22 | ///
23 | /// Page Address in the format File Id : Page Id
24 | ///
25 | public PageAddress PageAddress { get; init; }
26 |
27 | ///
28 | /// Raw page data
29 | ///
30 | public byte[] Data { get; set; } = new byte[Size];
31 |
32 | ///
33 | /// Page Header
34 | ///
35 | public PageHeader PageHeader { get; init; } = new();
36 |
37 | ///
38 | /// Table/Array containing the data offset of each row in the page
39 | ///
40 | public List OffsetTable { get; init; } = new();
41 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Pages/PfsPage.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using InternalsViewer.Internals.Engine.Address;
3 | using InternalsViewer.Internals.Engine.Allocation.Enums;
4 |
5 | namespace InternalsViewer.Internals.Engine.Pages;
6 |
7 | ///
8 | /// PFS (Page Free Space) page
9 | ///
10 | ///
11 | /// Information about page allocation and free space available on pages.
12 | ///
13 | public class PfsPage : Page
14 | {
15 | ///
16 | /// Interval between PFS pages = 8088 bytes/pages (1 byte = 1 pfs entry)
17 | ///
18 | public const int PfsInterval = 8088;
19 |
20 | ///
21 | /// PFS bytes collection
22 | ///
23 | public List PfsBytes { get; set; } = new();
24 |
25 | public override string ToString()
26 | {
27 | var sb = new StringBuilder();
28 |
29 | for (var i = 0; i <= PfsBytes.Count - 1; i++)
30 | {
31 | sb.AppendFormat("{0,-14}{1}", new PageAddress(1, i), PfsBytes[i]);
32 | sb.Append(Environment.NewLine);
33 | }
34 |
35 | return sb.ToString();
36 | }
37 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/Blob/BlobPointers/BlobChildLink.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Annotations;
2 | using InternalsViewer.Internals.Engine.Address;
3 |
4 | namespace InternalsViewer.Internals.Engine.Records.Blob.BlobPointers;
5 |
6 | public class BlobChildLink : DataStructure
7 | {
8 | public BlobChildLink()
9 | {
10 | }
11 |
12 | public BlobChildLink(RowIdentifier rowIdentifier, int offset, int length)
13 | {
14 | RowIdentifier = rowIdentifier;
15 | Offset = offset;
16 | Length = length;
17 | }
18 |
19 | [DataStructureItem(ItemType.Rid)]
20 | public RowIdentifier? RowIdentifier { get; set; }
21 |
22 | [DataStructureItem(ItemType.BlobChildOffset)]
23 | public int Offset { get; set; }
24 |
25 | [DataStructureItem(ItemType.BlobChildLength)]
26 | public int Length { get; set; }
27 |
28 | public override string ToString()
29 | {
30 | return $"Page: {RowIdentifier} Offset: {Offset} Length: {Length}";
31 | }
32 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/Blob/BlobPointers/BlobField.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using InternalsViewer.Internals.Annotations;
3 |
4 | namespace InternalsViewer.Internals.Engine.Records.Blob.BlobPointers;
5 |
6 | ///
7 | /// BLOB internal field
8 | ///
9 | public class BlobField : Field
10 | {
11 | ///
12 | /// Timestamp used by DBCC CHECKTABLE
13 | ///
14 | [DataStructureItem(ItemType.Timestamp)]
15 | public uint Timestamp { get; set; }
16 |
17 | public List Links { get; set; } = new();
18 |
19 | [DataStructureItem(ItemType.Rid)]
20 | public BlobChildLink[] LinksArray => Links.ToArray();
21 |
22 | [DataStructureItem(ItemType.PointerType)]
23 | public BlobFieldType PointerType { get; set; }
24 |
25 | public int Offset { get; set; }
26 |
27 | public override string ToString()
28 | {
29 | var sb = new StringBuilder();
30 |
31 | sb.AppendLine(Timestamp.ToString());
32 |
33 | foreach (var b in Links)
34 | {
35 | sb.AppendLine(b.ToString());
36 | }
37 |
38 | return sb.ToString();
39 | }
40 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/Blob/BlobPointers/BlobFieldType.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Records.Blob.BlobPointers;
2 |
3 | public enum BlobFieldType
4 | {
5 | LobPointer = 0x00,
6 | LobRoot = 0x04,
7 | RowOverflow = 0x02
8 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/Blob/BlobPointers/OverflowField.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Annotations;
2 |
3 | namespace InternalsViewer.Internals.Engine.Records.Blob.BlobPointers;
4 |
5 | ///
6 | /// Row Overflow field
7 | ///
8 | public class OverflowField : BlobField
9 | {
10 | [DataStructureItem(ItemType.OverflowLevel)]
11 | public byte Level { get; set; }
12 |
13 | [DataStructureItem(ItemType.OverflowLength)]
14 | public int Length { get; set; }
15 |
16 | [DataStructureItem(ItemType.Unused)]
17 | public byte Unused { get; set; }
18 |
19 | ///
20 | /// Update seq (used by optimistic concurrency control for cursors)
21 | ///
22 | [DataStructureItem(ItemType.UpdateSeq)]
23 | public short UpdateSeq { get; set; }
24 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/Blob/BlobPointers/PointerField.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Records.Blob.BlobPointers;
2 |
3 | public class PointerField : BlobField;
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/Blob/BlobPointers/RootField.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Annotations;
2 |
3 | namespace InternalsViewer.Internals.Engine.Records.Blob.BlobPointers;
4 |
5 | public class RootField : BlobField
6 | {
7 | [DataStructureItem(ItemType.SlotCount)]
8 | public int SlotCount { get; set; }
9 |
10 | [DataStructureItem(ItemType.Level)]
11 | public byte Level { get; set; }
12 |
13 | [DataStructureItem(ItemType.Unused)]
14 | public byte Unused { get; set; }
15 |
16 | [DataStructureItem(ItemType.UpdateSeq)]
17 | public short UpdateSeq { get; set; }
18 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/Blob/BlobType.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace InternalsViewer.Internals.Engine.Records.Blob;
3 |
4 | public enum BlobType : byte
5 | {
6 | Data = 3,
7 | Internal = 2,
8 | LargeRoot = 5,
9 | SmallRoot = 0
10 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/CdRecordType/ColumnDescriptor.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Annotations;
2 | using InternalsViewer.Internals.Helpers;
3 |
4 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType;
5 |
6 | public class ColumnDescriptor(byte value) : DataStructure
7 | {
8 | public ColumnDescriptorFlag Value { get; } = (ColumnDescriptorFlag)value;
9 |
10 | public int Size => Value switch
11 | {
12 | ColumnDescriptorFlag.Null => 0,
13 | ColumnDescriptorFlag.ZeroByteShort => 0,
14 | ColumnDescriptorFlag.OneByteShort => 1,
15 | ColumnDescriptorFlag.TwoByteShort => 2,
16 | ColumnDescriptorFlag.ThreeByteShort => 3,
17 | ColumnDescriptorFlag.FourByteShort => 4,
18 | ColumnDescriptorFlag.FiveByteShort => 5,
19 | ColumnDescriptorFlag.SixByteShort => 6,
20 | ColumnDescriptorFlag.SevenByteShort => 7,
21 | ColumnDescriptorFlag.EightByteShort => 8,
22 | ColumnDescriptorFlag.Long => 0,
23 | ColumnDescriptorFlag.BitTrue => 0,
24 | ColumnDescriptorFlag.PageSymbol => 1,
25 | _ => 0
26 | };
27 |
28 | public override string ToString()
29 | {
30 | return Value.ToString().SplitCamelCase();
31 | }
32 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/CdRecordType/ColumnDescriptorFlag.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType;
2 |
3 | public enum ColumnDescriptorFlag : byte
4 | {
5 | Null = 0,
6 | ZeroByteShort = 1,
7 | OneByteShort = 2,
8 | TwoByteShort = 3,
9 | ThreeByteShort = 4,
10 | FourByteShort = 5,
11 | FiveByteShort = 6,
12 | SixByteShort = 7,
13 | SevenByteShort = 8,
14 | EightByteShort = 9,
15 | Long = 10,
16 | BitTrue = 11,
17 | PageSymbol = 12
18 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/CdRecordType/CompressedRecordType.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType;
2 |
3 | public enum CompressedRecordType
4 | {
5 | Primary = 0,
6 | GhostEmpty = 1,
7 | Forwarding = 2,
8 | GhostData = 3,
9 | Forwarded = 4,
10 | GhostForwarded = 5,
11 | Index = 6,
12 | GhostIndex = 7
13 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/CdRecordType/CompressionInfo.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Annotations;
2 |
3 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType;
4 |
5 | ///
6 | /// Compression Info record
7 | ///
8 | ///
9 | /// The CI record is a record added for pages compressed using the PAGE compression type.
10 | ///
11 | /// It has the following structure:
12 | ///
13 | /// - Header
14 | /// - Page Modification Count
15 | /// - Offsets (Length = CI Record Size, Size = CI Record Size)
16 | /// - Anchor Record
17 | /// - Dictionary
18 | ///
19 | public class CompressionInfo(int offset) : DataStructure
20 | {
21 | [DataStructureItem(ItemType.PageModificationCount)]
22 | public short PageModificationCount { get; set; }
23 |
24 | [DataStructureItem(ItemType.Size)]
25 | public short Size { get; set; }
26 |
27 | [DataStructureItem(ItemType.Length)]
28 | public ushort Length { get; set; }
29 |
30 | [DataStructureItem(ItemType.Header)]
31 | public byte Header { get; set; }
32 |
33 | public static ushort SlotOffset => 96;
34 |
35 | [DataStructureItem(ItemType.AnchorRecord)]
36 | public CompressedDataRecord? AnchorRecord { get; set; }
37 |
38 | [DataStructureItem(ItemType.CompressionDictionary)]
39 | public Dictionary? CompressionDictionary { get; set; }
40 |
41 | public bool HasAnchorRecord { get; set; }
42 |
43 | public bool HasDictionary { get; set; }
44 |
45 | public int Offset { get; set; } = offset;
46 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/CdRecordType/CompressionInfoStructure.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType;
2 |
3 | public enum CompressionInfoStructure
4 | {
5 | None,
6 | Header,
7 | Anchor,
8 | Dictionary
9 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/CdRecordType/CompressionType.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType;
2 |
3 | ///
4 | /// 2008 Page compression type
5 | ///
6 | [Flags]
7 | public enum CompressionType : byte
8 | {
9 | ///
10 | /// No compression (default)
11 | ///
12 | None = 0,
13 | ///
14 | /// Row level compressions
15 | ///
16 | Row = 1,
17 | ///
18 | /// Page level compression
19 | ///
20 | Page = 2,
21 |
22 | Columnstore = 3,
23 |
24 | ColumnstoreArchive = 4,
25 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/CdRecordType/Dictionary.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using InternalsViewer.Internals.Annotations;
3 |
4 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType;
5 |
6 | public class Dictionary(int offset) : DataStructure
7 | {
8 | public int Offset { get; } = offset;
9 |
10 | [DataStructureItem(ItemType.DictionaryEntries)]
11 | public DictionaryEntry[] DictionaryEntries { get; set; } = Array.Empty();
12 |
13 | [DataStructureItem(ItemType.DictionaryEntryCount)]
14 | public int EntryCount { get; set; }
15 |
16 | [DataStructureItem(ItemType.DictionaryColumnOffsets)]
17 | public ushort[] EntryOffsets { get; set; } = Array.Empty();
18 |
19 | public override string ToString()
20 | {
21 | var sb = new StringBuilder();
22 |
23 | sb.AppendLine($"Dictionary at {Offset}");
24 | sb.AppendLine($"Entry count = {EntryCount}");
25 | sb.AppendLine($"Entry offset = {EntryOffsets}");
26 | sb.AppendLine();
27 | sb.AppendLine($"Dictionary entries");
28 | sb.AppendLine();
29 |
30 | foreach (var entry in DictionaryEntries)
31 | {
32 | sb.AppendLine(entry.ToString());
33 | }
34 |
35 | return sb.ToString();
36 | }
37 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/CdRecordType/DictionaryEntry.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Annotations;
2 |
3 | namespace InternalsViewer.Internals.Engine.Records.CdRecordType;
4 |
5 | public class DictionaryEntry(int symbol, ushort offset, byte[] data) : DataStructure
6 | {
7 | [DataStructureItem(ItemType.DictionarySymbol)]
8 | public int Symbol { get; } = symbol;
9 |
10 | [DataStructureItem(ItemType.DictionaryValue)]
11 | public byte[] Data { get; } = data;
12 |
13 | public ushort Offset { get; } = offset;
14 |
15 | public override string ToString()
16 | {
17 | return $"Dictionary Entry = Symbol {Symbol}, offset {Offset} length {Data.Length}";
18 | }
19 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/Data/DataRecord.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Annotations;
2 | using InternalsViewer.Internals.Converters;
3 | using InternalsViewer.Internals.Engine.Address;
4 | using InternalsViewer.Internals.Engine.Records.FixedVarRecordType;
5 |
6 | namespace InternalsViewer.Internals.Engine.Records.Data;
7 |
8 | public class DataRecord : FixedVarRecord
9 | {
10 | public SparseVector? SparseVector { get; set; }
11 |
12 | [DataStructureItem(ItemType.StatusBitsB)]
13 | public string StatusBitsBDescription => "";
14 |
15 | [DataStructureItem(ItemType.ForwardingStub)]
16 | public RowIdentifier? ForwardingStub { get; set; }
17 |
18 | public RowIdentifier? RowIdentifier { get; set; }
19 |
20 | public T? GetValue(string columnName)
21 | {
22 | var field = Fields.FirstOrDefault(f => f.Name.ToLower() == columnName.ToLower());
23 |
24 | if (field == null)
25 | {
26 | throw new ArgumentException($"Column {columnName} not found");
27 | }
28 |
29 | if (field.Data.Length == 0)
30 | {
31 | return default;
32 | }
33 |
34 | return DataConverter.GetValue(field.Data,
35 | field.ColumnStructure.DataType,
36 | field.ColumnStructure.Precision,
37 | field.ColumnStructure.Scale);
38 | }
39 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/Field.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Annotations;
2 |
3 | namespace InternalsViewer.Internals.Engine.Records;
4 |
5 | public class Field : DataStructure;
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/Index/IndexRecord.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Annotations;
2 | using InternalsViewer.Internals.Engine.Address;
3 | using InternalsViewer.Internals.Engine.Records.FixedVarRecordType;
4 |
5 | namespace InternalsViewer.Internals.Engine.Records.Index;
6 |
7 | public class IndexRecord : FixedVarRecord
8 | {
9 | ///
10 | /// Down page pointer to the next page in the index
11 | ///
12 | [DataStructureItem(ItemType.DownPagePointer)]
13 | public PageAddress DownPagePointer { get; set; }
14 |
15 | ///
16 | /// RID (Row Identifier) the index is pointing to
17 | ///
18 | [DataStructureItem(ItemType.Rid)]
19 | public RowIdentifier? Rid { get; set; }
20 |
21 | public bool IncludeKey { get; set; }
22 |
23 | public NodeType NodeType { get; set; }
24 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/Index/IndexTypes.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace InternalsViewer.Internals.Engine.Records.Index;
3 |
4 | [Flags]
5 | public enum IndexTypes
6 | {
7 | Heap = 1,
8 | Clustered = 2,
9 | NonClustered = 4,
10 | Leaf = 8,
11 | Node = 16,
12 | TableClustered = 32,
13 | NonClusteredLeaf = NonClustered | Leaf
14 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/Index/NodeType.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Engine.Records.Index;
2 |
3 | public enum NodeType
4 | {
5 | Node,
6 | Leaf,
7 | Root,
8 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/Record.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Annotations;
2 |
3 | namespace InternalsViewer.Internals.Engine.Records;
4 |
5 | ///
6 | /// Database Record Structure
7 | ///
8 | public abstract class Record : DataStructure
9 | {
10 | public int Slot { get; set; }
11 |
12 | public ushort Offset { get; set; }
13 |
14 | public List Fields { get; } = new();
15 |
16 | public RecordField[] FieldsArray => Fields.ToArray();
17 |
18 | [DataStructureItem(ItemType.ColumnCount)]
19 | public short ColumnCount { get; set; }
20 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/RecordField.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Metadata.Structures;
2 |
3 | namespace InternalsViewer.Internals.Engine.Records;
4 |
5 | public abstract class RecordField(ColumnStructure columnStructure) : Field
6 | {
7 | public ColumnStructure ColumnStructure { get; } = columnStructure;
8 |
9 | ///
10 | /// Length of the field (in bytes)
11 | ///
12 | public int Length { get; set; }
13 |
14 | ///
15 | /// Offset of the field in the row
16 | ///
17 | public int Offset { get; set; }
18 |
19 | ///
20 | /// Raw data for the field
21 | ///
22 | public byte[] Data { get; set; } = Array.Empty();
23 |
24 | public string Name => ColumnStructure.ColumnName;
25 |
26 | ///
27 | /// String representation of the field value
28 | ///
29 | public abstract string Value { get; }
30 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/RecordHelpers.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 |
3 | namespace InternalsViewer.Internals.Engine.Records;
4 |
5 | public static class RecordHelpers
6 | {
7 | ///
8 | /// Flips the high bit if set
9 | ///
10 | public static ushort DecodeOffset(ushort value)
11 | {
12 | if ((value | 0x8000) == value)
13 | {
14 | return Convert.ToUInt16(value ^ 0x8000);
15 | }
16 |
17 | return value;
18 | }
19 |
20 | ///
21 | /// Get an offset array from a record
22 | ///
23 | /// An array of 2-byte integers representing a start offset in the page
24 | public static ushort[] GetOffsetArray(byte[] record, int size, int offset)
25 | {
26 | var offsetArray = new ushort[size];
27 |
28 | for (var i = 0; i < size; i++)
29 | {
30 | offsetArray[i] = BitConverter.ToUInt16(record, offset);
31 |
32 | offset += sizeof(ushort);
33 | }
34 |
35 | return offsetArray;
36 | }
37 |
38 | public static string GetArrayString(ushort[] array)
39 | {
40 | var sb = new StringBuilder();
41 |
42 | foreach (var offset in array)
43 | {
44 | if (sb.Length > 0)
45 | {
46 | sb.Append(", ");
47 | }
48 |
49 | sb.AppendFormat("{0} - 0x{0:X}", offset);
50 | }
51 |
52 | return sb.ToString();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Engine/Records/RecordType.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace InternalsViewer.Internals.Engine.Records;
3 |
4 | public enum RecordType
5 | {
6 | Primary = 0,
7 | Forwarded = 1,
8 | ForwardingStub = 2,
9 | Index = 3,
10 | Blob = 4,
11 | GhostIndex = 5,
12 | GhostData = 6,
13 | GhostRecordVersion = 7
14 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Extensions/ByteExtensions.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Helpers;
2 | using System.Collections;
3 | using System.Text;
4 |
5 | namespace InternalsViewer.Internals.Extensions;
6 |
7 | public static class ByteExtensions
8 | {
9 | ///
10 | /// Extension method to return a binary/bitmap string for a given byte
11 | ///
12 | public static string ToBinaryString(this byte input)
13 | {
14 | var bitArray = new BitArray(new[] { input });
15 |
16 | var stringBuilder = new StringBuilder();
17 |
18 | for (var i = 0; i < bitArray.Length; i++)
19 | {
20 | stringBuilder.Append(bitArray[i] ? "1" : "0");
21 | }
22 |
23 | return stringBuilder.ToString();
24 | }
25 |
26 | ///
27 | /// Extension method to return a hex string for a given byte
28 | ///
29 | public static string ToHexString(this byte input)
30 | {
31 | return StringHelpers.ToHexString(input);
32 | }
33 |
34 | ///
35 | /// Extension method to return a hex string for a given array of bytes
36 | ///
37 | public static string ToHexString(this byte[] input)
38 | {
39 | return StringHelpers.ToHexString(input);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Extensions/EnumerableExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace InternalsViewer.Internals.Extensions;
8 |
9 | public static class EnumerableExtensions
10 | {
11 | public static void AddIf(this List collection, T item, bool condition)
12 | {
13 | if (condition)
14 | {
15 | collection.Add(item);
16 | }
17 | }
18 |
19 | public static T? Pop(this List collection, Func predicate)
20 | {
21 | if (collection.Count(predicate) > 0)
22 | {
23 | var result = collection.Where(predicate).First();
24 |
25 | collection.Remove(result);
26 |
27 | return result;
28 | }
29 |
30 | return default;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | // Global using directives
2 |
3 | global using System;
4 | global using System.Collections.Generic;
5 | global using System.Linq;
6 | global using System.Threading.Tasks;
7 | global using Microsoft.Extensions.Logging;
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Helpers/History.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Helpers;
2 |
3 | public class History
4 | {
5 | private Stack BackStack { get; } = new();
6 |
7 | private Stack ForwardStack { get; } = new();
8 |
9 | public void Add(T item)
10 | {
11 | switch (BackStack.Count)
12 | {
13 | case > 0 when BackStack.Peek()!.Equals(item):
14 | return;
15 | case > 0:
16 | ForwardStack.Clear();
17 | break;
18 | }
19 |
20 | BackStack.Push(item);
21 | }
22 |
23 | public T? Back()
24 | {
25 | if (BackStack.Count > 1)
26 | {
27 | ForwardStack.Push(BackStack.Pop());
28 |
29 | return BackStack.Peek();
30 | }
31 |
32 | return default;
33 | }
34 |
35 | public T? Forward()
36 | {
37 | if (ForwardStack.Count > 0)
38 | {
39 | var item = ForwardStack.Pop();
40 |
41 | BackStack.Push(item);
42 |
43 | return item;
44 | }
45 |
46 | return default;
47 | }
48 |
49 | public bool CanGoBack() => BackStack.Count > 1;
50 |
51 | public bool CanGoForward() => ForwardStack.Count > 0;
52 | }
53 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Helpers/IdHelpers.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Helpers;
2 |
3 | public class IdHelpers
4 | {
5 | ///
6 | /// Gets the Allocation Unit Id from the Object Id and Index Id
7 | ///
8 | ///
9 | /// Taken from
10 | ///
11 | public static long GetAllocationUnitId(int objectId, int indexId)
12 | {
13 | return (long)indexId << 48 | (long)objectId << 16;
14 | }
15 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Connections/IConnectionProvider.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Interfaces.Readers;
2 |
3 | namespace InternalsViewer.Internals.Interfaces.Connections;
4 |
5 | public interface IConnectionProvider
6 | {
7 | IPageReader PageReader { get; }
8 |
9 | string Name { get; }
10 | }
11 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Connections/IConnectionType.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Interfaces.Readers;
2 |
3 | namespace InternalsViewer.Internals.Interfaces.Connections;
4 |
5 | public interface IConnectionType
6 | {
7 | IPageReader PageReader { get; }
8 |
9 | string Identifier { get; }
10 |
11 | string Name { get; set; }
12 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Connections/IConnectionTypeFactory.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Connections;
2 |
3 | namespace InternalsViewer.Internals.Interfaces.Connections;
4 |
5 | public interface IConnectionTypeFactory where TConfig : ConnectionTypeConfig
6 | {
7 | static abstract IConnectionType Create(Action configDelegate);
8 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Engine/IAllocationChain.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 | using InternalsViewer.Internals.Engine.Pages;
3 |
4 | namespace InternalsViewer.Internals.Interfaces.Engine;
5 |
6 | public interface IAllocationChain where T: AllocationPage
7 | {
8 | bool IsExtentAllocated(int extent, short fileId, bool isInverted);
9 |
10 | PageAddress[] SinglePageSlots { get; }
11 |
12 | List Pages { get; }
13 | }
14 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/MetadataProviders/IBufferPoolInfoProvider.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 |
3 | namespace InternalsViewer.Internals.Interfaces.MetadataProviders;
4 |
5 | public interface IBufferPoolInfoProvider
6 | {
7 | Task<(List Clean, List Dirty)> GetBufferPoolEntries(string databaseName);
8 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/MetadataProviders/ITransactionLogProvider.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Metadata;
2 |
3 | namespace InternalsViewer.Internals.Interfaces.MetadataProviders;
4 |
5 | public interface ITransactionLogProvider
6 | {
7 | Task Checkpoint();
8 |
9 | Task> GetTransactionLog();
10 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Readers/IPageReader.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 |
3 | namespace InternalsViewer.Internals.Interfaces.Readers;
4 |
5 | public interface IPageReader
6 | {
7 | Task Read(string name, PageAddress pageAddress);
8 | }
9 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Readers/Internals/IRecordReader.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 | using InternalsViewer.Internals.Engine.Database;
3 | using InternalsViewer.Internals.Engine.Records.Data;
4 | using InternalsViewer.Internals.Metadata.Structures;
5 |
6 | namespace InternalsViewer.Internals.Interfaces.Readers.Internals;
7 |
8 | public interface IRecordReader
9 | {
10 | Task> Read(DatabaseSource database, PageAddress startPage, TableStructure structure);
11 | }
12 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Services/Loaders/Chains/IAllocationChainService.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 | using InternalsViewer.Internals.Engine.Allocation;
3 | using InternalsViewer.Internals.Engine.Database;
4 | using InternalsViewer.Internals.Engine.Pages.Enums;
5 |
6 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Chains;
7 |
8 | public interface IAllocationChainService
9 | {
10 | Task LoadChain(DatabaseSource database, short fileId, PageType pageType);
11 |
12 | Task LoadChain(DatabaseSource database, PageAddress startPageAddress);
13 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Services/Loaders/Chains/IIamChainService.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 | using InternalsViewer.Internals.Engine.Allocation;
3 | using InternalsViewer.Internals.Engine.Database;
4 |
5 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Chains;
6 |
7 | public interface IIamChainService
8 | {
9 | Task LoadChain(DatabaseSource database, PageAddress startPageAddress);
10 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Services/Loaders/Chains/IPfsChainService.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Allocation;
2 | using InternalsViewer.Internals.Engine.Database;
3 |
4 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Chains;
5 |
6 | public interface IPfsChainService
7 | {
8 | Task LoadChain(DatabaseSource databaseDetail, short fileId);
9 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Services/Loaders/Engine/IDatabaseService.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Database;
2 | using InternalsViewer.Internals.Interfaces.Connections;
3 |
4 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Engine;
5 |
6 | public interface IDatabaseService
7 | {
8 | Task LoadAsync(string name, IConnectionType connection);
9 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Services/Loaders/Engine/IMetadataLoader.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Database;
2 | using InternalsViewer.Internals.Metadata.Internals;
3 |
4 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Engine;
5 |
6 | public interface IMetadataLoader
7 | {
8 | Task Load(DatabaseSource database);
9 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Services/Loaders/Pages/IPageLoader.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 | using InternalsViewer.Internals.Engine.Database;
3 | using InternalsViewer.Internals.Engine.Pages;
4 |
5 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Pages;
6 |
7 | public interface IPageLoader
8 | {
9 | Task Load(DatabaseSource database, PageAddress pageAddress);
10 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Services/Loaders/Pages/IPageParser.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Pages;
2 | using InternalsViewer.Internals.Engine.Pages.Enums;
3 |
4 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Pages;
5 |
6 | public interface IPageParser
7 | {
8 | PageType[] SupportedPageTypes { get; }
9 |
10 | Page Parse(PageData page);
11 | }
12 |
13 | public interface IPageParser : IPageParser where T : Page, new()
14 | {
15 | new T Parse(PageData page);
16 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Services/Loaders/Pages/IPageService.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 | using InternalsViewer.Internals.Engine.Database;
3 | using InternalsViewer.Internals.Engine.Pages;
4 |
5 | namespace InternalsViewer.Internals.Interfaces.Services.Loaders.Pages;
6 |
7 | public interface IPageService
8 | {
9 | Task GetPage(DatabaseSource database, PageAddress pageAddress);
10 |
11 | Task GetPage(DatabaseSource database, PageAddress pageAddress) where T : Page;
12 | }
13 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Interfaces/Services/Records/IRecordService.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Pages;
2 | using InternalsViewer.Internals.Engine.Records.CdRecordType;
3 | using InternalsViewer.Internals.Engine.Records.Data;
4 | using InternalsViewer.Internals.Engine.Records.Index;
5 |
6 | namespace InternalsViewer.Internals.Interfaces.Services.Records;
7 |
8 | public interface IRecordService
9 | {
10 | List GetDataRecords(DataPage page);
11 |
12 | List GetCompressedDataRecords(DataPage page);
13 |
14 | List GetIndexRecords(IndexPage page);
15 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Metadata/ColumnAttribute.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 |
3 | namespace InternalsViewer.Internals.Metadata;
4 |
5 | [AttributeUsage(AttributeTargets.Property)]
6 | public class ColumnAttribute : Attribute
7 | {
8 | ///
9 | /// Name of the column.
10 | ///
11 | public string ColumnName { get; set; } = string.Empty;
12 |
13 | ///
14 | /// Column id.
15 | ///
16 | public int ColumnId { get; set; }
17 |
18 | ///
19 | /// Data type
20 | ///
21 | public SqlDbType DataType { get; set; }
22 |
23 | ///
24 | /// Data length
25 | ///
26 | public short DataLength { get; set; } = 0;
27 |
28 | ///
29 | /// Leaf offset for fixed length fields
30 | ///
31 | public short LeafOffset { get; set; }
32 |
33 | ///
34 | /// 1-based index for the column in the null bitmap.
35 | ///
36 | public short NullBitIndex { get; set; }
37 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Metadata/HobtEntryPoint.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 |
3 | namespace InternalsViewer.Internals.Metadata;
4 |
5 | ///
6 | /// The entry point pages for a HOBT
7 | ///
8 | /// See https://learn.microsoft.com/en-us/sql/relational-databases/pages-and-extents-architecture-guide
9 | ///
10 | public struct HobtEntryPoint
11 | {
12 | ///
13 | /// Initializes a new instance of the struct.
14 | ///
15 | public HobtEntryPoint(PageAddress firstIam, PageAddress rootPage, PageAddress firstPage, int partitionNumber)
16 | {
17 | FirstIam = firstIam;
18 | RootPage = rootPage;
19 | FirstPage = firstPage;
20 | PartitionNumber = partitionNumber;
21 | }
22 |
23 | ///
24 | /// First IAM page address.
25 | ///
26 | ///
27 | /// The first page in the HOBTs allocation IAM chain
28 | ///
29 | public PageAddress FirstIam { get; set; }
30 |
31 | ///
32 | /// Index root page address.
33 | ///
34 | ///
35 | /// The root page of the b-tree (index)
36 | ///
37 | public PageAddress RootPage { get; set; }
38 |
39 | ///
40 | /// First page address
41 | ///
42 | ///
43 | /// The first page at the leaf/heap level
44 | ///
45 | public PageAddress FirstPage { get; set; }
46 |
47 | ///
48 | /// Partition number.
49 | ///
50 | public int PartitionNumber { get; set; }
51 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Metadata/Internals/Tables/InternalIndexColumn.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 | using InternalsViewer.Internals.Generators;
3 |
4 | namespace InternalsViewer.Internals.Metadata.Internals.Tables;
5 |
6 | ///
7 | /// sys.sysiscols
8 | ///
9 | [InternalsMetadata]
10 | public record InternalIndexColumn
11 | {
12 | [InternalsMetadataColumn("idmajor", 1, SqlDbType.Int, 4, 4, 1)]
13 | public int ObjectId { get; set; }
14 |
15 | [InternalsMetadataColumn("idminor", 2, SqlDbType.Int, 4, 8, 2)]
16 | public int IndexId { get; set; }
17 |
18 | [InternalsMetadataColumn("subid", 3, SqlDbType.Int, 4, 12, 3)]
19 | public int IndexColumnId { get; set; }
20 |
21 | [InternalsMetadataColumn("status", 4, SqlDbType.Int, 4, 16, 4)]
22 | public int Status { get; set; }
23 |
24 | [InternalsMetadataColumn("intprop", 5, SqlDbType.Int, 4, 20, 5)]
25 | public int ColumnId { get; set; }
26 |
27 | [InternalsMetadataColumn("tinyprop1", 6, SqlDbType.TinyInt, 1, 24, 6)]
28 | public byte KeyOrdinal { get; set; }
29 |
30 | [InternalsMetadataColumn("tinyprop2", 7, SqlDbType.TinyInt, 1, 25, 7)]
31 | public byte PartitionOrdinal { get; set; }
32 |
33 | [InternalsMetadataColumn("tinyprop3", 8, SqlDbType.TinyInt, 1, 26, 8)]
34 | public byte TinyProp3 { get; set; }
35 |
36 | [InternalsMetadataColumn("tinyprop4", 9, SqlDbType.TinyInt, 1, 27, 9)]
37 | public byte ColumnStoreOrderOrdinal { get; set; }
38 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Metadata/Internals/TypeInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 |
3 | namespace InternalsViewer.Internals.Metadata.Internals;
4 |
5 | ///
6 | /// Parses the ti (Type Info) field of the sys.sysallocunits table
7 | ///
8 | public record TypeInfo
9 | {
10 | public byte Scale { get; set; }
11 |
12 | public byte Precision { get; set; }
13 |
14 | public short MaxLength { get; set; }
15 |
16 | public short MaxInRowLength { get; set; }
17 |
18 | public SqlDbType DataType { get; set; }
19 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Metadata/Structures/IndexColumnStructure.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Metadata.Structures;
2 |
3 | public record IndexColumnStructure : ColumnStructure
4 | {
5 | public bool IsIncludeColumn { get; set; }
6 |
7 | public bool IsIndexKey { get; set; }
8 |
9 | public int IndexColumnId { get; set; }
10 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Metadata/Structures/IndexStructure.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Metadata.Structures;
2 |
3 | public record IndexStructure(long AllocationUnitId)
4 | : Structure(AllocationUnitId)
5 | {
6 | public bool IsUnique { get; set; }
7 |
8 | public bool HasFilter { get; set; }
9 |
10 | public TableStructure? TableStructure { get; set; }
11 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Metadata/Structures/Structure.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Database.Enums;
2 | using InternalsViewer.Internals.Engine.Records.CdRecordType;
3 |
4 | namespace InternalsViewer.Internals.Metadata.Structures;
5 |
6 | public abstract record Structure(long AllocationUnitId) where T : ColumnStructure
7 | {
8 | public int ObjectId { get; set; }
9 |
10 | public int IndexId { get; set; }
11 |
12 | public long AllocationUnitId { get; set; } = AllocationUnitId;
13 |
14 | public long PartitionId { get; set; }
15 |
16 | public List Columns { get; set; } = new();
17 |
18 | public bool HasSparseColumns => Columns.Any(c => c.IsSparse);
19 |
20 | public IndexType IndexType { get; set; }
21 |
22 | public CompressionType CompressionType { get; set; }
23 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Metadata/Structures/StructureType.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Metadata.Structures;
2 |
3 | ///
4 | /// HOBT structure type
5 | ///
6 | public enum StructureType
7 | {
8 | ///
9 | /// Heap - table without a clustered index
10 | ///
11 | Heap,
12 | ///
13 | /// B-Tree (Index)
14 | ///
15 | BTree
16 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Metadata/Structures/TableStructure.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Metadata.Structures;
2 |
3 | public record TableStructure(long AllocationUnitId)
4 | : Structure(AllocationUnitId)
5 | {
6 | public int ColumnCount => Columns.Count;
7 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Metadata/TransactionLogEntry.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Metadata;
2 |
3 | public class TransactionLogEntry
4 | {
5 | }
6 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Providers/Metadata/FileProvider.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Database;
2 | using InternalsViewer.Internals.Metadata.Internals;
3 | using InternalsViewer.Internals.Metadata.Internals.Tables;
4 |
5 | namespace InternalsViewer.Internals.Providers.Metadata;
6 |
7 | ///
8 | /// Provider responsible for providing file information from the metadata collection
9 | ///
10 | public static class FileProvider
11 | {
12 | public static List GetFiles(InternalMetadata metadata)
13 | {
14 | return metadata.Files.Select(GetFile).Where(f => f.FileType == FileType.Rows).ToList();
15 | }
16 |
17 | private static DatabaseFile GetFile(InternalFile source)
18 | {
19 | var fileId = (short)(source.FileId & 0x7fff);
20 |
21 | var file = new DatabaseFile(fileId)
22 | {
23 | FileGroupId = (short)source.FileGroupId,
24 | Size = source.Size,
25 | FileType = (FileType)source.FileType,
26 | Name = source.LogicalName,
27 | PhysicalName = source.PhysicalName,
28 | };
29 |
30 | return file;
31 | }
32 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Readers/Pages/DataFilePageReader.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using InternalsViewer.Internals.Engine.Address;
3 | using InternalsViewer.Internals.Engine.Pages;
4 | using InternalsViewer.Internals.Interfaces.Readers;
5 |
6 | namespace InternalsViewer.Internals.Readers.Pages;
7 |
8 | ///
9 | /// Page Reader for SQL Server data files
10 | ///
11 | ///
12 | public class DataFilePageReader(string path) : PageReader, IPageReader
13 | {
14 | private string FilePath { get; } = path;
15 |
16 | ///
17 | /// Reads a page from a SQL Server data file
18 | ///
19 | ///
20 | /// SQL Server data files (MDF/LDF) are stored in 8 KB (8192 bytes) pages, so a page is located in the file at location
21 | /// (Page Id * 8192)
22 | ///
23 | /// The file has to be detached/not attached to SQL Server to be read as it will be locked by the SQL Server process.
24 | ///
25 | public async Task Read(string name, PageAddress pageAddress)
26 | {
27 | var offset = pageAddress.PageId * PageData.Size;
28 |
29 | var data = new byte[PageData.Size];
30 |
31 | await using var file = File.OpenRead(FilePath);
32 |
33 | file.Seek(offset, SeekOrigin.Begin);
34 |
35 | _ = await file.ReadAsync(data, 0, PageData.Size);
36 |
37 | return data;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Readers/Pages/PageReader.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using InternalsViewer.Internals.Engine.Pages;
3 |
4 | namespace InternalsViewer.Internals.Readers.Pages;
5 |
6 | ///
7 | /// Abstract class for reading pages from hex dump
8 | ///
9 | public abstract class PageReader
10 | {
11 | protected static int ReadData(string currentRow, int offset, byte[] data)
12 | {
13 | var currentData = currentRow.Substring(20, 44).Replace(" ", string.Empty);
14 |
15 | for (var i = 0; i < currentData.Length; i += 2)
16 | {
17 | var byteString = currentData.Substring(i, 2);
18 |
19 | if (!byteString.Contains("†") && !byteString.Contains(".") && offset < PageData.Size)
20 | {
21 | if (byte.TryParse(byteString,
22 | NumberStyles.HexNumber,
23 | CultureInfo.InvariantCulture,
24 | out data[offset]))
25 | {
26 | offset++;
27 | }
28 | }
29 | }
30 |
31 | return offset;
32 | }
33 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Services/BufferPoolService.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Interfaces.MetadataProviders;
2 | using InternalsViewer.Internals.Engine.Database;
3 |
4 | namespace InternalsViewer.Internals.Services;
5 |
6 | ///
7 | /// Service responsible for getting the Buffer Pool
8 | ///
9 | ///
10 | /// From Books Online:
11 | ///
12 | /// Buffer pool
13 | ///
14 | /// Also called buffer cache. The buffer pool is a global resource shared by all databases for their cached data pages.
15 | ///
16 | public class BufferPoolService(IBufferPoolInfoProvider provider)
17 | {
18 | public IBufferPoolInfoProvider Provider { get; } = provider;
19 |
20 | public async Task GetBufferPool(string databaseName)
21 | {
22 | var (clean, dirty) = await Provider.GetBufferPoolEntries(databaseName);
23 |
24 | var bufferPool = new BufferPool(clean, dirty);
25 |
26 | return bufferPool;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Services/Loaders/Engine/InternalTableConstants.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.Internals.Services.Loaders.Engine;
2 |
3 | public class InternalTableConstants
4 | {
5 | public static readonly (int ObjectId, int IndexId) ColumnLayoutsId = (3, 0);
6 |
7 | public static readonly (int ObjectId, int IndexId) ColumnsId = (41, 1);
8 |
9 | public static readonly (int ObjectId, int IndexId) RowSetId = (5, 0);
10 |
11 | public static readonly (int ObjectId, int IndexId) AllocationUnitsId = (7, 0);
12 |
13 | public static readonly (int ObjectId, int IndexId) FilesId = (24, 1);
14 |
15 | public static readonly (int ObjectId, int IndexId) ObjectsId = (34, 1);
16 |
17 | public static readonly (int ObjectId, int IndexId) EntitiesId = (64, 1);
18 |
19 | public static readonly (int ObjectId, int IndexId) IndexesId = (54, 1);
20 |
21 | public static readonly (int ObjectId, int IndexId) IndexColumnsId = (55, 1);
22 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Services/Loaders/Records/Fields/LobPointerFieldLoader.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 | using InternalsViewer.Internals.Engine.Records.Blob.BlobPointers;
3 |
4 | namespace InternalsViewer.Internals.Services.Loaders.Records.Fields;
5 |
6 | public class LobPointerFieldLoader
7 | {
8 | public const int RowIdOffset = 8;
9 |
10 | public static PointerField Load(byte[] data, int offset)
11 | {
12 | var field = new PointerField();
13 |
14 | field.MarkProperty("PointerType", offset, sizeof(byte));
15 |
16 | field.PointerType = (BlobFieldType)data[0];
17 |
18 | field.MarkProperty("Timestamp", offset + sizeof(byte), sizeof(int));
19 |
20 | field.Timestamp = BitConverter.ToUInt32(data, 0);
21 |
22 | LoadLinks(field, data, offset);
23 |
24 | return field;
25 | }
26 |
27 | private static void LoadLinks(PointerField field, byte[] data, int offset)
28 | {
29 | field.Links = new List();
30 |
31 | var rowIdData = new byte[8];
32 | Array.Copy(data, RowIdOffset, rowIdData, 0, 8);
33 |
34 | field.MarkProperty("LinksArray", string.Empty, 0);
35 |
36 | var rowId = new RowIdentifier(rowIdData);
37 |
38 | var link = new BlobChildLink(rowId, 0, 0);
39 |
40 | link.MarkProperty("RowIdentifier", offset + RowIdOffset, 8);
41 |
42 | field.Links.Add(link);
43 | }
44 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Services/Pages/Parsers/AllocationPageParser.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using InternalsViewer.Internals.Engine.Pages;
3 | using InternalsViewer.Internals.Engine.Pages.Enums;
4 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages;
5 |
6 | namespace InternalsViewer.Internals.Services.Pages.Parsers;
7 |
8 | ///
9 | /// Parser for Allocation pages
10 | ///
11 | public class AllocationPageParser : PageParser, IPageParser
12 | {
13 | public PageType[] SupportedPageTypes => new[] { PageType.Gam, PageType.Sgam, PageType.Bcm, PageType.Dcm };
14 |
15 | Page IPageParser.Parse(PageData page)
16 | {
17 | return Parse(page);
18 | }
19 |
20 | public AllocationPage Parse(PageData page)
21 | {
22 | var allocationPage = CopyToPageType(page);
23 |
24 | return Parse(allocationPage);
25 | }
26 |
27 | private static AllocationPage Parse(AllocationPage page)
28 | {
29 | var allocationData = new byte[AllocationPage.AllocationInterval / 8];
30 |
31 | Array.Copy(page.Data,
32 | AllocationPage.AllocationArrayOffset,
33 | allocationData,
34 | 0,
35 | allocationData.Length);
36 |
37 | var bitArray = new BitArray(allocationData);
38 |
39 | bitArray.CopyTo(page.AllocationMap, 0);
40 |
41 | return page;
42 | }
43 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Services/Pages/Parsers/DataPageParser.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Database;
2 | using InternalsViewer.Internals.Engine.Pages;
3 | using InternalsViewer.Internals.Engine.Pages.Enums;
4 | using InternalsViewer.Internals.Engine.Records.CdRecordType;
5 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages;
6 | using InternalsViewer.Internals.Services.Loaders.Compression;
7 |
8 | namespace InternalsViewer.Internals.Services.Pages.Parsers;
9 |
10 | ///
11 | /// Parser for Data pages
12 | ///
13 | public class DataPageParser(CompressionInfoLoader compressionInfoLoader) : PageParser, IPageParser
14 | {
15 | private CompressionInfoLoader CompressionInfoLoader { get; } = compressionInfoLoader;
16 |
17 | public PageType[] SupportedPageTypes => new[] { PageType.Data };
18 |
19 | Page IPageParser.Parse(PageData page)
20 | {
21 | return Parse(page);
22 | }
23 |
24 | public DataPage Parse(PageData page)
25 | {
26 | var dataPage = CopyToPageType(page);
27 |
28 | dataPage.AllocationUnit = dataPage.Database
29 | .AllocationUnits
30 | .FirstOrDefault(a => a.AllocationUnitId == dataPage.PageHeader.AllocationUnitId)
31 | ?? AllocationUnit.Unknown;
32 |
33 | if(dataPage.AllocationUnit.CompressionType == CompressionType.Page && dataPage.IsPageCompressed)
34 | {
35 | dataPage.CompressionInfo = CompressionInfoLoader.Load(dataPage, CompressionInfo.SlotOffset);
36 | }
37 |
38 | return dataPage;
39 | }
40 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Services/Pages/Parsers/EmptyPageParser.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Pages;
2 | using InternalsViewer.Internals.Engine.Pages.Enums;
3 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages;
4 |
5 | namespace InternalsViewer.Internals.Services.Pages.Parsers;
6 |
7 | public class EmptyPageParser : PageParser, IPageParser
8 | {
9 | public PageType[] SupportedPageTypes => new[] { PageType.None };
10 |
11 | Page IPageParser.Parse(PageData page)
12 | {
13 | return Parse(page);
14 | }
15 |
16 | public EmptyPage Parse(PageData page)
17 | {
18 | var emptyPage = CopyToPageType(page);
19 |
20 | return emptyPage;
21 | }
22 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Services/Pages/Parsers/FileHeaderPageParser.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Pages;
2 | using InternalsViewer.Internals.Engine.Pages.Enums;
3 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages;
4 |
5 | namespace InternalsViewer.Internals.Services.Pages.Parsers;
6 |
7 | public class FileHeaderPageParser: PageParser, IPageParser
8 | {
9 | public PageType[] SupportedPageTypes => new[] { PageType.FileHeader };
10 |
11 | public FileHeaderPage Parse(PageData page)
12 | {
13 | var fileHeaderPage = CopyToPageType(page);
14 |
15 | return fileHeaderPage;
16 | }
17 |
18 | Page IPageParser.Parse(PageData page)
19 | {
20 | return Parse(page);
21 | }
22 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Services/Pages/Parsers/IndexPageParser.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Database;
2 | using InternalsViewer.Internals.Engine.Pages;
3 | using InternalsViewer.Internals.Engine.Pages.Enums;
4 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages;
5 |
6 | namespace InternalsViewer.Internals.Services.Pages.Parsers;
7 |
8 | ///
9 | /// Parser for Index pages
10 | ///
11 | public class IndexPageParser : PageParser, IPageParser
12 | {
13 | public PageType[] SupportedPageTypes => new[] { PageType.Index };
14 |
15 | Page IPageParser.Parse(PageData page)
16 | {
17 | return Parse(page);
18 | }
19 |
20 | public IndexPage Parse(PageData page)
21 | {
22 | var indexPage = CopyToPageType(page);
23 |
24 | indexPage.AllocationUnit = indexPage.Database
25 | .AllocationUnits
26 | .FirstOrDefault(a => a.AllocationUnitId == indexPage.PageHeader.AllocationUnitId)
27 | ?? AllocationUnit.Unknown;
28 |
29 | return indexPage;
30 | }
31 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Services/Pages/Parsers/LobPageParser.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Pages;
2 | using InternalsViewer.Internals.Engine.Pages.Enums;
3 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages;
4 |
5 | namespace InternalsViewer.Internals.Services.Pages.Parsers;
6 |
7 | ///
8 | /// Parser for LOB (Large Object Data) pages
9 | ///
10 | public class LobPageParser : PageParser, IPageParser
11 | {
12 | public PageType[] SupportedPageTypes => new[] { PageType.Lob3, PageType.Lob4 };
13 |
14 | Page IPageParser.Parse(PageData page)
15 | {
16 | return Parse(page);
17 | }
18 |
19 | public LobPage Parse(PageData page)
20 | {
21 | var lobPage = CopyToPageType(page);
22 |
23 | return lobPage;
24 | }
25 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Services/Pages/Parsers/PageParser.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Pages;
2 |
3 | namespace InternalsViewer.Internals.Services.Pages.Parsers;
4 |
5 | public abstract class PageParser
6 | {
7 | protected T CopyToPageType(PageData pageData) where T : Page, new()
8 | {
9 | var page = new T
10 | {
11 | PageHeader = pageData.PageHeader,
12 | Database = pageData.Database,
13 | Data = pageData.Data,
14 | PageAddress = pageData.PageAddress,
15 | OffsetTable = pageData.OffsetTable,
16 | };
17 |
18 | return page;
19 | }
20 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/Services/Pages/Parsers/PfsPageParser.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Allocation.Enums;
2 | using InternalsViewer.Internals.Engine.Pages;
3 | using InternalsViewer.Internals.Engine.Pages.Enums;
4 | using InternalsViewer.Internals.Engine.Parsers;
5 | using InternalsViewer.Internals.Interfaces.Services.Loaders.Pages;
6 |
7 | namespace InternalsViewer.Internals.Services.Pages.Parsers;
8 |
9 | ///
10 | /// Parser for PFS (Page Free Space) pages
11 | ///
12 | public class PfsPageParser: PageParser, IPageParser
13 | {
14 | private const int PfsOffset = 100; // PFS byte array starts at offset 100
15 | private const int PfsSize = 8088; // PFS byte array is 8088 bytes/pages
16 |
17 | public PageType[] SupportedPageTypes => new[] { PageType.Pfs };
18 |
19 | Page IPageParser.Parse(PageData page)
20 | {
21 | return Parse(page);
22 | }
23 |
24 | public PfsPage Parse(PageData pageData)
25 | {
26 | var page = CopyToPageType(pageData);
27 |
28 | var pfsBytes = GetPfsBytes(page);
29 |
30 | page.PfsBytes = pfsBytes;
31 |
32 | return page;
33 | }
34 |
35 | ///
36 | /// Loads the PFS bytes collection from the page data
37 | ///
38 | private static List GetPfsBytes(PageData page)
39 | {
40 | var pfsData = new byte[PfsSize];
41 |
42 | Array.Copy(page.Data, PfsOffset, pfsData, 0, PfsSize);
43 |
44 | return pfsData.Select(PfsByteParser.Parse).ToList();
45 | }
46 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.Internals/TransactionLog/LogData.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 | using InternalsViewer.Internals.Engine.Pages;
3 | using InternalsViewer.Internals.Helpers;
4 |
5 | namespace InternalsViewer.Internals.TransactionLog;
6 |
7 | ///
8 | /// Log Data containing fragment of a page at a particular offset
9 | ///
10 | public class LogData
11 | {
12 | public ushort Offset { get; set; }
13 |
14 | public ushort Slot { get; set; }
15 |
16 | public byte[] Data { get; set; } = Array.Empty();
17 |
18 | public LogSequenceNumber LogSequenceNumber { get; set; }
19 |
20 | public override string ToString()
21 | {
22 | return string.Format("LSN: {0} Slot: {1} Offset: {2} Data: {3}",
23 | LogSequenceNumber,
24 | Slot,
25 | Offset,
26 | StringHelpers.ToHexString(Data));
27 | }
28 |
29 | ///
30 | /// Merges the data into a page
31 | ///
32 | /// The target page.
33 | public Page MergeData(Page page)
34 | {
35 | var dataOffset = page.OffsetTable[Slot] + Offset;
36 |
37 | var pageData = new byte[PageData.Size];
38 |
39 | Array.Copy(page.Data, pageData, PageData.Size);
40 |
41 | Array.Copy(Data, 0, pageData, dataOffset, Data.Length);
42 |
43 | page.Data = pageData;
44 |
45 | return page;
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Activation/ActivationHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace InternalsViewer.UI.App.Activation;
4 |
5 | // Extend this class to implement new ActivationHandlers. See DefaultActivationHandler for an example.
6 | // https://github.com/microsoft/TemplateStudio/blob/main/docs/WinUI/activation.md
7 | public abstract class ActivationHandler : IActivationHandler
8 | where T : class
9 | {
10 | // Override this method to add the logic for whether to handle the activation.
11 | protected virtual bool CanHandleInternal(T args) => true;
12 |
13 | // Override this method to add the logic for your activation handler.
14 | protected abstract Task HandleInternalAsync(T args);
15 |
16 | public bool CanHandle(object args) => args is T && CanHandleInternal((args as T)!);
17 |
18 | public async Task HandleAsync(object args) => await HandleInternalAsync((args as T)!);
19 | }
20 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Activation/DefaultActivationHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace InternalsViewer.UI.App.Activation;
4 |
5 | public class DefaultActivationHandler : ActivationHandler
6 | {
7 | public DefaultActivationHandler()
8 | {
9 | }
10 |
11 | protected override bool CanHandleInternal(LaunchActivatedEventArgs args)
12 | {
13 | return false;
14 | }
15 |
16 | protected override async Task HandleInternalAsync(LaunchActivatedEventArgs args)
17 | {
18 | await Task.CompletedTask;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Activation/IActivationHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace InternalsViewer.UI.App.Activation;
4 |
5 | public interface IActivationHandler
6 | {
7 | bool CanHandle(object args);
8 |
9 | Task HandleAsync(object args);
10 | }
11 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/AppIcon16.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/AppIcon16.bmp
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/AppIcon16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/AppIcon16.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/AppLogo-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/AppLogo-512.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/AppLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/AppLogo.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/AppLogo.svg:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/Index32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/Index32.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/InternalsViewer.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/InternalsViewer.ico
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/TabIcons/Database.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/TabIcons/Database.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/TabIcons/Database16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/TabIcons/Database16.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/TabIcons/Database32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/TabIcons/Database32.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/TabIcons/Page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/TabIcons/Page.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/TabIcons/Page16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/TabIcons/Page16.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/TabIcons/Page32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/TabIcons/Page32.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Assets/Untitled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Assets/Untitled.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Controls/Connections/BackupFileConnectionControl.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using InternalsViewer.UI.App.ViewModels.Connections;
3 |
4 | namespace InternalsViewer.UI.App.Controls.Connections;
5 |
6 | public sealed partial class BackupFileConnectionControl
7 | {
8 | public BackupFileConnectionViewModel ViewModel => (BackupFileConnectionViewModel)DataContext;
9 |
10 | public BackupFileConnectionControl()
11 | {
12 | InitializeComponent();
13 |
14 | DataContext = new BackupFileConnectionViewModel();
15 | }
16 |
17 | private void OpenButton_Click(object sender, RoutedEventArgs e)
18 | {
19 | throw new NotImplementedException();
20 | }
21 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Controls/CopyButton.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml.Controls;
2 | using Microsoft.UI.Xaml.Media.Animation;
3 |
4 | namespace InternalsViewer.UI.App.Controls;
5 |
6 | public sealed class CopyButton : Button
7 | {
8 | public CopyButton()
9 | {
10 | DefaultStyleKey = typeof(CopyButton);
11 | }
12 |
13 | private void CopyButton_Click(object sender, RoutedEventArgs e)
14 | {
15 | if (GetTemplateChild("CopyToClipboardSuccessAnimation") is Storyboard storyBoard)
16 | {
17 | storyBoard.Begin();
18 | }
19 | }
20 |
21 | protected override void OnApplyTemplate()
22 | {
23 | Click -= CopyButton_Click;
24 |
25 | base.OnApplyTemplate();
26 |
27 | Click += CopyButton_Click;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Controls/CopyTextBlock.xaml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Controls/CopyTextBlock.xaml.cs:
--------------------------------------------------------------------------------
1 | using Windows.ApplicationModel.DataTransfer;
2 |
3 | namespace InternalsViewer.UI.App.Controls;
4 |
5 | public sealed partial class CopyTextBlock
6 | {
7 | public string Value
8 | {
9 | get { return (string)GetValue(ValueProperty); }
10 | set { SetValue(ValueProperty, value); }
11 | }
12 |
13 | private static readonly DependencyProperty ValueProperty =
14 | DependencyProperty.Register(nameof(Value), typeof(string), typeof(CopyTextBlock), new PropertyMetadata(string.Empty));
15 |
16 | public CopyTextBlock()
17 | {
18 | InitializeComponent();
19 | }
20 |
21 | private void CopyButton_Click(object sender, RoutedEventArgs e)
22 | {
23 | var value = (sender as CopyButton)?.Tag.ToString() ?? string.Empty;
24 |
25 | var package = new DataPackage();
26 |
27 | package.SetText(value);
28 |
29 | Clipboard.SetContent(package);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Controls/CursorUserControl.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Input;
2 | using Microsoft.UI.Xaml.Controls;
3 |
4 | namespace InternalsViewer.UI.App.Controls;
5 |
6 | ///
7 | /// User control with a public method for Change Cursor
8 | ///
9 | public class CursorUserControl: UserControl
10 | {
11 | protected void ChangeCursor(InputCursor cursor)
12 | {
13 | ProtectedCursor = cursor;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Controls/ExceptionDialog.xaml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 | Error details:
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Controls/ExceptionDialog.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml.Controls;
2 | using System.Text;
3 | using Windows.ApplicationModel.DataTransfer;
4 |
5 | namespace InternalsViewer.UI.App.Controls;
6 |
7 | public sealed partial class ExceptionDialog
8 | {
9 | public string Message
10 | {
11 | get { return (string)GetValue(MessageProperty); }
12 | set { SetValue(MessageProperty, value); }
13 | }
14 |
15 | public static readonly DependencyProperty MessageProperty =
16 | DependencyProperty.Register(nameof(Message), typeof(string), typeof(ExceptionDialog), new PropertyMetadata(null));
17 |
18 | public string StackTrace
19 | {
20 | get { return (string)GetValue(StackTraceProperty); }
21 | set { SetValue(StackTraceProperty, value); }
22 | }
23 |
24 | public static readonly DependencyProperty StackTraceProperty =
25 | DependencyProperty.Register(nameof(Title), typeof(string), typeof(ExceptionDialog), new PropertyMetadata(null));
26 |
27 | public ExceptionDialog()
28 | {
29 | InitializeComponent();
30 | }
31 |
32 | private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
33 | {
34 | var text = new StringBuilder();
35 |
36 | text.AppendLine(Message);
37 | text.AppendLine();
38 | text.AppendLine(StackTrace);
39 |
40 | var package = new DataPackage();
41 |
42 | package.SetText(text.ToString());
43 |
44 | Clipboard.SetContent(package);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Controls/Page/LabelTextBox.xaml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Controls/Page/LabelTextBox.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.UI.App.Controls.Page;
2 | public sealed partial class LabelTextBox
3 | {
4 | public LabelTextBox()
5 | {
6 | InitializeComponent();
7 |
8 | DataContext = this;
9 | }
10 |
11 | public string Label
12 | {
13 | get { return (string)GetValue(LabelProperty); }
14 | set { SetValue(LabelProperty, value); }
15 | }
16 |
17 | public static readonly DependencyProperty LabelProperty = DependencyProperty
18 | .Register(nameof(Label),
19 | typeof(string),
20 | typeof(LabelTextBox),
21 | PropertyMetadata.Create(() => "Label"));
22 |
23 | public string Text
24 | {
25 | get { return (string)GetValue(TextProperty); }
26 | set { SetValue(TextProperty, value); }
27 | }
28 |
29 | public static readonly DependencyProperty TextProperty = DependencyProperty
30 | .Register(nameof(Text),
31 | typeof(string),
32 | typeof(LabelTextBox),
33 | PropertyMetadata.Create(() => "TextBox"));
34 |
35 | public double LabelWidth
36 | {
37 | get { return (double)GetValue(LabelWidthProperty); }
38 | set { SetValue(LabelWidthProperty, value); }
39 | }
40 |
41 | public static readonly DependencyProperty LabelWidthProperty = DependencyProperty
42 | .Register(nameof(LabelWidth),
43 | typeof(double),
44 | typeof(LabelTextBox),
45 | PropertyMetadata.Create(() => 100));
46 | }
47 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Controls/PasswordDialog.xaml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Controls/PasswordDialog.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.UI.App.Controls;
2 |
3 | public sealed partial class PasswordDialog
4 | {
5 | public string Password
6 | {
7 | get { return (string)GetValue(PasswordProperty); }
8 | set { SetValue(PasswordProperty, value); }
9 | }
10 |
11 | public static readonly DependencyProperty PasswordProperty =
12 | DependencyProperty.Register(nameof(Password), typeof(string), typeof(PasswordDialog), new PropertyMetadata(null));
13 |
14 | public PasswordDialog()
15 | {
16 | InitializeComponent();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | // Global using directives
2 |
3 | global using Microsoft.UI.Xaml;
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Helpers/CollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 |
5 | namespace InternalsViewer.UI.App.Helpers;
6 |
7 | public static class CollectionExtensions
8 | {
9 | public static ObservableCollection ToObservableCollection(this IEnumerable source)
10 | {
11 | if (source == null)
12 | {
13 | throw new ArgumentNullException(nameof(source));
14 | }
15 |
16 | return new ObservableCollection(source);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Helpers/ConnectionHelper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Data.SqlClient;
2 |
3 | namespace InternalsViewer.UI.App.Helpers;
4 |
5 | internal static class ConnectionHelper
6 | {
7 | public static string SetPassword(string connectionString, string result)
8 | {
9 | var connectionStringBuilder = new SqlConnectionStringBuilder(connectionString);
10 |
11 | connectionStringBuilder.Password = result;
12 |
13 | return connectionStringBuilder.ToString();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Helpers/Converters/ColorToSolidColorBrushValueConverter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml.Data;
2 | using System;
3 | using System.Drawing;
4 | using Microsoft.UI.Xaml.Media;
5 |
6 | namespace InternalsViewer.UI.App.Helpers.Converters;
7 |
8 | public class ColorToSolidColorBrushValueConverter : IValueConverter
9 | {
10 | ///
11 | /// Converts a Color (System.Drawing or Windows.UI) to a SolidColorBrush
12 | ///
13 | public object? Convert(object? value, Type targetType, object parameter, string language)
14 | {
15 | if (value is null)
16 | {
17 | return null;
18 | }
19 |
20 | if (value is Color drawingColour)
21 | {
22 | return new SolidColorBrush(Windows.UI.Color.FromArgb(drawingColour.A,
23 | drawingColour.R,
24 | drawingColour.G,
25 | drawingColour.B));
26 | }
27 |
28 | if (value is Windows.UI.Color windowsColour)
29 | {
30 | return new SolidColorBrush(windowsColour);
31 | }
32 |
33 | throw new InvalidOperationException(@"Type cannot be converted - {value.GetType()}");
34 | }
35 |
36 | public object ConvertBack(object value, Type targetType, object parameter, string language)
37 | {
38 | throw new NotImplementedException();
39 | }
40 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Helpers/Converters/PageAddressToStringConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using InternalsViewer.Internals.Engine.Parsers;
3 | using Microsoft.UI.Xaml.Data;
4 |
5 | namespace InternalsViewer.UI.App.Helpers.Converters;
6 |
7 | public class PageAddressToStringConverter: IValueConverter
8 | {
9 | public object? Convert(object? value, Type targetType, object parameter, string language)
10 | {
11 |
12 | return value?.ToString() ?? string.Empty;
13 | }
14 |
15 | public object? ConvertBack(object? value, Type targetType, object parameter, string language)
16 | {
17 | if (value?.ToString() is null)
18 | {
19 | return null;
20 | }
21 |
22 | var stringValue = value.ToString();
23 |
24 | if (PageAddressParser.TryParse(stringValue!, out var pageAddress))
25 | {
26 | return pageAddress;
27 | }
28 |
29 | return null;
30 | }
31 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Helpers/Converters/RecordValueConverter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml.Data;
2 | using System;
3 | using System.Linq;
4 | using InternalsViewer.UI.App.Models.Index;
5 |
6 | namespace InternalsViewer.UI.App.Helpers.Converters;
7 |
8 | internal class RecordValueConverter: IValueConverter
9 | {
10 | public object Convert(object value, Type targetType, object parameter, string language)
11 | {
12 | if (value is not IndexRecordModel record)
13 | {
14 | return string.Empty;
15 | }
16 |
17 | var field = record.Fields.FirstOrDefault(f => f.Name == parameter.ToString());
18 |
19 | return field?.Value ?? $"{ parameter} not found";
20 | }
21 |
22 | public object ConvertBack(object value, Type targetType, object parameter, string language)
23 | {
24 | throw new NotImplementedException();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Helpers/FileHelpers.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using System.Text.Json;
4 | using System.Threading.Tasks;
5 |
6 | namespace InternalsViewer.UI.App.Helpers;
7 |
8 | public static class FileHelpers
9 | {
10 | public static async Task ReadFile(string folderPath, string fileName)
11 | {
12 | var path = Path.Combine(folderPath, fileName);
13 |
14 | if (File.Exists(path))
15 | {
16 | var json = await File.ReadAllTextAsync(path);
17 |
18 | return JsonSerializer.Deserialize(json);
19 | }
20 |
21 | return default;
22 | }
23 |
24 | public static async Task SaveFile(string folderPath, string fileName, T content)
25 | {
26 | if (!Directory.Exists(folderPath))
27 | {
28 | Directory.CreateDirectory(folderPath);
29 | }
30 |
31 | var fileContent = JsonSerializer.Serialize(content);
32 |
33 | await File.WriteAllTextAsync(Path.Combine(folderPath, fileName), fileContent, Encoding.UTF8);
34 | }
35 |
36 | public static void DeleteFile(string folderPath, string? filename)
37 | {
38 | if (filename != null && File.Exists(Path.Combine(folderPath, filename)))
39 | {
40 | File.Delete(Path.Combine(folderPath, filename));
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Helpers/LayoutHelpers.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml.Media;
2 |
3 | namespace InternalsViewer.UI.App.Helpers;
4 |
5 | public static class LayoutHelpers
6 | {
7 | public static T? FindParent(DependencyObject? source) where T : DependencyObject
8 | {
9 | var target = source;
10 |
11 | while (target != null && target is not T)
12 | {
13 | target = VisualTreeHelper.GetParent(target);
14 | }
15 |
16 | return target as T;
17 | }
18 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Helpers/RuntimeHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 | using System.Text;
3 |
4 | namespace InternalsViewer.UI.App.Helpers;
5 |
6 | public static class RuntimeHelper
7 | {
8 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
9 | private static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder? packageFullName);
10 |
11 | public static bool IsMsix
12 | {
13 | get
14 | {
15 | var length = 0;
16 |
17 | return GetCurrentPackageFullName(ref length, null) != 15700L;
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Helpers/Selectors/MarkerTemplateSelector.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Annotations;
2 | using InternalsViewer.UI.App.Models;
3 | using Microsoft.UI.Xaml.Controls;
4 | using System.Linq;
5 |
6 | namespace InternalsViewer.UI.App.Helpers.Selectors;
7 |
8 | public class MarkerTemplateSelector : DataTemplateSelector
9 | {
10 | public DataTemplate DefaultTemplate { get; set; } = null!;
11 |
12 | public DataTemplate PointerTemplate { get; set; } = null!;
13 |
14 | protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
15 | {
16 | var marker = item as Marker;
17 |
18 | var pointerTypes = new[]
19 | {
20 | ItemType.DownPagePointer,
21 | ItemType.Rid,
22 | ItemType.HeaderPageAddress,
23 | ItemType.NextPage,
24 | ItemType.PreviousPage,
25 | };
26 |
27 | if (pointerTypes.Any(p => p == marker?.Type))
28 | {
29 | return PointerTemplate;
30 | }
31 |
32 | return DefaultTemplate;
33 | }
34 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Helpers/Selectors/SlotTemplateSelector.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.UI.App.Models;
2 | using Microsoft.UI.Xaml.Controls;
3 |
4 | namespace InternalsViewer.UI.App.Helpers.Selectors;
5 |
6 | internal class SlotTemplateSelector : DataTemplateSelector
7 | {
8 | public DataTemplate SlotTemplate { get; set; } = null!;
9 |
10 | public DataTemplate ItemTemplate { get; set; } = null!;
11 |
12 | protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
13 | {
14 | var slot = item as PageSlot;
15 |
16 | if (slot?.Index < 0)
17 | {
18 | return ItemTemplate;
19 | }
20 |
21 | return SlotTemplate;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/LargeTile.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/LargeTile.scale-100.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/LargeTile.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/LargeTile.scale-125.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/LargeTile.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/LargeTile.scale-150.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/LargeTile.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/LargeTile.scale-200.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/LargeTile.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/LargeTile.scale-400.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/SmallTile.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SmallTile.scale-100.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/SmallTile.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SmallTile.scale-125.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/SmallTile.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SmallTile.scale-150.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/SmallTile.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SmallTile.scale-200.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/SmallTile.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SmallTile.scale-400.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/SplashScreen.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SplashScreen.scale-100.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/SplashScreen.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SplashScreen.scale-125.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/SplashScreen.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SplashScreen.scale-150.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/SplashScreen.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SplashScreen.scale-200.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/SplashScreen.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/SplashScreen.scale-400.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-100.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-125.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-150.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-200.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square150x150Logo.scale-400.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-16.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-24.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-256.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-32.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.altform-unplated_targetsize-48.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-100.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-125.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-150.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-200.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.scale-400.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-16.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-24.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-256.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-32.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Square44x44Logo.targetsize-48.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/StoreLogo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/StoreLogo.scale-100.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/StoreLogo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/StoreLogo.scale-125.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/StoreLogo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/StoreLogo.scale-150.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/StoreLogo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/StoreLogo.scale-200.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/StoreLogo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/StoreLogo.scale-400.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-100.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-125.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-125.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-150.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-200.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/danny-sg/internals-viewer/d4a207adb24e054d552f42bf75c0cec8163270c0/src/InternalsViewer.UI.App/Images/Wide310x150Logo.scale-400.png
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Messages/ConnectMessages.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.Messaging.Messages;
2 | using InternalsViewer.UI.App.Models.Connections;
3 |
4 | namespace InternalsViewer.UI.App.Messages;
5 |
6 | public class ConnectServerMessage(string connectionString, RecentConnection recent) : AsyncRequestMessage
7 | {
8 | public string ConnectionString { get; } = connectionString;
9 |
10 | public RecentConnection Recent { get; } = recent;
11 |
12 | public bool IsPasswordRequired { get; set; }
13 | }
14 |
15 | public class ConnectFileMessage(string filename, RecentConnection recent) : AsyncRequestMessage
16 | {
17 | public string Filename { get; } = filename;
18 |
19 | public RecentConnection Recent { get; } = recent;
20 | }
21 |
22 | public class ConnectBackupMessage(string filename, RecentConnection recent) : AsyncRequestMessage
23 | {
24 | public string Filename { get; } = filename;
25 |
26 | public RecentConnection Recent { get; set; } = recent;
27 | }
28 |
29 | public class ConnectRecentMessage(RecentConnection recent) : AsyncRequestMessage
30 | {
31 | public RecentConnection Recent { get; set; } = recent;
32 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Messages/ExceptionMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommunityToolkit.Mvvm.Messaging.Messages;
3 |
4 | namespace InternalsViewer.UI.App.Messages;
5 |
6 | public class ExceptionMessage(Exception exception) : AsyncRequestMessage
7 | {
8 | public Exception Exception { get; } = exception;
9 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Messages/NavigateMessage.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.Messaging.Messages;
2 |
3 | namespace InternalsViewer.UI.App.Messages;
4 |
5 | public class NavigateMessage(string target) : ValueChangedMessage(target);
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Messages/OpenPageMessage.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.Messaging.Messages;
2 | using InternalsViewer.Internals.Engine.Address;
3 | using InternalsViewer.Internals.Engine.Database;
4 |
5 | namespace InternalsViewer.UI.App.Messages;
6 |
7 | public class OpenPageRequest(DatabaseSource database, PageAddress pageAddress)
8 | {
9 | public PageAddress PageAddress { get; } = pageAddress;
10 |
11 | public DatabaseSource Database { get; } = database;
12 |
13 | public ushort? Slot { get; set; }
14 | }
15 |
16 | public class OpenIndexRequest(DatabaseSource database, PageAddress rootPage)
17 | {
18 | public DatabaseSource Database { get; } = database;
19 |
20 | public PageAddress RootPageAddress { get; } = rootPage;
21 | }
22 |
23 | public class OpenPageMessage(OpenPageRequest request) : AsyncRequestMessage
24 | {
25 | public OpenPageRequest Request { get; } = request;
26 | }
27 |
28 | public class OpenIndexMessage(OpenIndexRequest request) : AsyncRequestMessage
29 | {
30 | public OpenIndexRequest Request { get; } = request;
31 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/AllocationUnit.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 |
3 | namespace InternalsViewer.UI.App.Models;
4 |
5 | public class AllocationUnit: ObservableObject
6 | {
7 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/Connections/RecentConnection.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using System;
3 |
4 | namespace InternalsViewer.UI.App.Models.Connections;
5 |
6 | public partial class RecentConnection : ObservableObject
7 | {
8 | [ObservableProperty]
9 | private string name = string.Empty;
10 |
11 | [ObservableProperty]
12 | private string connectionType = string.Empty;
13 |
14 | [ObservableProperty]
15 | private string value = string.Empty;
16 |
17 | [ObservableProperty]
18 | private string id = Guid.NewGuid().ToString();
19 |
20 | public bool IsPasswordRequired { get; init; }
21 | }
22 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/Connections/ServerConnectionSettings.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.UI.App.Models.Connections;
2 |
3 | public class ServerConnectionSettings
4 | {
5 | public string InstanceName { get; init; } = string.Empty;
6 |
7 | public int AuthenticationType { get; init; }
8 |
9 | public string DatabaseName { get; init; } = string.Empty;
10 |
11 | public string UserId { get; init; } = string.Empty;
12 | }
13 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/DatabaseFile.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using InternalsViewer.UI.App.ViewModels.Database;
3 |
4 | namespace InternalsViewer.UI.App.Models;
5 |
6 | public partial class DatabaseFile(DatabaseTabViewModel parent) : ObservableObject
7 | {
8 | [ObservableProperty]
9 | private short fileId;
10 |
11 | [ObservableProperty]
12 | private int size;
13 |
14 | public DatabaseTabViewModel Parent { get; } = parent;
15 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/ExtentAllocation.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.UI.App.Models;
2 |
3 | public class ExtentAllocation(short fileId, int extentId)
4 | {
5 | public int ExtentId { get; } = extentId;
6 |
7 | public short FileId { get; } = fileId;
8 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/Index/IndexPageModel.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 |
3 | namespace InternalsViewer.UI.App.Models.Index;
4 |
5 | public class IndexPageModel
6 | {
7 | public PageAddress PageAddress { get; set; }
8 |
9 | public PageAddress NextPage { get; set; }
10 |
11 | public PageAddress PreviousPage { get; set; }
12 |
13 | public string Name { get; set; } = string.Empty;
14 |
15 | public int Slots { get; set; }
16 |
17 | public int Level { get; set; }
18 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/Index/IndexRecordFieldModel.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 |
3 | namespace InternalsViewer.UI.App.Models.Index;
4 |
5 | public class IndexRecordFieldModel
6 | {
7 | public string Name { get; set; } = string.Empty;
8 |
9 | public string Value { get; set; } = string.Empty;
10 |
11 | public SqlDbType DataType { get; set; }
12 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/Index/IndexRecordModel.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace InternalsViewer.UI.App.Models.Index;
9 |
10 | public class IndexRecordModel
11 | {
12 | public int Slot { get; set; }
13 |
14 | public PageAddress DownPagePointer { get; set; } = PageAddress.Empty;
15 |
16 | public RowIdentifier? RowIdentifier { get; set; } = RowIdentifier.Empty;
17 |
18 | public List Fields { get; set; } = new();
19 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/MarkStyle.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI;
2 | using Microsoft.UI.Xaml.Media;
3 |
4 | namespace InternalsViewer.UI.App.Models;
5 |
6 | public class MarkStyle
7 | {
8 | ///
9 | /// Gets or sets the fore colour.
10 | ///
11 | /// The fore colour.
12 | public SolidColorBrush ForeColour { get; set; } = new(Colors.Black);
13 |
14 | ///
15 | /// Gets or sets the back colour.
16 | ///
17 | /// The back colour.
18 | public SolidColorBrush BackColour { get; set; } = new(Colors.Transparent);
19 |
20 | ///
21 | /// Gets or sets the alternate back colour.
22 | ///
23 | /// The alternate back colour.
24 | public SolidColorBrush AlternateBackColour { get; set; } = new(Colors.Transparent);
25 |
26 | public string Name { get; set; } = string.Empty;
27 |
28 | public int? Ordinal { get; set; }
29 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/Marker.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 | using System.Linq;
4 | using InternalsViewer.Internals.Annotations;
5 | using InternalsViewer.UI.App.Services.Markers;
6 | using Windows.UI;
7 |
8 | namespace InternalsViewer.UI.App.Models;
9 |
10 | public class Marker: DependencyObject
11 | {
12 | public string Name { get; set; } = string.Empty;
13 |
14 | public int StartPosition { get; set; }
15 |
16 | public int EndPosition { get; set; }
17 |
18 | public int Length => EndPosition - StartPosition;
19 |
20 | public Color BackColour { get; set; }
21 |
22 | public Color AlternateBackColour { get; set; }
23 |
24 | public Color ForeColour { get; set; }
25 |
26 | public string Value { get; set; } = string.Empty;
27 |
28 | public bool IsNull { get; set; }
29 |
30 | public bool IsVisible { get; set; } = true;
31 |
32 | public MarkerType MarkerType { get; set; }
33 |
34 | public ItemType Type { get; set; }
35 |
36 | public int? Ordinal { get; set; }
37 |
38 | public List Tags { get; set; } = new();
39 |
40 | public static Marker? GetMarkerAtPosition(int startPosition, int endPosition, List markers)
41 | {
42 | return markers.FirstOrDefault(marker => marker.StartPosition >= startPosition & marker.EndPosition <= endPosition);
43 | }
44 |
45 | public ObservableCollection Children { get; set; } = new();
46 |
47 | public bool HasKey { get; set; }
48 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/Page/DecodePair.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.UI.App.Models.Page;
2 |
3 | internal class DecodePair
4 | {
5 | public string DataType { get; set; } = string.Empty;
6 |
7 | public string Value { get; set; } = string.Empty;
8 | }
9 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/PageBookmark.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.Internals.Engine.Address;
2 |
3 | namespace InternalsViewer.UI.App.Models;
4 |
5 | public record PageBookmark
6 | {
7 | public string DatabaseName { get; set; } = string.Empty;
8 |
9 | public PageAddress PageAddress { get; set; }
10 | }
11 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/PageSlot.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.UI.App.Models;
2 |
3 | public class PageSlot
4 | {
5 | public short Index { get; init; }
6 |
7 | public ushort Offset { get; init; }
8 |
9 | public string Description { get; init; } = string.Empty;
10 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Models/SettingsOptions.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.UI.App.Models;
2 |
3 | public class SettingsOptions
4 | {
5 | public string ApplicationDataFolder { get; set; } = "InternalsViewer/ApplicationData";
6 |
7 | public string SettingsFile { get; set; } = "LocalSettings.json";
8 | }
9 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Properties/PublishProfiles/win-x64.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | FileSystem
8 | x64
9 | win-x64
10 | bin\\\win-x64\publish\
11 | true
12 | false
13 | False
14 | true
15 | Release
16 | net8.0-windows10.0.19041.0
17 | false
18 |
22 |
23 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Properties/PublishProfiles/win-x86.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | FileSystem
8 | x86
9 | win-x86
10 | bin\\\win-x86\publish\
11 | true
12 | false
13 | False
14 | true
15 | Release
16 | net8.0-windows10.0.19041.0
17 | false
18 |
22 |
23 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Local": {
4 | "commandName": "MsixPackage",
5 | "commandLineArgs": "", /* Command line arguments to pass to the app. */
6 | "alwaysReinstallApp": false, /* Uninstall and then reinstall the app. All information about the app state is deleted. */
7 | "remoteDebugEnabled": false, /* Indicates that the debugger should attach to a process on a remote machine. */
8 | "allowLocalNetworkLoopbackProperty": true, /* Allow the app to make network calls to the device it is installed on. */
9 | "authenticationMode": "Windows", /* The authentication scheme to use when connecting to the remote machine. */
10 | "doNotLaunchApp": false, /* Do not launch the app, but debug my code when it starts. */
11 | "remoteDebugMachine": "", /* The name of the remote machine. */
12 | "nativeDebugging": false /* Enable debugging for managed and native code together, also known as mixed-mode debugging. */
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Services/Markers/MarkStyleProvider.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.WinUI.Helpers;
2 | using InternalsViewer.Internals.Annotations;
3 | using InternalsViewer.UI.App.Models;
4 | using System.Diagnostics;
5 |
6 | namespace InternalsViewer.UI.App.Services.Markers;
7 |
8 | public class MarkStyleProvider
9 | {
10 | private ResourceDictionary? ThemeDictionary { get; set; }
11 |
12 | public MarkStyleProvider()
13 | {
14 | Initialize();
15 | }
16 |
17 | private void Initialize()
18 | {
19 | var themeListener = new ThemeListener();
20 |
21 | var currentTheme = themeListener.CurrentTheme;
22 |
23 | ThemeDictionary = Application.Current.Resources.ThemeDictionaries[currentTheme.ToString()] as ResourceDictionary;
24 | }
25 |
26 | public MarkStyle GetDefaultMarkStyle()
27 | {
28 | object? resource = null;
29 |
30 | ThemeDictionary?.TryGetValue("DefaultMarkerStyle", out resource);
31 |
32 | var style = resource as MarkStyle ?? new MarkStyle();
33 |
34 | return style;
35 | }
36 |
37 | public MarkStyle GetMarkStyle(ItemType itemType)
38 | {
39 | object? resource = null;
40 |
41 | ThemeDictionary?.TryGetValue($"{itemType}MarkerStyle", out resource);
42 |
43 | if(Debugger.IsAttached && resource == null)
44 | {
45 | Debugger.Break();
46 | }
47 |
48 | if (resource == null)
49 | {
50 | ThemeDictionary?.TryGetValue("DefaultMarkerStyle", out resource);
51 | }
52 |
53 | var style = resource as MarkStyle ?? new MarkStyle();
54 |
55 | return style;
56 | }
57 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/ViewModels/Allocation/AllocationOverViewModel.cs:
--------------------------------------------------------------------------------
1 | using System.Drawing;
2 | using CommunityToolkit.Mvvm.ComponentModel;
3 | using InternalsViewer.Internals.Engine.Allocation.Enums;
4 |
5 | namespace InternalsViewer.UI.App.ViewModels.Allocation;
6 |
7 | public partial class AllocationOverViewModel : ObservableObject
8 | {
9 | [ObservableProperty]
10 | private bool isOpen;
11 |
12 | [ObservableProperty]
13 | private int pageId;
14 |
15 | [ObservableProperty]
16 | private int extentId;
17 |
18 | [ObservableProperty]
19 | private Color layerColour = Color.Transparent;
20 |
21 | [ObservableProperty]
22 | private string layerName = string.Empty;
23 |
24 | [ObservableProperty]
25 | private PfsByte pfsValue = PfsByte.Unknown;
26 | }
27 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/ViewModels/Connections/BackupFileConnectionViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 |
3 | namespace InternalsViewer.UI.App.ViewModels.Connections;
4 |
5 | [ObservableObject]
6 | public partial class BackupFileConnectionViewModel
7 | {
8 | [ObservableProperty]
9 | private string filename = string.Empty;
10 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/ViewModels/Connections/ConnectFileViewModel.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 | using System.IO;
3 | using System.Threading.Tasks;
4 | using CommunityToolkit.Mvvm.ComponentModel;
5 | using CommunityToolkit.Mvvm.Input;
6 | using CommunityToolkit.Mvvm.Messaging;
7 | using InternalsViewer.UI.App.Messages;
8 | using InternalsViewer.UI.App.Models.Connections;
9 |
10 | namespace InternalsViewer.UI.App.ViewModels.Connections;
11 |
12 | public static class ConnectFileViewModelFactory
13 | {
14 | public static ConnectFileViewModel Create() => new();
15 | }
16 |
17 | public partial class ConnectFileViewModel : ObservableValidator
18 | {
19 | [Required(AllowEmptyStrings = false)]
20 | [ObservableProperty]
21 | private string filename = string.Empty;
22 |
23 | [ObservableProperty]
24 | private bool isValid;
25 |
26 | partial void OnFilenameChanged(string value)
27 | {
28 | IsValid = File.Exists(value);
29 | }
30 |
31 | [RelayCommand]
32 | private async Task Connect()
33 | {
34 | var recent = new RecentConnection
35 | {
36 | Name = Path.GetFileName(Filename),
37 | ConnectionType = "File",
38 | Value = Filename
39 | };
40 |
41 | var message = new ConnectFileMessage(Filename, recent);
42 |
43 | await WeakReferenceMessenger.Default.Send(message);
44 |
45 | await message.Response;
46 | }
47 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/ViewModels/Page/HexControlViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using System.Collections.ObjectModel;
3 | using CommunityToolkit.Mvvm.Input;
4 | using InternalsViewer.Internals.Converters.Decoder;
5 |
6 | namespace InternalsViewer.UI.App.ViewModels.Page;
7 |
8 | [ObservableObject]
9 | public partial class HexControlViewModel
10 | {
11 | [ObservableProperty]
12 | private bool isDataTipOpen;
13 |
14 | [ObservableProperty]
15 | private ObservableCollection decodeResults = new();
16 |
17 | [ObservableProperty]
18 | private int startOffset;
19 |
20 | [ObservableProperty]
21 | private int endOffset;
22 |
23 | [ObservableProperty]
24 | private string selectedText = string.Empty;
25 |
26 | [ObservableProperty]
27 | private string dataTipTitle = string.Empty;
28 |
29 | partial void OnSelectedTextChanged(string value)
30 | {
31 | DecodeResults = new ObservableCollection(DataDecoder.Decode(value));
32 |
33 | IsDataTipOpen = DecodeResults.Count > 0;
34 | }
35 |
36 | partial void OnStartOffsetChanged(int value)
37 | {
38 | SetDataTipTitle();
39 | }
40 |
41 | partial void OnEndOffsetChanged(int value)
42 | {
43 | SetDataTipTitle();
44 | }
45 |
46 | private void SetDataTipTitle()
47 | {
48 | DataTipTitle = $" {StartOffset} (0x{StartOffset:X8}) - {EndOffset} (0x{EndOffset:X8})";
49 | }
50 |
51 | [RelayCommand]
52 | private void CloseDataTip()
53 | {
54 | IsDataTipOpen = false;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/ViewModels/Tabs/TabType.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.UI.App.ViewModels.Tabs;
2 |
3 | public enum TabType
4 | {
5 | Database,
6 | Page,
7 | Connect
8 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/ViewModels/Tabs/TabViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommunityToolkit.Mvvm.ComponentModel;
3 | using Microsoft.UI.Dispatching;
4 |
5 | namespace InternalsViewer.UI.App.ViewModels.Tabs;
6 |
7 | public partial class TabViewModel : ObservableObject
8 | {
9 | [ObservableProperty]
10 | private string tabId = string.Empty;
11 |
12 | [ObservableProperty]
13 | private string name = string.Empty;
14 |
15 | [ObservableProperty]
16 | private bool isLoading = true;
17 |
18 | protected DispatcherQueue DispatcherQueue { get; }
19 |
20 | protected TabViewModel()
21 | {
22 | DispatcherQueue = DispatcherQueue.GetForCurrentThread();
23 |
24 | TabId = Guid.NewGuid().ToString();
25 | }
26 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Views/Connect/ConnectBackupPage.xaml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 | This functionality is not yet implemented
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Views/Connect/ConnectBackupPage.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace InternalsViewer.UI.App.Views.Connect;
2 |
3 | public sealed partial class ConnectBackupPage
4 | {
5 | public ConnectBackupPage()
6 | {
7 | InitializeComponent();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Views/Connect/ConnectFilePage.xaml.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.UI.App.ViewModels.Connections;
2 | using Microsoft.UI.Xaml.Navigation;
3 | using System;
4 | using Windows.Storage.Pickers;
5 |
6 | namespace InternalsViewer.UI.App.Views.Connect;
7 |
8 | public sealed partial class ConnectFilePage
9 | {
10 | private ConnectFileViewModel ViewModel => (ConnectFileViewModel)DataContext;
11 |
12 | public ConnectFilePage()
13 | {
14 | InitializeComponent();
15 | }
16 |
17 | protected override void OnNavigatedTo(NavigationEventArgs e)
18 | {
19 | base.OnNavigatedTo(e);
20 |
21 | if (e.Parameter is ConnectFileViewModel viewModel)
22 | {
23 | DataContext = viewModel;
24 | }
25 | }
26 |
27 | private async void BrowseButton_Click(object sender, RoutedEventArgs e)
28 | {
29 | var openPicker = new FileOpenPicker();
30 |
31 | var window = App.MainWindow;
32 |
33 | var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window);
34 |
35 | WinRT.Interop.InitializeWithWindow.Initialize(openPicker, hWnd);
36 |
37 | openPicker.ViewMode = PickerViewMode.List;
38 |
39 | openPicker.FileTypeFilter.Add(".mdf");
40 |
41 | var file = await openPicker.PickSingleFileAsync();
42 |
43 | if (file != null)
44 | {
45 | ViewModel.Filename = file.Path;
46 | ViewModel.IsValid = true;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Views/Connect/ConnectServerPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using InternalsViewer.UI.App.ViewModels.Connections;
2 | using Microsoft.UI.Xaml.Navigation;
3 |
4 | namespace InternalsViewer.UI.App.Views.Connect;
5 |
6 | public sealed partial class ConnectServerPage
7 | {
8 | public ConnectServerPage()
9 | {
10 | InitializeComponent();
11 |
12 | NavigationCacheMode = NavigationCacheMode.Enabled;
13 | }
14 |
15 | private ConnectServerViewModel ViewModel => (ConnectServerViewModel)DataContext;
16 |
17 | protected override void OnNavigatedTo(NavigationEventArgs e)
18 | {
19 | base.OnNavigatedTo(e);
20 |
21 | if (e.Parameter is ConnectServerViewModel viewModel)
22 | {
23 | DataContext = viewModel;
24 | }
25 | }
26 |
27 | private async void DatabaseComboBox_DropDownOpened(object? sender, object e)
28 | {
29 | await ViewModel.RefreshDatabases();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Views/Connect/ConnectStartPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.Messaging;
2 | using InternalsViewer.UI.App.Messages;
3 | using InternalsViewer.UI.App.ViewModels;
4 |
5 | namespace InternalsViewer.UI.App.Views.Connect;
6 |
7 | public sealed partial class ConnectStartPage
8 | {
9 | public ConnectStartPage()
10 | {
11 | InitializeComponent();
12 | }
13 |
14 | public MainViewModel ViewModel => (MainViewModel)DataContext;
15 |
16 | private void ConnectFileTile_OnClick(object? sender, RoutedEventArgs e)
17 | {
18 | WeakReferenceMessenger.Default.Send(new NavigateMessage("ConnectFilePage"));
19 | }
20 |
21 | private void ConnectSqlServerHeaderTile_Click(object sender, RoutedEventArgs e)
22 | {
23 | WeakReferenceMessenger.Default.Send(new NavigateMessage("ConnectServerPage"));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Views/SettingsPage.xaml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 | Settings
14 |
18 |
19 | Created by Danny Gould
20 | Copyright © 2007 - 2024
21 |
22 | https://github.com/danny-sg/internals-viewer
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/Views/SettingsPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml.Controls;
2 |
3 | // To learn more about WinUI, the WinUI project structure,
4 | // and more about our project templates, see: http://aka.ms/winui-project-info.
5 |
6 | namespace InternalsViewer.UI.App.Views;
7 |
8 | ///
9 | /// An empty page that can be used on its own or navigated to within a Frame.
10 | ///
11 | public sealed partial class SettingsPage : Page
12 | {
13 | public SettingsPage()
14 | {
15 | this.InitializeComponent();
16 | }
17 | }
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | PerMonitorV2
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/InternalsViewer.UI.App/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "LocalSettingsOptions": {
3 | "ApplicationDataFolder": "App1/ApplicationData",
4 | "LocalSettingsFile": "LocalSettings.json"
5 | },
6 | "Logging": {
7 | "LogLevel": {
8 | "Default": "Debug",
9 | "Microsoft.AspNetCore": "Warning"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------