Setting up install files to share for prototyping with security

Following on from Compile a program for install for distribution article.

I’m thinking about doing some paid work, so I want to be able to send prototype scripts to people to use, but not the full version, or rather a version that will run for a while and then stop.

That way the customer can test it out and see whether it meets their need, but not take it and run without paying if it does.

After playing with a couple of installer setup programs (see other post) I’m satisfied, at the moment with NSIS. So this post is about using that.

It seems to require a rethink on how I write files and also think about using files.

Whatever procedures I setup at this point, I do not want to mess with the OS registry at this point. So do it all with files.

Processes for short active scripts

There are a couple of methods that I’ve thought of (as in re-inventing the wheel) and they are ,

  1. via count, you can run so many times, then it stops
  2. a specific duration (trial period).

File Structure

For the file structure, you are giving them an .EXE compiled file (so they can’t EASILY look at the script- AHK scripts viewable) and whatever added support files that script requires, license, readMe, Library Files and importantly .ini files and write-to files.

The .EXE file is compiled, so can’t change, so any changes- eg Start/End dates has to be put into a writable file such as an ini file. So if you do a FROM DATE (first time the program runs, eg INSTALL DATE) to an END DATE eg 30 days after the FROM DATE, then you need to be able to write these date somewhere and a .ini file is a good place to do so.

The same goes for COUNT. If you preset the .ini file to Count 5 and count down, or set as 0 and count up.

So, main file .EXE and a counter with an IF /Then statement of some kind in the .EXE . If the COUNT less than 5 then DO …. The main script, if NOT then Quit with a Message Box saying no more playing, program has expired.

File Locations & where to define their path

I think, if you fix the path in the compiled .EXE file then you have to re-set each time. When designing the program you know what files you want, but you may change your mind later on where you want to locate them.

In fact, there is the opportunity to allow for the client to say where they want to keep & name the files that they use. I may look at that later, for the moment I want to define the paths.

So, better to have the file paths in the ini file and get the .EXE to pull them in to read them, this will work with all the other files apart from the ini file as its got to be found by the .EXE file, actually you can create another ini file, but this will then give away the location of the hidden ini file unless you do jiggery pokery and only give half the name and allow for a concatenation inside the compiled file.

File locations & first iteration

So you don’t want your .ini file in the same folder as your .EXE file, otherwise they can just reset the count. Then they can use the program regardless.

An aside Note: The main .EXE has to be able to find the .ini file, otherwise it NEEDS TO BREAK. So if the directories are not setup right and the .EXE can’t check the .ini file then it shouldn’t be able to work at all. This would stop someone moving all the files somewhere else to run them.

So you want to put your .ini file somewhere a bit obscure, like in the AppData folder, that seems to be the programmers common place that people hide their files. As the AppData folder is a hidden folder you do need know its there to go looking for it.

Install Program

First Install

On first install the programs (using NSIS) all go to C:\Program Files (x86)\ and because this is a program folder they don’t like you writing to it unless you have Admin permission. So it makes sense not to put files that need to be written to in this directory.

Now, if you have a file that you need to write to, for say, Time Capture or FileSaveLaunch, then the .TXT or .CSV file needs to be in a place that is :

  1. Accessible by users, if they need to copy/edit that file
  2. In a writable place such as C:\users\%A_User%\Documents or C:\users\%A_User%\Downloads, so you need to make sure that its in the current users directory so easily found.
  3. Set the ini file such that it can’t be overwritten on an update. This stops the file from starting anew at a new install. Otherwise you just reinstall and the file starts afresh. So new program working on each new install.

So, in your compiled .EXE file, sitting in C:\Program Files (x86)\ you need to make sure that the paths to its files are correct and working.

This means allowing for a File location at the top of the ..to be compiled file and then using a variable in the code below for that location, this makes it easy to relocate files you are reading or writing to.

In the install file you need to be able to create new directories in different places

Uninstall script in the First install build

You need to allow for both the write to file & the ini file not to be uninstalled when looking to uninstall the program, this allows you to keep data and also remember, in the ini file that the timer/counter has gone past its date/count

If you delete the ini fil then re-installing will overwrite/create anew so that you can use the same setup to start again. That is why its important that 1/ you do not delete the ini file and that you don’t allow overwrite if you try and install again.

