Les fichiers de verrouillage Access (ldb et laccdb)

Thierry GASPERMENT

Pour gérer les connexions aux fichiers de base de données, Access utilise des fichiers de verrouillage (extension ldb ou laccdb).
Nous découvrirons dans cet article comment sont créés et remplis ces fichiers.
Nous verrons également comment lire les verrous posés sur ces fichiers pour connaître les connexions actives.

Commentez

- Titre : Les fichiers de verrouillage Access (ldb et laccdb)

- Auteur : Thierry GASPERMENT

- Parution : 15 février 2012

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2012 Thierry GASPERMENT. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.

Sommaire

- I. Introduction

- II. Le fichier de verrouillage

- II-A. Extension du fichier

- II-B. Structure

- II-C. Création, Modification et Suppression

- II-D. Les Verrous

- II-D-1. Le verrouillage de fichiers sous Windows

- II-D-2. Verrouillage des fichiers ldb et accdb

- III. Lire et filtrer le fichier de verrouillage

- III-A. Lire le contenu du fichier

- III-B. Filtrer le contenu du fichier grâce aux verrous

- IV. Cas particulier du mode exclusif

- V. Cas de la fermeture brutale d'Access

- VI. Verrouiller les connexions

- VI-A. En posant des verrous sur le fichier ldb ou accdb

- VI-B. Avec ADO et Connection Control

- VII. Application à télécharger

- VIII. Autres possibilités pour lister les utilisateurs

- IX. Remerciements

I - Introduction

Vous avez sans doute remarqué, à l'ouverture d'une base de données, qu'un fichier avec l'extension ldb ou laccdb est généralement créé.

Ce fichier contient une liste d'utilisateurs (et le nom de leur ordinateur) qui se sont connectés à la base de données.

Mais cette liste n'est pas utilisable en l'état sans connaître le mécanisme de verrous utilisé.

Ce mécanisme est expliqué dans un document (en anglais) que vous pouvez trouver ici :
ACC : Utilitaires de Microsoft Jet disponibles dans le centre de téléchargement.

Le document en question est Jetlock.doc (Understanding Microsoft Jet Locking White Paper).
Il contient beaucoup d'informations techniques pas forcément à jour pour les dernières versions d'Access.
Il est donc réservé aux plus curieux et motivés !

Cet article explique ce mécanisme de connexion aux bases de données et comment lire les informations des connexions actives.
En fin d'article, vous pourrez télécharger le code qui permet de dresser la liste des utilisateurs d'une base de données.

II - Le fichier de verrouillage

Le fichier de verrouillage est géré par le moteur de base de données :
- JET jusqu'à Access 2003 inclus ;
- ACE à partir d'Access 2007.

Ce n'est donc pas l'application Access qui crée ce fichier.
Un accès à la base de données par une application externe utilisera donc ce même mécanisme de verrous.

II-A - Extension du fichier

Le fichier de verrouillage a une extension ldb pour les formats de fichier jusqu'à Access 2003 (mdb = JET).
À partir du format de fichier Access 2007 (accdb = ACE), le fichier de verrouillage a une extension laccdb.

II-B - Structure

Il contient une liste d'ordinateurs et d'utilisateurs.
Chacune de ces deux informations est écrite sur une longueur fixe de 32 caractères.
Un caractère Null (chr(0) ou vbNullChar) marque la fin de chaque information.

Si on ouvre le fichier de verrouillage avec Notepad++ par exemple, on voit très bien ce caractère de fin NUL :

Le nom d'utilisateur est celui de la sécurité au niveau utilisateur.
Si aucune sécurité utilisateur n'est mise en place, Admin sera l'utilisateur pour toutes les connexions.
Seul le nom de PC pourra alors différencier les utilisateurs.

Le nombre maximal de connexions est 255.
Le fichier de verrouillage sera donc d'une taille maximale de 255 * 64 = 16 320 octets.

II-C - Création, Modification et Suppression

Le fichier de verrouillage est créé à la première connexion (partagée) à la base de données.
Ce fichier est placé dans le même répertoire que le fichier de la base de données et porte le même nom, à part l'extension qui diffère.

Lors d'une ouverture de la base de données en mode exclusif, aucun fichier de verrouillage n'est créé.
C'est le fichier de base de données (mdb, accdb...) qui est verrouillé. Une seule personne à la fois peut ouvrir une base de données en mode exclusif.

Dans ce fichier, une ligne est ajoutée (ou modifiée) avec les informations de l'utilisateur (cf. le chapitre suivant pour la structure des données).

Lors de nouvelles connexions, une ligne est ajoutée pour chaque nouvel utilisateur.

Attention : lors d'une déconnexion, la ligne correspondant à l'utilisateur n'est pas supprimée.

La ligne d'information d'un utilisateur déconnecté est marquée comme disponible grâce à un mécanisme de verrou que nous verrons dans le chapitre suivant.
Une prochaine connexion écrasera les informations d'une ligne disponible dans le fichier s'il en existe une, ou créera une nouvelle ligne si nécessaire.

Lorsqu'il n'y a plus aucun utilisateur connecté, le fichier de verrou est supprimé.

Notez que le contenu du fichier de verrous est présenté avec une ligne par connexion pour une meilleure compréhension.
En réalité les informations sont écrites dans le fichier sans retour à la ligne.

Lire le contenu du fichier de verrouillage n'est donc pas suffisant pour connaître la liste des utilisateurs connectés.
Des utilisateurs qui se sont connectés puis déconnectés peuvent apparaître dans la liste.

II-D - Les Verrous

II-D-1 - Le verrouillage de fichiers sous Windows

Un fichier sous Windows peut être ouvert en mode partagé ou en mode exclusif :
- en mode partagé, chaque utilisateur peut verrouiller une partie différente du fichier ;
- en mode exclusif, l'ensemble du fichier est verrouillé et un seul utilisateur à la fois peut ouvrir le fichier.

Dans ces deux cas, le fichier verrouillé en totalité ou en partie ne peut pas être supprimé.

Type d'ouverture du fichier

Type de verrouillage

Utilisateurs simultanés

Partagé

Chaque utilisateur peut verrouiller une partie du fichier

Plusieurs possibles

Exclusif

Un seul utilisateur verrouille l'ensemble du fichier

Un seul

Les bases de données Access sont ouvertes en mode exclusif ou partagé.

Les fichiers de verrouillage Access sont ouverts en mode partagé.

II-D-2 - Verrouillage des fichiers ldb et accdb

À chaque connexion, le système vérifie si une connexion est disponible.
Un verrou est alors posé pour cette connexion.

Lorsqu'un utilisateur quitte la base de données, le verrou utilisé est alors supprimé et la connexion est réutilisable.

On voit sur ce diagramme que la connexion de l'utilisateur 2 est réutilisée par l'utilisateur 4.
Pourtant la deuxième "ligne" du fichier était déjà renseignée avec les données de l'utilisateur 2, mais le verrou (qui n'est pas visible) a été supprimé.

III - Lire et filtrer le fichier de verrouillage

Dans un premier temps nous allons lire le contenu d'un fichier de verrouillage.
Ensuite nous verrons comment filtrer la liste des utilisateurs pour ne garder que ceux qui sont connectés.

III-A - Lire le contenu du fichier

On pourrait lire le fichier de verrouillage grâce aux fonctions VBA.
Voici un exemple de code pour illustrer cette éventualité.

Sub ReadLDB()
Dim iFile As Integer
Dim sComputer As String
Dim sUser As String
Dim cptUser As Long
iFile = FreeFile
Open "C:\MaBase.ldb" For Input As iFile
For cptUser = 1 To LOF(iFile) / 64
sComputer = RTrim(Input(31, iFile))
sUser = RTrim(Input(31, iFile))
Debug.Print sComputer & ":" & sUser
Next
Close iFile
End Function

