vendredi 26 janvier 2018

Annonce - Nouveau compilateur

J'ai téléchargé la dernier version de mon "compilateur" préféré. Après quelques testes, je me suis rendu compte que plusieurs problèmes avait été résolue dans les dernières versions :

- Intégration natif des descriptions dans le champs détail de l'exe.
- Résolution des problèmes d'accents qui s'affichait mal.
- La fonction "Parcourir les dossiers" fonctionne également.
- Les faux positif avec les anti-virus semble également résolu.
 
Auparavant, j'étais obligé de modifier le code du compilateur pour contourner tout ces petites problèmes. Malheureusement les faux positif de l'anti-virus étais devenu vraiment très gênant. Du coup, j'ai "recompiler" tout mes applications avec la nouvelle version, et maintenant mes anti-virus ne râle plus du tout.
J'en ai profité pour moderniser l'interface d'Eco 7. Actuellement, je continue à moderniser son code. Je compte faire quelques modifications ici et là, et en particulier intégrer la base de données LiteDB.

jeudi 11 janvier 2018

Annonce - Le site passe en Https

Il y a quelques jours, j'ai appris que le https était devenu activable sur les blogs utilisant un domaine personnalisé. Je suis passé aussitôt sur le site bêta de Blogger (draft.blogger.com) pour activé la fonction HTTPS sur mon domaine personnalisé. Il faut un peu de temps pour que cela se mette en place. Après 24/48h le site est enfin accessible en https.

Pour que le site soit pleinement compatible http et https, j'ai du modifier le code de mes liens internes. Pour cela j'ai utilisé l'astuce suivant :

Généralement, les liens sont écrit sous la forme href="http://exemple.com". Si j'ouvre mon site en mode https, ce lien basculera le site en mode http. Pour corriger cela, je pourrai modifier mon lien pour qu'il ouvre la page en mode https en écrivant href="https://exemple.com". Malheureusement, si j'ouvre mon site en mode http, cela basculera le site en https. Pour éviter que le site bascule d'un mode à l'autre en cours d'exploration, l'astuce consiste à supprimer les mots "http:" et "https:" dans mes liens internes en utilisant la forme href="//exemple.com". Le navigateur comprendra alors qu'il faut conserver le protocole utilisé à l'ouverture du site et ne basculera plus inopinément entre les modes http et https.

Note du 2/3/2018 : Pour essayer de trouver une solution à mes problèmes d'indexations, j'ai basculé le site en full https.

vendredi 5 janvier 2018

Mémo-LiteDB (LiteDB.FileStorage)

Memo-LiteDB est un exemple d'utilisation des fonctions "FileStorage" de LiteDB. Ce programme ouvre un fichier RichTextBox depuis la base de données, et l'affiche dans la fenêtre "Mémo" après l'avoir décrypté à l'aide du mot de passe saisi à l'ouverture du programme. A la fermeture de la fenêtre, le fichier est enregistré dans la base de données LiteDB après l'avoir crypté.

Memo-LiteDB


Ce programme nécessite la Dll "LiteDB.dll" v5.0.10. Créer un dossier "ldb" au même niveau que le script. Puis télécharger la Dll dans le dossier "ldb".

Site de référence : http://www.litedb.org


 

###################################################################################
#                                                                                 #
# Mémo v2.1.0                                                                     #
# http://powershell.sekki.fr                                                      #
# Validé sous : Windows 10 / PowerShell v5 / LiteDB v5                            #
#                                                                                 #
###################################################################################


