找出占用Installer 目录空间的元凶
找出占用Installer 目录空间的元凶为什么谈这个话题今天看到一台windows 7 的计算机,C盘分了50GB,结果installer 目录有47GB,幸亏我对该目录启用过压缩,压缩后实际占用32GB的样子,但也足够大了,已经导致C盘满了,我删了下TEMP目录,清了c:users下一些很久没用的用户配置文件,救回2GB出来。但这个Installer目录为什么会占用这么多空间?什么样的靠谱方法可以缩减该尺寸。 你以为我没做过尝试列举下一些之前尝试的方法,这些方法安全,但是收效甚微。
不靠谱的清理软件OK,一般这种状况下会找些流行的专用软件来干这个事,毕竟术业有专攻,第一次使用了WICleanup。 WICleanup列出了冗余文件,而且我的文件清单上有多个文件大小都是一个尺寸,我说这个软件难道是可以算出重复文件的功能,然后把重复文件删掉?!,然后鉴于桌面说用过,我觉得应该至少问题不大吧,看了下目录下有个命令行的版本带-s 可以静默清理,我试了一下,发现清掉30GB多的空间,到installer 目录一看,我就知道坏了,里面的MSP、MSI文件全干掉了。 这个软件我后面看开发时间也是超级古老了,最近还有人发Blog介绍这个工具,而且评论区还有好多人反馈清理了好多.............没发现副作用很大么?。 pjl6523853 爱武侠的程序员2018-04-09 20:37:08#4楼 太感谢博主了!帮我清理了30G!请问博主可以转载嘛 Maxwell_STU Maxwell_STU2018-03-11 00:45:01#3楼 感谢博主分享,突然就清理出来10G以上的空间,感觉清爽了超级多,压力一下子就小了。 KEVIN_LI_MY KEVIN_LI_MY2017-11-29 08:59:26#2楼 我清理出了3g,也不少了。 我稍后测试了下用Wicleanup清理过的计算机,控制面板中部分程序的卸载、修复,windows 更新均有问题,主要问题是弹对话框提示找文件。 不靠谱的参考文档一篇看起很高深的文章指引,又是解构msi文件,又是C++清注册表,主要是一个操作,就是删除
一个看起来可能还靠谱的软件patchCleaner有了上面教训,我想我得了解下此类软件的原理,然后确定可行后才能使用。首先我参考了微软的员工的解决方法,算是廖胜于无吧,大概意思就是官方仅支持通过卸载软件的方式来清理installer目录,这个Blog在评论区讨论了很多次,但似乎没有什么好的结论,也没有提供太多有价值的信息。 在我访问类似superuser 上的讨论时,我发现了这个软件patchCleaner,为啥说可能还靠谱,因为下面:
准备让数据说话总结了上面的一些信息,我目前有下列问题,需要让实际的数据说话:
我的计划:
自己写了powershell 脚本,按照patchCleaner的思路,自己过滤出孤立的安装文件,这部分孤立文件我后来只过滤出MSI、MSP后缀的文件(这部分文件占用最大)。其他后缀的Installer目录下的文件,我们不去动它(因为可能被引用,比如ICON文件或者EXE等文件)。MSI,MSP文件当中会有一些除了发布者为微软的安装文件,比如Adobe的文件用get-msisummaryinfo 获取不到信息,我们也过滤掉(在PatchCleaner中也默认过滤掉了adobe的安装文件),过滤后的孤立安装文件大概如下图所示。
自己的工具?当上面数据很明了清晰时,我们是否可以写出自己的工具来..大致臆想了自己工具的功能和执行步骤
列下PatchCleaner存在的不足的地方: 由于windows 上有PSMSI 这个powershell 模组,所以最开始省去我大部分代码,把主要精力放在测试上(反复考虑后,还是自己写powershell 调用Installer Com 接口的函数用于获取信息,虽然比较困难,全程要用反射功能来操作Installer COM,而且读取MSP文件额度问题已经解决,读MSP时,数据库的Openmode需要指定其他值,这样可以不依赖外部模组)。 $Installer = New-Object -ComObject WindowsInstaller.Installer
$Type = $Installer.GetType()
function Get-MsiProducts {
$Products = $Type.InvokeMember('Products',"GetProperty",$null,$Installer,$null)
foreach ($Product In $Products) {
$hash = @{}
$hash.ProductCode = $Product
$Attributes = @('Language','ProductName','PackageCode','Transforms','AssignmentType','PackageName','InstalledProductName','VersionString','RegCompany','RegOwner','ProductID','ProductIcon','InstallLocation','InstallSource','InstallDate','Publisher','LocalPackage','HelpLink','HelpTelephone','URLInfoAbout','URLUpdateInfo')
foreach ($Attribute In $Attributes) {
$hash."$($Attribute)" = $null
}
foreach ($Attribute In $Attributes) {
try {
$hash."$($Attribute)" = $Type.InvokeMember('ProductInfo',@($Product,$Attribute))
} catch [System.Exception] {
}
}
if($hash."LocalPackage"){
if(test-path $hash."LocalPackage"){
$hash.size=$(get-item $hash."LocalPackage").Length
}
}
New-Object -TypeName PSObject -Property $hash
}
}
function Get-MsiPatch {
[cmdletbinding()]
param(
$product
)
$Patches = $Type.InvokeMember('Patches',@($product))
foreach ($Patch In $Patches) {
$hash = @{}
$hash.ProductCode = $Product
$hash.PatchCode=$Patch
$Attributes = @('LocalPackage')
foreach ($Attribute In $Attributes) {
$hash."$($Attribute)" = $null
}
foreach ($Attribute In $Attributes) {
try {
$hash."$($Attribute)" = $Type.InvokeMember('PatchInfo','GetProperty',@($Patch,$Attribute))
} catch [System.Exception] {
#$error[0]|format-list –force
}
}
if($hash."LocalPackage"){
if(test-path $hash."LocalPackage"){
$hash.size=$(get-item $hash."LocalPackage").Length
}
}
New-Object -TypeName PSObject -Property $hash
}
}
function Get-MSIFileInfo {
[cmdletbinding()]
param
(
[Parameter(Mandatory = $true)]$Path
)
try {
if(test-path $path){
$path=get-item $path
$extension=$path.Extension.ToLower()
$DBOPENMODE=0
$TABLENAME='Property'
if($extension -eq '.msp'){
$DBOPENMODE=32
$TABLENAME="MsiPatchMetadata"
}
$msiProps = @{}
$Database = $Type.InvokeMember("OpenDatabase","InvokeMethod",$Null,@($Path.FullName,$DBOPENMODE))
$Query = "SELECT Property,Value FROM $TABLENAME"
$View = $Database.GetType().InvokeMember("OpenView",$Database,($Query))
$View.GetType().InvokeMember("Execute",$View,$null)|Out-Null
$record=$view.gettype().invokemember("Fetch",$view,$null)
# Loop thru the table
while($record -ne $null) {
$propName=$null
$propValue=$null
$propName=$record.gettype().invokeMember("StringData",$record,1)
$propValue= $record.gettype().invokeMember("StringData",2)
$msiProps[$propName] =$propValue
$record=$view.gettype().invokemember("Fetch",$null)
}
$view.gettype().invokemember("Close",$null)|Out-Null
# Compose a unified object to express the MSI and MSP information
# MSP 'DisplayName','ManufacturerName','Description','MoreInfoURL','TargetProductName'
# MSI 'ProductName','Manufacturer','ProductVersion','ProductCode','UpgradeCode'
if($extension -eq '.msi'){
New-Object -TypeName PSObject -Property @{
'DisplayName'=$msiProps['ProductName']
'Manufacturer'=$msiProps['Manufacturer']
'Version'=$msiProps['ProductVersion']
'PackageCode'=$msiProps['ProductCode']
'Description'=$msiProps['Description']
'TargetProductName'=$msiProps['TargetProductName']
'MoreInfoURL'=$msiProps['MoreInfoURL']
'Size'=$path.Length
'Path'=$path.FullName
'CreationTime'=$path.CreationTime
}
}elseif($extension -eq ".msp"){
New-Object -TypeName PSObject -Property @{
'DisplayName'=$msiProps['DisplayName']
'Manufacturer'=$msiProps['ManufacturerName']
'Version'=$msiProps['BuildNumber']
'PackageCode'=$msiProps['ProductCode']
'Description'=$msiProps['Description']
'TargetProductName'=$msiProps['TargetProductName']
'MoreInfoURL'=$msiProps['MoreInfoURL']
'Size'=$path.Length
'Path'=$path.FullName
'CreationTime'=$path.CreationTime
}
}
}
} catch {
Write-Error $_.Exception.Message
}
}
function filter_product{
param(
$productName
)
$PRODUCT_FILTER=@("adobe")
$r=$PRODUCT_FILTER|?{$productName -like "*$_*"}
if($r){
return $true
}else{
return $false
}
}
$products=Get-MsiProducts
$patches=$products|%{Get-MsiPatch -product $_.ProductCode}
$productsHash=@{}
$products|?{$_.LocalPackage}|%{$productsHash.add($_.LocalPackage,$true)}
$patchesHash=@{}
$patches|?{$_.LocalPackage}|%{if(!$patchesHash.ContainsKey($_.localPackage)){$patchesHash.add($_.LocalPackage,$true)}}
$InstallFolder="$($env:SystemRoot)installer"
$files=dir -Recurse -Include "*.msi","*.msp" -path $InstallFolder
$Files2=$files|%{
if($productsHash.ContainsKey($_.FullName)){
$_|Add-Member -MemberType NoteProperty -Name "installerState" -Value "InstalledProduct"
}elseif($patchesHash.ContainsKey($_.FullName)){
$_|Add-Member -MemberType NoteProperty -Name "installerState" -Value "InstalledPatch"
}else{
$_|Add-Member -MemberType NoteProperty -Name "installerState" -Value "Orphaned"
}
$_
}
$groups=$files2|Group-Object -Property "installerState"
$groups|%{
@{$($_.name)=($_.group|Measure-Object -Property Length -Sum).Sum}
}
$OrphanedFiles=$($groups|?{$_.name -eq 'Orphaned'}).Group
if($OrphanedFiles){
$ValidOrphanedFiles=($OrphanedFiles|%{
$item=Get-MSIFileInfo -path $_.FullName;
if((filter_product $item.DisplayName) -or (filter_product $item.Manufacturer)){
# do nothing for this filtered products
}else{
$item
}
})
$selectedOrphanedFiles=$ValidOrphanedFiles|select DisplayName,Manufacturer,Size,Path,CreationTime|Out-GridView -PassThru -Title "select the Orphaned Files to delete"
if($ValidOrphanedFiles){
$ValidOrphanedFiles|Export-Csv -Path $PSScriptRootValidOrphanedFiles.$((get-date).ToString('yyyyMMddhhmmss')).csv -NoClobber -NoTypeInformation -Encoding UTF8
}
if($selectedOrphanedFiles){
$selectedOrphanedFiles|Export-Csv -Path $PSScriptRootCleanedOrphanedFiles.$((get-date).ToString('yyyyMMddhhmmss')).csv -NoClobber -NoTypeInformation -Encoding UTF8
# delete code
#$selectedOrphanedFiles|remove-item -Force
}
}
需要进一步深挖使用上面的powershell 脚本在另外一台计算机运行,发现输出如下图,大部分是office的更新,还有4个关于7zip的,所以我又瞄了一眼添加删除程序里的信息。 (编辑:甘南站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- windows – 用户和内核之间的线程fs段寄存器切换
- Windows下wamp php单元测试工具PHPUnit安装及生成日志文件配
- 找出占用Installer 目录空间的元凶
- 深度技术 Win11 64位全新系统下载
- Qt 5 QML应用程序,包含许多Windows或复杂的UI
- 身份验证错误 要求的函数不受支持 Windows远程桌面连接
- Windows – Win7 Virtualbox在尝试启动虚拟机时出现此错误:
- Windows BATCH:如何为单个脚本禁用QuickEdit模式?
- ms-office – Microsoft Office 2010功能区自定义UI中的Pha
- NonSerialized 字段如果在dll 中读到数据写入MonoClassFiel
