Please note that this script can only remove the password-protected editing restriction.
#Requires -Version 5.1
<#
.SYNOPSIS
A script to process a Word document, remove document protection, and restore its original file format.
.DESCRIPTION
This script performs a three-stage process on a Word document provided via command-line.
It correctly handles both relative and absolute file paths.
Stage 1: Converts legacy '.doc' files to the modern '.docx' format if necessary.
Stage 2: Treats the .docx file as a ZIP archive to modify its internal 'word/settings.xml'.
It removes the entire '<w:documentProtection>...</w:documentProtection>' XML node from this file.
Stage 3: If the original file was a '.doc', it converts the modified '.docx' file back
to the '.doc' format, preserving the original filename.
IMPORTANT:
- You must have Microsoft Word installed for the conversion steps.
- The .NET Framework 4.5 or higher is required for the ZIP manipulation functions.
.PARAMETER InputFilePath
The full or relative path to the Word document you want to process.
.EXAMPLE
# Using a full path
.\Process-WordDoc.ps1 -InputFilePath "C:\Shared\ProtectedReport.docx"
.EXAMPLE
# Using a relative path from the script's location
.\Process-WordDoc.ps1 -InputFilePath "..\Reports\LegacyProtected.doc"
#>
# --- PARAMETERS ---
param (
[Parameter(Mandatory = $true, HelpMessage = "Please provide the full or relative path to the Word document.")]
[ValidateScript({
if (-not (Test-Path $_ -PathType Leaf)) {
throw "File not found at path: $_"
}
return $true
})]
[string]$InputFilePath
)
# --- SCRIPT INITIALIZATION ---
# Convert the provided path (which could be relative) to a fully qualified absolute path.
# This is crucial for ensuring the Word COM object and other file operations work reliably.
$InputFilePath = (Resolve-Path -LiteralPath $InputFilePath).ProviderPath
Write-Host "Processing absolute file path: $InputFilePath"
# This flag tracks if the original file was a .doc format.
$wasOriginallyDoc = $false
# This variable will hold the path to the .docx file we need to modify.
$targetDocxPath = $InputFilePath
# --- STAGE 1: CONVERT .DOC TO .DOCX (IF NECESSARY) ---
if ([System.IO.Path]::GetExtension($InputFilePath).ToLower() -eq ".doc") {
$wasOriginallyDoc = $true
Write-Host "Stage 1: Legacy '.doc' format detected. Starting conversion."
$word = $null
$doc = $null
try {
$word = New-Object -ComObject Word.Application
$word.Visible = $false
$targetDocxPath = $InputFilePath -replace '\.doc$', '.docx'
$doc = $word.Documents.Open($InputFilePath)
$wdFormatXMLDocument = 12 # Constant for .docx format
$doc.SaveAs2($targetDocxPath, $wdFormatXMLDocument)
Write-Host "Successfully converted and saved file to: $targetDocxPath"
}
catch {
Write-Error "An error occurred during the Word conversion process: $_"
return # Stop the script if conversion fails.
}
finally {
if ($doc) { $doc.Close($false) }
if ($word) { $word.Quit() }
if ($doc) { [System.Runtime.InteropServices.Marshal]::ReleaseComObject($doc) | Out-Null }
if ($word) { [System.Runtime.InteropServices.Marshal]::ReleaseComObject($word) | Out-Null }
Remove-Variable doc, word -ErrorAction SilentlyContinue
}
}
else {
Write-Host "Stage 1: File is already a .docx. No conversion necessary."
}
# --- STAGE 2: MODIFY SETTINGS.XML WITHIN THE .DOCX ARCHIVE ---
Write-Host "Stage 2: Modifying internal XML for file: $targetDocxPath"
# Explicitly load the assembly containing the ZipFile class.
# This resolves the "Unable to find type" error on systems where it's not loaded by default.
try {
Add-Type -AssemblyName System.IO.Compression.FileSystem
}
catch {
Write-Error "Failed to load the System.IO.Compression.FileSystem assembly. This script requires .NET Framework 4.5 or higher."
return
}
$tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid().ToString())
New-Item -Path $tempDir -ItemType Directory -Force | Out-Null
try {
Write-Host "Extracting archive to temporary location..."
[System.IO.Compression.ZipFile]::ExtractToDirectory($targetDocxPath, $tempDir)
$settingsXmlPath = Join-Path $tempDir "word\settings.xml"
if (Test-Path $settingsXmlPath) {
Write-Host "Found word/settings.xml. Attempting to remove document protection node."
$xml = [xml](Get-Content $settingsXmlPath)
$nsm = New-Object System.Xml.XmlNamespaceManager($xml.NameTable)
$nsm.AddNamespace("w", "http://schemas.openxmlformats.org/wordprocessingml/2006/main")
# Find the <w:documentProtection> node using the namespace manager.
$protectionNode = $xml.SelectSingleNode("//w:documentProtection", $nsm)
if ($protectionNode) {
# If the node is found, remove it from its parent.
$protectionNode.ParentNode.RemoveChild($protectionNode) | Out-Null
$xml.Save($settingsXmlPath)
Write-Host "Successfully removed <w:documentProtection> node and saved changes."
}
else {
Write-Host "<w:documentProtection> node not found. No changes made."
}
}
else {
Write-Host "File 'word/settings.xml' does not exist in the archive. Skipping modification."
}
Write-Host "Re-compressing archive..."
if (Test-Path $targetDocxPath) { Remove-Item $targetDocxPath -Force }
[System.IO.Compression.ZipFile]::CreateFromDirectory($tempDir, $targetDocxPath)
Write-Host "Successfully updated the Word document."
}
catch {
Write-Error "An error occurred during the ZIP archive manipulation: $_"
}
finally {
if (Test-Path $tempDir) {
Remove-Item -Path $tempDir -Recurse -Force
Write-Host "Temporary files have been cleaned up."
}
}
# --- STAGE 3: CONVERT BACK TO .DOC (IF NECESSARY) ---
if ($wasOriginallyDoc) {
Write-Host "Stage 3: Original file was '.doc'. Converting back."
$word = $null
$doc = $null
try {
$word = New-Object -ComObject Word.Application
$word.Visible = $false
$doc = $word.Documents.Open($targetDocxPath)
$wdFormatDocument = 0 # Constant for .doc format
$doc.SaveAs2($InputFilePath, $wdFormatDocument) # Save back to original filename
Write-Host "Successfully converted back and saved to: $InputFilePath"
}
catch {
Write-Error "An error occurred during the final conversion back to .doc: $_"
}
finally {
if ($doc) { $doc.Close($false) }
if ($word) { $word.Quit() }
if ($doc) { [System.Runtime.InteropServices.Marshal]::ReleaseComObject($doc) | Out-Null }
if ($word) { [System.Runtime.InteropServices.Marshal]::ReleaseComObject($word) | Out-Null }
Remove-Variable doc, word -ErrorAction SilentlyContinue
# Clean up the intermediate .docx file.
if (Test-Path $targetDocxPath) {
Remove-Item $targetDocxPath -Force
Write-Host "Cleaned up intermediate .docx file."
}
}
}
Write-Host "Processing complete."
最后一次更新于2025-07-10
0 comment