ASP.NET can not call outlook function to create .msg file

0

I need create a outlook .msg file by IIS, it is ok when I run it in "IIS Express", but can not run in IIS even set applicationpool to LocalSystem.

Error message:Operation aborted (Exception from HRESULT: 0x80004004 (E_ABORT)) at Microsoft.Office.Interop.Outlook.Attachments.Add(Object Source, Object Type, Object Position, Object DisplayName)

Environment: Win7 32bit, Office 2010, Vistual Studio Pro 2013

Source code as following:

    Try
        Dim oApp As Interop.Outlook._Application
        Dim oMsg As Interop.Outlook._MailItem
        oApp = New Interop.Outlook.Application
        oMsg = oApp.CreateItem(Interop.Outlook.OlItemType.olMailItem)
        oMsg.Subject = "Test Subject"
        oMsg.Body = "Test Body"
        oMsg.To = ""

        Dim attachedFilePath As String = "C:\\temp\\A1234563A.zip"
        If String.IsNullOrEmpty(attachedFilePath) = False Then
            Dim sBodyLen As Integer = Int(oMsg.Body)
            Dim oAttachs As Interop.Outlook.Attachments = oMsg.Attachments
            Dim oAttach As Interop.Outlook.Attachment
            oAttach = oAttachs.Add(attachedFilePath, , sBodyLen, "A1234563A.zip")
        End If
        oMsg.SaveAs("c:\\temp\\abcd.msg", Microsoft.Office.Interop.Outlook.OlSaveAsType.olMSG)

    Catch ex As System.Exception
        'xxxxx
    Finally
       GC.Collect()
       GC.WaitForPendingFinalizers()
    End Try
asp.net
outlook
msg
asked on Stack Overflow Apr 17, 2015 by chen shuguang

4 Answers

2

Microsoft does not currently recommend, and does not support, Automation of Microsoft Office applications from any unattended, non-interactive client application or component (including ASP, ASP.NET, DCOM, and NT Services), because Office may exhibit unstable behavior and/or deadlock when Office is run in this environment.

If you are building a solution that runs in a server-side context, you should try to use components that have been made safe for unattended execution. Or, you should try to find alternatives that allow at least part of the code to run client-side. If you use an Office application from a server-side solution, the application will lack many of the necessary capabilities to run successfully. Additionally, you will be taking risks with the stability of your overall solution.

Read more about that in the Considerations for server-side Automation of Office article.

answered on Stack Overflow Apr 17, 2015 by Eugene Astafiev
1

