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

API, COM et SDKs Delphi Discussion :

zero terminal et widestring


Sujet :

API, COM et SDKs Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Inscrit en
    Août 2003
    Messages
    45
    Détails du profil
    Informations forums :
    Inscription : Août 2003
    Messages : 45
    Par défaut zero terminal et widestring
    Bonjour,

    J'utilise une API externe qui dialogue avec un matériel sur un port série.
    La chaine hexa que je dois envoyer est la suivante : 1B FA 02 00 01 03 5E.

    Là ou ça se complique c'est que la procédure externe que je dois appeler admet en argument un paramètre de type Widestring.
    Du coup mon appel ressemble à ceci : ProcAAppeler(#$1B#$FA#$00#$01#$03#$5E).
    La difficulté provient du zéro terminal se situant en milieu de chaine. La procédure appelée va tronquer cette donnée et la chaine hexa ne sera donc pas envoyée entièrement sur le port série.

    Comment faire ?

    Merci de votre aide.

    Rom

  2. #2
    Expert confirmé
    Avatar de Cl@udius
    Homme Profil pro
    Développeur Web
    Inscrit en
    Février 2006
    Messages
    4 878
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Février 2006
    Messages : 4 878
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    var
      S: WideString;
    begin
      S := #$1B#$FA#$02#$00#$01#$03#$5E;
      ShowMessage(IntToStr(Length(S)));
    end;
    Le ShowMessage renvoie 7, donc correct.

    En fait il s'agit d'un WideString non un PWideString.

    @+ Claudius

  3. #3
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 086
    Par défaut
    Citation Envoyé par Cl@udius Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    var
      S: WideString;
    begin
      S := #$1B#$FA#$02#$00#$01#$03#$5E;
      ShowMessage(IntToStr(Length(S)));
    end;
    Le ShowMessage renvoie 7, donc correct.

    En fait il s'agit d'un WideString non un PWideString.

    @+ Claudius
    C'est là l'erreur, tu insère 7 caractères mais S contient 14 octets !
    Une WideString ne peut pas contenir des nombres impaires d'octets en Delphi !
    On peut supposer que le choix de la WideString est parce qu'elle est alloué dans la mémoire COM, c'est juste vilain, si c'est utilisé avec une DLL non OLE ...

    Essaye ça, il y a surement plus simple (par transtypage peut-être)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var
      SA: AnsiString;
      SW: WideString;
    begin
      SA := #$1B#$FA#$02#$00#$01#$03#$5E;
      SetLength(SW, (Length(SA) + 1) div 2);
      ZeroMemory(@SW[1], SizeOf(WideChar) * Length(SW));
      CopyMemory(@SW[1], @SA[1], Length(SA));
      ProcAAppeler(SW);
    Version par Transtypage, mais j'ai un doute, conversion ou pas, à tester
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    var
      SA: AnsiString;
    begin
      SA := #$1B#$FA#$02#$00#$01#$03#$5E#$00#$00; // 00 en plus pour éviter des violations d'accès ... disons que c'est risqué ...
      ProcAAppeler(WideString(SA)); // Doute si Conversion ou Transtypage ...
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  4. #4
    Membre averti
    Inscrit en
    Août 2003
    Messages
    45
    Détails du profil
    Informations forums :
    Inscription : Août 2003
    Messages : 45
    Par défaut
    Je reviens un peu tardivement, j'avais laissé tomber le sujet pendant quelques temps.

    Mon Api externe attend bien un WideString sans pouvoir lui spécifier une quelconque longueur.

    J'ai testé vos différentes solution, rien à y faire. A mon avis, la procédure externe s'arrête à #0 quoi que je lui passe.

    Je vais me rapprocher du fabricant pour lui demander conseil.

    Merci à vous en tout cas.

    Rom

  5. #5
    Membre éclairé
    Avatar de gb_68
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2006
    Messages
    232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 232
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    C'est là l'erreur, tu insère 7 caractères mais S contient 14 octets !
    Bien vu!
    C'est vrai que les littéraux en Delphi sont typés par le contexte (ou du moins s'y adapte), contrairement au C++ par exemple ( "chaine ansi" -> char * vs L"chaine wide" -> wchar_t *, 'A' -> char vs L'W' -> wchar_t). En Delphi, 'chaine' sera Ansi ou Wide selon son affection, de même pour #$FF.
    Or dans un contexte Wide, #$.. est WideChar = #$0..#$FFFF. Exemple :
    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
    41
    procedure TForm1.Button1Click(Sender: TObject);
    var WStr : WideString; WC : WideChar;
        AStr : AnsiString; WA : AnsiChar;
     
        RawWStr : PUBoundArray absolute WStr;
        RawAStr : PUBoundArray absolute AStr;
     
        I : Integer; Msg : string;
    begin
      WStr := #$01#$02#$03#$04#$05;
      AStr := #$01#$02#$03#$04#$05;
     
      Msg := 'AnsiString : ';
      for WA in AStr do
      begin
        Msg := Msg + IntToHex( Ord(WA), 2 ) + ' ';
      end;
      Msg := Msg + sLineBreak;
     
      Msg := Msg + 'WideString : ';
      for WC in WStr do
      begin
        Msg := Msg + IntToHex( Ord(WC), 4 ) + ' ';
      end;
      Msg := Msg + sLineBreak;
     
      Msg := Msg + 'RawAStr : ';
      for I:=0 to Length(AStr)*SizeOf(WA)-1 do
      begin
        Msg := Msg + IntToHex( Ord(RawAStr[I]), 2 ) + ' ';
      end;
      Msg := Msg + sLineBreak;
     
      Msg := Msg + 'RawWStr : ';
      for I:=0 to Length(WStr)*SizeOf(WC)-1 do
      begin
        Msg := Msg + IntToHex( Ord(RawWStr[I]), 2 ) + ' ';
      end;
     
      ShowMessage(Msg);
    end;
    Affiche :
    AnsiString : 01 02 03 04 05
    WideString : 0001 0002 0003 0004 0005
    RawAStr : 01 02 03 04 05
    RawWStr : 01 00 02 00 03 00 04 00 05
    Une solution (mais pas très lisible)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    begin
      WStr := #$0201#$0403#$05;
      AStr := #$01#$02#$03#$04#$05;
    end;
    Sinon la solution de ShaiLeTroll avec SetLength me semble bonne. Tu peux en faire une fonction du type
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function DataToWideString( var Data; Count : Integer  ): WideString;
    begin
      SetLength( Result, (Count+1) div 2 );
      if Count mod 2 = 1 then
      begin
        Result[Length(Result)]:=#0;
      end;
      Move( Data, Result[1], Count);
    end;
    Ou on peut aussi essayer de passer par les Api Windows (après test ce code semble bon)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // vrai signature
    function __SysAllocStringByteLen( Data : PAnsiChar; len : Cardinal ) : PWideChar; stdcall;
      external 'oleaut32.dll' name 'SysAllocStringByteLen';
     
    function SysAllocStringByteLen( Data : PAnsiChar; len : Cardinal ) : WideString; 
    // inline; entraine une copie de chaine supplémentaire (?!?) mais libérée
    begin
      Pointer(Result) := __SysAllocStringByteLen( Data, len );
    end;
     
    function SysAllocStringData( var Data ; len : Cardinal ) : WideString;
    begin
      Pointer(Result) := __SysAllocStringByteLen( PAnsiChar(@Data), len );
    end;
    attention ne pas faire directement
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    function SysAllocStringByteLen( Data : PAnsiChar; len : Cardinal ) : WideString; stdcall;
      external 'oleaut32.dll' name 'SysAllocStringByteLen';
    Car en paramètre de sortie Delphi ne passe pas un WideString comme un pointeur (directement dans eax) mais en tant que paramètre var supplémentaire (ce qui change complètement la signature de la fonction avec de jolis Acess Violation à l'exécution )
    • Les résultats scalaires sont renvoyés, quand c'est possible, dans un registre CPU : les octets sont renvoyés dans AL, les mots dans AX, les doubles mots dans EAX.
    • Pour un résultat chaîne, tableau dynamique, pointeur de méthode ou variant, les effets sont les mêmes que si le résultat de la fonction était déclaré en tant que paramètre var supplémentaire suivant les paramètres déclarés. En d'autres termes, l'appelant passe un pointeur 32 bits supplémentaire qui pointe sur une variable dans laquelle le résultat de la fonction doit être renvoyé.
    D'où la fonction intermédiaire __SysAllocStringByteLen/SysAllocStringByteLen. Mais si la données a un nombre impaire d'octets, elle sera tronqué par l'Api (dernier octet).

  6. #6
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 086
    Par défaut
    Oh, tu as vraiement poussé le sujet sur l'allocation des chaines dans la mémoire COM ...

    C'est tout de même une étrange fonction dans cette DLL, pour écrire sur un port, un PByte aurait tellement été plus simple et plus pertinent ... on sent vraiement la grosse panique pour la gestion de la mémoire d'un tableau de Byte soulagée par la gestion d'une OleSTR ... baaah ...

    En tout cas, ravi de t'avoir aidé, un petit
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  7. #7
    Membre averti
    Inscrit en
    Août 2003
    Messages
    45
    Détails du profil
    Informations forums :
    Inscription : Août 2003
    Messages : 45
    Par défaut
    Oui j'avais remarqué ça mais du coup l'API externe que j'utilise et qui attend bien en argument un widestring doit mal le gérer et tronquer les données puisque la trame envoyée sur le port série est incomplète...

    Donc peut être que je cherche à résoudre un problème qui ne dépend pas de moi.

  8. #8
    Membre Expert

    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    1 519
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 1 519
    Billets dans le blog
    1
    Par défaut
    Oui sauf que Delphi c'est un malin, il stocke combien de caractère possède la chaîne ce qui fait qu'il ne s'arrête pas au premier #0 trouvé pour déclarer que c'est la fin de la chaîne.

    En revanche ton API j'en doutes, elle doit utiliser la chaîne comme étant une chaîne AZT. Dans ces conditions effectivement ta chaîne est tronqué au premier #0 trouvé. Est-ce que t'on API n'attends pas que tu lui passe le nombre de caractère à lire des fois ?

    Ce qui est bizarre aussi c'est pourquoi passer par un WideString pour faire transiter des données binaire plutôt qu'utiliser une combinaison PByte + nombre d'octet à lire... Est-ce que des fois ton API ne s'attend pas à recevoir '1BFA020001035E' plutôt que #$1B#$FA#$00#$01#$03#$5E (soit la représentation hexadécimale sous forme de chaîne plutôt que la valeur binaire elle-même) ?

  9. #9
    Membre éclairé
    Avatar de gb_68
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2006
    Messages
    232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 232
    Par défaut
    Bonjour,
    il me semble que les WideString Delphi sont l'encapsulation des BSTR de Windows.

    Ce type de chaines peuvent parfois embarquer des données au lieu de caractères
    Citation Envoyé par msdn
    In situations where a BSTR will not be translated from ANSI to Unicode, or vice versa, you can use BSTRs to pass binary data....
    (exemple), et donc normalement ne pas interpréter les zéros terminaux qui sont à l'intérieur de la chaîne).
    Il faut dans ce cas les allouer avec SysReAllocStringLen, mais c'est normalement ce que fait Delphi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    // unité System
    procedure _WStrAsg(var Dest: WideString; const Source: WideString);
    //[...]
            CALL    SysReAllocStringLen
    //[...]
    end;
    Comme dit précédement, vérifie si ton Api prend un PWideString ou WideString/BSTR.
    Sinon, as tu essayé d'utiliser une variable intermédiaire comme dans l'exemple de Cl@udius ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    var
      S: WideString;
    begin
      S := #$1B#$FA#$02#$00#$01#$03#$5E; //...

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

Discussions similaires

  1. ERROR: pg_atoi: zero-length string
    Par steelspirit92 dans le forum PostgreSQL
    Réponses: 1
    Dernier message: 23/08/2003, 22h29
  2. Réponses: 3
    Dernier message: 02/07/2003, 16h24
  3. [transtypage]PChar et WideString
    Par rbag dans le forum Bases de données
    Réponses: 2
    Dernier message: 05/09/2002, 20h12
  4. [TTHREAD] ne termine pas sont exécution
    Par Bbenj dans le forum Langage
    Réponses: 4
    Dernier message: 02/08/2002, 16h42

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