I am trying to automate multiple concurrent instances of Office InfoPath 2010 through a Windows service. I understand that Office automation from a service is not supported, but this is a requirement of my client.
I can automate other Office applications in parallel, however InfoPath behaves differently.
I found that only one instance of the INFOPATH.EXE process will be created, regardless of how many concurrent calls to CreateObject("InfoPath.Application") made. In contrast, multiple instances of WINWORD.EXE can be created using a similar CreateObject("Word.Application") mechanism CreateObject("Word.Application")
To reproduce this problem, you can use a simple console application.
static void Main(string[] args) {
The InfoPathTest and WordTest (VB) classes are in another project.
Public Class InfoPathTest Public Sub Test() Dim ip As Microsoft.Office.Interop.InfoPath.Application ip = CreateObject("InfoPath.Application") System.Threading.Thread.Sleep(5000) ip.Quit(False) End Sub End Class Public Class WordTest Public Sub Test() Dim app As Microsoft.Office.Interop.Word.Application app = CreateObject("Word.Application") System.Threading.Thread.Sleep(5000) app.Quit(False) End Sub End Class
Mutual classes simply create automation objects, sleep, and then exit (although in the case of Word, I completed more complex tests).
When I start the console application, I see (through the task manager) two WINWORD.EXE processes created in parallel, and only one INFOPATH.EXE process is created. In fact, when the first instance of InfoPathTest calls ip.Quit, the INFOPATH.EXE process terminates. When the second instance of InfoPathTest calls ip.Quit, a DCOM timeout exception is thrown - it seems that both instances use the same main automation object, and this object no longer exists after the first call to ip.Quit.
At this point, my thoughts were just one INFOPATH.EXE supported for each user login. I expanded the Windows service to start two new processes (a console application called InfoPathTest), each of which runs under a different user account. These new processes will then attempt to automate INFOPATH.EXE
Here, where it gets interesting, it really works, but only on some machines, and I cannot understand why this is so.
And the service code (using AsproLock ):
public partial class InfoPathService : ServiceBase { private Thread _mainThread; private bool isStopping = false; public InfoPathService() { InitializeComponent(); } protected override void OnStart(string[] args) { if (_mainThread == null || _mainThread.IsAlive == false) { _mainThread = new Thread(ProcessController); _mainThread.Start(); } } protected override void OnStop() { isStopping = true; } public void ProcessController() { while (isStopping == false) { try { IntPtr hWinSta = GetProcessWindowStation(); WindowStationSecurity ws = new WindowStationSecurity(hWinSta, System.Security.AccessControl.AccessControlSections.Access); ws.AddAccessRule(new WindowStationAccessRule("user1", WindowStationRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow)); ws.AddAccessRule(new WindowStationAccessRule("user2", WindowStationRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow)); ws.AcceptChanges(); IntPtr hDesk = GetThreadDesktop(GetCurrentThreadId()); DesktopSecurity ds = new DesktopSecurity(hDesk, System.Security.AccessControl.AccessControlSections.Access); ds.AddAccessRule(new DesktopAccessRule("user1", DesktopRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow)); ds.AddAccessRule(new DesktopAccessRule("user2", DesktopRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow)); ds.AcceptChanges(); ThreadPool.QueueUserWorkItem(Process1); ThreadPool.QueueUserWorkItem(Process2); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(String.Format("{0}: Process Controller Error {1}", System.Threading.Thread.CurrentThread.ManagedThreadId, ex.Message)); } Thread.Sleep(15000); } } private static void Process1(object context) { SecureString pwd2; Process process2 = new Process(); process2.StartInfo.FileName = @"c:\debug\InfoPathTest.exe"; process2.StartInfo.UseShellExecute = false; process2.StartInfo.LoadUserProfile = true; process2.StartInfo.WorkingDirectory = @"C:\debug\"; process2.StartInfo.Domain = "DEV01"; pwd2 = new SecureString(); foreach (char c in "password") { pwd2.AppendChar(c); }; process2.StartInfo.Password = pwd2; process2.StartInfo.UserName = "user1"; process2.Start(); process2.WaitForExit(); } private static void Process2(object context) { SecureString pwd2; Process process2 = new Process(); process2.StartInfo.FileName = @"c:\debug\InfoPathTest.exe"; process2.StartInfo.UseShellExecute = false; process2.StartInfo.LoadUserProfile = true; process2.StartInfo.WorkingDirectory = @"C:\debug\"; process2.StartInfo.Domain = "DEV01"; pwd2 = new SecureString(); foreach (char c in "password") { pwd2.AppendChar(c); }; process2.StartInfo.Password = pwd2; process2.StartInfo.UserName = "user2"; process2.Start(); process2.WaitForExit(); } [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetProcessWindowStation(); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr GetThreadDesktop(int dwThreadId); [DllImport("kernel32.dll", SetLastError = true)] public static extern int GetCurrentThreadId(); }
The InfoPathTest.exe process simply calls the InfoPathTest.Test () method described above.
Thus, it works, but only on certain machines. When it fails, the second INFOPATH.EXE process is actually created, but immediately exits with exit code 0. Nothing happens in the event logs and there are no exceptions in the code.
I looked at many things to try to distinguish between working / idle machines, but now I'm stuck.
Any pointers appreciated, especially if you have other thoughts on how to automate multiple InfoPath instances in parallel.