How to close outlook after automating it in c# - c#

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.

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/

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.

Related

Trying to check processes for digital signatures, having an error

Hi i've wrote this method in C# that checks all windows processes for digital signatures. Howver, it tells me that File doesn't contain a definiton for GetDigitalSignatures.
void DriverCheck()
{
Process[] processes = Process.GetProcesses();
foreach (Process process in processes)
{
try
{
// Check if the process has a main module
if (process.MainModule != null)
{
// Check if the main module has a digital signature
bool isSigned = File.GetDigitalSignatures(process.MainModule.FileName).Length > 0;
if (isSigned)
{
// The main module is signed
// You can also get the certificate that was used to sign the file using the following code:
}
else
{
// The main module is not signed
}
}
}
catch (System.ComponentModel.Win32Exception)
{
// The process does not have a main module
}
}
}
can someone help me?
I tried finding a namespace that contains those but didn't suceed.
You can try something like this.
using System;
using System.IO;
using System.Windows.Forms; // I have this here, because I wanted to make a WinForm app
using System.Security.Cryptography.X509Certificates;
// rest of the code - form.load, checking the signature
// on button click, displaying the info in a multiline textbox, etc
string curFile = txtPath.Text.Trim(); // I'm reading the path from a textbox
if(File.Exists(curFile))
{
try
{
byte[] fileBytes = File.ReadAllBytes(curFile);
X509Certificate cert = new X509Certificate(fileBytes);
byte[] signature = cert.GetRawCertData();
string extraInfo = "Subject: " + cert.Subject + "\r\n------\r\nIssuer: " + cert.Issuer;
txtResult.Text = extraInfo;
} catch (Exception ex)
{
txtResult.Text = DateTime.Now.ToString("HH:mm:ss") + "\r\nException: " + ex.Message;
}
} else
{
txtResult.Text = "Signature not found";
}
This is how it would look in a tiny WinForm app, made just for this.
The case when the file doesn't have a digital signature is handled in the Exception. You might want to change that for your specific use case, as you would for the way you get the file path (get all the processes, loop through them, check them individually, do something if a signature is not found, etc). For simplicity's sake, I went with a textbox solution and a GUI.
You could call signtool as external tool to validate the process signatures.
// Compile as x64 executable to be able to access 64-bit processes.
// Execute tool as Administrator to get access to more processes.
[DllImport("Kernel32.dll")]
static extern uint QueryFullProcessImageName(IntPtr hProcess, uint flags, StringBuilder text, out uint size);
// Get the path to a process excutable
// https://stackoverflow.com/a/46671939/1911064
private static string GetPathToApp(Process process)
{
string pathToExe = string.Empty;
if (null != process)
{
uint nChars = 256;
StringBuilder Buff = new StringBuilder((int)nChars);
uint success = QueryFullProcessImageName(process.Handle, 0, Buff, out nChars);
if (0 != success)
{
pathToExe = Buff.ToString();
}
else
{
int error = Marshal.GetLastWin32Error();
pathToExe = ("Error = " + error + " when calling GetProcessImageFileName");
}
}
return pathToExe;
}
static bool CheckIfSigned(string fileName)
{
bool ret = false;
if (File.Exists(fileName))
{
// https://learn.microsoft.com/en-us/windows/win32/seccrypto/signtool
// https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk
// signtool.exe is part of Windows 10 SDK
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "signtool.exe",
Arguments = $"verify /all /pa /v {fileName}",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
process.Start();
while (!process.StandardOutput.EndOfStream)
{
var line = process.StandardOutput.ReadLine();
// Console.WriteLine(line);
if (line.StartsWith("Successfully verified:"))
{
Console.WriteLine("success!");
return true;
}
if (line.StartsWith("SignTool Error:"))
{
Console.WriteLine("error!");
return false;
}
if (line.StartsWith("Number of errors: 0"))
{
Console.WriteLine("should have announced success!");
return false;
}
if (line.StartsWith("Number of errors:"))
{
Console.WriteLine("Signtool found errors!");
return false;
}
}
Console.WriteLine($"Could not recognized signtool output for {fileName}");
}
else
{
Console.WriteLine($"File not found: {fileName}");
}
return ret;
}
static void DriverCheck()
{
Process[] processes = Process.GetProcesses();
foreach (Process process in processes)
{
try
{
if (!process.HasExited)
{
string processPath = GetPathToApp(process);
// Check if the process has a main module
if (null != processPath)
{
// Check if the main module has a digital signature
bool isSigned = CheckIfSigned(processPath);
if (isSigned)
{
Console.WriteLine($"signed: {process.MainModule.FileName}");
}
else
{
Console.WriteLine($"**NOT** signed: {process.MainModule.FileName}");
}
}
}
}
catch (System.ComponentModel.Win32Exception ex)
{
// The process does not have a main module?
Console.WriteLine($"Win32Exception for ID={process.Id} {process.ProcessName}: {ex.Message}");
}
catch(Exception ex)
{
Console.WriteLine($"Exception for ID={process.Id} {process.ProcessName}: {ex.Message}");
}
}
}

