7

Sto tentando di automatizzare più istanze parallele di Office InfoPath 2010 tramite un servizio di Windows. Comprendo che l'automazione di Office da un servizio non è supportata, tuttavia è un requisito del mio cliente.Istanze di automazione di interoperabilità multiple di InfoPath

Posso automatizzare altre applicazioni di Office in modo parallelo, tuttavia InfoPath si comporta diversamente.

Quello che ho trovato è che ci sarà sempre un'istanza del processo INFOPATH.EXE creato, indipendentemente dal numero di chiamate parallele a CreateObject("InfoPath.Application"). Diversamente, è possibile creare più istanze di WINWORD.EXE tramite il meccanismo simile CreateObject("Word.Application")

Per riprodurre questo problema, è possibile utilizzare una semplice applicazione di console.

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(); 
} 

Le classi di InfoPathTest e WordTest (VB) si trovano in un altro progetto.

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 

Le classi di interoperabilità semplicemente creare gli oggetti di automazione, il sonno e poi uscire (anche se nel caso di Word, ho completato prove più complesse).

Quando si esegue l'app della console, è possibile visualizzare (tramite Task Manager) due processi WINWORD.EXE creati in parallelo e un solo processo INFOPATH.EXE creato. Infatti quando la prima istanza di InfoPathTest chiama ip.Quit, termina il processo INFOPATH.EXE. Quando la seconda istanza di InfoPathTest chiama ip.Quit, viene generata un'eccezione di timeout DCOM - sembra che le due istanze condividessero lo stesso oggetto di automazione sottostante e quell'oggetto non esista più dopo la prima chiamata a ip.Quit.

In questa fase i miei pensieri erano solo un singolo INFOPATH.EXE è supportato per accesso utente. Ho espanso il servizio di Windows per avviare due nuovi processi (un'applicazione console chiamata InfoPathTest), ognuno con un account utente diverso. Questi nuovi processi tenterebbero quindi di automatizzare INFOPATH.EXE

Ecco dove diventa interessante, questo in realtà funziona, ma solo su alcune macchine, e non riesco a capire perché sia ​​così.

E il codice di servizio (con l'aiuto di 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(); 

} 

Il processo InfoPathTest.exe chiama semplicemente il metodo InfoPathTest.Test() descritto sopra.

In breve, questo funziona, ma solo su alcune macchine. Quando non riesce, il secondo processo INFOPATH.EXE viene effettivamente creato, ma si chiude immediatamente con un exitcode di 0. Non c'è nulla nei registri eventi, né alcuna eccezione nel codice.

Ho esaminato molte cose per cercare di distinguere tra macchine funzionanti/non funzionanti, ma ora sono bloccato.

Tutti i puntatori sono apprezzati, soprattutto se si hanno altri pensieri su come automatizzare più istanze di InfoPath in parallelo.

risposta

1

Immagino che avresti un comportamento simile se provassi a fare la stessa cosa con Outlook, il che significherebbe che Microsoft pensa che sia una cattiva idea eseguire più copie.

Se è così, vedo due opzioni.

L'opzione uno consiste nel rendere l'automazione di Infopath sincrona, eseguendo un'istanza alla volta.

L'opzione due, e io ho l'idea NO se dovesse funzionare, sarebbe vedere se è possibile avviare macchine virtuali per realizzare il lavoro di InfoPath.

Spero che questo possa almeno innescare un nuovo treno, anche se questo porterà al successo.

+0

Il nostro attuale approccio di automazione è sincrono, tuttavia per motivi di prestazioni stiamo cercando di consentire processi paralleli. – user1369371

1

Ho riscontrato un problema molto simile a Outlook. La limitazione di consentire l'esecuzione di una singola istanza dell'applicazione in esecuzione non si applica per utente, ma per sessione di accesso interattivo. Puoi leggere ulteriori informazioni a riguardo in Investigating Outlook's Single-Instance Restriction:

Outlook stava determinando se un'altra istanza era già in esecuzione nella sessione di accesso interattivo. [...] Durante l'inizializzazione di Outlook, controlla se esiste una finestra denominata "Microsoft Outlook" con nome di classe "mspim_wnd32" e, in tal caso, presuppone che un'altra istanza sia già in esecuzione.

ci sono modi di hacking intorno ad esso - v'è uno strumento per lanciare più istanze di Outlook sul Hammer of God sito (scorrere verso il basso) - ma che probabilmente coinvolgerà intercettare chiamate Win32.

Per quanto riguarda il vostro codice funziona solo su alcune macchine: probabilmente a causa di una condizione di gara. Se entrambi i processi riescono ad avviarsi in modo sufficientemente veloce simultaneamente, non rileveranno la rispettiva finestra e presumeranno che siano l'unica istanza in esecuzione. Tuttavia, se la macchina è lenta, un processo aprirebbe la sua finestra prima dell'altro, facendo in modo che il secondo processo rilevasse la finestra del primo processo e si chiudesse automaticamente. Per riprodurre, prova a introdurre un ritardo di alcuni secondi tra l'avvio del primo processo e il secondo: in questo modo, solo il primo processo dovrebbe mai riuscire.

+0

Una condizione di gara è qualcosa che ho già preso in considerazione. L'aggiunta di un ritardo artificiale tra l'avvio di ogni processo (tentativi di incrementi fino a 10 secondi) non ha avuto alcun effetto, sulle macchine in cui funziona, ha funzionato ancora, su altre macchine, ha comunque fallito. Grazie comunque per le informazioni, potrei provare a eseguire il debug dei processi (come previsto dall'indagine di Outlook) e vedere se mi viene in mente qualcosa. – user1369371

Problemi correlati