I am trying to automate Outlook using COM, and we are having terrible problems with Outlook crashing at arbitrary points when trying to use MAPI methods (eg Outlook.Recipient r = MAPI.CreateRecipient("me#there.com")).
One solution that has been suggested is to use Marshal.GetActiveObject() instead of new Outlook.Application().
This appears to work fine, however I have run into a very strange issue - the code fires up a copy of outlook and tries to get the application object, but calls to Marshal.GetActiveObject throw a System.Runtime.InteropServices.COMException exception while Outlook is the active application (Has the focus).
If I run the below code, the try keeps failing over and over.
However, if I hit ALT-TAB or click anywhere such that Outlook is no longer the active application, the code INSTANTLY succeeds.
Any ideas why? Failing that, does anyone have code that will de-focus outlook, so the code succeeds?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using Outlook = Microsoft.Office.Interop.Outlook;
using System.Threading;
using System.Reflection;
using System.Runtime.InteropServices;
namespace MAPI_Repro
{
class FakeTest
{
public Outlook.Application oApp;
public Outlook.NameSpace MAPI;
public void DoTest(){
if (Process.GetProcessesByName("OUTLOOK").Count() == 0)
{
var process = Process.Start(new ProcessStartInfo("outlook.exe"));
}
while (Process.GetProcessesByName("OUTLOOK").Count() == 0)
{
Thread.Sleep(100);
}
bool success = false;
while (!success)
{
Debug.WriteLine("Waiting for Marshal.GetActiveObject");
try
{
// This FAILS while Outlook is the active application.
// As soon as you hit ALT-TAB or click another app, this succeeds.
oApp = Marshal.GetActiveObject("Outlook.Application") as Outlook.Application;
success = true;
Debug.WriteLine("SUCCESS");
}
catch (System.Runtime.InteropServices.COMException exp)
{
Debug.WriteLine("FAILED " + exp);
}
Thread.Sleep(100);
}
MAPI = oApp.GetNamespace("MAPI");
MAPI.Logon("", "", Missing.Value, Missing.Value);
}
}
}
Related
I am working on building a primitive and basic web browser on which my workplace would like to host some internal applications. I"m using cefSharp in a WinForms application written in C#. I've succeeded in building the browser to navigate the application, but I'm having trouble with the download handler. I would like to download files directly to the C:\Users\[username]\Downloads folder (all of our computers are Windows computers) without having to use the dialog.
Reading from Force CEFSharp to download without showing dialog suggests that using showDialog: false should work, but when I apply this, nothing downloads. Likewise, I've made no progress by studying any of the following:
WPF : download files through CefSharp
https://github.com/cefsharp/CefSharp/blob/cd934267c65f494ceb9ee75995cd2a1ca0954543/CefSharp.Example/DownloadHandler.cs
WPF : download files through CefSharp
https://groups.google.com/forum/?nomobile=true#!topic/cefsharp/bS8PhHRlSAc
https://groups.google.com/forum/#!topic/cefsharp/3cMUHSGxPDc
As a bonus, it'd be nice to have the option to open the file, such as in Google Chrome, but this isn't strictly necessary.
The code below runs smoothly and approximates what I am attempting. This example opens to a GitHub Gist. Clicking on the "Download Zip" button on the right opens the dialog to download and save the file.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;
using System.IO;
namespace ShinyChrome
{
public partial class ShinyApp : Form
{
public class DownloadHandler : IDownloadHandler
{
public event EventHandler<DownloadItem> OnBeforeDownloadFired;
public event EventHandler<DownloadItem> OnDownloadUpdatedFired;
public void OnBeforeDownload(IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback)
{
var handler = OnBeforeDownloadFired;
if (handler != null)
{
handler(this, downloadItem);
}
if (!callback.IsDisposed)
{
using (callback)
{
callback.Continue(downloadItem.SuggestedFileName, showDialog: true);
}
}
}
public void OnDownloadUpdated(IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback)
{
var handler = OnDownloadUpdatedFired;
if (handler != null)
{
handler(this, downloadItem);
}
}
}
public ShinyApp()
{
InitializeComponent();
}
ChromiumWebBrowser chrome;
private void ShinyApp_Load(object sender, EventArgs e)
{
CefSettings settings = new CefSettings();
Cef.Initialize(settings);
chrome = new ChromiumWebBrowser("https://gist.github.com/nutterb/32992747c1a69aa7a8fdcc2b5347178f");
chrome.DownloadHandler = new DownloadHandler();
this.shinyContainer.Controls.Add(chrome);
}
}
}
On TEK's advice, I replaced the if(!callback.IsDisposed) block in the question with the code below.
if (!callback.IsDisposed)
{
using (callback)
{
callback.Continue(#"C:\Users\" +
System.Security.Principal.WindowsIdentity.GetCurrent().Name. +
#"\Downloads\" +
downloadItem.SuggestedFileName,
showDialog: false);
}
}
I've very new to the TestStack (White) UI Automation library and I'm having a bit of an issue in terms of "hooking" the process. I'm trying to hook CCleaner, but I keep getting
An unhandled exception of type 'TestStack.White.AutomationException'
occurred in TestStack.White.dll
Additional information: Couldn't find window with title Piriform
CCleaner in process 1156, after waiting for 30 seconds:
My current code is:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using TestStack.White;
using TestStack.White.Factory;
using TestStack.White.UIItems.Finders;
using TestStack.White.InputDevices;
using TestStack.White.UIItems.WindowItems;
namespace NightWipe
{
class Program
{
private const string ExeSourceFile = #"C:\Program Files\CCleaner\CCleaner.exe";
private static TestStack.White.Application _application;
private static TestStack.White.UIItems.WindowItems.Window _mainWindow;
static void Main(string[] args)
{
clean();
}
public static string clean()
{
var psi = new ProcessStartInfo(ExeSourceFile);
_application = TestStack.White.Application.AttachOrLaunch(psi);
_mainWindow = _application.GetWindow("Piriform CCleaner");
_mainWindow.WaitWhileBusy();
return "";
}
}
}
I thought that maybe it was the name of the process since CCleaner starts another process (not CCleaner.exe) but CCleaner64.exe as seen here, which I can assume is for 64 bit operating systems maybe? Anyway I tried names including: "CCleaner", "CCleaner64"; but this threw the same exact exception.
I'm using Inspect by Microsoft and this is what it pulls for me (large image):
Inspect's information. Any idea what I'm doing wrong here?
The problem is that CCleaner is visible as WIN32 app. So GetWindow() doesn't work. You can try this code:
public void CCleanerSample()
{
var application = Application.AttachOrLaunch(new ProcessStartInfo(#"C:\Program Files\CCleaner\CCleaner.exe"));
AutomationElement ccleanerAutomationElement = null;
Console.Write("Waiting till WIN32 app is launching");
while (ccleanerAutomationElement == null)
{
ccleanerAutomationElement = AutomationElement.RootElement.FindFirst(TreeScope.Children,
new PropertyCondition(AutomationElement.NameProperty, "Piriform CCleaner"));
Thread.Sleep(1000);
Console.Write(".");
}
Console.WriteLine(" Done");
var mainWindow = new Win32Window(ccleanerAutomationElement, WindowFactory.Desktop, InitializeOption.NoCache,
new WindowSession(application.ApplicationSession, InitializeOption.NoCache));
}
I am working on EMAIL service and trying to get emails using IMAP Ae.net.mail, I am able to read the emails from GMAIL comfortably but when i try to do it for Outlook express it throws an exception as given below
Error :
xm007 bad command argument 11
The code is given below for authentication which work fine
ImapClient ic = new ImapClient("outlook.office365.com", MailBoxID, Password,
AuthMethods.Login, 993), true);
IMAPService im = new IMAPService();
im.LoadExchangeEmail(ic, dtEmailServer.Rows[i]);
Now, when i am trying to read email it gives me an exception as i mentioned above
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using AE.Net.Mail;
using AE.Net.Mail.Imap;
using EmailDownloadService.Library;
namespace EmailDownloadService
{
public class IMAPService
{
public void LoadExchangeEmail(ImapClient ic, DataRow Dt)
{
try
{
ic.SelectMailbox("Inbox");
Lazy<MailMessage>[] mm = ic.SearchMessages(SearchCondition.SentSince(Convert.ToDateTime("1/10/2016 11:48:59 AM")), false, true)
InsertIMAP ObjectIMAPInsert = new InsertIMAP();
ObjectIMAPInsert.InsertEmail(mm, Dt);
#region "Get email code"
//foreach (Lazy<MailMessage> message in mm)
//{
// MailMessage m = message.Value;
// string FromAddress= m.From.Address;
//}
#endregion
ic.Dispose();
}
catch (Exception ex)
{
ErrorHandling.ErrorLog(string.Format("Method:LoadIMAP, LineNumber:{0}, Source:{1}, Message:{2}", ex.StackTrace, ex.Source, ex.Message));
}
}
}
}
Here in this code when i am trying to execute
Lazy<MailMessage>[] mm = ic.SearchMessages(SearchCondition.SentSince(Convert.ToDateTime("1/10/2016 11:48:59 AM")), false, true)
it throws an exception like
xm007 bad command argument 11
Can anybody guide to solve this?
I'm attempting to grab a device handle on the Synaptics Touchpad using the Synaptics SDK, specifically using methods in the SYNCTRLLib.
However, the SYNCTRL method failed to find it, returning -1.
Syn.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SYNCOMLib;
using SYNCTRLLib;
namespace TP_Test1
{
class Syn
{
SynAPICtrl SynTP_API = new SynAPICtrl();
SynDeviceCtrl SynTP_Dev = new SynDeviceCtrl();
SynPacketCtrl SynTP_Pack = new SynPacketCtrl();
int DeviceHandle;
//Constructor
public Syn ()
{
SynTP_API.Initialize();
SynTP_API.Activate();
//DeviceHandle == -1 ? Can't find device?
DeviceHandle = SynTP_API.FindDevice(new SynConnectionType(), new SynDeviceType(), 0);
//Below line causing Unhandled Exception
SynTP_Dev.Select(DeviceHandle);
SynTP_Dev.Activate();
SynTP_Dev.OnPacket += SynTP_Dev_OnPacket;
}
public void SynTP_Dev_OnPacket()
{
Console.WriteLine(SynTP_Pack.FingerState);
Console.WriteLine(SynTP_Pack.X);
Console.WriteLine(SynTP_Pack.Y);
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SYNCOMLib;
using SYNCTRLLib;
namespace TP_Test1
{
class Program
{
static void Main(string[] args)
{
Syn mySyn = new Syn();
mySyn.SynTP_Dev_OnPacket();
}
}
}
I see that you are using the C# wrappers for Synaptics SDK. Even though CPP code might be not trivial to you, you might want to take a look at the file Samples/ComTest.cpp. It contains some example logic in order to find devices, more specifically at lines 66-76:
// Find a device, preferentially a TouchPad or Styk.
ISynDevice *pDevice = 0;
long lHandle = -1;
if ((pAPI->FindDevice(SE_ConnectionAny, SE_DeviceTouchPad, &lHandle) &&
pAPI->FindDevice(SE_ConnectionAny, SE_DeviceStyk, &lHandle) &&
pAPI->FindDevice(SE_ConnectionAny, SE_DeviceAny, &lHandle)) ||
pAPI->CreateDevice(lHandle, &pDevice))
{
printf("Unable to find a Synaptics Device.\n");
exit(-1);
}
Also, make sure you have registered the dlls. According to the ReadSynSDK.txt file:
For certain purposes it may be necessary to register the dlls
that are provided with the SDK. This can be done with the windows regsvr32
utility.
I already have a C#-based Outlook Addin application which may or may not be installed on my clients versions of Outlook. Is it possible to determin whether the addin is installed and enabled from an external C# application, running on the same client's machine? And if so, how?
Many thanks in advance!
John
If you are installing via MSI, you can check if it has been installed with the Windows Installer API (see MSDN for more, P/Invoke.net has a C# example).
In the end, the following code solved my problem:
using System.Reflection;
using System.Runtime.InteropServices;
using Outlook = Microsoft.Office.Interop.Outlook;
using Microsoft.Office.Core;
...
public static bool IsOutlookAddinEnabled(string addinName)
{
bool isEnabled = false;
Outlook.Application outlookApp = null;
if (System.Diagnostics.Process.GetProcessesByName("OUTLOOK").Length > 0)
{
outlookApp = Marshal.GetActiveObject("Outlook.Application") as Outlook.Application;
}
else
{
outlookApp = new Outlook.Application();
Outlook.NameSpace nameSpace = outlookApp.GetNamespace("MAPI");
nameSpace.Logon("", "", Missing.Value, Missing.Value);
nameSpace = null;
}
try
{
COMAddIn addin = outlookApp.COMAddIns.Item(addinName);
isEnabled = addin.Connect;
}
catch { }
return isEnabled;
}
Many thanks to Mitch for his quick response.