Convert DOC with macro to DOTM - powershell

I am trying to convert DOC files with macros to DOTM with macros. My code changes the files but after the marco section is totally broken. When I convert it manually my macro code stays.
My code is:
function ReleaseRef ($ref) {
if($ref){
([System.Runtime.InteropServices.Marshal]::ReleaseComObject(
[System.__ComObject]$ref) -gt 0)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
}
function Convert-DOC{
Param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$filepath,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$filefilter
)
try {
$files = Get-ChildItem $filepath -Include "$filefilter" -recurse -ErrorAction SilentlyContinue | Where-Object { ($_.PSIsContainer -eq $false) -and ( $_.Extension -like "$filefilter") }
$totalfiles = $files.Count
[int]$currentfile = 0
Write-Host "converting files... [$totalfiles]"
#word object********************************
#load dotnet assembly
Add-Type -AssemblyName Microsoft.Office.Interop.Word
#create word object
$word = New-Object -ComObject Word.Application -Verbose:$false
$word.visible = $true
$word.DisplayAlerts = [Microsoft.Office.InterOp.Word.WdAlertLevel]::wdAlertsNone
foreach ($file in $files) {
#Current file number
[int]$currentfile = [int]$currentfile + 1
#Check for password
$catch = $false
try {
#open file
Write-Host $file
$worddoc = $word.Documents.Open($file.FullName, $null, $null, $null, "")
} catch {
#if error, file has password
Write-Host "$file is protected by Password, skipping..." -ForegroundColor Yellow
$catch = $true
continue
}
if ($catch -eq $false) {
try {
#**********convert file**********
write-host "converting " $file.fullname "file $currentfile of $totalfiles" -ForegroundColor DarkGreen
#check for links
if([IO.Path]::GetExtension($file) -eq ".doc"){
#check for macros in in file
if ($worddoc.HasVBProject) {
$doFixedFormat = [Microsoft.Office.Interop.Word.WdSaveFormat]::wdFormatXMLDocumentMacroEnabled
$newfile = ($file.fullname).substring(0, ($file.FullName).lastindexOf("."))
$newfile += ".docm"
} else {
$doFixedFormat = [Microsoft.Office.Interop.Word.XlFileFormat]::wdFormatXMLDocument
$newfile = ($file.fullname).substring(0, ($file.FullName).lastindexOf("."))
$newfile += ".docx"
}
}
#save file
if(Test-Path $newfile){
Write-host "$newfile already exists" -ForegroundColor Yellow
} else {
$worddoc.SaveAs($newfile, $doFixedFormat )
}
#close file
$worddoc.close()
Write-Host "done" -ForegroundColor DarkGreen
#**********Garbage Collector**********
$gc_int++
if([int]$gc_int -gt 5){
Write-Host 'Run Garbage Collector' -ForegroundColor DarkBlue -BackgroundColor White
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
[int]$gc_int = 0
}
} catch {
$formatstring = "{0} : {1}`n{2}`n" +
" + CategoryInfo : {3}`n" +
" + FullyQualifiedErrorId : {4}`n" +
" + Filename : {5}`n"
$fields = $_.InvocationInfo.MyCommand.Name,
$_.ErrorDetails.Message,
$_.InvocationInfo.PositionMessage,
$_.CategoryInfo.ToString(),
$_.FullyQualifiedErrorId,
$file.fullname
Write-Host -Foreground Red -Background Black ($formatstring -f $fields)
"$fields" | Out-File ($scriptpath + '\error_convert.log') -Append
}
}
}
} finally {
#**********clean-up************
Write-Host ""
Write-Host "Cleaning Up" -ForegroundColor DarkMagenta
Write-Host "Quiting Word"
$word.Quit()
Write-Host "Garbage Collector"
[gc]::collect()
[gc]::WaitForPendingFinalizers()
Write-Host "Release Com Object Workbook"
$a = ReleaseRef($worddoc)
Write-Host "Release Com Object Word"
$a = ReleaseRef($word)
Write-Host "Finishing Clean-Up"
}
}
Convert-DOC -filepath "C:\_testmacro\" -filefilter "*.doc"
What I do is, checking if the file has a VB part and is so setting the extension:
[Microsoft.Office.Interop.Word.WdSaveFormat]::wdFormatXMLDocumentMacroEnabled
and then save it:
$worddoc.SaveAs($newfile, $doFixedFormat)
A similar function with XLS and XLAM works fine
Edit:
seems like I was missing a $worddoc.Convert() but now struggling with checkin/checkout

As my edit stated, I missed the convert command:
$worddoc.Convert()
$worddoc.SaveAs($newfile, $doFixedFormat)
before saving

Related

Powershell - Move Mail

I'm newbie to powershell, and I have a challenge on process my script. It did everything perfectly, but it wont move the message to the target folder and I don't know why.
I thought maybe to process at end cycle, but the issue is that I can encour that will transfer also file that have not any reference, and I would avoid to process something that I have no info about.
I tried this
$MailboxName = "fatture#abc.com"
write-host "Now Processing" -ForegroundColor Yellow
write-host "Searching on Fatture" -ForegroundColor Yellow
$olFolderInbox = 6
$outlook = new-object -com outlook.application;
$namespace = $outlook.GetNameSpace("MAPI");
$recipient = $namespace.CreateRecipient($MailboxName)
$inbox = $namespace.GetSharedDefaultFolder($recipient,6)
$messages = $inbox.Folders.Item("Carriers").folders.Item("Gpac").folders.item("BLR")
$MoveTarget = $inbox.Folders.Item("Carriers").folders.Item("Gpac").folders.item("BLS")
$filepath = "C:\temp\FATTURE\Automation\"
$messages.Items | % {
Write-Host "New Message " $_.Subject
$msg = $_
$Ref = $_.Subject -replace ' ',''
$RefStr = $ref.split('#')
$SearchSTR = $RefStr[2]
write-host $SearchStr
# $invNbr = $body.Substring($body.IndexOf("INVOICE NO")+19,10)
#write-host "The Invoice Number is : $invNbr" -ForegroundColor Cyan
write-host "Querying for $SearchSTR" -ForegroundColor Green
$t = (Query_file($SearchSTR))[-1].CONSOL_REF
write-host "File Found : $t" -ForegroundColor Cyan
write-host $t
if ($t -ne $null) {
$filetype = $t.Substring(0,1)
$date = Get-Date -format "yyyyMMddhhmmss"
write-host "Today is Date is $date" -ForegroundColor Red
Switch($filetype.tostring())
{
{($_ -eq 1) -or ($_ -eq 2) -or ($_ -eq 7) -or ($_ -eq 7) -or ($_ -eq 8) }{($dept = "0001"), ($dept1 = "ZLAG")}
{($_ -eq "F") -or ($_ -eq "V") -or ($_ -eq "M")}{($dept = "0002"), ($dept1 = "Zero"), ($type = "MOR")}
{($_ -eq 3) -or ($_ -eq 5) -or ($_ -eq "J")} {($dept = "0028"), ($dept1 = "One"), ($type = "OBR") }
{($_ -eq 9)} {($dept = "0031"), ($dept1 = "CONSOLE")}
}
write-host "The KEY TYPE is $dept1 " -ForegroundColor Yellow
$fileStr = $t + "_INV_" + $dept + "_$date_.tsk"
write-host " FILE Path: " $fileStr -ForegroundColor Cyan
$msg.attachments | ?{$_.filename -like "*.pdf"} | %{
$file = $filestr
$_.saveasfile((Join-Path $filepath $filestr))
}
$msg.UnRead = $false
$msg.Move($MoveTarget)
}
#fine cycle messages
}
I tried to move from the cycle but the issue is that it goes for all of email, instead, I want to process only the email that should be. I would like when the attachment has being downloaded, that the email will be moved.

Powershell: Delete class from JAR file with invoke-command