Your options are

  1. Extended MAPI (OpenImsgOnIStg etc.) to create an MSG file and set all the relevant MAPI properties, but it is only accessible from the C++ or Delphi

  2. Use Windows API to build the file explicitly in your code (it's format is documented) - https://msdn.microsoft.com/en-us/library/cc463912(v=exchg.80).aspx

  3. Use Redemption - it is an Extended MAPI wrapper that can be used from a service in any language including C#, VB.Net or VB script:



      set Session = CreateObject("Redemption.RDOSession")
      set Msg = Session.CreateMessageFromMsgFile("C:\Temp\test.msg")
      Msg.Sent = true
      set recip = Msg.Recipients.AddEx("This user", "this.user@domain.demo", "SMTP", olTo)
      Msg.Subject = "fake received message"
      Msg.Body = "just a test"
      Msg.SentOn = Now
      Msg.ReceivedTime = Now
      'set the sender related properties
      vSenderEntryId = Session.CreateOneOffEntryID("Joe The Sender", "SMTP", "joe@domain.demo", false, true)
      'PR_SENDER_xyz
      Msg.Fields("http://schemas.microsoft.com/mapi/proptag/0x0C1E001F") = "SMTP"
      Msg.Fields("http://schemas.microsoft.com/mapi/proptag/0x0C1F001F") = "joe@domain.demo"
      Msg.Fields("http://schemas.microsoft.com/mapi/proptag/0x0C190102") = vSenderEntryId
      Msg.Fields("http://schemas.microsoft.com/mapi/proptag/0x0C1A001F") = "Joe The Sender"
      'PR_SENT_REPRESENTING_xyz
      Msg.Fields("http://schemas.microsoft.com/mapi/proptag/0x0064001F") = "SMTP"
      Msg.Fields("http://schemas.microsoft.com/mapi/proptag/0x0065001F") = "joe@domain.demo"
      Msg.Fields("http://schemas.microsoft.com/mapi/proptag/0x00410102") = vSenderEntryId
      Msg.Fields("http://schemas.microsoft.com/mapi/proptag/0x0042001F") = "Joe The Sender"
      'all done
      Msg.Save


1

Thanks All, At last I do it as following: 1. Create a template .msg file by outlook manually. 2. Open it and copy to a new stream. Many code for this from intenet. 3. Update my information to this stream, and save as a new .msg file. (Following is source when I update attachment file in .msg file, and you should get other source from OutlookStorage.cs).

//create a ILockBytes (unmanaged byte array) and then create a IStorage using the byte array as a backing store
NativeMethods.CreateILockBytesOnHGlobal(IntPtr.Zero, true, out memoryStorageBytes);
                    NativeMethods.StgCreateDocfileOnILockBytes(memoryStorageBytes, NativeMethods.STGM.CREATE | NativeMethods.STGM.READWRITE | NativeMethods.STGM.SHARE_EXCLUSIVE, 0, out memoryStorage);

//copy the save storage into the new storage
saveMsg.storage.CopyTo(0, null, IntPtr.Zero, memoryStorage);

//Attachments (37xx):
//0x3701: Attachment data       <- This is the binary attachment
//0x3703: Attach extension
//0x3704: Attach filename
//0x3707: Attach long filenm
//0x370E: Attach mime tag
//0x3712: Attach ID (?)

// replace attachment with myself file
var myNameIdSourceStorage = memoryStorage.OpenStorage(OutlookStorage.ATTACH_STORAGE_PREFIX + "00000000", IntPtr.Zero, NativeMethods.STGM.READWRITE | NativeMethods.STGM.SHARE_EXCLUSIVE,IntPtr.Zero, 0);
                                     myNameIdSourceStorage.DestroyElement("__substg1.0_37010102");

// Create the property stream again and write in the padded version
var pStream = myNameIdSourceStorage.CreateStream("__substg1.0_37010102",
                        NativeMethods.STGM.READWRITE | NativeMethods.STGM.SHARE_EXCLUSIVE, 0, 0);
pStream.Write(newFileByte, newFileByte.Length, IntPtr.Zero);

// Open stream from the storage
var mystream = myNameIdSourceStorage.OpenStream("__properties_version1.0", IntPtr.Zero,
                        NativeMethods.STGM.READWRITE | NativeMethods.STGM.SHARE_EXCLUSIVE, 0);

System.Runtime.InteropServices.ComTypes.STATSTG elementStats;
mystream.Stat(out elementStats, 0);                    

// Read the stream into a managed byte array
var iStreamContent = new byte[elementStats.cbSize];
mystream.Read(iStreamContent, iStreamContent.Length, IntPtr.Zero);
iStreamContent[0xc0] = (byte)(newFileByte.Length & 0xFF);
iStreamContent[0xc1] = (byte)(newFileByte.Length >> 8);
iStreamContent[0xc2] = (byte)(newFileByte.Length >> 16);
iStreamContent[0xc3] = (byte)(newFileByte.Length >> 24);
mystream.Write(iStreamContent, 0, IntPtr.Zero);

//0x3704: Attach filename
myNameIdSourceStorage.DestroyElement("__substg1.0_3704001F");
pStream = myNameIdSourceStorage.CreateStream("__substg1.0_3704001F",
                        NativeMethods.STGM.READWRITE | NativeMethods.STGM.SHARE_EXCLUSIVE, 0, 0);
pStream.Write(newProps, 8, IntPtr.Zero);
answered on Stack Overflow Apr 23, 2015 by chen shuguang
0

I use System.Net.Mail instead - https://msdn.microsoft.com/en-us/library/system.net.mail(v=vs.110).aspx

You set up a MailMessage, you can add attachments and set importance, and do everything you're doing above.

This works very well for me, and you don't need to install anything on the server.

EDIT

Here's some sample code for you - import the System.Net.Mail and System.Configuration namespaces. In this example, I'm sending a log, so I get the to addresses and SMTP relay from App.Config. You can change these for what you need.

This also attaches a file from a location also specified in App.Config.

string[] recipients = ConfigurationManager.AppSettings["recipients"].ToString().Split(',');


            MailMessage msg = new MailMessage();
            msg.From = new MailAddress("noreply@somewhere.com");
            msg.Subject = "Log Error Report";

            foreach (string addr in recipients)
            {
                msg.To.Add(new MailAddress(addr));
            }

            string body = System.Environment.NewLine + "Please check these logs for errors.";
            body += System.Environment.NewLine + "Message line 2";

            msg.Body = body;
            msg.Priority = MailPriority.High;

            String attchMnts = ConfigurationManager.AppSettings["logfile"].ToString();
            String[] attchPaths = attchMnts.Split(',');

            foreach (string path in attchPaths)
            {
                Attachment atch = new Attachment(path);
                msg.Attachments.Add(atch);
            }

            SmtpClient client = new SmtpClient(ConfigurationManager.AppSettings["smtpRelay"].ToString());
            client.Send(msg);
answered on Stack Overflow Apr 17, 2015 by Tim • edited Apr 23, 2015 by Tim

User contributions licensed under CC BY-SA 3.0