Функция не найдена с первой попытки - PullRequest
0 голосов
/ 24 февраля 2020

У меня есть скрипт, который создает GUI для ввода параметров для создания пользователя AD. Скрипт содержит большую функцию (ту, которая создает браузер AD, который я нашел на net). Функция вызывается в самом начале скрипта (одно текстовое поле требует ввода от функции для предварительного заполнения значения) или нажатием кнопки в GUI. Проблема в том, что когда я запускаю скрипт, я получаю сообщение об ошибке, в котором говорится, что «термин« Browse-AD »(= имя функции) не распознается как имя командлета, функция ...» Когда я запускаю это из PowerShell ISE, я получаю эту ошибку при первой попытке, скрипт запускается (но браузер не работает) и после отмены скрипта и его повторного запуска браузер работает нормально, сообщение об ошибке не отображается. В то же время я не трогал сценарий, я просто запускал его во второй раз, без изменений. Но большая проблема в том, что когда скрипт запускается из файла напрямую (что, естественно, является его предполагаемым способом использования), это всегда первая попытка, и поэтому всегда отображается ошибка, и браузер не работает. Любая идея, что происходит?

Это функция:

function Browse-AD()
{
    # original inspiration: https://itmicah.wordpress.com/2013/10/29/active-directory-ou-picker-in-powershell/
    # author: Rene Horn the.rhorn@gmail.com
<#
    Copyright (c) 2015, Rene Horn
    All rights reserved.
    Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#>
    $dc_hash = @{}
    $selected_ou = $null
    Import-Module ActiveDirectory
    $forest = Get-ADForest
    [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | Out-Null
    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null

    function Get-NodeInfo($sender, $dn_textbox)
    {
        $selected_node = $sender.Node
        $dn_textbox.Text = $selected_node.Name
    }

    function Add-ChildNodes($sender)
    {
        $expanded_node = $sender.Node
        if ($expanded_node.Name -eq "root") {
            return
        }
        $expanded_node.Nodes.Clear() | Out-Null
        $dc_hostname = $dc_hash[$($expanded_node.Name -replace '(OU=[^,]+,)*((DC=\w+,?)+)','$2')]
        $child_OUs = Get-ADObject -Server $dc_hostname -Filter 'ObjectClass -eq "organizationalUnit" -or ObjectClass -eq "container"' -SearchScope OneLevel -SearchBase $expanded_node.Name
        if($child_OUs -eq $null) {
            $sender.Cancel = $true
        } else {
            foreach($ou in $child_OUs) {
                $ou_node = New-Object Windows.Forms.TreeNode
                $ou_node.Text = $ou.Name
                $ou_node.Name = $ou.DistinguishedName
                $ou_node.Nodes.Add('') | Out-Null
                $expanded_node.Nodes.Add($ou_node) | Out-Null
            }
        }
    }

    function Add-ForestNodes($forest, [ref]$dc_hash)
    {
        $ad_root_node = New-Object Windows.Forms.TreeNode
        $ad_root_node.Text = $forest.RootDomain
        $ad_root_node.Name = "root"
        $ad_root_node.Expand()
        $i = 1
        foreach ($ad_domain in $forest.Domains) {
            Write-Progress -Activity "Querying AD forest for domains and hostnames..." -Status $ad_domain -PercentComplete ($i++ / $forest.Domains.Count * 100)
            $dc = Get-ADDomainController -Server $ad_domain
            $dn = $dc.DefaultPartition
            $dc_hash.Value.Add($dn, $dc.Hostname)
            $dc_node = New-Object Windows.Forms.TreeNode
            $dc_node.Name = $dn
            $dc_node.Text = $dc.Domain
            $dc_node.Nodes.Add("") | Out-Null
            $ad_root_node.Nodes.Add($dc_node) | Out-Null
        }
        return $ad_root_node
    }

    $main_dlg_box = New-Object System.Windows.Forms.Form
    $main_dlg_box.ClientSize = New-Object System.Drawing.Size(400,600)
    $main_dlg_box.MaximizeBox = $false
    $main_dlg_box.MinimizeBox = $false
    $main_dlg_box.FormBorderStyle = 'FixedSingle'

    # widget size and location variables

    $ctrl_width_col = $main_dlg_box.ClientSize.Width/20
    $ctrl_height_row = $main_dlg_box.ClientSize.Height/15
    $max_ctrl_width = $main_dlg_box.ClientSize.Width - $ctrl_width_col*2
    $max_ctrl_height = $main_dlg_box.ClientSize.Height - $ctrl_height_row
    $right_edge_x = $max_ctrl_width
    $left_edge_x = $ctrl_width_col
    $bottom_edge_y = $max_ctrl_height
    $top_edge_y = $ctrl_height_row

    # setup text box showing the distinguished name of the currently selected node

    $dn_text_box = New-Object System.Windows.Forms.TextBox

    # can not set the height for a single line text box, that's controlled by the font being used

    $dn_text_box.Width = (14 * $ctrl_width_col)
    $dn_text_box.Location = New-Object System.Drawing.Point($left_edge_x, ($bottom_edge_y - $dn_text_box.Height))
    $main_dlg_box.Controls.Add($dn_text_box)

    # /text box for dN

    # setup Ok button

    $ok_button = New-Object System.Windows.Forms.Button
    $ok_button.Size = New-Object System.Drawing.Size(($ctrl_width_col * 2), $dn_text_box.Height)
    $ok_button.Location = New-Object System.Drawing.Point(($right_edge_x - $ok_button.Width), ($bottom_edge_y - $ok_button.Height))
    $ok_button.Text = "Ok"
    $ok_button.DialogResult = 'OK'
    $main_dlg_box.Controls.Add($ok_button)

    # /Ok button

    # setup tree selector showing the domains

    $ad_tree_view = New-Object System.Windows.Forms.TreeView
    $ad_tree_view.Size = New-Object System.Drawing.Size($max_ctrl_width, ($max_ctrl_height - $dn_text_box.Height - $ctrl_height_row*1.5))
    $ad_tree_view.Location = New-Object System.Drawing.Point($left_edge_x, $top_edge_y)
    $ad_tree_view.Nodes.Add($(Add-ForestNodes $forest ([ref]$dc_hash))) | Out-Null
    $ad_tree_view.Add_BeforeExpand({Add-ChildNodes $_})
    $ad_tree_view.Add_AfterSelect({Get-NodeInfo $_ $dn_text_box})
    $main_dlg_box.Controls.Add($ad_tree_view)

    # /tree selector

    if ($main_dlg_box.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK)
    {
        return  $dn_text_box.Text
    }
    return $null
}

Это определение кнопки, которая должна запустить браузер:

$Select_AD_path                  = New-Object System.Windows.Forms.Button
$Select_AD_path.Text             = "Browse..."
$Select_AD_path.Width            = 100
$Select_AD_path.Height           = 30
$Select_AD_path.Location         = New-Object System.Drawing.Point(90,155)
$Select_AD_path.Font             = 'Microsoft Sans Serif,10'
$Select_AD_path.add_Click({
    $dn = Browse-AD
    if ($dn)
    {
        $Location_val.Text = $dn
    }
})

Также я положил функция к значению текстового поля, так что браузер запускается до запуска самого скрипта и предварительно заполняет путь AD непосредственно до нужного мне значения:

$Location_val                    = New-Object system.Windows.Forms.TextBox
$Location_val.multiline          = $false
$Location_val.text               = Browse-AD
$Location_val.width              = 250
$Location_val.height             = 20
$Location_val.location           = New-Object System.Drawing.Point(200,160)

Точное сообщение об ошибке таково:

Browse-AD : The term 'Browse-AD' is not recognized as the name of a cmdlet, function, script file, or operable
program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\a.Petr.Synek\Documents\New_ADuser_woodgroup_complete.ps1:204 char:42
+ $Location_standard_val.Text            = Browse-AD
+                                          ~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Browse-AD:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

В 204-й строке есть текст: $Location_val.text = Browse-AD

1 Ответ

0 голосов
/ 26 февраля 2020

По запросу, мой комментарий как ответ:

В вашем скрипте вам нужно поставить Browse-AD функцию поверх , а весь остальной код под ним.

Если этот порядок неверен, при первом запуске функция вызывается до ее анализа , что выдает сообщение об ошибке.

Во второй раз PowerShell уже проанализировал весь код, поэтому функция известна. PowerShell (в отличие от VBScript) анализирует код сверху вниз.

...