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

C++ Discussion :

Crash sur un redim VBA et un appel COM C++


Sujet :

C++

  1. #1
    Membre régulier
    Inscrit en
    Avril 2013
    Messages
    93
    Détails du profil
    Informations forums :
    Inscription : Avril 2013
    Messages : 93
    Points : 77
    Points
    77
    Par défaut Crash sur un redim VBA et un appel COM C++
    J'ai un problème lorsque je dimensionne un tableau dans une structure en VBA puis que j'envoi la structure à une fonction C++. Ça plante direct et impossible de voir quoique ce soit.

    Code C++
    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
    typedef struct XData
    {
    	int Count;
    	SAFEARRAY *X;
    	SAFEARRAY *Y;
    } 	XData;
    typedef struct T
    {
    	BSTR s;
    	XData X;
    } T;
    typedef struct Input
    {
    	SAFEARRAY *X;
    } Input;
     
    __declspec(dllexport) int __stdcall  Cpp_D1(Input *in)
    {
    	return 0;
    }
    code VBA
    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
    Private Declare PtrSafe Function Cpp_D1 Lib "MyDLL.dll" (i As InputData) As Long
     
    Private Type XData
        Count As Long
        X() As Double
    End Type
     
    Private Type T
    	s as string
        X As XData
    End Type
     
    Private Type InputData
        X() As T
    End Type
     
    Sub test()
        Dim i As InputData
        ReDim i.X(5)
        ReDim i.X(0).X.X(10)
        Call Cpp_D1(i)
    End Sub
    Si je ne mets pas le string ça marche.
    Si je ne redimensionne pas i.X(0).X.X(10) ça marche.

    Je suis bloqué sur ce problème depuis plusieurs jours et je ne peux pas utiliser un autre schéma de déclaration de structure (je pense à la déclaration dans le .idl qui marche bien) car je dois avoir une compatibilité sous OS X.

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 170
    Points : 12 291
    Points
    12 291
    Par défaut
    Heu, je ne comprends toujours pas pourquoi vous ne voulez pas utiliser l'idl.
    Faut juste vérifier que le code généré par midl soit portable ou le rendre portable, non ?

  3. #3
    Membre régulier
    Inscrit en
    Avril 2013
    Messages
    93
    Détails du profil
    Informations forums :
    Inscription : Avril 2013
    Messages : 93
    Points : 77
    Points
    77
    Par défaut
    Le problème de la portabilité et du côté du vba. Quand je veux utiliser les structures définis côté c++ sur OS X, je reçois un joli message vba "This functionnality is not yet implemented". Du coup, je suis bloqué de ce côté là sous MAC.
    Je veux donc utiliser la double déclaration de la structure (juste pour MAC) car ça marche. Je l'utilise déjà pour des petites structures sans string.

  4. #4
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 381
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 381
    Points : 41 582
    Points
    41 582
    Par défaut
    Euh, pourquoi manque-t-il la moitié de ta structure dans la version VBA?

    Surtout pour une structure que tu mets ensuite dans un tableau...

  5. #5
    Membre régulier
    Inscrit en
    Avril 2013
    Messages
    93
    Détails du profil
    Informations forums :
    Inscription : Avril 2013
    Messages : 93
    Points : 77
    Points
    77
    Par défaut
    Oups, mauvais copier coller. Désolé. Je corrige ça!
    Pour le cas qui ne marche pas ça n'a pas d'impact car je ne descend pas chercher cette structure côté c++.

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 381
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 381
    Points : 41 582
    Points
    41 582
    Par défaut
    Hum...
    Est-ce qu'un truc comme ceci marcherait?
    Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Sub SetXDataDimX(ByRef xd as XData, ByVal dimx as Long)
    	ReDim xd.X(dimx)
    End Sub
     
     
    Sub test()
        Dim i As InputData
        ReDim i.X(5)
        SetXDataDimX i.X(0).X, 10
        Call Cpp_D1(i)
    End Sub
    Sinon, il va falloir faire le truc manuellement: Insérer les éléments déjà dimensionnés plutôt que tenter de redimensionner "sur place":
    Code VBA : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Sub test()
    	Dim i As InputData
    	ReDim i.X(5)
     
    	Dim xd As XData
    	ReDim xd.X(10)
    	i.X(0).X = xd
     
    	Call Cpp_D1(i)
    End Sub

    PS: Tes accès aux variables sont bien difficiles à lire, tu devrais donner des noms différents à tes variables:
    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
    Private Type XData
    	Count As Long
    	X() As Double
    	Y() As Double
    End Type
     
    Private Type T
    	s as string
    	xd As XData
    End Type
     
    Private Type InputData
    	tArray() As T
    End Type

  7. #7
    Membre régulier
    Inscrit en
    Avril 2013
    Messages
    93
    Détails du profil
    Informations forums :
    Inscription : Avril 2013
    Messages : 93
    Points : 77
    Points
    77
    Par défaut
    Ça continue de planter systématiquement.
    J'ai eu une info en faisant le test sous MAC, j'ai une erreur "Internal error (Error 51)" avant le crash.
    Sur la doc officiel, ils disent juste ça: "An internal malfunction has occurred in Visual Basic. Unless this call was generated by the Error statement or Raise method, contact Microsoft Product Support Services to report the conditions under which the message appeared."

    Peut-être un bug dans le VBA...

    P.S:
    Oui je suis d'accord pour les noms de variable, j'ai juste fait ce cas pour reproduire le problème qui est dans un contexte beaucoup plus gros.

  8. #8
    Membre régulier
    Inscrit en
    Avril 2013
    Messages
    93
    Détails du profil
    Informations forums :
    Inscription : Avril 2013
    Messages : 93
    Points : 77
    Points
    77
    Par défaut
    J'ai réussi à réduire un peu le problème à ça:
    C++
    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
    __declspec(align(4)) typedef struct XData
    {
    	LPSAFEARRAY X;
    	int Count;
    }   XData;
     
    __declspec(align(4)) typedef struct T
    {
    	BSTR s;
    	XData X;
    } T;
     
    __declspec(dllexport) int __stdcall  Cpp_D2(T *p)
    {
    	double *x = NULL;
    	HRESULT hr = SafeArrayAccessData(p->X.X, reinterpret_cast<void**>(&x));
    	SafeArrayUnaccessData(p->X.X);
    	return 0;
    }
    VBA
    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
    Private Declare PtrSafe Function Cpp_D2 Lib "MyDLL.dll" (i As T) As Long
     
    Private Type XData
        X() As Double
        Count As Long
    End Type
     
    Private Type T
        s As String
        data_T As XData
    End Type
     
     
    Sub tt22t()
        Dim obj As T
        ReDim i.data_T.X(5)
        i.s = "ok"
        Call Cpp_D2(obj)
    End Sub

    J'ai bien vérifier l'alignement mémoire des données et il n'y a aucun soucis la dessus.
    Quand je compile ma dll en release, ça plante. En debug, ça ne plante pas mais toutes les valeurs de mon tableau de double sur lequel j'ai récupéré le pointeur passe à "application-defined or object-defined error" quand je regarde ensuite en VBA.
    En debug, je lit bien la valeur du string et les éventuels valeurs de mon tableau X si j'en ai mis en VBA.

    Je suis perdu

  9. #9
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 170
    Points : 12 291
    Points
    12 291
    Par défaut
    je reçois un joli message vba "This functionnality is not yet implemented".
    Flute, un niveau fonctionnel dégradé de COM sous MAC, comme c'est étonnant.
    Comme ça fait 20 ans que COM est sorti, dire d'attendre que le niveau fonctionnel de COM sur MAC rattrape son retard, ça serait un peu du foutage de gueule.
    Mais comme cela doit être une limitation documentée, on devrait pouvoir trouver les workarounds inventés à cette époque, non ?
    J'ai pas de MAC sous la main pour caractériser le manque dans COM sous MAC.

    Que le comportement soit différent entre la version DEBUG et RELEASE su C++, ça m'étonne pas beaucoup, car le layout d'une structure peut largement changer entre DEBUG et RELEASE (alignement, packing, taille des champs, etc...).
    C'est déjà super casse-gueule entre du C++ compiler avec des options différentes (pas d'ABI standardisée en C++), alors avec un langage et un Runtime différent, c'est même plus du casse-gueule, c'est du quasi automatique.

    Comme on n'a beaucoup moins de visibilité et de contrôle sur le layout en VB, je pense qu'il faut adapter la définition des structures en C++ pour qu'elles correspondent à celles de VB.

    Donc, moi, je ferais le code en VB et j'inférerais la structure en fonction du layout de VB.
    Elle ressemble à quoi en mémoire ces structures VB ?

  10. #10
    Membre régulier
    Inscrit en
    Avril 2013
    Messages
    93
    Détails du profil
    Informations forums :
    Inscription : Avril 2013
    Messages : 93
    Points : 77
    Points
    77
    Par défaut
    Oui c'est clairement pas une surprise pour MAC mais le problème reste sur PC
    Je pense que le problème levé sur MAC est une conséquence du problème vu sous PC. Le résoudre sous PC devrai suffire!

    Le problème c'est que d'un point de vue mémoire, tout est parfait!
    J'ai comparé l'adresse de chaque variable contenue dans chacune des structures et je retombe bien sur mes petits.
    Leur taille mémoire est correcte et tout le monde est aligné sur 4 bytes.

    Le string en VBA est sur 4 bytes (cf https://support.microsoft.com/en-us/kb/171583) et si je le remplace par une variable de même taille (ou autres d'ailleurs) dans les structures côté VBA et C++, tout marche bien.
    Le fait de passer dans le C++ altère la mémoire.

    J'ai essayé plein de trucs, je cherche des infos mais pour le moment je reste sans réponse.
    La seule chose que j'ai pu remarqué, c'est que ça marche si ma structure T est
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    Private Type T
        s As String
        X() As Double
        Count As Long
    End Type
    Il y a donc un problème mémoire avec la sous-structure XData et le string.

  11. #11
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 170
    Points : 12 291
    Points
    12 291
    Par défaut
    Oui c'est clairement pas une surprise pour MAC mais le problème reste sur PC
    Si vous utilisez MIDL.EXE il n'y pas de problème.
    Si vous avez une solution de contournement sur MAC pour utiliser le résultat de MIDL, vous n’aurez plus de problèmes ni sous Windows ni sous MAC, en utilisant MIDL.

    Vous avez un problème sous PC, parce que vous n'utilisez pas MIDL.

    Je pense que le problème levé sur MAC est une conséquence du problème vu sous PC. Le résoudre sous PC devrai suffire!
    Le problème fonctionnel du résultat de MIDL montre que c'est loin d'être si "automatique".
    Là, c'est plus : "On cherche à faire marcher le bousin sur PC sans MIDL, et on sert les fesses pour que le layout MAC de VB colle avec le layout Windows de VB", en sachant que ce n'est déjà pas le cas au niveau du Runtime.


    L'exemple que vous nous donnez est pour VB4, VBA doit être basé sur VB6, non ?
    De plus, l'exemple aborde bien des détails que vous ne sembler pas prendre en compte comme le paddind (#pragma pack(1) par exemple) la différence entre chaine de taille fixe ou pas, ou encode les champs de padding utilisé dans le code C++.

    On va essayé d'arrêter de faire des hypothèses et travailler sur du concret :
    Pouvez-vous faire un code VBA qui remplit TOUS les champs de la structure et nous donner le contenu de la RAM indiqué par le pointeur passé au code C++ ?

  12. #12
    Membre régulier
    Inscrit en
    Avril 2013
    Messages
    93
    Détails du profil
    Informations forums :
    Inscription : Avril 2013
    Messages : 93
    Points : 77
    Points
    77
    Par défaut
    L'alignement est effectué par Je remet le code VBA qui rempli toute les structures
    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
    Private Declare PtrSafe Function Cpp_D2 Lib "MyDLL.dll" (i As T) As Long
     
    Private Type XData
        X() As Double
        Count As Long
    End Type
     
    Private Type T
        s As String
        data_T As XData
    End Type
     
     
    Sub Test()
        'declaration
        Dim obj As T
        Dim i As Long
     
        'fill obj
        obj.s = "ok"
     
        'fill XData of obj
        ReDim obj.data_T.X(5)
        For i = 0 To 5
            obj.data_T.X(i) = i
        Next i
        obj.data_T.Count = 5
     
        Call Cpp_D2(obj)
    End Sub
    Dans l'ordre:
    - les valeurs des structures en VBA avant l'appel au C++
    - les champs donné par le pointeur en C++
    - les valeurs des structures en VBA après l'appel au C++

    Nom : VBA.JPG
Affichages : 112
Taille : 27,1 Ko
    Nom : ram.JPG
Affichages : 118
Taille : 45,6 Ko
    Nom : errorVBA.JPG
Affichages : 115
Taille : 38,8 Ko

    J'aurai préféré utiliser MIDL car j'ai fait la même chose avec et cela marche sans aucun soucis bien sur!

  13. #13
    Membre régulier
    Inscrit en
    Avril 2013
    Messages
    93
    Détails du profil
    Informations forums :
    Inscription : Avril 2013
    Messages : 93
    Points : 77
    Points
    77
    Par défaut
    Je n'ai malheureusement toujours pas compris pourquoi la mémoire est altérée mais j'ai trouvé un contournement en passant l'adresse de mon objet en argument et non l'objet lui même ByRef depuis le VBA.
    Le code C++ n'a pas besoin d'être retouché.
    Il suffit juste de changer, dans le VBA, la déclaration de la fonction C++ et l'appel à la fonction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Private Declare PtrSafe Function Cpp_D2 Lib "MyDLL.dll" (ByVal i As Long) As Long
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Call Cpp_D2(VarPtr(obj))
    J'ai testé sur des structures bien plus complexes et avec l'alignement sur 4 octets je n'ai plus aucun souci.
    L'exemple marche sur MAC avec le même alignement.

    L'opération est totalement logique puisque on travaille directement sur l'adresse VBA. La question qui en découle est pourquoi le mot clef ByRef n'est pas suffisant dans ce cas de figure... (je sais que le fait de ne pas utiliser MIDL peu avoir des effets sur l'utilisation des librairies de Windows).

    En tout cas merci pour votre aide!

  14. #14
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 170
    Points : 12 291
    Points
    12 291
    Par défaut
    Merci pour ce retour.

    >L'alignement est effectué par
    L'alignement oui, mais pas le padding, etc...

    Il serait bien plus prudent d'utiliser la vue mémoire et non l'espion.
    En utilisant l'espion, vous voyez comment le runtime C++ interprète la zone mémoire si l'on cherche à savoir comment VB passe ses données.

    J'insiste, il faudrait essayer de garder MIDL et de voir la littérature pour trouver les solutions de contournement documentées.

    Votre approche, c'est de lire directement dans les structures internes de VBA, c'est clairement une utilisation non documenté et donc fragile de connaissances empiriques.
    Bon, des modifications dans VBA, je pense que c'est plus vraiment un truc probable, mais attention à Murphy.

    L'utilisation de ByRef implique que les modifications faites sur l'objet ne soient pas perdu. Cela veut dire que le runtime VB sérialise l'objet selon l'interprétation COM du type VB.
    Le code C++ en face doit donc désérialiser cette objet COM selon la définition du type C++.
    En Utilisant MIDL, on s'assure que la définition du type C++ et VB soient les mêmes et la génération des proxys faites par MIDL permet de faire la sérialisation/désérialisation au format COM des objets C++.
    Le code C++ peut donc faire mumuse avec l'objet désérialisé.
    Au moment du retour, le proxy sérialise l'objet (c'est un paramètre [in,out] dans l'IDL).
    Le Runtime VB désérialise l'objet et l'utilise à la place de celui passé en paramètre d'appel.

    Sans MIDL, vous avez toujours la sérialisation VB, mais vous n'avez pas la désérialisation C++, c'est vous qui vous la paluché (de travers, c'est un travail colossal).
    Vous repassez un truc mémoire ad-hoc, la désérialisation VB fait ce qu'elle peut doit les messages d'erreurs dans tous les expions de VB.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [WD-2010] word crash sur l'appel à doc.close
    Par dzincou dans le forum Word
    Réponses: 0
    Dernier message: 12/06/2012, 09h52
  2. [OLE2/ActiveX] Crash sur appel à InvokeHelper
    Par pierre_h dans le forum MFC
    Réponses: 7
    Dernier message: 10/02/2009, 10h51
  3. [VBA-E] Procédure appelée sur clic des boutons de regroupement
    Par truman dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 02/05/2006, 11h34
  4. [Disques Durs] Prévenir un crash sur un disque dur
    Par Furius dans le forum Composants
    Réponses: 6
    Dernier message: 07/01/2006, 17h04
  5. [VB.NET] Information sur instruction Redim
    Par Aspic dans le forum VB.NET
    Réponses: 4
    Dernier message: 21/12/2005, 19h54

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