[C / C++] un client FTP activeX pour uploader un fichier en ASYNC ?

un client FTP activeX pour uploader un fichier en ASYNC ? [C / C++] - C++ - Programmation

Marsh Posté le 29-04-2002 à 12:27:43    

voilà ce que je dois faire...
(un activeX permettant de se connecter sur un serveur FTP et d'uploader des fichiers).
 
Le problème c que lorsque je créer mon objet CInternetSession avec le flag = INTERNET_FLAG_ASYNC j'ai un assert failed .
 
de plus j'aimerai afficher la progression de l'upload d'un fichier mais ça a l'air compliqué: Je dois apparement par la fonction CFtpConnection::OpenFile et ::CreateFile ( et ::ReadFile, CInternetFile::Write en boucle ....
dans le CInternetSession::OnStatusCallback pour ne pas bloquer l'exécution ??  ) au lieu de CFtpConnection::PutFile() qui est beaucoup plus facile.. n'y-a-t-il pas un moyen d'afficher la progression avec la fonc. putFile par l'intermédiaire de CInternetSession::OnStatusCallback ??  
 
merci d'avance !

Reply

Marsh Posté le 29-04-2002 à 12:27:43   

Reply

Marsh Posté le 29-04-2002 à 15:18:10    

:bounce:

Reply

Marsh Posté le 29-04-2002 à 16:27:51    

:bounce:  :bounce:

Reply

Marsh Posté le 29-04-2002 à 16:37:16    

poste le code ou tu créé ton objet CInternetSession

 

[jfdsdjhfuetppo]--Message édité par Harkonnen le 29-04-2002 à 16:37:42--[/jfdsdjhfuetppo]

Reply

Marsh Posté le 30-04-2002 à 10:39:15    

d'aprés ce que j'ai compris le flag "INTERNET_FLAG_ASYNC" n'est pas supporté c pourquoi cela provoque un assert failed... maintenant je vois pas trop comment je vais faire dans mon implémentatoin pour rendre les transfert asynchrones...
 
voilà le code:
 
"
#include "CFtp.h"  
 
// constructeur
CFtp::CFtp() : //CInternetSession("NTV Factory ActiveX FTP Client", 1, INTERNET_OPEN_TYPE_PRECONFIG,
    //    NULL, NULL, INTERNET_FLAG_ASYNC), m_pFtpConnection(NULL)  
    CInternetSession("NTV Factory ActiveX FTP Client" ), m_pFtpConnection(NULL)  
{
 strcpy(Error, "no error" );
 NumError = 0;
 ErrorLength = 9;
 strcpy(BaseDirectory, "\\" );
 
 Status = NOT_CONNECTED;
 
 // c à l'utilisateur de l'appeler explicitement
 //EnableStatusCallback(TRUE);
}
 
// destructeur
CFtp::~CFtp() {
 CloseConnect();
 Close();
}
 
 
// fin de connection (détruit m_pFtpConnection)
void CFtp::CloseConnect()
{
 if(m_pFtpConnection) {
  m_pFtpConnection->Close();
  delete m_pFtpConnection;
  m_pFtpConnection = NULL;
 }
 
 Status = NOT_CONNECTED;
}
 
// connection au serveur ftp (crée m_pFtpConnection)
// retourne 0 si succès, sinon Error contient le message d'erreur
HRESULT CFtp::Connect(const char* ServerName, const char* Login, const char* Password,
       INTERNET_PORT Port, BOOL Passive) {
 
 if(m_pFtpConnection) {
  strcpy(Error, "Already connected" );
  return E_FAIL;
 }
 
 // la solution pour de l'async, serait de créer un thread qui crée la connection FTP
 // cale ne va-t-il pas poser problème dans une classe ??
 //CreateThread(NULL, 0, ThreadProc, this, 0  , &ThreadID);
 
 JI_ERROR(m_pFtpConnection = GetFtpConnection(ServerName, Login, Password, Port, Passive));
 
 
 DWORD size = MAX_PATH;
 JI_ERROR(m_pFtpConnection->GetCurrentDirectory(BaseDirectory, &size));
 
 return 0;
}
 
 
 
// envoie d'un fichier sur le serveur,  
// retourne 0 si succès, sinon Error contient le message d'erreur
HRESULT CFtp::PutFile(const char* LocalFile, const char* RemoteFile) {
 CHECK_CONNECT;
 
 if((!LocalFile)||(!RemoteFile)||(!strcmp(LocalFile, "" ))||(!strcmp(RemoteFile, "" ))) {
  strcpy(Error, "PutFile: Invalid arguments" );
  return E_INVALIDARG;
 }
 
 // il faut décomposer le nom de fichier en chemin + nom de fichier effectif
 const char* p = RemoteFile;
 // on se place au dernier '/'
 while(*p) p++; do p--; while((*p != '/';)&&(p > RemoteFile));
 
 int nbchar = p - RemoteFile;
 
 // on sauvegarde le repertoire d'appel
 CString CurrentDir;
 JI_ERROR(m_pFtpConnection->GetCurrentDirectory(CurrentDir));
 
 // s'il y il y a un repertoire on le stocke dans une variable à part
 if( (nbchar)||(*p == '/';) ) {
  char* Directory = (char*)malloc((nbchar+2)*sizeof(char));
  if(!Directory) {
   strcpy(Error, "PutFile: Out of memory !" );
   return E_OUTOFMEMORY;
  }
  strncpy(Directory, RemoteFile, nbchar+1);
  // on termine la chaine de car
  *(Directory+nbchar+1) = '\0';
 
  if(SetRemoteDirectory(Directory, TRUE)) {
   free(Directory);
   return E_FAIL;
  }
 
  free(Directory);
 
  // RemoteFile ne contient que le nom de fichier
  RemoteFile += nbchar+1;
 }
 
 JI_ERROR(m_pFtpConnection->PutFile(LocalFile, RemoteFile));
 
 // ptêt devoir utiliser ::CreateFile
 // m_pFtpConnection->OpenFile
 // et ::ReadFile /  CInternetFile::Write en boucle ....
 // (dans le callback pour ne pas bloquer l'exécution ?? )
 
 // on restore le repertoire d'appel
 JI_ERROR(m_pFtpConnection->SetCurrentDirectory(CurrentDir.GetBuffer(1)));
 
 return 0;
}
 
 
// envoie un repertoire et son contenu sur le serveur
// retourne 0 si succès, sinon Error contient le message d'erreur
// ne modifie pas le repertoire courant
HRESULT CFtp::PutDirectory(const char* LocalDirectory, const char* RemoteDirectory) {
 CHECK_CONNECT;
   
 if((!LocalDirectory)||(!RemoteDirectory)) {
  strcpy(Error, "PutDirectory: Invalid arguments" );
  return E_INVALIDARG;
 }
 
 // on sauvegarde le repertoire d'appel
 CString CurrentDir;
 JI_ERROR(m_pFtpConnection->GetCurrentDirectory(CurrentDir));
 
 // on copie le repertoire source sur le serveur
 HRESULT rval = RecPutDirectory(LocalDirectory, RemoteDirectory) ;
 
 // on restore le repertoire courant
 JI_ERROR(m_pFtpConnection->SetCurrentDirectory(CurrentDir.GetBuffer(1)));
 
 return rval;
}
 
 
const char* CFtp::GetError()
{
 return Error;
}
 
 
 
// Va dans le répertoire spécifié sur le serveur
// (créer le répertoire si besoin si Create = TRUE)
// retourne 0 si succès, sinon Error contient le message d'erreur
// appelle RecRemoteDirectory();
HRESULT CFtp::SetRemoteDirectory(const char* RemoteDirectory, BOOL Create) {
 CHECK_CONNECT;
   
 if(!RemoteDirectory) {
  strcpy(Error, "SetRemoteDirectory: Invalid arguments" );
  return E_INVALIDARG;
 }
 
 // si le repertoire commence par un slash alors on se place dans le repertoire de base du serveur ftp
 if(*RemoteDirectory == '/';) {
  JI_ERROR(m_pFtpConnection->SetCurrentDirectory(BaseDirectory));
 
  // ensuite on peut virer le(s) premier(s) slash(s)
  while(*RemoteDirectory == '/';) RemoteDirectory++;
 }
 
 return RecRemoteDirectory(RemoteDirectory, Create);  
}
 
 
// retourne le repertoire courant dans 'RemoteDirectory'
// retourne 0 si succès, sinon Error contient le message d'erreur
HRESULT CFtp::GetRemoteDirectory(CString& RemoteDirectory) {
 CHECK_CONNECT;
   
 JI_ERROR(m_pFtpConnection->GetCurrentDirectory(RemoteDirectory));
 
 int i = 0;
 CString sav = RemoteDirectory;
 
 // on compte le nb de car dans le rep. de base
 while( *(BaseDirectory + i) != '\0';) i++;
 
 // on retire le rep. de base dans le rep. courant
 char* p = sav.GetBuffer(1);
 char* j = RemoteDirectory.GetBuffer(1);
 strcpy(j, p + i);
 
 // et on ajoute un slash '/' final
 strcat(j ,"/" );
 RemoteDirectory.ReleaseBuffer();
 
 return 0;
}
 
 
 
// ********************* protected ******************** //
 
 
// Va dans le répertoire spécifié sur la machine locale
// retourne 0 si succès, sinon Error contient le message d'erreur
HRESULT CFtp::SetLocalDirectory(const char* LocalDirectory) {
 CHECK_CONNECT;
   
 if(!LocalDirectory) {
  strcpy(Error, "SetLocalDirectory: Invalid arguments" );
  return E_INVALIDARG;
 }
 
 if(!SetCurrentDirectory(LocalDirectory)) {
  Msg(Error, "SetLocalDirectory: Cannot set current directory %s", LocalDirectory);
  return E_FAIL;
 }
 
 return 0;  
}
 
 
 
 
 
// ********************* private ********************* //
 
HRESULT CFtp::RecRemoteDirectory(const char* RemoteDirectory, BOOL Create = TRUE) {
 // on enlève le slash '/' de début si besoin
 while(*RemoteDirectory == '/';) RemoteDirectory++;
 
 // on a fini: on est dans le bon répertoire
 if(*RemoteDirectory == '\0';) return 0;
 if( (!strcmp((char*)RemoteDirectory, "." ))||(!strcmp((char*)RemoteDirectory, "./" )) ) return 0;
 
 
 // on se place au premier '/' s'il existe
 const char* p = RemoteDirectory;
 while((*p)&&(*p != '/';)) p++;   // un foirage ici ????
 
 int nbchar = p - RemoteDirectory;
 
 char* ImDir = (char*)malloc((nbchar+2)*sizeof(char));
 if(!ImDir) {
  strcpy(Error, "RecRemoteDirectory: Out of memory !" );
  return E_OUTOFMEMORY;
 }
   
 strncpy(ImDir, RemoteDirectory, nbchar);
 *(ImDir+nbchar) = '\0';
 
 // on essaie de le créer
 if(Create) m_pFtpConnection->CreateDirectory(ImDir);
 
 // et on y va
 HRESULT rval;
 LI_ERROR(m_pFtpConnection->SetCurrentDirectory(ImDir));
 free(ImDir);
 
 if(!rval) return E_FAIL; // le LI_ERROR le place à 0 si echec
 
 RemoteDirectory = p;
 
 return RecRemoteDirectory(RemoteDirectory, Create);  
}
 
 
 
// fonction récursive appelé par PutDirectory()
HRESULT CFtp::RecPutDirectory(const char* LocalDirectory, const char* RemoteDirectory) {
 CHECK_CONNECT;
   
 if((!LocalDirectory)||(!RemoteDirectory)) {
  strcpy(Error, "PutDirectory: Invalid arguments" );
  return E_INVALIDARG;
 }
 
 // on se place dans le repertoire cible local
 if(SetLocalDirectory(LocalDirectory)) return E_FAIL;
 
 // on fait de même dans le rep distant
 if(SetRemoteDirectory(RemoteDirectory, TRUE)) return E_FAIL;
 
 
 // pour chaque fichier: on le stocke sur le serveur
 // pour chaque repertoire on appelle la fonction récursivement
 WIN32_FIND_DATA find_data;
 HANDLE File = FindFirstFile("*.*", &find_data);
 
 if(File == INVALID_HANDLE_VALUE) {
  Msg(Error, "Cannot find file in %s", LocalDirectory);
  return E_FAIL;
 }
 
 while(File) {
  char* FileName = find_data.cFileName;
   
  if(find_data.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) {
   if( (strcmp(FileName, "." ))&&(strcmp(FileName, "./" ))&&
    (strcmp(FileName, ".." ))&&(strcmp(FileName, "../" )) ) {
    if(RecPutDirectory(FileName, FileName)) return E_FAIL;
   }
  }
  else {
   if(PutFile(FileName, FileName)) return E_FAIL;
  }
     
  if(!FindNextFile(File, &find_data)) {
   FindClose(File);
   File = 0;
  }
 }
 
 // on remonte l'arborescence d'un niveau pour revenir au répertoire courant
 // lors de l'appel de la fonction
 if(SetLocalDirectory(".." )) return E_FAIL;
 if(SetRemoteDirectory("..", FALSE)) return E_FAIL;
 
 return 0;  
}
 
 
 
 
// ******************* callback ************************ //
 
// on surchage l'évenement OnStatusCallback pour afficher une progression du telechargement
void CFtp::OnStatusCallback(DWORD dwContext, DWORD dwInternetStatus,
    LPVOID lpvStatusInformation, DWORD dwStatusInformationLength) {
 
 AFX_MANAGE_STATE( AfxGetAppModuleState( ) );
 
 if(dwContext == 1) {
  switch(dwInternetStatus) {
   case INTERNET_STATUS_CONNECTING_TO_SERVER: Status = CONNECTING; break;
   case INTERNET_STATUS_CONNECTED_TO_SERVER: Status = CONNECTED; break;
   case INTERNET_STATUS_SENDING_REQUEST: Status = SENDING; break;
   case INTERNET_STATUS_REQUEST_SENT: Status = SENT; break;
   case INTERNET_STATUS_RECEIVING_RESPONSE: Status = RECEIVING; break;
   case INTERNET_STATUS_RESPONSE_RECEIVED: Status = RECEIVED; break;
   case INTERNET_STATUS_CLOSING_CONNECTION: Status = CLOSING; break;
   case INTERNET_STATUS_CONNECTION_CLOSED: Status = CLOSED; break;
   case INTERNET_STATUS_REQUEST_COMPLETE:
    {
      Status = COMPLETE;  
       
      INTERNET_ASYNC_RESULT* result = (INTERNET_ASYNC_RESULT*) lpvStatusInformation;
     if( (dwStatusInformationLength == ERROR_INTERNET_EXTENDED_ERROR) ||
      (result->dwError != ERROR_SUCCESS) ) { // on affiche l'erreur
     
      ErrorLength = ErrorMaxLength;  
      InternetGetLastResponseInfo(&NumError, Error, &ErrorLength);
      msg(Error);
     }
     else { // pas d'erreur  
      msg("reçu un INTERNET_STATUS_REQUEST_COMPLETE sans erreur !" );
     }
 
    }
    break;
   default:  
     msg("reçu un statuscallback !" );
 
  }
 }
}
 
"

 

[jfdsdjhfuetppo]--Message édité par ZZZzzz le 30-04-2002 à 14:31:04--[/jfdsdjhfuetppo]

Reply

Marsh Posté le 30-04-2002 à 12:59:12    

Bein alors Kyle, keske tu fais, on t'appelle :D

Reply

Marsh Posté le 30-04-2002 à 13:20:16    

Kyle ?? c ki ça ??

Reply

Marsh Posté le 30-04-2002 à 14:02:01    

Kyle Mc Lachlan, ou Paul Atreides dans le film Dune  :D  
 
Je suis la, mais au boulot où on utilise la VCL, dur de tester des MFC dans ces conditions  :D  
 
Dès que je débauche, je regarde ton source

Reply

Marsh Posté le 30-04-2002 à 14:13:49    

ah bah c sympa ça :) !
merci !
 
en fait j'ai entendu dire que je devais faire une classe (ce que j'ai fait, ça tombe bien ;)) qui crée un thread pour crée le CFtpConnection ou un truc de ce genre, afin d'assurer une gestion asynchrone parfaite des commandes. le problèmes c que dès lors, quand je veux par exemple transférer un fichier (la méthode putfile() de ma classe CFtp), comment je fais pour dire à mon thread contenant l'instance de mon CFtpConnection de lancer la commande... car faut bien voir qu'à terme cette classe sera utlisé par un contrôle activeX (en "wrappant" les méthodes). ça risque d'être bordélique à gérer.. il faut faire un système de communication entre les 2 threads suffisement fiables pour ne pas générer de plantages (au cas ou par exmple on appelle très rapidement 2 méthodes et que la premièr n'a pas terminée, etc...) bref.. comment faire de l'asynchrone très simplement ?  
 
j'aimerai aussi pouvoir gérer la progression du download... susi-je obligé dès alors d'utiliser les fonctions de plus bas niveau genre CFtpConnection::OpenFile et CFtpConnection::Write ?? pas moyen de faire plus simple aussi ? (je sais je suis un feignant :p !)

 

[jfdsdjhfuetppo]--Message édité par ZZZzzz le 30-04-2002 à 14:32:21--[/jfdsdjhfuetppo]

Reply

Marsh Posté le 30-04-2002 à 20:42:08    

bon j'ai fait entre temps le système "OpenFile(), Write, etc..." (cété pas long finalement ;) ) et je crée un thread commme un barbare avant d'appeler la fonction PutFile  de ma classe mais c pas très propre...

Reply

Marsh Posté le 30-04-2002 à 20:42:08   

Reply

Marsh Posté le 01-05-2002 à 14:14:09    

:bounce:

Reply

Marsh Posté le 02-05-2002 à 10:44:38    

Harkonnen, j'ai besoin de ton aide :cry: ;) !!

Reply

Marsh Posté le 02-05-2002 à 11:03:51    

Oui, je t'oublie pas  :D  
mais ma machine perso est en vrac, et je ne peux te répondre que du boulot, où j'utilise C++ builder et non VC++
un peu de patience, le temps que je remette ma machine en état :)
ce soir ou demain
 :hello:

Reply

Marsh Posté le 02-05-2002 à 19:40:40    

ok pas de probs.. de toute manière j'ai un peu tout chambooulé le code.. donc ce n'est pas trop la peine de t'attarde sur le code que j'ai posté ... je crois que j'ai trouvé un truc qui devrait bien fonctionner (même si à mon aivs c un peu du "bourrin" ). Je crée un thread avant d'appeler de faire des transferts de fichiers sur le serveur FTP et tant que celui-ci n'est pas terminé j'interdit tout autre commande FTP.. je me demande d'ailleurs comment régiraient les APIs  MFC (de l'objet CFtpConnection) si j'appelais plusieurs méthode d'une même instance en même temps... mais de toute manière à partir du moment où il y a une seule connection (car une seule instance de CFtpConnection) on ne peut faire qu'une commande FTP à la fois sur le serveur...

Reply

Marsh Posté le 03-05-2002 à 12:20:56    

j'ai encore un problème... j'essaie d'epxlorer récursivement un répertoire sur le serveur FTP mais j'ai l'impression qu'il ne peut pas y avoir plusieurs instances de l'objet CFtpFindFile en mémoire...  
 
voila la fonction en question:
 
"// fonction récursive appelée par RemoveDirectory, retourne 0 si succès
HRESULT CFtp::RecRemoveDirectory(const char* RemoteDirectory) {
 
 // on se place dans le repertoire cible distant
 JI_ERROR(m_pFtpConnection->SetCurrentDirectory(RemoteDirectory));
 
 CFtpFileFind MyFtpFileFind(m_pFtpConnection);
 
 if(MyFtpFileFind.FindFile(NULL)) {
  HRESULT rval;
  do {
   rval = MyFtpFileFind.FindNextFile();
 
   CString CFileName = MyFtpFileFind.GetFileName();
   char* FileName = CFileName.GetBuffer(1);
msg("file to delete:" );
msg(FileName);
   if(MyFtpFileFind.IsDirectory()) { // c un rep
    if( (strcmp(FileName, "." ))&&(strcmp(FileName, "./" ))&&
     (strcmp(FileName, ".." ))&&(strcmp(FileName, "../" )) ) {
      if(RecRemoveDirectory(FileName)) return E_FAIL;
    }
   }
   else {
    JI_ERROR(m_pFtpConnection->Remove(FileName)); // c un fichier
   }
  } while(rval);
 }
else {
Msg(Error, "Cannot find files in %s", RemoteDirectory);
msg(Error);
}
 
   
 // on remonte l'arborescence d'un niveau pour revenir au répertoire parent de RemoteDirectory
 JI_ERROR(m_pFtpConnection->SetCurrentDirectory(".." ));
 
 // et on supprime le repertoire que l'on viens de visiter
 JI_ERROR(m_pFtpConnection->RemoveDirectory(RemoteDirectory));
 
 return 0;
}
"
 
 
en fait je lance la fonction sur un rep "essai/" qui contient plusieurs fichiers et repertoires: les fichiers sont supprimés mais dès que la fonction est appelé récursivement sur un sous-repertoire j'obtiens le message d'erreur "Cannot find files in dir" même si le dis répertoire (dir) n'est pas vide...
alors comme cela fonctionne ua niveau du répertoire père j'ai l'impression que le problème viens du fait que je crée un autre CFtpFilefind... que dois-je faire ? rendre le CFtpFileFind global ou static ?? mais si je fais ça... dès que je vais appelé la méthode FileFind dans un sous-répertoire... il va me "flusher" la recherche du répetoire père... comment faire alors ???

 

