Несколько экземпляров автоматизации взаимодействия InfoPath

120
10


Я пытаюсь автоматизировать несколько параллельных экземпляров Office InfoPath 2010 через службу Windows. Я понимаю, что автоматизация 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();
}


Классы 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 параллельно.

спросил(а) 2021-01-28T01:49:09+03:00 4 недели, 1 день назад
1
Решение
60

У меня возникла очень похожая проблема с Outlook. Ограничение допуска только одного экземпляра приложения не применяется для каждого пользователя, но для каждого сеанса интерактивного входа. Вы можете прочитать больше об этом в Исследование ограничения одного экземпляра Outlook:


Outlook определял, был ли еще один экземпляр уже запущен в интерактивном сеансе входа в систему. [...] Во время инициализации Outlook он проверяет, существует ли окно с именем "Microsoft Outlook" с именем класса mspim_wnd32, и если это так, предполагается, что еще один экземпляр уже запущен.


Существуют способы взлома - есть инструмент для запуска нескольких экземпляров Outlook на сайте Hammer of God (прокрутите вниз) - но они, вероятно, будут включать перехват вызовов Win32.


Что касается вашего кода, который работает только на определенных машинах: возможно, из-за состояния гонки. Если оба процесса удается запустить достаточно быстро одновременно, тогда они не обнаружат окна друг друга, и предположим, что theyre единственный экземпляр работает. Однако, если машина работает медленно, один процесс откроет свое окно перед другим, тем самым, чтобы второй процесс обнаружил первое окно процессов и закрылся. Чтобы воспроизвести, попробуйте ввести задержку в несколько секунд между запуском первого процесса и вторым - таким образом, только первый процесс должен когда-либо преуспеть.

ответил(а) 2021-01-28T01:49:09+03:00 4 недели, 1 день назад
61

Я предполагаю, что у вас будет подобное поведение, если вы попытаетесь сделать то же самое с Outlook, что означает, что Microsoft думает, что это плохая идея запускать несколько копий.


Если это так, я вижу два варианта.


Вариант один - сделать вашу автоматизацию Infopath синхронной, запуская один экземпляр за раз.

Вариант второй, и я понятия не имею, будет ли он работать, будет ли вы можете запустить виртуальные машины для выполнения вашей работы InfoPath.


Я надеюсь, что это, по крайней мере, может привести к некоторому новому ходу, хотя это приведет к успеху.

ответил(а) 2021-01-28T01:49:09+03:00 4 недели, 1 день назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема