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

Web & réseau Delphi Discussion :

Problème REST Oauth2 Delphi 10.4 / Delphi 11


Sujet :

Web & réseau Delphi

  1. #1
    Membre à l'essai
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2021
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Nièvre (Bourgogne)

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

    Informations forums :
    Inscription : Juillet 2021
    Messages : 9
    Points : 12
    Points
    12
    Par défaut Problème REST Oauth2 Delphi 10.4 / Delphi 11
    Bonjour à tous,

    Je rencontre actuellement un problème sur une authentification REST oAuth2.

    J'ai mis en place un petit programme d’authentification qui nous permet de me connecter à azure AD ; il fait une demande de code dans un premier temps puis une demande de Token.

    Le développement a été fait sous Delphi 10.4.2, or ce programme ne fonctionne plus une fois compilé en Delphi 11. On a une erreur bad request au moment de la demande du Token.

    D’ailleurs, j'ai remarqué que le RESTdebugger fonctionnait correctement en 10.4.2 mais en 10.5 (version de Delphi 11) celui-ci génère une erreur :

    Nom : err1.jpg
Affichages : 150
Taille : 52,4 Ko

    Lorsque l’on prend la requête envoyée par Delphi et qu’on la met dans un navigateur web on obtient :

    Nom : err2.jpg