Chaque entrée dans le fichier contient l'ordinateur et l'utilisateur, chacun sur trente-deux caractères.
Notez qu'on ne lit que trente-et-un caractères. Le trente-deuxième est un caractère Null que l'instruction Input ignore.

Lien vers un tutoriel : Manipulation des fichiers en VBA

Nous allons plutôt utiliser des API pour lire le fichier, car elles nous seront également utiles pour lire les verrous.
Ces API sont des fonctions contenues dans la bibliothèque kernel32.dll.

Voici leurs déclarations, ainsi que les constantes dont nous avons besoin :

Déclaration des API et constantes

Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, _
ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As Any) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, _
ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Any, _
ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, _
ByVal hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long
Private Declare Function LockFile Lib "kernel32" (ByVal hFile As Long, ByVal dwFileOffsetLow As Long, _
ByVal dwFileOffsetHigh As Long, ByVal nNumberOfBytesToLockLow As Long, _
ByVal nNumberOfBytesToLockHigh As Long) As Long
Private Declare Function UnlockFile Lib "kernel32" (ByVal hFile As Long, ByVal dwFileOffsetLow As Long, _
ByVal dwFileOffsetHigh As Long, ByVal nNumberOfBytesToUnlockLow As Long, _
ByVal nNumberOfBytesToUnlockHigh As Long) As Long
Private Declare Function SetFilePointer Lib "kernel32" (ByVal hFile As Long, ByVal lDistanceToMove As Long, _
lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long

Private Const GENERIC_READ = &H80000000     ' Ouverture en lecture
Private Const GENERIC_WRITE = &H40000000    ' Ouverture en écriture
Private Const FILE_SHARE_READ = &H1         ' Lecture partagée
Private Const FILE_SHARE_WRITE = &H2        ' Écriture partagée
Private Const OPEN_EXISTING = 3             ' Le fichier doit exister
Private Const DEBUT_LOCK = &H10000001       ' Adresse de début des locks

Pour simplifier, le code est limité aux versions 32 bits d'Office.
Pour une version 64 bits d'Office, voir cet article : Développer avec Office 64 bits.

Les modules de l'application à télécharger sont compatibles avec Office 64 bits.

Retrouvez la documentation de ces fonctions sur MSDN : File Management Functions.

Voici une procédure qui utilise les API déclarées précédemment pour lire le contenu du fichier de verrous :

Sub ReadLDB()
Dim hFile As Long
Dim lReturn As Long
Dim lBytesRead As Long
Dim sComputer As String
Dim sUser As String
' Ouverture fichier en mode partagé
hFile = CreateFile(ByVal "C:\Article_Dvp\documents\utilisateurs-ldb\work\ldbviewer - Copie.ldb", _
ByVal GENERIC_READ Or GENERIC_WRITE, _
ByVal FILE_SHARE_READ Or FILE_SHARE_WRITE, _
ByVal 0&, ByVal OPEN_EXISTING, ByVal 0&, ByVal 0&)
' Si -1 => erreur
If hFile <> -1 Then
' Boucle pour lire le contenu du fichier
Do
' Initialise la zone qui reçoit le nom de l'ordinateur
sComputer = Space(32)
' Lecture de 32 caractères
lReturn = ReadFile(hFile, ByVal sComputer, 32, lBytesRead, ByVal 0&)
' Si erreur ou plus de données => on sort
If lReturn = 0 Or lBytesRead = 0 Then Exit Do
' Retire le Null et les espaces à droite
sComputer = Left(sComputer, InStr(sComputer, Chr(0)) - 1)
' Initialise la zone qui reçoit le nom de l'utilisateur
sUser = Space(32)
' Lecture de 32 caractères
lReturn = ReadFile(hFile, ByVal sUser, 32, lBytesRead, ByVal 0&)
' Retire le Null et les espaces à droite
sUser = Left(sUser, InStr(sUser, Chr(0)) - 1)
' Si erreur ou plus de données => on sort
If lReturn = 0 Or lBytesRead = 0 Then Exit Do
' Écriture dans fenêtre exécution
Debug.Print sComputer & ":" & sUser
Loop
' Fermeture du fichier
CloseHandle hFile
End If
End Function

Il nous reste à filtrer les connexions non actives.

III-B - Filtrer le contenu du fichier grâce aux verrous

Les verrous sont posés par Access à un emplacement défini à &H10000001 (en hexadécimal), c'est-à-dire au-delà du fichier physique.
Cf. Understanding Microsoft Jet Locking White Paper pour plus d'explication (en anglais).

Cet emplacement a été défini précédemment par la constante DEBUT_LOCK.
La première connexion a donc son verrou à DEBUT_LOCK.
La deuxième connexion à DEBUT_LOCK + 1.
Il peut y avoir 255 connexions au maximum.

Windows peut placer un verrou à une position supérieure à la taille de fichier.
Cela ne verrouillera pas un endroit spécifique du fichier.
Imaginez un catalogue de verrous liés à un fichier, et non pas un verrou posé "sur" ce fichier.

En fait il n'est pas possible de lire l'état d'un verrou.
Il faut tenter d'en poser un : si cela retourne une erreur c'est qu'il y a déjà un verrou.

Voici la procédure avec l'ajout de la tentative de pose de verrou pour savoir si la connexion est active.

Function ReadLDB()
Dim hFile As Long
Dim lReturn As Long
Dim lBytesRead As Long
Dim sComputer As String
Dim sUser As String
Dim lNbUser As Long
' Ouverture fichier en mode partagé
hFile = CreateFile(ByVal "C:\Article_Dvp\documents\utilisateurs-ldb\work\ldbviewer.ldb", _
ByVal GENERIC_READ Or GENERIC_WRITE, _
ByVal FILE_SHARE_READ Or FILE_SHARE_WRITE, _
ByVal 0&, ByVal OPEN_EXISTING, ByVal 0&, ByVal 0&)
' Si -1 => erreur
If hFile <> -1 Then
' Boucle pour lire le contenu du fichier
Do
lNbUser = lNbUser + 1
' Initialise la zone qui reçoit le nom de l'ordinateur
sComputer = Space(32)
' Lecture de 32 caractères
lReturn = ReadFile(hFile, ByVal sComputer, 32, lBytesRead, ByVal 0&)
' Si erreur ou plus de données => on sort
If lReturn = 0 Or lBytesRead = 0 Then Exit Do
' Retire le Null et les espaces à droite
sComputer = Left(sComputer, InStr(sComputer, Chr(0)) - 1)
' Initialise la zone qui reçoit le nom de l'utilisateur
sUser = Space(32)
' Lecture de 32 caractères
lReturn = ReadFile(hFile, ByVal sUser, 32, lBytesRead, ByVal 0&)
' Retire le Null et les espaces à droite
sUser = Left(sUser, InStr(sUser, Chr(0)) - 1)
' Si erreur ou plus de données => on sort
If lReturn = 0 Or lBytesRead = 0 Then Exit Do
' Test le lock pour savoir si l'utilisateur est toujours connecté
If LockFile(hFile, DEBUT_LOCK + lNbUser - 1, 0, 1, 0) = 0 Then
' Erreur de lock => utilisateur connecté
' Écriture dans fenêtre exécution
Debug.Print sComputer & ":" & sUser
Else
' Lock réussi => on "unlock"
Call UnlockFile(hFile, DEBUT_LOCK + lNbUser - 1, 0, 1, 1)
End If
Loop
' Fermeture du fichier
CloseHandle hFile
End If
End Function

On a dû rajouter un compteur lNbUser qui permet d'incrémenter la position du verrou à tester.

Ne pas oublier de déverrouiller si la tentative de pose de verrou aboutit.
Cependant, les verrous posés sont supprimés lorsque le fichier hFile est fermé par CloseHandle.

IV - Cas particulier du mode exclusif

Si la base est ouverte en mode exclusif, aucun fichier de verrouillage n'est créé.
Il est possible de détecter qu'une base est ouverte en mode exclusif en essayant de l'ouvrir... en mode exclusif.

Function isExclusive(pDataBase As String) As Boolean
Dim f As Integer
On Error GoTo Gestion_Erreur
' Cherche un identifiant fichier libre
f = FreeFile
' Tente d'ouvrir la base de données en lecture exclusive
Open pDataBase For Binary Access Read Lock Read As #f
' Ferme le fichier s'il a été ouvert
Close f
Exit Function
Gestion_Erreur:
' Erreur 70 (Permission refusée) si base déjà ouverte en exclusif
If Err.Number = 70 Then isExclusive = True
End Function

Dans la fonction ci-dessus, l'instruction Open lève une erreur si la base (dont le chemin est donné dans le paramètre pDatabase) est déjà ouverte en mode exclusif.

Il est possible de faire la même tentative d'ouverture avec les API : cf. les modules de l'application à télécharger pour un exemple.

V - Cas de la fermeture brutale d'Access

Ce mécanisme de verrou a un avantage non négligeable : le verrou ne survit pas à la fermeture du processus (donc de l'application) qui l'a posé.

C'est-à-dire que la connexion est rendue disponible car il n'y a plus de verrou dessus.

Notez que le fichier ldb (ou accdb) n'est pas supprimé lors de la déconnexion brutale du dernier utilisateur.
Comme il n'y a plus de verrou sur le fichier, il est possible (mais pas indispensable) de le supprimer manuellement.

VI - Verrouiller les connexions

VI-A - En posant des verrous sur le fichier ldb ou accdb

Ce paragraphe et notamment le code qui s'y trouve n'est pas prévu pour fonctionner en production.
Ce n'est qu'un exemple pour illustrer le mécanisme de verrou.

Petite astuce si vous souhaitez empêcher toute nouvelle connexion à la base de données :
Posez un verrou avec LockFile sur chacune des 255 connexions.
Cela nécessite de conserver un pointeur vers le fichier (le hFile obtenu avec CreateFile) pour que les verrous perdurent.

Voici un module pour exemple :

Option Compare Database
Option Explicit

Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, _
ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As Any) As Long
Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, _
ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Any, _
ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, _
ByVal hTemplateFile As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long
Private Declare Function LockFile Lib "kernel32" (ByVal hFile As Long, ByVal dwFileOffsetLow As Long, _
ByVal dwFileOffsetHigh As Long, ByVal nNumberOfBytesToLockLow As Long, _
ByVal nNumberOfBytesToLockHigh As Long) As Long
Private Declare Function UnlockFile Lib "kernel32" (ByVal hFile As Long, ByVal dwFileOffsetLow As Long, _
ByVal dwFileOffsetHigh As Long, ByVal nNumberOfBytesToUnlockLow As Long, _
ByVal nNumberOfBytesToUnlockHigh As Long) As Long
Private Declare Function SetFilePointer Lib "kernel32" (ByVal hFile As Long, ByVal lDistanceToMove As Long, _
lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long

Private Const GENERIC_READ = &H80000000     ' Ouverture en lecture
Private Const GENERIC_WRITE = &H40000000    ' Ouverture en écriture
Private Const FILE_SHARE_READ = &H1         ' Lecture partagée
Private Const FILE_SHARE_WRITE = &H2        ' Écriture partagée
Private Const OPEN_EXISTING = 3             ' Le fichier doit exister
Private Const DEBUT_LOCK = &H10000001       ' Adresse de début des locks

Private hFile As Long

Public Sub LockDb(pFile As String)
Dim lNbUser As Long
Dim lNbOK As Long
Dim lNbKO As Long
' Ouverture fichier en mode partagé
hFile = CreateFile(ByVal pFile, _
ByVal GENERIC_READ Or GENERIC_WRITE, _
ByVal FILE_SHARE_READ Or FILE_SHARE_WRITE, _
ByVal 0&, ByVal OPEN_EXISTING, ByVal 0&, ByVal 0&)
' Si -1 => erreur
If hFile <> -1 Then
' Boucle sur les 255 verrous
For lNbUser = 1 To 255
' Pose d'un verrou
If LockFile(hFile, DEBUT_LOCK + lNbUser - 1, 0, 1, 0) = 0 Then
' Connexion déjà utilisée => pose de verrou impossible
lNbKO = lNbKO + 1
Else
' Pose de verrou réussie
lNbOK = lNbOK + 1
End If
Next
End If
MsgBox lNbOK & " connexions verrouillées" & vbCrLf & _
lNbKO & " connexions en utilisation"
End Sub

Public Sub UnlockDb()
Dim lNbUser As Long
Dim lNbOK As Long
Dim lNbKO As Long
' Si -1 => erreur
If hFile <> -1 And hFile <> 0 Then
' Boucle sur les 255 verrous
For lNbUser = 1 To 255
' Retire un verrou
If UnlockFile(hFile, DEBUT_LOCK + lNbUser - 1, 0, 1, 1) = 0 Then
' Connexion déjà utilisée par un autre process => retrait de verrou impossible
lNbKO = lNbKO + 1
Else
' Retrait de verrou réussi
lNbOK = lNbOK + 1
End If
Next
End If
' Libère le fichier
CloseHandle hFile
hFile = 0
MsgBox lNbOK & " connexions déverrouillées" & vbCrLf & _
lNbKO & " connexions en utilisation"
End Sub

Verrouillez les connexions avec la procédure LockDb :

LockDb "C:\MaBase.ldb"

Mettez en paramètre le fichier ldb ou accdb.
Notez que seules les connexions libres peuvent être verrouillées.

Après verrouillage de toutes les connexions, un message apparait à l'ouverture de la base de données comme s'il y avait 255 utilisateurs connectés.

Exécutez la procédure UnLockDb pour déverrouiller les connexions.

Il faut un fichier ldb ou accdb pour verrouiller une base de données.
Si aucun utilisateur n'est connecté, on peut créer un fichier texte vide nommé NomDeLaBase.ldb ou NomDeLaBase.accdb et y poser des verrous.

Les verrous posés sont retirés lorsqu'on ferme l'application qui les a créés, même si on n'exécute pas la fonction UnlockFile.

VI-B - Avec ADO et Connection Control

Beaucoup plus simple (à partir d'Access 2000), ouvrez la base de données à verrouiller et exécutez ce code :

CurrentProject.Connection.Properties("Jet OLEDB:Connection Control") = 1

La base de données est alors verrouillée pour les nouvelles connexions, tant que vous ne fermez pas la base de données d'où vous avez exécuté ce code.
Les utilisateurs déjà connectés conservent leur connexion jusqu'à sa fermeture.

Un message apparait à l'ouverture de la base de données :

Pour déverrouiller la base de données, exécutez le même code avec la valeur 2 au lieu de 1.

Source : Connection Control.

VII - Application à télécharger

Voici une application qui utilise la technique expliquée dans cet article.
clLDBUser est un module qui contient les données des utilisateurs.
clLDBViewer est le module qui contient le code permettant de lister les utilisateurs connectés.

Le formulaire FLdbViewer utilise ces deux modules au sein d'une interface.

Vous pouvez réutiliser les modules de code clLDBUser et clLDBViewer.

Télécharger l'application au format Access 2000

VIII - Autres possibilités pour lister les utilisateurs

La méthode OpenSchema de la bibliothèque ADO permet de lire les connexions d'une base de données.
Lien MSDN : Use ADO to Return a List of Users Connected to a Database.

Si vous avez besoin d'un outil prêt à l'emploi, utilisez cette visionneuse d'argyronet :
Visionneuse des Utilisateurs d'une base de données Access.

IX - Remerciements

Merci à Domi2 et Pierre Fauconnier pour leurs remarques.

Merci à ClaudeLELOUP pour sa relecture orthographique.