#Assembly.
[void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")

#Chemin courant.
$global:curpath = ""
if ($psISE) { $global:curpath = Split-Path -parent $psISE.CurrentFile.Fullpath }
else        { $global:curpath = Split-Path $MyInvocation.MyCommand.Path }

#Alerte.
function Alerte([string]$TEXT,[switch]$Q)
  {
    if (!$TEXT) { Return }
    $MessageBox = [Windows.Forms.MessageBox]
    if ($Q) { $BoutonBox = [Windows.Forms.MessageBoxButtons]::YESNO
              $titre     = "Memo - Choix"
              $IconBox   = [windows.forms.MessageBoxIcon]::Question
              $MessageBox::show($TEXT,$Titre,$BoutonBox,$IconBox) }
    else    { $BoutonBox = [Windows.Forms.MessageBoxButtons]::OK
              $titre     = "Memo - Alerte"
              $IconBox   = [windows.forms.MessageBoxIcon]::Warning
              $null      = $MessageBox::show($TEXT,$Titre,$BoutonBox,$IconBox) }
  }

#Charge les fonctions LiteDB.
Try { [void][Reflection.Assembly]::LoadFile($global:curpath+"\ldb\LiteDB.dll") }
Catch { Alerte "LiteDB.dll manquant."; Return }


################################################
# Fonctions.
################################################


#Input.
function Password([string]$TEXT)
{
   $formB = New-Object Windows.Forms.Form
   $formB.text = "Mémo"
   $formB.ClientSize = New-Object System.Drawing.Size(220,70)
   $formB.MinimumSize = New-Object System.Drawing.Size(220,70)
   $labelB1 = New-Object Windows.Forms.Label
   $labelB1.Location = New-Object Drawing.Point 10,10
   $labelB1.Size = New-Object Drawing.Point 200,16
   $labelB1.Text = $TEXT
   $textB1 = New-Object Windows.Forms.MaskedTextBox
   $textB1.PasswordChar = '*'
   $textB1.Location = New-Object Drawing.Point 10,30
   $textB1.Size = New-Object Drawing.Point 200,16
   $textB1.Add_KeyDown({ if ($_.KeyCode -eq "Enter") { $formB.Close() } })
   $formB.Controls.Add($labelB1)
   $formB.Controls.Add($textB1)
   [void]$formB.ShowDialog()
   $textB1.Text
}

#Codage/décodage du texte.
function Code_Txt([string]$crcode,[string]$mk,[int]$crsw)
{
   $j = 0
   $out = ""
   $key = @()
   $lk = $mk.length
   $lc = $crcode.length
   For($i = 0;$i -lt $lk;$i++) { $d=[Convert]::ToInt32($mk[$i], 16); $key += ,$d }
   For($i = 0;$i -lt $lc;$i++)
    {
      $dec = [byte][char]$crcode[$i]
      $dec = $dec + ($key[$j] * $crsw)
      if ($dec -lt 0)   {$dec = $dec + 256}
      if ($dec -ge 256) {$dec = $dec - 256}
      $car = [char][byte]$dec
      $out = $out + $car
      $j++
      if ($j -gt $lk) {$j = 0}
    }
   return $out
}

#Fonctions RichTextBox (voir article dédié).
Function ChangeRT($rtmode,$rtdata)
{
   $rtfont  = $global:textA1.SelectionFont.FontFamily
   $rtsize  = $global:textA1.SelectionFont.Size
   $rtstyle = $global:textA1.SelectionFont.Style
   if ($rtmode -eq 1) { $rtfont = $rtdata }   #Mode 1 : Change le nom.
   if ($rtmode -eq 2) { $rtsize = $rtdata }   #Mode 2 : Change la taille.
   if ($rtmode -eq 3) {                       #Mode 3 : Change le style.
                        $tbit = $rtstyle -band $rtdata
                        if ($tbit -ne $rtdata)
                          { $rtstyle = $rtstyle -bor $rtdata }
                        else                  
                          { $rbit = 255 -bxor $rtdata
                            $rtstyle = $rtstyle -band $rbit  }
                      }
   $myfont  = New-Object System.Drawing.Font($rtfont,$rtsize,$rtstyle,3,0)
   $global:textA1.SelectionFont = $myfont
}

#Template Bride.
Function New_Button([string]$text,[int]$p)
{
   $l = 72
   $button = New-Object Windows.Forms.Button
   $button.Location = New-Object Drawing.Point ((($l-1)*($p-1))-1),-1
   $button.Size = New-Object Drawing.Point $l,23
   $button.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
   $button.BackColor = [System.Drawing.Color]::FromArgb(255,225,225,225)
   $button.Text = $text
   return $button
}


################################################
# Main.
################################################


#Initialisation.
$global:bride = 1

#Ouvrir/Créer une Base de Données.
$db = New-Object LiteDB.LiteDatabase(($curpath+"\ldb\memo.db"),$null)

#Demander le code d'ouverture.
$smdp = Password "Code d'ouverture."

#Converti le mot de passe en clé.
$imdp = ""
$l = $smdp.length
for($i = 0;$i -lt $l;$i++)
{
   if ($smdp[$i] -match "^[0-9]+$") { $imdp += $smdp[$i] }
   else { $imdp += [string][int][char]$smdp[$i] }
}

#Vérification.
if ($imdp.length -lt 4) { Alerte "Le mot de passe n'est pas valide."; Return }

#Fenêtre principale.
$formA = New-Object Windows.Forms.Form
$formA.text = "Mémo - v2.1.0"
$formA.ClientSize = New-Object System.Drawing.Size(279,367)
$formA.MinimumSize = New-Object System.Drawing.Size(300,186)
$formA.KeyPreview  = $True
$formA.Add_KeyDown({if ($_.KeyCode -eq "Escape") { $gridA1.ClearSelection() }})

#Client Size.
$csw = $formA.ClientSize.Width
$csh = $formA.ClientSize.Height

#Couleurs.
$noir  = [System.Drawing.Color]::FromArgb(255,0,0,0)
$rouge = [System.Drawing.Color]::FromArgb(255,200,0,0)
$vert  = [System.Drawing.Color]::FromArgb(255,0,200,0)
$gris1 = [System.Drawing.Color]::FromArgb(255,240,240,240)
$bleu1 = [System.Drawing.Color]::FromArgb(255,94,191,212)
$blue2 = [System.Drawing.Color]::FromArgb(255,44,40,109)

#Création du menu contextuel.
$menu1 = New-Object System.Windows.Forms.ContextMenu
$item1 = New-Object System.Windows.Forms.MenuItem -ArgumentList "Gras"
$item1.Add_Click({ ChangeRT 3 ([System.Drawing.FontStyle]::Bold) })
$rtn = $menu1.MenuItems.Add($item1)
$item2 = New-Object System.Windows.Forms.MenuItem -ArgumentList "Italique"
$item2.Add_Click({ ChangeRT 3 ([System.Drawing.FontStyle]::Italic) })
$rtn = $menu1.MenuItems.Add($item2)
$item3 = New-Object System.Windows.Forms.MenuItem -ArgumentList "Souligne"
$item3.Add_Click({ ChangeRT 3 ([System.Drawing.FontStyle]::Underline) })
$rtn = $menu1.MenuItems.Add($item3)
$rtn = $menu1.MenuItems.Add("-")
$item4 = New-Object System.Windows.Forms.MenuItem -ArgumentList "Noir"
$item4.Add_Click({ $global:textA1.SelectionColor = $noir })
$rtn = $menu1.MenuItems.Add($item4)
$item5 = New-Object System.Windows.Forms.MenuItem -ArgumentList "Rouge"
$item5.Add_Click({ $global:textA1.SelectionColor = $rouge })
$rtn = $menu1.MenuItems.Add($item5)
$item6 = New-Object System.Windows.Forms.MenuItem -ArgumentList "Vert"
$item6.Add_Click({ $global:textA1.SelectionColor = $vert })
$rtn = $menu1.MenuItems.Add($item6)

#Création d'une RichTextBox.
$textA1 = New-Object Windows.Forms.RichTextBox
$textA1.Location = New-Object Drawing.Point -1, 21
$textA1.Size = New-Object Drawing.Point ($csw+2),($csh-36)
$textA1.Font = New-Object System.Drawing.Font("Lucida Console","10",0,3,0)
$textA1.DetectUrls = $true   #Active les liens Hyperlink.
$textA1.add_LinkClicked({ Invoke-Expression "start $($_.LinkText)" })
$textA1.ContextMenu = $menu1
$textA1.Visible = $true

#DataGrid - DataGridView.
$gridA1 = New-Object System.Windows.Forms.DataGridView
$gridA1.Location = New-Object Drawing.Point -1, 21
$gridA1.Size = New-Object Drawing.Point ($csw+2),($csh-36)
$gridA1.ScrollBars = "Vertical"
$gridA1.ReadOnly = $true
$gridA1.AutoSize = $false
$gridA1.GridColor = "Black"
$gridA1.AllowUserToResizeRows = $false
$gridA1.BorderStyle = [System.Windows.Forms.BorderStyle]::None
$gridA1.BackgroundColor = $gris1
$gridA1.DefaultCellStyle.SelectionBackColor = $bleu1
#$gridA1.SelectionMode = "FullRowSelect"
$gridA1.MultiSelect = $false


#DataGrid - Titre et Entête.
$gridA1.RowHeadersVisible    = $false
$gridA1.ColumnHeadersVisible = $true
$gridA1.ColumnHeadersHeightSizeMode = "DisableResizing"
$gridA1.EnableHeadersVisualStyles = $false
$gridA1.ColumnHeadersDefaultCellStyle.BackColor = $blue2
$gridA1.ColumnHeadersDefaultCellStyle.ForeColor = "White"
$gridA1.ColumnHeadersBorderStyle = "Single"
$gridA1.Visible = $false
$gridA1.ContextMenu = $menu1

#Script : #Charge les données dans la DataGrid.
$script1 = {
             $ascm = [System.Windows.Forms.DataGridViewAutoSizeColumnMode]::Fill
             $grille = $true; $i = 0; $noah = $false
             $lines = ($textA1.Text).split("`r`n")
             foreach ($line in $lines)
              {
                if ($line -notmatch "^.*;.*$") { $grille = $false}
              }
             if ($grille)
               {
                 $gridA1.Columns.Clear()
                 $textA1.Visible = $false
                 $labels = $lines[0].split(";")
                 $gridA1.ColumnCount = $labels.length
                 foreach ($label in $labels)
                  {
                    $gridA1.Columns[$i++].Name = $label
                  }
                 foreach($line in $lines)
                  {
                    if ($noah) { [void]$gridA1.Rows.Add($line.split(";")) }
                    else       { $noah = $true } }
                 foreach($column in $gridA1.Columns)
                  {
              
                    $column.SortMode = "NotSortable"
                    $column.AutoSizeMode = $ascm
                  }  
                 $gridA1.Visible = $true
                 $gridA1.ClearSelection()
                 $menu2 = New-Object System.Windows.Forms.ContextMenuStrip
                 [void]$menu2.Items.Add("Copier")
                 [void]$menu2.Items.Add("Editer")
                 $menu2.Add_ItemClicked({
Switch ($_.ClickedItem) {
Editer { $textA1.Visible = $true; $gridA1.Visible = $false }
Copier { $r = $gridA1.SelectedCells[0].RowIndex
          $c = $gridA1.SelectedCells[0].ColumnIndex
          [Windows.Forms.Clipboard]::Clear()
          [Windows.Forms.Clipboard]::SetText($gridA1.Rows[$r].Cells[$c].Value) }
        }  
                                       })
                 foreach($column in $gridA1.Columns)
                  {
                    $column.ContextMenuStrip = $menu2
                  }
               }
             else { $textA1.Visible = $true; $gridA1.Visible = $false }
           }

#Menu 1.
$buttonA1 = New_Button "I" 1
$buttonA1.Add_Click({
                      Switch ($global:bride)
                       {
                         1 { $global:bride1 = $textA1.Rtf }
                         2 { $global:bride2 = $textA1.Rtf }
                         3 { $global:bride3 = $textA1.Rtf }
                         4 { $global:bride4 = $textA1.Rtf }
                       }
                      Try { $textA1.Rtf = $global:bride1 }
                      Catch { $textA1.Text = $global:bride1 }
                      $global:bride = 1
                      &$script1
                   })
#Menu 2.
$buttonA2 = New_Button "II" 2
$buttonA2.Add_Click({
                      Switch ($global:bride)
                       {
                         1 { $global:bride1 = $textA1.Rtf }
                         2 { $global:bride2 = $textA1.Rtf }
                         3 { $global:bride3 = $textA1.Rtf }
                         4 { $global:bride4 = $textA1.Rtf }
                       }
                      Try { $textA1.Rtf = $global:bride2 }
                      Catch { $textA1.Text = $global:bride2 }
                      $global:bride = 2
                      &$script1
                   })
#Menu 3.
$buttonA3 = New_Button "III" 3
$buttonA3.Add_Click({
                      Switch ($global:bride)
                       {
                         1 { $global:bride1 = $textA1.Rtf }
                         2 { $global:bride2 = $textA1.Rtf }
                         3 { $global:bride3 = $textA1.Rtf }
                         4 { $global:bride4 = $textA1.Rtf }
                       }
                      Try { $textA1.Rtf = $global:bride3 }
                      Catch { $textA1.Text = $global:bride3 }
                      $global:bride = 3
                      &$script1
                   })
#Menu 4.
$buttonA4 = New_Button "IV" 4
$buttonA4.Add_Click({
                      Switch ($global:bride)
                       {
                         1 { $global:bride1 = $textA1.Rtf }
                         2 { $global:bride2 = $textA1.Rtf }
                         3 { $global:bride3 = $textA1.Rtf }
                         4 { $global:bride4 = $textA1.Rtf }
                       }
                      Try { $textA1.Rtf = $global:bride4 }
                      Catch { $textA1.Text = $global:bride4 }
                      $global:bride = 4
                      &$script1
                   })

#Lecture des fichiers dans la base.
$global:bride1 = "";$global:bride2 = "";$global:bride3 = "";$global:bride4 = ""
$file = $db.FileStorage.FindById("$/memo/bride1.rtf")
if ($file) { $sr = New-Object System.IO.StreamReader($file.OpenRead())
             $global:bride1 = Code_Txt $sr.ReadToEnd() $imdp -1 }
$file = $db.FileStorage.FindById("$/memo/bride2.rtf")
if ($file) { $sr = New-Object System.IO.StreamReader($file.OpenRead())
             $global:bride2 = Code_Txt $sr.ReadToEnd() $imdp -1 }
$file = $db.FileStorage.FindById("$/memo/bride3.rtf")
if ($file) { $sr = New-Object System.IO.StreamReader($file.OpenRead())
             $global:bride3 = Code_Txt $sr.ReadToEnd() $imdp -1 }
$file = $db.FileStorage.FindById("$/memo/bride4.rtf")
if ($file) { $sr = New-Object System.IO.StreamReader($file.OpenRead())
             $global:bride4 = Code_Txt $sr.ReadToEnd() $imdp -1 }  

#Chargement initial.
Try { $textA1.Rtf = $global:bride1 } Catch { $textA1.Text = $global:bride1 }
&$script1

#Liens.
$formA.controls.add($textA1)
$formA.controls.add($gridA1)
$formA.controls.add($buttonA1)
$formA.controls.add($buttonA2)
$formA.controls.add($buttonA3)
$formA.controls.add($buttonA4)

#Affiche le tout.
$formA.Add_Resize({
                    $csw = $formA.ClientSize.Width
                    $csh = $formA.ClientSize.Height
                    $textA1.Size = New-Object Drawing.Point ($csw+2),($csh-36)
                    $gridA1.Size = New-Object Drawing.Point ($csw+2),($csh-36)
                 })
$formA.Add_FormClosing({
           #Mémorise le contenu dans la fenêtre.
           Switch ($global:bride)
            {
              1 { $global:bride1 = $textA1.Rtf }
              2 { $global:bride2 = $textA1.Rtf }
              3 { $global:bride3 = $textA1.Rtf }
              4 { $global:bride4 = $textA1.Rtf }
            }
           #Sauvegarde des brides.
           if ((Alerte "Voulez-vous sauvegarder les infos ?" -Q) -eq "Yes")
             {
               #Bride 1:
               $tx = Code_Txt $global:bride1 $imdp 1
               [byte[]] $bytes = [System.Text.Encoding]::UTF8.GetBytes($tx)
               $ms = New-Object System.IO.MemoryStream($bytes,0,$bytes.Length)
               $db.FileStorage.Upload("$/memo/bride1.rtf", "bride1.rtf", $ms)
               #Bride 2:
               $tx = Code_Txt $global:bride2 $imdp 1
               [byte[]] $bytes = [System.Text.Encoding]::UTF8.GetBytes($tx)
               $ms = New-Object System.IO.MemoryStream($bytes,0,$bytes.Length)
               $db.FileStorage.Upload("$/memo/bride2.rtf", "bride2.rtf", $ms)
               #Bride 3:
               $tx = Code_Txt $global:bride3 $imdp 1
               [byte[]] $bytes = [System.Text.Encoding]::UTF8.GetBytes($tx)
               $ms = New-Object System.IO.MemoryStream($bytes,0,$bytes.Length)
               $db.FileStorage.Upload("$/memo/bride3.rtf", "bride3.rtf", $ms)
               #Bride 4:
               $tx = Code_Txt $global:bride4 $imdp 1
               [byte[]] $bytes = [System.Text.Encoding]::UTF8.GetBytes($tx)
               $ms = New-Object System.IO.MemoryStream($bytes,0,$bytes.Length)
               $db.FileStorage.Upload("$/memo/bride4.rtf", "bride4.rtf", $ms)
             }
                      })

[void]$formA.ShowDialog()

#Compresser la base de données.
[void]$db.Rebuild()

#Fermer la base de données.
$db.Dispose()

#Fin.

mardi 2 janvier 2018

PowerShell et LiteDB v3

Dans cet article, nous allons voir l'utilisation de la base de données LiteDB dans un environnement Windows PowerShell. Nous allons créer la base et ses collections. Nous verrons ensuite l'utilisation des instructions Insert, Update, Delete, Find, les requêtes [LiteDB.Query], et le stockage de fichiers.

Référence


Toujours à la recherche d'une base de données légère pour mes scriptes PowerShell, j'ai découvert LiteDB. Cette base de données "sans serveur" de type NoSQL se présente sous la forme d'une simple Dll de moins de 300Ko. Elle ne nécessite ni installation, ni droit administrateur. LiteDB est donc facilement portable pour peu que le FrameWork 3.5 (ou compatible) soit installé sur le système.

Site de référence : http://www.litedb.org

Exemple


Dans l'exemple ci-dessous, j'utilise la version 3.1.4 car la version 4.1.0 refusait de se charger sous certain environnement vieillissant (mais encore utilisé en entreprise).

 
################################################################
#
#               Base de données : LiteBD v3.1.4
#                     Powershell.sekki.fr
#
# 1 - Créer un dossier ldb à côté du script.
# 2 - Télécharger et copier LiteDB.dll+xml dans le dossier ldb.
#
################################################################

#Chemin courant.
$curpath = ""
if ($psISE) { $curpath = split-path -parent $psISE.CurrentFile.Fullpath }
else        { $curpath = Split-Path $MyInvocation.MyCommand.Path  }

#Charge les fonctions LiteDB.
Try   { [void][Reflection.Assembly]::LoadFile($curpath+"\ldb\LiteDB.dll") }
Catch { write-host "LiteDB.dll manquant.";Return 2 }

#Efface l'écran.
cls

###Fonctions personnalisées.
#Renvoi un nombre entre 0 et 100.
function GR() { Get-Random -minimum 0 -maximum 100 }

###Fonctions personnalisées.
#Converti le résultat de la query pour l'afficher.
function BsonToObj($bsn)
 {
   #Conversion.
   $obj = New-Object System.Object
   $obj | Add-Member -type NoteProperty -name id    -Value ([string]$bsn["_id"])
   $obj | Add-Member -type NoteProperty -name date  -Value ([string]$bsn["date"])
   $obj | Add-Member -type NoteProperty -name info1 -Value ([string]$bsn["Nom"])
   $obj | Add-Member -type NoteProperty -name info2 -Value ([string]$bsn["Prix"])
   $obj | Add-Member -type NoteProperty -name info3 -Value ([string]$bsn["Cat"])
   $obj
 }

#Ouvrir/Créer une Base de Données.
$db = New-Object LiteDB.LiteDatabase(($curpath+"\ldb\LiteDB.db"),$null)

#Ouvrir/Créer une Collection.
$coll = $db.GetCollection("Info")

#Créer un document Bson.
$bson = New-Object LiteDB.BsonDocument

#Créer 10 enregistrements.
for($i=0;$i -le 10;$i++)
 {
   $x = GR   #Nombre aléatoire entre 0 et 100.
   if ($x -le 50) { $nom = "Sekki";   $prix = $x; $cat = "Z" }
   else           { $nom = "Jumbor";  $prix = $x; $cat = "B" }
  
   #Ajouter un enregistrement dans la base de données.
   $bson["_id"]    = [LiteDB.ObjectId]::NewObjectId()
   $bson["date"]  = date
   $bson["Nom"] = $nom
   $bson["Prix"]  = $prix
   $bson["Cat"]   = $cat
   [void]$coll.Insert($bson)   #Insére le document dans la collection.
 }

#Modifier un enregistrement.
$query = $coll.Find([LiteDB.Query]::eq("Nom","Sekki"))
foreach ($q in $query)
 {
   $q["Cat"] = "A"
   [void]$coll.Update($q)
 }

#Supprimer un enregistrement : Méthode 1.
#$query = $coll.Find([LiteDB.Query]::eq("Nom","Sekki"))
#foreach ($q in $query)
# {
#   $coll.Delete($q["_id"])
# }

#Supprimer un enregistrement : Méthode 2.
#$nb = $coll.Delete([LiteDB.Query]::eq("Nom","Jumbor"))

###Les requétes.
#Renvoi les articles dont le nom exact est :
$query = $coll.Find([LiteDB.Query]::eq("Nom","Sekki"))
#Renvoi les articles dont le prix est plus grand que :
$query = $coll.Find([LiteDB.Query]::gt("Prix",30))
#Renvoi les articles dont le prix est plus petit que :
$query = $coll.Find([LiteDB.Query]::lt("Prix",30))
#Renvoi les articles dont le nom commence par :
$query = $coll.Find([LiteDB.Query]::startswith("Nom","S"))
#Renvoi tout les enregistrements :
$query = $coll.Find([LiteDB.Query]::all())

#Afficher une requête.
$query = $coll.Find([LiteDB.Query]::all())
$query | %{BsonToObj $_} | Out-GridView

###Charger des fichiers dans la base de données.
#Insérer/remplacer un fichier dans la base de données :
#$db.FileStorage.Upload("$/Fichiers/file1.jpg", $curpath+"\file1.jpg")
#Lire un fichier dans la base de données et l'enregistrer sur le disque local :
#[LiteDB.LiteFileInfo] $file = $db.FileStorage.FindById("$/Fichiers/file1.jpg")
#$file.SaveAs($curpath+"\file1.jpg")
#Lire un fichier dans la base de données et le charger dans une variable :
#[LiteDB.LiteFileInfo] $file = $db.FileStorage.FindById("$/Fichiers/file1.jpg")
#$image = [System.Drawing.Image]::FromStream($file.OpenRead())

#Pause
if (!$psISE) { Write-Host -NoNewLine "Press any key to continue..."
               $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") }

#Compresser la base de données.
[void]$db.Shrink()

#Fermer la base de données.
$db.Dispose()

#Fin.

jeudi 1 décembre 2016

PowerShell et PostgreSQL

Dans cet article, nous allons voir l’accès aux bases PostgreSQL avec Windows PowerShell et le Data Provider "Ngpsql.dll". L'exemple suivant montre comment se connecter sur une base PostgreSQL, et utilisation des instructions Create Table, Insert, Query, Update et Drop Table.

Référence


Avant d’exécuter le script, notez que j'ai préalablement installé un serveur "PostgreSQL Portable" sur lequel j'ai créé la base de données "Base1". J'ai également créé un dossier "npgsql" au même niveau que mon script. Ce dossier contient contient les fichiers du Data Provider NpgSQL. La table et les données peuvent être créé ensuite à l'aide des exemples de requêtes SQL présent dans le script.

Liens utiles :
- Serveur PostgreSQL portable
- Data Provider NpgSQL              

Exemple



 
#Chemin courant.
$curpath = ""
Try   { $curpath = Split-Path $MyInvocation.MyCommand.Path }
Catch { $curpath = split-path -parent $psISE.CurrentFile.Fullpath }

#Assembly.
[void][Reflection.Assembly]::LoadFile($curpath+"\npgsql\Npgsql.dll")
[void][Reflection.Assembly]::LoadFile($curpath+"\npgsql\Mono.Security.dll")

#Paramêtres PostgreSQL.
$port="5432"
$server="10.151.16.234"
$user="user1"
$password="P@ssw0rd"

#Chaine de connexion.
$database = "base1"
$connexion = "Server="+$server+";Port="+$port+";User Id="+$user+";Password="+$password+";Integrated Security=true"

function postgre_SQL([string] $ps_conn, [string] $ps_base, [string] $ps_query)
 {
   #Initialisation
   $conn = $ps_conn+";Database="+$ps_base

   #Déclaration des objets.
   $DBconn = New-Object NpgSql.NpgsqlConnection($conn)
   $cmd    = New-Object Npgsql.NpgsqlCommand
   $ds     = New-Object System.Data.DataSet
   $da     = New-Object Npgsql.NpgsqlDataAdapter

   #Exécuter.
   $cmd.CommandTimeout = 30
   $cmd.Connection     = $DBconn
   $cmd.CommandText    = $ps_query
   $da.SelectCommand   = $cmd
   [void]$da.Fill($ds)

   #Ferme la connexion.
   $DBconn.Close()

   #Retour.
   return $ds.Tables[0]
 }

#---------------------------------------------------------------------------#
#Voici la liste de différentes requêtes SQL que vous pouvez essayer.
#Supprimez le # devant la requête SQL que vous souhaitez tester.
#---------------------------------------------------------------------------#
#Créer une table.
#$psQuery = "CREATE TABLE Table1(Colonne1 nchar(16) PRIMARY KEY NOT NULL, Colonne2 nchar(16));"
#Ajouter une ligne.
#$psQuery = "INSERT INTO Table1(Colonne1,Colonne2) VALUES ('Jumbor','PowerShell');"
#Lire une table.
#$psQuery = "select * from Table1;"
#Lire une ligne.
#$psQuery = "select * from Table1 WHERE Colonne1 = 'Jumbor12';"
#Modifier une valeur.
#$psQuery = "UPDATE Table1 SET Colonne2 = 'PowerShell 2.0' WHERE Colonne2 = 'PowerShell';"
#Supprimer une ligne.
#$psQuery = "DELETE FROM Table1 WHERE Colonne1 = 'Jumbor12';"
#Supprimer une table.
#$psQuery = "DROP TABLE Table1;"
#---------------------------------------------------------------------------#

#Affiche la requête exécutée.
Write-Host $psQuery

#Exécute la requête.
$datas = postgre_SQL $connexion $database $psQuery
$datas | Out-GridView

#Exécution terminé.
Write-Host "Exécution terminé."

#Fin.

vendredi 29 juillet 2016

Zone de notification.

Cet exemple montre comment mettre en place une icône dans la zone de notification (System Tray). Pour cela, nous allons construire notre l’icône avec l'object System.Windows.Forms.NotifyIcon auquel nous allons associer un menu contextuel. Le menu contextuel permettra d'afficher la fenêtre lorsque celle-ci est masquer, de simuler un popup dans la zone de notification, et de quitter l'application.

Exemple


 
#Déclaration.
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

#Construction du menu contextuel.
$itemT1 = New-Object System.Windows.Forms.MenuItem -ArgumentList "Afficher Popup"
$itemT1.Add_Click({
                    #BalloonTipIcon = "None", "Info", "Warning" ou "Error".
                    #ShowBalloonTip(en millisecondes).
                    $TrayIcon.BalloonTipTitle = "Tray Icon"
                    $TrayIcon.BalloonTipText  = $textA1.Text
                    $TrayIcon.BalloonTipIcon  = "Info"  
                    $TrayIcon.ShowBalloonTip(300000)
                 })
$itemT2 = New-Object System.Windows.Forms.MenuItem -ArgumentList "Afficher Fenêtre"
$itemT2.Add_Click({  $formA.Visible = $true  })
$itemT3 = New-Object System.Windows.Forms.MenuItem -ArgumentList "Quitter"
$itemT3.Add_Click({  $formA.Close()  })
$menuT1 = New-Object System.Windows.Forms.ContextMenu
[void]$menuT1.MenuItems.Add($itemT1)
[void]$menuT1.MenuItems.Add($itemT2)
[void]$menuT1.MenuItems.Add($itemT3)

#Construction de l'icone pour la zone de notification.
#Attention : Mettre un chemin valide pour le fichier .ico.
$TrayIcon = New-Object System.Windows.Forms.NotifyIcon
$TrayIcon.Icon = "c:\xxx.ico"   #Image au format .ico.
$TrayIcon.Text = "TrayIcon"     #Texte de l'info bulle.
$TrayIcon.ContextMenu = $menuT1 #Menu contextuel.
$TrayIcon.Visible = $True

#Fenêtre.
$formA = New-Object System.Windows.Forms.form
$formA.Size = New-Object Drawing.Point 200,90
$formA.Text = "Tray Icon"   #Titre de la fenêtre.

#Masque la fenetre dans la barre de taches.
$formA.ShowInTaskbar = $false

#Bouton pour masquer la fenêtre.
$boutonA1 = New-Object Windows.Forms.Button
$boutonA1.Location = New-Object Drawing.Point 95,28
$boutonA1.Size = New-Object System.Drawing.Size(85,23)
$boutonA1.text = "Masquer"
$boutonA1.add_click({  $formA.Visible = $false  })

#Bouton pour afficher un popup dans la zone de notification.
$boutonA2 = New-Object Windows.Forms.Button
$boutonA2.Location = New-Object Drawing.Point 5,28
$boutonA2.Size = New-Object System.Drawing.Size(85,23)
$boutonA2.text = "Popup"
$boutonA2.add_click({   
                      $TrayIcon.BalloonTipTitle = "Tray Icon"
                      $TrayIcon.BalloonTipText  = $textA1.Text
                      $TrayIcon.BalloonTipIcon  = "Info"   #None,Info,Warning,Error
                      $TrayIcon.ShowBalloonTip(300000) 
                   })

#Champ texte pour changer le texte du popup.
$textA1 = New-Object Windows.Forms.TextBox
$textA1.Location = New-Object Drawing.Point 5,5
$textA1.Size = New-Object System.Drawing.Size(175,23)
$textA1.Text = "Changez moi !"

#Afficher la fenêtre.
$formA.controls.add($boutonA1)
$formA.controls.add($boutonA2)
$formA.controls.add($textA1)
[void][System.Windows.Forms.Application]::Run($formA)

#Fin.
$TrayIcon.Dispose()


Merci de remplacer le chemin "c:\xxx.ico" dans l'exemple par un chemin valide vers un fichier .ico (celui de votre choix). Sinon vous obtiendrez une erreur lors de l’exécution de l'exemple.

jeudi 23 juin 2016

PowerShell, CSharp et Global Hook

Dans cet exemple, j'utilise un script en cSharp pour récupérer les frappes clavier sur les touches F1 à F12. Puis je passe l'information au script Windows PowerShell. Cette technique me permet de déclencher une action sans avoir le focus sur l'application PowerShell (Global Hook)

Dans cet exemple, j'utilise un programme déjà publié un peu partout, mais adapté ici pour récupère que les touches de fonctions de F1 à F12. Ce programme se décomposer en deux parties. Un programme en CShape que j’intègre directement dans le script PowerShell et qui est lancé dans un RunSpace afin de ne pas bloquer mon script. Et un programme principale en PowerShell qui lance l'action lorsque la frappe est détecté. La communication entre les deux programmes n'est pas ce qu'il y a de plus simple à traiter. Après plusieurs essai, j'ai actuellement opté pour un socket réseau afin de pouvoir échanger des données entre le programme en CShape et le programme principale en Powershell. Ce n'est pas forcement l'idéal, mais c'est la solution qui m'a posé le moins de problèmes.

Exemple


 
##################################################################
# Script C# :
# Récupère les frappes aux claviers (quelques soit l'application),
# et en informe le programme en écrit en Powershell.
##################################################################
# Script PowerShell :
# Lance le script en C# dans un nouveau thread, et écoute les
# informations transmissent par le script en C#.
##################################################################

#Assembly.
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

##################################################################
# Script C#
# Récupère les frappes aux claviers (F1 à F12),
# et on informe le script PS1.
##################################################################

$csharp1 = {
  Add-Type -TypeDefinition @"
  using System;
  using System.IO;
  using System.Net;
  using System.Text;
  using System.Net.Sockets;
  using System.Diagnostics;
  using System.Windows.Forms;
  using System.Runtime.InteropServices;
  namespace KL {
  
    public static class Program {
      private const int WH_KEYBOARD_LL = 13;
      private const int WM_KEYDOWN = 0x0100;
   
      private static HookProc hookProc = HookCallback;
      private static IntPtr hookId = IntPtr.Zero;
   
      public static void Main() {
                                  hookId = SetHook(hookProc);
                                  Application.Run();
                                  UnhookWindowsHookEx(hookId);
                                }
      private static IntPtr SetHook(HookProc hookProc) {
        IntPtr moduleHandle = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
        return SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, moduleHandle, 0);
                                                       }
      private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
      private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
                                                                                    if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
                                                                                      {
                                                                                        int vkCode = Marshal.ReadInt32(lParam);
                                                                                        if ((vkCode > 111) && (vkCode < 128))
                                                                                          {
          string text = "";
          Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,ProtocolType.Udp);
          IPAddress serverAddr = IPAddress.Parse("127.0.0.1");
          IPEndPoint endPoint = new IPEndPoint(serverAddr, 12345);
          KeysConverter kc = new KeysConverter();
          text = kc.ConvertToString(vkCode);
          byte[] send_buffer = Encoding.ASCII.GetBytes(text);
          sock.SendTo(send_buffer , endPoint);
          sock.Close();
          if (vkCode == 127) { Application.ExitThread(); }
                                                                                          }
                                                                                      }
                                                                                    return CallNextHookEx(hookId, nCode, wParam, lParam);
                                                                                  }
      [DllImport("user32.dll")]
      private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
      [DllImport("user32.dll")]
      private static extern bool UnhookWindowsHookEx(IntPtr hhk);
      [DllImport("user32.dll")]
      private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
      [DllImport("kernel32.dll")]
      private static extern IntPtr GetModuleHandle(string lpModuleName);
    }
  }
"@ -ReferencedAssemblies System.Windows.Forms
#Lancement du programme en CShape.
[KL.Program]::Main()
}

##################################################################
# Ouverture d'un canal de communication réseau en UDP.
# Communication entre le script C# et PowerShell.
##################################################################

$global:udpport = 12345
$global:endpoint = new-object System.Net.IPEndPoint ([IPAddress]::Any,$global:udpport)
$global:udpclient = new-Object System.Net.Sockets.UdpClient $global:udpport

##################################################################
# Lance l'application C# dans un Runspace.
##################################################################

#Création du runspace.
$runspace1 = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
$runspace1.Open()
$pipeline1 = $runspace1.CreatePipeline()
$pipeline1.Commands.AddScript($csharp1)
$pipeline1.InvokeAsync()
$running = [System.Management.Automation.Runspaces.PipelineState]::Running
if ($pipeline1.PipelineStateInfo.State -ne $running) { Exit }

##################################################################
# Timer pour écouter les retours de l'application C#
##################################################################
               
$timer1 = new-object System.Windows.forms.timer
$timer1.Interval = 250
$timer1.Add_Tick({
                   while ($global:udpclient.Available)
                    {
                      $content = $global:udpclient.Receive([ref]$global:endpoint)
                      $fxx = [Text.Encoding]::ASCII.GetString($content)
                      switch ($fxx){
                                     F1      { write-host "F1" ; break }
                                     #...
                                     F12     { write-host "F12"; break }
                                     default { write-host $fxx ; break }
                                   }
                    }
                })