2nd Install/update

Some situations that need to be managed:

1. Client tries reinstalling existing install setup file.

Directly reinstalling the original setup file to overwrite the previous install.

Need to block overwrite of .ini & any data files- in NSIS you need to :

SetOverwrite off
File "Time-Capture_File_HotKeys.ini"

2. Clients program expired without testing. Too busy, or not enough attempts.

In this scenario you want to be able to delete the existing .ini file and maybe even reload most of the program files (so updating looks obvious). So that you can reset clock/count in ini file, and possibly send an adjusted ini file

  • create a new script installer that will delete old ini file and write a new one.
  • Keep existing data .txt files held elsewhere (ie ignore them)

The problem with this one , unless you can make them only run it once, or give them a key that can only be used once, they can keep on using it to update the program so it will always run.

One thought is a method of running this is to install a dud file somewhere , and if that exists then don’t overwrite .ini file, I’m not sure you can do that with the install. Needs a bit of research

Another thought is to have 3 or 4 .ini files, all with their Timers set at 0. The main program reads an ini file, say Count.ini in the main Folder, it has a single number in it, 1, 2, 3 ,4 and depending on number is which hidden ini file it reads. So you send someone an update install script that updates the Count.ini file. When running the program the Count.ini file points to which hidden ini file to read. A little crude, but it will work.

3. You’ve updated the file for changes to program and you want to re-set timer/count on ini file so they can test the new program

In this scenario you have updated code in main file, so you can create a new version and also create a different .ini and .txt files that you can send data to, keeping them in the same folders as the original .ini and .txt files just with a version update.

Scripts

  1. ShareFiles.ahk
/*
ShareFilesTest.ahk
This is a setup test for sharing files with others that will run for a specific period then stop

The setup is planned to allow others to test the compiled code for X number of runs then it stops working


*/
;-----------------------Setup -----------------------------
#NoEnv
#SingleInstance, Force
SetWorkingDir %A_ScriptDir%
;-----------------------Include libraries -----------------------------
#Include %A_ScriptDir%\Notify.ahk

;-----------------------Files & Locations -----------------------------
;CountIni has a single number, showing which ini file to be used. Defaults to 1 (the first file)
;If client wants to run more of said program- send updated CountIni file with say 2 in it and 
;it will open the next sharefile.ini where counter starts back at 0. 
CountIni = ShareFileInf.ini 

IniRead, FileCount, %CountIni%, fileNum, FileCount

;location of ini files
IniFile = %A_AppData%\MSys\ShareFile%FileCount%.ini 
;The one below for testing if working 
;IniFile = C:\Users\%A_UserName%\Downloads\AHK-Working\ShareFilesScript\ShareFile.ini


;get location of text file m, define as FileList
IniRead, FileListDir, %IniFile%,FileLoc, FileListDir

FileList= C:\Users\%A_UserName%\%FileListDir%

MsgBox, %FileList%  ; this is just to check that %A_UserName% is working correctly and plointing to the correct file. 
;------------------GLOBAL VARIABLES ----------------------

;------These for Notify popup class-----------------
Text:= "Share Files Started"
Background=0x456789 ; a bluey color 
Color=0xFFFFFF ; white;
Title = 	SHARE FILES ;This is my TITLE
TitleFont="Tahoma"
TitleColor= 0xFFFFFF ; white
TitleSize=20
Size=12 ;text height
Time= 3000 ;3000
Icon =145 ;297or145  tick,132 "x", 278 blue circle exclamation , 234 orange triangle exclamation; 220 no way,211 question in blue circle
IconSize =30


;------------------INITIATE INSTANCE OF NOTIFY ----------------------
Notify:=Notify()
;------------------NOTIFY POPUP  STARTING THE PROGRAM ----------------------
Notify().AddWindow(Text,{Size:Size,Icon:Icon "," Ico,IconSize:IconSize,Title:Title,TitleColor:TitleColor,TitleFont:TitleFont,TitleSize:TitleSize,Background:Background,Color:Color,Time:Time}) ;Flash:1000,


FileRead, Text4Message, %FileList%

;------------------ PROGRAM STARTS----------------------
;make sure path to & name of  .ini file set 

