# Read locked GeViDB.mdb using Windows Volume Shadow Copy # This allows reading the database even when GeViServer has it locked param( [string]$Action = "test" ) $ErrorActionPreference = "Stop" Write-Host "========================================" -ForegroundColor Cyan Write-Host "Volume Shadow Copy Database Reader" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" $dbPath = "C:\GEVISOFT\DATABASE\GeViDB.mdb" $drive = "C:" function Create-ShadowCopy { Write-Host "Creating shadow copy of $drive..." -ForegroundColor Yellow # Create shadow copy using WMI $class = [WMICLASS]"root\cimv2:Win32_ShadowCopy" $result = $class.Create($drive + "\", "ClientAccessible") if ($result.ReturnValue -ne 0) { throw "Failed to create shadow copy. Return value: $($result.ReturnValue)" } $shadowID = $result.ShadowID Write-Host " [OK] Shadow copy created: $shadowID" -ForegroundColor Green # Get the shadow copy $shadow = Get-WmiObject -Class Win32_ShadowCopy | Where-Object { $_.ID -eq $shadowID } if (-not $shadow) { throw "Failed to retrieve shadow copy" } $deviceObject = $shadow.DeviceObject Write-Host " Device: $deviceObject" -ForegroundColor Gray return @{ ID = $shadowID DeviceObject = $deviceObject Shadow = $shadow } } function Remove-ShadowCopy { param($ShadowInfo) Write-Host "Removing shadow copy..." -ForegroundColor Yellow try { $ShadowInfo.Shadow.Delete() Write-Host " [OK] Shadow copy removed" -ForegroundColor Green } catch { Write-Host " [WARN] Failed to remove shadow copy: $($_.Exception.Message)" -ForegroundColor Yellow } } function Test-DatabaseAccess { param($ShadowDevicePath) Write-Host "Testing database access from shadow copy..." -ForegroundColor Yellow # Create a symbolic link to access the shadow copy $linkPath = "C:\ShadowLink" # Remove existing link if present if (Test-Path $linkPath) { cmd /c "rmdir $linkPath" } Write-Host " Creating symbolic link to shadow copy..." -ForegroundColor Gray $linkCmd = "mklink /d `"$linkPath`" `"$ShadowDevicePath\`"" $linkResult = cmd /c $linkCmd 2>&1 if (-not (Test-Path $linkPath)) { throw "Failed to create symbolic link: $linkResult" } Write-Host " [OK] Symbolic link created" -ForegroundColor Green try { # List what's in the shadow link root Write-Host " Listing shadow copy root..." -ForegroundColor Gray $rootItems = Get-ChildItem $linkPath -ErrorAction SilentlyContinue | Select-Object -First 10 -ExpandProperty Name if ($rootItems) { foreach ($item in $rootItems) { Write-Host " - $item" -ForegroundColor DarkGray } } else { Write-Host " [WARN] No items found in shadow root" -ForegroundColor Yellow } # The relative path from the drive root $relativePath = $dbPath.Substring(3) # Remove "C:\" to get "GEVISOFT\DATABASE\GeViDB.mdb" $shadowDbPath = Join-Path $linkPath $relativePath Write-Host " Shadow DB path: $shadowDbPath" -ForegroundColor Gray # Check if GEVISOFT folder exists $gevisoftPath = Join-Path $linkPath "GEVISOFT" if (Test-Path $gevisoftPath) { Write-Host " [OK] GEVISOFT folder found in shadow" -ForegroundColor Green # Check DATABASE subfolder $databasePath = Join-Path $gevisoftPath "DATABASE" if (Test-Path $databasePath) { Write-Host " [OK] DATABASE folder found" -ForegroundColor Green # List database files - try different methods Write-Host " Attempting to list database files..." -ForegroundColor Gray try { $dbFiles = Get-ChildItem $databasePath -Force -ErrorAction Stop Write-Host " Found $($dbFiles.Count) files:" -ForegroundColor Gray foreach ($file in $dbFiles) { Write-Host " - $($file.Name) ($([math]::Round($file.Length / 1MB, 2)) MB)" -ForegroundColor DarkGray } } catch { Write-Host " [WARN] Get-ChildItem failed: $($_.Exception.Message)" -ForegroundColor Yellow # Try dir command instead Write-Host " Trying dir command..." -ForegroundColor Gray $dirOutput = cmd /c "dir `"$databasePath`" /b" 2>&1 Write-Host " Dir output: $dirOutput" -ForegroundColor DarkGray } } else { Write-Host " [WARN] DATABASE folder not found at: $databasePath" -ForegroundColor Yellow } } else { Write-Host " [WARN] GEVISOFT folder not found in shadow copy" -ForegroundColor Yellow } Write-Host "" Write-Host " Preparing to copy database..." -ForegroundColor Yellow # Copy from shadow to temp location for testing $tempDb = "$env:TEMP\GeViDB_shadow_copy.mdb" if (Test-Path $tempDb) { Remove-Item $tempDb -Force } Write-Host " Copying database from shadow..." -ForegroundColor Gray Write-Host " Target: $tempDb" -ForegroundColor Gray # Try .NET file I/O which may handle shadow copies better Write-Host " Attempting .NET File.Copy..." -ForegroundColor Yellow try { # Use .NET FileStream to read from shadow copy Write-Host " Opening source file..." -ForegroundColor Gray $sourceStream = [System.IO.File]::Open( $shadowDbPath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite ) Write-Host " Source opened, size: $([math]::Round($sourceStream.Length / 1MB, 2)) MB" -ForegroundColor Green # Create destination file Write-Host " Creating destination file..." -ForegroundColor Gray $destStream = [System.IO.File]::Create($tempDb) # Copy data Write-Host " Copying data..." -ForegroundColor Gray $sourceStream.CopyTo($destStream) # Close streams $destStream.Close() $sourceStream.Close() Write-Host " [OK] .NET copy completed" -ForegroundColor Green } catch { Write-Host " [ERROR] .NET copy failed: $($_.Exception.Message)" -ForegroundColor Red # Clean up if open if ($null -ne $destStream) { $destStream.Dispose() } if ($null -ne $sourceStream) { $sourceStream.Dispose() } # Try direct .NET File.Copy as last resort Write-Host " Trying System.IO.File.Copy..." -ForegroundColor Yellow try { [System.IO.File]::Copy($shadowDbPath, $tempDb, $true) Write-Host " [OK] System.IO.File.Copy succeeded" -ForegroundColor Green } catch { Write-Host " [ERROR] All copy methods failed: $($_.Exception.Message)" -ForegroundColor Red } } } finally { # Clean up symbolic link if (Test-Path $linkPath) { Write-Host " Removing symbolic link..." -ForegroundColor Gray cmd /c "rmdir $linkPath" 2>&1 | Out-Null } } if (Test-Path $tempDb) { $size = (Get-Item $tempDb).Length / 1MB Write-Host " [OK] Database copied successfully ($([math]::Round($size, 2)) MB)" -ForegroundColor Green # Try to read it Write-Host "" Write-Host "Testing database read..." -ForegroundColor Yellow # Try different providers $providers = @( "Microsoft.Jet.OLEDB.4.0", # Access 2003 and earlier "Microsoft.ACE.OLEDB.12.0", # Access 2007-2013 "Microsoft.ACE.OLEDB.16.0" # Access 2016+ ) $connected = $false $conn = New-Object -ComObject ADODB.Connection foreach ($provider in $providers) { $connectionString = "Provider=$provider;Data Source=$tempDb;" Write-Host " Trying provider: $provider" -ForegroundColor Gray try { $conn.Open($connectionString) Write-Host " [OK] Connected with $provider" -ForegroundColor Green $connected = $true break } catch { Write-Host " [WARN] Failed with $provider : $($_.Exception.Message)" -ForegroundColor Yellow if ($conn.State -ne 0) { try { $conn.Close() } catch { } } } } if (-not $connected) { throw "Failed to connect with any provider" } try { # Test query $rs = $conn.Execute("SELECT COUNT(*) FROM MSysObjects WHERE Type=1") $tableCount = $rs.Fields.Item(0).Value $rs.Close() Write-Host " [OK] Database has $tableCount user tables" -ForegroundColor Green $conn.Close() return $tempDb } catch { Write-Host " [ERROR] Failed to query database: $($_.Exception.Message)" -ForegroundColor Red if ($conn.State -ne 0) { try { $conn.Close() } catch { } } throw } } else { throw "Failed to copy database from shadow" } } # Main execution try { # Check if running as administrator $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) $isAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { Write-Host "[ERROR] This script requires Administrator privileges" -ForegroundColor Red Write-Host "Please run PowerShell as Administrator and try again" -ForegroundColor Yellow exit 1 } Write-Host "[OK] Running as Administrator" -ForegroundColor Green Write-Host "" # Create shadow copy $shadowInfo = Create-ShadowCopy Write-Host "" try { # Test access $tempDb = Test-DatabaseAccess -ShadowDevicePath $shadowInfo.DeviceObject Write-Host "" Write-Host "========================================" -ForegroundColor Cyan Write-Host "SUCCESS!" -ForegroundColor Green Write-Host "========================================" -ForegroundColor Cyan Write-Host "" Write-Host "Database successfully copied from shadow to:" -ForegroundColor Green Write-Host " $tempDb" -ForegroundColor White Write-Host "" Write-Host "You can now use this copy for read operations." -ForegroundColor Gray } finally { Write-Host "" Remove-ShadowCopy -ShadowInfo $shadowInfo } } catch { Write-Host "" Write-Host "[ERROR] $($_.Exception.Message)" -ForegroundColor Red Write-Host "" Write-Host "Stack trace:" -ForegroundColor Gray Write-Host $_.ScriptStackTrace -ForegroundColor Gray exit 1 } Write-Host "" Write-Host "========================================" -ForegroundColor Cyan