4

I am creating a program which converts Msg outlook file into pdf. What I did was export the Msg file into Html then convert the Html output to pdf. This is my code:

Microsoft.Office.Interop.Outlook.Application app = new Microsoft.Office.Interop.Outlook.Application();

string filename = System.IO.Path.GetFileNameWithoutExtension(msgLocation) + ".html";
string attachmentFiles = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetFileNameWithoutExtension(msgLocation) + "_files");
string extractLocation = System.IO.Path.Combine(System.IO.Path.GetTempPath(), filename);

Console.WriteLine(filename);
Console.WriteLine(attachmentFiles);
Console.WriteLine(extractLocation);
var item = app.Session.OpenSharedItem(msgLocation) as Microsoft.Office.Interop.Outlook.MailItem;
item.SaveAs(extractLocation, Microsoft.Office.Interop.Outlook.OlSaveAsType.olHTML);

int att = item.Attachments.Count;
if (att > 0)
{
    for (int i = 1; i <= att; i++)
    {
        item.Attachments[i].SaveAsFile(System.IO.Path.Combine(attachmentFiles, item.Attachments[i].FileName));
    }
}

app.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(app);

The MSG file convertion to HTML is working perfectly, but why is outlook.exe is still running? I want to close it, but app.Quit() doesn't close the app.

Rocklan
  • 7,888
  • 3
  • 34
  • 49
outlook email
  • 361
  • 1
  • 3
  • 14

2 Answers2

4

The issue is that the outlook com object is holding on to references and stopping the app from closing. Use the following function and pass your "app" object to it:

private void ReleaseObj(object obj)
{
    try 
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
    }
    finally 
    {
        obj = null;
    }
}

See https://blogs.msdn.microsoft.com/deva/2010/01/07/best-practices-how-to-quit-outlook-application-after-automation-from-visual-studio-net-client/

Rocklan
  • 7,888
  • 3
  • 34
  • 49
  • 2
    i tried it sir but the process is still there. and in my code above i have the similar code System.Runtime.InteropServices.Marshal.ReleaseComObject(app); – outlook email May 04 '17 at 02:06
  • are you calling Release before you call .Quit() ? – Rocklan May 04 '17 at 02:09
  • and you need to set it to null in the finally block. – Rocklan May 04 '17 at 02:09
  • and do it for your Item object too – Rocklan May 04 '17 at 02:09
  • +2 for refer these Best Pratices – Marcelo Scofano Diniz Jan 06 '21 at 12:53
  • This should be done with all Outlook COM objects that your program holds references to. My program also had a Namespace object (holding reference to Outlook session) and a Folder object (holding reference to inbox), that had to be released. However, if you use something like a _rows_ variable (holds data from a table object), this object only needs to be set to null. Calling the ReleaseComObject method with it will result in an error, as it is not classed as a COM object. Hence, the reason for the try-catch statement. – Tristan Bailey Apr 16 '23 at 11:51
0

This should work for any and App by referencing the App Name and it will kill all instances of the application. For instance if you have 5 instances of Notepad, it will kill them all...

To Kill the app, use the following:

 KillProcessByPID.KillProcessByName("OUTLOOK");

Create the following static class (c# .net Core 6.0 is my weapon of choice in this case, but it should be fairly universal).

using System.Management;
using System.Diagnostics;


public static class KillProcessByPID
{
    public static void KillProcessByName(string ProcessName)
    {
        string OutlookProcessName = "";
        foreach (Process otlk in Process.GetProcesses())
        {
            if (otlk.ProcessName.ToLower().Contains(ProcessName.ToLower())) //OUTLOOK is the one I am seeking - yours may vary
            {
                OutlookProcessName = otlk.ProcessName;
            }
        }
        //Get process ID by Name
        var processes = Process.GetProcessesByName(OutlookProcessName);
        foreach (var process in processes)
        {
            Console.WriteLine("PID={0}", process.Id);
            Console.WriteLine("Process Handle={0}", process.Handle);
            PortfolioTrackerXML.KillProcessByPID.KillProcessAndChildren(process.Id);
        }
    }


    /// <summary>
    /// Kill a process, and all of its children, grandchildren, etc.
    /// </summary>
    /// <param name="pid">Process ID.</param>
    public static void KillProcessAndChildren(int pid)
    {
        // Cannot close 'system idle process'.
        if (pid == 0)
        {
            return;
        }
        ManagementObjectSearcher searcher = new ManagementObjectSearcher
                ("Select * From Win32_Process Where ParentProcessID=" + pid);
        ManagementObjectCollection moc = searcher.Get();
        foreach (ManagementObject mo in moc)
        {
            KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
        }
        try
        {
            Process proc = Process.GetProcessById(pid);
            proc.Kill();
        }
        catch (ArgumentException)
        {
            // Process already exited.
        }
    }
}

I borrowed from many and I'm not sure where, but I thank you all and give you all credit... apologies for not referencing you directly, but this was instrumental.

Danimal111
  • 1,976
  • 25
  • 31