;------------------ Read INI file for Count & MaxNumber----------------------
IniRead, Countx, %IniFile%,Counter, counter1
;Test check count
IniRead, MaxRun, %IniFile%, MaxCount, MaxRun
MsgBox, %Countx% of total %MaxRun%  of %FileCount%   ; for display you need the %% signs either side v

If (Countx<=MaxRun) { ; for function you DO NOT need the %% signs either side

/*
THIS IS WHERE YOU PUT PROGRAM CODE
Between brackets 
*/

    MsgBox, , Share Files Message Box, %Text4Message%`n If %Countx% less than %MaxRun% `nprogram will run `nEnjoy    ;Welcome to the %Countx% of %MaxRun%, after which program stops 
    Countx+=1 ; for function you DO NOT need the %% signs either side
    IniWrite, %Countx%, %IniFile%, Counter, counter1
     ExitApp




    }else{
/*
THIS IS WHERE YOU PUT EXIT MESSAGE WHEN PROGRAM TO STOP
Between brackets 
*/


    MsgBox, , CLOSE Share Files Message Box,  %Text4Message% `nIf %Countx% bigger than %MaxRun% `nthe program stops working `nContact `[email protected]     ;You are on %Countx% of %MaxRun% so program won't run `n contact provider at `[email protected]
    ExitApp
    }

2. ShareFile.ini

[Counter]
counter1=0
[MaxCount]
MaxRun = 4
[FileLoc]
;FileList= C:\Users\%A_UserName%\Documents\ShareFile\ShareFile.txt 
; DraxList= C:\Users\%A_UserName%\
;Note, rest of path setup in main script as it doesn't like %% coming from this file
FileListDir= Documents\ShareFile\ShareFile.txt

3. NSIS install Script .nsi (in HS NIS Edit program)

Note- I’ve commented out all the items to do with modifying Windows registry . I wanted a simple install.

; Script generated by the HM NIS Edit Script Wizard.
/*
This is 1st Install Script
It installs main program in C:/Program Files (+86)/ShareFiles
It installs ini in Current User/ AppData
It installs .txt in Current User/ Documents/ShareFiles

ini & txt files not to be deleted on uninistall
ini & txt files not to be overwritten if 1/ Reinstall program

Max Drake
Sunday 24 October 2021
*/

; HM NIS Edit Wizard helper defines
!define PRODUCT_NAME "ShareFiles"
!define PRODUCT_VERSION "1.0"
!define PRODUCT_PUBLISHER "Drax"
!define PRODUCT_WEB_SITE "https://max-drake.cc"
;!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\ShareFiles.exe"
;!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
;!define PRODUCT_UNINST_ROOT_KEY "HKLM"
;!define PRODUCT_STARTMENU_REGVAL "NSIS:StartMenuDir"

; MUI 1.67 compatible ------
!include "MUI.nsh"

; MUI Settings
!define MUI_ABORTWARNING
!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"

; Welcome page
!insertmacro MUI_PAGE_WELCOME
; License page
!insertmacro MUI_PAGE_LICENSE "..\..\Licence\Licence-MD.txt"
; Directory page
!insertmacro MUI_PAGE_DIRECTORY
; Start menu page
var ICONS_GROUP
!define MUI_STARTMENUPAGE_NODISABLE
!define MUI_STARTMENUPAGE_DEFAULTFOLDER "ShareFiles"
/*
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "${PRODUCT_UNINST_KEY}"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${PRODUCT_STARTMENU_REGVAL}"
*/
!insertmacro MUI_PAGE_STARTMENU Application $ICONS_GROUP
; Instfiles page
!insertmacro MUI_PAGE_INSTFILES
; Finish page
!define MUI_FINISHPAGE_RUN "$INSTDIR\ShareFiles.exe"
!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\ReadMe.rtf"
!insertmacro MUI_PAGE_FINISH

; Uninstaller pages
!insertmacro MUI_UNPAGE_INSTFILES

; Language files
!insertmacro MUI_LANGUAGE "English"

; MUI end ------

Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
OutFile "..\Builds\ShareFiles_V1.1_20211025.exe"
InstallDir "$PROGRAMFILES\ShareFiles"
;InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
ShowInstDetails show
ShowUnInstDetails show

Section "Prog Files" SEC01
  SetOutPath "$INSTDIR"
  SetOverwrite on
  File "..\Files\ShareFiles.exe"
  File "..\Files\share.jpg"
  File "..\Files\share.ico"
  File "..\Files\ReadMe.rtf"
  File "..\Files\Notify.ahk"
  File "..\Files\launch.ico"
  File "..\Files\ShareFileInf.ini"

; Shortcuts
  !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
  CreateDirectory "$SMPROGRAMS\$ICONS_GROUP"
  CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\ShareFiles.lnk" "$INSTDIR\ShareFiles.exe"
  CreateShortCut "$DESKTOP\ShareFiles.lnk" "$INSTDIR\ShareFiles.exe"
  !insertmacro MUI_STARTMENU_WRITE_END
SectionEnd

Section "Hidden" SEC02
  SetOutPath "$APPDATA\MSys"
  SetOverwrite off   ; DO NOT OVERWRITE EXISTING INI
  File "..\Files\ShareFile1.ini"
  File "..\Files\ShareFile2.ini"
  File "..\Files\ShareFile3.ini"
  File "..\Files\ShareFile4.ini"

; Shortcuts
  !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
  !insertmacro MUI_STARTMENU_WRITE_END
SectionEnd

Section "WriteFiles" SEC03
  SetOutPath "$DOCUMENTS\ShareFile"
  SetOverwrite off      ; DO NOT OVERWRITE EXISTING DATA
  File "..\Files\ShareFile.txt"

; Shortcuts
  !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
  !insertmacro MUI_STARTMENU_WRITE_END
SectionEnd

Section -AdditionalIcons
  SetOutPath $INSTDIR
  !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
  WriteIniStr "$INSTDIR\${PRODUCT_NAME}.url" "InternetShortcut" "URL" "${PRODUCT_WEB_SITE}"
  CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Website.lnk" "$INSTDIR\${PRODUCT_NAME}.url"
  CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Uninstall.lnk" "$INSTDIR\uninst.exe"
  !insertmacro MUI_STARTMENU_WRITE_END
SectionEnd

Section -Post
  WriteUninstaller "$INSTDIR\uninst.exe"
  /*
  WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\ShareFiles.exe"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\ShareFiles.exe"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
  */
SectionEnd


Function un.onUninstSuccess
  HideWindow
  MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer."
FunctionEnd

Function un.onInit
  MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" IDYES +2
  Abort
FunctionEnd

Section Uninstall
  !insertmacro MUI_STARTMENU_GETFOLDER "Application" $ICONS_GROUP
  Delete "$INSTDIR\${PRODUCT_NAME}.url"
  Delete "$INSTDIR\uninst.exe"
  ;Delete "$DOCUMENTS\ShareFile\ShareFile.txt"  ; don't delete the data
  ;Delete "$APPDATA\MSys\ShareFile.ini"     ;don't delete the ini
  Delete "$INSTDIR\launch.ico"
  Delete "$INSTDIR\Notify.ahk"
  Delete "$INSTDIR\ReadMe.rtf"
  Delete "$INSTDIR\share.ico"
  Delete "$INSTDIR\share.jpg"
  Delete "$INSTDIR\ShareFiles.exe"
  

  Delete "$SMPROGRAMS\$ICONS_GROUP\Uninstall.lnk"
  Delete "$SMPROGRAMS\$ICONS_GROUP\Website.lnk"
  Delete "$DESKTOP\ShareFiles.lnk"
  Delete "$SMPROGRAMS\$ICONS_GROUP\ShareFiles.lnk"

  RMDir "$SMPROGRAMS\$ICONS_GROUP"
  RMDir "$INSTDIR"
  ;RMDir "$DOCUMENTS\ShareFiles"     ;don't delete the data
  ;RMDir "$APPDATA\MSys"             ;don't delete the ini

  ;DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
  ;DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}"
  SetAutoClose true
SectionEnd

Install Files can be downloaded HERE

End comment

I’m still trying to work out the logic for when you get the program resolved and you need to give someone the full program. What is to stop them from just giving it to everyone? How do you protect your code then? That is another topic and another article after this.

Its interesting looking at the process and structure of the files and re-writing the script to be able to allow this to happen.

A fun exercise. I think you’ Stream Deck have to install this without a GUI so you don’t see where the files get written to. I need to check that out.

Video

https://youtu.be/zVqk-KoufP0