There are allready a few solutions available, but none of them do fit my needs:
1.) I don't want to have an extra update.exe or something like this
2.) The checking and downloading should also work through a proxy and even redirected urls should be catched.
3.) Should be easy and short.
4.) threadsafe without threadsafe

5.) Should work with unicode also.
Because of point 2 and 3 i decided very early to go the api-way, so this is win-only.
This means, the user only has to make sure, that his/her internet explorer is configured correctly (with proxy settings or whatever) and the code should work as expected.
Here comes the main include:
Code: Select all
;/-----------------
;|
;| CheckForUpdates.pbi
;| V1.05 [01.06.2010]
;|
;| ©HeX0R 2009/2010
;|
;| Small include, which should make it
;| very easy, to give your applications
;| the power of an
;| Online Update
;|
;| for PB >= 4.0 (no DEMO)
;|
;| Needs the freely available
;| Online-Update-Creat0r
;| to create the online-info-files
;| (Just run example to get it)
;|
;| New since 1.04:
;| Error handling
;| (and also auto error logging)
;/-----------------
#CFU_IDENT = $6f3bc31a
Structure _CFU_VERSION_INFO_
Name.s ;Name of your Program
Version.s ;Version as String (for example "1.05.12")
URL.s ;Link, where Patch can be downloaded
FileSize.q ;Filesize of the patch
ReleaseDate.l ;Releasedate of the patch (unix timestamp)
MD5.s ;MD5 FileFingerprint of Patchfile
ServerFileName.s ;Real name of the Patch (if it is redirected for example)
InfoLink.s ;Link, to get further information
Text.s[3] ;Optional Texts, you maybe need
Number.l[3] ;Optional Long-Variables (for example used as Flags)
*Description ;Description of this update
EndStructure
Structure _CFU_THREAD_VALUES_
*URL
AuthMode.i
BlockSize.i
WindowHandle.i
Message.i
FileHandle.i
FileSize.q
StatusGadget.i
*Result
EndStructure
Structure _CFU_CallBack_Cookie_
*Buffer
Pointer.i
EndStructure
#CFU_NO_AUTH = $00
#CFU_VERSIONINFO_AUTH = $01
#CFU_FILEDOWNLOAD_AUTH = $02
Enumeration
#CFU_ERROR_NO_ERROR
#CFU_ERROR_CANT_CREATE_FILE
#CFU_ERROR_CANT_CREATE_THREAD
#CFU_ERROR_NO_MEMORY
#CFU_ERROR_NO_INTERNET
#CFU_ERROR_UNABLE_TO_OPEN_URL
#CFU_ERROR_UNABLE_TO_READ_ONLINEFILE
#CFU_ERROR_EMPTY_INFOFILE
#CFU_ERROR_NO_INFOFILE
EndEnumeration
CompilerIf Defined(CFU_LOG_ERROR, #PB_Constant) = 0
#CFU_LOG_ERROR = #True
CompilerEndIf
Prototype CFU_ErrorHandlerPrototype(ErrorNr)
Global *CFU_AuthBuffer
Global CFU_AuthBufferLength
Global CFU_AuthMode
Global CFU_LastError
Global CFU_ErrorHandler.CFU_ErrorHandlerPrototype = #False
Procedure CFU_InitErrorHandler(*Proc)
CFU_ErrorHandler = *Proc
EndProcedure
Procedure CFU_GetLastError()
Protected A = #CFU_ERROR_NO_ERROR
Swap A, CFU_LastError
ProcedureReturn A
EndProcedure
CompilerIf Defined(CFU_GetErrorMessage, #PB_Procedure) = 0
Procedure.s CFU_GetErrorMessage(Error = #PB_Default)
Protected Result.s = "No Error", Msg.s
If Error = #PB_Default
Error = CFU_GetLastError()
EndIf
Msg = Space(1024)
Select Error
Case #CFU_ERROR_CANT_CREATE_FILE
Result = "Unable to create File"
Case #CFU_ERROR_CANT_CREATE_THREAD
Result = "Unable to create Thread"
Case #CFU_ERROR_NO_MEMORY
Result = "Not enough Memory"
Case #CFU_ERROR_NO_INTERNET
Result = "WinINet-Init failed"
If FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError_(), 0, @Msg, 1024, 0)
Result + #CRLF$ + Msg
EndIf
Case #CFU_ERROR_UNABLE_TO_OPEN_URL
Result = "Unable to connect to URL"
If FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError_(), 0, @Msg, 1024, 0)
Result + #CRLF$ + Msg
EndIf
Case #CFU_ERROR_UNABLE_TO_READ_ONLINEFILE
Result = "Unable to open File on Server"
If FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError_(), 0, @Msg, 1024, 0)
Result + #CRLF$ + Msg
EndIf
Case #CFU_ERROR_EMPTY_INFOFILE
Result = "This seems to be an empty infofile"
Case #CFU_ERROR_NO_INFOFILE
Result = "The infofile doesn't exist on server!"
EndSelect
ProcedureReturn Result
EndProcedure
CompilerEndIf
Procedure CFU_LogError()
Protected FID, Error
If #CFU_LOG_ERROR
Error = CFU_GetLastError()
If Error <> #CFU_ERROR_NO_ERROR
If CFU_ErrorHandler
CFU_ErrorHandler(Error)
Else
If FileSize(GetEnvironmentVariable("APPDATA") + "\C4U\") <> -2
CreateDirectory(GetEnvironmentVariable("APPDATA") + "\C4U\")
EndIf
FID = OpenFile(#PB_Any, GetEnvironmentVariable("APPDATA") + "\C4U\ErrorLog.txt")
If FID
FileSeek(FID, Lof(FID))
WriteStringN(FID, FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss ", Date()) + CFU_GetErrorMessage(Error))
CloseFile(FID)
EndIf
EndIf
EndIf
EndIf
EndProcedure
Procedure CFU_Editor_StreamCallback(*dwCookie._CFU_CallBack_Cookie_, *pbBuff, cb, *pcb.LONG)
Protected Length
;/------
;| Callback used for CFU_Editor_Load()
;/------
Length = MemorySize(*dwCookie\Buffer) - *dwCookie\Pointer
If Length > cb
CopyMemory(*dwCookie\Buffer + *dwCookie\Pointer, *pbBuff, cb)
*pcb\l = cb
Else
If Length
CopyMemory(*dwCookie\Buffer + *dwCookie\Pointer, *pbBuff, Length)
EndIf
*pcb\l = Length
EndIf
*dwCookie\Pointer + *pcb\l
ProcedureReturn 0
EndProcedure
Procedure CFU_Editor_Load(Gadget, *Buffer)
Protected Stream.EDITSTREAM, C._CFU_CallBack_Cookie_
;/------
;| Procedure to get rtf-Data into an EditorGadget
;/------
If *Buffer And MemorySize(*Buffer)
C\Pointer = 0
C\Buffer = *Buffer
Stream\dwCookie = @C
Stream\pfnCallback = @CFU_Editor_StreamCallback()
SendMessage_(GadgetID(Gadget), #EM_STREAMIN, #SF_RTF, @Stream)
EndIf
EndProcedure
Procedure CFU_GetHTTPFile(*B._CFU_THREAD_VALUES_)
Protected hINet, hData, Bytes, Size, DownloadedBytes, f.f
;/------
;| Procedure to download something from the internet.
;| We use the WinAPI, because it makes sure
;| it will also work, when the user is behind a proxy.
;| (and he/she configures his/her IE correctly)
;| and even redirected files will be catched.
;|
;| This procedure is called as thread, to make sure
;| there won't be any lags, when the host is not reachable
;| or something like this.
;|
;| This procedure can download directly into memory or in a file.
;| See CFU_LoadInternetFile (in file) and CFU_LoadVersionInfo (in memory)
;/-------
*B\Result = 0
Size = 0
hINet = InternetOpen_(?GVI_Agent, 0, 0, 0, 0)
If Not hInet
CFU_LastError = #CFU_ERROR_NO_INTERNET
Else
;INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_PRAGMA_NOCACHE
If *B\AuthMode And *CFU_AuthBuffer And CFU_AuthBufferLength
hData = InternetOpenUrl_(hINet, *B\URL, *CFU_AuthBuffer, CFU_AuthBufferLength, $4000100, 0)
Else
hData = InternetOpenUrl_(hINet, *B\URL, 0, 0, $4000100, 0)
EndIf
If Not hData
CFU_LastError = #CFU_ERROR_UNABLE_TO_OPEN_URL
Else
If *B\BlockSize = 0
*B\BlockSize = 8192
EndIf
*B\Result = AllocateMemory(*B\BlockSize)
If Not *B\Result
CFU_LastError = #CFU_ERROR_NO_MEMORY
Else
Repeat
If InternetReadFile_(hData, *B\Result + Size, *B\BlockSize, @Bytes) = #False
FreeMemory(*B\Result)
*B\Result = 0
CFU_LastError = #CFU_ERROR_UNABLE_TO_READ_ONLINEFILE
Break
ElseIf *B\FileHandle = -1
Size + Bytes
*B\Result = ReAllocateMemory(*B\Result, Size + *B\BlockSize)
If Not *B\Result
CFU_LastError = #CFU_ERROR_NO_MEMORY
Break
EndIf
Else
If *B\StatusGadget <> -1 And IsGadget(*B\StatusGadget)
f = ((DownloadedBytes + Bytes) / *B\FileSize) * 100
SetGadgetState(*B\StatusGadget, f)
EndIf
If Bytes
WriteData(*B\FileHandle, *B\Result, Bytes)
DownloadedBytes + Bytes
EndIf
EndIf
Until Bytes = 0
EndIf
If *B\Result And *B\AuthMode And *CFU_AuthBuffer And CFU_AuthBufferLength
If CFU_AuthMode = #CFU_VERSIONINFO_AUTH Or (CFU_AuthMode & #CFU_FILEDOWNLOAD_AUTH And *B\AuthMode = #CFU_FILEDOWNLOAD_AUTH)
FreeMemory(*CFU_AuthBuffer)
*CFU_AuthBuffer = 0
CFU_AuthBufferLength = 0
CFU_AuthMode = #CFU_NO_AUTH
EndIf
EndIf
InternetCloseHandle_(hData)
EndIf
InternetCloseHandle_(hINet)
EndIf
If *B\FileHandle <> -1
CloseFile(*B\FileHandle)
EndIf
If *B\WindowHandle And *B\Message And IsWindow_(*B\WindowHandle)
PostMessage_(*B\WindowHandle, *B\Message, *B\Result, #Null)
EndIf
If *B\FileHandle <> -1 And *B\Result
FreeMemory(*B\Result)
EndIf
FreeMemory(*B\URL)
FreeMemory(*B)
CFU_LogError()
EndProcedure
Procedure CFU_CheckVersionInfo(*R._CFU_VERSION_INFO_, wparam, delete = #True)
Protected Size, i
;/------
;| After Versioninfo has been downloaded (CFU_LoadVersionInfo), you can feed
;| this procedure with the wParam-Value of the Event
;| If anything is correct, the CFU_VERSION_INFO-Structure will be filled
;| with the values.
;/------
If *R
If Not wparam
;No Info there, maybe an empty file?
CFU_LastError = #CFU_ERROR_EMPTY_INFOFILE
*R = 0
Else
If PeekL(wparam) <> #CFU_IDENT
;This is no correct Infofile!
CFU_LastError = #CFU_ERROR_NO_INFOFILE
*R = 0
Else
;Ok, lets fill the VERSION_INFO-Structure
Size + SizeOf(LONG)
*R\Name = PeekS(wparam + Size, -1, #PB_UTF8)
Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
*R\Version = PeekS(wparam + Size, -1, #PB_UTF8)
Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
*R\URL = PeekS(wparam + Size, -1, #PB_UTF8)
Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
*R\FileSize = PeekQ(wparam + Size)
Size + SizeOf(QUAD)
*R\ReleaseDate = PeekL(wparam + Size)
Size + SizeOf(LONG)
*R\MD5 = PeekS(wparam + Size, -1, #PB_UTF8)
Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
*R\ServerFileName = PeekS(wparam + Size, -1, #PB_UTF8)
Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
*R\InfoLink = PeekS(wparam + Size, -1, #PB_UTF8)
Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
For i = 1 To SizeOf(_CFU_VERSION_INFO_\Text) / SizeOf(STRING)
*R\Text[i - 1] = PeekS(wparam + Size, -1, #PB_UTF8)
Size + MemoryStringLength(wparam + Size, #PB_UTF8) + 1
Next i
For i = 1 To SizeOf(_CFU_VERSION_INFO_\Number) / SizeOf(LONG)
*R\Number[i - 1] = PeekL(wparam + Size)
Size + SizeOf(LONG)
Next i
;Check, if there is a description
If MemorySize(wparam) - Size > 0
*R\Description = AllocateMemory(MemorySize(wparam) - Size)
CopyMemory(wparam + Size, *R\Description, MemorySize(wparam) - Size)
Else
*R\Description = 0
EndIf
EndIf
EndIf
EndIf
If wparam And delete
;Normaly delete here, maybe someone wants to delete it later
FreeMemory(wparam)
EndIf
CFU_LogError()
ProcedureReturn *R
EndProcedure
Procedure CFU_LoadVersionInfo(Url.s, WindowHandle, Message, UserName.s = "", Password.s = "", AuthMode = #CFU_NO_AUTH)
Protected T, i, a$, b$, *B
Protected *TV._CFU_THREAD_VALUES_ = AllocateMemory(SizeOf(_CFU_THREAD_VALUES_))
;/------
;| Through CFU_LoadVersionInfo, you start the thread, which tries to
;| load the online versioninfo.
;| When the thread has finished, it will send the Message [Message] to [WindowHandle]
;| with wparam as Buffer.
;| This Buffer should then be sent to CFU_CheckVersionInfo(), to get the correct values.
;|
;| New since V1.03:
;| Username and Password for basic htaccess secured sites.
;| AuthMode is a combination of:
;| AuthMode = #CFU_NO_AUTH: Don't use Authorization
;| AuthMode = #CFU_VERSIONINFO_AUTH: Use Authorization when loading versioninfo
;| AuthMode = #CFU_FILEDOWNLOAD_AUTH: Use Authorization when loading patchfile
;/------
*TV\URL = AllocateMemory(StringByteLength(Url) + SizeOf(CHARACTER))
PokeS(*TV\URL, Url)
CFU_AuthBufferLength = 0
If *CFU_AuthBuffer
FreeMemory(*CFU_AuthBuffer)
EndIf
If AuthMode > #CFU_NO_AUTH And UserName
CFU_AuthMode = AuthMode
*B = AllocateMemory(StringByteLength(UserName + ":" + Password) + 1)
PokeS(*B, UserName + ":" + Password, -1, #PB_Ascii)
i = (Len(UserName) + Len(Password)) * 2
b$ = Space(i)
Base64Encoder(*B, MemorySize(*B), @b$, i * SizeOf(CHARACTER))
a$ = "Authorization: Basic " + PeekS(@b$, -1, #PB_Ascii)
FreeMemory(*B)
*CFU_AuthBuffer = AllocateMemory(StringByteLength(a$) + SizeOf(CHARACTER))
PokeS(*CFU_AuthBuffer, a$)
CFU_AuthBufferLength = Len(a$)
Else
CFU_AuthMode = #CFU_NO_AUTH
EndIf
*TV\WindowHandle = WindowHandle
*TV\Message = Message
*TV\BlockSize = 0
*TV\AuthMode = CFU_AuthMode & #CFU_VERSIONINFO_AUTH
*TV\FileHandle = -1
*TV\StatusGadget = -1
T = CreateThread(@CFU_GetHTTPFile(), *TV)
If Not T
FreeMemory(*TV\URL)
FreeMemory(*TV)
CFU_LastError = #CFU_ERROR_CANT_CREATE_THREAD
EndIf
CFU_LogError()
ProcedureReturn T
EndProcedure
Procedure CFU_LoadInternetFile(Url.s, DestinationFile.s, WindowHandle, Message, FileSize, StatusbarGadget = -1, BlockSize = $10000)
Protected T, FID
Protected *TV._CFU_THREAD_VALUES_
;/------
;| This procedure will download the patchfile to the destination
;/------
FID = CreateFile(#PB_Any, DestinationFile)
If Not FID
CFU_LastError = #CFU_ERROR_CANT_CREATE_FILE
Else
*TV = AllocateMemory(SizeOf(_CFU_THREAD_VALUES_))
*TV\URL = AllocateMemory(StringByteLength(Url) + SizeOf(CHARACTER))
PokeS(*TV\URL, Url)
*TV\WindowHandle = WindowHandle
*TV\Message = Message
*TV\BlockSize = BlockSize
*TV\AuthMode = CFU_AuthMode & #CFU_FILEDOWNLOAD_AUTH
*TV\FileHandle = FID
*TV\FileSize = FileSize
*TV\StatusGadget = StatusbarGadget
T = CreateThread(@CFU_GetHTTPFile(), *TV)
If Not T
FreeMemory(*TV\URL)
FreeMemory(*TV)
CloseFile(FID)
CFU_LastError = #CFU_ERROR_CANT_CREATE_THREAD
EndIf
EndIf
CFU_LogError()
ProcedureReturn T
EndProcedure
DataSection
;Mozilla/4.0 (compatible; ST)
GVI_Agent:
Data.l $697A6F4D, $2F616C6C, $20302E34, $6D6F6328, $69746170, $3B656C62, $29545320
Data.b 0
EndDataSection
Code: Select all
;/-------------
;| Example for the
;| CheckForUpdates.pbi
;|
;| Should be easy to understand
;| and also easy to integrate
;| in own applications.
;|
;| PB >= 4.0 (no DEMO)
;|
;| ©HeX0R 2009
;/-------------
XIncludeFile "CheckForUpdates.pbi"
InitNetwork()
#URL_OF_INFOFILE = "http://h3x0r.ath.cx/Sonstiges/example.dat"
;Our VersionInfo Variable
Global VI._CFU_VERSION_INFO_
;Two new Events
Enumeration #WM_USER + 2
#EVENT_VERSION_INFO_RECEIVED
#EVENT_DOWNLOAD_FINISHED
EndEnumeration
Enumeration
#Window_Main
;
;If you want to use the OpenWindow_NewUpdate-Procedure in your own prog
;you have to integrate the following Windowconstant into your main window enumeration:
#Window_NewUpdate
EndEnumeration
Enumeration
#Button_Start_Check
#Editor_0
;
;If you want to use the OpenWindow_NewUpdate-Procedure in your own prog
;you have to integrate the following Gadgetconstants into your main gadget enumeration:
#Button_Update_Load
#Button_Update_Link
#Button_Update_Close
#ProgressBar_Update
EndEnumeration
Procedure.s ByteRechner(dSize.d)
Protected Namen = $4B4D4754, i = 24, C.c, Result.s = "0"
If dSize > 0.0
While dSize > 1024 And i >= 0
dSize / 1024
C = (Namen >> i) & $FF
i - 8
Wend
Result = StrD(dSize, 2) + " " + Chr(C) + "Byte"
EndIf
ProcedureReturn Result
EndProcedure
;/ New Update found Window Example
Procedure OpenWindow_NewUpdate(Parent = 0)
Protected Gadget
OpenWindow(#Window_NewUpdate, 0, 0, 300, 350, "New update found!", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_WindowCentered, Parent)
CompilerIf #PB_Compiler_Version < 430
CreateGadgetList(WindowID(#Window_NewUpdate))
CompilerEndIf
TextGadget(#PB_Any, 5, 5, 100, 20, "Name:")
StringGadget(#PB_Any, 105, 3, 190, 22, VI\Name, #PB_String_ReadOnly)
TextGadget(#PB_Any, 5, 30, 100, 20, "Version:")
StringGadget(#PB_Any, 105, 28, 190, 22, VI\Version, #PB_String_ReadOnly)
TextGadget(#PB_Any, 5, 55, 100, 20, "ReleaseDate:")
StringGadget(#PB_Any, 105, 53, 190, 22, FormatDate("%dd.%mm.%yyyy %hh:%ii", VI\ReleaseDate), #PB_String_ReadOnly)
TextGadget(#PB_Any, 5, 80, 100, 20, "FileSize:")
StringGadget(#PB_Any, 105, 78, 190, 22, ByteRechner(VI\FileSize), #PB_String_ReadOnly)
TextGadget(#PB_Any, 5, 105, 100, 20, "Info:")
Gadget = EditorGadget(#PB_Any, 5, 130, 290, 165, #PB_Editor_ReadOnly)
ButtonGadget(#Button_Update_Load, 5, 300, 70, 22, "Download", #PB_Button_Default)
If Left(LCase(VI\InfoLink), 7) = "http://"
ButtonGadget(#Button_Update_Link, 115, 300, 70, 22, "Info")
EndIf
ButtonGadget(#Button_Update_Close, 225, 300, 70, 22, "Close")
ProgressBarGadget(#ProgressBar_Update, 5, 325, 290, 20, 0, 100)
;Activate WordWrap in EditorGadget
SendMessage_(GadgetID(Gadget), #EM_SETTARGETDEVICE, 0, 0)
;Check, if there is any Description
If VI\Description
CFU_Editor_Load(Gadget, VI\Description)
FreeMemory(VI\Description)
EndIf
EndProcedure
Procedure MyCallBack(Window, Msg, wparam, lparam)
Protected Result = #PB_ProcessPureBasicEvents
;Here are our two new Window-events
;those Messages get sent through PostMessage, so you will see
;them also in your mainloop.
;But i decided to do it inside the callback because of two reasons:
;
; 1.) EventwParam() will be obsolete and removed someday (maybe)
; 2.) There is a bug, when user moves the window:
; http://www.purebasic.fr/english/viewtopic.php?t=36027
; So it is possible, that messages never will arrive
Select Msg
Case #EVENT_VERSION_INFO_RECEIVED
If CFU_CheckVersionInfo(@VI, wParam)
;Ok, we have some info!
;Now we should check if MV\Version is newer then our Version
;But as long as this is just an example, we go on and show the window
OpenWindow_NewUpdate(WindowID(#Window_Main))
EndIf
Case #EVENT_DOWNLOAD_FINISHED
If wParam
AddGadgetItem(#Editor_0, -1, "Download finished!, File should be here:")
AddGadgetItem(#Editor_0, -1, GetTemporaryDirectory() + VI\ServerFileName)
;Check integrity of downloaded file
If MD5FileFingerprint(GetTemporaryDirectory() + VI\ServerFileName) = VI\MD5
;Now run Patch (well, this time, this is no patch)
RunProgram(GetTemporaryDirectory() + VI\ServerFileName)
;Send close message to own Program
SendMessage_(WindowID(#Window_Main), #WM_CLOSE, #Null, #Null)
Else
AddGadgetItem(#Editor_0, -1, "File seems to be corrupted!")
EndIf
Else
AddGadgetItem(#Editor_0, -1, "Something went wrong with the download!")
EndIf
DisableGadget(#Button_Update_Load, 0)
EndSelect
ProcedureReturn Result
EndProcedure
Procedure main()
OpenWindow(#Window_Main, 0, 0, 400, 400, "VersionCheck-Test", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
CompilerIf #PB_Compiler_Version < 430
CreateGadgetList(WindowID(#Window_Main))
CompilerEndIf
SetWindowCallback(@MyCallBack(), #Window_Main)
ButtonGadget(#Button_Start_Check, 140, 5, 120, 22, "Start Check Now")
EditorGadget(#Editor_0, 5, 35, 390, 360)
;Main Loop
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Select EventWindow()
Case #Window_Main
Break
Case #Window_NewUpdate
CloseWindow(#Window_NewUpdate)
EndSelect
Case #PB_Event_Gadget
Select EventGadget()
Case #Button_Start_Check
;Start the Check-Thread
CFU_LoadVersionInfo(#URL_OF_INFOFILE, WindowID(#Window_Main), #EVENT_VERSION_INFO_RECEIVED)
;We will get informed through a #EVENT_VERSION_INFO_RECEIVED Event, when info download is finished
;Integrate those three buttons into your main eventloop:
Case #Button_Update_Link
RunProgram(VI\InfoLink)
Case #Button_Update_Close
CloseWindow(#Window_NewUpdate)
Case #Button_Update_Load
;Start the download
If CFU_LoadInternetFile(VI\URL, GetTemporaryDirectory() + VI\ServerFileName, WindowID(#Window_Main), #EVENT_DOWNLOAD_FINISHED, VI\FileSize, #ProgressBar_Update)
;We will get informed through a #EVENT_DOWNLOAD_FINISHED Event, when file download is finished
DisableGadget(#Button_Update_Load, 1)
EndIf
EndSelect
EndSelect
ForEver
EndProcedure
main()
End
With this you can create your own info-files and can upload them via ftp (and also the patchfile).

It should be selfexplaining.
Have Fun