##################################################################
# Script Powershell.
##################################################################

$global:formA = New-Object Windows.Forms.Form
$global:formA.text = "GlobalHook Keyboard."            
$global:formA.Size = New-Object System.Drawing.Size(250,110)
$global:formA.Add_KeyDown({
                            if ($_.KeyCode -eq "Escape")
                              { $global:formA.Close() }
                          })
$labelA1 = New-Object Windows.Forms.Label
$labelA1.Location = New-Object Drawing.Point 20,30
$labelA1.Size = New-Object Drawing.Point 210,16
$labelA1.Text = "Appuyer sur un touche de F1 à F12."
$formA.controls.add($labelA1)
$timer1.Start()
$global:formA.ShowDialog()

##################################################################
# Fermeture des ressources.
##################################################################

$timer1.Stop()                                       #Stop Timer.
[System.Windows.Forms.SendKeys]::SendWait("{F16}")   #Stop C# App.
$runspace1.Close()                                   #Stop Runspace.
$udpclient.Close()                                   #Stop Socket.


Pour éviter qu'une partie du programme continue de tourner tout seul dans son coin, il est important de bien fermer tout ce petit monde dans l'ordre. Tout d'abord il faut demander au programme CSharp de se fermer. Pour cela je simule dans mon programme PowerShell une frappe sur la touche F16. Le programme CShape est configuré pour s'arrêter lorsqu’il capture une frappe sur la touche F16. Ceci fait, je ferme mon RunSpace, puis je libérer mes ressources réseaux.

