OT: Brainstorming: How to handle sw-updates

Re: OT: Brainstorming: How to handle sw-updates

Postby Otto » Mon Dec 14, 2009 5:51 pm

Hello James

>I mean if the exe is in use by a workstation which does not log out.

OK, I assume you mean this would be a problem when trying to update the EXE? If so, then you have a worse problem than when using local copies. You have no way of knowing if local copies are in use. You could update the server copy, but then you have the situation that users running the local version during the update would prevent DBF modifications.


I have a control.dbf on the network data drive where every workstation logs in and locks ‘his’ record.
The station which executes the dbf-file update must first try to lock the control.dbf successfully (flock - oControl:flock() ).

Or, if there were no DBF modifications, you still could have some users running the old version while users running the app after the server update would be running the new version.


We have a version field in the control.dbf. Every exe checks against this control.dbf. If the version number does not correspond the exe does not start.

Many users will leave a major app running for days so some users may not get the update for days.
If all users are running the server EXE, then everyone must be logged out before the server EXE can be updated. This also allows you to make DBF modifications. You can also be sure all users are using the same version at all times.


I would like to have the exe on the server. But my design is not made for that.
I have a system with many exe files. I have a design that I use for nearly every main task (clients, bills, plan, etc. ) an own exe file.
Therefore and if it is only some seconds on start this is a problem because I open and close very much the exe files.

Otto
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6029
Joined: Fri Oct 07, 2005 7:07 pm

Re: OT: Brainstorming: How to handle sw-updates

Postby José Luis Sánchez » Mon Dec 14, 2009 5:57 pm

Hello,
See this two post fron Biem Maimo. They describe an automatic update using an FTP system.

http://bielsys.blogspot.com/2009/02/act ... ca-de.html
http://bielsys.blogspot.com/2009/04/act ... ca-de.html

Regards,
José Luis
User avatar
José Luis Sánchez
 
Posts: 539
Joined: Thu Oct 13, 2005 9:23 am
Location: Novelda - Alicante - España

Re: OT: Brainstorming: How to handle sw-updates

Postby Otto » Mon Dec 14, 2009 6:02 pm

Otto,

>What if a user does not switch his PC on before 20.1.?

This is solved by running all EXEs from the server.

James


Hello James,
and if nobody - because they are all on holidays – enters before the 20.1.
This is not a joke.
You must keep in mind that most of my clients have only 1 or 2 workstations and sometimes not always a dedicated fileserver.
It could happen that some users don’t operate the PC for some times.
Some of my clients have their business only open during the seasons.
Best regards,
Otto
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6029
Joined: Fri Oct 07, 2005 7:07 pm

Re: OT: Brainstorming: How to handle sw-updates

Postby Otto » Mon Dec 14, 2009 6:07 pm

Hello José,

thank you.

I started today with the coding of the download client.
I found the nearly ready solution published by Biel.

But there are some open questions, too.

Best regards,
Otto
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6029
Joined: Fri Oct 07, 2005 7:07 pm

Re: OT: Brainstorming: How to handle sw-updates

Postby James Bott » Mon Dec 14, 2009 6:09 pm

Otto,

Why so many EXEs? There was a time in the way past, when memory was such an issue that we had to resort to such things, but now you can run very large EXEs without problems.

>You must keep in mind that most of my clients have only 1 or 2 workstations and sometimes not always a dedicated fileserver.

Even without a dedicated server, you have to store the DBFs somewhere. If you store the EXEs in the same location and everyone runs the same EXEs then you don't have version problems.

To answer your question about insuring multiple EXE updates, you will need to check the date and time of all EXEs whenever the user starts any app EXE. This just seems like a lot of extra overhead and headache when using a single set of EXEs on a server (or workstation) solves the problem.

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

Re: OT: Brainstorming: How to handle sw-updates

Postby Otto » Mon Dec 14, 2009 6:31 pm

Hello James,

Why so many EXEs?

I think it is more clearly arranged.
In my case what does a DMS have to do with billing or reservation of rooms?
And if there is an error the whole system is blocked. If you have for every task
different exe files maybe only a report or something not so important is broken.
This is only my personal view and also only valid for my special situation.

when using a single set of EXEs on a server (or workstation) solves the problem

This would be really an advantage but as I said I am to fare away from that.

Best regards,
Otto
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6029
Joined: Fri Oct 07, 2005 7:07 pm

Re: OT: Brainstorming: How to handle sw-updates

Postby Otto » Mon Dec 14, 2009 6:37 pm

Hello James,
May I ask what size such a unique exe more or less would have.
A total update of all my files is about 60 MB.
Best regards,
Otto
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6029
Joined: Fri Oct 07, 2005 7:07 pm

Re: OT: Brainstorming: How to handle sw-updates

Postby James Bott » Mon Dec 14, 2009 8:39 pm

Otto,

Wow, 60MB!

I have some very large apps that are less than 2MB. They contain complete business systems: sales orders, purchase orders, invoicing, inventory, customers, vendors, and about 25 reports or so.

Of course, all my apps are written OOP style. This means I use a lot less code. E.G. an invoice is a subclass of a sales order. The sales order class is about 2800 lines of code (it is a very complex sales order), but the invoice is only 150 lines of code.

Also, keep in mind that there is a lot of overhead code in every EXE, so if you combine two 1MB EXEs you might get an 11MB EXE.

Resources in the EXEs will, of course, also create much larger EXEs.

How many EXEs do you have? You might (just as a test) try compiling a couple of them together just to see what the combined size is.

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

Re: OT: Brainstorming: How to handle sw-updates

Postby Otto » Mon Dec 14, 2009 9:32 pm

Hello James,
I see. My single exe’s are between 2 and 3 MB of size. But then there are word-templates with pictures, FastReport.dll, VRD and some bmp files.
Best regards,
Otto
********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6029
Joined: Fri Oct 07, 2005 7:07 pm

Re: OT: Brainstorming: How to handle sw-updates

Postby TimStone » Tue Dec 15, 2009 1:51 am

Otto,

I have three files that I want to update. One stays on the server ( to manage data ), and two go to the workstations ( the client .exe and the .chm file ). The files, individually, are 6.7 MB, 8.1 MB and 5.5 MB. However, when they are zipped for upload to my server, they are a total of 9.0 MB.

My client management software takes the latest version of the software, and puts into the one update file, then copies it to my host server.

My update system downloads the latest copy, unzips it, and places it in the proper locations.

I do not try to support multiple updates /version. I make the latest version available and everyone who is eligible gets it. If they don't pay for support, they won't get it. How much they use the system isn't important. If they shut it off for a few weeks, when they get back online, it will grab the latest version. I deserve to get paid because I'm still working on their behalf even if they didn't get an update or two ... everything is cumulative.

Also, I don't want to be supporting various versions. If a client is running an older version, and has a problem, the update will surely have the fix unless it is 100% user error. So the answer to their need is to install the update.

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: 2904
Joined: Fri Oct 07, 2005 1:45 pm
Location: Trabuco Canyon, CA USA

Re: OT: Brainstorming: How to handle sw-updates

Postby Otto » Tue Dec 15, 2009 9:13 pm

Flowchart “Update”
[*]Version- Check
[*]Check for and remove non finished updates
[*]Check if all files to update are closed
[*]Create sign "Update Running"
[*]Create download folder
[*]Download ZIP file
[*]extract / unzip
[*]move current files to archive folder and copy new files into current folder
[*]retry function with msg which error occurs and how to handle it
[*]remove temp files
[*]remove sign "Update Running”

This way I plan to make my new update client.
I post what I have so far.
Best regards,
Otto

Code: Select all  Expand view


/*
   Updating applications via FTP
   (c) 2008 Biel Maimo bmaimo@gmail.com - bielsys.blogspot.com
   FileTimes. basado sobre fuente publicacdo en foro FiveWin por Manuel Mercado
   Time2Sec De contrib de Harbour HbCt

*/


#include "FiveWin.ch"

#INCLUDE "BOX.CH"
#INCLUDE "FILEIO.CH"
#INCLUDE "directry.ch"


STATIC cDirlocal                                //Directorio local del ejecutable
STATIC cDstZip         := ""
STATIC cDirTempInstall := ""
STATIC cDirArchiv      := ""
//----------------------------------------------------------------------------//

FUNCTION Main()
   local cIp           := ""
   local cUser         := ""
   local cPW           := ""
   local cFTPFolder    := ""                    //'/UpdFtp/'
   local cZIPFile      := ""
   *----------------------------------------------------------

   //FTP - Setup
   cIp         := 'ftp.asdfa-software.com'
   cUser       := "so"
   cPW         := "adfasd"
   cFTPFolder  := "/asdfaso/downloads/"                
   cZIPFile    := "xCrmUpdate.zip"
   *----------------------------------------------------------

   //local Setup
   cDstZip          := "c:\x_zipUpdate"
   cDirTempInstall  := "c:\x_tempInstall"
   cDirLocal        := "c:\x_test"    
   cDirArchiv       := "c:\x_archiv"


   if lIsDir( cDirLocal ) =.F.
      lMKDir( cDirLocal )
   endif
   
   memowrit( cDirLocal + "\" + "upDateRunning.txt", dtoc(date()) + " " + time() )


   if lIsDir( cDstZip ) =.F.
      lMKDir( cDstZip )
   endif

   if lIsDir( cDirTempInstall ) =.F.
      lMKDir( cDirTempInstall )
   endif

   ChkUpdFtp( cIp, cFTPFolder , cUser, cPW ,cZIPFile )

   Unzip( cDstZip, cDirTempInstall, cZIPFile )

   InstallFiles( cDirTempInstall )

   DELETEDIR( cDirTempInstall )

   // DELETEDIR( cDirArchiv )

   ferase (cDirLocal + "
\" + "upDateRunning.txt")

   MsgInfo('Update succesful')
   

RETURN NIL
//----------------------------------------------------------------------------//

function InstallFiles( cDirTempInstall )
   LOCAL oDlg, oSay, lOk:=.F.

   DEFINE DIALOG oDlg TITLE "
Updating application" FROM 0,0 TO 10,50
   @ 1.5,03 SAY oSay  PROMPT "
Copied bytes:" OF oDlg

   oDlg:bStart := { || lOk := dir_recurs2( cDirTempInstall, oSay ), oDlg:END()  }

   ACTIVATE DIALOG oDlg CENTERED

RETURN NIL
//----------------------------------------------------------------------------//

function Unzip( cSrc, cDst, cZIPFile)
   local DCOM

   cSrc := cSrc + "
\" + cZIPFile

   DCOM  := '7Z.exe x '  + cSrc  + "
-o" +  cDst  + " *.* -r"

   WAITRUN(DCOM,1)
   SYSREFRESH()

return nil
//----------------------------------------------------------------------------//

STATIC FUNCTION ChkUpdFtp( cIp, cFTPFolder, cUser, cPW, cZIPFile )
   LOCAL oInternet, oFtp
   LOCAL  nSize2
   LOCAL aFiles := {}

   IF !Empty(cIP) .AND. !Empty(cFTPFolder)      //.AND. File( cDirLocal + 'ActVer.exe' )

      oInternet := tInternet():New()
      oFTP    := TFTP():New( cIp, oInternet,cUser, cPW  )

      IF !Empty( oFtp:hFtp)

        FtpSetCurrentDirectory(oFTP:hFTP, cFTPFolder )

         aFiles = oFTP:Directory( cZIPFile )

         nSize2:= aFiles[1,2]

         IF Len(aFiles)>0

            IF MsgYesno( 'A new version is available, Download it?' )
               IF fileUpdate(oFtp,Lower(aFiles[1,1]),aFiles[1,1],nSize2)
                  oFtp:END()
                  oInternet:END()
               ENDIF
            ENDIF
         ENDIF
      ENDIF

      oFtp:END()
      oInternet:END()

   ENDIF
RETURN NIL
//----------------------------------------------------------------------------//

STATIC FUNCTION fileUpdate(oFtp,cSource,cFileName,nSize)
   LOCAL oDlg, oSay, oBtnCancel, oMeter, lEnd:=.F., nAmount, lOk:=.F., lValRet:=.F.

   DEFINE DIALOG oDlg TITLE "
Updating application" FROM 0,0 TO 10,50
   @ 0.5,03 SAY oSay  PROMPT "
Copied bytes:" OF oDlg
   @ 02,01 METER oMeter VAR nAMount SIZE 180,20 TOTAL nSize OF oDlg

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

   oDlg:bStart := { || lOk := GetFile( cSource, nSize, oSay, oMeter, @lEnd, oDlg, oFTP ),;
      oBtnCancel:SetText( "
&Ok" ) ,oDlg:END()  }

   ACTIVATE DIALOG oDlg CENTERED

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

RETURN lValRet
//----------------------------------------------------------------------------------------

STATIC FUNCTION GetFile( cSource, nSize, oSay, oMeter, lEnd, oDlg, oFtp )
   LOCAL oFile, hTarget, lValRet:=.F.
   LOCAL nBufSize,cBuffer,nBytes, nTotal:=0,nFile:=0
   *----------------------------------------------------------

   nBufSize := 4096

   cBuffer := Space( nBufSize )

   *  oMeter:nTotal := nSize

   hTarget := FCreate( cDstZip + '' + cSource )

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

   WHILE  ( nBytes := Len( cBuffer := oFile:Read( nBufSize ) ) ) > 0 .and. ! lEnd
      FWrite( hTarget, cBuffer, nBytes )
      oSay:SetText( "
Bytes copiados: " + ;
         AllTrim( Str( nTotal += nBytes ) ) )
      oMeter:Set( nTotal )
      SysRefresh()
   END

   FClose( hTarget )
   oFile:End()
RETURN nTotal == nSize
//----------------------------------------------------------------------------//

static function dir_recurs2( cPath, oSay  )
   local x
   local aFiles    := directory( cPath + '\*.*', 'D' )
   local nFilCount := len( aFiles )
   local cZiel     := "
"
   local cTarget   := "
"
   *----------------------------------------------------------

   for x := 1 to nFilCount

      if aFiles[ X, F_NAME ] <> '..'

         if 'D' $ aFiles[ X, F_ATTR ]
            makeDirectory2(  cPath  )
         endif

         if aFiles[ X, F_ATTR ] <> 'D'
            cZiel   := STRTRAN( cPath + "
\" + aFiles[ X, F_NAME ], cDirTempInstall, cDirLocal )
            cTarget := STRTRAN( cPath + "
\" + aFiles[ X, F_NAME ], cDirTempInstall, cDirArchiv )

            oSay:SetText( "
Bytes copiados: " +  aFiles[ X, F_NAME ]  )

            if file( cZiel ) = .t.              // only if a file is present
               MOVEFILE( cZiel , cTarget )
            endif

            IF filcopyraw(  cPath + "
\" + aFiles[ X, F_NAME ], cZiel )= .f.
               Msginfo( "
Error beim Update" + CRLF + ;
                  "
Source  " + cPath + "\" + aFiles[ X, F_NAME ]+ CRLF + ;
                  "
Destination " + cZiel )
            endif

         endif

      endif

      if 'D' $ aFiles[ X, F_ATTR ]
         if aFiles[ X, F_NAME ] <> '.'
            dir_recurs2( cPath + '' + aFiles[ X, F_NAME ], oSay )
         endif
      endif
   next

return NIL
//----------------------------------------------------------------------------//

function makeDirectory2( cPfad )
   local n, cResult  := "
", nTimes := StrCharCount( cPfad, "\" ) + 1
   local cDst        := "
"

   if lIsDir( cDirArchiv) = .F.
      lMKDir(cDirArchiv)
   endif


   if lIsDir( cDirLocal) = .F.
      lMKDir(cDirLocal)
   endif

   for n = 1 to nTimes
      cResult := StrToken( cPfad, n, "
\" )

      if n > 2                                  //ohne Wurzel
         cDst := cDst + "
\" + cResult

         if lIsDir( cDirArchiv +  cDst) = .F.
            lMKDir(cDirArchiv  +  cDst)
         endif

         if lIsDir( cDirLocal +  cDst) = .F.
            lMKDir( cDirLocal  +  cDst)
         endif

      endif

   next

return nil
//----------------------------------------------------------------------------//

function  filcopyraw( cSrc, cDst )              //from NagesWaraRao

   local hSrc, hDst, nBytes
   local nBuf  := 64000
   local cBuf  := Space( nBuf )
   local lCopied := .f.

   if ( hSrc := FOpen( cSrc, 64 ) ) >= 0
      if ( hDst := FCreate( cDst, 0 ) ) >= 0
         do while .t.
            nBytes   := FRead( hSrc, @cBuf, nBuf )
            if nBytes > 0
               FWrite( hDst, cBuf, nBytes )
            endif
            if nBytes < nBuf
               lCopied  := .t.
               exit
            endif
         enddo
         fClose( hDst )
      endif
      fClose( hSrc )
   endif

return lCopied
//----------------------------------------------------------------------------//


FUNCTION DELETEDIR( cDir )

   LOCAL aDir, cName

   LOCAL i

   aDir = DIRECTORY( cDir + "
\*.*", "HRD" )

   FOR i = 1 TO LEN( aDir )
      cName = aDir[ i, F_NAME ]

      IF cName == "
."; LOOP; ENDIF
         IF cName == "
.."; LOOP; ENDIF

            cName = cDir + "
\" + cName

            IF "
D" $ aDir[ i, F_ATTR ]
               IF !DELETEDIR( cName )
                  RETURN .F.
               ENDIF
            ELSE
               IF FERASE( cName ) = -1
                  ? "
File cannot be deleted " + cName + "."
                  RETURN .F.
               ENDIF
            ENDIF
         NEXT

         IF !LRMDIR( cDir )
            ? "
Folder cannot be deleted " + cDir + "."
            RETURN .F.
         ENDIF

RETURN .T.

//----------------------------------------------------------------------------//


DLL32 FUNCTION MOVEFILE( cExistingFileName AS LPSTR, cNewFileName AS LPSTR ) AS BOOL PASCAL   FROM "
MoveFileA" LIB "kernel32.dll"

//----------------------------------------------------------------------------//

   #pragma BEGINDUMP
   #include <Windows.h>
   #include <mapiwin.h>
   #include <hbApi.h>
   //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);
                 }

              }
              }

              #pragma ENDDUMP




********************************************************************
mod harbour - Vamos a la conquista de la Web
modharbour.org
https://www.facebook.com/groups/modharbour.club
********************************************************************
User avatar
Otto
 
Posts: 6029
Joined: Fri Oct 07, 2005 7:07 pm

Previous

Return to FiveWin for Harbour/xHarbour

Who is online

Users browsing this forum: Google [Bot] and 16 guests