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

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Macros et VBA Excel Discussion :

Windows API ByRef lParam As Any vs lParam As Any


Sujet :

Macros et VBA Excel

  1. #1
    Membre éclairé
    Homme Profil pro
    Ingénieur aéronautique
    Inscrit en
    Octobre 2018
    Messages
    216
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur aéronautique

    Informations forums :
    Inscription : Octobre 2018
    Messages : 216
    Par défaut Windows API ByRef lParam As Any vs lParam As Any
    Bonjour,

    J'utilise énormément de déclarations de l'API Windows, mais je me rends compte que j'ai négligé quelques aspects.

    Y-a-t'il notamment une différence entre les déclarations suivantes :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    'https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessage
    Public Declare PtrSafe Function SendMessage Lib "user32.dll" Alias "SendMessageA" ( _
        ByVal hwnd As LongPtr, _
        ByVal wMsg As Long, _
        ByVal wParam As Long, _
              lParam As Any) As Long
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Public Declare Function PtrSafe SendMessage Lib "user32.dll" Alias "SendMessageA" ( _
        ByVal hwnd As LongPtr, _
        ByVal wMsg As Long, _
        ByVal wParam As Long, _
        ByRef lParam As Any) As Long

    J'imagine que dans la deuxième déclarations, il faille que je précise lParam:=ByVal value lors des appels, mais que ce n'est pas forcément nécessaire avec la première déclaration ?

    Pouvez-vous s'il vous plaît m'en dire davantage à ce sujet, car des suspecte des p'tits bugs potentiels dans mes scripts ?

    Merci par avance

  2. #2
    Expert confirmé
    Avatar de Arkham46
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    5 865
    Détails du profil
    Informations personnelles :
    Localisation : France, Loiret (Centre)

    Informations forums :
    Inscription : Septembre 2003
    Messages : 5 865
    Par défaut
    Bonjour,

    Les arguments des API sont par référence par défaut.
    Il n'y a donc pas de différence.

    Par contre :
    - wParam est un LongPtr
    - la fonction SendMessage renvoie un LongPtr

    La déclaration correcte pour 64bits est :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As LongPtr, ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr
    Pour le lParam, son contenu varie en fonction du message envoyé.
    C'est pour cela qu'il est ByRef as Any.
    Avec cette déclaration on peut envoyer ce qu'on veut : une variable par référence, un pointeur par valeur...

  3. #3
    Membre éclairé
    Homme Profil pro
    Ingénieur aéronautique
    Inscrit en
    Octobre 2018
    Messages
    216
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur aéronautique

    Informations forums :
    Inscription : Octobre 2018
    Messages : 216
    Par défaut
    Merci pour ces précisions , il y a vrai que j'utilise mon script sur des versions de VBA 6 / 7 en 32 et 64 bits, donc pour dire...

    Je vrais donc revoir les différentes déclarations, car c'est sûr que ça n'aide pas à éviter d'éventuels bugs... En fait, je me sers de cet outils https://www.rondebruin.nl/win/dennis...sapiviewer.htm qui en réfère bon nombre sans pour autant être exhaustif. Etrangement, la déraction de SendMessage est bien cette que tu mentionnes, mais concernant PostMessage (que je n'utilise pas vraiment d'ailleurs), il resort :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Declare PtrSafe Function PostMessage Lib "user32" Alias "PostMessageA" ( _
        ByVal hwnd As LongPtr, _
        ByVal wMsg As Long, _
        ByVal wParam As LongPtr, _
        ByVal lParam As LongPtr) As Long
    Du coup, j'émets des doutes quand à la crédébilité de cet outil. Je sais qu'il y a aussi des tables comme https://www.codingdomain.com/visualb...api/datatypes/ qui listes les équivalences, mais je ne n'en ai pas trouvé pour du 64 bits...

  4. #4
    Expert confirmé
    Avatar de Arkham46
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    5 865
    Détails du profil
    Informations personnelles :
    Localisation : France, Loiret (Centre)

    Informations forums :
    Inscription : Septembre 2003
    Messages : 5 865
    Par défaut
    La seule source fiable est celle de microsoft :
    https://www.microsoft.com/en-us/down...s.aspx?id=9970

    Il n'y a pas tout mais c'est déjà beaucoup.

    Sinon mettre lParam en ByVal LongPtr n'est pas une erreur.
    C'est juste incohérent si SendMessage n'est pas présenté de manière identique.

    SendMessage et PostMessage sont identiques sur la forme (le premier est synchrone, le deuxième asynchrone).

  5. #5
    Membre éclairé
    Homme Profil pro
    Ingénieur aéronautique
    Inscrit en
    Octobre 2018
    Messages
    216
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur aéronautique

    Informations forums :
    Inscription : Octobre 2018
    Messages : 216
    Par défaut
    Il me semble avoir déjà entrevu cette resource, sans trop y prêter attention . Je vais donc me baser sur cette resource.

    Pour infos, j'avais aussi rencontré des problèmes pour contrôller le menu et accèder aux tooltips d'une application externe. J'y suis quelque peu arrivé avec UI Automation, mais il subsiste des problèmes. Je reprends donc espoir ...

  6. #6
    Membre éclairé
    Homme Profil pro
    Ingénieur aéronautique
    Inscrit en
    Octobre 2018
    Messages
    216
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur aéronautique

    Informations forums :
    Inscription : Octobre 2018
    Messages : 216
    Par défaut
    Dans le cas où par exemple SendMessage renvoit une valeur telle que la longueur d'un buffer ou le nombre d'éléments d'une list box, est-ce que je peux déclarer lRet comme Long voire même Integer (voire example ci-dessous) ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    '
    Public Property Get count() As Integer
     
        If Not (className = "ListBox" Or className = "ComboBox") Then GoTo ERROR_HANDLE
     
        Select Case className
        Case "ListBox": count = API.SendMessage(hwnd:=pHwnd, wMsg:=API.LB_GETCOUNT, wParam:=0&, lParam:=0&)
        Case "ComboBox": count = API.SendMessage(hwnd:=pHwnd, wMsg:=API.CB_GETCOUNT, wParam:=0&, lParam:=0&)
        End Select
     
        Exit Property
     
    ERROR_HANDLE:
     
    End Property
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    '
    Public Property Get className() As String
     
        className = VBA.Space$(255)
     
        Dim lRet As Long: lRet = API.GetClassName(hwnd:=pHwnd, lpClassName:=className, nMaxCount:=Len(className))
     
        className = VBA.left$(className, lRet)
     
    End Property
    Merci encore

  7. #7
    Expert confirmé
    Avatar de Arkham46
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    5 865
    Détails du profil
    Informations personnelles :
    Localisation : France, Loiret (Centre)

    Informations forums :
    Inscription : Septembre 2003
    Messages : 5 865
    Par défaut
    GetClassName retourne un long, donc pas de problème sur celle-là.

    SendMessage doit être déclaré avec un retour un longPtr, sinon ça risque de crasher si le retour est trop grand.
    Ce que tu fais ensuite, c'est comme tu veux, il y aura une conversion implicite pour alimenter la variable si elle n'est pas en longPtr.
    Il pourrait y avoir une erreur d'overflow, mais c'est peu probable dans ce cas sauf si tu as des listes énormes. En tout cas on peut capturer.

  8. #8
    Membre éclairé
    Homme Profil pro
    Ingénieur aéronautique
    Inscrit en
    Octobre 2018
    Messages
    216
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur aéronautique

    Informations forums :
    Inscription : Octobre 2018
    Messages : 216
    Par défaut
    Effectivement, className n'était pas un bon exemple...

    Par exemple, j'ai la méthode suivante qui me permet de rafraichir (dans le sens où je modélise chaque fenêtre par une classe clsWindow, mais il faut que je "force" le rafraichissement), mais le contenu d'une list box ou combo box. Ça m'a l'ai de passer, mais je dois pousser les tests.

    Code : 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
    '
    Public Sub refresh()
     
        Set pItems = New Collection
     
        If Not (className = "ListBox" Or className = "ComboBox") Then GoTo ERROR_HANDLE
     
        'loops through items
        Dim index As Integer: For index = 0 To count - 1
     
            Dim strBuffer As String: strBuffer = VBA.Space$(1024)
     
    #If VBA7 Then
            Dim strLength As LongPtr
            Dim lRet As LongPtr
    #Else
            Dim strLength As Long
            Dim lRet As Long
    #End If
     
            Select Case className
            Case "ListBox":
                strLength = API.SendMessage(hwnd:=pHwnd, wMsg:=API.LB_GETTEXTLEN, wParam:=index, lParam:=0&)
     
                lRet = API.SendMessage(hwnd:=pHwnd, wMsg:=API.LB_GETTEXT, wParam:=index, lParam:=ByVal strBuffer)
            Case "ComboBox":
                strLength = API.SendMessage(hwnd:=pHwnd, wMsg:=API.CB_GETLBTEXTLEN, wParam:=index, lParam:=0&)
     
                lRet = API.SendMessage(hwnd:=pHwnd, wMsg:=API.CB_GETLBTEXT, wParam:=index, lParam:=ByVal strBuffer)
            End Select
     
            pItems.add item:=VBA.left$(strBuffer, VBA.CLng(strLength))
     
        Next index
     
        Exit Sub
     
    ERROR_HANDLE:
     
    End Sub
    Au fait, est-ce possible que des problèmes de typages peuvent expliquer des Runtime Exception (Click OK to terminate), car j'en ai pas mal...

    Ps : j'avais bien entendu envisager de déclarer conditionnellement lRet comme membre privé de la classe, mais je préfère finalement les déclarer individuellement dans chaque propriété et méthodes vu que c'est tantôt du Long, tantôt du LongPtr. Bon, je sais que ce n'est pas toujours nécessaire de traiter lRet, mais je préfère quand même y avoir accès pour déboggage.

    C'est quand même un sacré chantier tout ça, j'espère que MFC me desservira par la suite

  9. #9
    Expert confirmé
    Avatar de Arkham46
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    5 865
    Détails du profil
    Informations personnelles :
    Localisation : France, Loiret (Centre)

    Informations forums :
    Inscription : Septembre 2003
    Messages : 5 865
    Par défaut
    Pour éviter de faire des déclarations conditionnelles, j'utilise DefLngPtr :
    https://arkham46.developpez.com/arti...a64bits/#LIV-C

    Comme je type toutes les variables, même variante, j'utilise :
    Comme ça, tout ce qui n'est pas typé est long ou longPtr en fonction du système.

    Et oui un mauvais typage peut provoquer des runtime error.
    En fait ça travaille avec des pointeurs, vu que ce sont des fonctions externes, elles n'ont pas connaissances des types de variables vba.
    Si la fonction écrit sur 8 octets (longPtr) et que tu utilises un long (de 4 octets) dans la déclaration de l'API, alors la fonction écrira 8 octets à l'emplacement de ta variable et écrasera donc 4 octets potentiellement utilisés pour autre chose.
    Si L'API est bien déclarée, il y a juste un risque lors de conversion implicites. Mais ça se passe dans le vba donc erreurs gérables.

  10. #10
    Membre éclairé
    Homme Profil pro
    Ingénieur aéronautique
    Inscrit en
    Octobre 2018
    Messages
    216
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur aéronautique

    Informations forums :
    Inscription : Octobre 2018
    Messages : 216
    Par défaut
    Merci pour cette astuce, j'avais déjà rencontré ce genre d'instruction, sans vraiment y avoir prété attention . Du coup, ça revient à reprendre (ou à étendre) la notation hongroise classiquement utilisée par l'API Windows.

    Pour les run-time exception (presque qu'exclusivement sous CATIA), il m'arrive d'en avoir si je travaille en parallèle sur un autre logiciel pendant quelques minutes et que je revienne sur le formulaire sans avoir y avoir d'ailleurs déclenché de nouveaux événements. J'imagine qu'il devait donc y avoir un pointeur ou autre variable LongLong résiduel qui polluait en quelque sorte la mémoire et dont les conséquences ne se faisaient ressentir qu'à l'activation de CATIA (ne serait qu'en survolant CATIA avec la souris). Des fois, j'avais un crash systématique, sans run-time exception, comme c'est le cas avec https://www.developpez.net/forums/d2...n-vs-vba-lenb/.

    Bon, je vais continuer à peaufiner méticuleusement mes déclarations dans l'espoir de ne plus rencontrer de run-time exception .

Discussions similaires

  1. Manipulation du registre windows [APIs]
    Par developersystem dans le forum Windows
    Réponses: 2
    Dernier message: 28/08/2007, 01h19
  2. [VBscript] Comment utiliser les windows API en vbs
    Par daniel_gre dans le forum VBScript
    Réponses: 3
    Dernier message: 21/05/2007, 18h14
  3. Windows API
    Par couet dans le forum Windows
    Réponses: 4
    Dernier message: 15/05/2006, 13h31
  4. Windows API
    Par datax dans le forum Windows
    Réponses: 1
    Dernier message: 22/08/2005, 10h58
  5. [Windows]Api win32 pour java
    Par cpanette dans le forum API standards et tierces
    Réponses: 1
    Dernier message: 27/06/2005, 15h06

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo