mardi 20 mars 2018

Expressions régulières (RegEx)

Windows PowerShell utilise les expressions régulières (RegEx) pour déterminer si une chaine de caractères est conforme au format définit par l'expression régulière. Pour comparer une chaine de caractères avec une RegEx, Windows PowerShell utilise -match -notmatch, -cmatch, -notcmatch.

Principe du RegEx.


Une expression régulière est une chaîne de caractères représentant un ensemble de chaîne de caractères possibles. Dit comme ça ce ne parle pas forcement à tout le monde. Je vais essayer d'expliquer ça plus simplement. Imaginez que vous voulez rechercher le fichier "toto.txt"sur votre disque. Dans le champ de recherche vous utilisez le caractères génériques * pour dire au système de trouver tout les fichiers commençant par "toto" (ex : toto*). Le système va alors afficher des fichiers commençant par "toto" comme "toto1.txt", "toto.xml", etc. Le RegEx c'est pareil, mais avec des caractères génériques infiniment plus élaborés.

Note : Oui je sais, le caractère générique * n'est pas interprété pareil dans la recherche de fichier que dans le RegEx, mais c'est pour l'explication du principe de base ... "Il écoute vraiment rien Hervé" ^^.

Les bases de l'expression régulière.


^ = En début de chaine signifie "commence par".
^ = En milieu de chaine ce symbole représente la négation.
$ = En fin de chaine signifie "se termine par".
\w = [a-zA-Z0-9_]
\d = Contient au moins 1 chiffre.
\s = Contient au moins 1 espace.
\D = Ne contient aucun chiffre.
\S = Ne contient aucun espace.
[] = Liste ou range de caractère ex: [a-z] tout les caractères de "a" à "z".
{} = Longueur de la chaine à valider ex: {1,5} = 1 à 5 caractères.
{5} = Longueur exacte de la chaine (ici 5 caractère).
{5,} = Longueur au moins de 5 caractères (de 5 à n).
{5,9} = Longueur de 5 à 9 caractères.
* = N'importe quel longueur de 0 à n caractères.
+ = N'importe quel longueur de 1 à n caractères.
? = Indique que la condition précédente est optionnel.
| = Indique le choix entre deux conditions (Ou).

Exemples :
[a-z]* = Chaine alphabétique de longueur de 0 à n caractères.
[a-z]+ = Chaine alphabétique de longueur de 1 à n caractères.
[a-z]{1,12} = Chaine alphabétique de longueur de 1 à 12 caractères.
\d{1,6} = Nombre de 0 à 999999.
[:;,.] = Liste de caractères.
([a-z]+\d{1,4})+ = Le (...)+ indique une structure répétable de 1 à n fois.

Les comparaisons :
-match = Correspond à ... (insensible à la casse).
-notmatch = Ne correspond pas à ... (insensible à la casse).
-cmatch = Correspond à ... (sensible à la casse).
-cnotmatch = Ne correspond pas à ... (sensible à la casse).

Caractère d'échappement :
En dehors des cas vu précédemment, la barre oblique inverse "\" est utilisée dans une expression régulière pour indiquer que le caractère suivant doit être interprété littéralement. Par exemple :
\\ = \
\. = .
\+ = +
\* = *

Exemples classiques.


Texte avec caractères alphabétiques :
$texte = "Jumbor"
$texte -match  "^[a-z]*$"
$texte -cmatch "^[a-zA-Z]*$"

Texte en minuscule uniquement :
$texte = "jumbor"
$texte -cmatch "^[a-z]*$"


Texte en majuscule uniquement :
$texte = "JUMBOR"
$texte -cmatch "^[A-Z]*$"


Texte de 1 à 12 caractères alphabétiques :
$texte = "Jumbor"
$texte -match "^[a-z]{1,12}$"


Texte commençant par une majuscule, et de 1 à 12 caractères alphabétiques :
$texte = "Jumbor"
$texte -cmatch "^[A-Z]{1}[a-zA-Z]{0,11}$"


Texte de 1 à 12 caractères sans espace et sans chiffre :
$texte = "Jumbor"
$texte -match "^\S\D{1,12}$"


Chiffre de 0 à 99999 :
$texte = "12"
$texte -match "^\d{1,5}$"


Ne contient pas ces caractères :
$texte = "Jumbor"
$texte -notmatch "[:;,.]"


Contient que des chiffres et lettres :
$texte = "Jumbor12"
$texte -match "^[0-9a-z]*$"
$texte -cmatch "^[0-9a-zA-Z]*$"

Exemples avancés.


Vérifier le format "Prénom NOM" avec des prénoms simples :
$texte = "Barus CROW"
$texte -cmatch "^[A-Z]{1}[a-zA-Z]* [A-Z]*$"


Idem en ajoutant des prénoms composés en "Prénom-Prénom" :
$texte = "Jean-Pierre CROW"
$texte -cmatch "^(([A-Z]{1}[a-z]*)|([A-Z]{1}[a-z]*-[A-Z]{1}[a-z]*)) [A-Z]*$"


Idem en ajoutant des caractères accentué :
$texte = "Loïc CROW"
$texte -cmatch "^(([A-Z]{1}[éïëèa-z]*)|([A-Z]{1}[éïëèa-z]*-[A-Z]{1}[éïëèa-z]*)) [A-Z]*$"


Vérifier une adresse ip4 :
$ip = "192.168.1.1"
$ip -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"


Vérifier une adresse ip4 plus précisément :
$ip = "192.168.1.1"
$ip -match "((\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b)"


Vérifier une adresse email en particulier :
$email = "Jumbor@sekki.fr"
$email -match "^(([a-z]+)|([a-z]+\.[a-z]+))@sekki.fr$"


Vérifier une adresse mail générique :
$email = "Jumbor@sekki.fr"
$email -match "^(([a-z]+)|([a-z]+\.[a-z]+))@[a-z]+\.[a-z]+$"


Vérifier une adresse DNS simple :
$dns = "powershell.sekki.fr"
$site -match $dns -match "^([a-z]+\.)*([a-z]+)$"


Vérifier un site web :
$site = "http://powershell.sekki.fr"
$site -match "^(http://|https://|ftp://)([a-z]+\.)*([a-z]+)$"

Extraire une chaine en RegEx.


Exemple 1 :
$text = "Mon numéro de téléphone n'est pas le 06-11-22-33-44 !!!"
$phone = [regex]::match($text,'((\d{2}(-|\.){0,1}){5})').Value

Remplacer une chaine en RegEx.


Remplace une Ip dans le texte :
$text = "Serveur;Site;10.112.120.6;Type"
$rtn = [regex]::replace($text,'((\d{1,3}(\.){0,1}){4})','xxx.xxx.xxx.xxx')


#Remplace le début d'une Ip dans le texte :
$text = "Serveur;Site;10.112.120.6;Type"
$ip1 = [regex]::match($text,'((\d{1,3}(\.){0,1}){4})').Value
$ip2 = [regex]::replace($ip1,'^(\d{1,3}(\.))','xxx.')
$rtn = [regex]::replace($text,'((\d{1,3}(\.){0,1}){4})',$($ip2))


lundi 5 mars 2018

Bouton Parcourir (OpenFileDialog)


Dans cet article, nous allons utiliser Windows PowerShell pour ouvrir une fenêtre (Form) et y ajouter le bouton de sélection de fichier "Parcourir" (OpenFileDialog). L'objet OpenFileDialog gère à la fois la création de la fenêtre de sélection, l'exploration des fichiers, et la sélection du fichier. Il ne nous reste plus qu'à configurer quelques options, lancer l'ouverture de la fenêtre, et récupérer le nom du fichier choisi.


 
#Menu contextuel sur un contrôle.
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

#Ouvre une fenêtre.
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$form = New-Object Windows.Forms.Form
$form.text = "Ouvrir un fichier"            
$form.Size = New-Object System.Drawing.Size(250,150)

#Création d'un label (Label).
$label1 = New-Object Windows.Forms.Label
$label1.Location = New-Object Drawing.Point 20,30
$label1.Size = New-Object Drawing.Point 150,16
$label1.text = "Sélectionner un fichier."

#Création d'une zone texte (TextBox).
$texte1 = New-Object Windows.Forms.TextBox
$texte1.Location = New-Object Drawing.Point 20,50
$texte1.Size = New-Object Drawing.Point 140,30
$texte1.Text = ""

#Création d'un bouton parcourir (Button + OpenFileDialog).
$bouton1 = New-Object Windows.Forms.Button
$bouton1.Location = New-Object Drawing.Point 160,49
$bouton1.Size = New-Object Drawing.Point 65,21
$bouton1.text = "Parcourir"
$bouton1.add_click({
                     #Création d'un objet "ouverture de fichier".
                     $ouvrir1 = New-Object System.Windows.Forms.OpenFileDialog

                     #Initialisation du chemin par défaut.
                     $ouvrir1.initialDirectory = "C:\"

                     #Ici on va afficher que les fichiers en ".txt".
                     $ouvrir1.filter = "TXT Files (*.txt)| *.txt"

                     #Affiche la fenêtre d'ouverture de fichier.
                     $retour1 = $ouvrir1.ShowDialog()

                     #Traitement du retour.
                     #Si "OK" on affiche le fichier sélectionné dans la TextBox.
                     #Sinon on afficher un fichier par défaut.
                     if ($retour1 -eq "OK") { $texte1.Text = $ouvrir1.filename }
                     else { $texte1.Text = "C:\jumbor12.txt" }
                  })

#Attache le contrôle à la fenêtre.
$form.controls.add($label1)
$form.controls.add($texte1)
$form.controls.add($bouton1)#Affiche le tout.
$form.ShowDialog()

#Fin.



Dans notre exemple, nous n'avons pas intégré la vérification de l'existence du fichier en cas de saisi manuel dans la TextBox. Pour faire cette vérification, nous pouvons utiliser la commande "Test-Path" :


$fichier1 = "C:\jumpor12.jpg"
$test1 = Test-Path $fichier1
if ($test1 -ne "True") { Write-Host "Fichier absent."  }
else                   { Write-Host "Fichier présent." }


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.