Powershell: Openfile dialog and Set-Content Issue. Cannot access file. In use - powershell

What I'm trying to do is utilize the openfile dialog, select an ini file and make line changes to it at the end of this script with set-content. But I keep getting the error that Set-Content : The process cannot access the file, and that it's in use.
$a = $env:userprofile
Function Get-FileName($InitialDirectory)
{
Get-FileName -InitialDirectory "$a\AppData\Roaming\Milliman"
}#end function Get-FileName
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
$dialog = New-Object System.Windows.Forms.OpenFileDialog
$dialog.DefaultExt = '.*'
$dialog.Filter = 'All Files|*.*'
$dialog.FilterIndex = 0
$dialog.InitialDirectory = $InitialDirectory
$dialog.Multiselect = $false
$dialog.RestoreDirectory = $true
$dialog.Title = "Select a file"
$dialog.ValidateNames = $true
$dialog.ShowHelp = $true
$dialog.ShowDialog()
$dialog.FileName
##Folder Dialog
$dir = new-object -com Shell.Application
$aldir = $dir.BrowseForFolder(0, "AL Dir", 0, "C:\Program Files\Milliman\")
if ($aldir.Self.Path -ne "") {write-host "You selected " $aldir.Self.Path}
## Grid Integration Steps
Copy-Item -path "\\ap102aric\alfaadmin$\Ver70andAbove\DataSynapse\*" -destination "C:\Program Files\Common Files\Milliman\MG-ALFA Shared\DataSynapse" -Force
Copy-Item -path "\\ap102aric\alfaadmin$\Ver70andAbove\JobOptions-RPRic\*" -destination "C:\Program Files\Common Files\Milliman\MG-ALFA Shared\DataSynapse" -Force
Copy-Item -path "\\ap102aric\alfaadmin$\Ver70andAbove\GSDLL\dsdrv.dll" -Destination $aldir.Self.Path -Force
## Set Environment Variable
[Environment]::SetEnvironmentVariable("DSDRIVER_DIR","C:\Program Files\Common Files\Milliman\MG-ALFA Shared\DataSynapse\Config","Machine")
## Edit Config UI.ini to set SDP LOGON for Datasynapse
#Write-Host $dialog.FileName
Get-Content $dialog.FileName | ForEach-Object {
$_ -replace 'SDPAvailable=*','SDPAvailable=DataSynapse'
-replace 'SDPFolder=*','SDPFolder=C:\Program Files\Common Files\Milliman\MG-ALFA Shared\DataSynapse'
-replace 'SDPLogon=*','SDPAvailable=Yes'
} | Set-Content $dialog.FileName

Try destroying the $aldir object. It may be holding a handle to the file. I'm not sure how to do that. Maybe set it to $null after you grab the path the user selected.
You can also try using Process Monitor to figure out what process is locking the file.
Finally, you can't pipe the output from Get-Content to Set-Content, e.g.
Get-Content $Path | Set-Content $Path
Items are sent down the PowerShell pipeline immediately, so when Get-Content reads a line, it immediately gets set to Set-Content, which won't work because Get-Content has the file open. Instead, try saving the contents of the file:
$file = Get-Content $path
# Modify $file
$file | Set-Content $path

Related

Compact and Repair all Access databases in a directory using Powershell

I am looking to find a way to compact and repair all the Access databases in a certain directory using Powershell via a script.
The VBA codes below work, but need one for Powershell:
Find all Access databases, and Compact and Repair
I am new to Powershell so will be grateful for the assistance.
Thanks
You may try this.
Add-Type -AssemblyName Microsoft.Office.Interop.Access
$rootfolder = 'c:\some\folder'
$createlog = $true # change to false if no log desired
$access = New-Object -ComObject access.application
$access.Visible = $false
$access.AutomationSecurity = 1
Get-ChildItem -Path $rootfolder -File -Filter *.accdb -Recurse -PipelineVariable file | ForEach-Object {
$newname = Join-Path $file.Directory ("{0}_compacted{1}" -f $file.BaseName,$file.Extension)
$message = #"
Current file: {0}
Output file: {1}
"# -f $file.FullName,$newname
Write-Host $message -ForegroundColor Cyan
$access.CompactRepair($file.fullname,$newname,$createlog)
}
$access.Quit()
This will output each compacted database as the name of the original file with _compacted appended to the name (before the extension.) I have tested this in every way except actually compacting databases.
Edit
Regarding your comment, a few minor changes should achieve the desired result. Keep in mind that this will put all new files in the same folder. This may not be an issue for your case but if there are duplicate file names you will have problems.
$rootfolder = 'c:\some\folder'
$destination = 'c:\some\other\folder'
$todaysdate = get-date -format '_dd_MM_yyyy'
Add-Type -AssemblyName Microsoft.Office.Interop.Access
$createlog = $true # change to false if no log desired
$access = New-Object -ComObject access.application
$access.Visible = $false
$access.AutomationSecurity = 1
Get-ChildItem -Path $rootfolder -File -Filter *.accdb -Recurse -PipelineVariable file | ForEach-Object {
$newname = Join-Path $destination ("{0}$todaysdate{1}" -f $file.BaseName,$file.Extension)
$message = #"
Current file: {0}
Output file: {1}
"# -f $file.FullName,$newname
Write-Host $message -ForegroundColor Cyan
$access.CompactRepair($file.fullname,$newname,$createlog)
}
$access.Quit()

Powershell Script - Find the a list with path of the password protected .xlsx AND .xls files in a network folder

I am currently working on finding a way to get a list with path of all the .xlsx and .xls file that are password protected in a network drive that contains tons and tons of folders and sub folders. I put together this script below that works fine, but it only returns .xlsx files, none of the .xls files with password protected were returned. I am wondering if anyone knows how to get the .xls file with password or any other script that would get this job done? Appreciate all your help!
Script
$path = "C:\Users\DC\Desktop"
$dest = "C:\Users\DC\Desktop\ExcelWithPassword.txt"
$Full = Get-ChildItem $path -Include *.xlsx*, *.xls* -Recurse -ErrorAction SilentlyContinue
$List = select-string -pattern "<encryption" $Full
foreach ($file in $List) {
$file.path | Out-File $dest -Append -Force
}
The output is basically a list of paths where those password protected files are located.
unless you have other files in the target directory tree, with an '.xl extension. Why are you doing this ...
Get-ChildItem $path -Include *.xlsx*, *.xls* -Recurse -ErrorAction SilentlyContinue
... you only need this...
Get-ChildItem $path -Include *.xl* -Recurse -ErrorAction SilentlyContinue
If you are after just the full path, ask for it, using this ...
Get-ChildItem $path -Include *.xl* -Recurse -ErrorAction SilentlyContinue |
Select-Object -Property Fullname
# Results
<#
FullName
--------
D:\Temp\NewFolder\Test.xlsx
D:\Temp\Test.xls
D:\Temp\Test.xlsx
#>
... or this.
(Get-ChildItem $path -Include *.xl* -Recurse -ErrorAction SilentlyContinue).FullName
# Results
<#
D:\Temp\NewFolder\Test.xlsx
D:\Temp\Test.xls
D:\Temp\Test.xlsx
#>
As far as the loop, you can also shorten your code to something similar.
(Get-ChildItem $path -Include *.xl* -Recurse -ErrorAction SilentlyContinue |
Select-Object -Property Fullname) -match '<encryption' |
Out-File $dest -Append -Force
Or
(Get-ChildItem $path -Include *.xl* -Recurse -ErrorAction SilentlyContinue).FullName -match '<encryption' |
Out-File $dest -Append -Force
You are not saying how the files were encrypted. Excel allows for protecting the sheet, the workbook, etc. You can't check a password-protected file by searching for a string without opening the file. To open the file you must use the application interface to open the file. For Excel it's:
### Automate Excel
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$workbook1 = $excel.Workbooks.Add()
$Sheet = $Excel.WorkSheets.Item(1)
$Sheet.Cells.Item(1,1) = "Hello from Powershell "
$Sheet.Cells.Item(1,2) = "Using VBA from Excel Button object"
Based on what you are after, there are a few other considerations you must have. Scanning and doing this across the whole network and thousands of files requires planning, and parallel processing.

Using powershell to batch convert docx to pdf

I'm attempting to use powershell to batch convert a lot of docx into pdf, into a different directory while maintaining the folder structure of the root.
I have the script working, however around 1 out of every 10 documents word pops up a "SaveAs" dialog, which i do not understand prompting me to save the docx file, although i have visible set to false.
#Stage the files
$sourceDir = "C:\Documents\"
$targetDir = "C:\Temp_Stage\"
Get-ChildItem $sourceDir -filter "*.doc?" -recurse | foreach{
$targetFile = $targetDir + $_.FullName.SubString($sourceDir.Length);
New-Item -ItemType File -Path $targetFile -Force;
Copy-Item $_.FullName -destination $targetFile
}
#Convert the files
$wdFormatPDF = 17
$word = New-Object -ComObject word.application
$word.visible = $false
$folderpath = "c:\Temp_Stage\*"
$fileTypes = "*.docx","*doc"
Get-ChildItem -path $folderpath -include $fileTypes -Recurse |
foreach-object {
$path = ($_.fullname).substring(0,($_.FullName).lastindexOf("."))
$doc = $word.documents.open($_.fullname)
$doc.saveas([ref] $path, [ref]$wdFormatPDF)
$doc.close()
}
$word.Quit()
Is there a way to suppress all word dialogs / warning / errors it should be a fairly automatic process that has ended up being pretty manual process.
I found out that you should pause between the Word-COM commands.
I also had to write a script that Word converts the documents from. dot to. dotm.
Not only did I occasionally get the save dialog, but also a lot of E_FAIL errors in the console.
The breaks (maximum 50ms) helped a lot.
Break in Powershell:
Start-Sleep -m 50
I hope it will help you.
Greetings
Bloodrayne1995

powershell backup script with error logging per file

Really need help creating a script that backs up, and shoots out the error along the file that did not copy
Here is what I tried:
Creating lists of filepaths to pass on to copy-item, in hopes to later catch errors per file, and later log them:
by using $list2X I would be able to cycle through each file, but copy-item loses the Directory structure and shoots it all out to a single folder.
So for now I am using $list2 and later I do copy-item -recurse to copy the folders:
#create list to copy
$list = Get-ChildItem -path $source | Select-Object Fullname
$list2 = $list -replace ("}"),("")
$list2 = $list2 -replace ("#{Fullname=") , ("")
out-file -FilePath g:\backuplog\DirList.txt -InputObject $list2
#create list crosscheck later
$listX = Get-ChildItem -path $source -recurse | Select-Object Fullname
$list2X = $listX -replace ("}"),("")
$list2X = $list2X -replace ("#{Fullname=") , ("")
out-file -FilePath g:\backuplog\FileDirList.txt -InputObject $list2X
And here I would pass the list:
$error.clear()
Foreach($item in $list2){
Copy-Item -Path $item -Destination $destination -recurse -force -erroraction Continue
}
out-file -FilePath g:\backuplog\errorsBackup.txt -InputObject $error
Any help with this is greatly appreciated!!!
The answer to complex file-copying or backup scripts is almost always: "Use robocopy."
Bill
"Want to copy all the items in C:\Scripts (including subfolders) to C:\Test? Then simply use a wildcard character..."
Next make it easier on yourself and do something like this:
$files = (Get-ChildItem $path).FullName #Requires PS 3.0
#or
$files = Get-ChildItem $path | % {$_.Fullname}
$files | Out-File $outpath
well it took me a long time, considering my response time. here is my copy function, which logs most errors(network drops, failed copies , etc) the copy function , and targetobject.
Function backUP{ Param ([string]$destination1 ,$list1)
$destination2 = $destination1
#extract new made string for backuplog
$index = $destination2.LastIndexOf("\")
$count = $destination2.length - $index
$source1 = $destination2.Substring($index, $count)
$finalstr2 = $logdrive + $source1
Foreach($item in $list1){
Copy-Item -Container: $true -Recurse -Force -Path $item -Destination $destination1 -erroraction Continue
if(-not $?)
{
write-output "ERROR de copiado : " $error| format-list | out-file -Append "$finalstr2\GCI-ERRORS-backup.txt"
Foreach($erritem in $error){
write-output "Error Data:" $erritem.TargetObject | out-file -Append "$finalstr2\GCI- ERRORS-backup.txt"
}
$error.Clear()
}
}
}

Powershell - How to complete/close a zip file in powershell

I want to create a zip file in powershell, add items to the zip file, then get the compressed content of that zip file as bytes immediately after the zip file is created, in the same scipt. The problem is that it does not seem that the zip application has written its contents to the file system. How does one close/flush the zip application so that the next powershell statement can gain access to the newly created zip file?
Example:
new-item -type File test.zip -force
$zip = ( get-item test.zip ).fullname
$app = new-object -com shell.application
$folder = $app.namespace( $zip )
$item = new-item file.txt -itemtype file -value "Mooo" -force
$folder.copyhere( $item.fullname )
dir test.zip # <---- Empty test.zip file
Get-Content -Encoding byte $zip | echo # <-- nothing echoed
The "dir test.zip" shows a zip file with no contents, thus the Get-Content returns nothing.
Please note that this seems to be a problem with the asynchronous behavior of the copyhere action. If I sleep after the copyhere line, the zip file will become populated. However, I do not know how long one must sleep, nor do I want to delay the processing.
Much Thanks in advance!
You might want to reconsider using a third party library. However, if you must use copyhere, try this:
new-item -type File test.zip -force
$zip = ( get-item test.zip ).fullname
$app = new-object -com shell.application
$folder = $app.namespace( $zip )
$item = new-item file.txt -itemtype file -value "Mooo" -force
$folder.copyhere( $item.fullname)
while($folder.Items().Item($item.Name) -Eq $null)
{
start-sleep -seconds 0.01
write-host "." -nonewline
}
dir test.zip # <---- Empty test.zip file
Get-Content -Encoding byte $zip
I also had this problem, all that you need is to wait until zipping operation is not completed. So, i come up with this solution, you should place this code after executing "$folder.copyhere" or "Copy-ToZip" powerpack cmdlet.
$isCompleted = $false
$guid = [Guid]::NewGuid().ToString()
$tmpFileName = $zipFileName + $guid
# The main idea is to try to rename target ZIP file. If it success then archiving operation is complete.
while(!$isCompleted)
{
start-sleep -seconds 1
Rename-Item $zipFileName $tmpFileName -ea 0 #Try to rename with suppressing errors
if (Test-Path $tmpFileName)
{
Rename-Item $tmpFileName $zipFileName #Rename it back
$isCompleted = $true
}
}
This method to zip files is not appropriate for automated scripts. This has limitations in Windows 2003 and Windows xp server for 3 gigs. Also, it does not give proper errors.
Create the file in a closure, so that the variable goes out of scope and powershell closes the file... if you'd made that part into a subroutine then it would have closed naturally when you returned.
new-item -type File test.zip -force
{
$zip = ( get-item test.zip ).fullname
$app = new-object -com shell.application
$folder = $app.namespace( $zip )
$item = new-item file.txt -itemtype file -value "Mooo" -force
$folder.copyhere( $item.fullname )
}
dir test.zip # <---- Empty test.zip file
Get-Content -Encoding byte $zip
try creating the zip empty file like this:
$zipfilename = "myzip.zip"
set-content $zipfilename ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
Then the rest of your code.
this code in my box works:
$zipfilename = "a.zip"
set-content $zipfilename ("PK" + [char]5 + [char]6 + ("$([char]0)" * 18))
$app = new-object -com shell.application
$zip = ( get-item a.zip ).fullname
$folder = $app.namespace( $zip )
$item = new-item file.txt -itemtype file -value "Mooo" -force
$folder.copyhere( $item.fullname )
to get the content of a zip use this:
$zipfilename = "myzip.zip"
$shellApplication = new-object -com shell.application
$zipPackage = $shellApplication.NameSpace($zipfilename)
$zipPackage.Items() | Select Path