I am trying to remove vulnerable classes from the log4j jar file with powershell.
I am able to remove the file using the script locally on the server, however, I want to remove the class from many paths on many servers and trying to use invoke-command.
The script can open and read the JAR file but doesnt seem to action the delete() method. Is there a way to get powershell to delete the class "remotely"?
Here is my script:
$servers = #(
"server"
)
$class_to_delete = "JMSSink"
$unable_to_connect = #()
Add-Type -AssemblyName System.IO.Compression
Add-Type -AssemblyName System.IO.Compression.Filesystem
write-host "`nTesting connection to:" -ForegroundColor Yellow
$servers | ForEach-Object {Write-Host "$_"}
$servers | ForEach-Object {
$server = $_
try {
write-host "`nTesting $($server)"
Invoke-Command -ComputerName $server -ScriptBlock {
Write-Host "Connection successful to $($env:computername)" -ForegroundColor Green
} -ErrorAction Stop
} catch {
write-host "`nConnection failed to $($server)"
$unable_to_connect += $server
}
}
Write-Host "`nStarting script to remove $($class_to_delete) class from log4j" -ForegroundColor Yellow
$objects_skipped = #()
$servers | ForEach-Object {
$server_node = $_
write-host "`nPut in the file paths for $($server_node)" -ForegroundColor Yellow
$file_locations = (#(While($l=(Read-Host).Trim()){$l}) -join("`n"))
$file_locations | Out-File C:\temp\output.txt #Change this path to the temp folder and file on the server you execute from
$file_objects = Get-Content -Path C:\temp\output.txt #Change this path to the temp folder and file on the server you execute from
$stats_x = foreach ($file_object in $file_objects) {
$stats = Invoke-Command -ComputerName $server_node -ScriptBlock {
Write-Host "`nStarting on $($env:COMPUTERNAME)"
$class = $using:class_to_delete
Add-Type -AssemblyName System.IO.Compression
Add-Type -AssemblyName System.IO.Compression.Filesystem
$ful_path = $using:file_object
$fn = Resolve-Path $ful_path
try {
$zip = [System.io.Compression.ZipFile]::Open("$fn", "Read")
Write-Host "Backing up $($fn) to $($fn).bak"
$zip.Dispose()
Copy-Item "$fn" "$($fn).bak"
$zip = [System.io.Compression.ZipFile]::Open($fn, "Update")
$files = $zip.Entries | Where-Object { $_.name -eq "$($class).class" }
if (!$files) {
write-host "`nNo $($class) class found on $($env:COMPUTERNAME) for path: $($ful_path)"
$files.dispose()
$not_found = #()
$not_found += New-Object -TypeName PSObject -Property #{
Server = $env:COMPUTERNAME;
Path = $ful_path;
Result = "$($class) class NOT FOUND"
}
Write-Output $not_found
} else {
foreach ($file in $files) {
write-host "`n$($class) class found on $($env:COMPUTERNAME) for path: $($ful_path)"
write-host "`nDeleting file $($file)" -ForegroundColor Green
#delete class
$file.delete()
#check if class was successfully deleted
$confirm_delete = $zip.Entries | Where-Object { $_.name -eq "$($class).class" }
write-host $confirm_delete
if ($confirm_delete.name -match "$class.class") {
$deleted_status = "$($class) !!NOT REMOVED!!"
} else {
$deleted_status = "$($class) REMOVED"
}
$Output = #()
$Output += New-Object -TypeName PSObject -Property #{
Server = $env:COMPUTERNAME;
Path = $ful_path;
Result = $deleted_status
}
Write-Output $Output
}
}
} catch {
Write-Host "Cannot open $($ful_path) as a Zip File. $($Error[0])"
}
}
Write-Output $stats
}
$objects_skipped += $stats_x
}
#result
write-host "`nEnd result"
$objects_skipped | select Server,Result,Path | ft -AutoSize
You need to explicitly call Dispose() on the containing archive to persist the updates to the file on disk:
# this goes immediately after the `catch` block:
finally {
if($zip -is [IDisposable]){ $zip.Dispose() }
}
By placing the call to $zip.Dispose() inside a finally block, we ensure that it's always disposed regardless of whether an exception was thrown in the preceding try block.

Logging actual error when script fails

I have a script here that reads a list of computers and changes the administrator password. When the script is ran, it'll have a log file that says whether the task succeeded or fail. But I also want to log the actual error to the log when it fails. How do I accomplish this?
[cmdletbinding()]
param (
[parameter(mandatory = $true)]
$InputFile,
$OutputDirectory
)
if(!$outputdirectory) {
$outputdirectory = (Get-Item $InputFile).directoryname
}
$failedcomputers = Join-Path $outputdirectory "output.txt"
$stream = [System.IO.StreamWriter] $failedcomputers
$stream.writeline("ComputerName `t IsOnline `t PasswordChangeStatus")
$stream.writeline("____________ `t ________ `t ____________________")
$password = Read-Host "Enter the password" -AsSecureString
$confirmpassword = Read-Host "Confirm the password" -AsSecureString
$pwd1_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))
$pwd2_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($confirmpassword))
if($pwd1_text -ne $pwd2_text) {
Write-Error "Entered passwords are not same. Script is exiting"
exit
}
if(!(Test-Path $InputFile)) {
Write-Error "File ($InputFile) not found. Script is exiting"
exit
}
$Computers = Get-Content -Path $InputFile
foreach ($Computer in $Computers) {
$Computer = $Computer.toupper()
$Isonline = "OFFLINE"
$Status = "SUCCESS"
Write-Verbose "Working on $Computer"
if((Test-Connection -ComputerName $Computer -count 1 -ErrorAction 0)) {
$Isonline = "ONLINE"
Write-Verbose "`t$Computer is Online"
} else { Write-Verbose "`t$Computer is OFFLINE" }
try {
$account = [ADSI]("WinNT://$Computer/username,user")
$account.psbase.invoke("setpassword",$pwd1_text)
Write-Verbose "`tPassword Change completed successfully"
}
catch {
$status = "FAILED"
Write-Verbose "`tFailed to Change the administrator password. Error: $_"
}
$obj = New-Object -TypeName PSObject -Property #{
ComputerName = $Computer
IsOnline = $Isonline
PasswordChangeStatus = $Status
}
$obj | Select ComputerName, IsOnline, PasswordChangeStatus
if($Status -eq "FAILED" -or $Isonline -eq "OFFLINE") {
$stream.writeline("$Computer `t $isonline `t $status")
}
}
$stream.close()
Write-Host "`n`nFailed computers list is saved to $failedcomputers"
Change this:
catch {
$status = "FAILED"
Write-Verbose "`tFailed to Change the administrator password. Error: $_"
}
to this:
catch {
$status = "FAILED"
Write-Verbose "`tFailed to Change the administrator password. Error: $_"
$errmsg = $_.Exception.Message
}
to preserve the error message(s). And change this:
if($Status -eq "FAILED" -or $Isonline -eq "OFFLINE") {
$stream.writeline("$Computer `t $isonline `t $status")
}
to this:
if($Status -eq "FAILED" -or $Isonline -eq "OFFLINE") {
$stream.writeline("$Computer `t $isonline `t $status `t $errmsg")
}
to include the error message in the log.
If you want to unroll inner exceptions as well you can use this:
$e = $_.Exception
$errmsg = $e.Message
while ($e.InnerException) {
$e = $e.InnerException
$errmsg = "`n" + $e.Message
}
instead of just
$errmsg = $_.Exception.Message

How to change custom properties for many Word documents

