powershell / runspace в потоке

2

Я запускаю следующий код:

RunspaceConfiguration config = RunspaceConfiguration.Create();
PSSnapInException warning;
config.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out warning);
if (warning != null) throw warning;            

Runspace thisRunspace = RunspaceFactory.CreateRunspace(config);
thisRunspace.Open();

string alias = usr.AD.CN.Replace(' ', '.');
string letter = usr.AD.CN.Substring(0, 1);
string email = alias + "@" + (!usr.Mdph ? Constantes.AD_DOMAIN : Constantes.MDPH_DOMAIN) + "." + Constantes.AD_LANG;
string db = "CN=IS-" + letter + ",CN=SG-" + letter + ",CN=InformationStore,CN=" + ((char)letter.ToCharArray()[0] < 'K' ? Constantes.EXC_SRVC : Constantes.EXC_SRVD) + Constantes.EXC_DBMEL;
string cmd = "Enable-Mailbox -Identity \"" + usr.AD.CN + "\" -Alias " + alias + " -PrimarySmtpAddress " + email + " -DisplayName \"" + usr.AD.CN + "\" -Database \"" + db + "\"";
Pipeline thisPipeline = thisRunspace.CreatePipeline(cmd);
thisPipeline.Invoke();

Код работает в потоке, созданном таким образом:

        t.WorkThread = new Thread(cu.CreerUser);
        t.WorkThread.Start();

Если я запускаю код напрямую (не через поток), он работает.

Когда в потоке возникает следующее исключение: ObjectDisposedException "Безопасный дескриптор закрыт". (Перевод с французского)

Затем я заменил "Open" wirh "OpenAsync", который помог не получить предыдущее исключение. Но когда в Invoke я получаю следующее исключение: InvalidRunspaceStateException "Невозможно вызвать конвейер, потому что его состояние выполнения не открыто, его текущее состояние -" Открытие ". (Также переведен с французского)

Я не знаю...

Любая помощь приветствуется!!! Спасибо!!!


С Open:

   à Microsoft.Win32.Win32Native.GetTokenInformation(SafeTokenHandle TokenHandle, UInt32 TokenInformationClass, SafeLocalAllocHandle TokenInformation, UInt32 TokenInformationLength, UInt32& ReturnLength)
   à System.Security.Principal.WindowsIdentity.GetTokenInformation(SafeTokenHandle tokenHandle, TokenInformationClass tokenInformationClass, UInt32& dwLength)
   à System.Security.Principal.WindowsIdentity.get_User()
   à System.Security.Principal.WindowsIdentity.GetName()
   à System.Security.Principal.WindowsIdentity.get_Name()
   à System.Management.Automation.MshLog.GetLogContext(ExecutionContext executionContext, InvocationInfo invocationInfo, Severity severity)
   à System.Management.Automation.MshLog.GetLogContext(ExecutionContext executionContext, InvocationInfo invocationInfo)
   à System.Management.Automation.MshLog.LogEngineLifecycleEvent(ExecutionContext executionContext, EngineState engineState, InvocationInfo invocationInfo)
   à System.Management.Automation.MshLog.LogEngineLifecycleEvent(ExecutionContext executionContext, EngineState engineState)
   à System.Management.Automation.Runspaces.LocalRunspace.OpenHelper()
   à System.Management.Automation.Runspaces.RunspaceBase.CoreOpen(Boolean syncCall)
   à System.Management.Automation.Runspaces.RunspaceBase.Open()
   à Cg62.ComposantsCommuns.ActiveDirectory.Exchange.BoitesAuxLettres.CreationBAL(User usr, IList`1 log) dans D:\Applications\Commun\Sources  .Net\COMIAD\COMIAD\Exchange.cs:ligne 141
   à Cg62.ComposantsCommuns.ActiveDirectory.ComptesUtilisateurs.CreationUser.CreerUser() dans D:\Applications\Commun\Sources  .Net\COMIAD\COMIAD\ComptesUtilisateurs.cs:ligne 199
   à System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   à System.Threading.ExecutionContext.runTryCode(Object userData)
   à System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   à System.Threading.ThreadHelper.ThreadStart()

С OpenAsync:

   à System.Management.Automation.Runspaces.RunspaceBase.AddToRunningPipelineList(PipelineBase pipeline)
   à System.Management.Automation.Runspaces.RunspaceBase.DoConcurrentCheckAndAddToRunningPipelines(PipelineBase pipeline, Boolean syncCall)
   à System.Management.Automation.Runspaces.PipelineBase.CoreInvoke(IEnumerable input, Boolean syncCall)
   à System.Management.Automation.Runspaces.PipelineBase.Invoke(IEnumerable input)
   à System.Management.Automation.Runspaces.Pipeline.Invoke()
   à Cg62.ComposantsCommuns.ActiveDirectory.Exchange.BoitesAuxLettres.CreationBAL(User usr, IList`1 log) dans D:\Applications\Commun\Sources  .Net\COMIAD\COMIAD\Exchange.cs:ligne 149
   à Cg62.ComposantsCommuns.ActiveDirectory.ComptesUtilisateurs.CreationUser.CreerUser() dans D:\Applications\Commun\Sources  .Net\COMIAD\COMIAD\ComptesUtilisateurs.cs:ligne 199
   à System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   à System.Threading.ExecutionContext.runTryCode(Object userData)
   à System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   à System.Threading.ThreadHelper.ThreadStart()

Извините за поздний ответ... У меня было много дел


Обновлено до Powershell 2.0, и я прошел мимо ошибки Open но теперь у меня есть следующее в Invoke. Я изменил свою команду на:

Enable-Mailbox -Identity "Aagtest Abe" -Alias Aagtest.Abe -PrimarySmtpAddress [email protected] -DisplayName "Aagtest Abe" -Database "myDb" -DomainController adc.domain.int

Команда отлично работает от powershell. Я получаю следующее исключение CmdletInvocationException: "Une exception a été levée par l'initialiseur de type pour '<Module> '." Не знаю, как это сделать...

StackTrace:

   à Microsoft.Exchange.Data.Directory.DSAccessTopologyProvider..ctor(String machineName)
   à Microsoft.Exchange.Data.Directory.DSAccessTopologyProvider..ctor()
   à Microsoft.Exchange.Data.Directory.DirectoryServicesTopologyProvider.DiscoverConfigDC()
   à Microsoft.Exchange.Data.Directory.DirectoryServicesTopologyProvider..ctor()
   à Microsoft.Exchange.Data.Directory.TopologyProvider.InitializeInstance()
   à Microsoft.Exchange.Data.Directory.TopologyProvider.GetInstance()
   à Microsoft.Exchange.Data.Directory.ADSession.GetConnection(String preferredServer, Boolean isWriteOperation, Boolean isNotifyOperation, ADObjectId& rootId)
   à Microsoft.Exchange.Data.Directory.ADSession.GetReadConnection(String preferredServer, ADObjectId& rootId)
   à Microsoft.Exchange.Data.Directory.ADSession.IsReadConnectionAvailable()
   à Microsoft.Exchange.Configuration.Tasks.RecipientObjectActionTask`2.InternalBeginProcessing()
   à Microsoft.Exchange.Management.RecipientTasks.EnableMailbox.InternalBeginProcessing()
   à Microsoft.Exchange.Configuration.Tasks.Task.BeginProcessing()
   à System.Management.Automation.Cmdlet.DoBeginProcessing()
   à System.Management.Automation.CommandProcessorBase.DoBegin()
  • 1
    У меня та же проблема. Вы когда-нибудь получали ответ? Проблема появляется в коде, который работал нормально в течение двух лет (протестировано в Windows Vista, Windows 7 и Windows Server 2003). Но с тех пор, как мы развернули Windows Server 2008, мы получаем эту ошибку.
Теги:
multithreading
powershell

4 ответа

2
Лучший ответ

Итак, окончательный ответ на эту проблему заключается в том, что вы не можете выполнять удаленные команды командной строки Exchange, если вы выдаете себя за использование advapi32 LogonUser.

Wether, команды выполняются в потоке или вообще не имеют значения, насколько я проверял.

Кажется, что правильный путь - и тот, который работает для меня, - это аутентификация сразу после подключения к Runspace.

Как это происходит... не стесняйтесь говорить мне.

Этот код не должен выполняться во время выдачи себя.

        IContexteRemotePowerShell crp:
        crp.ConfigurationName = "Microsoft.Exchange";
        crp.RemoteUri = "http://exhangeserver/powershell";
        crp.User = "account who has rights to do stuff on the exchange server";
        crp.Password = "its password";
        crp.Domaine = "Domain";


private static void Connect(IContexteRemotePowerShell contexte)
    {
        try
        {
            Espace = RunspaceFactory.CreateRunspace();
            Espace.Open();
        }
        catch (InvalidRunspaceStateException ex)
        {
            throw new TechniqueException(MethodBase.GetCurrentMethod(), "Error while creating runspace.", ex);
        }

        // Create secure password
        SecureString password = new SecureString();
        foreach (char c in contexte.Password)
        {
            password.AppendChar(c);
        }

        // Create credential
        PSCredential psc = new PSCredential(contexte.User, password);

        PSCommand command = new PSCommand();
        command.AddCommand("New-PSSession");
        command.AddParameter("Credential", psc);

        if (!String.IsNullOrEmpty(contexte.Serveur))
            command.AddParameter("computername", contexte.Serveur);
        if (!String.IsNullOrEmpty(contexte.RemoteUri))
            command.AddParameter("ConnectionUri", new Uri(contexte.RemoteUri));
        if (!string.IsNullOrEmpty(contexte.ConfigurationName))
            command.AddParameter("ConfigurationName", contexte.ConfigurationName);

        //// Create the session
        PowerShell powershell = PowerShell.Create();
        powershell.Commands = command;
        powershell.Runspace = Espace;
        Collection<PSObject> result = ExecuterCommande(command);

        if (result.Count != 1)
            throw new TechniqueException(MethodBase.GetCurrentMethod(),
                                            "Error while connecting.");

        // Create session variable
        command = new PSCommand();
        command.AddCommand("Set-Variable");
        command.AddParameter("Name", "ra");
        command.AddParameter("Value", result[0]);
        ExecuterCommande(command);
    }

private const string InvokeCommand = "Invoke-Command -ScriptBlock {{ {0} }} -Session $ra";

    private static string ExecuteRemoteScript(string cmd)
    {
        PSCommand command = new PSCommand();
        command.AddScript(string.Format(InvokeCommand, cmd));
        Collection<PSObject> result = ExecuterCommande(command);

        StringBuilder sb = new StringBuilder();
        foreach (PSObject obj in result)
        {
            sb.AppendLine(obj.ToString());
        }
        return sb.ToString().Trim();
    }
1

Спасибо, Рональд. Даже если ваш ответ не помог OP, это помогло мне.

У меня возникла проблема, просто пытающаяся запустить что-то в AppDomain на веб-сервере. Прямо на линии:

ObjectHandle handle = domain.CreateInstance(_assemblyName, _className);

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

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

Оказалось, что поток, на котором он был запущен, был создан при входе пользователя в систему, а IIS был в режиме "выдачи", поэтому текущий текущий поток был истекшим директором этого оригинального пользователя.

Я положил это на код:

IPrincipal oldPrincipal = Thread.CurrentPrincipal;
try
{
    Thread.CurrentPrincipal = new GenericPrincipal(
        new GenericIdentity("NetPlugins"), null);
    ObjectHandle handle = domain.CreateInstance(_assemblyName, _className);
    // unwrapping and calling code omitted
}
finally
{
    Thread.CurrentPrincipal = oldPrincipal;
}

Мне, вероятно, не нужно ставить старого принципала назад, а также, возможно, решить эту проблему, изменив текущий принцип перед созданием потока, о котором идет речь, но спасибо, Рональд: это работает как топ!

1

Что не так, когда вы вызываете Runspace.Open из другого потока, является симптомом той же проблемы, описанной здесь. У меня была такая же проблема в веб-проекте, где я начал новый поток из потока, который обслуживал запрос. Новый поток в конечном итоге называется командлетом Enable-Mailbox.

В моем случае проблема заключалась в том, что сам веб-запрос давно закончился до того, как произошел вызов Runspace.Open. Веб-запрос связан с WindowsIdentity, который хранится в потоке. Идентификатор передается потоку, который вызывает Runspace.Open. Тем не менее, поскольку поток, который обрабатывает веб-запрос, заканчивается перед этим вызовом, идентификатор удаляется до открытия рабочей области. Где-то во время вызова Runspace.Open предпринимается попытка получить токен безопасности для теперь удаленного идентификатора (см. Первую строку в вашем стеке: Microsoft.Win32.Win32Native.GetTokenInformation). Это не работает с ObjectDisposedException.

Есть два способа исправить это:

  • Не вызывайте Runspace.Open из другого потока. По крайней мере, не тогда, когда вызывающий поток заканчивается до Runspace.Open.
  • Перед тем, как позвонить Runspace.Open, убедитесь, что в потоке есть другая личность. Например, вы можете сделать следующее:

    Thread.CurrentPrincipal =
        new GenericPrincipal(new GenericIdentity("PSTest"), null)
    

    То, что вы заполните для имени GenericIdentity, действительно не имеет значения.

1

Вы пробовали это?

//set the default runspace for this thread
System.Management.Automation.Runspaces.Runspace.DefaultRunspace = runspace;

Ещё вопросы

Сообщество Overcoder
Наверх
Меню