Powershell close .msg after reading the body - windows

I want to read the Body of the email file (.msg) in a powershell script, however I can only open it once because the file is "locked" or already opened, so there is an error the 2nd time.
My Code:
Get-ChildItem $scriptPath -Filter *.msg |
ForEach-Object {
$outlook = New-Object -comobject outlook.application
$msg = $outlook.Session.OpenSharedItem($_.FullName)
$msg | Select-Object -ExpandProperty Body
$outlook.Quit()
}
Error is: The file XXX cant be opened. Maybe it is already opened....
Thanks in advance

I know it's an old thread, but its the top when searching for this issue. This is what worked for me:
Get-ChildItem $scriptPath -Filter *.msg |
ForEach-Object {
$outlook = New-Object -comobject outlook.application
$msg = $outlook.Session.OpenSharedItem($_.FullName)
$msg | Select-Object -ExpandProperty Body
$msg.Close(1) # oldispose
$msg = $null
}

Which Outlook version do you use?
There's a bug in Outlook and a workaround is to mark the .msg files with readonly attribute.

Related

Powershell - extract specific items from zip then send a message (loop with message box)

I'm trying to achieve the following with this powershell script.
Copy any .zip file from folder dropfilehere to folder IN.
For each .zip file in folder "IN" open the zip file, find only the .csv file.
When .csv file is found, extract it to $dst under name DB.csv (overwrite old file).
Empty contents of folders "dropfilehere" and "IN"
Finally, when all the above is done, create a popup box with a message to the user using wscriptshell -
This is the issue. When the message is sent, the user gets 10+ popup boxes or an endless loop of them.
In the background i see cmd.exe and conhost.exe processes appearing as each popup box gets created.
I use a batch file to call the powershell script.
Powershell.exe -ExecutionPolicy Bypass -File C:\pathtoscript\call.ps1
exit
The script is:
$dst = "C:\Testing\DB"
Copy-item -Path "C:\Users\user\dropfilehere\*.zip" -destination "C:\Testing\Other\In" -Force
Foreach ($zipfile in (Get-ChildItem "C:\Testing\Other\In\*.zip" -Recurse)) {
Add-Type -Assembly System.IO.Compression.FileSystem
$zipFile = [IO.Compression.ZipFile]::OpenRead($zipfile)
$zipFile.Entries | where {$_.Name -like '*.csv'} | foreach {$FileName = $_.Name
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$dst\DB.csv", $true)}
$zipFile.Dispose()
Remove-Item "C:\Testing\Other\In\*" -Recurse -Force
Remove-Item "C:\Users\user\dropfilehere\*" -Recurse -Force
$org="Name of Org"
$timeout = 60 # in seconds
$ws = New-Object -ComObject "Wscript.Shell"
$intButton = $ws.Popup("A new update message here`n
Another message here.",$timeout,$org, 0)
}
exit
There is code inside your foreach loop that should be placed after it, as shown below (properly indenting your code would have made that more obvious):
Add-Type -Assembly System.IO.Compression.FileSystem
$dst = "C:\Testing\DB"
Copy-item -Path "C:\Users\user\dropfilehere\*.zip" -destination "C:\Testing\Other\In" -Force
# Process all files.
foreach ($zipfile in (Get-ChildItem "C:\Testing\Other\In\*.zip" -Recurse)) {
$zipFile = [IO.Compression.ZipFile]::OpenRead($zipfile)
$zipFile.Entries |
Where-Object { $_.Name -like '*.csv' } |
ForEach-Object {
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$dst\DB.csv", $true)
}
$zipFile.Dispose()
}
# Remove the folders containing the original *.zip files.
Remove-Item "C:\Testing\Other\In\*" -Recurse -Force
Remove-Item "C:\Users\user\dropfilehere\*" -Recurse -Force
# Show a message box.
$org = "Name of Org"
$timeout = 60 # in seconds
$ws = New-Object -ComObject "Wscript.Shell"
$intButton = $ws.Popup("A new update message here`nAnother message here.", $timeout, $org, 0)

How to get the Quick Access Paths

I want to write all paths stored in quick access to a text file. With a normal folder this would not be a problem. But "quick access" is not a folder and I don't know how to read what is inside.
You can use COM for this:
$shell = New-Object -ComObject Shell.Application
$paths = $shell.Namespace("shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}").Items() | ForEach-Object { $_.Path }
# view on screen
$paths
# write to text file
$paths | Set-Content -Path 'X:\MyQuickAccessPaths.txt'
# don't forget to remove the used COM object from memory when done
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

Trying to get folder sizes for all users directory

I am trying to write a powershell that will look at a network share and write out to a CSV the full name of the share and the size in MB or GB of those folders for each user home directory folder.
This is my code so far:
$StorageLocation = '\\wgsfs01\USERDIR\USERS'
$Roots = Get-ChildItem $StorageLocation | Select Fullname
ForEach ($Root in $Roots) { (Get-ChildItem $Root -Recurse | Measure-Object -Property Length -Sum).Sum }
I believe there is something wrong with my ForEach statement as this is my error message
Get-ChildItem : Cannot find path 'C:#{FullName=\wgsfs01\USERDIR\USERS' because it does not exist.
I appreciate any advice and thank you in advance.
The issue you have is that FullName contains a DirectoryInfo object, you have two options;
Change your select to ExpandProperty which will change it to a string of the full path.
Select-Object -ExpandProperty Fullname
Refer to $Root using the property FullName which is a property on the DirectoryInfo Object.
Get-ChildItem -path $Root.FullName -Recurse
This is one solution to what you are trying to achieve, note that errors (e.g. access denied) are ignored.
Get-ChildItem $StorageLocation | ForEach-Object {
$sizeInMB = (Get-ChildItem $_.FullName -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum / 1MB
New-Object PSObject -Property #{
FullName = $_.FullName
SizeInMB = $sizeInMB
}
}

powershell: delete files previously used as argument inside script

I need to periodically scan a folder for new fontfiles to install and delete them afterwards using a powershell script.
During processing I want to skip already installed files and to achieve that I need to resolve the "real" fontname of the provided file.
I figured out everything and it seems to work everything but the file deletion.
The deletion did work until I added the font name resolution using this GlythTypeInterface Object. It seems like the invoked object does "file lock" the fontfile resulting in an UnauthorizedAccessException.
Thats why I tried some garbage collection stuff I found but I can't make it work.
My code so far:
Add-Type -AssemblyName PresentationCore
$FONTS = 0x14
$Path="C:\_fonts_to_install"
$FontItem = Get-Item -Path $Path
$FontList = Get-ChildItem -Path "$FontItem\*" -Include ('*.fon','*.otf','*.ttc','*.ttf')
$objShell = New-Object -ComObject Shell.Application
$objFolder = $objShell.Namespace($FONTS)
$Fontdir = dir $Path
$username = $env:UserName
foreach($File in $FontList) {
$try = $true
$installedFonts = #(Get-ChildItem C:\Users\$username\AppData\Local\Microsoft\Windows\Fonts | Where-Object {$_.PSIsContainer -eq $false} | Select-Object basename)
$fontObject = New-Object -TypeName Windows.Media.GlyphTypeface -ArgumentList $File.fullname
$fontName = $fontObject.Win32FamilyNames.Values
Write-Host $fontName
$fontObject = $null
Remove-Variable fontObject
foreach($font in $installedFonts)
{
if ($font -match $fontName)
{
$try = $false
}
}
if ($try)
{
$objFolder.CopyHere($File.fullname)
}
Write-Host $File
Remove-Item $File -Force -Verbose
}

Find the most recent file in a folder/dir and send that file in a email using shell script in windows cmd or powershell

I need to find the most recent file in a folder/dir and send that file as an attachment in a email, so far i have this code that find the most recent file in my windows SO, but i need to specify a route a find the most recent file there and then send that file in a email so far i have this:
EDIT 1:
So far i have this:
This part gives me the last file created in a dir/folder:
$dir = "D:\Users\myUser\Desktop\dirTest"
$latest = Get-ChildItem -Path $dir | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$latest.Fullname
$attachment = $latest.Fullname
And this send the email (i'm using yahoo accounts):
$emailSmtpServer = "smtp.mail.yahoo.com"
$emailSmtpServerPort = "587"
$emailSmtpUser = "yahooAccountThatSendsTheEmail#yahoo.com"
$emailSmtpPass = "passForThisquestion"
$emailMessage = New-Object System.Net.Mail.MailMessage
$emailMessage.From = "yahooAccountThatSendsTheEmail#yahoo.com"
$emailMessage.To.Add( "yahooAccountThatRECIEVESTheEmail#yahoo.com" )
$emailMessage.Subject = "Testing e-mail"
$emailMessage.Body = "email from power shell"
$emailMessage.Attachments.Add( $attachment ) <---- this part gives me problems
$SMTPClient = New-Object Net.Mail.SmtpClient($emailSmtpServer, $emailSmtpServerPort)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential($emailSmtpUser, $emailSmtpPass);
$SMTPClient.Send($emailMessage)
It works now, this is my final script.
This script search the most recent file created in a Dir and it sends that file created to a email account.
Here is my script it works for me but it takes a few minutes to send the email, thanks for the help
This script do what i wanted, it find the most recent file and send tha file in a email.
$dir = "d:\Users\myUser\Desktop\testDir"
$latest = Get-ChildItem -Path $dir | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$latest.Fullname
$attachment = $latest.Fullname
$emailSmtpServer = "smtp.mail.yahoo.com"
$emailSmtpServerPort = "587"
$emailSmtpUser = "test_sender_mail_account#yahoo.com"
$emailSmtpPass = "MyPassword"
$emailMessage = New-Object System.Net.Mail.MailMessage
$emailMessage.From = "test_sender_mail_account#yahoo.com"
$emailMessage.To.Add( "test_receiver_mail_account#gmail.com" )
$emailMessage.Subject = "My Subject"
$emailMessage.Body = "My body message"
$emailMessage.Attachments.Add($attachment)
$SMTPClient = New-Object Net.Mail.SmtpClient($emailSmtpServer, $emailSmtpServerPort)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential($emailSmtpUser, $emailSmtpPass);
$SMTPClient.Send($emailMessage)
With PowerShell, something like this should do alright:
function Send-RecentFile {
param(
[ValidateScript({Test-Path $_ })]
[String] $Path
)
$file = Get-ChildItem -Path $Path -File | Sort CreationTime | Select -Last 1
Write-Output "The most recently created file is $($file.Name)"
$messageParameters = #{
From = "myaccount#mydomain.com"
To = "destinationAccount#theirdomain.com"
Subject = "title"
Body = "message body"
SMTPServer = "mail.mydomain.com"
Attachments = $file.FullName
}
Send-MailMessage #messageParameters -Credential (Get-Credential "peopleo#anotherDomain.com")
}
If you do want to store the credentials in the file, you might have to do something slightly different with it.
Please check this powershell command.
$Body = “get the information here to show the data with attachement”
$dir = "Path\of\the\folder(C:\..\..)"
$latest = Get-ChildItem -Path $dir | Sort-Object LastAccessTime -Descending | Select-Object -First 1
$latest.Fullname
$file = $latest.Fullname
$EmailFrom = “sender#gmail.com”
$EmailTo = “receiver#gmail.com”
$SMTPServer = “smtp.gmail.com”
$EmailSubject = “Enter Your Subject”
$att = new-object Net.Mail.Attachment($file)
$mailmessage = New-Object system.net.mail.mailmessage
$mailmessage.from = ($EmailFrom)
$mailmessage.To.add($EmailTo)
$mailmessage.Subject = $EmailSubject
$mailmessage.Body = $Body
$mailmessage.IsBodyHTML = $true
$mailmessage.Attachments.Add($att)
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential(“sender_username”, “sender_password”);
$SMTPClient.Send($mailmessage)
$att.Dispose()

Resources