I found very helpful informations to get started with the script here:
http://blogs.technet.com/b/heyscriptingguy/archive/2010/04/06/hey-scripting-guy-how-can-i-add-custom-properties-to-a-microsoft-word-document.aspx
But I just can't do the same for several Word documents - for exemple 3 or 4 word documents in the same folder.
I tried the command ForEach but I always got an error message.
Could someone help me how to modify the following script in order to take into consideration all the word documents in the path folder?
$path = "C:\fso\Test.docx"
$application = New-Object -ComObject word.application
$application.Visible = $false
$document = $application.documents.open($path)
$binding = "System.Reflection.BindingFlags" -as [type]
$customProperties = $document.CustomDocumentProperties
$typeCustomProperties = $customProperties.GetType()
$CustomProperty = "Client"
$Value = "My_WayCool_Client"
[array]$arrayArgs = $CustomProperty,$false, 4, $Value
Try {
$typeCustomProperties.InvokeMember(`
"add", $binding::InvokeMethod,$null,$customProperties,$arrayArgs) |
out-null
} Catch [system.exception] {
$propertyObject = $typeCustomProperties.InvokeMember(`
"Item", $binding::GetProperty, $null, $customProperties, $CustomProperty)
$typeCustomProperties.InvokeMember(`
"Delete", $binding::InvokeMethod, $null, $propertyObject, $null)
$typeCustomProperties.InvokeMember(`
"add", $binding::InvokeMethod, $null, $customProperties, $arrayArgs) |
Out-Null
}
$document.Saved = $false
$document.save()
$application.quit()
$application = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
I also tried this
Get-ChildItem -path $path | Where-Object { $_.Name -like '*.docx' }
and the ForEach cmdlet.
As Matt suggested, I would put in a ForEach loop after the application is open. I would also add closing the current document within the ForEach loop. Something like:
$path = "C:\fso\*.docx"
$application = New-Object -ComObject word.application
$application.Visible = $false
ForEach($File in (GCI $path|Select -Expand FullName)){
$document = $application.documents.open($file)
$binding = "System.Reflection.BindingFlags" -as [type]
<Other Commands>
$document.Saved = $false
$document.save()
$document.Close()
}
$application.quit()
$application = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
Here's how I was able to do it:
#Set the PATH variable to the location where you saved the script and CSV file
$path = "c:\temp\PowerShell Scripts\"
#Set the DOC variable to the location of the document you want to update
$doc = "c:\temp\test.docx"
$application = New-Object -ComObject word.application
$application.Visible = $false
$document = $application.documents.open($doc)
$binding = "System.Reflection.BindingFlags" -as [type]
$customProperties = $document.CustomDocumentProperties
$typeCustomProperties = $customProperties.GetType()
$CustomPropertiesWorklist = Import-Csv $path\args.csv
if($CustomPropertiesWorklist.Count){
for($i = 0; $i -lt $CustomPropertiesWorklist.Count; $i++)
{
$CustomProperty = $CustomPropertiesWorklist[$i].CP
$msoPropertyType = $CustomPropertiesWorklist[$i].Type
$Value = $CustomPropertiesWorklist[$i].Value
[array]$arrayArgs = $CustomProperty,$false,$msoPropertyType,$Value
Try
{
$typeCustomProperties.InvokeMember(`
"add", $binding::InvokeMethod,$null,$customProperties,$arrayArgs) |
out-null
}
Catch [system.exception]
{
$propertyObject = $typeCustomProperties.InvokeMember(`
"Item", $binding::GetProperty,$null,$customProperties,$CustomProperty)
$typeCustomProperties.InvokeMember(`
"Delete", $binding::InvokeMethod,$null,$propertyObject,$null)
$typeCustomProperties.InvokeMember(`
"add", $binding::InvokeMethod,$null,$customProperties,$arrayArgs) |
Out-Null
}
}
}
else
{
$CustomProperty = $CustomPropertiesWorklist.CP
$msoPropertyType = $CustomPropertiesWorklist.Type
$Value = $CustomPropertiesWorklist.Value
[array]$arrayArgs = $CustomProperty,$false,$msoPropertyType,$Value
Try
{
$typeCustomProperties.InvokeMember(`
"add", $binding::InvokeMethod,$null,$customProperties,$arrayArgs) |
out-null
}
Catch [system.exception]
{
$propertyObject = $typeCustomProperties.InvokeMember(`
"Item", $binding::GetProperty,$null,$customProperties,$CustomProperty)
$typeCustomProperties.InvokeMember(`
"Delete", $binding::InvokeMethod,$null,$propertyObject,$null)
$typeCustomProperties.InvokeMember(`
"add", $binding::InvokeMethod,$null,$customProperties,$arrayArgs) |
Out-Null
}
}
$document.Saved = $false
$document.save()
$application.quit()
$application = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
I adapted and combined some of the solutions I found in this thread and others to create what I think is how a script like this should behave, i.e. change multiple custom properties in multiple word documents and automatically update the actual fields in the documents. Hopefully this will help others too!
You just need to change the list of properties that will be added / updated and set the path to the folder where your .docx files are, and it should handle the rest.
#Comment out (or remove, you barbarian) the properties that do not need updating
$propertiesToUpdate = #{
"Product Name" = "Amazing Product"
"Project Name" = "Best Project"
"Revision (Record)" = "01.00"
"Approved By (Record)" = "Me"
"Date Approved (Record)" = "10.03.2016"
}
#Update the path to the documents to update:
$path = "C:\path\*.docx"
Write-Host -ForegroundColor Cyan "Loading Application..."
$application = New-Object -ComObject word.application
$application.Visible = $false
function AddOrUpdateCustomProperty ($CustomPropertyName, $CustomPropertyValue, $DocumentToChange)
{
$customProperties = $DocumentToChange.CustomDocumentProperties
$typeCustomProperties = $customProperties.GetType()
$binding = "System.Reflection.BindingFlags" -as [type]
[array]$arrayArgs = $CustomPropertyName,$false, 4, $CustomPropertyValue
Try
{
$typeCustomProperties.InvokeMember("add", $binding::InvokeMethod,$null,$customProperties,$arrayArgs) | out-null
}
Catch [system.exception]
{
$propertyObject = $typeCustomProperties.InvokeMember("Item", $binding::GetProperty, $null, $customProperties, $CustomPropertyName)
$typeCustomProperties.InvokeMember("Delete", $binding::InvokeMethod, $null, $propertyObject, $null)
$typeCustomProperties.InvokeMember("add", $binding::InvokeMethod, $null, $customProperties, $arrayArgs) | Out-Null
}
Write-Host -ForegroundColor Green "Success! Custom Property:" $CustomPropertyName "set to value:" $CustomPropertyValue
}
ForEach($File in (GCI $path|Select -Expand FullName))
{
Write-Host -ForegroundColor Cyan "Opening Document..." $File
$document = $application.documents.open($File)
ForEach($property in $propertiesToUpdate.GetEnumerator())
{
AddOrUpdateCustomProperty $($property.Name) $($property.Value) $document
}
Write-Host -ForegroundColor Cyan "Updating document fields."
$document.Fields.Update() | Out-Null
Write-Host -ForegroundColor Cyan "Saving document."
$document.Saved = $false
$document.save()
$document.close()
}
$application.quit()
$application = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
Write-Host -ForegroundColor Green "Done!"
I couldn't get the above to work. Always object.GetType() failed to retrieve anything, resulting in an error. This is what I got to work for BuiltIn properties; same would apply to custom properties:
#Properties to update (BuiltIn)
$propertyUpdates = #{
"Company" = "Company"
"Manager" = "Manager"
}
#Path to the documents to update:
$path = "C:\FilesToUpdate\*.docx"
Write-Host -ForegroundColor Cyan "Loading Application..."
$app = New-Object -ComObject Word.Application
$app.Visible = $false
ForEach($file in (GCI $path|Select -Expand FullName))
{
Write-Host -ForegroundColor Cyan "Opening document: " $file
$doc = $app.Documents.Open($file)
$binding = "System.Reflection.BindingFlags" -as [type]
Write-Host -ForegroundColor Cyan "Updating document properties..."
ForEach($p in $propertyUpdates.GetEnumerator())
{
Try {
$props = $doc.BuiltInDocumentProperties
$prop = [System.__ComObject].InvokeMember("Item", $binding::GetProperty, $null, $props, $p.Name)
[System.__ComObject].InvokeMember("Value", $binding::SetProperty, $null, $prop, $p.Value)
}
Catch [system.exception] {
write-host -ForegroundColor red "Value not found for $p.Name"
}
}
$doc.Fields.Update() | Out-Null
Write-Host -ForegroundColor Cyan "Saving document."
$doc.Saved = $false
$doc.save()
$doc.close()
}
$app.quit()
$app = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
Write-Host -ForegroundColor Green "Done!"