Getting absolute path of file from process

I can't figure out how to get the absolute path of a file from the process. For example, I am searching for the absolute file path of a .txt file which is opened in notepad.
So far I get the process ID of the foreground window. Use that to create an instance of a Process class by using GetProcessById().
I tried the FileName of the main module of the process, however that gives me the location of the executable, not the file itself!
public static int GetActiveWindowPid()
{
int processID = 0;
uint threadID = GetWindowThreadProcessId(GetForegroundWindow(), out processID);
return processID;
}
public static Process CreateProcess()
{
Process process = Process.GetProcessById(GetActiveWindowPid());
return process;
}
public static string GetFilePath(Process process)
{
//return the filepath of the main module of the process
//return process.MainModule.FileName;
try
{
return process.MainModule.FileName;
}
catch
{
string query = "SELECT ExecutablePath, ProcessID FROM Win32_Process";
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
foreach (ManagementObject item in searcher.Get())
{
object id = item["ProcessID"];
object path = item["ExecutablePath"];
if (path != null && id.ToString() == process.Id.ToString())
{
return path.ToString();
}
}
}
return "Error";
}

Set Image Name and Description for a process using System.Diagnostics.Process()

How to set Image Name and Description for a process when using System.Diagnostics.Process() to start a process?
So that it appears in the Windows task manager with desired name and description.
For Example currently I am invoking some console application as shown below:
static void Main(string[] args)
{
int incVal = 0;
Process[] process = null;
try
{
process = new Process[Properties.Settings.Default.TargetLayers.Length];
for (incVal = 0; incVal < Properties.Settings.Default.TargetLayers.Split(',').Length; incVal++)
{
process[incVal] = new Process();
process[incVal].StartInfo.FileName = "PMSchedulerTask.exe";
process[incVal].StartInfo.Arguments = "\"" + Properties.Settings.Default.TargetLayers.Split(',')[incVal] + "$" + Properties.Settings.Default.TableMVRelation.Split('|')[incVal] + "\"";
process[incVal].Start();
}
for (incVal = 0; incVal < Properties.Settings.Default.TargetLayers.Split(',').Length; incVal++)
{
process[incVal].WaitForExit();
}
}
catch (Exception ex)
{
throw ex;
}
}
Now I would like to have each process shown in the task manager with different Image Name and Description.
These names are extracted by task manager from the executable image the process was started from. There is no setting to override this. Windows doesn't even know these strings exist. They are just part of the PE structure of the executable used to launch the process.
You could create a wrapper executable if you really need to do this.

show running applications (alt-tab program list)

Found the solution here: http://blogs.msdn.com/b/oldnewthing/archive/2007/10/08/5351207.aspx
I'm trying to go a list of running applications, i found on several forums this solution:
Process[] processes = Process.GetProcesses();
foreach (var proc in processes)
{
if (!string.IsNullOrEmpty(proc.MainWindowTitle))
Console.WriteLine(proc.MainWindowTitle);
}
exept this is not giving me the same list as when you press alt-tab. For example: firefox, explorer, and iexplore all return an empty/null MainWindowTitle. Is there another way to access this list? Maybe thru a windowsAPI?
I'm am using Windows 7 32bit
Thank you in advanced.
There are no hidden processes on Windows. Only processes you do not have (security) rights to see.
have a look at the below:
Retrieve a complete processes list using C#
Try this (taken from here), but I'm not sure it solves your problem:
static void Main(string[] args)
{
GetProcesses();
GetApplications();
Console.Read();
}
public static void GetProcesses()
{
StringBuilder sb = new StringBuilder();
ManagementClass MgmtClass = new ManagementClass("Win32_Process");
foreach (ManagementObject mo in MgmtClass.GetInstances())
Console.WriteLine("Name:" + mo["Name"] + "ID:" + mo["ProcessId"]);
Console.WriteLine();
}
public static void GetApplications()
{
StringBuilder sb = new StringBuilder();
foreach (Process p in Process.GetProcesses("."))
try
{
if (p.MainWindowTitle.Length > 0)
{
Console.WriteLine("Window Title:" + p.MainWindowTitle.ToString());
Console.WriteLine("Process Name:" + p.ProcessName.ToString());
Console.WriteLine("Window Handle:" + p.MainWindowHandle.ToString());
Console.WriteLine("Memory Allocation:" + p.PrivateMemorySize64.ToString());
}
}
catch { }
}

