use*_*371 7 c# infopath windows-services office-automation office-interop
我试图通过Windows服务自动化Office InfoPath 2010的多个并行实例.我知道不支持从服务自动化Office,但这是我的客户的要求.
我可以以并行方式自动化其他Office应用程序,但InfoPath的行为则不同.
我发现只要创建了一个INFOPATH.EXE进程的实例,无论进行多少并行调用CreateObject("InfoPath.Application").与此相反,可以通过类似的机制创建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();
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
互操作类只是创建自动化对象,睡眠然后退出(虽然在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();
}
Run Code Online (Sandbox Code Playgroud)
InfoPathTest.exe进程只调用上面详述的InfoPathTest.Test()方法.
总之,这是有效的,但仅适用于某些机器.当它失败时,实际创建了第二个INFOPATH.EXE进程,但是立即以exitcode为0退出.事件日志中没有任何内容,代码中也没有任何异常.
我已经看了很多东西,试图区分工作/非工作机器,但我现在卡住了.
任何指针都很受欢迎,特别是如果您对如何并行自动化多个InfoPath实例有其他想法.
我猜如果您尝试对 Outlook 执行相同的操作,您也会得到类似的行为,这意味着 Microsoft 认为运行多个副本是一个坏主意。
如果是这样,我看到两个选择。
选项一是使您的 Infopath 自动化同步,一次运行一个实例。
选项二(我不知道它是否有效)是看看是否可以启动虚拟机来完成 InfoPath 工作。
我希望这至少能引发一些新的思路,从而带来成功。