7

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.

Trả lời

1

Tôi đoán bạn sẽ nhận được hành vi tương tự nếu bạn cố gắng làm điều tương tự với Outlook, điều này có nghĩa là Microsoft cho rằng đó là một ý tưởng tồi để chạy nhiều bản sao.

Nếu vậy, tôi thấy hai tùy chọn.

Tùy chọn một là làm cho Tự động hóa Infopath của bạn đồng bộ, chạy một thể hiện tại một thời điểm.

Tùy chọn hai, và tôi có NO ý tưởng nếu nó thậm chí sẽ hoạt động, sẽ là để xem bạn có thể khởi động máy ảo để hoàn thành công việc InfoPath của bạn không.

Tôi hy vọng điều này ít nhất có thể gây ra một số chuyến tàu mới mặc dù điều đó sẽ dẫn đến thành công.

+0

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

1

Tôi đã gặp sự cố rất giống với Outlook. Hạn chế cho phép chỉ một cá thể của ứng dụng đang chạy không áp dụng cho mỗi người dùng, nhưng mỗi phiên đăng nhập tương tác. Bạn có thể đọc thêm về nó trong Investigating Outlook's Single-Instance Restriction:

Outlook đã xác định xem một cá thể khác đã chạy trong phiên đăng nhập tương tác chưa. […] Trong quá trình khởi tạo Outlook, nó kiểm tra xem cửa sổ có tên "Microsoft Outlook" có tên lớp "mspim_wnd32" không, và nếu có, nó giả định rằng một cá thể khác đang chạy.

Có nhiều cách để hack xung quanh nó - có một công cụ để tung ra nhiều trường hợp Outlook trên Hammer of God trang web (di chuyển xuống) - nhưng có lẽ họ sẽ liên quan đến chặn Win32 gọi.

Đối với mã của bạn chỉ hoạt động trên một số máy nhất định: Đó có thể là do điều kiện chủng tộc. Nếu cả hai quy trình quản lý để bắt đầu đủ nhanh đồng thời, thì chúng sẽ không phát hiện cửa sổ của nhau và cho rằng chúng là trường hợp duy nhất chạy. Tuy nhiên, nếu máy chậm, một quá trình sẽ mở cửa sổ của nó trước cửa sổ kia, do đó gây ra quá trình thứ hai để phát hiện cửa sổ của quá trình đầu tiên và tự tắt. Để tái tạo, hãy thử giới thiệu một sự trì hoãn vài giây giữa việc khởi chạy quy trình đầu tiên và quá trình thứ hai - theo cách này, chỉ có quá trình đầu tiên mới thành công.

+0

Điều kiện chạy đua là điều tôi đã cân nhắc. Thêm một sự chậm trễ nhân tạo giữa bắt đầu mỗi quá trình (cố gắng tăng lên đến 10 giây) không có hiệu lực, trên các máy nơi nó hoạt động, nó vẫn hoạt động, trên các máy khác, nó vẫn thất bại. Cảm ơn cho các thông tin mặc dù, tôi có thể thử gỡ lỗi các quy trình (theo điều tra Outlook) và xem nếu tôi đến với bất cứ điều gì. – user1369371

Các vấn đề liên quan