Multiple InfoPath Interaction Automation Instances

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) { // Create two instances of word in parallel ThreadPool.QueueUserWorkItem(Word1); ThreadPool.QueueUserWorkItem(Word2); System.Threading.Thread.Sleep(5000); // Attempt to create two instances of infopath in parallel ThreadPool.QueueUserWorkItem(InfoPath1); ThreadPool.QueueUserWorkItem(InfoPath2); } static void Word1(object context) { OfficeInterop.WordTest word = new OfficeInterop.WordTest(); word.Test(); } static void Word2(object context) { OfficeInterop.WordTest word = new OfficeInterop.WordTest(); word.Test(); } static void InfoPath1(object context) { OfficeInterop.InfoPathTest infoPath = new OfficeInterop.InfoPathTest(); infoPath.Test(); } static void InfoPath2(object context) { OfficeInterop.InfoPathTest infoPath = new OfficeInterop.InfoPathTest(); infoPath.Test(); } 

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.

+7
source share
2 answers

I assume that you will have similar behavior if you try to do the same with Outlook, which means that Microsoft thinks it is a bad idea to run multiple copies.

If so, I see two options.

Option one is to make your Infopath automation synchronous by running one instance at a time.

The second option, and I have no idea whether it will work, whether you can run virtual machines to do your InfoPath work.

I hope that this, at least, can lead to some new move, although it will lead to success.

+1
source

I had a very similar problem with Outlook. The admission limit of only one instance of the application does not apply for each user, but for each interactive login session. You can read more about this in the Study of the Limitations of a Single Outlook Instance :

Outlook determined whether another instance was already running in an interactive login session. [...] During the initialization of Outlook, it checks if a window named "Microsoft Outlook" exists with the class name mspim_wnd32, and if so, it is assumed that another instance is already running.

There are hacking methods — there is a tool to run multiple instances of Outlook on the Hammer of God site (scroll down) —but they will probably include intercepting Win32 calls.

As for your code, which only works on certain machines: perhaps due to the state of the race. If both processes manage to start quickly enough at the same time, then they will not find each other's windows, and suppose theyre the only instance working. However, if the machine is running slowly, one process will open its window in front of the other, so that the second process detects the first process window and closes. To play, try introducing a delay of several seconds between the start of the first process and the second - so only the first process should ever succeed.

+1
source

All Articles