Développer avec Office 64 bits

Thierry GASPERMENT

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 ?

Commentez

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

- Titre : Développer avec Office 64 bits

- Auteur : Thierry GASPERMENT

- Parution : 6 septembre 2010

- Mise à jour : 23 décembre 2019

- 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 ® 2010 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. Quelle version dois-je installer ? 32 ou 64 bits ?

- I-A. Avantages de la version 64 bits

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

- I-C. Que choisir ?

- II. Modification du modèle d'objets

- III. Cibler le code en fonction de la version

- IV. Les API et Office 64 bits

- IV-A. Exemple GetOpenFileName

- IV-B. Quand dois-je utiliser PtrLong ?

- IV-C. Autre solution aux constantes de compilation ?

- V. Conclusion

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 :

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

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 :

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 ;

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; …

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) :

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

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

Microsoft Office Code Compatibility Inspector

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

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

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

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

#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

'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

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

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

'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

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

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

#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

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

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

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

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

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

DefLngPtr Z

Variables LongPtr par défaut pour toutes les variables

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

' 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.