mardi 14 juin 2016

Utiliser Xml dans PowerShell

Nous allons voir dans cet article les bases permettant de gérer un fichier Xml avec Windows PowerShell. Dans les exemples suivants, nous allons lire le fichier Xml. Le premier exemple utilise la méthode de l'index pour lire les données. Le second exemple utilise la commande foreach.

Fichier XML


En premier lieu, nous allons créer un fichier texte "livres.xml". Ce fichier doit contenir les lignes de données suivantes :

<?xml version="1.0" standalone="yes"?>

<Livres>

  <Manga Edition="Kana">
    <Titre>Jumbor</Titre>
    <Numero>1</Numero>
  </Manga>

  <Manga Edition="Ki-oon">
    <Titre>Ubel Blatt</Titre>
    <Numero>1</Numero>
  </Manga>

</Livres>

Une fois le fichier enregistré (dans c:\temp par exemple), nous allons utiliser PowerShell pour le lire.

Exemple 1


#Lecture du fichier.
[xml]$xml = get-content (“c:\temp\livres.xml”)

#Je récupéré dans $Livres les données entre la balise <Livres> et </Livres>.
$Livres = $xml.Livres

#Puis j'affiche mes données.
Write-Host $Livres.Manga[0].Edition
Write-Host $Livres.Manga[0].Titre
Write-Host $Livres.Manga[0].Numero