[jfdsdjhfuetppo]--Message édité par ZZZzzz le 03-05-2002 à 12:28:07--[/jfdsdjhfuetppo]

Reply

Marsh Posté le 03-05-2002 à 13:41:20    

raah.. je perds mon temps sur cette fichu fonction, j'ai essayé de passer par un tableau de char dans lequel je retrouve les reps. et files avec les foncs strtok, etc.. mais encore une fois strtok() utilise des variables static et ça fout la merde dans du récursif... rahH.. j'en peux plus. que faire !

Reply

Marsh Posté le 03-05-2002 à 14:06:45    

ma machine est complètement en croix  :gun: je vais devoir reformater
je vais essayer de tester ton source sur la bécane d'un copain pendant le week end

Reply

Marsh Posté le 12-05-2002 à 00:04:16    

apparement tu as oublier de tester, mais c pas grave.. depuis le temps j'ai fait mes propres fonctions à la place de strtok. J'ai trouvé une solution finalement simple (à laquelle je pensais dès le début mais qui me semblait assez compliqué): c de crée un thread dès le début afin que toutes les commandes FTP soient exécutées en asynchrones. je comunique avec le thread "fils" grâce à une variable partagée qui contient le numéro de la commande courante à exécuter ( = 0 si pas de commande), la syncrhonisatin est très simple: seul le thread "fils" peut remettre ce numéro à 0 après avoir exécuter une commande tandis que seul le thread "père" peut changer ce numéro si celui-ci est à 0. J'ai aussi prévu une fonction qui tue le thread "fils" dans le cas d'un éventuel bloquage.. docn à priori je devrais pouvoir m'en sortir seul, merci kan même !

