If you're able to shell out, outlook.exe can just run emls without conversion like so
outlook.exe /eml "path\to\file.eml"
This even works if the email is editable (X-Unsent= 1
).
There may even be a way to do an equivalent without shell which would be nice.
Alternatively though, you can do this programatically, but requires conversion; in fact you have to if you want any editable email to automatically insert the user's signature (as that would be an oft, not an msg/eml).
Below I have a powershell script that takes an eml and conditionally either saves out an msg or an oft, and then opens it.
You can make it just convert without opening, or just open without converting (but you do still need to make temporary files: OOM does not accept making MailItem
s from memory); I'm just covering all bases for posterity.
What I use it for is the eml file extension is associated to open with the script on the clients machines, they double-click an eml file, it opens in outlook.
Even though it's powershell, C# is pretty interchangable; I mean I wrote it reading only examples and documentation for C# (maybe some vbs for the old CDO).
I dont know C#, and feel putting this here will be magnitudes more help to someone seeing this post than not writing anything (since I had to scour the internet and do it from scratch).
I'll glady accept any edit if someone ports it
$location = $PSScriptRoot
Start-Transcript "$location\LOG.txt" -Append
#ADODB only works if MIME is at the top
. {
'MIME-Version: 1.0'
Get-Content $args[0] <#-AsByteStream#>
} $args[0] | Set-Content "$location\tmp" <#-AsByteStream#>
#parse the eml
$eml = New-Object -ComObject ADODB.Stream
$eml.Open()
$eml.LoadFromFile("$location\tmp")
$eml.Flush()
$email = New-Object -ComObject "CDO.Message"
$email.DataSource.OpenObject($eml, "_Stream")
$email.DataSource.Save()
$eml.Close()
#!moved this to the bottom to demonstrate no-shellingout and msg conversion
#if the email is not editable, just open it normally
#if ($email.Fields('urn:schemas:mailheader:X-Unsent').Value -ne '1') {
# & "${env:ProgramFiles}\Microsoft Office\root\Office16\OUTLOOK.EXE" /eml $args[0]
# exit
#}
#build the template
$outlook = New-Object -ComObject Outlook.Application
$output = $outlook.CreateItem(<#olMailItem#>0)
$output.Sender = $email.From
$output.To = $email.To
$output.CC = $email.CC
$output.BCC = $email.BCC
$output.Subject = $email.Subject
if ($email.ReplyTo) {
$output.ReplyRecipients.Add($email.ReplyTo) | Out-Null
}
$output.BodyFormat = <#olFormatHTML#>2
$output.HTMLBody = $email.HTMLBody
#for each of the attachments
. {for ($part = $email.HTMLBodyPart; $part = $part.Parent) {
$part.BodyParts | Where-Object {
$_.Fields('urn:schemas:httpmail:content-disposition-type').Value -match '^(inline|attachment)$'
}
}} | %{
#get the name
$name = ($_.FileName -replace '^.*[/\\]','').trim('.')
#make one if it didnt have one
if (!$name) {
$name = (
'Untitiled attachment' +
($_.Fields('urn:schemas:httpmail:content-media-type').Value `
-replace '[/\\]' ,'.' `
-replace '^\.+|\.+$','' `
-replace '^(.)' ,' $1'
)
)
}
#save the attachment to file
$_.
GetDecodedContentStream().
SaveToFile("$location\$name", <#adSaveCreateOverWrite#>2)
#TODO unicode,bigendianunicode,utf8,utf7,utf32,ascii,default,oem
if ($_.Charset -imatch 'UTF-8') {
Get-Content "$location\$name" | Out-File 'tmp' -encoding utf8
Move-Item 'tmp' "$location\$name" -Force
}
#pull it into the email
$attachment = $output.Attachments.Add("$location\$name").PropertyAccessor
Remove-Item "$location\$name"
#set up its properties
if ($_.Fields('urn:schemas:mailheader:content-id').Value) {
$attachment.SetProperty(
<#PR_ATTACH_CONTENT_ID(unicode)#>'http://schemas.microsoft.com/mapi/proptag/0x3712001F',
$_.Fields('urn:schemas:mailheader:content-id').Value.trim('<>')
)
}
if ($_.Fields('urn:schemas:httpmail:content-media-type').Value) {
$attachment.SetProperty(
<#PR_ATTACH_MIME_TAG(unicode)#>'http://schemas.microsoft.com/mapi/proptag/0x370E001F',
$_.Fields('urn:schemas:httpmail:content-media-type').Value
)
}
}
#save and open the email
if ($email.Fields('urn:schemas:mailheader:X-Unsent').Value -eq '1') {
$output.SaveAs("email.oft", <#olTemplate#>2)
$output.Close(<#olDiscard#>1)
$outlook.CreateItemFromTemplate("email.oft").Display()
}
else {
$output.SaveAs("email.msg", <#olMSG#>3)
$output.Close(<#olDiscard#>1)
$outlook.Session.OpenSharedItem("email.msg").Display()
}
#!as per the above #!; I would normally not have the x-unset test here
#!and would only save the template, but calling it simply 'tmp', so no leftover files are made
Remove-Item "$location\tmp"
I think I've covered everything. This works with attachments, embedded images and css, but assumes HTML email and utf8 for attachments that are text.
That's what I needed, and although it wouldn't be hard to add support for other stuff, if you are private, or can use paid 3rd party, Dmitry seems to have done a fantastically holistic thing with Redemption (I have seen his avatar A LOT in my recent travels) and will be much simpler for you.