New class TUpdate

New class TUpdate

Postby StefanHaupt » Mon Dec 31, 2012 2:24 pm

Hi friends,

last month I needed an easy possibility to update my application over the internet. I found some specialized functions, but nothing for common use. So I wrote my own new class to do this job. It´s very easy to use, you can integrate it in every application.

It´s working fine with XP, Windows 7, Windows 8 is not tested.

I publish this code, so everyone can benefit from this new class. Maybe you´ll find it useful.

Any comments are welcome :)

Code: Select all  Expand view  RUN
/*
   Class TUpdate - application update over ftp
   --------------------------------------------------
   (based on a function from Biel Maimo)

   Version 1.2
   (c) Stefan Haupt 2012

   Description: This is a claas to update your application over a Ftp-Server.
                Itïs very easy to use, I think the code is self explaining. You
                just need some vars to be set, the update is automatically done.
                That means your application is closed, the files are copied and
                your application is restarted.
   Sample:
           FUNCTION Update ()

             LOCAL oUpdate
             LOCAL cFtp  := "YourFtpServer"
             LOCAL cUser := "YourLoginName"
             LOCAL cPW   := "YourPassword"
             LOCAL cFtpDir   := "YourUpdateFolderOnFtp"
             LOCAL cUpdFile  := "NameOfTheUpdatefile"
             LOCAL cLocalDir := "NameOfTheLocalDir"
             LOCAL nFlags := <SpecialConnectionFlags> // use 134217728 to set passive mode

             oUpdate := TUpdate():New(cFtp, cUser, cPW, cFtpDir, cUpdFile, cLocalDir+"Updates\")
             oUpdate:nFlags := nFlags
             oUpdate:Update ()
             oUpdate:End ()

           RETURN (nil)
   ****************************************************************************
   Remarks: If you want to use the passive mode in your ftp connection, you have
   to update the class TFtp that comes with fwh.
   Just replace the method new with this one:

       METHOD New( cFTPSite, oInternet, cUserName, cPassword, nFlags ) CLASS TFTP

         DEFAULT nFlags := 0

         ::oInternet = oInternet
         ::cSite     = cFTPSite
         ::cUserName = cUserName
         ::cPassword = cPassword

         if oInternet:hSession != nil
            ::hFTP = InternetConnect( oInternet:hSession, cFTPSite, FTP_PORT,;
                                      ::cUserName, ::cPassword,;
                                      INTERNET_SERVICE_FTP, nFlags, 0 )
            AAdd( oInternet:aFTPs, Self )
         endif

      return Self

   ****************************************************************************/


#include "FiveWin.ch"
//#include "xBrowse.ch"

#define INTERNET_FLAG_PASSIVE  0x08000000  // used for FTP connections - 134217728
#define ZTRIM( cString ) Left( cString, At( Chr( 0 ), cString ) - 1 )


CLASS TUpdate

DATA oInternet AS OBJECT
DATA oFtp      AS OBJECT
DATA nFlags    AS NUMERIC

DATA cIP     AS CHARACTER         // Ftp-Server
DATA cUser   AS CHARACTER         // login name
DATA cPW     AS CHARACTER         // password

DATA cFtpFolder AS CHARACTER      // folder on ftp where update files are
DATA cZipFile   AS CHARACTER      // name of the update file (must be zip)

DATA cLocalDir  AS CHARACTER      // local folder where the update files are copied
DATA cAppDir    AS CHARACTER      // applications folder
DATA cAppFile   AS CHARACTER      // name of the application

DATA aUpdateFiles AS ARRAY        // files in the zip
DATA cUpdateBatch AS CHARACTER    // path and name of the update batchfile

//DATA lRestartApp AS LOGICAL INIT .f.

DATA nError INIT 0                // common error

METHOD New () CONSTRUCTOR         // create a new instance, initialize all vars
METHOD End ()                     // close the connection
METHOD Update ()                  // update the application

//METHOD Setup ()        HIDDEN     // all other methods are only for internal use
METHOD Download ()     HIDDEN
METHOD DownloadFile () HIDDEN
METHOD UnpackFile ()   HIDDEN
METHOD WriteBatch ()   HIDDEN

ENDCLASS

//----------------------------------------------------------------------
METHOD New (cIP, cUser, cPW, cFTPFolder, cZIPFile, cLocalDir, nFlags) CLASS TUpdate

  DEFAULT cIP := "localhost",;
          cUser := "anonymous",;
          cPW := "anonymous@localhost",;
          cFtpFolder := "/",;
          cZipFile := "",;
          cLocalDir := cFilePath( GetModuleFileName( GetInstance() ) ) + "Updates\"  ,;
          nFlags := 0

  ::cIP := cIp
  ::nFlags := nFlags   // special flags for ftp, eg. passive mode
  ::cUser := cUser
  ::cPW := cPW
  ::cFtpFolder := cFtpFolder
  ::cZipFile := cZipFile

  ::cAppFile := GetModuleFileName( GetInstance() )
  ::cAppDir  := cFilePath (GetModuleFileName( GetInstance() ) )
  ::cLocalDir := cLocalDir
  ::cUpdateBatch := ::cLocalDir + "
Update.cmd"

  IF !IsDir (::cLocalDir)                       // create updatefolder
    IF (::nError := MakeDir (::cLocalDir)) != 0
      MsgAlert ("
Updateverzeichnis konnte nicht erstellt werden","Programmaktualisierung")
    ENDIF
  ENDIF

  IF !IsAdmin ()                               // n
    ::nError := 1
    MsgAlert ("
Sie benötigen Administratorrechte für dieses Update","Programmaktualisierung")
  ENDIF

RETURN (self)


//----------------------------------------------------------------------
METHOD End ()

  LOCAL bClose := {|| ::oFtp:END(), ::oInternet:END()}

  IF ::nError = 0
    MsgRun ("
Verbindung beenden...",,bClose)
  ENDIF

RETURN (nil)


//----------------------------------------------------------------------
METHOD Update () CLASS TUpdate

  LOCAL cFile, nSize, dDate, cTime, aTime
  LOCAL cFtpFile, dFtpDate, cFtpTime, nFtpSize
  LOCAL aF := {}, aFiles:={}
  LOCAL lIsFile := .f.  // update file exists ?

  LOCAL aUpdate := {}, lSuccess := .f.
  LOCAL bConnect := {|| ::oInternet := tInternet():New(),;
                        ::oFtp      := tFtp():New (::cIp, ::oInternet, ::cUser, ::cPW, ::nFlags) }

  IF ::nError != 0
    Return (nil)
  ENDIF

  IF !Empty (::cIP)

    CursorWait ()
    MsgRun ("
Verbindung aufbauen...",,bConnect)
    CursorArrow ()

    IF Empty (::oFtp:hFtp)
      MsgStop ("
Verbindung zum Server gescheitert", "Programmaktualisierung")
      //::oFtp:END()
      //::oInternet:END()
    ELSE

      ::oFtp:SetCurrentDirectory( ::cFtpFolder )
//     cDir := oFtp:GetCurrentDirectory()
      aFiles := ::oFtp:Directory (::cZipFile)     // all files in ftp folder

      IF !Empty (aFiles)
        AEval (aFiles, {|x| Aadd (aF, {ZTRIM (x[1]), x[2], x[3], x[4] } )} )

        cFtpFile := aF[1,1]   // filename
        dFtpDate := aF[1,3]   //
        cFtpTime := aF[1,4]   //
        nFtpSize := aF[1,2]   //

        cFile := ::cLocalDir + ::cZipFile // local file
        IF File (cFile)
          aTime := FileTimes ( cFile, 1 )
          dDate := CToD (Str( aTime[ 3 ], 2 ) + "
/" + StrZero( aTime[ 2 ], 2 ) + "/" + StrZero( aTime[ 1 ], 4 ))
          cTime := StrZero ( aTime[ 4 ], 2 ) + "
:" + StrZero( aTime[ 5 ], 2 ) + ":" + StrZero( aTime[ 6 ], 2 )
          nSize := FileSize ( cFile )
          lIsFile := .t.
        ENDIF

        IF !lIsFile .or. ;            // updatefile not present
           (dDate < dFtpDate).OR.;    // copmpare date and time
           (dDate == dFtpDate .AND. (TimeToSec (cTime ) < TimeToSec (cFtpTime) ) )

          IF MsgYesNo('Neue Version vorhanden, Aktualisierung durchführen ?',;
                      "
Überprüfung auf neue Programmversion")

            IF ::Download (cFtpFile , cFile, nFtpSize, dFtpDate, cFtpTime)
              ::oFtp:END()
              ::oInternet:END()
              IF ::WriteBatch ()
                CLOSE ALL
                WinExec (::cUpdateBatch)
                PostQuitMessage(0)
                QUIT
              ELSE
                MsgStop ("
Aktualisierung fehlgeschlagen", "Programmaktualisierung")
              ENDIF
            ENDIF

          ENDIF // MsgYesNo
        ELSE
          MsgInfo ("
Die Programmversion ist aktuell","Programmaktualisierung")
        ENDIF // IF (dDate < dFtpDate).OR

      ELSE
        MsgInfo ("
Die Programmversion ist aktuell","Programmaktualisierung")
      ENDIF // !Empty (aFiles)

    ENDIF  // Empty (::oFtp:hFtp)
  ENDIF // !Empty (::cIP)

RETURN (nil)



//----------------------------------------------------------------------
METHOD Download (cSource, cTarget, nSize, dDate, cTime) CLASS TUpdate

  LOCAL oDlg, oSay1, oSay2, oBtnCancel, oMeter1, oMeter2, nMeter1, nMeter2
  LOCAL lEnd:=.F., nAmount, lOk:=.F., lValRet:=.F.
  LOCAL hFile
  LOCAL cError1 := "
Fehler beim Download"
  LOCAL cError2 := "
Fehler beim Entpacken"

  // orange
  //  GRADIENT TRACK { { 1/2, nRGB( 198, 203, 213 ), nRGB( 219, 224, 233 ) },;
  //                              { 1/2, nRGB( 224, 238,237 ), nRGB( 224, 238,237 ) } } ;

  DEFINE DIALOG oDlg TITLE "
Programm-Aktualisierung" FROM 0,0 TO 10,50

   @ 0.5,01 SAY oSay1 PROMPT "
Dateien herunterladen" SIZE 80,8 OF oDlg
   @ 1.2,01 METEREX oMeter1 VAR nMeter1 SIZE 180,10 TOTAL nSize ;
                  GRADIENT TRACK { { 1/2, nRGB( 198, 203, 213 ), nRGB( 219, 224, 233 ) },;
                                 { 1/2, nRGB( 224, 238,237 ), nRGB( 224, 238,237 ) } } ;
                  OF oDlg

   @ 02 ,01 SAY oSay2 PROMPT "
Entpacken" OF oDlg
   @ 2.7,01 METEREX oMeter2 VAR nMeter2 SIZE 180,10 TOTAL 0 ;
                  GRADIENT TRACK { { 1/2, nRGB( 198, 203, 213 ), nRGB( 219, 224, 233 ) },;
                                 { 1/2, nRGB( 224, 238,237 ), nRGB( 224, 238,237 ) } } ;
                  OF oDlg

   @ 3.2,12 BUTTON oBtnCancel PROMPT "
&Abbrechen" ACTION ( lEnd := .t., SysRefresh(), oDlg:End() )

   oDlg:bStart := {|| lOk := ::DownloadFile ( cSource, nSize, oMeter1, @lEnd, oDlg, cTarget ),;
                      IIF (lOk, SetFDaTi (cTarget, dDate, cTime), MsgStop (cError1) ),;
                      IIF (lOk, lOk := ::UnPackFile (cTarget, oMeter2), ),;
                      IIF (lOk, (oBtnCancel:SetText( "
&Neu starten" ), oBtnCancel:bAction := {|| lEnd := .f., oDlg:End()} ),;
                                 MsgStop (cError2) ) }

   ACTIVATE DIALOG oDlg CENTERED

   IF !lEnd .AND. lOk
     lValRet:=.T.
   ENDIF

RETURN (lValRet)


//----------------------------------------------------------------------------------------
METHOD DownloadFile ( cSource, nSize, oMeter, lEnd, oDlg, cTarget ) CLASS TUpdate

   LOCAL oFile, hTarget, lValRet:=.F.
   LOCAL nBufSize,cBuffer,nBytes := 0, nTotal := 0//, nFile:=0
   LOCAL lRet := .f.

   nBufSize := 4096
   cBuffer  := Space(nBufSize)
   hTarget  := FCreate (cTarget)

   oFile := tFtpFile():New( cSource, ::oFtp )
   oFile:OpenRead()

   SysRefresh()

   WHILE  ( nBytes := Len( cBuffer := oFile:Read( nBufSize ) ) ) > 0 .and. !lEnd
     FWrite( hTarget, cBuffer, nBytes )
     nTotal += nBytes
     oMeter:Set( nTotal )
     SysRefresh()
   END

   FClose( hTarget )
   oFile:End()

   IF nTotal > 0
     lRet := (nTotal==nSize)
   ENDIF

RETURN (lRet)


//----------------------------------------------------------------------------------------
METHOD UnPackFile (cZip, oMeter) CLASS TUpdate

  LOCAL aUpdate :={}, aFiles := {}, aUnzip := {}, n := 1
  LOCAL lSuccess := .f.
  LOCAL bProgress := {|| oMeter:Set (n++) }
  LOCAL cPath :=  cFilePath (cZip)

  aUpdate := hb_GetFilesInZip( cZip, .t. )

  IF Len (aUpdate) > 0

    AEval (aUpdate, {|x| Aadd (aFiles, {x[1], x[6], x[7]} ) } )
    AEval (aUpdate, {|x| Aadd (aUnzip, x[1]) } )
    oMeter:nTotal:= Len (aUnzip)

    lSuccess := hb_UnZipFile( cZip  , ;
                              bProgress,; //
                              nil,;       // lWithSubDir
                              nil,;       // cPassword
                              cPath,;     // cZipDir
                              aUnzip ,;
                              nil )       // bFileProgress

    AEval (aFiles, {|x| SetFDaTi (cPath+x[1], x[2], x[3]) } )  // restore original date and time !!

  ENDIF

  ::aUpdateFiles := AClone (aUnzip)

RETURN (lSuccess)


//-------------------------------------------------------------------------------
METHOD WriteBatch () CLASS TUpdate

  LOCAL hBatch,i
  LOCAL cBatch, cCopy := "
", cDel := "", cS := ["]

  FErase (::cUpdateBatch)

  FOR i := 1 TO Len (::aUpdateFiles)
    cCopy += "Copy /Y /B /V " + cS + ::cLocalDir + ::aUpdateFiles[i] + cS + " " + cS + ::cAppDir + cS + " > NUL" + CRLF
    cDel  += "Del /F " + cS + ::cLocalDir + ::aUpdateFiles[i] + cS +  " >NUL" + CRLF
  NEXT

  cBatch := "@Echo off"+CRLF+;
            "echo Updating ..."+CRLF+;
            "ping -n 2 127.0.0.1 > NUL"+CRLF+;    // waiting 2 secs
            cCopy +;
            "Start " + cS + "update" + cS + " " + cS + ::cAppFile + cS + CRLF +;
            cDel +;
            "EXIT"

  hBatch := FCreate (::cUpdateBatch,0)
  FWrite (hBatch, cBatch)
  FClose (hBatch)

RETURN (FError() = 0)



// convert hours to seconds
//--------------------------------------------------------
STATIC FUNCTION  TimeToSec( cTime )

  local nSec := 0, nLen, i, aLim, aMod, nInd, n

if cTime == NIL
   nSec := seconds()
elseif HB_ISCHAR( cTime )
   nLen := len( cTime )
   if ( nLen + 1 ) % 3 == 0 .and. nLen <= 11
      nInd := 1
      aLim := { 24, 60, 60, 100 }
      aMod := { 3600, 60, 1, 1/100 }
      for i := 1 to nLen step 3
         if isdigit( substr( cTime, i,     1 ) ) .and. ;
            isdigit( substr( cTime, i + 1, 1 ) ) .and. ;
            ( i == nLen - 1 .or. substr( cTime, i + 2, 1 ) == ":" ) .and. ;
            ( n := val( substr( cTime, i, 2 ) ) ) < aLim[ nInd ]
            nSec += n * aMod[ nInd ]
         else
            nSec := 0
            exit
         endif
         ++nInd
      next
   endif
 endif

RETURN (Round( nSec, 2)) /* round FL val to be sure that you can compare it */


//----------------------------------------------------------------------
#pragma BEGINDUMP

#include <WinTen.h>
#include <Windows.h>
#include <mapiwin.h>
#include <hbApi.h>
#include <CommDlg.h>

extern LPSTR LToStr( long w );

                     //nTime 1=Last Update, 2=Last Acces, 3=Creation, defecto last update
HB_FUNC( FILETIMES ) // params cFileName, nTime --> { nYear, nMonth, nDay, nHour, nMin, nSec }
{
   LPSTR cFileName = hb_parc( 1 ) ;
   int nTime       = ( ISNUM( 2 ) ? hb_parni( 2 ) :  1 ) ; // defaults to 1

   FILETIME ftCreate, ftAccess, ftWrite ;
   SYSTEMTIME stTime ;
   BOOL bRet ;
   HANDLE hFile = CreateFile( cFileName, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) ;

   if( ! hFile )
      return ;

   GetFileTime( (HANDLE) hFile, &ftCreate, &ftAccess, &ftWrite ) ;

   switch( nTime )
   {
      case 1 : // last update
         FileTimeToSystemTime( &ftWrite, &stTime ) ;
         break ;
      case 2 : // last access
         FileTimeToSystemTime( &ftAccess, &stTime ) ;
         break ;
      case 3 : // creation
         FileTimeToSystemTime( &ftCreate, &stTime ) ;
         break ;
      default : // last update
         FileTimeToSystemTime( &ftWrite, &stTime ) ;
         break ;
   }

   SystemTimeToTzSpecificLocalTime( NULL, &stTime, &stTime ) ;
   CloseHandle( hFile ) ;
   hb_reta( 6 ) ;
   hb_storni( stTime.wYear,   -1, 1 ) ;
   hb_storni( stTime.wMonth,  -1, 2 ) ;
   hb_storni( stTime.wDay,    -1, 3 ) ;
   hb_storni( stTime.wHour,   -1, 4 ) ;
   hb_storni( stTime.wMinute, -1, 5 ) ;
   hb_storni( stTime.wSecond, -1, 6 ) ;
}


#define FA_RDONLY           1   /* R */
#define FA_HIDDEN           2   /* H */
#define FA_SYSTEM           4   /* S */
#define FA_LABEL            8   /* V */
#define FA_DIREC           16   /* D */
#define FA_ARCH            32   /* A */
#define FA_NORMAL           0

HB_FUNC(FILESIZE)

   {
   LPCTSTR szFile;
   DWORD dwFlags=FILE_ATTRIBUTE_ARCHIVE;
   HANDLE hFind;
   WIN32_FIND_DATA  hFilesFind;
      int iAttr;
      if (hb_pcount() >=1){
         szFile=hb_parc(1);
         if (ISNUM(2))      {
            iAttr=hb_parnl(2);
         }
         else{
         iAttr=63;
         }
            if( iAttr & FA_RDONLY )
               dwFlags |= FILE_ATTRIBUTE_READONLY;

            if( iAttr & FA_HIDDEN )
               dwFlags |= FILE_ATTRIBUTE_HIDDEN;

            if( iAttr & FA_SYSTEM )
               dwFlags |= FILE_ATTRIBUTE_SYSTEM;
            if( iAttr & FA_NORMAL )
               dwFlags |=    FILE_ATTRIBUTE_NORMAL;

            hFind = FindFirstFile(szFile,&hFilesFind);
                  if (hFind != INVALID_HANDLE_VALUE){
                      if (dwFlags & hFilesFind.dwFileAttributes) {
                         if(hFilesFind.nFileSizeHigh>0)
                              hb_retnl((hFilesFind.nFileSizeHigh*MAXDWORD)+hFilesFind.nFileSizeLow);
                         else
                              hb_retnl(hFilesFind.nFileSizeLow);
                       }
                   else
                           hb_retnl(-1);
                     }

         }
}


HB_FUNC (ISADMIN)

{

  HANDLE hToken;
  PTOKEN_GROUPS pGroupInfo;
  DWORD dwSize = 0, dwResult;
  DWORD nError = 0, i;
  BOOL lError, lAdMin = FALSE;
  LPSTR cFunc = "";
  PSID   psidAdmin;
  CHAR cMess[200];
  SID_IDENTIFIER_AUTHORITY SystemSidAuthority= SECURITY_NT_AUTHORITY;

  if ( lError = (! OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&hToken) ))
    {
     cFunc = "OpenProcessToken";
     nError = GetLastError();
     if (nError == ERROR_CALL_NOT_IMPLEMENTED)
       {
        hb_retl( TRUE );
        return;
       }
    }

  if ( ! lError &&  ! GetTokenInformation(hToken, TokenGroups, NULL, dwSize, &dwSize))
    {
      dwResult = GetLastError();
      if( lError=(dwResult != ERROR_INSUFFICIENT_BUFFER ))
       {
        nError = dwResult;
        cFunc = "GetTokenInformation";
       }
    }

  if ( ! lError )
   {
    pGroupInfo = (PTOKEN_GROUPS) GlobalAlloc( GPTR, dwSize );
    if( lError = (! GetTokenInformation(hToken, TokenGroups, pGroupInfo, dwSize, &dwSize ) ))
      {
        nError = GetLastError();
        cFunc = "GetTokenInformation";
      }
   }

  if ( ! lError )
    if ( lError = (! AllocateAndInitializeSid ( &SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdmin) ))
     {
      nError = GetLastError();
      cFunc = "AllocateAndInitializeSid";
     }

  if ( ! lError )
    {
      for( i=0; i<pGroupInfo->GroupCount; i++)
       {
          if ( EqualSid(psidAdmin, pGroupInfo->Groups[i].Sid) )
           {
               lAdMin = TRUE;
               break;
           }
       }
    }
  else
    {
      cMess[0]=0;
      lstrcat(cMess,"Error calling ");
      lstrcat(cMess,cFunc);
      lstrcat(cMess,": ");
      lstrcat(cMess,LToStr(nError));
      MessageBox(GetActiveWindow(),cMess,"Attention", MB_OK);
    }

  if (psidAdmin)
      FreeSid(psidAdmin);

  if ( pGroupInfo )
      GlobalFree( pGroupInfo );

  CloseHandle( hToken );
  hb_retl( lAdMin );

}

#pragma ENDDUMP
 
Last edited by StefanHaupt on Thu Jan 03, 2013 11:00 am, edited 1 time in total.
kind regards
Stefan
StefanHaupt
 
Posts: 824
Joined: Thu Oct 13, 2005 7:39 am
Location: Germany

Re: New class TUpdate

Postby Adolfo » Mon Dec 31, 2012 2:26 pm

Stefan

Thanks for your work, gonna try it NEXT YEAR :-)

Greetings

From Chile
Adolfo
;-) Ji,ji,ji... buena la cosa... "all you need is code"

http://www.xdata.cl - Desarrollo Inteligente
----------
Asus TUF F15, 32GB Ram, 2 * 1 TB NVME M.2, GTX 1650
User avatar
Adolfo
 
Posts: 860
Joined: Tue Oct 11, 2005 11:57 am
Location: Chile

Re: New class TUpdate

Postby hua » Wed Jan 02, 2013 3:21 am

Thanks for sharing Stefan :)
FWH 11.08/FWH 19.12
BCC5.82/BCC7.3
xHarbour/Harbour
hua
 
Posts: 1072
Joined: Fri Oct 28, 2005 2:27 am

Re: New class TUpdate

Postby richard-service » Wed Jan 02, 2013 8:03 am

Thanks a lot. I got it.
Best Regards,

Richard

Harbour 3.2.0dev (r2402101027) => Borland C++ v7.7 32bit
MySQL v8.0 /ADS v10
Harbour 3.2.0dev (r2011030937) => Borland C++ v7.4 64bit
User avatar
richard-service
 
Posts: 804
Joined: Tue Oct 16, 2007 8:57 am
Location: New Taipei City, Taiwan

Re: New class TUpdate

Postby James Bott » Wed Jan 02, 2013 3:27 pm

Stephan,

Thanks for sharing this.

Could you explain how this works? Can it be called from a menu in an app, and if so, what then happens?

Can we somehow check for a new version from a running app?

Regards,
James
User avatar
James Bott
 
Posts: 4840
Joined: Fri Nov 18, 2005 4:52 pm
Location: San Diego, California, USA

Re: New class TUpdate

Postby RAMESHBABU » Thu Jan 03, 2013 8:32 am

Mr.James,

I checking for latest version at the start up of the application.

Regards,

-Ramesh Babu P
User avatar
RAMESHBABU
 
Posts: 625
Joined: Fri Oct 21, 2005 5:54 am
Location: Secunderabad (T.S), India

Re: New class TUpdate

Postby StefanHaupt » Thu Jan 03, 2013 10:58 am

James,

James Bott wrote:Could you explain how this works? Can it be called from a menu in an app, and if so, what then happens?


It´s very easy to use. You can call it from inside your application. This is a small sample how this class can be called.

Code: Select all  Expand view  RUN
FUNCTION Update ()

             LOCAL oUpdate
             LOCAL cFtp  := "YourFtpServer"
             LOCAL cUser := "YourLoginName"
             LOCAL cPW   := "YourPassword"
             LOCAL cFtpDir   := "YourUpdateFolderOnFtp"
             LOCAL cUpdFile  := "NameOfTheUpdatefile"
             LOCAL cLocalDir := "NameOfTheLocalDir"
             LOCAL nFlags := <SpecialConnectionFlags> // flag to set passive mode

             oUpdate := TUpdate():New(cFtp, cUser, cPW, cFtpDir, cUpdFile, cLocalDir+"Updates\")
             oUpdate:nFlags := nFlags
             oUpdate:Update ()
             oUpdate:End ()

           RETURN (nil)


It creates a subdir (called "Updates" by default) in the folder you specified with <cLocalDir>. There the update file is copied und unpacked. The update file has to be a .zip file. By default the update folder is located in the .exe dir. Take care that you must have the right to create files in that folder.

Can we somehow check for a new version from a running app?


Yes, of course. You can check for a new version at any time while your app is running. With the function above everything is done. The class automatically checks if there is a new version. If so, you are asked to install it, otherwise there is a message that there is no upate available.

First the update method checks if there is an update file on the ftp server and compares the date and time with the last existing file in the local update folder. If there is no existing file (first update) or the local file is older, the update is done.

The update process itself creates a batch, closes your application, copies the files and restarts your application.

Some ftp server need to set the passive mode. You can do this by setting oUpdate:nFlag to 134217728 (0x08000000). But if you want to use this feature you must change the method new of the class TFtp that comes with fwh.

Code: Select all  Expand view  RUN
METHOD New( cFTPSite, oInternet, cUserName, cPassword, nFlags ) CLASS TFTP

         DEFAULT nFlags := 0

         ::oInternet = oInternet
         ::cSite     = cFTPSite
         ::cUserName = cUserName
         ::cPassword = cPassword

         if oInternet:hSession != nil
            ::hFTP = InternetConnect( oInternet:hSession, cFTPSite, FTP_PORT,;
                                      ::cUserName, ::cPassword,;
                                      INTERNET_SERVICE_FTP, nFlags, 0 )  // changed !
            AAdd( oInternet:aFTPs, Self )
         endif

      return Self
 


A little change in the sources, before the application is closed all dbf are closed ( CLOSE ALL inserted in line 211 after ::WriteBatch() ).
kind regards
Stefan
StefanHaupt
 
Posts: 824
Joined: Thu Oct 13, 2005 7:39 am
Location: Germany

Re: New class TUpdate

Postby Antonio Linares » Thu Jan 03, 2013 12:16 pm

very good, thanks for sharing :-)
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
 
Posts: 42122
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain

Re: New class TUpdate

Postby James Bott » Thu Jan 03, 2013 3:24 pm

Stephan,

Thanks a lot for that very detailed explanation. I hope to try it soon.

Regards,
James
User avatar
James Bott
 
Posts: 4840
Joined: Fri Nov 18, 2005 4:52 pm
Location: San Diego, California, USA

Re: New class TUpdate

Postby TimStone » Thu Jan 03, 2013 4:31 pm

This is a nice contribution. However, I handled this process a bit differently because not everyone is eligible for an update. I wrote an auto update routine which runs in the background all of the time in the folder where the database files are stored. It works this way.

1) Every 3 hours query the server and download a "version file". Compare to see if it is greater than the current version.
2) Check the customer's status. If they are eligible, then download and unpack the zip file to its update directory.
3) Copy any files that need to be moved to the main directory ( server ).
4) When a "client" program starts, it checks the local server to see if a newer version is available. If so, it copies the newer main .exe file to the local machine, then starts the program. Otherwise, it starts the existing program.

This is the least disruptive process. Often updates to the server are downloaded and run overnight. The customer gets a pop up notice anytime they have received a newer version.

In addition, the auto update program also does automatic FTP transfers to remote servers of data used by 3rd party services, and also emails the company's customers automatically with Thank You notes and service reminders. It resides in the system tray, and has a popup menu for forcing updates. The system is also used to maintain authorizations for customers, especially useful in rentals, and quick tracking of who is eligible for updates.

TIm
Tim Stone
http://www.MasterLinkSoftware.com
http://www.autoshopwriter.com
timstone@masterlinksoftware.com
Using: FWH 23.10 with Harbour 3.2.0 / Microsoft Visual Studio Community 2022-24 32/64 bit
User avatar
TimStone
 
Posts: 2945
Joined: Fri Oct 07, 2005 1:45 pm
Location: Trabuco Canyon, CA USA

Re: New class TUpdate

Postby StefanHaupt » Fri Jan 04, 2013 12:27 pm

Hi Tim,

many thanks for your comment.

TimStone wrote:I wrote an auto update routine which runs in the background all of the time in the folder where the database files are stored.

That´s very similar to this class, just that it doesn´t run in background. But I can also use a timer, so the update function is called automatically.

1) Every 3 hours query the server and download a "version file".

How much updates do you offer a day ? Surely, it depends on the kind of software you are selling, but for my application it´s enough to check for an update when the application starts. Everybody who needs a higher frequency can use a timer.

I also thought of using a version file, but for the moment I decided against it. However it offers the possibility to make a distinction between updates (bugfixes) and upgrades (new version). So, maybe I´ll implement this in future versions.

2) Check the customer's status. If they are eligible, then download and unpack the zip file to its update directory.

That´s a problem, I fully agree. How do you solve it ? Sql database, text file ?

I personally differentiate between updates and upgrades. An update is a bugfixed version, every customer should be eligible to get it. An upgrade is new version with new functions. Customers have to pay for it, so only certain ones are eliglible to get it. I solved it with an encrypted configuration file, which contains all custumers data.

Your update mechanism works in a another but similar way, it´s specialised for your software. But it gives me some ideas how an update process can be realized. A good way to enhance my class.
kind regards
Stefan
StefanHaupt
 
Posts: 824
Joined: Thu Oct 13, 2005 7:39 am
Location: Germany

Re: New class TUpdate

Postby MarcoBoschi » Fri Jan 04, 2013 12:51 pm

I'll try it as soon as possible
King regards
marco
User avatar
MarcoBoschi
 
Posts: 1066
Joined: Thu Nov 17, 2005 11:08 am
Location: Padova - Italy

Re: New class TUpdate

Postby TimStone » Fri Jan 04, 2013 4:58 pm

Stefan,

When our company server is queried ( every 3 hours ), then it downloads a small file that has the latest version number, and a bulletin version number. That number is compared to the stored version number on the computer. If a new version is available, the system automatically downloads it to the server. The info on the local server is stored in a version info file and compared to the very small file downloaded.

The customer information is passed in a compacted, encrypted, file. It is also compared to the local data. We provide the customer name, address, phone, and some other data to the program, so if they make a change we are notified, and then we issue the change through our system. It cuts down on theft, and keeps us in touch.

We also have a blog, and when we make a new post, we push through the "version" of the new bulletin, and that generates a popup on the customer's workstation.

We query every 3 hours from the one machine alone. That program also does many other tasks as I mentioned. I usually put out an update early in a month, and then follow it with any needed adjustments ( bugs, awkward UI issues, spelling fixes, etc. ) immediately. Sometimes people suggest new reports and they are easy to build so I do it quickly and toss out an update.

It works out well, and customers feel like they are receiving value on an regular basis.

Tim
Tim Stone
http://www.MasterLinkSoftware.com
http://www.autoshopwriter.com
timstone@masterlinksoftware.com
Using: FWH 23.10 with Harbour 3.2.0 / Microsoft Visual Studio Community 2022-24 32/64 bit
User avatar
TimStone
 
Posts: 2945
Joined: Fri Oct 07, 2005 1:45 pm
Location: Trabuco Canyon, CA USA

Re: New class TUpdate

Postby Silvio.Falconi » Fri Jan 04, 2013 10:37 pm

Stephan,
a question
the test may work through or from connections to the proxy server?
Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)
I use : FiveWin for Harbour November 2023 - January 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
User avatar
Silvio.Falconi
 
Posts: 7075
Joined: Thu Oct 18, 2012 7:17 pm

Re: New class TUpdate

Postby StefanHaupt » Mon Jan 07, 2013 2:58 pm

Silvio,

I have no proxy, so I can´t test it. But it should work the same way with and without a proxy. You connect to the ftp server directly.
kind regards
Stefan
StefanHaupt
 
Posts: 824
Joined: Thu Oct 13, 2005 7:39 am
Location: Germany

Next

Return to FiveWin for Harbour/xHarbour

Who is online

Users browsing this forum: No registered users and 89 guests