;---- 0 - BEGIN
;
;************************************************************************
;Titre      : myservices.pb
;Auteur     : (C) 2017 CAGE
;Date       : 2017/09/10
;Version PB : PureBasic 5.60 (Windows - x86)
;Version PB : PureBasic 5.60 (Windows - x64)
;Compiler Options
;Compiler Options : Create threadsafe executable
;Compiler Options : Enable moderm theme support (for Windows X and above)
;Compile/Run      : Create temporary executable in the source directory
;
;Libairies: aucunes
;
;https://github.com/deseven/pbsamples/tree/master/crossplatform/libcurl
;************************************************************************
;

;DisableDebugger ; disable the debugger checks

EnableExplicit ; toutes les variables doivent êtres déclarées

CompilerIf Not #PB_Compiler_Thread
  CompilerError "'Create threadsafe executable' must be checked."
;;CompilerError "L'option Activer la gestion des thread doit etre activée."
CompilerEndIf

OnErrorGoto(?ErrorHandler)

#MyApp$ = "myservices" ; nom de l'exécutable

#TITLE$ = #MyApp$     ; requester title and co

#BITEXE = #False      ; ? application name includes 32 or 64 OS version (app32.exe or app64.exe)

#RUNONE = #True       ; ? application can be started only one time

#TRIAL  = #False      ; ? trial version with limited time

#WRITE  = #True       ; ? needs read/write on disk

#FOLDER = #False      ; ? application folder must be named FOLDER$

Global FOLDER$ = UCase(#MyApp$)

#menuroll = 5

#bgcolor = 14474460 ; bgcolor = RGB(220,220,220)

CompilerIf #PB_Compiler_Debugger
  ;;; Nous sommes dans l'IDE
  #WinApp = 999
CompilerElse
  #WinApp = 51
CompilerEndIf

XIncludeFile "..\pb-version.pbi"

XIncludeFile "..\pb-include.pbi"

#WinAppWidth  = 620
#WinAppHeight = 300

#MyAppIni$ = #MyApp$ + ".ini"
#MyAppSvc$ = #MyApp$ + ".txt"

#homemail$ = "pbcage@free.fr"
#homesite$ = "http://pbcage.free.fr"

Global threadCheckIP
Global threadCheckUpdate

Global confirm.a
Global Mutex
Global Message$
Global optConfirm
Global optMinimize

Global event, result, state

; ENVIRON

Global appdata$     = LCase(GetEnvironmentVariable("APPDATA"))
Global temp$        = LCase(GetEnvironmentVariable("TEMP"))
Global pfx64$       = LCase(GetEnvironmentVariable("PROGRAMFILES"))
Global pfx86$       = LCase(GetEnvironmentVariable("PROGRAMFILES(x86)"))
Global programdata$ = LCase(GetEnvironmentVariable("PROGRAMDATA"))
Global systemroot$  = LCase(GetEnvironmentVariable("SYSTEMROOT"))

If pfx86$ = #Null$ : pfx86$ = pfx64$ : EndIf

Global ipaddr$
Global notepad$ = "notepad.exe"
Global runonce = #False

;---- 1 - [OPTIONS]

Global optOntop     = #False
Global optConfirm   = #False
Global optMinimize  = #False

;---- 2 - DECLARE

Declare checkIP()
Declare constructAppPopupMenu()
Declare get_services(type$)
Declare displayMyServices()
Declare display_services()

;---- 3 - ENUMERATION

UseGIFImageDecoder()
UsePNGImageDecoder()

DataSection ; Images
  Image_Button:
  IncludeBinary "myservices.png"
  Image_App:
  IncludeBinary "myservices.ico"
  Image_About:
  IncludeBinary "..\icones\info.png"
  Image_Check:
  IncludeBinary "..\icones\page_white_put.png"
  Image_Edit:
  IncludeBinary "..\icones\edit.png"
  Image_Folder:
  IncludeBinary "..\icones\folder.png"
  Image_Home:
  IncludeBinary "..\icones\apphome.png"
  Image_Quit:
  IncludeBinary "..\icones\exit.png"
  Image_Refresh:
  IncludeBinary "..\icones\refresh.png"
  Image_Update:
  IncludeBinary "..\icones\page_white_put.png"
EndDataSection

Enumeration Images
  #Image_App
  #Image_About
  #Image_Check
  #Image_Edit
  #Image_Folder
  #Image_Home
  #Image_Quit
  #Image_Refresh
  #Image_Update
  #Image_Button
EndEnumeration

Global IconApp     = CatchImage(#Image_App     , ?Image_App)
Global IconAbout   = CatchImage(#Image_About   , ?Image_About)
Global IconCheck   = CatchImage(#Image_Check   , ?Image_Check)
Global IconEdit    = CatchImage(#Image_Edit    , ?Image_Edit)
Global IconFolder  = CatchImage(#Image_Folder  , ?Image_Folder)
Global IconHome    = CatchImage(#Image_Home    , ?Image_Home)
Global IconQuit    = CatchImage(#Image_Quit    , ?Image_Quit)
Global IconRefresh = CatchImage(#Image_Refresh , ?Image_Refresh)
Global IconUpdate  = CatchImage(#Image_Update  , ?Image_Update)
Global LogoButton  = CatchImage(#Image_Button  , ?Image_Button)

Enumeration Menus
  #MenuApp
  #MenuTray
EndEnumeration

Enumeration FormMenu
  #MenuAppAbout
  #MenuAppCheckIP
  #MenuAppCheckVer
  #MenuAppCreate
  #MenuAppEdit1
  #MenuAppEdit2
  #MenuAppFolder
  #MenuAppHide
  #MenuAppHome
  #MenuAppQuit
  #MenuAppQuitX
  #MenuAppDisplay1
  #MenuAppDisplay2
  #MenuAppDisplay3
  #MenuAppShow
  #MenuAppServices
  ;
  #MenuOptConfirm
  #MenuOptMinimize
  #MenuOptOntop
  ;
  #MenuPopupSYS
  #MenuPopupSYS00
  #MenuPopupSYS01
  #MenuPopupSYS02
  #MenuPopupSYS03
  #MenuPopupSYS04
  #MenuPopupSYS05
  #MenuPopupSYS06
  #MenuPopupSYS07
  #MenuPopupSYS08
  #MenuPopupSYS09
  #MenuPopupSYS10
  #MenuPopupSYS11
  #MenuPopupSYS12
  ;
  #MenuLastItem ; must be the last enumerated menu
EndEnumeration

Enumeration Gadgets
  #Gadget
  #GadgetBtnA1         ; bouton Services
  #GadgetBtnA2         ; bouton Menu popup
  #GadgetBtnS1         ; bouton modifier le status du service sélectionné
  #GadgetComboBox1     ; liste des services ou drivers
  #GadgetComboBox2     ; liste des actions a réaliser
  #GadgetComboBox3     ; liste des exe
  ;
  #GadgetEditor        ; zone d'affichage des résultats de la commande passée
  ;
  #GadgetBtnQuitter    ; bouton Quitter
  #GadgetBtnAPropos    ; bouton A Propos
  #GadgetBtnServices   ; bouton Services
  #GadgetBtnReduire    ; bouton Réduire
  ;
  #GadgetEnd           ; fin de changement des couleurs bg/fg
  ;
  #GadgetStatusBar
EndEnumeration

Enumeration Fonts
  #Font0
  #Font1
EndEnumeration

Global NewMap mycpl.s()        ; to dynamically populating the popup menu Consoles->...
Global NewMap mymsc.s()        ; to dynamically populating the popup menu Control panel->...

Global NewList SERVICES.s()

Global NewMap SERVICE_NAME.s() ; to dynamically populating all windows drivers and service SERVICE_NAME
Global NewMap DISPLAY_NAME.s() ; to dynamically populating all windows drivers and service DISPLAY_NAME

;{ dynamic index for dynamic menus
Global dynIndex.u = #MenuLastItem

Global lastIndexPopup.u     = 0
Global firstIndexPopup.u    = 0

Global lastIndexPopupCPL.u  = 0
Global firstIndexPopupCPL.u = 0

Global lastIndexPopupMSC.u  = 0
Global firstIndexPopupMSC.u = 0

;}-ENDED-<<< dynamic index for dynamic menus

Procedure initSysTray()
  If FindWindow_("Progman", #NUL) <> 0
    AddSysTrayIcon(#WinApp, WindowID(#WinApp), IconApp)
    SysTrayIconToolTip(#WinApp, #MyApp$)
  EndIf
EndProcedure

Procedure constructAppMainMenu()
  If IsMenu(#MenuApp) : FreeMenu(#MenuApp) : EndIf
  
  If CreateImageMenu(#MenuApp, WindowID(#WinApp), #PB_Menu_ModernLook)
; menu Fichier
    MenuTitle("Fichier")
      MenuItem(#MenuAppEdit1    , "Editer "+#MyAppIni$        ,IconEdit)
      MenuItem(#MenuAppEdit2    , "Editer "+#MyAppSvc$        ,IconEdit)
      MenuItem(#MenuAppFolder   , "Ouvrir dossier application",IconFolder)
      MenuBar()
      MenuItem(#MenuAppHome     , "Visiter le site pbcage"    ,IconHome)
      MenuItem(#MenuAppCheckVer , "Vérifier les mises à jour" ,IconUpdate)
      MenuItem(#MenuAppCheckIp  , "Vérifier votre adresse ip" ,IconCheck)
      MenuBar()
      MenuItem(#MenuAppQuit     , "Quitter"+Chr(9)+"Ctrl+Q"   ,IconQuit)
; menu Options
    MenuTitle("Options")
      MenuItem(#MenuOptConfirm  , "Confirmer quitter")
      MenuItem(#MenuOptOntop    , "Toujours au dessus")
      MenuItem(#MenuOptMinimize , "Réduire dans systray")
; menu Afficher
    MenuTitle("Afficher")
      MenuItem(#MenuAppDisplay1 , "Afficher mes services"     ,IconRefresh)
      MenuItem(#MenuAppDisplay2 , "Afficher tous les services",IconRefresh)
      MenuItem(#MenuAppDisplay3 , "Afficher tous les drivers" ,IconRefresh)
  EndIf
  constructAppPopupMenu()
EndProcedure

Procedure constructAppPopupMenu()
  Protected file$
  Protected foldr.s
  
  Define title.s, node.s
  
  firstIndexPopup = dynIndex
  
  If IsMenu(#MenuPopupSYS) : FreeMenu(#MenuPopupSYS) : EndIf
  
  If CreatePopupMenu(#MenuPopupSYS)
    ClearMap(mycpl())
    ClearMap(mymsc())
    OpenSubMenu("Desktop")
      MenuItem(#MenuPopupSYS00, "menu")
      MenuBar()
      MenuItem(#MenuPopupSYS01, "show")
      MenuItem(#MenuPopupSYS02, "hide")
    CloseSubMenu()
    OpenSubMenu("Consoles")
      firstIndexPopupMSC = dynIndex
      foldr = SystemRoot$ + "\System32"
      If ExamineDirectory(0, foldr, "*.msc")
        While NextDirectoryEntry(0)
          If DirectoryEntryType(0) = #PB_DirectoryEntry_File
            file$ = LCase(DirectoryEntryName(0))
            ;Debug file$
            file$ = GetFilePart(file$, #PB_FileSystem_NoExtension)
            MenuItem(dynIndex, file$)
            mymsc(Str(dynIndex)) = file$
            dynIndex + 1
          EndIf
        Wend
        FinishDirectory(0)
      EndIf
      If firstIndexPopupMSC = dynIndex
        lastIndexPopupMSC = dynIndex
      Else
        lastIndexPopupMSC = dynIndex - 1
      EndIf
    CloseSubMenu()
    OpenSubMenu("Control panel")
      firstIndexPopupCPL = dynIndex
      foldr = SystemRoot$ + "\System32"
      If ExamineDirectory(0, foldr, "*.cpl")
        While NextDirectoryEntry(0)
          If DirectoryEntryType(0) = #PB_DirectoryEntry_File
            file$ = LCase(DirectoryEntryName(0))
            ;Debug file$
            file$ = GetFilePart(file$, #PB_FileSystem_NoExtension)
            MenuItem(dynIndex, file$)
            mycpl(Str(dynIndex)) = file$
            dynIndex + 1
          EndIf
        Wend
        FinishDirectory(0)
      EndIf
      If firstIndexPopupCPL = dynIndex
        lastIndexPopupCPL = dynIndex
      Else
        lastIndexPopupCPL = dynIndex - 1
      EndIf
    CloseSubMenu()
    MenuBar()
    MenuItem(#MenuPopupSYS03, "Help")
    MenuItem(#MenuPopupSYS04, "FileRun")
    MenuItem(#MenuPopupSYS05, "ShutdownWindows")
    MenuItem(#MenuPopupSYS06, "TrayProperties")
    MenuItem(#MenuPopupSYS07, "ToggleDesktop")
    MenuItem(#MenuPopupSYS08, "MinimizeAll")
    MenuItem(#MenuPopupSYS09, "UndoMinimizeAll")
    MenuItem(#MenuPopupSYS10, "TileHorizontally")
    MenuItem(#MenuPopupSYS11, "TileVertically")
    MenuItem(#MenuPopupSYS12, "CascadeWindows")
  EndIf
  
  If dynIndex = firstIndexPopup
    lastIndexPopup = firstIndexPopup
  Else
    lastIndexPopup = dynIndex - 1
  EndIf
;   : Debug "first index MSC = " + firstIndexPopupMSC
;   : Debug "last  index MSC = " + lastIndexPopupMSC
;   : Debug "first index CPL = " + firstIndexPopupCPL
;   : Debug "last  index CPL = " + lastIndexPopupCPL
EndProcedure ; constructAppPopupMenu()

Procedure constructAppInterface()
;   If LoadFont(#Font0, "Microsoft Sans Serif", 9)
;     SetGadgetFont(#PB_Default , FontID(#Font0))
;   EndIf
  CreateStatusBar(#GadgetStatusBar , WindowID(#WinApp))
  AddStatusBarField(190)
  AddStatusBarField(40)
  AddStatusBarField(#PB_Ignore)
  StatusBarText(#GadgetStatusBar, 0, "Votre IP externe: ")
  StatusBarText(#GadgetStatusBar, 1, "0")
  StatusBarText(#GadgetStatusBar, 2, "")
  
  Protected X,Y,W,H,G
  ;
  X=2:Y=2:W=24:H=24
  ButtonImageGadget(#GadgetBtnA1, X, Y, W, H, ImageID(#Image_Button))
  GadgetToolTip(#GadgetBtnA1, " Cliquez pour lancer la console services.msc ")
  ;
  X+W+3
  ButtonImageGadget(#GadgetBtnA2, X, Y, W, H, ImageID(#Image_Button))
  GadgetToolTip(#GadgetBtnA2, " Cliquez pour afficher le menu popup ")
  ;
  X+W+3:Y=3:W=230:H=22
  ComboBoxGadget(#GadgetComboBox1, X, Y, W, H)
  get_services("service")
  get_services("my")
  display_services()
  ;
  X+W+4:W=120
  ComboBoxGadget(#GadgetComboBox2, X, Y, W, H)
  AddGadgetItem(#GadgetComboBox2, -1, "query")
  AddGadgetItem(#GadgetComboBox2, -1, "query all")
  AddGadgetItem(#GadgetComboBox2, -1, "query driver")
  AddGadgetItem(#GadgetComboBox2, -1, "query service")
  AddGadgetItem(#GadgetComboBox2, -1, "queryex")
  AddGadgetItem(#GadgetComboBox2, -1, "queryex all")
  AddGadgetItem(#GadgetComboBox2, -1, "queryex driver")
  AddGadgetItem(#GadgetComboBox2, -1, "queryex service")
  AddGadgetItem(#GadgetComboBox2, -1, "list")
  AddGadgetItem(#GadgetComboBox2, -1, "start")
  AddGadgetItem(#GadgetComboBox2, -1, "stop")
  AddGadgetItem(#GadgetComboBox2, -1, "delayed-auto")
  AddGadgetItem(#GadgetComboBox2, -1, "auto")
  AddGadgetItem(#GadgetComboBox2, -1, "demand")
  AddGadgetItem(#GadgetComboBox2, -1, "disabled")
  SetGadgetState(#GadgetComboBox2, 0)
  ;
  X+W+4:W=70
  ComboBoxGadget(#GadgetComboBox3, X, Y, W, H)
  AddGadgetItem(#GadgetComboBox3, -1, "sc.exe")
  AddGadgetItem(#GadgetComboBox3, -1, "net.exe")
  SetGadgetState(#GadgetComboBox3, 0)
  ;
  X+W+3:Y=2:W=24:H=24
  ButtonImageGadget(#GadgetBtnS1, X, Y, W, H, ImageID(#Image_Button))
  GadgetToolTip(#GadgetBtnS1, " Cliquez pour lire ou modifier le status du service ")
  ;
  X=2:Y=27:H=208
  EditorGadget(#GadgetEditor, X , Y , #WinAppWidth-4 , H, #PB_Editor_ReadOnly)
  SetGadgetColor(#GadgetEditor, #PB_Gadget_BackColor , #bgcolor)
  SetGadgetColor(#GadgetEditor, #PB_Gadget_FrontColor, RGB(0,0,0))
  ;
  Y=#WinAppHeight - 64 : H=20
  ButtonGadget(#GadgetBtnQuitter ,  1               , Y, #WinAppWidth/4, H, "Quitter")
  ButtonGadget(#GadgetBtnAPropos ,  #WinAppWidth/4*1, Y, #WinAppWidth/4, H, "À Propos")
  ButtonGadget(#GadgetBtnServices,  #WinAppWidth/4*2, Y, #WinAppWidth/4, H, "Services")
  ButtonGadget(#GadgetBtnReduire ,  #WinAppWidth/4*3, Y, #WinAppWidth/4, H, "Réduire")
  ;
  GadgetToolTip(#GadgetBtnAPropos    , "À Propos de "+#MyApp$)       ; À Propos (Info)
  GadgetToolTip(#GadgetBtnQuitter    , "Fermer l'application")       ; Quitter
  GadgetToolTip(#GadgetBtnReduire    , "Réduire l'application")      ; Réduire
  GadgetToolTip(#GadgetBtnServices   , "Lancer la console services") ; Services
  ;  
  For G = #Gadget + 1 To #GadgetEnd - 1
    ;: Debug G
    SetGadgetColor(G, #PB_Gadget_BackColor , #bgcolor)
    SetGadgetColor(G, #PB_Gadget_FrontColor, RGB(0,0,0))
  Next
  
  If LoadFont(#Font0, "", 9)
    SetGadgetFont(#GadgetComboBox1 , FontID(#Font0))
    SetGadgetFont(#GadgetComboBox2 , FontID(#Font0))
    SetGadgetFont(#GadgetComboBox3 , FontID(#Font0))
  EndIf
  
  If LoadFont(#Font1, "Consolas", 9)
    SetGadgetFont(#GadgetEditor , FontID(#Font1))
  EndIf
EndProcedure

Procedure constructAppTrayMenu()
  If CreatePopupImageMenu(#MenuTray, #PB_Menu_ModernLook)
    MenuItem(#MenuAppShow , #MyApp$ , IconApp)
    MenuBar()
    MenuItem(#MenuAppAbout ,"À Propos",IconAbout)
    MenuItem(#MenuAppQuit  ,"Quitter" ,IconQuit)
  EndIf
EndProcedure

Procedure setMenuItemMark(item)
  Result = GetMenuItemState(#MenuApp, item)
  If Result
    State = #False
  Else
    State = #True
  EndIf
  SetMenuItemState(#MenuApp, item, State)
  ProcedureReturn State
EndProcedure

Procedure readIniFile()
  Protected File$ = AppPath$ + #MyAppIni$
  Protected option$
  
  If OpenPreferences(File$)
    PreferenceGroup("OPTIONS")
      option$       = ReadPreferenceString("Ontop"    , "0")
      optOntop      = Bool(option$ = "1")
      option$       = ReadPreferenceString("Confirm"  , "1")
      optConfirm    = Bool(option$ = "1")
      option$       = ReadPreferenceString("Minimize" , "0")
      optMinimize   = Bool(option$ = "1")
    ClosePreferences()
  EndIf
EndProcedure

Procedure makeIniFile()
  #FileNum = 1
  
  Protected File$ = AppPath$ + #MyAppIni$
   
  Protected Now$ = FormatDate("%dd/%mm/%yyyy %hh:%ii:%ss", Date())
  
  DeleteFile(File$, #PB_FileSystem_Force)
  
  If OpenFile(#FileNum, File$)
    WriteStringN(#FileNum, #Null$)
    WriteStringN(#FileNum, "["+UCase(#MyApp$)+"]")
    WriteStringN(#FileNum, ";")
    WriteStringN(#FileNum, "; "+#MyAppIni$+" v" + #AppVer$ + " - " + Now$)
    WriteStringN(#FileNum, ";")
    WriteStringN(#FileNum, "; Utilisé par "+#MyApp$+".exe")
    WriteStringN(#FileNum, ";")
    WriteStringN(#FileNum, "; "+#MyAppIni$+" est automatiquement créé par "+#MyApp$+".exe")
    WriteStringN(#FileNum, ";")
    WriteStringN(#FileNum, ";[OPTIONS] - Exemples")
    WriteStringN(#FileNum, ";Ontop     = 0/1 - 1 pour garder l'application au dessus des autres fenetres.")
    WriteStringN(#FileNum, ";Confirm   = 0/1 - 1 pour confirmer la fermeture de l'application.")
    WriteStringN(#FileNum, ";Minimize  = 0/1 - 1 pour réduire l'application uniquement dans la systray.")
    WriteStringN(#FileNum, ";")
    WriteStringN(#FileNum, ";Note: Toutes les options sont automatiquement sauvées a la fermeture de l'application.")
    WriteStringN(#FileNum, #Null$)
    WriteStringN(#FileNum, "[OPTIONS]")
    WriteStringN(#FileNum, "Ontop     = " + Str(optOntop))
    WriteStringN(#FileNum, "Confirm   = " + Str(optConfirm))
    WriteStringN(#FileNum, "Minimize  = " + Str(optMinimize))
    CloseFile(#FileNum)
  EndIf
  
  ProcedureReturn 0
EndProcedure

Procedure appReload()
  Protected n
  ;: Debug "procedure appReload()"
  readIniFile()
  Repeat
    result = makeIniFile()
  Until result = 0
  hideAppWindow()
  constructAppMainMenu()
  constructAppTrayMenu()
  constructAppInterface()
  initSysTray()
  ;
  SetMenuItemState(#MenuApp, #MenuOptConfirm   , optConfirm)
  SetMenuItemState(#MenuApp, #MenuOptMinimize  , optMinimize)
  SetMenuItemState(#MenuApp, #MenuOptOntop     , optOntop)
  checkIP()
  SetWindowState(#WinApp, #PB_Window_Normal)
  HideWindow(#WinApp, #False, #PB_Window_ScreenCentered)
  showAppWindow()
EndProcedure

Procedure winRoll()
  Protected Y
  If WindowHeight(#WinApp) = #WinAppHeight
    StickyWindow(#WinApp, #True)
    ResizeWindow(#WinApp, #PB_Ignore, WindowY(#WinApp), #WinAppWidth, MenuHeight())
    SetMenuTitleText(#MenuApp, #menuroll, Chr($2193))
  Else
    StickyWindow(#WinApp, #False)
    Y=WindowY(#WinApp) ; -30
    If Y<=0:Y=40:EndIf
    ResizeWindow(#WinApp, #PB_Ignore, Y, #WinAppWidth, #WinAppHeight)
    SetMenuTitleText(#MenuApp, #menuroll, Chr($2191))
  EndIf
EndProcedure

Procedure windowCallback(win, msg, wParam, lParam)
  Protected Result = #PB_ProcessPureBasicEvents
  Select Msg
    Case TB_Message ; explorer crash
      initSysTray()
    Case #WM_MENUSELECT
      If (wparam&$ffff)=#menuroll And (wparam&$ffff0000)/$ffff = #MF_MOUSESELECT|#MF_HILITE|#MF_POPUP ; $8090 ;2156920832
        winRoll()
      EndIf
  EndSelect
  ProcedureReturn Result
EndProcedure

Procedure threadCheckUpdate(checkWeb=#False)
  Static isRunning.b
  
  If isRunning
    ProcedureReturn -1
  Else
    isRunning = #True
  EndIf
  
  ClearDebugOutput()
  
  DisableMenuItem(#MenuApp, #MenuAppCheckVer, #True)
  
  Protected fileversion$ = temp$ + "\" + #MyApp$ + ".txt"
  
  SetFileAttributes(fileversion$, #PB_FileSystem_Normal)
  
  DeleteFile(fileversion$, #PB_FileSystem_Force)
  
  Protected entry$, in, retval, result$, URL$, header$
  
  ;;; On récupére le fichier myservices.txt dans /versions/ du serveur web
  If InitNetwork()
    Debug "Get version without proxy"
    URL$ = #homesite$+"/versions/"+#MyApp$+".txt"
    Debug URL$
    header$ = GetHTTPHeader(URL$,#PB_HTTP_NoRedirect)
    result$ = StringField(header$, 1, #LF$)
    Debug result$
    If FindString(result$, "200 OK",1,#PB_String_NoCase)
      retval = ReceiveHTTPFile(URL$, fileversion$, #PB_HTTP_NoRedirect)
      If retval : Debug "Get version without proxy is OK" : EndIf
    EndIf
  EndIf
  
  Protected yy, mm, dd, VersionExe, versionExe$, versionWeb, versionWeb$, mil=2000
  
  Protected version$=#Null$
  
  ;;; On teste si le fichier myservices.txt existe => avec ou sans proxy
  If fileExist(fileversion$) And FileSize(fileversion$) >= 10
    in = ReadFile(#PB_Any, fileversion$, #PB_Ascii)
    If in
      version$ = ReadString(in)
      CloseFile(in)
      DeleteFile(fileversion$)
      version$ = Trim(version$)
      If FindString(version$, "version=", 1) ; "version=17.09.09"
        version$ = ReplaceString(version$,"version=",#Null$)
        yy = Val(StringField(version$, 1, "."))
        mm = Val(StringField(version$, 2, "."))
        dd = Val(StringField(version$, 3, "."))
        yy+mil
        retval = Date(yy,mm,dd,0,0,1)
        If Not retval
          version$ = #Null$
        EndIf
        Debug "version="+version$
      Else
        version$ = #Null$
        Debug "version="+version$
      EndIf
    EndIf
  EndIf
  
  If version$ <> #Null$
    version$ = Trim(version$)
    version$ = Left(version$, 8)
    Debug "version="+version$
    versionWeb$ = version$
    Debug "version web= "+versionWeb$
    yy = Val(StringField(versionWeb$, 1, "."))
    mm = Val(StringField(versionWeb$, 2, "."))
    dd = Val(StringField(versionWeb$, 3, "."))
    yy+mil
    retval = Date(yy,mm,dd,0,0,1)
    If retval
      versionWeb = retval
    Else
      version$ = #Null$
    EndIf
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    versionExe$ = #AppVer$
    Debug "version exe= "+versionExe$
    yy = Val(StringField(versionExe$, 1, "."))
    mm = Val(StringField(versionExe$, 2, "."))
    dd = Val(StringField(versionExe$, 3, "."))
    yy+mil
    retval = Date(yy,mm,dd,0,0,1)
    If retval
      VersionExe = retval
    Else
      version$ = #Null$
    EndIf
    If checkWeb And version$ <> #Null$
      If versionWeb > VersionExe
        Message$ = "Une nouvelle version de " + #MyApp$ + " est disponible."
      Else
        Message$ = "Bravo, votre version de " + #MyApp$ + " est à jour."
      EndIf
      Message$ + #CRLF$ + #CRLF$
      Message$ + "Version installée : "  + versionExe$ + #CRLF$
      Message$ + "Version disponible : " + versionWeb$ + #CRLF$
      Message$ + #CRLF$
      Message$ + "Souhaitez-vous visiter le site "+#homesite$
      Protected Response = messageBox(#True)
      If Response = #PB_MessageRequester_Yes
        RunProgram(#homesite$)
      EndIf
    EndIf
  EndIf
  DisableMenuItem(#MenuApp, #MenuAppCheckVer, #False)
  isRunning = #False
EndProcedure

Procedure checkUpdate()
;   Protected Response
;   Message$ = "Voulez vous vérifier la présence d'une mise à jour ?"
;   Response = messageBox(#True)
;   If Response = #PB_MessageRequester_Yes
    If Not IsThread(threadCheckUpdate)
      threadCheckUpdate = CreateThread(@threadCheckUpdate(), #True)
    EndIf
;   EndIf
EndProcedure

Procedure makeSomeFiles()
  Protected addr.i, file.s, leng.l, fkey.s
  #file1$ = "myservices.txt"
  #file2$ = "myservices.exe.lisezmoi.txt"
  
  DataSection
    IB01:
    IncludeBinary "Data\"+#file1$
    IB02:
    IncludeBinary "Data\"+#file2$
    IB03:
  EndDataSection

  Structure ref
    addr.i
    name.s
    size.i
  EndStructure
  
  NewMap files.ref()
  
  files("1")\addr = ?IB01  
  files("1")\name = AppPath$ + #file1$
  files("1")\size = ?IB02 - ?IB01 ; get size of included binary
  ;
  files("2")\addr = ?IB02
  files("2")\name = AppPath$ + #file2$
  files("2")\size = ?IB03 - ?IB02 ; get size of included binary

  ForEach files()
    file = LCase(files()\name)
    fkey = MapKey(files())
    ;: Debug "fkey="+fkey+"="+file
    Select fkey
      Case "1" : If fileExist(file) : Continue : EndIf
      Default
    EndSelect
    If CreateFile(1, file)
      addr = files()\addr
      leng = files()\size   ; size of Included Binary
      ;: Debug "addr=" + Str(addr)
      ;: Debug "size=" + Str(leng)
      WriteData(1, addr, leng)
      CloseFile(1)
    EndIf
  Next
  
  FreeMap(files())
  
EndProcedure ; makeSomeFiles()

Procedure threadCheckIP(checkip)
  Static isRunning.b
  
  If isRunning
    ProcedureReturn -1
  Else
    isRunning = #True
  EndIf
  
  DisableMenuItem(#MenuApp, #MenuAppCheckIP, #True)
  
  If InitNetwork()
    Protected URL$, header$, result$
    Protected *buffer, Size
    ;: Debug "Get IP Address"
    URL$ = "http://monip.outils-rezo.info/text"
    ;: Debug URL$
    header$ = GetHTTPHeader(URL$,#PB_HTTP_NoRedirect)
    result$ = StringField(header$, 1, #LF$)
    ;: Debug result$
    StatusBarText(#GadgetStatusBar, 0, Space(30))
    If FindString(result$, "200 OK",1,#PB_String_NoCase)
      *buffer = ReceiveHTTPMemory(URL$, #PB_HTTP_NoRedirect)
      If *Buffer
        Size = MemorySize(*Buffer)
        ipaddr$ = PeekS(*Buffer, Size, #PB_UTF8|#PB_ByteLength)
        ;: Debug "Content: " + ipaddr$
        FreeMemory(*Buffer)
        StatusBarText(#GadgetStatusBar, 0, "Votre IP externe : "+ipaddr$)
      Else
        ;: Debug "Get IP Address is KO"
        StatusBarText(#GadgetStatusBar, 0, "Votre IP externe : ")
      EndIf
    EndIf
  EndIf
  DisableMenuItem(#MenuApp, #MenuAppCheckIP, #False)
  isRunning = #False
EndProcedure ; threadCheckIP()

Procedure checkIP()
  If Not IsThread(threadCheckIP)
    threadCheckIP = CreateThread(@threadCheckIP(), #True)
  EndIf
EndProcedure ; checkIP()

Procedure runMenuProgram(progr$, param$, foldr$, popup)
  Protected confirm
  
  If progr$ <> #Null$
    If runonce
      Define ptrPID = 0
      Define exename$ = GetFilePart(progr$)
      If getNamePID(exename$,@ptrPID) : ProcedureReturn #False : EndIf
    EndIf
    confirm = RunProgram(progr$, param$, foldr$)
    Debug "progr="+progr$
    Debug "param="+param$
    Debug "foldr="+foldr$
    If Not confirm And popup
      Message$ = "Check " + LCase(progr$ + " " + param$)
      messageBox()
    EndIf
    Debug "end"
    ProcedureReturn confirm
  Else
    ProcedureReturn #False
  EndIf
EndProcedure ; runMenuProgram()

Procedure objShell(object.s)
  Protected file$ = AppPath$ + "~objshell~.vbs"
  Protected set$ = "Set objShell = CreateObject(" + Chr(34) + "Shell.Application" + Chr(34) + ")"
  Protected run$

  Select LCase(object)
    Case "cascadewindows"
      run$ = "objShell.CascadeWindows"
    Case "filerun"
      run$ = "objShell.FileRun"
    Case "help"
      run$ = "objShell.Help"
    Case "minimizeall"
      run$ = "objShell.MinimizeAll"
    Case "shutdownwindows"
      run$ = "objShell.ShutdownWindows"
    Case "tilehorizontally"
      run$ = "objShell.TileHorizontally"
    Case "tilevertically"
      run$ = "objShell.TileVertically"
    Case "toggledesktop"
      run$ = "objShell.ToggleDesktop"
    Case "trayproperties"
      run$ = "objShell.TrayProperties"
    Case "undominimizeall"
      run$ = "objShell.UndoMinimizeALL"
  EndSelect
  
  If CreateFile(0, file$)
    WriteStringN(0, set$)
    WriteStringN(0, run$)
    CloseFile(0)
    RunProgram("wscript.exe", file$, #Null$, #PB_Program_Wait)
    DeleteFile(file$)
  EndIf
  
EndProcedure

Procedure get_services(type$)
  Protected count=0 ,in, n, rc, entry$, file$
  Protected SERVICE_NAME.s, DISPLAY_NAME.s
  Protected string1$ = "SERVICE_NAME: "
  Protected string2$ = "DISPLAY_NAME: "
  Protected parameters$
  Select LCase(type$)
    Case "my"
    Case "driver"
    Case "service"
    Default
      type$ = "service"
  EndSelect
  If type$ <> "my"
    parameters$ = "/c sc.exe query type= "+type$+" state= all | findstr /i /c:"+string1$+" /c:"+string2$+" 2>&1|clip"
    
    rc = RunProgram("cmd.exe",parameters$,#Null$,#PB_Program_Wait|#PB_Program_Hide)
    
    entry$ = GetClipboardText() : ClearClipboard()
    
    count  = CountString(entry$, string1$)
    entry$ = RemoveString(entry$, string1$, #PB_String_NoCase)
    entry$ = RemoveString(entry$, string2$, #PB_String_NoCase)
    
    If count
      ClearList(SERVICES())
      ClearMap(DISPLAY_NAME())
      ClearGadgetItems(#GadgetComboBox1)
      For n = 1 To 2 * count Step 2
        SERVICE_NAME = Trim(StringField(entry$, n+0, #CRLF$))
        DISPLAY_NAME = Trim(StringField(entry$, n+1, #CRLF$))
        DISPLAY_NAME(LCase(SERVICE_NAME)) = DISPLAY_NAME
        AddElement(SERVICES())
        SERVICES() = SERVICE_NAME
      Next
    EndIf
  Else
    file$ = AppPath$ + "myservices.txt"
    If fileExist(file$)
      in = OpenFile(#PB_Any, file$, #PB_UTF8)
      If in
        ClearList(SERVICES())
        While Eof(in) = 0
          SERVICE_NAME = Trim(ReadString(in))
          If SERVICE_NAME <> #Null$
            AddElement(SERVICES())
            SERVICES() = SERVICE_NAME
          EndIf
        Wend
      CloseFile(in)
      EndIf
    EndIf
  EndIf
EndProcedure ; get_services()

Procedure display_services()
  Protected COUNT, SERVICE_NAME.s
  
  ClearGadgetItems(#GadgetComboBox1)
  
  COUNT = ListSize(SERVICES()) ;: Debug "COUNT="+COUNT
  
  If COUNT
    ForEach SERVICES()
      AddGadgetItem(#GadgetComboBox1, -1, SERVICES())
    Next
    SetGadgetState(#GadgetComboBox1, 0)
  EndIf
  COUNT = CountGadgetItems(#GadgetComboBox1)
  StatusBarText(#GadgetStatusBar, 1, Str(COUNT), #PB_StatusBar_Center)
  SERVICE_NAME = LCase(GetGadgetText(#GadgetComboBox1))
  StatusBarText(#GadgetStatusBar, 2, DISPLAY_NAME(SERVICE_NAME))
EndProcedure ; displayServices()

Procedure set_service(service$=#Null$, program$=#Null$, action$=#Null$)
  Protected parameters$, status$, rc=#False
  Protected scexe$  = "sc.exe"
  Protected netexe$ = "net.exe"
  DisableGadget(#GadgetBtnS1, #True)
  ClearClipboard()
  ClearGadgetItems(#GadgetEditor)
  ;: Debug service$
  ;: Debug program$
  ;: Debug action$
  service$ = Chr(34)+service$+Chr(34)
  Select LCase(action$)
    Case "list"            : program$ = scexe$ : action$ = "query | findstr /i /c:SERVICE_NAME: /c:DISPLAY_NAME:"
    Case "start"           :                     action$ = "start   " + service$
    Case "stop"            :                     action$ = "stop    " + service$
    Case "query"           : program$ = scexe$ : action$ = "query   " + service$
    Case "query all"       : program$ = scexe$ : action$ = "query   type= all"
    Case "query driver"    : program$ = scexe$ : action$ = "query   type= driver"
    Case "query service"   : program$ = scexe$ : action$ = "query   type= service"
    Case "queryex"         : program$ = scexe$ : action$ = "queryex " + service$
    Case "queryex all"     : program$ = scexe$ : action$ = "queryex type= all"
    Case "queryex driver"  : program$ = scexe$ : action$ = "queryex type= driver"
    Case "queryex service" : program$ = scexe$ : action$ = "queryex type= service"
    Case "auto"            : program$ = scexe$ : action$ = "config  " + service$ + " start= auto"
    Case "delayed-auto"    : program$ = scexe$ : action$ = "config  " + service$ + " start= delayed-auto"
    Case "demand"          : program$ = scexe$ : action$ = "config  " + service$ + " start= demand"
    Case "disabled"        : program$ = scexe$ : action$ = "config  " + service$ + " start= disabled"
    Default
      action$ = #Null$
  EndSelect
  If action$ <> #Null$
    parameters$ = "/c "+program$+" "+action$+" 2>&1|clip"
    
    rc = RunProgram("cmd.exe",parameters$,#Null$,#PB_Program_Wait|#PB_Program_Hide)
    
    AddGadgetItem(#GadgetEditor, 0, GetClipboardText())
    
    ClearClipboard()
  EndIf
  DisableGadget(#GadgetBtnS1, #False)
  ProcedureReturn rc
EndProcedure

;;;;;;;
;---- 4 - MAIN
;;;;;;;

Define Flags = #PB_Window_SystemMenu|#PB_Window_TitleBar|#PB_Window_Minimize|#PB_Window_WindowCentered

ExamineDesktops()
Define X = (DesktopWidth(0)-#WinAppWidth)/2
Define Y = (DesktopHeight(0)-#WinAppHeight)/2

If OpenWindow(#WinApp, X, Y, #WinAppWidth, #WinAppHeight, #WinAppTitle$, Flags)
  SetWindowColor(#WinApp, #bgcolor)
  
  makeSomeFiles()
  
  appReload()
  
  AddKeyboardShortcut(#WinApp, #PB_Shortcut_Escape, #MenuAppHide)
  AddKeyboardShortcut(#WinApp, #PB_Shortcut_F1    , #MenuAppAbout)
  AddKeyboardShortcut(#WinApp, #PB_Shortcut_Control|#PB_Shortcut_Q, #MenuAppQuit)
  
  StickyWindow(#WinApp, optOntop)
  
  AddKeyboardShortcut(#WinApp, #PB_Shortcut_Control|#PB_Shortcut_X, #MenuAppQuitX) ; quit
  
  SetWindowCallback(@windowCallback(),#WinApp) ; crash explorer
  
  Define index, indexMenu, indexGadget, indexGadgetType, loop, lHwd, input$, title$
  Define foldr$, param$, progr$
;;;;;;;
;---- 5 - REPEAT
;;;;;;;
  Repeat
    Event = WaitWindowEvent(10)
    
    input$  = #Null$
;     output$ = #Null$
    
    Select Event
;;;;;;
;---- Events MENUS
;;;;;;
      Case #PB_Event_Menu
        indexMenu = EventMenu()
        progr$ = #Null$
        param$ = #Null$
        Select indexMenu
          ;
          ; begin menu Fichier
          ;
          Case #MenuAppEdit1
            RunProgram(notepad$, AppPath$+#MyAppIni$, AppPath$)
            
          Case #MenuAppEdit2
            RunProgram(notepad$, AppPath$+#MyAppSvc$, AppPath$)
            
          Case #MenuAppDisplay1
            get_services("service")
            get_services("my")
            display_services()
            
          Case #MenuAppDisplay2
            get_services("service")
            display_services()
            
          Case #MenuAppDisplay3
            get_services("driver")
            display_services()
            
          Case #MenuAppCheckVer
            checkUpdate()
            
          Case #MenuAppCheckIP
            checkIP()
            
          Case #MenuAppHome
            RunProgram(#homesite$)
            
          Case #MenuOptConfirm
            setMenuItemMark(#MenuOptConfirm)
            optConfirm = GetMenuItemState(#MenuApp, #MenuOptConfirm)
            
          Case #MenuOptMinimize
            setMenuItemMark(#MenuOptMinimize)
            optMinimize = GetMenuItemState(#MenuApp, #MenuOptMinimize)
            
          Case #MenuOptOntop
            setMenuItemMark(#MenuOptOntop)
            optOntop = GetMenuItemState(#MenuApp, #MenuOptOntop)
            StickyWindow(#WinApp, optOntop)
            
          Case #MenuAppHide
            hideAppWindow()
            
          Case #MenuAppShow
            showAppWindow()
            
          Case #MenuAppAbout
            appAbout()
            
          Case #MenuAppQuit, #MenuAppQuitX
            If appQuit() : Break : EndIf
            
          Case #MenuAppFolder
            RunProgram(AppPath$,#Null$,AppPath$)
;;;;;;;
;---- Events MENU POPUP
;;;;;;;
          Case #MenuPopupSYS00
            param$ = #Null$
            If OSVersion() < #PB_OS_Windows_7
              progr$ = pfx64$ + "\folderspopup\folderspopup.exe"
            Else
              progr$ = pfx64$ + "\quickaccesspopup\quickaccesspopup.exe"
            EndIf
            foldr$ = GetPathPart(progr$)
            confirm = runMenuProgram(progr$, param$, foldr$, #True)
          Case #MenuPopupSYS01 To #MenuPopupSYS02
            lHwd = FindWindow_("Progman", #NUL)
            Select indexMenu
              Case #MenuPopupSYS01
                ;: Debug "show"
                ShowWindow_(lHwd, #SW_SHOWNA) ; #SW_SHOW = 5 et #SW_SHOWNA = 8
              Case #MenuPopupSYS02
                ;: Debug "hide"
                ShowWindow_(lHwd, #SW_HIDE) ; #SW_HIDE = 0
            EndSelect
            
          Case #MenuPopupSYS03 To #MenuPopupSYS12
            input$ = GetMenuItemText(#MenuPopupSYS, indexMenu)
            Debug input$
            objShell(input$)
            
        Case firstIndexPopupMSC To lastIndexPopupMSC
          input$ = mymsc(Str(indexMenu))
          If input$ <> #Null$
            input$ + ".msc"
            RunProgram(input$)
          EndIf
        
        Case firstIndexPopupCPL To lastIndexPopupCPL
          input$ = mycpl(Str(indexMenu))
          If input$ <> #Null$
            input$ + ".cpl"
            RunProgram(input$)
          EndIf
        EndSelect
;;;;;;;
;---- Events GADGETS
;;;;;;;
      Case #PB_Event_Gadget
        indexGadget     = EventGadget()
        indexGadgetType = EventType()
        Select indexGadget
          Case #GadgetComboBox1
            StatusBarText(#GadgetStatusBar, 2, #Null$)
            input$ = LCase(GetGadgetText(#GadgetComboBox1))
            StatusBarText(#GadgetStatusBar, 2, DISPLAY_NAME(input$))
          Case #GadgetComboBox2, #GadgetComboBox3
            Select indexGadgetType
              Case #PB_EventType_Change
                input$ = GetGadgetText(#GadgetComboBox2)
                Select input$
                  Case "start", "stop"
                  Default : SetGadgetState(#GadgetComboBox3, 0)
                EndSelect
            EndSelect
            
          Case #Gadget To #GadgetEnd
            progr$ = #Null$ : param$ = #Null$ : foldr$ = #Null$
            Select indexGadget
              Case #GadgetBtnA1         : RunProgram("services.msc")
              Case #GadgetBtnA2         : DisplayPopupMenu(#MenuPopupSYS, WindowID(#WinApp))
              Case #GadgetBtnS1
                Define service$ = GetGadgetText(#GadgetComboBox1)
                Define status$  = GetGadgetText(#GadgetComboBox2)
                Define program$ = GetGadgetText(#GadgetComboBox3)
                set_service(service$, program$, status$)
              Case #GadgetBtnQuitter    : If appQuit() : Break : EndIf ; Quitter
              Case #GadgetBtnAPropos    : appAbout()                   ; A Propos
              Case #GadgetBtnServices   : RunProgram("services.msc")
              Case #GadgetBtnReduire    : hideAppWindow()              ; Réduire
              Default                   : progr$ = #Null$
            EndSelect
            If progr$ <> #Null$
              DisableWindow(#WinApp, #True)
              RunProgram(progr$, param$, foldr$)
              Delay(2000)
              DisableWindow(#WinApp, #False)
            EndIf
        EndSelect
;;;;;;;
;---- Events SYSTEM SYSTRAY
;;;;;;;
      Case #PB_Event_SysTray
        Select EventType()
          Case #PB_EventType_RightClick
            DisplayPopupMenu(#MenuTray, WindowID(#WinApp))
            
          Case #PB_EventType_LeftClick
            If GetWindowState(#WinApp) = #PB_Window_Normal
              hideAppWindow()
            Else
              showAppWindow()
              StickyWindow(#WinApp, #True)
            EndIf
        EndSelect
;;;;;;;
;---- Events SYSTEM WINDOW
;;;;;;;
      Case #PB_Event_CloseWindow
        hideAppWindow()
        
      Case #PB_Event_MinimizeWindow
        If optMinimize And loop
          HideWindow(#WinApp, #True)
        EndIf

    EndSelect
    loop = #True
  ForEver
  
  makeIniFile()
  
ErrorHandler:
  
  lHwd = FindWindow_("Progman", #NUL)
  ShowWindow_(lHwd, #SW_SHOWNA)
  RemoveSysTrayIcon(#WinApp)
  DisableWindow(#WinApp, #False)
  ReleaseMutex_(Mutex)
  End
  
EndIf

;;;;;;;
;---- 9 - END
;;;;;;;

; IDE Options = PureBasic 5.60 (Windows - x64)
; CursorPosition = 18
; Folding = DAAJGK--
; Markers = 102,281,502
; EnableXP
; CompileSourceDirectory
; EnablePurifier
; EnableUnicode