Voir le flux RSS

pijaku

Limites et lacunes de VBA

Note : 2 votes pour une moyenne de 1,00.
par , 14/09/2018 à 16h34 (76 Affichages)
________________________________________________________________________________________________________________


Bonjour,

Comme chacun sait, Excel et VBA ont des spécifications, limites et lacunes.

En ce qui concerne Excel, je ne vais pas les lister dans le sens ou c'est très bien fait ICI, du moins pour ce qui concerne les versions postérieures à 2007.

Vous trouverez ICI les limites de chaque type de données (Byte, Integer, Long, Currency, String,...).

Nous allons, dans ce sujet (re)découvrir et tenter de lister et résoudre un maximum de celles de VBA.

Etant persuadé en avoir oublié, n'hésitez pas à me les indiquer en réponse pour que ce sujet soit le plus exhaustif possible.

________________________________________________________________________________________________________________

  1. Le nombre maximum d'Areas dans une feuille
    • Limite
      Ce nombre maximum est fixé à 8 192.
      Ce nombre correspond à la moitié de la limite de la méthode SpecialCells (cf plus bas) qui, vous en conviendrez, est fortement liée à la notion d'Areas.
      A noter toutefois que ce nombre semble s'augmenter au fur et à mesure des tests, Excel "réagissant" en effet sur ce sujet afin de s'adapter à la demande de l'utilisateur.
      Ainsi, un même utilisateur, sur un même pc, sans toucher à l'installation d'Office, peut voir ce maximum considérablement s'agrandir...
      Cela reste, pour moi, un des mystères de MS Excel.
    • Comment y pallier
      cf paragraphe ci-dessous, à propos de la méthode SpecialCells.


  2. Limite de la méthode SpecialCells
    • Limite
      Elle ne peut recenser d'un seul coup plus d'un certain nombre d'aires (Areas) résultant de son application.
      Ce nombre est égal à la moitié de la limite d'un type integer soit 16 385.
    • Comment y pallier
      Lire la contribution d'unparia à ce sujet


  3. Le nombre maximum d'éléments dans un Array
    Ce nombre est limité par la quantité de mémoire disponible.
    Il varie donc selon le type de données contenues dans cet Array.
    C'est pourquoi il est toujours vivement recommandé de bien typer toutes vos variables.


  4. La division entière : \
    • Limite
      Le plus grand nombre que l'on puisse diviser grâce à l'opérateur \ est 2 147 483 647.
      Il correspond à la limite du type Long.
    • Test :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      Sub Demo_Division_Entiere()
      Dim d As Double, i As Double
         d = 2147483647
         MsgBox d \ 83647
         d = 2147483648#
         On Error Resume Next
         MsgBox d \ 2
         If Err.Number > 0 Then MsgBox Err.Description
         On Error GoTo 0
      End Sub
    • Comment y pallier
      Il suffit, pour cela, de prendre la partie entière de la division de deux Doubles.
      Voici une fonction qui fait très bien l'affaire, à titre d'exemple :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      Function Division_Entiere(Nb1 As Double, Nb2 As Double) As Double
         Division_Entiere = Int(Nb1 / Nb2)
      End Function
      Code appelant :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      MsgBox Division_Entiere(21474836489#, 12542)
    • A noter :
      VBA n'étant pas très bien armé pour les opérations sur de grands nombres, la division de deux Double peut générer des erreurs...


  5. Le Modulo : Mod
    • Limite
      L'opérateur Mod est limité à 2 147 483 647.
      Il correspond à la limite du type Long, tout comme pour la division entière.
    • Test :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      Sub Demo_Mod()
      Dim d As Double
         d = 2147483647
         MsgBox d Mod 83647
         d = 2147483648#
         On Error Resume Next
         MsgBox d Mod 2
         If Err.Number > 0 Then MsgBox Err.Description
         On Error GoTo 0
      End Sub
    • Comment y pallier
      Tout comme pour la division entière, avec une petite fonction perso. Exemple :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      Function ModSansLimite(Nb1 As Double, Nb2 As Double) As Double
         ModSansLimite = Nb1 - (Int(Nb1 / Nb2) * Nb2)
      End Function
      Code appelant :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      MsgBox ModSansLimite(2147483648, 83647)


  6. La méthode WorksheetFunction.Match
    • Limite
      La limite de cette méthode est de 65 536, en ce qui concerne les variables tableaux.
      Ce nombre correspond à la limites de lignes dans Excel 2003.
      Cette limite n'existe plus si vous transférez votre tableau dans une feuille.
    • Test :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      Sub Demo_Match()
      Dim t() As String, v, var As String, i As Long
         var = "liste21"
         ReDim t(65535)
         For i = LBound(t) To UBound(t)
            t(i) = "liste" & i
         Next
         v = Application.Match(var, t, 0)
         MsgBox v
         ReDim Preserve t(65536)
         t(65536) = "liste65536"
         On Error Resume Next
         v = Application.Match(var, t, 0)
         If Err.Number > 0 Then MsgBox Err.Description
         On Error GoTo 0
      End Sub
    • Comment y pallier
      Par une fonction personnelle.

      Cette fonction perso va utiliser une fonction qui détermine le nombre de dimensions d'un Array (Function Nb_Dimensions).
      Fonction Nb_Dimensions (utile également dans le paragraphe sur Transpose (ci-dessous)) :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      Function Nb_Dimensions(ByRef Tableau As Variant) As Integer
      'Retourne le nombre de dimensions d'une variable tableau
      Dim d As Integer, t As Long
         On Error GoTo Fin
         Do
            d = d + 1
            t = UBound(Tableau, d)
         Loop
      Fin:
         On Error GoTo 0
         Nb_Dimensions = d - 1
      End Function
      La fonction de "remplacement" de Match :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      Function Get_Row(ByRef Tableau As Variant, ByRef Texto As Variant, Optional ByRef Colonne As Long) As Long
      'Détermine l'index d'un élément à partir de son contenu
      'Retourne un Long représentant l'index de cet élément
      'Retourne -1 si l'élément n'existe pas dans le tableau ou erreur (de colonne, de Tableau, etc).
      Dim i As Long, strTemp As String
         Get_Row = -1
         Select Case Nb_Dimensions(Tableau)
             Case 1
      '===== le présent code, déposé par ucfoutu sur VBFrance, est la seule propriété de VBFrance
      '=====VBFrance en autorise les libres copie et utilisation à la seule condition d'y laisser
      '=====insérées les trois présentes lignes commentées --- ucfoutu ---
               strTemp = Chr(0) & Join(Tableau, Chr(0)) & Chr(0)
               i = InStr(strTemp, Chr(0) & Texto & Chr(0))
               If i = 0 Then
                  Get_Row = -1
               Else
                  strTemp = Mid(strTemp, 1, i)
                  Get_Row = UBound(Split(strTemp, Chr(0))) - 1
                  If Get_Row < 0 Then Get_Row = -1
               End If
      '======================
            Case 2
               'si paramètre colonne omis, on prends la première
               If Colonne = 0 Then Colonne = LBound(Tableau, 2)
               For i = LBound(Tableau, 1) To UBound(Tableau, 1)
                  If Tableau(i, Colonne) = Texto Then Get_Row = i: Exit For
               Next i
         End Select
      End Function
    • A noter :
      La même limite est fixée en ce qui concerne la fonction Index (WorksheetFunction.Index).
      Il est cependant beaucoup plus simple d'y remédier...


  7. La méthode Transpose
    • Limite
      La limite de cette méthode est de 65 536.
      Tout comme Match et Index, ce nombre correspond à la limite de lignes dans Excel 2003.
    • A noter :
      Transpose, étant à la base une fonction de feuille de calcul, retourne un Array en Base 1 même si l'Array qui lui est injecté est en Base 0.
      Notre fonction pour pallier à la limite de Transpose palliera également à cela.
    • Test :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      Sub EssaiTranspose()
      Dim t() As String, v, i As Long
       
         ReDim t(2, 65535)
         For i = LBound(t) To UBound(t)
            t(1, i) = "liste" & i
         Next
         v = Application.Transpose(t)
         MsgBox "Avec TRANSPOSE, cas < 65 536" & vbCrLf & vbCrLf & _
         "t : " & LBound(t, 1) & " To " & UBound(t, 1) & "  <=>  " & LBound(t, 2) & " To " & UBound(t, 2) & vbCrLf & _
          "v : " & LBound(v, 1) & " To " & UBound(v, 1) & "  <=>  " & LBound(v, 2) & " To " & UBound(v, 2)
         Erase v
         ReDim Preserve t(2, 66000)
         For i = 65536 To 66000
            t(1, i) = "liste" & i
         Next i
         On Error Resume Next
         v = Application.Transpose(t)
         If Err.Number > 0 Then MsgBox "Avec TRANSPOSE, cas > 65 536" & vbCrLf & vbCrLf & Err.Description
         On Error GoTo 0
      End Sub
    • Comment y pallier
      Il nous faut une fonction personnalisée qui :
      > transpose l'array
      > le restitue dans la même base
      Exemple (Attention : utilise la fonction Nb_Dimensions cf paragraphe précédent) :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      Function Transposition(ByRef Tableau As Variant) As Variant
      'Transpose une variable tableau à 2 dimensions
      Dim tabl, i As Long, j As Long, c As Long
         Select Case Nb_Dimensions(Tableau)
            Case 1
               c = LBound(Tableau)
               c = IIf(c = 0, 1, 0)
               ReDim tabl(1 To UBound(Tableau) + c, 1 To 1)
               For i = 1 To UBound(tabl)
                  tabl(i, 1) = Tableau(i - c)
               Next
            Case 2
               ReDim tabl(LBound(Tableau, 2) To UBound(Tableau, 2), LBound(Tableau, 1) To UBound(Tableau, 1))
               For i = LBound(Tableau, 1) To UBound(Tableau, 1)
                  For j = LBound(Tableau, 2) To UBound(Tableau, 2)
                     tabl(j, i) = Tableau(i, j)
                  Next j
               Next i
            Case Else
               MsgBox "Le tableau ne comporte pas une ou deux dimensions"
               Exit Function
         End Select
         Transposition = tabl
         Erase tabl
      End Function
      Code appelant :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      Dim t() As String, v, i As Long
         ReDim t(2, 66666)
         For i = LBound(t) To UBound(t)
            t(1, i) = "liste" & i
         Next
         v = Transposition(t)
         MsgBox "Avec Transposition, cas > 65 536" & vbCrLf & vbCrLf & _
         "t : " & LBound(t, 1) & " To " & UBound(t, 1) & "  <=>  " & LBound(t, 2) & " To " & UBound(t, 2) & vbCrLf & _
          "v : " & LBound(v, 1) & " To " & UBound(v, 1) & "  <=>  " & LBound(v, 2) & " To " & UBound(v, 2)

  8. Limite de la méthode Union
    • Lacunes
      Deux lacunes sont à déplorer au sujet de cette Méthode :
      > Si un des arguments = Nothing elle engendre une erreur Argument ou appel de procédure incorrect
      > Si deux Range s'intersectent, leurs cellules communes sont comptabilisées 2 fois.
    • Test :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      Sub Demo_Union()
      Dim Rng As Range, R As Range
         On Error Resume Next
         Set Rng = Application.Union(R, Range("A8:C10"), Range("B9:D11"))
         If Err.Number > 0 Then MsgBox Err.Description
         On Error GoTo 0
         Set Rng = Application.Union(Range("A8:C10"), Range("B9:D11"))
         MsgBox Rng.Address & "   " & Rng.Cells.Count
      End Sub
    • Comment y pallier
      Avec cette fonction :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      Function UnionSansDoublons(ParamArray Rngs() As Variant) As Range
      Dim RngTemp As Range, R As Range, i As Long
         For i = LBound(Rngs) To UBound(Rngs)
            If Not Rngs(i) Is Nothing Then
               If RngTemp Is Nothing Then
                  Set RngTemp = Rngs(i)
               Else
                  For Each R In Rngs(i).Cells
                     If Application.Intersect(RngTemp, R) Is Nothing Then
                        Set RngTemp = Application.Union(RngTemp, R)
                     End If
                  Next R
               End If
            End If
         Next i
         Set UnionSansDoublons = RngTemp
      End Function
      Code appelant :
      Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
      1
      2
      3
      Dim Rng As Range, R As Range
         Set Rng = UnionSansDoublons(R, Range("A8:C10"), Range("B9:D11"))
         MsgBox Rng.Address & "   " & Rng.Cells.Count


  9. Limite de la fonction Join
    La fonction Join comporte 2 défauts :
    1. Elle ne fonctionne qu'avec un tableau de String (et/ou de Variant/Variant),
    2. Elle ne fonctionne qu'avec des Arrays à une dimension.

    Ces deux défauts, pris simplement, ne méritent pas forcément que l'on s'y attelle car ils sont très aisément contournables en VBA.

    Si l'on regarde de près "l'équivalant" de Join dans les formules Excel, on se rend compte que la fonction CONCATENER comporte également deux défauts :
    1. Elle ne fonctionne pas avec des plages entières, ex : =CONCATENER(A1:C10),
    2. On ne peut pas y intégrer un séparateur, ex : =CONCATENER(A1;A2;"-")


    Fort de ces 4 "défauts", je vous propose aujourd'hui une fonction qui va gérer tout cela.

    Note : une seconde fonction ICI prends en charge les formats de cellules...

    Code de la fonction JOINT :
    Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    Public Function JOINT(ByRef Tableau As Variant, _
                            Optional ByRef RowSeparator As String = "", _
                               Optional ByRef ColumnSeparator As String = "", _
                                  Optional ByRef ByRow As Boolean = True) As String
    Dim st() As String, temp As Variant, maVar As Variant, i As Long, j As Long, d As Integer
       If TypeOf Tableau Is Excel.Range Then
          temp = Tableau.Value
       ElseIf IsArray(Tableau) Then
          temp = Tableau
       Else
          JOINT = "Erreur d'exécution '5' : " & vbCrLf & vbCrLf & _
                   "Argument ou appel de procédure incorrect" & vbCrLf & _
                      "Argument incorrect : ByRef Tableau As Variant"
          MsgBox JOINT, vbOKOnly + vbCritical, "Microsoft Visual Basic"
          Exit Function
       End If
       On Error Resume Next
       Do While Err.Number = 0
          d = d + 1
          i = UBound(temp, d)
       Loop
       On Error GoTo 0
       Select Case d - 1
          Case 1: maVar = temp((UBound(temp) + LBound(temp)) \ 2)
          Case 2: maVar = temp((UBound(temp) + LBound(temp)) \ 2, LBound(temp))
          Case Else
             JOINT = "Erreur d'exécution '5' : " & vbCrLf & vbCrLf & _
                   "Argument ou appel de procédure incorrect" & vbCrLf & _
                      "Argument incorrect : ByRef Tableau As Variant"
             MsgBox JOINT, vbOKOnly + vbCritical, "Microsoft Visual Basic"
             Erase temp
             Exit Function
       End Select
       Select Case VarType(maVar)
          Case 0, 2, 3, 4, 5, 6, 7, 8, 11, 12, 17
             Select Case d - 1
                Case 1
                   ReDim st(LBound(temp) To UBound(temp)) As String
                   For i = LBound(temp) To UBound(temp)
                      st(i) = temp(i)
                   Next
                   JOINT = Join(st, RowSeparator)
                Case 2
                   If ByRow Then
                      ReDim st(LBound(temp, 1) To UBound(temp, 1)) As String
                      For i = LBound(temp, 1) To UBound(temp, 1)
                         For j = LBound(temp, 2) To UBound(temp, 2)
                            st(i) = st(i) & ColumnSeparator & temp(i, j)
                         Next
                         st(i) = Right(st(i), Len(st(i)) - Len(ColumnSeparator))
                      Next
                      JOINT = Join(st, RowSeparator)
                   Else
                      ReDim st(LBound(temp, 1) To UBound(temp, 1)) As String
                      For i = LBound(temp, 2) To UBound(temp, 2)
                         For j = LBound(temp, 1) To UBound(temp, 1)
                            st(j) = temp(j, i)
                         Next
                         JOINT = JOINT & ColumnSeparator & Join(st, RowSeparator)
                      Next
                      JOINT = Right(JOINT, Len(JOINT) - Len(ColumnSeparator))
                   End If
             End Select
             Erase st
          Case 20 'LongLong
          Case 1 'VbNull
             JOINT = "Erreur d'exécution '94' : " & vbCrLf & vbCrLf & _
                      "Utilisation incorrecte de Null"
             MsgBox JOINT, vbOKOnly + vbCritical, "Microsoft Visual Basic"
          Case Else 'Objet ou vberror ou autre...
             JOINT = "Erreur d'exécution '5' : " & vbCrLf & vbCrLf & _
                      "Argument ou appel de procédure incorrect"
             MsgBox JOINT, vbOKOnly + vbCritical, "Microsoft Visual Basic"
       End Select
       Erase temp
    End Function


    Exemples de code appelant :
    Depuis VBA :
    Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    Option Explicit
     
    Sub Demonstration()
    Const NB As Long = 100000
    Dim Ra As Range, v, ll(1 To NB, 1 To 10) As Long, i As Long, j As Long, t As Double, dd(1 To NB, 1 To 10) As Double
    Dim vt(1000000) As Long, d(1000000) As Double, da(1000000) As Date, errnull(1000) As Variant
    Dim obj As Object
       Set obj = CreateObject("Scripting.dictionary")
       For i = LBound(vt) To UBound(vt)
          vt(i) = i
       Next
       For i = LBound(d) To UBound(d)
          d(i) = Timer
       Next
       For i = LBound(da) To UBound(da)
          da(i) = Now
       Next
       For i = LBound(errnull) To UBound(errnull)
          errnull(i) = Null
       Next
       Set Ra = Range("A1:J" & NB)
       Ra.Value = 10
       v = Ra.Value
       For i = 1 To NB
          For j = 1 To 10
             ll(i, j) = 10
          Next
       Next
       For i = 1 To NB
          For j = 1 To 10
             dd(i, j) = Timer
          Next
       Next
       Debug.Print "Erreurs : "
       Debug.Print JOINT(obj, ":", ";")
       Debug.Print "......................................"
       Debug.Print JOINT(errnull, ":", ";")
       Debug.Print ""
       Debug.Print "Test avec un Array à 1 dimension de 1 000 000 éléments."
       t = Timer
       Debug.Print TypeName(vt) & " : Longueur du String obtenu : " & Len(JOINT(vt, "/")) & " ==> " & Timer - t & " sec"
       t = Timer
       Debug.Print TypeName(d) & " : Longueur du String obtenu : " & Len(JOINT(d, "/")) & " ==> " & Timer - t & " sec"
       t = Timer
       Debug.Print TypeName(da) & " : Longueur du String obtenu : " & Len(JOINT(da, "/")) & " ==> " & Timer - t & " sec"
       Debug.Print ""
       Debug.Print "Test avec un Array à 2 dimensions de " & NB & " lignes et 10 colonnes."
       Debug.Print "ByRow............"
       t = Timer
       Debug.Print TypeName(Ra) & " : Longueur du String obtenu : " & Len(JOINT(Ra, "/", ":", True)) & " ==> " & Timer - t & " sec"
       t = Timer
       Debug.Print TypeName(v) & " : Longueur du String obtenu : " & Len(JOINT(v, "/", ":", True)) & " ==> " & Timer - t & " sec"
       t = Timer
       Debug.Print TypeName(ll) & " : Longueur du String obtenu : " & Len(JOINT(ll, "/", ":", True)) & " ==> " & Timer - t & " sec"
       t = Timer
       Debug.Print TypeName(dd) & " : Longueur du String obtenu : " & Len(JOINT(dd, "/", ":", True)) & " ==> " & Timer - t & " sec"
       Debug.Print ""
       Debug.Print "ByColumn............"
       t = Timer
       Debug.Print TypeName(Ra) & " : Longueur du String obtenu : " & Len(JOINT(Ra, "/", ":", False)) & " ==> " & Timer - t & " sec"
       t = Timer
       Debug.Print TypeName(v) & " : Longueur du String obtenu : " & Len(JOINT(v, "/", ":", False)) & " ==> " & Timer - t & " sec"
       t = Timer
       Debug.Print TypeName(ll) & " : Longueur du String obtenu : " & Len(JOINT(ll, "/", ":", False)) & " ==> " & Timer - t & " sec"
       t = Timer
       Debug.Print TypeName(dd) & " : Longueur du String obtenu : " & Len(JOINT(dd, "/", ":", False)) & " ==> " & Timer - t & " sec"
     
       Erase vt, d, da, ll, dd, v
       Ra.Clear
       Set Ra = Nothing
    End Sub
    Depuis une feuille de calcul :
    =JOINT(A1:C10;;;VRAI)
    =JOINT(A1:C10;;;FAUX)
    =JOINT(A1:C10;"_";"|";VRAI)
    =JOINT(A1:C10;"/";"|";FAUX)
    ....


Merci à vous, visiteurs, d'avoir lu ce billet.

A++
Franck

Envoyer le billet « Limites et lacunes de VBA » dans le blog Viadeo Envoyer le billet « Limites et lacunes de VBA » dans le blog Twitter Envoyer le billet « Limites et lacunes de VBA » dans le blog Google Envoyer le billet « Limites et lacunes de VBA » dans le blog Facebook Envoyer le billet « Limites et lacunes de VBA » dans le blog Digg Envoyer le billet « Limites et lacunes de VBA » dans le blog Delicious Envoyer le billet « Limites et lacunes de VBA » dans le blog MySpace Envoyer le billet « Limites et lacunes de VBA » dans le blog Yahoo

Mis à jour 10/11/2018 à 16h45 par LittleWhite

Tags: vba excel
Catégories
Sans catégorie

Commentaires