IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Développer avec Office 64 bits

Image non disponible

Depuis Office 2010, Microsoft propose une version 64 bits.
Quelle version dois-je installer ?
Quels sont les changements apportés à VBA par cette version ?
Comment mettre à niveau le code VBA de mon application ?

14 commentaires Donner une note à l´article (5)  

Il ne suffit pas de rajouter PtrSafe et de remplacer Long par LongPtr pour assurer une compatibilité correcte en 64 bits !!

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Quelle version dois-je installer ? 32 ou 64 bits ?

Tout d'abord, vérifiez que votre système Windows est 64 bits.
Ensuite, sachez qu’Office n'existe en version 64 bits qu'à partir d'Office 2010.

Pour plus de détails, consultez cette page du site de Microsoft :
Éditions 64 bits d'Office 2010

I-A. Avantages de la version 64 bits

Parmi les améliorations apportées, nous notons que :

Image non disponible Office 64 bits peut utiliser plus de mémoire que la version 32bits.
Cela peut améliorer les performances sur des fichiers volumineux ;

Image non disponible Excel peut charger des fichiers de plus de 2 Go.

I-B. Inconvénients de la version 64 bits

Les inconvénients sont à ce jour plus nombreux, nous notons que :

Image non disponible Les fichiers « compilés » de type MDE, ADE et ACCDE créés avec une version 32bits ne peuvent être utilisés sur une version 64 bits ;

Image non disponible Les contrôles ActiveX et AddIn COM doivent être compilés en 64 bits.
En d'autres termes, si vous utilisiez par exemple les programmes MzTools ou SmartIndenter, ceux-ci ne fonctionneront pas.
Même punition si vous avez installé un contrôle ActiveX.
Il faut alors installer une version 64 bits de ces composants, si elle arrive un jour…

Microsoft n'a pas prévu à ce jour de recompiler pour 64 bits les contrôles ActiveX tels que TreeView, ListView, ImageList; …

Image non disponible Le code d'appel aux API ne fonctionne plus en l'état.
Peut-être ne savez-vous pas que vous utilisez des API ?
Ces API sont des fonctions externes déclarées avec l'instruction Declare Function ou Declare Sub.
ShellExecute ou GetOpenFileName par exemple sont des API largement utilisées.
Une réécriture de ces instructions est nécessaire.

I-C. Que choisir ?

En attendant que 64 bits deviennent la norme pour tous les programmes, Microsoft conseille d'installer la version 32 bits d'Office.
C'est d'ailleurs la version installée par défaut.

N'installez la version 64 bits que si nécessaire.

Si vous n'êtes pas sûr, installez la version 32 bits.

Peut-être n'avez-vous pas le choix.
Vous avez des clients qui utilisent la version 64 bits ?
Alors nous allons voir comment rendre le code VBA compatible pour cette version.

II. Modification du modèle d'objets

Comme à chaque nouvelle version, le modèle d'objets a évolué.
Comprenez que certaines méthodes ou constantes ont été modifiées ou supprimées.
Certaines sont juste cachées, mais sont toujours utilisables (pour l'instant…).

Vous trouverez la liste de ces changements sur MSDN (en anglais) :

Image non disponible What's New in Excel 2010 ;
Image non disponible What's New in Access 2010 ;
Image non disponible What's New in Word 2010 ;
Image non disponible What's New in PowerPoint 2010.

Pour nous aider, Microsoft a mis à disposition un inspecteur de compatibilité :

Image non disponible Microsoft Office Code Compatibility Inspector

Cet outil n'est disponible que pour Excel, Word et PowerPoint.

Image non disponible Parmi les nouveautés du langage, Office 2010 a introduit deux nouveaux types de données : LongPtr et LongLong.
Ces types de données sont destinés à recevoir des pointeurs.
Ils peuvent contenir des valeurs supérieures à la limite du type de données Long.

LongPtr est en fait un Long en environnement 32 bits et un LongLong en environnement 64 bits.
Pour Office 2010, pas de problème donc : utilisez le Type LongPtr qui ciblera un Long en 32 bits et un LongLong en 64 bits.

Pour les versions antérieures, LongPtr n'existe pas, on continuera d'utiliser Long.

III. Cibler le code en fonction de la version

Pour que le code VBA cible une version particulière, il y a deux possibilités :

  • vérifier le retour de la propriété SysCmd(acSysCmdAccessVer) (pour Access) ou Application.Version (pour les autres applications) ;
  • utiliser des constantes de compilation.

La première possibilité ne peut être utilisée que si le code compile dans toutes les versions, par exemple :

Utilisation de acSysCmdAccessVer
Sélectionnez
If Val(SysCmd(acSysCmdAccessVer)) < 14 then
 MsgBox "Ce code s'exécute sur les versions antérieures à Office 2010"
Else
 MsgBox "Ce code s'exécute sur les versions à partir de Office 2010"
End if

L'instruction renvoie une chaîne de caractères :

  • 8.x : Office 97 ;
  • 9.x : Office 2000 ;
  • 10.x : Office 2002 ;
  • 11.x : Office 2003 ;
  • 12.x : Office 2007 ;
  • 14.x : Office 2010.

Notez l'absence de valeur 13 par superstition…

On utilise Val pour ne retourner que le chiffre avant le point.

La deuxième possibilité est utile pour écrire du code qui ne compilerait pas sur toutes les versions.
Par exemple les boutons de commandes possèdent, à partir d'Office 2010, une nouvelle propriété BorderColor.

Voici un petit code utilisant cette propriété :

Utilisation de acSysCmdAccessVer inappropriée
Sélectionnez
If Val(SysCmd(acSysCmdAccessVer)) >= 14 then
 Me.CommanButton1.BorderColor = vbRed
End if

Ce code fonctionne sur Office 2010, mais donne lieu à une erreur de compilation sur une version antérieure car BorderColor n'existe pas pour un bouton dans ces versions.

La solution est d'utiliser la nouvelle constante de compilation VBA7 :

Utilisation de constante de compilation VBA7
Sélectionnez
#If VBA7 Then
    Me.CommanButton1.BorderColor = vbRed
#End If

Notez l'utilisation importante des dièses (#).
VBA7 est une constante mise à disposition par VBA.
Elle vaut True (Vrai) à partir d'Office 2010.

Si on compile sur une version antérieure, ce qui est compris entre le #If VBA7 et le #End If est ignoré.

Il existe également la constante VBA6 qui est égale à True (Vrai) à partir d'Office 2000.

Pour finir, il existe également une nouvelle constante Win64 qui vaut True (Vrai) uniquement si Office est installé en 64 bits.

Ces constantes de compilation deviennent incontournables lorsqu'on utilise des API.

IV. Les API et Office 64 bits

Ce chapitre s'adresse plus particulièrement aux développeurs VBA.

Les API sont facilement détectables, ce sont des procédures dont la déclaration est :

  • Declare Function ;
  • Declare Sub.

Si vous exécutez un fichier contenant ce type de déclaration avec Office 2010 64 bits, ce code ne compile plus.

Pourquoi ?

Parce que le nouveau type de données LongPtr est désormais utilisé par les API.
Utiliser un type de données Long à la place ne serait pas correct car la valeur pourrait être tronquée.

Il est nécessaire de réécrire toutes ces déclarations et souvent l'appel à ces fonctions.

Cette page de MSDN résume la marche à suivre :
Compatibilité entre les versions 32 bits et 64 bits d'Office 2010

Et sur celle-ci, vous trouverez un fichier texte contenant les déclarations des principales API pour la version 64 bits :
Fichiers d’aide Office 2010 : Win32API_PtrSafe avec prise en charge de 64 bits

Le fichier est une archive autoextractible qui se décompresse dans : C:\Office 2010 Developer Resources.

Afin d'assurer une compatibilité entre les versions 32 bits et 64 bits,il est nécessaire d'utiliser les constantes de compilation vues précédemment afin d'écrire le code pour chaque version.

Nous allons maintenant voir un exemple concret.

IV-A. Exemple GetOpenFileName

L'exemple suivant s'applique à Access, mais le principe de développement est le même pour toutes les applications Office.

Afin d'ouvrir une fenêtre de sélection de fichier, l'API GetOpenFileName est couramment utilisée.
À partir d'Access 2002, FileDialog peut être employé mais n'est pas présent, par exemple, pour Access 2000.
Nous souhaitons pour cet exemple afficher une boîte de dialogue identique pour toutes les versions.
Nous décidons d'utiliser cette API, en utilisant le code de la FAQ Access de ce lien :
Afficher la boîte de dialogue ouvrir afin de récupérer le nom et le chemin du fichier sélectionné

Voici le code allégé de quelques commentaires et constantes non utilisés :

Fonction pour recherche de fichier
Sélectionnez
'Déclaration de l'API
Private Declare Sub PathStripPath Lib "shlwapi.dll" Alias "PathStripPathA" (ByVal pszPath As String)
Private Declare Function GetOpenFileName Lib "comdlg32.dll" Alias _
                   "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long
            
 'Structure du fichier
Private Type OPENFILENAME
    lStructSize As Long
    hwndOwner As Long
    hInstance As Long
    lpstrFilter As String
    lpstrCustomFilter As String
    nMaxCustFilter As Long
    nFilterIndex As Long
    lpstrFile As String
    nMaxFile As Long
    lpstrFileTitle As String
    nMaxFileTitle As Long
    lpstrInitialDir As String
    lpstrTitle As String
    flags As Long
    nFileOffset As Integer
    nFileExtension As Integer
    lpstrDefExt As String
    lCustData As Long
    lpfnHook As Long
    lpTemplateName As String
End Type
            
 'Constantes
Private Const OFN_HIDEREADONLY = &H4
            
Public Function OuvrirUnFichier(Handle As Long, _
                                Titre As String, _
                                TypeRetour As Byte, _
                                Optional TitreFiltre As String, _
                                Optional TypeFichier As String, _
                                Optional RepParDefaut As String) As String
            
Dim StructFile As OPENFILENAME
Dim sFiltre As String
            
 'Construction du filtre en fonction des arguments spécifiés
If Len(TitreFiltre) > 0 And Len(TypeFichier) > 0 Then
  sFiltre = TitreFiltre & " (" & TypeFichier & ")" & Chr$(0) & "*." & TypeFichier & Chr$(0)
End If
sFiltre = sFiltre & "Tous (*.*)" & Chr$(0) & "*.*" & Chr$(0)
            
 'Configuration de la boîte de dialogue
  With StructFile
    .lStructSize = Len(StructFile) 'Initialisation de la grosseur de la structure
    .hwndOwner = Handle 'Identification du handle de la fenêtre
    .lpstrFilter = sFiltre 'Application du filtre
    .lpstrFile = String$(254, vbNullChar) 'Initialisation du fichier '0' x 254
    .nMaxFile = 254 'Taille maximale du fichier
    .lpstrFileTitle = String$(254, vbNullChar) 'Initialisation du nom du fichier '0' x 254
    .nMaxFileTitle = 254  'Taille maximale du nom du fichier
    .lpstrTitle = Titre 'Titre de la boîte de dialogue
    .flags = OFN_HIDEREADONLY  'Option de la boite de dialogue
    If ((IsNull(RepParDefaut)) Or (RepParDefaut = "")) Then
        RepParDefaut = CurrentDb.Name
        PathStripPath (RepParDefaut)
        .lpstrInitialDir = Left(CurrentDb.Name, Len(CurrentDb.Name) - Len(Mid$(RepParDefaut, 1, _
InStr(1, RepParDefaut, vbNullChar) - 1)))
        Else: .lpstrInitialDir = RepParDefaut
    End If
  End With
            
If (GetOpenFileName(StructFile)) Then 'Si un fichier est sélectionné
    Select Case TypeRetour
      Case 1: OuvrirUnFichier = Trim$(Left(StructFile.lpstrFile, InStr(1, StructFile.lpstrFile, vbNullChar) - 1))
      Case 2: OuvrirUnFichier = Trim$(Left(StructFile.lpstrFileTitle, InStr(1, StructFile.lpstrFileTitle, vbNullChar) - 1))
    End Select
  End If
            
End Function

Si vous ne ciblez que des versions Office 32 bits, ce code est correct quelle que soit la version (2010 ou antérieure).
Par contre, ce code ne compile pas sur une version 64 bits d'Office!

Les déclarations d'API sont en cause.
Dans le fichier Win32API_PtrSafe.txt, vous trouverez la déclaration de GetOpenFileName pour 64 bits :

Déclaration de GetOpenFileName 64 Bits
Sélectionnez
Private Declare PtrSafe Function GetOpenFileName Lib "comdlg32.dll" _
   Alias "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long

J'ai juste ajouté Private pour réduire la portée de la fonction au module dans lequel elle est déclarée.
Le plus important est l'ajout de PtrSafe : cet attribut indique que l'on cible la version 64 bits.

Pour la déclaration de PathStripPath, il faut se débrouiller tout seul car elle n'apparaît pas dans le fichier précédent.
C'est toutefois assez simple, il suffit d'ajouter PtrSafe à la déclaration.
Il ne peut y avoir d'ambiguïté que sur les types Long, les autres types restant inchangés.

Déclaration de PathStripPath 64 Bits
Sélectionnez
Private Declare PtrSafe Sub PathStripPath Lib "shlwapi.dll" _
   Alias "PathStripPathA" (ByVal pszPath As String)

Utilisons la constante de compilation VBA7 afin de définir les déclarations en fonction de la version.

Déclaration des API pour les versions 32 et 64 bits
Sélectionnez
'Déclaration de l'API
#If VBA7 Then
Private Declare PtrSafe Sub PathStripPath Lib "shlwapi.dll" _
   Alias "PathStripPathA" (ByVal pszPath As String)
Private Declare PtrSafe Function GetOpenFileName Lib "comdlg32.dll" _
   Alias "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long
#Else
Private Declare Sub PathStripPath Lib "shlwapi.dll" _
   Alias "PathStripPathA" (ByVal pszPath As String)
Private Declare Function GetOpenFileName Lib "comdlg32.dll" _
   Alias "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long
#End If

Ce n'est malheureusement pas tout : on utilise une structure OPENFILENAME qui doit également être adaptée.
Cette structure peut être trouvée également dans le fichier Win32API_PtrSafe.txt :

Déclaration de la structure OPENFILENAME 64 bits
Sélectionnez
Type OPENFILENAME
        lStructSize As Long
        hwndOwner As LongPtr
        hInstance As LongPtr
        lpstrFilter As String
        lpstrCustomFilter As String
        nMaxCustFilter As Long
        nFilterIndex As Long
        lpstrFile As String
        nMaxFile As Long
        lpstrFileTitle As String
        nMaxFileTitle As Long
        lpstrInitialDir As String
        lpstrTitle As String
        flags As Long
        nFileOffset As Integer
        nFileExtension As Integer
        lpstrDefExt As String
        lCustData As LongPtr
        lpfnHook As LongPtr
        lpTemplateName As String
'#if (_WIN32_WINNT >= 0x0500)
        pvReserved As LongPtr
        dwReserved As Long
        FlagsEx As Long
'#endif // (_WIN32_WINNT >= 0x0500)
End Type

C'est ici que ça se complique!
D'abord, que signifie _WIN32_WINNT ?
C'est une constante de compilation, mais en C++; d'ailleurs la ligne est commentée.
Cela signifie que les trois derniers membres de la structure ne sont valables que pour une certaine version de Windows.
Comme nous n'utilisons pas ces trois derniers membres, le plus sûr est de ne pas les inclure dans la structure.
On pourra éventuellement les laisser et les commenter.

Ensuite on voit que certains membres de la structure sont typés en LongPtr.
Il faut agir à ce niveau.
On ne peut pas laisser un LongPtr qui ne compilerait pas sur une version antérieure à 2010.
Ici encore, utilisons la constante de compilation VBA7.

Déclaration du type OPENFILENAME pour les versions 32 et 64 bits
Sélectionnez
Private Type OPENFILENAME
        lStructSize As Long
#If VBA7 Then
        hwndOwner As LongPtr
        hInstance As LongPtr
#Else
        hwndOwner As Long
        hInstance As Long
#End If
        lpstrFilter As String
        lpstrCustomFilter As String
        nMaxCustFilter As Long
        nFilterIndex As Long
        lpstrFile As String
        nMaxFile As Long
        lpstrFileTitle As String
        nMaxFileTitle As Long
        lpstrInitialDir As String
        lpstrTitle As String
        flags As Long
        nFileOffset As Integer
        nFileExtension As Integer
        lpstrDefExt As String
#If VBA7 Then
        lCustData As LongPtr
        lpfnHook As LongPtr
#Else
        lCustData As Long
        lpfnHook As Long
#End If
        lpTemplateName As String
'#if (_WIN32_WINNT >= 0x0500)
        'pvReserved As LongPtr
        'dwReserved As Long
        'FlagsEx As Long
'#endif // (_WIN32_WINNT >= 0x0500)
End Type

Seuls les membres typés LongPtr en 64 bits changent selon les versions.
J'ai choisi de ne dupliquer que ces membres ; on aurait également pu dupliquer toute la structure.

Il nous reste encore un peu de travail.
En effet, avec ces déclarations, le code ne compile toujours pas en 64 bits.
Pourquoi ? Tout simplement parce que les types de données utilisés par la fonction OuvrirUnFichier ne sont pas corrects.
Handle est déclaré en Long alors que la version 64 bits du membre hwndOwner de la structure OPENFILENAME attend un LongPtr.

Il faut également modifier le type de données des variables utilisées pour les appels d'API.
Adaptez la fonction OuvrirUnFichier pour accepter un LongPtr en paramètre :

Déclaration de OuvrirUnFichier pour 32 bits et 64 Bits
Sélectionnez
#If VBA7 Then
Public Function OuvrirUnFichier(Handle As LongPtr, _
                                Titre As String, _
                                TypeRetour As Byte, _
                                Optional TitreFiltre As String, _
                                Optional TypeFichier As String, _
                                Optional RepParDefaut As String) As String
#Else
Public Function OuvrirUnFichier(Handle As Long, _
                                Titre As String, _
                                TypeRetour As Byte, _
                                Optional TitreFiltre As String, _
                                Optional TypeFichier As String, _
                                Optional RepParDefaut As String) As String
#End If

Et voilà ! Enfin presque…

Testez la fonction avec ce code :

Test de la fonction
Sélectionnez
Public Function TestDeLaFonction()
MsgBox OuvrirUnFichier(Application.hWndAccessApp, "Test", 1)
End Function

Et ça ne fonctionne pas !

Il faut faire attention à une dernière chose :
il est important d'utiliser l'instruction LenB pour calculer la taille d'une structure 64 bits.
LenB fonctionne également dans ce cas pour les versions 32 bits.
Remplacez donc .lStructSize = Len(StructFile) par .lStructSize = LenB(StructFile).

Il faut parfois utiliser Len pour 32 bits et LenB pour 64 bits.

Et voici le code complet, qui fonctionne sur toutes les versions d'Office.

Code complet pour 32 bits et 64 bits
Sélectionnez
Option Explicit
            
'Déclaration de l'API
#If VBA7 Then
Private Declare PtrSafe Sub PathStripPath Lib "shlwapi.dll" _
   Alias "PathStripPathA" (ByVal pszPath As String)
Private Declare PtrSafe Function GetOpenFileName Lib "comdlg32.dll" _
   Alias "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long
#Else
Private Declare Sub PathStripPath Lib "shlwapi.dll" _
   Alias "PathStripPathA" (ByVal pszPath As String)
Private Declare Function GetOpenFileName Lib "comdlg32.dll" _
   Alias "GetOpenFileNameA" (pOpenfilename As OPENFILENAME) As Long
#End If
Private Type OPENFILENAME
        lStructSize As Long
#If VBA7 Then
        hwndOwner As LongPtr
        hInstance As LongPtr
#Else
        hwndOwner As Long
        hInstance As Long
#End If
        lpstrFilter As String
        lpstrCustomFilter As String
        nMaxCustFilter As Long
        nFilterIndex As Long
        lpstrFile As String
        nMaxFile As Long
        lpstrFileTitle As String
        nMaxFileTitle As Long
        lpstrInitialDir As String
        lpstrTitle As String
        flags As Long
        nFileOffset As Integer
        nFileExtension As Integer
        lpstrDefExt As String
#If VBA7 Then
        lCustData As LongPtr
        lpfnHook As LongPtr
#Else
        lCustData As Long
        lpfnHook As Long
#End If
        lpTemplateName As String
'#if (_WIN32_WINNT >= 0x0500)
        'pvReserved As LongPtr
        'dwReserved As Long
        'FlagsEx As Long
'#endif // (_WIN32_WINNT >= 0x0500)
End Type
            
            
 'Constantes
Private Const OFN_HIDEREADONLY = &H4
            
#If VBA7 Then
Public Function OuvrirUnFichier(Handle As LongPtr, _
                                Titre As String, _
                                TypeRetour As Byte, _
                                Optional TitreFiltre As String, _
                                Optional TypeFichier As String, _
                                Optional RepParDefaut As String) As String
#Else
Public Function OuvrirUnFichier(Handle As Long, _
                                Titre As String, _
                                TypeRetour As Byte, _
                                Optional TitreFiltre As String, _
                                Optional TypeFichier As String, _
                                Optional RepParDefaut As String) As String
#End If
            
Dim StructFile As OPENFILENAME
Dim sFiltre As String
            
 'Construction du filtre en fonction des arguments spécifiés
If Len(TitreFiltre) > 0 And Len(TypeFichier) > 0 Then
  sFiltre = TitreFiltre & " (" & TypeFichier & ")" & Chr$(0) & "*." & TypeFichier & Chr$(0)
End If
sFiltre = sFiltre & "Tous (*.*)" & Chr$(0) & "*.*" & Chr$(0)
            
 'Configuration de la boîte de dialogue
  With StructFile
    .lStructSize = LenB(StructFile) 'Initialisation de la grosseur de la structure
    .hwndOwner = Handle 'Identification du handle de la fenêtre
    .lpstrFilter = sFiltre 'Application du filtre
    .lpstrFile = String$(254, vbNullChar) 'Initialisation du fichier '0' x 254
    .nMaxFile = 254 'Taille maximale du fichier
    .lpstrFileTitle = String$(254, vbNullChar) 'Initialisation du nom du fichier '0' x 254
    .nMaxFileTitle = 254  'Taille maximale du nom du fichier
    .lpstrTitle = Titre 'Titre de la boîte de dialogue
    .flags = OFN_HIDEREADONLY  'Option de la boite de dialogue
    If ((IsNull(RepParDefaut)) Or (RepParDefaut = "")) Then
        RepParDefaut = CurrentDb.Name
        PathStripPath (RepParDefaut)
        .lpstrInitialDir = Left(CurrentDb.Name, Len(CurrentDb.Name) - Len(Mid$(RepParDefaut, 1, _
InStr(1, RepParDefaut, vbNullChar) - 1)))
        Else: .lpstrInitialDir = RepParDefaut
    End If
  End With
            
If (GetOpenFileName(StructFile)) Then 'Si un fichier est sélectionné
    Select Case TypeRetour
      Case 1: OuvrirUnFichier = Trim$(Left(StructFile.lpstrFile, InStr(1, StructFile.lpstrFile, vbNullChar) - 1))
      Case 2: OuvrirUnFichier = Trim$(Left(StructFile.lpstrFileTitle, InStr(1, StructFile.lpstrFileTitle, vbNullChar) - 1))
    End Select
  End If
            
End Function

IV-B. Quand dois-je utiliser PtrLong ?

Cela ne vous a sans doute pas échappé : dans la structure OPENFILENAME, certains Long deviennentLongPtr en 64 bits et d'autres restent Long.

En effet, certaines valeurs numériques sont des entiers longs, même en 64 bits.

Seuls les pointeurs utilisent un type LongPtr.
Un identifiant de fenêtre (hwnd) est un pointeur, un contexte d'affichage aussi.

Notez qu'en environnement 64 bits, Me.Hwnd est d'ailleurs de type LongPtr.

IV-C. Autre solution aux constantes de compilation ?

Il devient vite pénible de devoir utiliser les constantes de compilation un peu partout dans le code.

Nous allons essayer de limiter leur utilisation aux seules déclarations de procédures et de structures.

Prenons l'exemple de l'API ShellExecute.
Celle-ci permet, par exemple, d'ouvrir un fichier.

En environnement 32 bits, voici son utilisation :

Test de la fonction ShellExecute
Sélectionnez
Option Explicit
            
Const SW_SHOWNORMAL = 1
Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
                (ByVal hwnd As Long, ByVal lpOperation As String, _
                 ByVal lpFile As String, ByVal lpParameters As String, _
                 ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
            
' Fonction qui ouvre le fichier "c:\temp\monfichier.txt",
Public Function TestShellExecute()
Dim lRetour As Long
Dim lParent As Long
lParent = Application.hWndAccessApp
lRetour = ShellExecute(lParent, "open", "c:\temp\monfichier.txt", "", "", SW_SHOWNORMAL)
If lRetour < 32 Then ' Erreur si retour < 32
    MsgBox "Erreur d'ouverture n° " & lRetour
End If
End Function

La déclaration pour un environnement 64 bits est la suivante :

Déclaration de ShellExecute 64 bits
Sélectionnez
Declare PtrSafe Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
   (ByVal hwnd As LongPtr, ByVal lpOperation As String, _
    ByVal lpFile As String, ByVal lpParameters As String, _
    ByVal lpDirectory As String, ByVal nShowCmd As Long) As LongPtr

Nous remarquons que l'identifiant de fenêtre parent (hwnd) et le retour de la fonction sont de type LongPtr.

Il faut donc modifier le type de nos variables lRetour et lParent.

Test de la fonction ShellExecute
Sélectionnez
Option Explicit
            
Const SW_SHOWNORMAL = 1
#If VBA7 Then
Declare PtrSafe Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
                (ByVal hwnd As LongPtr, ByVal lpOperation As String, _
                 ByVal lpFile As String, ByVal lpParameters As String, _
                 ByVal lpDirectory As String, ByVal nShowCmd As Long) As LongPtr
#Else
Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
                (ByVal hwnd As Long, ByVal lpOperation As String, _
                 ByVal lpFile As String, ByVal lpParameters As String, _
                 ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
#End If
            
' Fonction qui ouvre le fichier "c:\temp\monfichier.txt",
Public Function TestShellExecute()
#If VBA7 Then
Dim lRetour As LongPtr
Dim lParent As LongPtr
#Else
Dim lRetour As Long
Dim lParent As Long
#End If
lParent = Application.hWndAccessApp
lRetour = ShellExecute(lParent, "open", "c:\temp\monfichier.txt", "", "", SW_SHOWNORMAL)
If lRetour < 32 Then ' Erreur si retour < 32
    MsgBox "Erreur d'ouverture n° " & lRetour
End If
End Function

Ceci peut vite devenir très lourd si comme moi vous utilisez beaucoup d'API.

Une solution est d'utiliser les instructions DefType.
Ces instructions permettent de définir un type par défaut pour une plage de noms de variables :

  • DefLngPtr définit les variables de type LongPtr ;
  • DefLng définit les variables de type Long.

Par exemple :

Variables LongPtr par défaut pour les variables commençant par Z
Sélectionnez
DefLngPtr Z
Variables LongPtr par défaut pour toutes les variables
Sélectionnez
DefLngPtr A-Z

Ces instructions sont à écrire en en-tête de chaque module car elles ne s'appliquent qu'au module dans lequel on les écrit.
Attention : si vous utilisez ces instructions, pensez à bien typer chacune de vos variables pour éviter qu'elle ne prenne par erreur le type par défaut.

Il suffit d'utiliser les constantes de compilation pour définir le type de variables par défaut.
On peut par exemple décider que toutes les variables commençant par Z seront de type LongPtr ou Long suivant l'environnement.

Nous appellerons nos variables pointeurs zlRetour et zlParent.
Elles seront typées par défaut, il ne faut donc pas préciser leur type lors de leur déclaration.

Utilisation de la fonction ShellExecute compatible 32 bits et 64 bits
Sélectionnez
' Définition du type de données par défaut
'   pour les variables commençant par Z
#If VBA7 Then
DefLngPtr Z
#Else
DefLng Z
#End If
            
' Déclaration de l'API ShellExecute
Const SW_SHOWNORMAL = 1
#If VBA7 Then
Declare PtrSafe Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
                (ByVal hwnd As LongPtr, ByVal lpOperation As String, _
                 ByVal lpFile As String, ByVal lpParameters As String, _
                 ByVal lpDirectory As String, ByVal nShowCmd As Long) As LongPtr
#Else
Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
                (ByVal hwnd As Long, ByVal lpOperation As String, _
                 ByVal lpFile As String, ByVal lpParameters As String, _
                 ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
#End If
            
' Fonction qui ouvre le fichier "c:\temp\monfichier.txt",
Public Function TestShellExecute()
Dim zlRetour
Dim zlParent
zlRetour = Application.hWndAccessApp
zlRetour = ShellExecute(zlParent, "open", "c:\temp\monfichier.txt", "", "", SW_SHOWNORMAL)
If zlRetour < 32 Then
    MsgBox "Erreur d'ouverture n° " & zlRetour
End If
End Function

De cette manière, on évite de mettre des constantes de compilation partout dans nos fonctions.
Ce n'est pas très visible dans l'exemple, mais ça facilite la vie lors de l'utilisation massive d'API.

Remarque :
Sont typés par défaut avec les instructions DefLng et DefLngPtr :

  • les variables ;
  • les paramètres de fonction ;
  • les retours des fonctions.

Les membres des structures doivent être explicitement typés.

V. Conclusion

Mis à part les appels aux API, la migration de code VBA vers la version 2010 64 bits ne devrait pas être très problématique.

Par contre, on trouve beaucoup de code utilisant ces API, souvent copié d'internet sans trop comprendre.
La migration risque, dans ce cas, d'être parfois douloureuse pour un développeur VBA peu expérimenté.
La seule notion de pointeur n'est pas maîtrisée par tous, et quand bien même vous maîtrisez ce domaine, cela n'est pas toujours très simple.

Heureusement, pour l'instant, la version 64 bits d'Office est très peu utilisée.

Si vous avez le choix, suivez le conseil de Microsoft et installez la version 32 bits.

Merci à blade159 et jacques_jean pour leur relecture orthographique.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

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 © 2010 Thierry GASPERMENT. Aucune reproduction, même partielle, ne peut être faite de ce site ni 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.