Ici je suis obligé d'indexer $Livre.Manga[] car dans mon fichier Xml, j'ai plusieurs fois la balise <Manga>. Si la balise n'était présente qu'une seul fois, j'aurais dû écrire Write-Host $Livres.Manga.Edition (sans l'indexation). Ceci viens de l’interprétation de la commande Get-Content. Lorsqu'il rencontre plusieurs fois une même balise, il la traduit sous la forme d'un tableau. Pour éviter les problèmes d'écriture suivant le nombre d’élément répétable présent dans un document Xml, il vaut mieux passer par un Foreach :

Exemple 2


#Lecture du fichier.
[xml]$xml = get-content (“c:\temp\livres.xml”)

#Je récupéré dans $Livres les données entre la balise <Livres> et </Livres>.
$Livres = $xml.Livres

#Puis j'affiche mes données.
foreach ($m in $Livres.Manga)
 {
   Write-Host $m.Edition
   Write-Host $m.Titre
   Write-Host $m.Numero
 }

Ici, la commande foreach fonctionne quelques soit le nombre de balise <Manga> présent dans le document Xml.

vendredi 9 janvier 2015

PowerShell et FireBird

Dans cet article, nous allons voir comment utiliser les bases FireBird avec Windows PowerShell et le Data Provider "Ado.net". L'exemple suivant montre comment se connecter sur une base FireBird, et utilisation des instructions Create Table, Insert Into, Select, Update, Delete From et Drop Table.

Installer FireBird


