Page 1 of 1

Alternate Data Stream as data storage

Posted: 14 Feb 2020, 02:42
by haichen
I like autohotkey programs that only consist of one script or one exe file.
That's why I don't like ini files. But to save data beyond the end of the program you can use ADS.
ADS or Alternate Data Streams are separately addressable attachments to a file.
Under Windows the file system NTFS allows invisible storage of content as alternate data streams in files and folders.
My functions read and write values in ini-format to an ADS in the script or compiled program.

Requirement: NTFS file system

Code: Select all

ADSreset(stream:="sfdata"){
	filedelete,% A_ScriptFullPath ":" stream
	return
}

ADSload(stream, params*){
	stream_1:=A_ScriptFullPath ":" stream
	; If something goes wrong, you can delete the ADS by
	; renaming the Program eg ads.ahk => adsreset.ahk - after that, rename it back.
	; ADS will be also deleted by copying to fat32
	if InStr(A_ScriptName, "reset"){
		filedelete,%stream_1%
		return
	}
	
	for index,param in params
		IniRead, %param%, %stream_1%, Section, %param% , %a_space%

	return
}


ADSsave(stream, params*){
	stream_1:=A_ScriptFullPath ":" stream
	if InStr(A_ScriptName, "reset"){
		filedelete,%stream_1%
		return
	}
	
	for index,param in params
		IniWrite,% %param%, %stream_1%, section, %param%
	
	return
}
simple example:

Code: Select all

Gui +Resize -DPIScale
Gui, Add, Edit, x10 y10 w200 h200 vMyEdit, 
Gui, Add, text, x+10  w160 h200 , Write some text, save data, reload (or quit and restart) the program. Then load data
Gui, Add, button,x10 gsave,save data to ADS
Gui, Add, button,gload,load data from ADS
Gui, Add, button,greload,reload the program
Gui, Add, button,y+20 greset,reset (delete ADS)
gui, show, x200 y200 w400, Alternate Data Stream as Ini
return

load:
Gui, submit, noHide
ADSload("sfdata", "MyEdit")
GuiControl,,myEdit, % MyEdit
return

save:
Gui, submit, noHide
ADSsave("sfdata", "MyEdit")
return

reload:
Reload
return

esc::
GuiClose:
ExitApp

Reset:
ADSreset("sfdata")
return

ADSreset(stream:="sfdata"){
	filedelete,% A_ScriptFullPath ":" stream
	return
}

ADSload(stream, params*){
	stream_1:=A_ScriptFullPath ":" stream
	; If something goes wrong, you can delete the ADS by
	; renaming the Program eg ads.ahk => adsreset.ahk - after that, rename it back.
	; ADS will be also deleted by copying to fat32
	if InStr(A_ScriptName, "reset"){
		filedelete,%stream_1%
		return
	}
	
	for index,param in params
		IniRead, %param%, %stream_1%, Section, %param% , %a_space%

	return
}


ADSsave(stream, params*){
	stream_1:=A_ScriptFullPath ":" stream
	if InStr(A_ScriptName, "reset"){
		filedelete,%stream_1%
		return
	}
	
	for index,param in params
		IniWrite,% %param%, %stream_1%, section, %param%
	
	return
}
more complex example:

Code: Select all

global var1, var2, var3, laststart, gx, gy, gh, gw

FormatTime, start , a_now, dd.MM.yyyy HH:mm:ss

ADSload("sfdata","var1","var2","var3","laststart")
if (var1="") or (var2="") or (var3="") or (laststart="")
	var1:=0, var2:=200, var3:="hallo ", laststart := start

var1  +=1
var2  *=3
var3 .="a"


