多的InfoPath互操作自动化的实例

问题描述:

我试图通过Windows服务来自动运行Office的InfoPath 2010多个并行实例。我理解,从服务中自动执行Office不受支持,但这是我的客户的要求。多的InfoPath互操作自动化的实例

我可以以并行方式自动执行其他Office应用程序,但InfoPath的行为有所不同。

我发现的是,无论对CreateObject("InfoPath.Application")进行多少次并行调用,只会创建INFOPATH.EXE进程的一个实例。与此相反,可以通过类似的机制创建多个WINWORD.EXE实例CreateObject("Word.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(); 
} 

InfoPathTest和WordTest类(VB)在另一个项目中。

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 

的互操作类简单地创建自动化对象,睡眠,然后退出(尽管在Word的情况下,我已经完成更复杂的测试)。

当运行控制台应用程序,我可以看到(通过任务管理器)并联创建了两个WINWORD.EXE过程,并且只产生一个单一的INFOPATH.EXE过程。事实上,当InfoPathTest的第一个实例调用ip.Quit时,INFOPATH.EXE进程终止。当InfoPathTest的第二个实例调用ip.Quit时,会引发DCOM超时异常 - 看起来好像这两个实例共享相同的基础自动化对象,并且该对象在首次调用ip.Quit后不再存在。

在这个阶段,我的想法只有一个INFOPATH.EXE支持每个用户登录。我扩展了windows服务以启动两个新进程(一个名为InfoPathTest的控制台应用程序),每个进程都在不同的用户帐户下运行。然后,这些新工艺将尝试自动INFOPATH.EXE

在此处,它变得有趣,这实际工作,但只在某些机器上,我想不通,为什么是这种情况。

和(从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(); 

} 

的InfoPathTest.exe过程简单地调用上文详述的InfoPathTest.Test()方法。

总之,这个工作,但只适用于某些机器。当它失败时,第二个INFOPATH.EXE进程实际上是创建的,但立即退出,出口码为0.事件日志中没有任何内容,代码中也没有任何例外。

我已经看了很多东西,试图和工作/非工作机区分开来,但现在我卡住了。

任何指针赞赏,特别是如果你有关于如何在多个并行的InfoPath情况下自动其他的想法。

我猜你会得到类似的行为,如果你试图做与Outlook同样的事情,这将意味着微软认为这是一个坏主意,运行多个副本。

如果是这样,我看到两个选项。

选项一是让您的Infopath自动化同步,一次运行一个实例。

选项二,我有没有的想法,如果它甚至可以工作,将看看你是否可以启动虚拟机来完成你的InfoPath工作。

我希望这至少能引发一些新的火车,但这将导致成功。

+0

我们当前的自动化方法是同步的,但出于性能方面的原因,我们试图允许并行流程。 – user1369371

我遇到了与Outlook非常类似的问题。仅允许单个应用程序实例运行的限制不适用于每个用户,而是每个交互式登录会话。您可能会在Investigating Outlook's Single-Instance Restriction中看到更多信息:

Outlook正在确定另一个实例是否已在交互式登录会话中运行。 [...]在Outlook初始化期间,它会检查是否存在名为“Microsoft Outlook”的类名为“mspim_wnd32”的窗口,如果存在,则假定另一个实例已在运行。

有它周围的黑客攻击方式 - 有关于Hammer of God网站(向下滚动)推出多个Outlook实例的工具 - 但他们可能会涉及拦截Win32调用。

至于你的代码只在某些机器上工作:这可能是由于竞争条件。如果两个进程同时启动的速度足够快,那么它们将不会检测到对方的窗口,并假定它们是唯一正在运行的实例。但是,如果机器速度很慢,一个进程会在另一个进程之前打开它的窗口,从而导致第二个进程检测到第一个进程的窗口并关闭它。要重现,试着在启动第一个进程和第二个进程之间引入几秒的延迟 - 这样,只有第一个进程应该成功。

+0

竞赛条件是我已经考虑过的。在启动每个进程(尝试增加10秒)之间添加一个虚拟延迟对其工作的机器没有影响,但在其他机器上仍然有效,但仍然失败。感谢您的信息,但我可能会尝试调试过程(根据Outlook调查),看看我是否想出任何东西。 – user1369371