Why does PowerShell script fail to close mspaint when the image's filename contains brackets?

Here's the whole code:
$kansio = ($args[0]).replace("'","")
$tiedostot = 0
Get-ChildItem $kansio | foreach {if ($_.Extension -eq ".jpg"){$_.FullName}} |
foreach {
$id = (Start-process mspaint $_ -ea 0 -PassThru).Id
$vikaTallennus = (Get-ChildItem $_).LastWriteTime
[void] [System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
[void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")
do
{
try
{
if ((Get-Process -Id $id -ea 'stop').WaitForInputIdle())
{
[Microsoft.VisualBasic.Interaction]::AppActivate($id)
[System.Windows.Forms.SendKeys]::SendWait("^s")
[System.Windows.Forms.SendKeys]::SendWait("{esc}")
for ($i=1; $i -le 10; $i++)
{
if ((Get-ChildItem $_).LastWriteTime -gt $vikaTallennus)
{
try
{
Stop-Process -Id $id -force -ea 'stop'
Write-Host "Done: $_" -foregroundcolor Green
break
}
catch
{
Write-Host "Failed to close the process" -foregroundcolor DarkRed
Start-Sleep -milliseconds 100
}
}
}
}
}
catch
{
Write-Host "Failed to catch the process" -foregroundcolor DarkRed
Start-Sleep -milliseconds 100
}
} until ((Get-Process -Id $id -ea 0) -eq $null -or $id -eq $null)
$tiedostot++
}
Write-Host "Files saved: $tiedostot" -foregroundcolor Green
If the filename of a JPG picture contains "[" or "]" PowerShell fails to close MSPaint. Why?
What workarounds are there? Can I use some literalname thing in here?
Yes, if you want to use Get-ChildItem to return files/folders with brackets (and some other weird characters), you should use the -LiteralPath parameter. Here's a decent writeup.
So if you expect such characters in the filenames, and you don't need wildcard support, the pertinent line should be:
if ((Get-ChildItem -LiteralPath $_).LastWriteTime -gt $vikaTallennus)