Gui +Resize -DPIScale
Gui, Add, Text,, Resize and move the Window. `nClose and restart the Script.
Gui, Add, Text,vtt w300 h150, 
Gui, Add, button,greset,Reset
gui, show, x200 y200 w400 h300, Alternate Data Stream as data storage
loadGP()
GuiControl, , tt,  % "counter = " var1 " times started `nVar = " var2 "`notherVar = " var3 "`nLast Programstart = " laststart "`n`nX = " gx "`nY = " gy "`nWidth = " gw "`nHeight = " gh
return

esc::
GuiClose:
saveGP()
laststart := start
ADSsave("sfdata","var1","var2","var3","laststart")
ExitApp

saveGP(){
	WinGetPos, gx, gy, gw, gh, a
	ADSsave("sfdata","gx","gy","gw","gh")
	return
}


loadGP(){
	ADSload("sfdata","gx","gy","gw","gh")
	if (gx="") or (gy="") or (gw="") or (gh="")
		gx:=200, gy:=200, gw:=400, gh:=300
	else
		WinMove, a, , %gx%, %gy%, %gw%, %gh%
	return
}



Reset:
Gui, Submit , NoHide
ADSreset("sfdata")
Reload
return

ADSreset(stream:="sfdata"){
	filedelete,% A_ScriptFullPath ":" stream
	return
}

ADSload(stream, params*){
	stream_1:=A_ScriptFullPath ":" stream
	; If something goes wrong, you can delete the ADS by
	; renaming the Program eg ads.ahk => adsreset.ahk - after that, rename it back.
	; ADS will be also deleted by copying to fat32
	if InStr(A_ScriptName, "reset"){
		filedelete,%stream_1%
		return
	}
	
	for index,param in params
		IniRead, %param%, %stream_1%, Section, %param% , %a_space%

	return
}


ADSsave(stream, params*){
	stream_1:=A_ScriptFullPath ":" stream
	if InStr(A_ScriptName, "reset"){
		filedelete,%stream_1%
		return
	}
	
	for index,param in params
		IniWrite,% %param%, %stream_1%, section, %param%
	
	return
}

Re: Alternate Data Stream as data storage

Posted: 14 Feb 2020, 03:21
by haichen
I have added a simple example.

Re: Alternate Data Stream as data storage

Posted: 14 Feb 2020, 12:28
by guest3456
haichen wrote:
14 Feb 2020, 02:42
But to save data beyond the end of the program you can use ADS.
ADS or Alternate Data Streams are separately addressable attachments to a file.
Under Windows the file system NTFS allows invisible storage of content as alternate data streams in files and folders.
some old information for reference:
https://autohotkey.com/board/topic/65007-trickhide-ini-file-as-part-of-the-script-file/
haichen wrote:
14 Feb 2020, 02:42
I like autohotkey programs that only consist of one script or one exe file.
That's why I don't like ini files.
you can store the ini information directly in your script inside a comment section:

Code: Select all

/*
[section]
item1=1
*/

IniRead, value, %A_ScriptFullPath%, section, item1, %A_Space%
value++
IniWrite, %value%, %A_ScriptFullPath%, section, item1

Re: Alternate Data Stream as data storage

Posted: 14 Feb 2020, 13:53
by haichen
I have been writing autohotkey scripts for quite some time, but I didn't know that. Thanks a lot. Really great.

Re: Alternate Data Stream as data storage

Posted: 15 Feb 2020, 02:15
by haichen
A nice trick, but it only works with uncompiled scripts.
Saving as Alternate Data Stream works without changes even after compilation.
So it allows the distribution of a compiled exe.

Re: Alternate Data Stream as data storage

Posted: 15 Feb 2020, 20:41
by guest3456
haichen wrote:
15 Feb 2020, 02:15
A nice trick, but it only works with uncompiled scripts.
Saving as Alternate Data Stream works without changes even after compilation.
So it allows the distribution of a compiled exe.
yes obviously the ini trick needs an uncompiled script. data stream is a nice method

Re: Alternate Data Stream as data storage

Posted: 16 Feb 2020, 04:45
by just me
You have to keep in mind that when you copy the file to a none NTFS file system (i.e. a FAT formatted USB stick) all ADS information might be stripped.

Re: Alternate Data Stream as data storage

Posted: 16 Feb 2020, 05:38
by haichen
Yeah, I wrote that as a comment in the code. But as long as you don't want to start the program from USB-stick or any other Fat/Fat32 filesystem, it's like installing the program. I don't think you normally take an ini file with program settings with you to another computer. What I don't know is if the stream is preserved when downloading (OneDrive, Dropbox). Maybe you don't want to.

An error message would certainly be useful.

Code: Select all

DriveGet, fs, FileSystem,  % SubStr(A_ScriptDir,1,3)
if (fs <>"NTFS")
   msgbox, Program settings can only be saved under NTFS!
}