Affichages : 162
Taille : 28,3 Ko

    On a donc l’impression que la requête est envoyée en GET et pas en POST.

    J'ai cherché pas mal sur le net et en fouillant dans le code, mais je n'ai rien trouvé de particulier.

    Ci-dessous le code :

    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
     
    procedure TForm1.Button1Click(Sender: TObject);
    begin
     RESTDemandeCode;
    end;
     
    function TForm1.GetSimpleValue(aUrl:string;aVar:string):string;
    begin
      Result :='';
      if ContainsText(aUrl, aVar + '=') then begin
        Result := Copy(aUrl, Pos(aVar + '=', aUrl) + Length(aVar) + 1, Length(aUrl));
        if (Pos('&', Result) > 0) then
          Result := Copy(Result, 1, Pos('&', Result) - 1);
      end;
    end;
     
    procedure TForm1.RESTDemandeCode;
    var
      vUrl                    : string;
      vTenantID               : string;
      vClientID               : string;
      vRedirection            : string;
      vScope                  : string;
      vAuthorizationEndpoint  : string;
     
    begin
      vTenantID               := 'MonTenant';
      vClientID               := 'MonClientID';
      vRedirection            := System.NetEncoding.TURLEncoding.URL.Encode('https://localhost');
      vScope                  := 'https://graph.microsoft.com/.default';
      vAuthorizationEndpoint  := 'https://login.microsoftonline.com/' + vTenantID + '/oauth2/v2.0/authorize';
     
      vUrl := vAuthorizationEndpoint;
      vUrl := vUrl + '?client_id=' + vClientID;
      vUrl := vUrl + '&response_type=code';
      vUrl := vUrl + '&redirect_uri=' + vRedirection;
      vUrl := vUrl + '&scope=' + vScope;
     
      WebBrowser1.URL := vUrl;
     
     end;
     
    procedure TForm1.WebBrowser1DidFinishLoad(ASender: TObject);
    var
      vCode : String;
    begin
      Memo1.Lines.Add(WebBrowser1.URL);
      vCode := GetSimpleValue(WebBrowser1.Url,'code');
      if vCode <>'' then begin
        Code          := vCode;
        Code_lbl.Text := vCode;
      end;
    end;
     
    procedure TForm1.ConfigureProxy(aClient: TCustomRESTClient);
      begin
        aClient.ProxyServer   := '';
        aClient.ProxyPort     := 0 ;
        aClient.ProxyUsername := '';
        aClient.ProxyPassword := '';
      end;
     
    procedure TForm1.RESTDemandeToken;
    var
      vRESTClient                 : TRESTClient;
      vRESTRequest                : TRESTRequest;
      vTenantID                   : string;
      vClientID,  vClientSecret   : string;
      vRedirection                : string;
      vAccessTokenEndpoint        : string;
      vReponse                    : String;
     
    begin
      vTenantID               := 'MonTenant';
      vClientID               := 'MonClientID';
      vClientSecret           := 'MonClientSecret';
      vRedirection            := 'https://localhost';
      vAccessTokenEndpoint    := 'https://login.microsoftonline.com/' + vTenantID + '/oauth2/v2.0/token';
     
      vRESTClient                   := TRESTClient.Create(self);
      ConfigureProxy(vRESTClient);
     
      vRESTRequest                  := TRESTRequest.Create(self);
      vRESTRequest.Client           := vRESTClient;
      vRESTRequest.Method           := TRESTRequestMethod.rmPost;
     
      vRESTClient.BaseURL           := vAccessTokenEndpoint;
      vRESTRequest.AddParameter('code', code);
      vRESTRequest.AddParameter('client_id', vClientID);
      vRESTRequest.AddParameter('client_secret', vClientSecret);
      vRESTRequest.AddParameter('grant_type', 'authorization_code');
     
      vRESTRequest.AddParameter('redirect_uri', vRedirection);
     
      Try
        vRESTRequest.Execute;
     
        if (vRESTRequest.Response.StatusCode = 200) then begin
          vRESTRequest.Response.GetSimpleValue('access_token',vReponse);
          Token_lbl.Text := vReponse;
        end;
     
      Finally
        FreeAndNIL(vRESTRequest);
        FreeAndNIL(vRESTClient);
      End;
     
    end;
     
    procedure TForm1.Button2Click(Sender: TObject);
    begin
      RESTDemandeToken;
    end;
    Merci d'avance pour votre retour.

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 457
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    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 : 13 457
    Points : 24 870
    Points
    24 870
    Par défaut
    Pour RESTdebugger, on dirait carrément le DFM qui est mort, recompilé sans avoir ouvert la DFM pour en vérifier les property, une erreur de Flux bien vilaine, le problème a été évoqué sur le forum

    le WebBrowser avant c'était IE avec émulation en diverses version, je crois qu'il est maintenant dépendant de Edge, à vérifier
    J'utilise Navigate2 pour ma part en 10.0 et non la propriété URL

    GET c'est pour le authorize
    POST c'est pour le token

    Mon code est assez similaire, j'ai fait une classe TSLTOAuth2Authenticator qui simplifie l'utilisation de TOAuth2Authenticator
    On trouve ici une ancienne version de mon TSLTOAuth2Authenticator



    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //------------------------------------------------------------------------------
    procedure TSLTOAuth2AuthenticatorAzure.SetTenant(const Value: string);
    begin
      if Value <> '' then
      begin
        FAuthorizeEndpoint := Format('https://login.microsoftonline.com/%s/oauth2/v2.0/authorize', [Value]);
        FAccessTokenEndpoint := Format('https://login.microsoftonline.com/%s/oauth2/v2.0/token', [Value]);
      end
      else
        raise ESLTOAuth2AuthenticatorParamError.Create('Tenant is required by ' + ClassName());
    end;
    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    //------------------------------------------------------------------------------
    function TSLTOAuth2AuthenticatorAzure.GetAuthCodeRequestURL(): string;
    begin
      with TSLTOAuth2AuthenticatorAzure.TURLBuilder.Create() do
      try
        AddParam('client_id', FOwner.ClientID);
        if FOwner.ClientSecret <> '' then
          AddParam('client_secret', FOwner.ClientSecret);
        AddParam('response_type', 'code');
        AddParam('redirect_uri', FOwner.Context.RedirectionEndpoint);
        AddParam('scope', FOwner.Scope);
        AddParam('response_mode', 'query');
        if FOwner.Context.UserName <> ''  then
          AddParam('login_hint', FOwner.Context.UserName);
        AddParam('prompt', 'consent');
     
        Result := BuildURL(FAuthorizeEndpoint);
      finally
        Free();
      end;
    end;
     
    //------------------------------------------------------------------------------
    function TSLTOAuth2AuthenticatorAzure.Authenticate(): Boolean;
    var
      VClient: TRESTClient;
      VRequest: TRESTRequest;
    begin
      // On vérifie les valeurs avant d'envoyer inutilement une requête
      // En débogage, vous pouvez tenter de laisser les valeurs être envoyé dans l'état pour obtenir le message d'erreur Microsoft
      // voir - https://docs.microsoft.com/fr-fr/azure/active-directory/develop/reference-aadsts-error-codes
      // ClientID, GrantType & Scope also UserName/Password are required if ROPC
      // ClientSecret are recommended but not required (only if defined in Portal Azure)
      if FOwner.Tenant = '' then
        raise ESLTOAuth2AuthenticatorError.Create('Tenant is required for use ' + ClassName());
     
      if FOwner.ClientID = '' then
        raise ESLTOAuth2AuthenticatorParamError.Create('ClientID is required for use ' + ClassName() + ' : See Error "AADSTS900144: The request body must contain the following parameter: ''client_id''."');
     
      if FOwner.Scope = '' then
        raise ESLTOAuth2AuthenticatorParamError.Create('Scope is required for use ' + ClassName() + ' : See Error "AADSTS900144: The request body must contain the following parameter: ''scope''."');
     
      if not (FOwner.Context.FlowType in [oaftAuthorizationCode, oaftClientCredentials]) and not FOwner.Scope.Contains(TSLTOAuth2AuthenticatorAzure.SCOPE_AS_APPLICATION) then
        if (FOwner.Context.UserName = '') or (FOwner.Context.Password = '') then
          raise ESLTOAuth2AuthenticatorParamError.Create('UserName and Password has required for use ' + ClassName() + ' : See Error "AADSTS900144: The request body must contain the following parameter: ''username''." or "AADSTS900144: The request body must contain the following parameter: ''password''."');
     
      VClient := TRESTClient.Create(FAccessTokenEndpoint);
      try
        VRequest := TRESTRequest.Create(VClient); // The Client owns the Request (will free it) and it will assign as Client property
        VRequest.Method := TRESTRequestMethod.rmPOST;
     
        VRequest.AddAuthParameter('client_id', FOwner.ClientID, TRESTRequestParameterKind.pkGETorPOST);
        if FOwner.ClientSecret <> '' then
          VRequest.AddAuthParameter('client_secret', FOwner.ClientSecret, TRESTRequestParameterKind.pkGETorPOST);
        VRequest.AddAuthParameter('scope', FOwner.Scope, TRESTRequestParameterKind.pkGETorPOST);
     
        if FOwner.Context.FlowType = oaftAuthorizationCode then
        begin
          VRequest.AddAuthParameter('grant_type', 'authorization_code', TRESTRequestParameterKind.pkGETorPOST);
          VRequest.AddAuthParameter('code', FOwner.AuthorizationCode, TRESTRequestParameterKind.pkGETorPOST);
          VRequest.AddAuthParameter('redirect_uri', FOwner.RedirectionEndpoint, TRESTRequestParameterKind.pkGETorPOST);
        end
        else if not FOwner.Scope.Contains(TSLTOAuth2AuthenticatorAzure.SCOPE_AS_APPLICATION) then
        begin
          VRequest.AddAuthParameter('grant_type', 'password', TRESTRequestParameterKind.pkGETorPOST);
          VRequest.AddAuthParameter('username', FOwner.Context.UserName, TRESTRequestParameterKind.pkGETorPOST);
          VRequest.AddAuthParameter('password', FOwner.Context.Password, TRESTRequestParameterKind.pkGETorPOST);
        end
        else
          VRequest.AddAuthParameter('grant_type', 'client_credentials', TRESTRequestParameterKind.pkGETorPOST);
     
        Result := FOwner.Execute(VRequest);
      finally
        VClient.Free();
      end;
    end;
    Azure pour GRAPH, IMAP XOAUTH2 et POP3 XOAUTH2
    Google & Yahoo pour IMAP XOAUTH2 uniquement

    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    //------------------------------------------------------------------------------
    function TSLTOAuth2AuthenticatorGoogle.GetAuthCodeRequestURL(): string;
    begin
      FAuthorizeEndpoint := 'https://accounts.google.com/o/oauth2/v2/auth';
     
      with TSLTOAuth2AuthenticatorAzure.TURLBuilder.Create() do
      try
        AddParam('client_id', FOwner.ClientID);
        AddParam('response_type', 'code');
        AddParam('redirect_uri', FOwner.Context.RedirectionEndpoint);
        AddParam('scope', FOwner.Scope);
        if FOfflineAccess then
          AddParam('access_type', 'offline');
        AddParam('response_mode', 'query');
        if FOwner.Context.UserName <> ''  then
          AddParam('login_hint', FOwner.Context.UserName);
     
        Result := BuildURL(FAuthorizeEndpoint);
      finally
        Free();
      end;
    end;
     
    //------------------------------------------------------------------------------
    function  TSLTOAuth2AuthenticatorEngine.TURLBuilder.AddParam(const AName: string; const AValue: string): TParam;
    begin
      Result.Name := AName;
      Result.Value := AValue;
      FParams.Add(Result);
    end;
     
    //------------------------------------------------------------------------------
    function TSLTOAuth2AuthenticatorEngine.TURLBuilder.BuildURL(const AEndPoint: string): string;
    var
      Param: TParam;
    begin
      // inherited abstract
      with TRESTRequest.Create(nil) do
      try
        Client := TRESTClient.Create(AEndPoint);
        try
          Client.ContentType := REQUEST_CONTENT_TYPE;
     
          Method := TRESTRequestMethod.rmGET;
     
          for Param in FParams do
            Params.AddItem(Param.Name, Param.Value, REST.Types.TRESTRequestParameterKind.pkGETorPOST, [], TRESTContentType.ctAPPLICATION_X_WWW_FORM_URLENCODED);
     
          Result := GetFullRequestURL(True);
        finally
          Client.Free();
        end;
      finally
        Free();
      end;
    end;
    et dans l'application de Test :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function TForm1.GetAuthCode(AAuthenticator: TSLTOAuth2Authenticator): string;
    begin
      with TAuthCodeForm.Create() do
      try
        Navigate(AAuthenticator.AuthCodeRequestURL, AAuthenticator.RedirectionEndpoint, Result);
      finally
        Free();
      end;
    end;
    TAuthCodeForm étant REST.Authenticator.OAuth.WebForm.Win
    Mon code TSLTOAuth2Authenticator lui même encapsule un TOAuth2Authenticator de REST.Authenticator.OAuth
    Ainsi ensuite pour GRAPH, je passe directement l'objet standard TOAuth2Authenticator construit via un TSLTOAuth2Authenticator

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //------------------------------------------------------------------------------
    function TSLTMicrosoftGraph.CreateRESTClientForUser(): TRESTClient;
    begin
      Result := TRESTClient.Create(Format('https://graph.microsoft.com/v1.0/users/%s', [FUserName]));
      try
        Result.ContentType := REQUEST_CONTENT_TYPE;
        Result.Authenticator := FAuthenticator.OAuth2Authenticator;
      except
        FreeAndNil(Result);
        raise;
      end;
    end;

    le code réel est en ROPC ou ClientCredential au lieu d'être en Authorization Code.
    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

  3. #3
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 457
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    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 : 13 457
    Points : 24 870
    Points
    24 870
    Par défaut
    Comme je n'ai pas tout compris votre problème de GET à la place de POST
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    vRESTRequest.Method           := TRESTRequestMethod.rmPost;
    Il va falloir débogguer le TRESTRequest et TRESTClient, voir ce qu'il fait dans le Execute, que le POST soit bien respecté
    Une regression, je suis persuadé d'avoir lu une QR à ce sujet mais ça sera GRAVISSIME un nom-respect de la méthode !

    A mon avis, il y a une confusion entre le GET Authorize dans un Browser uniquement et le POST Token dans un Request simple uniquement
    Le Authorize implique un consentement utilisateur, une interaction humaine, elle peut être passée si le Azure trouve des infos d'authentification (surement dans les cookies), typique lorsque l'on s'authenfie une première fois, souvent en double facteur (Login+PWD avec SMS) puis le Token reste valide 60 jours par exemple pour simplifier la vie de l'utilisateur.
    le Token, c'est soit une conversion d'un AuthCode obtenu par Autorize ou un alors un RefreshToken (celui-ci dure 90 jours, le AccessToken souvent une heure et doit donc être Refresh à la date d'expiration)

    J'utilise plutôt AddAuthParameter au lieu du AddParameter qui ajoute TRESTRequestParameterOption.poTransient,
    je précise aussi TRESTRequestParameterKind.pkGETorPOST même si il me semble que c'est la valeur par défaut du AddParameter

    je note la séquence

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
      vRESTClient                   := TRESTClient.Create(self);
      ConfigureProxy(vRESTClient);
     
      vRESTRequest                  := TRESTRequest.Create(self);
      vRESTRequest.Client           := vRESTClient;
      vRESTRequest.Method           := TRESTRequestMethod.rmPost;
     
      vRESTClient.BaseURL           := vAccessTokenEndpoint;
    je lui préfererais pour des questions de Owner Self inutile et ordre des instructions, le try finally mal placé ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
      vRESTClient := TRESTClient.Create(vAccessTokenEndpoint);
      try
        ConfigureProxy(vRESTClient);
     
        vRESTRequest := TRESTRequest.Create(vRESTClient); // juste besoin de libérer vRESTClient
        vRESTRequest.Method := TRESTRequestMethod.rmPost;
     
        ...
      finally
        vRESTClient.Free();
      end;
    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 à l'essai
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juillet 2021
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Nièvre (Bourgogne)

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

    Informations forums :
    Inscription : Juillet 2021
    Messages : 9
    Points : 12
    Points
    12
    Par défaut
    Merci pour votre retour.

    Pour information, je m'étais également rapproché de Patrick Prémartin concernant ce problème et il semblerait qu'il y ait bien un problème au niveau du client REST. Il m'a redirigé vers ce lien :

    https://quality.embarcadero.com/browse/RSP-35365

    Je vais étudier toutes les solutions apportées et je reviendrai rapidement vers vous.

  5. #5
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 693
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 693
    Points : 13 126
    Points
    13 126
    Par défaut
    C'est anecdotique mais tu peux simplifier l'extraction des paramètres de l'URL.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    uses System.Net.URLClient;
     
    function TForm1.GetSimpleValue(aUrl:string;aVar:string):string;
    begin
      var URL := TURI.Create(aUrl);
      Result  := URL.ParameterByName[aVar];
    end;
    Et même si c'est moins flagrant à l'écriture :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    var vUrl := TURI.Create(vAuthorizationEndpoint);
     
    vUrl.AddParameter('client_id', vClientID);
    vUrl.AddParameter('response_type', 'code');
    vUrl.AddParameter('redirect_uri', vRedirection);
    vUrl.AddParameter('scope', vScope);
     
    WebBrowser1.URL := vUrl.ToString;

Discussions similaires

  1. Problème TqrChart et Delphi 2006
    Par informatiqueAS dans le forum Delphi
    Réponses: 3
    Dernier message: 12/12/2006, 09h45
  2. Problèmes d'installation Delphi 7
    Par ours27 dans le forum Delphi
    Réponses: 2
    Dernier message: 26/07/2006, 15h24
  3. problème avec BDE delphi 3 et serveur windows 2003
    Par korntex5 dans le forum Bases de données
    Réponses: 2
    Dernier message: 07/07/2006, 22h58
  4. problème de syntaxe delphi pour une requête sql
    Par socooooool dans le forum Bases de données
    Réponses: 12
    Dernier message: 07/07/2006, 16h53
  5. Problème Indy avec Delphi 2005.
    Par Bjarne93 dans le forum Web & réseau
    Réponses: 4
    Dernier message: 29/11/2005, 19h03

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