Tôi đang cố gắng tự động hóa nhiều phiên bản song song của Office InfoPath 2010 thông qua dịch vụ cửa sổ. Tôi hiểu việc tự động hóa Office từ một dịch vụ không được hỗ trợ tuy nhiên đó là yêu cầu của khách hàng của tôi.Nhiều trường hợp tự động hóa tương tác InfoPath
Tôi có thể tự động hóa các ứng dụng Office khác theo cách song song, tuy nhiên InfoPath hoạt động khác nhau.
Điều tôi đã tìm thấy là sẽ chỉ có một ví dụ của quá trình INFOPATH.EXE được tạo ra, cho dù có bao nhiêu cuộc gọi song song tới CreateObject("InfoPath.Application")
được thực hiện. Ngược lại, nhiều phiên bản WINWORD.EXE có thể được tạo thông qua cơ chế tương tự CreateObject("Word.Application")
Để tái tạo vấn đề này, bạn có thể sử dụng ứng dụng bảng điều khiển đơn giản.
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();
}
Lớp InfoPathTest và WordTest (VB) nằm trong một dự án khác.
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
Các lớp interop chỉ cần tạo các đối tượng tự động hóa, giấc ngủ và sau đó bỏ (mặc dù trong trường hợp của Word, tôi đã hoàn thành các bài kiểm tra phức tạp hơn).
Khi chạy ứng dụng bảng điều khiển, tôi có thể thấy (qua Trình quản lý tác vụ) hai quy trình WINWORD.EXE được tạo song song và chỉ tạo một quy trình INFOPATH.EXE đơn lẻ. Trong thực tế, khi thể hiện đầu tiên của InfoPathTest gọi ip.Quit, quá trình INFOPATH.EXE kết thúc. Khi instance thứ hai của InfoPathTest gọi ip.Quit, một ngoại lệ timeout DCOM được ném - nó xuất hiện như thể hai cá thể đang chia sẻ cùng một đối tượng tự động cơ bản, và đối tượng đó không còn tồn tại sau cuộc gọi đầu tiên tới ip.Quit.
Ở giai đoạn này, suy nghĩ của tôi chỉ là INFOPATH.EXE duy nhất được hỗ trợ cho mỗi lần đăng nhập của người dùng. Tôi mở rộng dịch vụ windows để bắt đầu hai quy trình mới (một ứng dụng giao diện điều khiển được gọi là InfoPathTest), mỗi ứng dụng đang chạy trong một tài khoản người dùng khác nhau. Các quy trình mới này sau đó sẽ cố gắng tự động hóa INFOPATH.EXE
Đây là nơi thú vị, điều này thực sự hiệu quả, nhưng chỉ trên một số máy và tôi không thể hiểu tại sao lại như vậy.
Và mã dịch vụ (với sự giúp đỡ từ 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();
}
Quá trình InfoPathTest.exe chỉ đơn giản gọi InfoPathTest.Test() phương pháp trình bày chi tiết ở trên.
Tóm lại, tính năng này chỉ hoạt động trên một số máy nhất định. Khi nó không thành công, quá trình INFOPATH.EXE thứ hai thực sự được tạo ra, nhưng ngay lập tức thoát với một exitcode là 0. Không có gì trong bản ghi sự kiện, cũng không có bất kỳ ngoại lệ nào trong mã.
Tôi đã xem xét nhiều thứ để thử và phân biệt giữa các máy làm việc/không hoạt động, nhưng bây giờ tôi bị kẹt.
Bất kỳ con trỏ nào được đánh giá cao, đặc biệt nếu bạn có những suy nghĩ khác về cách tự động hóa nhiều cá thể InfoPath song song.
Cách tiếp cận tự động hóa hiện tại của chúng tôi là đồng bộ, tuy nhiên vì lý do hiệu suất, chúng tôi đang cố gắng cho phép các quy trình song song. – user1369371