Wenn man aus einer als Textdatei vorliegendem C/AL-Objektpaket von oftmals Hunderten oder mehr Objekten nur einige benötigt, die dort als ein neues Paket herausgelöst werden müssen (bspw. für Abgleichszwecke), ist das normalerweise eine fummelige Aufgabe, aber nun nicht mehr .
Mit diesem Skript kann man das Objektpaket einlesen, sich die daraus benötigten Objekte in einer Treeview zusammenklicken und die Auswahl dann als eine neue Objektpaketdatei exportieren.
Bedienung
Skript starten, automatisch erscheint das Fenster um das Quellpaket auswählen.
Das Quellpaket wird zerlegt. Wenn dieses abgeschlossen ist, wird die Hauptform erzeugt. In den Vordergrund kommt sie dabei, wenn das Skript im ISE läuft, nicht automatisch, diese hängt dann am PowerShell ISE-Icon in der Taskleiste. Bei Ausführung über die PowerShell-Konsole poppt das Fenster dagegen hoch.
Hier die benötigten Objekte in der Treeview zusammenklicken, der Inhalt des Objekts, für das aktuell ein Haken gesetzt wurde, wird jeweils in der Textbox angezeigt.
Entfernt man den Haken wieder, wird auch der Inhalt der Textbox gelöscht, aber die zugrundeliegende Objektdatei natürlich nicht . In der Treeview kann man beliebig oft mit den Haken in den Checkboxen hin und her klicken, bis man mit dem Ergebnis zufrieden ist. Dann oben links auf "Create Object Package" klicken.
Den Namen des neuen Objektpakets angeben.
Die ausgewählten Objekte werden zusammengestellt und als neues Objektpaket exportiert.
Dann die Form schlieĂźen, das Skript erneut starten und man kann sich durch Ă–ffnen der gerade gespeicherten Datei das erzeugte Objektpaket anschauen .
Es können beliebig viele neue Pakete aus einem Quellpaket erzeugt werden, für weitere Pakete einfach die Haken neu setzen und noch einmal auf "Create Object Package" und einen anderen Dateinamen wählen.
GUI braucht noch etwas Feintuning, aber der wichtigere Teil des genauen Textexports in unveränderter Objektreihenfolge läuft schon.
- Code: Alles auswählen
<#
.SYNOPSIS
Object Package Viewer and Configurator
.DESCRIPTION
This script opens a NAV Object Package, displays it in a treeview and offers multiple selection of single objects
to create a new object package
.NOTES
File Name : NAVObjectPackageConfigurator.ps1
Author : Kai Kowalewski
Requires : PowerShell V3
.LINK
.EXAMPLE
#>
function NAVObjectPackageConfigurator
{
# Install .Net Assemblies
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$ErrorActionPreference = "Stop"
$PSDefaultParameterValues['*:ErrorAction']='Stop'
$initialDirectory = 'C:\temp'
[Windows.Forms.Application]::EnableVisualStyles()
[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null
[reflection.assembly]::loadwithpartialname("System.Windows.Forms.SaveFileDialog") | Out-Null
$sourceEncoding = [System.Text.Encoding]::GetEncoding(850)
$dt = New-Object System.Data.Datatable
[void]$dt.Columns.Add("ObjectIndex")
[void]$dt.Columns.Add("ObjectFileName")
$dt.Columns["ObjectIndex"].Datatype = [string]
$dt.Columns["ObjectFileName"].DataType = [string]
[void]$dt.clear
function Read-SingleObjectFile
{
param (
$SingleObjectFilename
)
$richTextBox1.text = [System.IO.File]::ReadAllText("$WorkingFolder\SPLITNAVOBJ\$SingleObjectFileName", $sourceencoding)
$form1.refresh()
}
function Clear-ObjectContent
{
$richTextBox1.text = ""
$form1.refresh()
}
function Concat-SelectedObjectFiles
{
Add-Type -AssemblyName System.Windows.Forms
$SelectedTabCnt = 0
$SelectedPagCnt = 0
$SelectedRepCnt = 0
$SelectedCodCnt = 0
$SelectedXmlCnt = 0
$SelectedForCnt = 0
$SelectedDatCnt = 0
$SelectedQueCnt = 0
$SelectedMenCnt = 0
$outputfile = Get-SaveFile "$Workingfolder"
if ($outputfile -eq "") {throw 'Please select a file name for new object package'}
[int]$SelObjCnt = 0
if (test-path $outputfile) {Remove-Item $outputfile -force}
$dw = New-Object System.Data.DataView($dt)
$dw.Sort="ObjectIndex"
ForEach ($DataRowView in $dw)
{
$CurrIndex = $($DataRowView[0])
$CurrFile = $($DataRowView[1])
[String]$SelectedObjectChar = $DataRowView[1].Substring(0,1)
Switch ($SelectedObjectChar)
{
'T' {$SelectedTabCnt++}
'P' {$SelectedPagCnt++}
'R' {$SelectedRepCnt++}
'C' {$SelectedCodCnt++}
'X' {$SelectedXmlCnt++}
'F' {$SelectedForCnt++}
'D' {$SelectedDatCnt++}
'Q' {$SelectedQueCnt++}
'M' {$SelectedMenCnt++}
}
#Write-Host "$CurrIndex $CurrFile"
$SelObjCnt++
Add-Content -path $outputfile -value(Get-Content $WorkingFolder\SPLITNAVOBJ\$CurrFile)
}
[string]$SelectedObjectList = ""
if ($SelectedTABCnt -gt 0) {$SelectedObjectList += "TAB:$SelectedTABCnt "}
if ($SelectedFORCnt -gt 0) {$SelectedObjectList += "FOR:$SelectedFORCnt "}
if ($SelectedPAGCnt -gt 0) {$SelectedObjectList += "PAG:$SelectedPAGCnt "}
if ($SelectedREPCnt -gt 0) {$SelectedObjectList += "REP:$SelectedREPCnt "}
if ($SelectedDATCnt -gt 0) {$SelectedObjectList += "DAT:$SelectedDATCnt "}
if ($SelectedCODCnt -gt 0) {$SelectedObjectList += "COD:$SelectedCODCnt "}
if ($SelectedQUECnt -gt 0) {$SelectedObjectList += "QUE:$SelectedQUECnt "}
if ($SelectedXMLCnt -gt 0) {$SelectedObjectList += "XML:$SelectedXMLCnt "}
if ($SelectedMENCnt -gt 0) {$SelectedObjectList += "MEN:$SelectedMENCnt "}
[System.Windows.Forms.MessageBox]::Show("File $outputfile with $SelObjCnt objects ($SelectedObjectList) created.")
}
$button1_OnClick=
{
Concat-SelectedObjectFiles
}
$form1 = New-Object System.Windows.Forms.Form
$linkLabel1 = New-Object System.Windows.Forms.LinkLabel
$label4 = New-Object System.Windows.Forms.Label
$label3 = New-Object System.Windows.Forms.Label
$label2 = New-Object System.Windows.Forms.Label
$button1 = New-Object System.Windows.Forms.Button
$richTextBox1 = New-Object System.Windows.Forms.RichTextBox
$linkLabel1_OpenLink=
{
[system.Diagnostics.Process]::start($linkLabel1.text)
}
Function Get-SaveFile($initialDirectory)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
Out-Null
$SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
$SaveFileDialog.Title = 'Save new NAV object package'
$SaveFileDialog.initialDirectory = $initialDirectory
#$SaveFileDialog.filename = 'SelectedObjects.txt'
$SaveFileDialog.filter = "NAV Object Files (*.txt)|*.txt"
$SaveFileDialog.ShowDialog() | Out-Null
$SaveFileDialog.filename
}
Function Get-FileName($initialDirectory)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
$OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
$OpenFileDialog.initialDirectory = $initialDirectory
$OpenFileDialog.filter = "NAV Object Files (*.txt)|*.txt"
$OpenFileDialog.ShowDialog() | Out-Null
$OpenFileDialog.filename
}
$inputfile = Get-FileName -initialDirectory "$env:HOMEDRIVE\temp" # This is the default path in OpenFile window.
if ($inputfile -eq "") {throw 'Please select a file'}
[decimal]$filesize = ((Get-Item $inputfile).length/1MB)
$filesize =[math]::round($filesize,2)
$inputfile = resolve-path $inputfile
$WorkingFolder = Split-Path -Parent $inputfile
#$form = New-Object System.Windows.Forms.Form
$treeView1 = New-Object System.Windows.Forms.TreeView
$newNode = new-object System.Windows.Forms.TreeNode
$treeView1.Dock = 'Left'
$treeView1.CheckBoxes = $true
if (Test-path "$WorkingFolder\SPLITNAVOBJ\")
{Remove-Item -path "$WorkingFolder\SPLITNAVOBJ\" -Recurse -Force}
Write-Host "Splitting NAV objects from $inputfile (Size: $filesize MB) to $WorkingFolder\SPLITNAVOBJ\, this may take a while..." -ForegroundColor Yellow
$startime = Get-Date
$Sr = new-object System.IO.StreamReader($inputfile,[system.text.encoding]::GetEncoding(850))
$ObjectLine = @{}
$ObjectType = @{}
$ObjectID = @{}
$ObjectName = @{}
[int]$ObjCnt = 0
[int]$TabCnt = 0
[int]$PagCnt = 0
[int]$RepCnt = 0
[int]$CodCnt = 0
[int]$XmlCnt = 0
[int]$ForCnt = 0
[int]$DatCnt = 0
[int]$QueCnt = 0
[int]$MenCnt = 0
while (-not $Sr.EndOfStream)
{
$Currline = $sr.ReadLine()
if ($Currline.StartsWith('OBJECT'))
{
$TotalLineCnt++
[String]$ObjectChar = $Currline.Substring(7,1)
$ObjectLine = $currline.Split(' ')
$ObjCnt++
Switch ($ObjectChar)
{
'T' {$TabCnt++}
'P' {$PagCnt++}
'R' {$RepCnt++}
'C' {$CodCnt++}
'X' {$XmlCnt++}
'F' {$ForCnt++}
'D' {$DatCnt++}
'Q' {$QueCnt++}
'M' {$MenCnt++}
}
$ObjectFileName = $ObjectLine[1].Substring(0,3).ToUpper() + $ObjectLine[2] + ".TXT"
$ObjectFullName = $CurrLine.Substring(7)
#$ObjectLine.Add($TotalLineCnt,$TotalLineCnt)
$ObjectType.Add($ObjCnt,$ObjectLine[1])
$ObjectID.Add($ObjCnt,$ObjectLine[2])
$ObjectName.Add($ObjCnt,$ObjectFullName)
$newNode = new-object System.Windows.Forms.TreeNode
$newNode.Name = $ObjectFileName
$newNode.Text = $ObjectFullName
$newNode.Tag = $ObjCnt
$treeView1.Nodes.Add($newNode) | Out-Null
$Objectfile = New-Item -path "$WorkingFolder\SPLITNAVOBJ\$ObjectFileName" -type file -force
IF (Test-Path $ObjectFile) {Remove-Item $ObjectFile}
$sw = new-object System.IO.Streamwriter($Objectfile,$false,[system.text.encoding]::GetEncoding(850))
}
if (-not $Currline.StartsWith('}'))
{$sw.writeline($Currline)}
else
{
$sw.writeline($Currline)
$sw.writeline()
$sw.Flush()
}
}
$endtime = Get-Date
$time = $endtime - $startime
Write-Host "$ObjCnt NAV objects splitted to $WorkingFolder\SPLITNAVOBJ\ in $($time.Minutes)m:$($time.Seconds)s:$($time.Milliseconds)ms" -ForegroundColor Yellow
Write-Host "Tables: $TabCnt" -ForegroundColor Yellow
Write-Host "Pages: $PagCnt" -ForegroundColor Yellow
Write-Host "Reports: $RepCnt" -ForegroundColor Yellow
Write-Host "Codeunits: $CodCnt" -ForegroundColor Yellow
Write-Host "XMLPorts: $XMLCnt" -ForegroundColor Yellow
Write-Host "Queries: $QueCnt" -ForegroundColor Yellow
Write-Host "MenuSuites: $MenCnt" -ForegroundColor Yellow
if ($ForCnt -gt 0)
{Write-Host "Forms: $ForCnt" -ForegroundColor Yellow}
if ($DatCnt -gt 0)
{Write-Host "Dataports: $DatCnt" -ForegroundColor Yellow}
[string]$ObjectList = ''
if ($TABCnt -gt 0) {$ObjectList += "TAB:$TABCnt "}
if ($FORCnt -gt 0) {$ObjectList += "FOR:$FORCnt "}
if ($PAGCnt -gt 0) {$ObjectList += "PAG:$PAGCnt "}
if ($REPCnt -gt 0) {$ObjectList += "REP:$REPCnt "}
if ($DATCnt -gt 0) {$ObjectList += "DAT:$DATCnt "}
if ($CODCnt -gt 0) {$ObjectList += "COD:$CODCnt "}
if ($QUECnt -gt 0) {$ObjectList += "QUE:$QUECnt "}
if ($XMLCnt -gt 0) {$ObjectList += "XML:$XMLCnt "}
if ($MENCnt -gt 0) {$ObjectList += "MEN:$MENCnt "}
$sr.Close()
$sr.Dispose()
$sw.close()
$sw.Dispose()
$TV_AfterCheck =
{
if($_.Node.Checked)
{
$_.Node.Setfocus
#$_.Node.SetFont = New-Object System.Drawing.Font("Microsoft Sans Serif",9,1,3,0)
$row = $dt.NewRow()
[String]$TempIndex = $_.Node.Tag.ToString("0000000000")
$row["ObjectIndex"] = $TempIndex
$row["ObjectFileName"] = $_.Node.Name
$dt.Rows.Add($row)
$dt.AcceptChanges()
#[void]$dt.Rows.Add("$_.Node.Tag","$_.Node.Name")
Read-SingleObjectFile($_.Node.Name)
#[System.Windows.Forms.MessageBox]::Show('Node ' + $_.Node.Text + ' checked')
}
else
{
[String]$TempIndex = $_.Node.Tag.ToString("0000000000")
#[System.Windows.Forms.MessageBox]::Show('Node ' + $TempIndex + ' uncheckĂŞd')
$rows = $dt.Select("ObjectIndex = '$TempIndex'").Delete()
foreach($row2 in $rows)
{
$row2.Delete()
}
$dt.AcceptChanges
Clear-ObjectContent
#[System.Windows.Forms.MessageBox]::Show('Node ' + $_.Node.Text + ' unchecked')
}
}
$treeView1.Add_AfterCheck($TV_AfterCheck)
$form1.text = 'NAV Object Package Configurator ' + [System.IO.Path]::GetFileName($inputfile) + ' ' + 'Objects:' + $ObjCnt + ' ' + $ObjectList
$form1.Name = "form1"
$form1.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 838
$System_Drawing_Size.Height = 612
$form1.ClientSize = $System_Drawing_Size
$linkLabel1.Font = New-Object System.Drawing.Font("Microsoft Sans Serif",9,0,3,0)
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 155
$System_Drawing_Size.Height = 23
$linkLabel1.Size = $System_Drawing_Size
$linkLabel1.TabIndex = 10
$linkLabel1.Text = "http://www.msdynamics.de"
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 180
$System_Drawing_Point.Y = 0
$linkLabel1.Location = $System_Drawing_Point
$linkLabel1.TabStop = $True
$linkLabel1.DataBindings.DefaultDataSourceUpdateMode = 0
$linkLabel1.Name = "linkLabel1"
$linkLabel1.add_click($linkLabel1_OpenLink)
$form1.Controls.Add($linkLabel1)
$button1.TabIndex = 4
$button1.Name = "button1"
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 150
$System_Drawing_Size.Height = 23
$button1.Size = $System_Drawing_Size
$button1.UseVisualStyleBackColor = $True
$button1.Text = "Create Object Package"
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 0
$System_Drawing_Point.Y = 0
$button1.Location = $System_Drawing_Point
$button1.DataBindings.DefaultDataSourceUpdateMode = 0
$button1.add_Click($button1_OnClick)
$form1.Controls.Add($button1)
<#
$label3.TabIndex = 6
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 100
$System_Drawing_Size.Height = 23
$label3.Size = $System_Drawing_Size
$label3.Text = "Object Content"
$label3.Font = New-Object System.Drawing.Font("Microsoft Sans Serif",9,0,3,0)
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 255
$System_Drawing_Point.Y = 3
$label3.Location = $System_Drawing_Point
$label3.DataBindings.DefaultDataSourceUpdateMode = 0
$label3.Name = "label3"
$form1.Controls.Add($label3)
#>
<#
$label2.TabIndex = 5
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 100
$System_Drawing_Size.Height = 12
$label2.Size = $System_Drawing_Size
$label2.Text = "Object List"
$label2.Font = New-Object System.Drawing.Font("Microsoft Sans Serif",8,0,3,0)
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 8
$System_Drawing_Point.Y = 8
$label2.Location = $System_Drawing_Point
$label2.DataBindings.DefaultDataSourceUpdateMode = 0
$label2.Name = "label2"
$form1.Controls.Add($label2)
#>
$richTextBox1.Name = "richTextBox1"
$richTextBox1.Text = ""
$richTextBox1.DataBindings.DefaultDataSourceUpdateMode = 0
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 255
$System_Drawing_Point.Y = 30
$richTextBox1.Location = $System_Drawing_Point
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 800
$System_Drawing_Size.Height = 454
$richTextBox1.Size = $System_Drawing_Size
$richTextBox1.Font = New-Object System.Drawing.Font("Courier New",11,0,3,0)
$richTextBox1.WordWrap = $false
$richtextbox1.ScrollBars = 'ForcedBoth'
$richtextbox1.Dock = 'Fill'
$richtextbox1.ReadOnly = $true
$RichTextBox1.BackColor = [Drawing.Color]::White
$RichTextBox1.ForeColor = [Drawing.Color]::Black
$richTextBox1.TabIndex = 1
$form1.Controls.Add($richTextBox1)
$System_Drawing_Size = New-Object System.Drawing.Size
$System_Drawing_Size.Width = 300
$System_Drawing_Size.Height = 563
$treeView1.Size = $System_Drawing_Size
$treeView1.Name = "treeView1"
$System_Drawing_Point = New-Object System.Drawing.Point
$System_Drawing_Point.X = 100
$System_Drawing_Point.Y = 100
$treeView1.Location = $System_Drawing_Point
$treeView1.DataBindings.DefaultDataSourceUpdateMode = 0
$treeView1.TabIndex = 0
$treeView1.Font = New-Object System.Drawing.Font("Microsoft Sans Serif",10,0,3,0)
$form1.Controls.Add($treeView1)
#Save the initial state of the form
#$InitialFormWindowState = $form1.WindowState
# Menu Options - File / Exit
$menuExit = New-Object System.Windows.Forms.ToolStripMenuItem
$menuFile = New-Object System.Windows.Forms.ToolStripMenuItem
#$menuExit.Image = [System.IconExtractor]::Extract("shell32.dll", 10, $true)
$menuExit.ShortcutKeys = "Control, X"
$menuExit.Text = "Exit"
$menuExit.Add_Click({$form1.Close()})
[void]$menuFile.DropDownItems.Add($menuExit)
$menuMain = New-Object System.Windows.Forms.MenuStrip
$mainToolStrip = New-Object System.Windows.Forms.ToolStrip
# Main ToolStrip
[void]$form1.Controls.Add($mainToolStrip)
# Main Menu Bar
[void]$form1.Controls.Add($menuMain)
$form1.ShowDialog() | Out-Null
#Invoke-item "$WorkingFolder\SPLITNAVOBJ\"
}
NAVObjectPackageConfigurator
Wegen des intensiven GUI-Einsatzes wird mindestens PowerShell Version 3 benötigt, aber das sollte mittlerweile ja kein Problem mehr darstellen .
Links zum Einsatz des Treeviewcontrols in PowerShell
How Can I Use the Windows Forms TreeView Control? (die dort anfangs benutzte Sapien PrimalForms Community Editon, um den fĂĽr Forms notwendigen Quellcode schnell zu generieren, gibt es leider nicht mehr)
Object Package Configurator Objektpaket Konfigurator