How to get the full path of running process?

I am having an application that is changing some settings of another application (it is a simple C# application that run by double clicking (no setup required)).
After changing the settings I need to restart the other application so that it reflects the changed settings.
So to do, I have to kill the running process and start the process again, But the problem is after killing I am not able to find the process. (Reason is system do not know where the exe file is..)
Is there any way to find out the path of running process or exe, if it is running?
I do not want to give path manually, i.e. if it is running get the path, kill the process and start again else .... I will handle later
using System.Diagnostics;
var process = Process.GetCurrentProcess(); // Or whatever method you are using
string fullPath = process.MainModule.FileName;
//fullPath has the path to exe.
There is one catch with this API, if you are running this code in 32 bit application, you'll not be able to access 64-bit application paths, so you'd have to compile and run you app as 64-bit application (Project Properties → Build → Platform Target → x64).
What you can do is use WMI to get the paths. This will allow you to get the path regardless it's a 32-bit or 64-bit application. Here's an example demonstrating how you can get it:
// include the namespace
using System.Management;
var wmiQueryString = "SELECT ProcessId, ExecutablePath, CommandLine FROM Win32_Process";
using (var searcher = new ManagementObjectSearcher(wmiQueryString))
using (var results = searcher.Get())
{
var query = from p in Process.GetProcesses()
join mo in results.Cast<ManagementObject>()
on p.Id equals (int)(uint)mo["ProcessId"]
select new
{
Process = p,
Path = (string)mo["ExecutablePath"],
CommandLine = (string)mo["CommandLine"],
};
foreach (var item in query)
{
// Do what you want with the Process, Path, and CommandLine
}
}
Note that you'll have to reference the System.Management.dll assembly and use the System.Management namespace.
For more info on what other information you can grab out of these processes such as the command line used to start the program (CommandLine), see the Win32_Process class and WMI .NET for for more information.
A solution for:
Both 32-bit AND 64-bit processes
System.Diagnostics only (no System.Management)
I used the solution from Russell Gantman and rewritten it as an extension method you can use like this:
var process = Process.GetProcessesByName("explorer").First();
string path = process.GetMainModuleFileName();
// C:\Windows\explorer.exe
With this implementation:
internal static class Extensions {
[DllImport("Kernel32.dll")]
private static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] uint dwFlags, [Out] StringBuilder lpExeName, [In, Out] ref uint lpdwSize);
public static string GetMainModuleFileName(this Process process, int buffer = 1024) {
var fileNameBuilder = new StringBuilder(buffer);
uint bufferLength = (uint)fileNameBuilder.Capacity + 1;
return QueryFullProcessImageName(process.Handle, 0, fileNameBuilder, ref bufferLength) ?
fileNameBuilder.ToString() :
null;
}
}
I guess you already have the process object of the running process (e.g. by GetProcessesByName()).
You can then get the executable file name by using:
Process p;
string filename = p.MainModule.FileName;
By combining Sanjeevakumar Hiremath's and Jeff Mercado's answers you can actually in a way get around the problem when retrieving the icon from a 64-bit process in a 32-bit process.
using System;
using System.Management;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int processID = 6680; // Change for the process you would like to use
Process process = Process.GetProcessById(processID);
string path = ProcessExecutablePath(process);
}
static private string ProcessExecutablePath(Process process)
{
try
{
return process.MainModule.FileName;
}
catch
{
string query = "SELECT ExecutablePath, ProcessID FROM Win32_Process";
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
foreach (ManagementObject item in searcher.Get())
{
object id = item["ProcessID"];
object path = item["ExecutablePath"];
if (path != null && id.ToString() == process.Id.ToString())
{
return path.ToString();
}
}
}
return "";
}
}
}
This may be a bit slow and doesn't work on every process which lacks a "valid" icon.
Here is a reliable solution that works with both 32bit and 64bit applications.
Add these references:
using System.Diagnostics;
using System.Management;
Add this method to your project:
public static string GetProcessPath(int processId)
{
string MethodResult = "";
try
{
string Query = "SELECT ExecutablePath FROM Win32_Process WHERE ProcessId = " + processId;
using (ManagementObjectSearcher mos = new ManagementObjectSearcher(Query))
{
using (ManagementObjectCollection moc = mos.Get())
{
string ExecutablePath = (from mo in moc.Cast<ManagementObject>() select mo["ExecutablePath"]).First().ToString();
MethodResult = ExecutablePath;
}
}
}
catch //(Exception ex)
{
//ex.HandleException();
}
return MethodResult;
}
Now use it like so:
int RootProcessId = Process.GetCurrentProcess().Id;
GetProcessPath(RootProcessId);
Notice that if you know the id of the process, then this method will return the corresponding ExecutePath.
Extra, for those interested:
Process.GetProcesses()
...will give you an array of all the currently running processes, and...
Process.GetCurrentProcess()
...will give you the current process, along with their information e.g. Id, etc. and also limited control e.g. Kill, etc.*
You can use pInvoke and a native call such as the following. This doesn't seem to have the 32 / 64 bit limitation (at least in my testing)
Here is the code
using System.Runtime.InteropServices;
[DllImport("Kernel32.dll")]
static extern uint QueryFullProcessImageName(IntPtr hProcess, uint flags, StringBuilder text, out uint size);
//Get the path to a process
//proc = the process desired
private string GetPathToApp (Process proc)
{
string pathToExe = string.Empty;
if (null != proc)
{
uint nChars = 256;
StringBuilder Buff = new StringBuilder((int)nChars);
uint success = QueryFullProcessImageName(proc.Handle, 0, Buff, out nChars);
if (0 != success)
{
pathToExe = Buff.ToString();
}
else
{
int error = Marshal.GetLastWin32Error();
pathToExe = ("Error = " + error + " when calling GetProcessImageFileName");
}
}
return pathToExe;
}
private void Test_Click(object sender, System.EventArgs e){
string path;
path = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase );
Console.WriiteLine( path );
}
Try:
using System.Diagnostics;
ProcessModuleCollection modules = Process.GetCurrentProcess().Modules;
string processpathfilename;
string processmodulename;
if (modules.Count > 0) {
processpathfilename = modules[0].FileName;
processmodulename= modules[0].ModuleName;
} else {
throw new ExecutionEngineException("Something critical occurred with the running process.");
}
using System;
using System.Diagnostics;
class Program
{
public static void printAllprocesses()
{
Process[] processlist = Process.GetProcesses();
foreach (Process process in processlist)
{
try
{
String fileName = process.MainModule.FileName;
String processName = process.ProcessName;
Console.WriteLine("processName : {0}, fileName : {1}", processName, fileName);
}catch(Exception e)
{
/* You will get access denied exception for system processes, We are skiping the system processes here */
}
}
}
static void Main()
{
printAllprocesses();
}
}
For others, if you want to find another process of the same executable, you can use:
public bool tryFindAnotherInstance(out Process process) {
Process thisProcess = Process.GetCurrentProcess();
string thisFilename = thisProcess.MainModule.FileName;
int thisPId = thisProcess.Id;
foreach (Process p in Process.GetProcesses())
{
try
{
if (p.MainModule.FileName == thisFilename && thisPId != p.Id)
{
process = p;
return true;
}
}
catch (Exception)
{
}
}
process = default;
return false;
}
As of .NET 6, you can use Environment.ProcessPath.
In a test, you can see that it gives the same result as Process.GetCurrentProcess().MainModule.FileName:
It's possible to implement process query path using PInvoke on OpenProcess, GetModuleFileNameEx.
See full answer in here.
using System.Management;
ManagementObjectSearcher search = new ManagementObjectSearcher("SELECT * FROM
Win32_Process");
foreach (ManagementObject currentObj in search.Get())
{
if (currentObj["Caption"].ToString() == "sqlservr.exe")
MessageBox.Show(currentObj["ExecutablePath"].ToString());
}
The Process class has a member StartInfo that you should check out:
http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo_members(v=VS.71).aspx
I got to this thread while looking for the current directory of an executing process. In .net 1.1 Microsoft introduced:
Directory.GetCurrentDirectory();
Seems to work well (but doesn't return the name of the process itself).

Categories

Resources