Reply

Marsh Posté le 12-05-2002 à 09:35:20    

sinon tu as mon K-FTP

Reply

Marsh Posté le 13-05-2002 à 19:10:13    

euh.. sur ton site c marqué que c un shareware, mais aussi qu'il est gratuit .. je comprend pas trop. je vois qu'il a été programmé en VB, donc il recquiert probablement des dll VB pour fonctionner je pense ?  
 
malheureusement je dois faire un activeX qui propose certaines fonctions "avancées" comme la copie ou supression d'un répertoire, ou obtenir son contenu... je ne pense pas que le tiens propose cela, de plus il ne semble pas supporter d'être inséré dans un projet Director (ce qui est le but de mon activeX).  
 
Je te remerci en tout cas de me l'avoir proposé (et c sympa l'idée de proposer des activeX gratuits, la plupart sont payant et onéreux en plus).
 
J'ai une question, comment fonctionne en gros ton activeX ? se reconnecte-t-il au serveur pour chaque transferts de fichier (à chaque fois qu'on appelle la méthode) ou reste-t-il connecté ?  
comment as-tu coder (en gros, juste l'idée) la fonction 'abort' en asynchrone car j'ai justement un problème avec une fonction équivalente sur mon activeX (la fonction 'Disconnect() ) ?
 
Voilà la fonction en question:
 
"// Termine la connection courante et tue le thread
// cette fonction peut-être appelé lorsque les commandes FTP ne répondent plus
// ou échouent à cause d'une erreur grave (déconnection du serveur, etc...)
// on peu alors relancer la connection en toute sécurité (à priori)
void CNTVFTPActiveXCtrl::Disconnect()
{
 // TODO: Add your dispatch handler code here
 
 BOOL KillThread = FALSE;
 DWORD ExitCode = 0;
 
 // pas de thread donc pas connecté à priori mais onenvoie kan même l'évenement
avec rval=-1
 if(!hThread) {
  MyFtp.CloseConnect();
  FireFTPCommandFinished(-1, DISCONNECT, "" );
  return;
 }
 
 // pas moyen de récupérer le status: on tue le thread
 if(!GetExitCodeThread(hThread, &ExitCode)) {
  KillThread = TRUE;
#ifdef _DEBUG
msg("pas moyen de récupérer le code du thread !" );
#endif
 }
 else {
  if(ExitCode == STILL_ACTIVE) {
   // obligé de tuer le thread si une commande est en cours...
   if(CurrentCommand != NOTHING) KillThread = TRUE;
  }
  else { // le thread est déjà tué... ya plus qu'à déconnecter
   MyFtp.CloseConnect();
   FireFTPCommandFinished(0, DISCONNECT, "" );
   hThread = NULL;
   return;
  }
 }
 
 if(KillThread) {
  TerminateThread(hThread, -1);
#ifdef _DEBUG
msg("Thread Tué !" );
#endif
  MyFtp.CloseConnect();
  FireFTPCommandFinished(0, DISCONNECT, "" );
 }
 else {
  CurrentCommand = DISCONNECT;
 
  int i = 0;
  do { // on attends 3 secondes (c normalement amplement suffisant pour se
déconnecter
   Sleep(2000); // on laisse le temps au thread concurrent de s'exécuter
   GetExitCodeThread(hThread, &ExitCode);
   i++;
  } while( (ExitCode == STILL_ACTIVE)&&(i < 3) );
 
  if(ExitCode == STILL_ACTIVE) { // faut tuer le thread et forcer la
déconnection
   TerminateThread(hThread, -1);
#ifdef _DEBUG
msg("Thread Tué 2 !" );
#endif
   MyFtp.CloseConnect();
   FireFTPCommandFinished(0, DISCONNECT, "" );
  }
 }
 
 hThread = NULL;
}
"
 
 
en fait lorsque j'appelle cette fonction pour me déconnecter, je reçois systématiquement le message "Thread tué 2" car la fonction d'exécution du thread est gelé durant l'attente que celui-ci se termine .. malgré le 'Sleep(2000)'. Comment faire pour permettre au thread de s'exécuter durant l'appel à cette fonction.. j'ai peur que ce ne soit pas possible...
 
voici la fonction du thread (threadproc):
 
"
// Le thread qui execute les commandes FTP
DWORD WINAPI ThreadProc(LPVOID lpParameter) {
 
 BOOL EndThread = FALSE;
 DWORD rval;
 CString Result;
 CNTVFTPActiveXCtrl* myCtrl = (CNTVFTPActiveXCtrl*)lpParameter;
 
 Result = "";
 
 if(!myCtrl) { ExitThread(-1); return -1; }
 
    do {
  switch(myCtrl->CurrentCommand) {
  case PUTFILE:
   rval = myCtrl->MyFtp.PutFile(myCtrl->Param1, myCtrl->Param2);
   Result = "File sent";
   break;
     case PUTDIRECTORY:
   rval = myCtrl->MyFtp.PutDirectory(myCtrl->Param1, myCtrl->Param2);
   break;
     case REMOVEFILE:
   rval = myCtrl->MyFtp.RemoveFile(myCtrl->Param1);
   break;
     case REMOVEDIRECTORY:
   rval = myCtrl->MyFtp.RemoveDirectory(myCtrl->Param1);
   break;
     case SETREMOTEDIRECTORY:
   rval = myCtrl->MyFtp.SetRemoteDirectory(myCtrl->Param1, myCtrl->Param5);
   break;
     case GETREMOTEDIRECTORY:
   rval = myCtrl->MyFtp.GetRemoteDirectory(Result);
   break;
     case GETREMOTEDIRECTORYCONTENT:
   rval = myCtrl->MyFtp.GetRemoteDirectoryContent(myCtrl->Param1, Result);
   break;
     case CONNECT:
   rval = myCtrl->MyFtp.Connect(myCtrl->Param1, myCtrl->Param2, myCtrl->Param3, myCtrl->Param4, myCtrl->Param5);
   break;  // le thread vient d'être crée
     case DISCONNECT:
   myCtrl->MyFtp.CloseConnect(); rval= 0;
   Result = "disconnected";
   EndThread = TRUE;
   // normalement après l'exécution de cette commande, le thread se termine
   break;  
 
  default: case NOTHING:  
   Sleep(1000); // on laisse du temps à l'OS pour exécuter les autres threads et processus
 
  }
 
#ifdef _DEBUG
 char ch[200];
 sprintf(ch, "currentcommand = %d .\n", myCtrl->CurrentCommand);
 fwrite(ch, 1, strlen(ch), myCtrl->file);
#endif
 
  if(myCtrl->CurrentCommand != NOTHING) {
   myCtrl->FireFTPCommandFinished(rval, myCtrl->CurrentCommand, Result);
   myCtrl->CurrentCommand = NOTHING;
  }
 
 } while(!EndThread);
   
 ExitThread(0);
 return 0;
}
"
 
merci d'avance !!

Reply

Marsh Posté le 13-05-2002 à 19:31:16    

ZZZzzz a écrit a écrit :

euh.. sur ton site c marqué que c un shareware, mais aussi qu'il est gratuit .. je comprend pas trop. je vois qu'il a été programmé en VB, donc il recquiert probablement des dll VB pour fonctionner je pense ?  
 
malheureusement je dois faire un activeX qui propose certaines fonctions "avancées" comme la copie ou supression d'un répertoire, ou obtenir son contenu... je ne pense pas que le tiens propose cela, de plus il ne semble pas supporter d'être inséré dans un projet Director (ce qui est le but de mon activeX).  
 
Je te remerci en tout cas de me l'avoir proposé (et c sympa l'idée de proposer des activeX gratuits, la plupart sont payant et onéreux en plus).
 
J'ai une question, comment fonctionne en gros ton activeX ? se reconnecte-t-il au serveur pour chaque transferts de fichier (à chaque fois qu'on appelle la méthode) ou reste-t-il connecté ?  
comment as-tu coder (en gros, juste l'idée) la fonction 'abort' en asynchrone car j'ai justement un problème avec une fonction équivalente sur mon activeX (la fonction 'Disconnect() ) ?




 
Mon ActiveX fait exactement ce que tu demande. Effectivement il a été fait en VB et requiert les VB runtimes pour fonctionner.
 
Il se déconnecte après chaque transfert (Envoie QUIT. puis déconnecte). Il permet de download/uploader (actif ou passif), de rappatrier un liste de contenu, de supprimer...
 
Il est dispo en shareware (10 Euros pour une licence pour tous mes softs) ou en adware (c'est un organisme de pub, RedV, qui vous offre la licence)

Reply

Marsh Posté le 13-05-2002 à 20:37:28    

http://curl.haxx.se/libcurl/
 
libcurl is a free and easy-to-use client-side URL transfer library, supporting FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE and LDAP. libcurl supports HTTPS certificates, HTTP POST, HTTP PUT, FTP uploading, kerberos, HTTP form based upload, proxies, cookies, user+password authentication, file transfer resume, http proxy tunneling and more!  
 
libcurl is highly portable, it builds and works identically on numerous platforms, including Solaris, Net/Free/Open BSD, Darwin, HPUX, IRIX, AIX, Tru64, Linux, Windows, Amiga, OS/2, BeOs, Mac OS X, Ultrix, QNX, OpenVMS, RISC OS and more...

Reply

Marsh Posté le 14-05-2002 à 00:16:06    

merci à vous je regarderai ça demain, ceci dis j'ai déjà programmé la plupart des mes fonctions: ça m'embêterai finalement d'utiliser le travail d'un autre et d'avoir donc bosser pour rien.  
 
kyle_katarn> juste par curiosité, sais-tu si ton activeX supporte d'être inséré dans un projet director une fois que l'on a les dites runtimes VB ? je pense que c à cause de ça que ça ne marche pas chez moi... et pour la fonction abort() tu ne m'as pas expliqué ;) ? il me semble que l'on peut utiliser la méthode CInternetFile::Close() ou un truc du genre...
 
youdon'tcare> n'as-tu pas une idée du pourquoi l'exécution d'un thread (en l'occurence le "père" ) empêche celle du "fils" (j'entends pas là un thread ayant été crée dans le "père" ) même avec un 'Sleep()'... comment windows gère-t-il le multi-thread ? est-ce que l'exécution d'une méthode de l'activeX (thread "père" ) gèle-t-il forcément l'exécution du thread "fils" ? comment peut-on attendre alors que le thread "fils" se termine dans une méthode de l'activeX (il me semble que j'ai déjà été confronté au prob. et que j'avais trouvé une solution) ?

Reply

Marsh Posté le 14-05-2002 à 00:38:06    

ZZZzzz >> je ne m'y connais pas bien en threading et encore moins en threading activex. la seule fois où j'en ai eu besoin, j'ai repompé du code : utiliser une interface activex dans un thread = CoMarshalInterThreadInterfaceInStream() et CoGetInterfaceAndReleaseStream(), tu vois à quel point c'est bô et élégant :/ ... essaye de demander dans un newsgroup COM.

 

[jfdsdjhfuetppo]--Message édité par youdontcare le 14-05-2002 à 00:39:11--[/jfdsdjhfuetppo]

Reply

Marsh Posté le 14-05-2002 à 09:58:16    

oulà en effet... dans la doc le descriptif de la fonc c:
 
"
CoMarshalInterThreadInterfaceInStream(
  REFIID riid,     //Reference to the identifier of the interface
  LPUNKNOWN pUnk,  //Pointer to the interface to be marshaled
  LPSTREAM * ppStm //Address of output variable that receives the  
                   // IStream interface pointer for the marshaled  
                   // interface
);
"
 
ils disent que ça permet de "marshaler" une fonciton (cool !! keske ça veut dire ?...). Comment se servir de cette fonction ? le param pUnk ça serait quoi dans mon cas ? le pointeur du contrôle activeX (le param "this" que je passe lors de la cration de l'activeX en fait).. je suis un peu perdu, je vais tenter de trouver la solution sur les groups...

Reply

Marsh Posté le 14-05-2002 à 10:27:58    

marshalling = utiliser un objet qui n'est pas dans le même thread / dans le même process / sur la même machine. (enfin, je crois ... moi aussi c'est flou [:dawa] )
 
tu as regardé http://msdn.microsoft.com/library/ [...] cation.asp
 
et tout particulièrement l'exemple ftptree qui a l'air de faire ce que tu veux ?

 

[jfdsdjhfuetppo]--Message édité par youdontcare le 14-05-2002 à 10:28:25--[/jfdsdjhfuetppo]

Reply

Marsh Posté le 14-05-2002 à 20:13:44    

oui j'ai déjà regardé, c à partir de ça que j'ai fait mon activeX mais FTPTree ne fonctionne pas en ASYNC je crois...
 
mais c bon j'ai trouvé la solution.. en fait j'ai modifié ma fonction Disconnect() et ThreadProc() de telle manière que ThreadProc n'essaie plus d'envoyer un évenement (et donc d'accéder à une méthode de l'activeX alors que la méthode eDisconnect() est en cours d'exécution, apparement c ça qui bloquait) lorsquu 'CurrentCommand = DISCONNECT': à la place il se termine tout simplement et c la méthode Disconnect() qui termine la connection...  
le problème c ke si il y avait une commande en cours je tue directement le thread... c un peu bourrin et ça peu sûrement poser des problèmes !
 
merci de ton aide !

Reply

Marsh Posté le 15-05-2002 à 00:12:50    

:bounce: up !

Reply

Marsh Posté le 15-05-2002 à 00:34:26    

tu tues le thread comment ? à la main ? normalement il faut une condition d'arrêt dans le thread en lui-même. qu'est-ce qui t'empêche de faire une condition d'arrêt ?

Reply

Marsh Posté le 16-05-2002 à 00:56:09    

j'ai une condition d'arrêt: c lorsque je déconnecte, je demande au thread de se terminer (via un flag commun que je passe à TRUE). cependant si une commande est en cours d'exécution dans le thread (le numéro de la commande est stocké dans une variable partagée aussi) alors je le tue comme une brute. c bourrin mais ça présente l'avantage de proposer une solution à un éventuel bloquage (une commande qui ne se termine pas)..
 
cependant j'ai encore des problèmes... des corruptions de mémoires divers lorsque je ferme l'activeX ou durant l'exécution de commandes.. je sais pas trop pourquoi mais ça me prend vraiment la tête ! ma "gestion" multi-thread est vraiment à chier il faut dire ... si tu veux je posterai un bout de code demain pour que tu voies ça.

Reply

Marsh Posté le 16-05-2002 à 01:00:25    

ZZZzzz a écrit a écrit :

si tu veux je posterai un bout de code demain pour que tu voies ça.


comme je l'ai dit plus haut je suis une quiche en multithreading ! :D
 
pas de réponses côté newsgroups ?

Reply

Marsh Posté le 16-05-2002 à 09:50:02    

non pas vraiment ce que je veux, c un cas assez spécifique de programmation.. c du multi-threading avec envoie d'évenements activeX (les fonctions 'Fire...()' ) et je ne trouve rien en rapport... j'y connais rien non plus, j'avais déjà fait un activeX multi-thread avec des évenements et ça marchait assez bien mais il n'y avait quasiement pas de communication à faire et ça fonctionnait bien... j'avais ptêt eu du bol ;) !  
je vais essayer de trouver des docs mais si ça se trouve le code le bug viens d'ailleurs...  
 
ce que je me demande kan même c si lorsque j'envoie un évenement quelcnque (FireCommandfinish()) par exemple dans une méthode de l'activeX et que cet évenement est traité dans le conteneur par l'appelle d'une nouvelle méthode cela peut-il provoquer un bloquage ? au sens ou l'appel de l'évenement dans la méthode ne rend la main qu'une fois donné que dans le conteneur le traitement de l'évenement (et donc le corps de la fonc. évenement en fait) est terminé ? ça me semble peu probable mais certaines réactions bizarres de mon activeX laisse à penser cela ? peut-être que kk'un connait les mécanismes de ces évenements ...

Reply

Marsh Posté le 16-05-2002 à 09:52:53    

jviens déjà de trouver un premier élément de réponse:
 
"I have designed an ActiveX control (in VC++/MFC) that launches one
worker thread. This thread waits for some external conditions to fire an
event which is handled by a VB application which includes the control.
Unfortunately, when many events are triggered, the system either hangs
in the OLE event transmission chain or ends up in a memory access error.
I assume this as much to do with the fact that an event is
asynchronously triggered from the worker thread. If you think I am
right, I would like to know if there anything I can do to synchronize my
events to VB being able to accept them properly or else to make VB
multi-thread compatible. Thank you.
Message 2 in thread  
De :Alexander Nickolov (agnickolov@geocities.com)
Objet :Re: Multi-threaded ActiveX control  
Groupes de discussion :comp.lang.basic.visual, comp.os.ms-windows.programmer.ole, comp.lang.basic.visual.misc, microsoft.public.vb.ole, microsoft.public.win32.programmer.ole, microsoft.public.fr.vb, microsoft.public.vb.controls
View this article only  
Date :2000/04/14  
 
 
Just abide by the COM rules - don't call the event interface directly
from your worker thread. See the COM/ATL FAQ in my signature,
the first question for possible solutions.
"

Reply

Marsh Posté le 16-05-2002 à 10:22:12    

"Solutions. There are three possible solutions (described in ATL terms, but they are appropriate for straight C++ COM coding too):
 
The easiest solution is to create a hidden window upon the object's construction in FinalConstruct (it is not a good idea to put such code in the constructor). Then whenever you need to raise an event, you post a message to that window instead. The message handler then fires the event. The drawback is that you have to package any arguments and unpackage them in the message handler. An additional benefit is that unlike the other approaches, this way the worker thread is immediately ready to continue with the next task - asynchronous notifications. This approach is made possible by the rule that all STA threads must have a message loop (in this case implemented by VB).  
 
Rewrite the implementation of IConnectionPointImpl::Advise and Unadvise and forward the call to the worker thread somehow. The implementation uses CoMarshalInterThreadInterfaceInStream and the worker thread uses CoGetInterfaceAndReleaseStream to marshal the interface pointer to the worker thread. Then the marshaled pointer is stored in the map instead of the direct pointer from the client. The worker thread must enter an apartment (MTA or create new STA). The advantage of this approach is that the code generated by the ATL connection points wizard doesn't change. The drawback is that the events must be fired from the worker thread's apartment only (so if you have multiple worker threads you better enter the MTA in all of them). The worker thread is suspended for the duration of the call.  
 
This one is a generalized version of the second. Instead of explicitly marshaling the interface pointer for a specific apartment up front, the code in the Advise method is modified to use the Global Interface Table to store the interface and a cookie is stored in the map instead. Whenever any thread wants to fire an event, the cookie is used to temporarily obtain an interface pointer for the current apartment then the event is fired and the interface pointer is released. The drawback (with ATL in mind) is that in addition to the IConnectionPointImpl code, you have to modify the code for the proxy generated by the ATL wizard. The advantage is that events can be fired from any apartment. All threads which fire events must enter an apartment. The thread firing the event is blocked for the duration of the call.  
"
 
on retrouve la fonction que tu m'avais donné youdon'tcare "CoMarshalInterThreadInterfaceInStream" .. mais comme je comprend pas grand chose aux méthodes 2 et 3, je vais ptêt me contenter de balancer des messages (j'y pensais déjà en fait)

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed