PowerShell: Generate Symbol Reference für AL (mit GUI)

23. Januar 2018 18:36

Das manuelle Generieren von Symbolen für AL ist ja bislang nur über die Kommandozeile möglich :roll: (Neuer Parameter für finsql.exe command=generatesymbolreference) .
Running C/SIDE and AL Side-by-Side
Mit diesem Skript mit Eingabefenster ist es deutlich einfacher, die Kommandozeile wird damit erzeugt und ausgeführt. Der Objektfilter ist dabei optional. Vorher nicht vergessen, das neue Feld Enable loading application symbol references at server startup
ServerEnableSymbolReference.png
auf dem Server zu aktivieren und den Dienst danach neu zu starten.

Im Programmverzeichnis liegt nach Beendigung diese Datei, die aber leider kein Indiz für eine korrekte Erzeugung ist, siehe hier :!: .
GenerateSymbols1.png




GenerateSymbols.png


Der Beispielcode hier für das Fenster enthält übrigens einen Fehler (Script bzw. Global Scope der $x-Variablen fehlen), das ist im Skript natürlich korrigiert :wink: .

[Edit 11.02.18] Für noch mehr Komfort mit einer automatisch generierten Datenbankliste siehe diese Variante hier.

Für Business Central-Programmpfad
Code:
function GenerateSymbolsBC
{
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

    function GenerateSymbolsForDatabase
    {
        [CmdletBinding()]param (
            [String]$Server,
            [String]$Database,
        [String]$ObjectFilter)
        $NAVFolder = 'C:\Program Files (x86)\Microsoft Dynamics 365 Business Central\130\RoleTailored Client'
        if ($ObjectFilter -ne '')
        {$Command = """$NAVFolder\finsql.exe"" command=generatesymbolreference,servername=$Server,database=$Database,filter=$ObjectFilter"}
        else
        {$Command = """$NAVFolder\finsql.exe"" command=generatesymbolreference,servername=$Server,database=$Database"}
       
        Write-host $Command
        cmd /c $Command
    }


    $objForm = New-Object System.Windows.Forms.Form
    $objForm.Text = "Generate Symbols"
    $objForm.Size = New-Object System.Drawing.Size(300,300)
    $objForm.StartPosition = "CenterScreen"

    $objForm.KeyPreview = $True
    $objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") {$script:x=$objTextBox.Text;$script:x2=$objTextBox2.Text;$script:x3=$objTextBox3.Text;$objForm.Close()}})
    $objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
    {$objForm.Close()}})

    $OKButton = New-Object System.Windows.Forms.Button
    $OKButton.Location = New-Object System.Drawing.Size(75,220)
    $OKButton.Size = New-Object System.Drawing.Size(75,23)
    $OKButton.Text = "OK"
    $OKButton.Add_Click({$script:x=$objTextBox.Text;$script:x2=$objTextBox2.Text;$script:x3=$objTextBox3.Text;$objForm.Close()})
    $objForm.Controls.Add($OKButton)

    $CancelButton = New-Object System.Windows.Forms.Button
    $CancelButton.Location = New-Object System.Drawing.Size(150,220)
    $CancelButton.Size = New-Object System.Drawing.Size(75,23)
    $CancelButton.Text = "Cancel"
    $CancelButton.Add_Click({$objForm.Close()})
    $objForm.Controls.Add($CancelButton)

    #############
    $objLabel = New-Object System.Windows.Forms.Label
    $objLabel.Location = New-Object System.Drawing.Size(10,20)
    $objLabel.Size = New-Object System.Drawing.Size(280,20)
    $objLabel.Text = "Server"
    $objForm.Controls.Add($objLabel)

    $objTextBox = New-Object System.Windows.Forms.TextBox
    $objTextBox.Location = New-Object System.Drawing.Size(10,40)
    $objTextBox.Size = New-Object System.Drawing.Size(260,20)
    $objForm.Controls.Add($objTextBox)

    $objLabel2 = New-Object System.Windows.Forms.Label
    $objLabel2.Location = New-Object System.Drawing.Size(10,65)
    $objLabel2.Size = New-Object System.Drawing.Size(280,20)
    $objLabel2.Text = "Database"
    $objForm.Controls.Add($objLabel2)

    $objTextBox2 = New-Object System.Windows.Forms.TextBox
    $objTextBox2.Location = New-Object System.Drawing.Size(10,85)
    $objTextBox2.Size = New-Object System.Drawing.Size(260,20)
    $objForm.Controls.Add($objTextBox2)
 
    $objLabel3 = New-Object System.Windows.Forms.Label
    $objLabel3.Location = New-Object System.Drawing.Size(10,105)
    $objLabel3.Size = New-Object System.Drawing.Size(280,20)
    $objLabel3.Text = "Object Filter"
    $objForm.Controls.Add($objLabel3)

    $objTextBox3 = New-Object System.Windows.Forms.TextBox
    $objTextBox3.Location = New-Object System.Drawing.Size(10,125)
    $objTextBox3.Size = New-Object System.Drawing.Size(260,20)
    $objForm.Controls.Add($objTextBox3)


    $objForm.Topmost = $True

    $objForm.Add_Shown({$objForm.Activate()})
    [void] $objForm.ShowDialog()

    #Write-host "$x $x2 $x3"


    if (($x -ne '') -and ($x2 -ne '') -and ($x3 -eq ''))
    {GenerateSymbolsForDatabase -Server $x -Database $x2}
    elseif (($x -ne '') -and ($x2 -ne '') -and ($x3 -ne ''))
    {GenerateSymbolsForDatabase -Server $x -Database $x2 -ObjectFilter $x3}
    else
    {
        [System.Windows.Forms.MessageBox]::Show("Please enter server and database. Object Filter is an option.")
        Write-Error 'Please enter server and database. Object Filter is an option.'
    }
}
GenerateSymbolsBC

Für NAV-Programmpfad
Code:
function GenerateSymbolsNAV
{
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

    function GenerateSymbolsForDatabase
    {
        [CmdletBinding()]param (
            [String]$Server,
            [String]$Database,
        [String]$ObjectFilter)
        $NAVFolder = 'C:\Program Files (x86)\Microsoft Dynamics NAV\110\RoleTailored Client'
        if ($ObjectFilter -ne '')
        {$Command = """$NAVFolder\finsql.exe"" command=generatesymbolreference,servername=$Server,database=$Database,filter=$ObjectFilter"}
        else
        {$Command = """$NAVFolder\finsql.exe"" command=generatesymbolreference,servername=$Server,database=$Database"}
       
        Write-host $Command
        cmd /c $Command
    }


    $objForm = New-Object System.Windows.Forms.Form
    $objForm.Text = "Generate Symbols"
    $objForm.Size = New-Object System.Drawing.Size(300,300)
    $objForm.StartPosition = "CenterScreen"

    $objForm.KeyPreview = $True
    $objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") {$script:x=$objTextBox.Text;$script:x2=$objTextBox2.Text;$script:x3=$objTextBox3.Text;$objForm.Close()}})
    $objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
    {$objForm.Close()}})

    $OKButton = New-Object System.Windows.Forms.Button
    $OKButton.Location = New-Object System.Drawing.Size(75,220)
    $OKButton.Size = New-Object System.Drawing.Size(75,23)
    $OKButton.Text = "OK"
    $OKButton.Add_Click({$script:x=$objTextBox.Text;$script:x2=$objTextBox2.Text;$script:x3=$objTextBox3.Text;$objForm.Close()})
    $objForm.Controls.Add($OKButton)

    $CancelButton = New-Object System.Windows.Forms.Button
    $CancelButton.Location = New-Object System.Drawing.Size(150,220)
    $CancelButton.Size = New-Object System.Drawing.Size(75,23)
    $CancelButton.Text = "Cancel"
    $CancelButton.Add_Click({$objForm.Close()})
    $objForm.Controls.Add($CancelButton)

    #############
    $objLabel = New-Object System.Windows.Forms.Label
    $objLabel.Location = New-Object System.Drawing.Size(10,20)
    $objLabel.Size = New-Object System.Drawing.Size(280,20)
    $objLabel.Text = "Server"
    $objForm.Controls.Add($objLabel)

    $objTextBox = New-Object System.Windows.Forms.TextBox
    $objTextBox.Location = New-Object System.Drawing.Size(10,40)
    $objTextBox.Size = New-Object System.Drawing.Size(260,20)
    $objForm.Controls.Add($objTextBox)

    $objLabel2 = New-Object System.Windows.Forms.Label
    $objLabel2.Location = New-Object System.Drawing.Size(10,65)
    $objLabel2.Size = New-Object System.Drawing.Size(280,20)
    $objLabel2.Text = "Database"
    $objForm.Controls.Add($objLabel2)

    $objTextBox2 = New-Object System.Windows.Forms.TextBox
    $objTextBox2.Location = New-Object System.Drawing.Size(10,85)
    $objTextBox2.Size = New-Object System.Drawing.Size(260,20)
    $objForm.Controls.Add($objTextBox2)
 
    $objLabel3 = New-Object System.Windows.Forms.Label
    $objLabel3.Location = New-Object System.Drawing.Size(10,105)
    $objLabel3.Size = New-Object System.Drawing.Size(280,20)
    $objLabel3.Text = "Object Filter"
    $objForm.Controls.Add($objLabel3)

    $objTextBox3 = New-Object System.Windows.Forms.TextBox
    $objTextBox3.Location = New-Object System.Drawing.Size(10,125)
    $objTextBox3.Size = New-Object System.Drawing.Size(260,20)
    $objForm.Controls.Add($objTextBox3)


    $objForm.Topmost = $True

    $objForm.Add_Shown({$objForm.Activate()})
    [void] $objForm.ShowDialog()

    #Write-host "$x $x2 $x3"


    if (($x -ne '') -and ($x2 -ne '') -and ($x3 -eq ''))
    {GenerateSymbolsForDatabase -Server $x -Database $x2}
    elseif (($x -ne '') -and ($x2 -ne '') -and ($x3 -ne ''))
    {GenerateSymbolsForDatabase -Server $x -Database $x2 -ObjectFilter $x3}
    else
    {
        [System.Windows.Forms.MessageBox]::Show("Please enter server and database. Object Filter is an option.")
        Write-Error 'Please enter server and database. Object Filter is an option.'
    }
}
GenerateSymbolsNAV
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