Je cherchais une base de donnée légère et portable pour pouvoir héberger mes données et je suis tombé sur FireBird. Cette base de donnée fait moins de 20Mo, et il est possible de la rendre portable à l'aide d'une petite procédure.

Tout d'abord nous devons installer FireBird. Les sources sont ici "Download_FireBird". Téléchargez la dernière version pour Win64 "SuperServer", puis installez la base de donnée.

Il nous faudra également un connecteur pour pouvoir utiliser FireBird avec PowerShell. Les sources du connecteur sont ici "ADO.NET".

La procédure pour convertir FireBird en version portable est ici "Procédure". Bon d'accord, c'est en anglais. Je vais vous traduire ca rapidement :
- Installez FireBird (si ce n'est pas déjà fait).
- Arrêtez les services de FireBird.
- Copiez le dossier “C:\Program Files\Firebird\Firebird_2_5\” sur votre clé USB par exemple.
- Ouvrez le dossier ou vous avez copié FireBird.
- Supprimez le fichier firebird.conf (Si vous avez apporté des modifications).
- Créez un nouveau batch nommé "Start.bat".
- Editez le fichier "Start.bat", et copiez les deux lignes suivante dedans :
  cd bin
  fbserver.exe -a -p 3050 -database "localhost:..\security2.fdb" –n

- Créez également le batch "Stop.bat".
- Editez le fichier "Stop.bat", et copiez la ligne suivante dedans :
  taskkill /F /IM fbserver.exe /T
Et voila c'est fini ;)

FlameRobin


Bien que cela ne sois pas indispensable pour cet article, vous pouvez également installer FlameRobin pour l'administration graphique de la base. Je vous laisse consulter le site ici "FlameRobin".

Créer une base


Maintenant que FireBird tourne, il est temps de créer une base de donnée. Allez dans le dossier "FireBird\bin", et lancez "iSQL.exe". Tapez la commande suivante :
SQL>CREATE DATABASE 'mypath\JumborDB.fdb' page_size 8192
CON>user 'SYSDBA' password 'masterkey';
N'oubliez pas de remplacer "mypath" par un chemin valide.

Exemple


 
#############################################################
# Prérequis :
#     Installer le connecteur ADO.NET.
#     Créer la base de test "JumborDB" avec iSql.
#############################################################

#Assembly.
[Reflection.Assembly]::LoadFile("C:\Program Files (x86)\FirebirdClient\FirebirdSql.Data.FirebirdClient.dll")

#Chaine de connection.
$Connetion = "User=SYSDBA;Password=masterkey;DataSource=localhost;Port=3050;Database=JumborDB;initial catalog=mypath\\JumborDB.fdb;Dialect=3;Charset=NONE;Role=;Connection lifetime=15;Pooling=true;MinPoolSize=0;MaxPoolSize=50;Packet Size=8192;ServerType=0;"
#N'oubliez pas de remplacer "mypath" par un chemin valide.

Function FB_Query([string] $fbConn, [string] $fbQuery)
 {
   #Etablit la connexion avec la base SQL.
   $firebird = new-object FirebirdSql.Data.FirebirdClient.FbConnection($fbConn)
   $firebird.Open()

   #Exécute et récupère le résultat.
   $DataSet = New-Object System.Data.DataSet
   $Adapter = New-Object FirebirdSql.Data.FirebirdClient.FBDataAdapter($fbQuery, $firebird)
   $Adapter.Fill($DataSet)

   #Ferme la connexion avec la base SQL.
   $firebird.Close()

   #Renvoie le résultat.
   return $DataSet.Tables[0]

   #Close.
   $Adapter.Close
   $DataSet.Close
 }

######################################################################
# Voici la liste de différentes requêtes SQL que vous pouvez essayer.
# Supprimez le # devant la requête SQL que vous souhaitez tester.
######################################################################
#Créer une table.
#$myQuery = "CREATE TABLE Table1 (Colonne1 varchar(20) not null, Colonne2 varchar(20) not null)"
#Ajouter une ligne.
#$myQuery = "INSERT INTO Table1 (Colonne1,Colonne2) VALUES ('Jumbor12','PowerShell')"
#Lire une table.
#$myQuery = "SELECT * FROM Table1"
#Lire une ligne.
#$myQuery = "SELECT * FROM Table1 WHERE Colonne1 = 'Jumbor12'"
#Modifier une valeur.
#$myQuery = "UPDATE Table1 SET Colonne2 = 'PowerShell 2.0' WHERE Colonne2 = 'PowerShell'"
#Supprimer une ligne.
#$myQuery = "DELETE FROM Table1 WHERE Colonne1 = 'Jumbor12'"
#Supprimer une table.
#$myQuery = "DROP TABLE Table1"
######################################################################

#Affiche la requête exécutée.
Write-Host $myQuery

#Exécute la requête.
$rtn = FB_Query $Connetion $myQuery
foreach ($raw in $rtn)
 {
   #Ici j'affiche le retour pour les requêtes de lecture de données.
   Try { Write-Host "Line :" $raw[0] $raw[1] } catch {}
 }
Write-Host "Exécution terminé."

#Fin.

mardi 30 décembre 2014

Barre de progression

Je vais vous présenter ici un exemple de barre de progression basé sur l'objet "Forms.ProgressBar". La barre de progression prend une valeur allant de 0 (vide) à 100 (plein). Bien entendu, par défaut cette valeur est à 0. La barre de progression augmente d'une valeur définit dans la propriété ".Step" en appelant la méthode ".PerformStep()". Par exemple, si la propriété ".Step" est définit à 25, il faudra appeler 4 fois la méthode ".PerformStep()" pour remplir la barre de progression.

Exemple


 
#Assembly.
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

#Ouvre une nouvelle fenêtre.
$formA = New-Object Windows.Forms.Form
$formA.FormBorderStyle = "FixedDialog"
$formA.text = "ProgressBar"
$formA.Size = New-Object Drawing.Point 300,100

#Création d'un label (Label).
$labelA1 = New-Object Windows.Forms.Label
$labelA1.Location = New-Object Drawing.Point 20,12
$labelA1.Size = New-Object System.Drawing.Size(250,16)
$labelA1.text = "Traitement en cours ..."

#Barre de progression (Progress bar).
$progressA1 = new-object System.Windows.Forms.ProgressBar
$progressA1.Location = new-object System.Drawing.Point 20,32
$progressA1.Size = new-object System.Drawing.Size(250,23)
$progressA1.Step = 1

#Timer.
#Le timer va me permettre de lancer automatiquement le traitement
#1 seconde après l'ouverture de la fenêtre.
$timer1 = new-object System.Windows.forms.timer
$timer1.Interval = 1000   #Initialement je lance le timer au bout d'une seconde.
$timer1.Enabled  = $true
$timer1.add_Tick({
                   #Initialisation.
                   #Pour éviter de lancer 2 fois le traitement je change
                   # l'intervalle d'exécution du timer.
                   $timer1.Interval = 999999

                   #Boucle de traitement.
                   for ($i=0;$i -le 100;$i++)
                    {
                      #Force l'exécution des events de la fenêtre.
                      [System.Windows.Forms.Application]::DoEvents()

                      #Mise à jour de la bar de progression.
                      $progressA1.PerformStep()

                      #Traitement de votre choix.
                      Sleep -Milliseconds 50
                    }

                   #Arrête le timer et ferme la fenêtre.
                   $timer1.Stop()
                   $formA.Close()
                })

#Attache les contrôles à la fenêtre.
$formA.controls.add($labelA1)
$formA.controls.add($progressA1)

#Lance le timer.
$timer1.Start()

#Affiche fenêtre.
$formA.ShowDialog()

#Fin.

jeudi 18 décembre 2014

Console avec minuteur (timer)

Nous allons voir dans cet article deux méthodes pour créer un timer et afficher le résultat d'une commande à intervalle régulier. Dans les exemples suivants, je vais afficher le résultat de la commande "Get-Process" toutes les 5 secondes. Mon premier script montre comment créer un timer dans une console PowerShell.

Exemple 1


 
#Création d'un timer.
$timer = new-object timers.timer
$timer.Interval = 1000 #Démarrage.
$nom = "T1"            #Identifiant de mon timer.
$boucle = 5            #Arrête le timer après 5 exécutions.

#Action.
$action = {
            #Initialisation.
            $timer.Interval = 5000  #En milliseconde.
            #Action.
            Write-Host (Get-Process | Out-String)
            #Supprimer le timer.
            if (--$boucle -eq 0)
              {
                $timer.stop()
                Unregister-Event $nom
                Write-host "Timer" $Nom "supprimé."
              }
          }

#Lancement du timer.
Register-ObjectEvent -InputObject $timer -EventName elapsed –SourceIdentifier $nom -Action $action
$timer.start()

#Fin.


Exemple 2


Le script suivant est une solution alternatif. Il  utilise le timer des fenêtres (Forms) pour afficher toutes les 5 secondes le résultat de la commande "Get-Process".

 
######################
# Console avec timer
######################

#Assembly.
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

#Plugin.
Get-Command -PSSnapin VirtualMachineManagerSnapin

#Paramêtres.
$global:consw = 600      #Largeur de la console au démarrage.
$global:consh = 300      #Hauteur de la console au démarrage.
$global:intvl = 5        #Intervale d'execution du timer (en seconde).
$global:titre = "Console avec timer"  #Titre de la fenêtre.

###########
# Timers
###########

#Timer.
$timer1 = new-object System.Windows.forms.timer
$timer1.Interval = 1000      #Démarrage rapide.

#Action.
$timer1.Add_Tick({
                   #Initialisation.
                   $global:labelA2.text = "Scan en cours ..."
                   #Intervalle après démarrage.
                   $timer1.Interval = ($global:intvl * 1000)
                   #Commande.
                   $global:labelA1.text = (Get-Process | Out-String)
                   #Fin.
                   $global:labelA2.text = "Dernier scan le " + (date -Format "dd-MM-yyyy à HH:mm:ss")
                })

#########################
# Fenêtre style console
#########################

#Ouvre une nouvelle fenêtre.
$formA = New-Object Windows.Forms.Form
$formA.text = $global:titre          
$formA.Size = New-Object System.Drawing.Size($global:consw,$global:consh)
$formA.BackColor = [System.Drawing.Color]::FromArgb(255,1,36,86)

#Création d'un label (Label).
$global:labelA1 = New-Object Windows.Forms.Label
$global:labelA1.Location = New-Object Drawing.Point 9,0
$global:labelA1.Size = New-Object System.Drawing.Size($formA.Size.Width,$formA.Size.Height)
$global:labelA1.ForeColor = [System.Drawing.Color]::FromArgb(255,255,255,255)
$global:labelA1.Font = New-Object System.Drawing.Font("Consolas","9",[System.Drawing.FontStyle]::Regular,3,0)
$global:labelA1.text = ""

#Création d'un label (Label).
$global:labelA2 = New-Object Windows.Forms.Label
$global:labelA2.Location = New-Object Drawing.Point 0,(($formA.Size.Height) - 56)
$global:labelA2.Size = New-Object System.Drawing.Size(($formA.Size.Width),20)
$global:labelA2.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
$global:labelA2.BackColor = [System.Drawing.Color]::FromArgb(255,222,222,222)
$global:labelA2.Font = New-Object System.Drawing.Font("Consolas","9",[System.Drawing.FontStyle]::Regular,3,0)
$global:labelA2.TextAlign = "MiddleLeft"
$global:labelA2.text = "Initialisation ..."

#Events.
$formA.Add_Resize({
                    $global:labelA1.Size = New-Object System.Drawing.Size($formA.Size.Width,$formA.Size.Height)
                    $global:labelA2.Location = New-Object Drawing.Point 0,(($formA.Size.Height) - 56)
                    $global:labelA2.Size = New-Object System.Drawing.Size(($formA.Size.Width),20)
                 })

#Attache les contrôles à la fenêtre.
$formA.controls.add($global:labelA2)
$formA.controls.add($global:labelA1)

#Lance le timer.
$timer1.Start()

#Affiche le tout.
$formA.ShowDialog()

#Libère le timer.
$timer1.Stop()

#Fin.

jeudi 30 octobre 2014

Liste des groupes d'un utilisateur (AD).

(NDA : Malheureusement cet article est un peu obsolète aujourd'hui. Je vais préparer un nouvel article avec le résumé des commandes AD actuelles.)

Il existe différentes façons d'interroger l'Active Directory en PowerShell. Dans cet article, j'utilise l'extension de commande de Quest pour manipuler l'AD.

Edit : A l’époque ou j'ai rédigé cet article, Quest était une très bonne alternative aux commandes natives disponibles sous PowerShell. Evidement, aujourd'hui en 2018 les systèmes Windows Serveurs AD intègre cela très bien sans avoir à passer par cet extension.

Installation


1) Allez sur le site de Quest "www.quest.com/powershell/activeroles-server.aspx"
2) Cliquez sur la case à cocher "I have read and accept the agreement".
3) Téléchargez la dernière version de "ActiveRoles Management Shell for AD".
4) Et installez l'extension.
(La documentation pdf est plutôt bien fait. N'hésitez pas à la consulter.)

Liste des groupes d'un utilisateur (méthode alternatif)


Normalement Quest propose la fonction "Get-QADMemberOf" pour réaliser cette action. Malheureusement lorsque cette commande rencontre un groupe d'un domaine qui ne vous est pas accessible, elle s'arrête avec une erreur et ne retourne rien. Si vous rencontrez ce problème, je vous propose de passer par le script suivant :

#Chargement des commandes Quest.
Add-psSnapin quest.activeroles.admanagement

#Compte de connexion à l'AD.
$username = "DOMAINE\login.admin"

#Invite de saisi du mot de passe.
$password = read-host ("Password pour "+$username) -assecurestring

#Credential.
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password

#Connexion à l'AD
$AD1 = Connect-QADService -service "domain.sekki.fr" -Credential $cred

#Affiche les groupes de l'utilisateur au format "Domain\Groupe".
$user = "DOMAINE\login.user"
Get-QADUser $user | foreach {
            foreach ($mof in $_.MemberOf)
             {
               #Initialise.
               $grp = "";$dom = "";
               #Converti les données séparées par une virgule en tableau.
               $tmof = $mof.split(",")
               #Recupère le nom du groupe.
               foreach($e in $tmof) { if ($e -like "CN=*") {$grp = $e;break} }
               #Récupère le nom du domaine du groupe.
               foreach($e in $tmof) { if ($e -like "DC=*") {$dom = $e;break} }
               #Supprime les infos initules.
               $grp = $grp.Replace("CN=","")
               $dom = $dom.Replace("DC=","")
               #Recompose le nom du groupe au format "Domain\Groupe".
               $groupe = $dom+"\"+$grp
               #Affiche le nom du groupe.
               write-host $groupe
             }
                           }

#Déconnexion de l'AD.
Disconnect-QADService -connection $AD1

#Fin.