Partager via


Règles d’utilisation de pointeurs

Le portage de votre code à compiler pour Microsoft Windows 32 et 64 bits est simple. Vous n’avez besoin que de suivre quelques règles simples concernant le cast des pointeurs et d’utiliser les nouveaux types de données dans votre code. Les règles de manipulation de pointeur sont les suivantes.

  1. Ne castez pas les pointeurs en int, long, ULONG ou DWORD.

    Si vous devez convertir un pointeur pour tester certains bits, définir ou effacer des bits, ou manipuler son contenu, utilisez le type UINT_PTR ou INT_PTR . Ces types sont des types intégraux qui sont mis à l’échelle à la taille d’un pointeur pour windows 32 et 64 bits (par exemple, ULONG pour Windows 32 bits et _int64 pour Windows 64 bits). Par exemple, supposons que vous portiez le code suivant :

    ImageBase = (PVOID)((ULONG)ImageBase | 1);

    Dans le cadre du processus de portage, vous devez modifier le code comme suit :

    ImageBase = (PVOID)((ULONG_PTR)ImageBase | 1);

    Utilisez UINT_PTR et INT_PTR le cas échéant (et si vous ne savez pas s’ils sont nécessaires, il n’y a aucun mal à les utiliser au cas où). Ne convertissez pas vos pointeurs en types ULONG, LONG, INT, UINT ou DWORD.

    Notez que HANDLE est défini comme un void*, donc la conversion d’une valeur HANDLE sur une valeur ULONG pour tester, définir ou effacer les 2 bits d’ordre inférieur est une erreur sur Windows 64 bits.

  2. Utilisez la fonction PtrToLong ou PtrToUlong pour tronquer les pointeurs.

    Si vous devez tronquer un pointeur vers une valeur 32 bits, utilisez la fonction PtrToLong ou PtrToUlong (définie dans Basetsd.h). Ces fonctions désactivent l’avertissement de troncation du pointeur pendant la durée de l’appel.

    Utilisez ces fonctions avec soin. Après avoir converti une variable pointeur à l’aide de l’une de ces fonctions, ne l’utilisez plus comme pointeur. Ces fonctions tronquent les 32 bits supérieurs d’une adresse, qui sont généralement nécessaires pour accéder à la mémoire référencée à l’origine par le pointeur. L’utilisation de ces fonctions sans examen minutieux entraîne la fragilité du code.

  3. Soyez prudent lorsque vous utilisez des valeurs POINTER_32 dans du code qui peuvent être compilées en tant que code 64 bits. Le compilateur signe-étend le pointeur lorsqu’il est affecté à un pointeur natif dans du code 64 bits, et non à zéro extension du pointeur.

  4. Soyez prudent lorsque vous utilisez des valeurs POINTER_64 dans du code qui peuvent être compilées en tant que code 32 bits. Le compilateur signera et étendra le pointeur dans du code 32 bits, et non pas étendre le pointeur à zéro.

  5. Faites attention à l’utilisation des paramètres OUT.

    Par exemple, supposons que vous ayez une fonction définie comme suit :

    void func( OUT PULONG *PointerToUlong );

    N’appelez pas cette fonction comme suit.

    ULONG ul;
    PULONG lp;
    func((PULONG *)&ul);
    lp = (PULONG)ul;
    

    Utilisez plutôt l’appel suivant.

    PULONG lp;
    func(&lp);
    

    La conversion de &type ul sur PULONG* empêche une erreur du compilateur, mais la fonction écrit une valeur de pointeur 64 bits dans la mémoire au niveau &ul. Ce code fonctionne sur Windows 32 bits, mais entraîne une altération des données sur Windows 64 bits et une altération subtile et difficile à trouver. L’essentiel : ne jouez pas de tours avec le code C. Il est préférable de le faire de manière simple et simple.

  6. Soyez prudent avec les interfaces polymorphes.

    Ne créez pas de fonctions qui acceptent les paramètres DWORD pour les données polymorphes. Si les données peuvent être un pointeur ou une valeur intégrale, utilisez le type UINT_PTR ou PVOID .

    Par exemple, ne créez pas de fonction qui accepte un tableau de paramètres d’exception tapés en tant que valeurs DWORD . Le tableau doit être un tableau de valeurs DWORD_PTR . Par conséquent, les éléments de tableau peuvent contenir des adresses ou des valeurs intégrales 32 bits. (La règle générale est que si le type d’origine est DWORD et qu’il doit être une largeur de pointeur, convertissez-le en valeur DWORD_PTR . C’est pourquoi il existe des types de précision de pointeur correspondants.) Si vous avez du code qui utilise DWORD, ULONG ou d’autres types 32 bits de manière polymorphe (autrement dit, vous souhaitez vraiment que le paramètre ou le membre de structure contienne une adresse), utilisez UINT_PTR à la place du type actuel.

  7. Utilisez les nouvelles fonctions de classe de fenêtre.

    Si vous avez des données privées de fenêtre ou de classe qui contiennent des pointeurs, votre code doit utiliser les nouvelles fonctions suivantes :

    Ces fonctions peuvent être utilisées sur Windows 32 et 64 bits, mais elles sont requises sur Windows 64 bits. Préparez la transition en utilisant ces fonctions maintenant.

    En outre, vous devez accéder aux pointeurs ou aux handles dans les données privées de classe à l’aide des nouvelles fonctions sur Windows 64 bits. Pour vous aider à trouver ces cas, les index suivants ne sont pas définis dans Winuser.h lors d’une compilation 64 bits :

    • GWL_WNDPROC
    • GWL_HINSTANCE
    • GWL_HWNDPARENT
    • GWL_USERDATA

    Au lieu de cela, Winuser.h définit les nouveaux index suivants :

    • GWLP_WNDPROC
    • GWLP_HINSTANCE
    • GWLP_HWNDPARENT
    • GWLP_USERDATA
    • GWLP_ID

    Par exemple, le code suivant ne se compile pas :

    SetWindowLong(hWnd, GWL_WNDPROC, (LONG)MyWndProc);

    Il doit être modifié comme suit :

    SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);

    Lorsque vous définissez le membre cbWndExtra de la structure WNDCLASS , veillez à réserver suffisamment d’espace pour les pointeurs. Par exemple, si vous réservez actuellement des octets sizeof(DWORD) pour une valeur de pointeur, réservez sizeof(DWORD_PTR) octets.

  8. Accédez à toutes les données de fenêtre et de classe à l’aide de FIELD_OFFSET.

    Il est courant d’accéder aux données de fenêtre à l’aide de décalages codés en dur. Cette technique n’est pas portable sur Windows 64 bits. Pour rendre votre code portable, accédez à vos données de fenêtre et de classe à l’aide de la macro FIELD_OFFSET . Ne partez pas du principe que le deuxième pointeur a un décalage de 4.

  9. Les types LPARAM, WPARAM et LRESULT changent de taille avec la plateforme.

    Lors de la compilation de code 64 bits, ces types sont étendus à 64 bits, car ils contiennent généralement des pointeurs ou des types intégraux. Ne mélangez pas ces valeurs avec des valeurs DWORD, ULONG, UINT, INT, int ou long . Examinez comment vous utilisez ces types et assurez-vous de ne pas tronquer par inadvertance les valeurs.