ImplicitType=Integer

26. Januar 2018 11:15

Kowa hat geschrieben:Running C/SIDE and AL Side-by-Side

Der dort erwähnte pauschale Startparameter generatesymbolreference=yes im Abschnitt Continuously generate symbols each time you compile objects in C/SIDE verursacht aktuell einen Bug, der aus dem Development Client exportierte C/AL-Code von Pages enthält dann bei Feldern die zusätzliche Zeile
Code:
ImplicitType=Integer

die den erneuten Import verhindert. (Quelle)
Beispiel Page 1310
ImplicitType.png

ImplicitType
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

Variante mit Drilldown-Datenbankauswahl

12. Februar 2018 00:08

Diese Variante bietet mehr Eingabekomfort bei der Auswahl der Datenbank, erfordert dazu aber ein installiertes SQL Server Management Studio (SMO/Server Class).
Bei diesem Skript kann man nach Eingabe des Servernamens auf den Button "List Databases" klicken.
GenerateSymbolsSQLMgt1.png

Dieser Klick ermittelt dann alle dortigen Datenbanken und listet diese in dem Drilldown (Combobox) des Databasefeldes auf, wo man die benötigte Datenbank dann auswählen kann.
GenerateSymbolsSQLMgt2.png
Auf das Erzeugen dieser Liste nur durch Klicken in das Feld bzw. wenn dieses den Fokus erhält, habe ich bewusst verzichtet, weil Tippfehler beim Serverfeld dann zu den bekannten längeren Suchaktionen nach einem nicht vorhandenen Server führen, die man aus den entsprechenden Feldern im NAV Development Environment ja leider nur allzugut kennt :roll: .


Code:
function GenerateSymbolsSQLMgt
{
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
    [void] [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")

    function GenerateSymbolsForDatabase
    {
        [CmdletBinding()]param (
            [String]$Server,
            [String]$Database,
        [String]$ObjectFilter)
        $NAVFolder = 'C:\Program Files (x86)\Microsoft Dynamics NAV\110\RoleTailored Client'
        if ($ObjectFilter -ne '')
        {$Command = """$NAVFolder\finsql.exe"" command=generatesymbolreference,servername=$Server,database=$Database,filter=$ObjectFilter"}
        else
        {$Command = """$NAVFolder\finsql.exe"" command=generatesymbolreference,servername=$Server,database=$Database"}
       
        Write-host $Command
        cmd /c $Command
    }


    function Get-DBList($server)
    {
       
        $script:DatabaseName  = @{}
        $srv = New-Object 'Microsoft.SqlServer.Management.Smo.Server' $server
   
        [int]$i = 0
        foreach($sqlDatabase in $srv.Databases)
        {
            $i++
            #[string]$ts = "$i"
            #write-host "$i,$sqlDatabase"
       
            $DatabaseName.Add($i,$sqlDatabase)
        }
        foreach($databaseID in $databasename.keys)
        {
   
            $DBname =  $databasename[$databaseID]
            $DBname2 = $DBname.ToString()
            $DBname2 = $DBname2.trimstart('[')
            $DBname2 = $DBname2.trimend(']')
            $IsSystemDatabase = ($DBname2 -like 'master') -or ($DBname2 -like 'tempdb') -or ($DBname2 -like  'model') -or ($DBname2 -like 'msdb') -or ($DBname2.startswith('ReportServer'))
            if (-not $IsSystemDatabase)
            {$comboBox1.Items.add($DBname2)}
        }

    }
    $objForm = New-Object System.Windows.Forms.Form
    $objForm.Text = "Generate Symbols"
    $objForm.Size = New-Object System.Drawing.Size(300,300)
    $objForm.StartPosition = "CenterScreen"

    $objForm.KeyPreview = $True
    $objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter") {$script:x=$objTextBox.Text;$script:x2=$comboBox1.Text;$script:x3=$objTextBox3.Text;$objForm.Close()}})

    $objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
    {$objForm.Close()}})

   


    $OKButton = New-Object System.Windows.Forms.Button
    $OKButton.Location = New-Object System.Drawing.Size(95,220)
    $OKButton.Size = New-Object System.Drawing.Size(75,23)
    $OKButton.Text = "OK"
    $OKButton.Add_Click({$script:x=$objTextBox.Text;$script:x2=$ComboBox1.Text;$script:x3=$objTextBox3.Text;$objForm.Close()})
    $objForm.Controls.Add($OKButton)

    $GetDBButton = New-Object System.Windows.Forms.Button
    $GetDBButton.Location = New-Object System.Drawing.Size(5,220)
    $GetDBButton.Size = New-Object System.Drawing.Size(95,23)
    $GetDBButton.Text = "List databases"
    $GetDBButton.Add_Click({
            $script:x=$objTextBox.Text
            if ($script:x -ne '') {Get-DBlist($script:x)} else
            {[System.Windows.Forms.MessageBox]::Show("Please enter <servername>\<instance> in the server field.")}
    })
    $objForm.Controls.Add($GetDBButton)

    $CancelButton = New-Object System.Windows.Forms.Button
    $CancelButton.Location = New-Object System.Drawing.Size(165,220)
    $CancelButton.Size = New-Object System.Drawing.Size(75,23)
    $CancelButton.Text = "Cancel"
    $CancelButton.Add_Click({$objForm.Close()})
    $objForm.Controls.Add($CancelButton)

    #############
    $objLabel = New-Object System.Windows.Forms.Label
    $objLabel.Location = New-Object System.Drawing.Size(10,20)
    $objLabel.Size = New-Object System.Drawing.Size(280,20)
    $objLabel.Text = "Server"
    $objForm.Controls.Add($objLabel)

    $objTextBox = New-Object System.Windows.Forms.TextBox
   
    $objTextBox.Location = New-Object System.Drawing.Size(10,40)
    $objTextBox.Size = New-Object System.Drawing.Size(260,20)
    $objForm.Controls.Add($objTextBox)
   
   

    $objLabel2 = New-Object System.Windows.Forms.Label
    $objLabel2.Location = New-Object System.Drawing.Size(10,65)
    $objLabel2.Size = New-Object System.Drawing.Size(280,20)
    $objLabel2.Text = "Database"
    $objForm.Controls.Add($objLabel2)

   
    $script:comboBox1 = New-Object System.Windows.Forms.ComboBox
    $comboBox1.Location = New-Object System.Drawing.Size(10,85)
    $comboBox1.Size = New-Object System.Drawing.Size(260,20)
    $objForm.Controls.Add($comboBox1)

 
    $objLabel3 = New-Object System.Windows.Forms.Label
    $objLabel3.Location = New-Object System.Drawing.Size(10,105)
    $objLabel3.Size = New-Object System.Drawing.Size(280,20)
    $objLabel3.Text = "Object Filter"
    $objForm.Controls.Add($objLabel3)

    $objTextBox3 = New-Object System.Windows.Forms.TextBox
    $objTextBox3.Location = New-Object System.Drawing.Size(10,125)
    $objTextBox3.Size = New-Object System.Drawing.Size(260,20)
    $objForm.Controls.Add($objTextBox3)


    $objForm.Topmost = $True

    $objForm.Add_Shown({$objForm.Activate()})
    [void] $objForm.ShowDialog()

    #Write-host "$x $x2 $x3"


    if (($x -ne '') -and ($x2 -ne '') -and ($x3 -eq ''))
    {GenerateSymbolsForDatabase -Server $x -Database $x2}
    elseif (($x -ne '') -and ($x2 -ne '') -and ($x3 -ne ''))
    {GenerateSymbolsForDatabase -Server $x -Database $x2 -ObjectFilter $x3}
    else
    {
        [System.Windows.Forms.MessageBox]::Show("Please enter server and database. Object Filter is an option.")
        Write-Error 'Please enter server and database. Object Filter is an option.'
    }
}
GenerateSymbolsSQLMgt
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

Re: PowerShell: Generate Symbol Reference für AL (mit GUI)

26. Februar 2019 11:55

Bei der navcommandresult.txt im Programmverzeichnis sollte man auch den Inhalt kontrollieren.
Diese kann nämlich auch Warnungen enthalten, die dann das Generieren von Symbols für bestimmte Objekte verhindern, so wie hier bei nicht mehr erlaubter UsageCategory in den RequestPages von Reports ab einem bestimmten Build (in diesem Fall BC CU 03).
FailedSymbolGeneration.png
Ein Fehlerprotokoll (Datei naverrorlog.txt an gleichem Ort) wird in solchen Fällen nicht erzeugt :roll: .
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.