modify hosts file with autohotkey without running as administrator - autohotkey

In C:\Windows\System32\drivers\etc, I have the standard hosts file
In C:\Windows\System32\drivers\etc, I have a file named hosts-backup.txt
Using autohotkey, when I press `abc1, I'd like to replace the hosts file with the contents of the file named hosts-backup.txt
Here's what I have so far:
`::
Check := true
SetTimer, CheckOff, 1000 ; 1 second to type in 001
return
:*:abc1::
If Check
; Read the contents of the backup file
FileRead, Contents, C:\Windows\System32\drivers\etc\hosts-backup.txt
if not ErrorLevel ; Successfully loaded.
{
ListVars
Pause
; delete the old hots file
FileDelete, C:\Windows\System32\drivers\etc\hosts.txt
; also tried this with C:\Windows\System32\drivers\etc\hosts
; save a new copy of the hosts file with the content from the backup file
FileAppend, %Contents%, C:\Windows\System32\drivers\etc\hosts.txt
; also tried this with C:\Windows\System32\drivers\etc\hosts
Contents = ; Free the memory.
}
return
CheckOff:
Check := false
return
ListVars returns the following:
Global Variables (alphabetical)
--------------------------------------------------
0[1 of 3]: 0
Check[1 of 3]: 0
Contents[0 of 0]:
ErrorLevel[1 of 3]: 0
When the above script is run, the file at C:\Windows\System32\drivers\etc\hosts is not updated.
How can I fix this?
Update per Blauhirn's suggestions (still not working)
`::
Check := true
SetTimer, CheckOff, -1000 ; 1 second to type in abc1
return
:*:abc1::
If Check{
; Read the contents of the backup file
FileRead, Contents, C:\Windows\System32\drivers\etc\hosts-backup.txt
if not ErrorLevel ; Successfully loaded.
{
ListVars
; delete the old hots file
FileDelete, C:\Windows\System32\drivers\etc\hosts.txt
; save a new copy of the hosts file with the content from the backup file
FileAppend, %Contents%, C:\Windows\System32\drivers\etc\hosts.txt
Contents = ; Free the memory.
}
}
return
CheckOff:
Check := false
return
Update
This appears to be a permissions issue, if I compile the script to an exe and run it as an administrator, it works as expected.
Is there a simple way around this using autohotkey?

This piece of code of yours:
`::
Check := true
SetTimer, CheckOff, 1000 ; 1 second to type in 001
return
CheckOff:
Check := false
return
Will set check to false every 1000 ms repeatedly. "Specify a negative Period to indicate that the timer should run only once". Since it doesn't get set back to true anywhere in your code automatically, this whole timer thingy is kind of useless. What is it you want to achieve with it? Check will only be true within the first 1 second. Thus,
If Check
FileRead, Contents, C:\Windows\System32\drivers\hosts-backup.txt
Will not do anything after the second is over. Also be aware that the if-clause only works for the next line, because you're not using any braces { }.
edit
I might be wrong, but seems that you can spare the timer and simply make a hotstring out of it: (two `` because backtick is the escape character in ahk)
:*:``abc1::
; Read the contents of the backup file
FileRead, Contents, C:\Windows\System32\drivers\etc\hosts-backup.txt
if not ErrorLevel ; Successfully loaded.
{
ListVars
Pause
; delete the old hots file
FileDelete, C:\Windows\System32\drivers\etc\hosts.txt
; also tried this with C:\Windows\System32\drivers\etc\hosts
; save a new copy of the hosts file with the content from the backup file
FileAppend, %Contents%, C:\Windows\System32\drivers\etc\hosts.txt
; also tried this with C:\Windows\System32\drivers\etc\hosts
Contents = ; Free the memory.
}
return
Seems logical to me that only Administrators may edit the system32 folder.. have you tried changing the AutoHotkey.exe to "run ALWAYS as Administrator"? http://technet.microsoft.com/en-us/magazine/ff431742.aspx

Related

How to do multiple actions with AutoHotKey

I haven't used AHK before so it's probably a typographical error but would love any help. I'm trying to click into the search bar on File Explorer, search for the phrase .pdf and then select all of the files. Then I want to click the back button so I can go to the parent folder and then paste the files. Lastly, I want to click home and then click the folder at the top and delete it. The purpose for all of this is to take all the PDFs out of the subfolders and put them into the main folder, and then delete the subfolders.
I've tried this.
^e::
Click 3510,201
Send, *.pdf*{Enter} ^a ^x
Click 2601,200
^v
Send {Home}
Click 2896,266
Send {Delete}
Return
Currently, when I try it, it deletes all of the files in my folder.
There appear to be a few issues with your code and addressing each may make your code work, but I suggest a different, more robust, approach.
Try using a file-loop to find all files in a location (and sub-locations) with the PDF extension and FileMove to move these to your desired location.
https://www.autohotkey.com/docs/commands/LoopFile.htm
https://www.autohotkey.com/docs/commands/FileMove.htm
Edit (per comments)
There are a few ways to grab the path from file explorer. The easiest I can think is to send a ^{f4} which will select the full path, from there you can send a ^c copy it to the clipboard and use it for the file-loop path.
Edit2 (working example)
f1::
WinGetClass , vClass , A
If !(vClass = "CabinetWClass") ; Stops here if file explorer isn't the active window
Return
clipboard := ""
Send , ^{f4}{esc}^c
ClipWait , 1
If ErrorLevel ; Stops here if nothing was copied
Return
Loop , Files , % clipboard . "\*.pdf" , R
FileMove , %A_LoopFileLongPath% , %clipboard%
Return
Note that this will fail for named locations like "Documents", Downloads", or "This PC"; it must be a full path to work. Also, this will not delete the subfolders, but see FileRemoveDir for help with that.
https://www.autohotkey.com/docs/commands/FileRemoveDir.htm
Edit3 (using FileRemoveDir to delete empty folders)
This snippet will delete empty folders, starting with the deepest level and working to the top level. I take no credit for this as it by SKAN from the original AHK forums.
SetBatchLines -1
FileSelectFolder, Folder, , % (Del:=0), Purge Empty Folders
If ( ErrorLevel Or Folder="" )
Return
Loop, %Folder%\*, 2, 1
FL .= ((FL<>"") ? "`n" : "" ) A_LoopFileFullPath
Sort, FL, R D`n ; Arrange folder-paths inside-out
Loop, Parse, FL, `n
{
FileRemoveDir, %A_LoopField% ; Do not remove the folder unless is empty
If ! ErrorLevel
Del := Del+1, RFL .= ((RFL<>"") ? "`n" : "" ) A_LoopField
}
MsgBox, 64, Empty Folders Purged : %Del%, %RFL%
This can be adapted to fit the example from Edit2 and you should be good to go!

"Send %variable%" evals my variable before sending it

I have an autohotkey script(A) that write another script(B).
The important line in my script(A):
InputBox variable, Variable
Send %variable%
Then, if i input:
helloworld{enter}
it will write "helloworld" and insert a new line in my script(B) instead of just writing "{enter}"
How do I force it to write %variable% without interpreting it beforehand ?
I agree with the other answer that using FileAppend would be a better method. However, if you truly want to accomplish this using the send command, then you need to use {raw} mode. You can read about {raw} in the documentation:
https://autohotkey.com/docs/commands/Send.htm
InputBox variable, Variable
Send, {raw}%variable%
The simplest way to write another script (scriptB) from scriptA is to use FileAppend:
variable = helloworld
FileAppend,
(
; a line of code
Send %variable%{enter}
; another line of code
)
,C:\scriptB.ahk
Run, edit "C:\scriptB.ahk"
; or:
; Run, C:\scriptB.ahk
or
InputBox variable, Variable, Enter a variable:
if not ErrorLevel
{
FileAppend,
(
; a line of code
Send %variable%{enter}
; another line of code
)
,C:\scriptB.ahk
Run, edit "C:\scriptB.ahk"
; or:
; Run C:\scriptB.ahk
}
return

AHK Load hotstrings without using #Include Subroutines.ahk

I have a txt file named subroutines.ahk in it is the below hotstring.
::btw::Thank you for the help
I know if I want to run my hotstring from another script all I have to do is include it in that script using:
#include subroutines.ahk
But I dont want to do that Instead from another script I want to Loop read the contents of subroutines.ahk and load the hotstring variables that way .
Loop, read,subroutines.ahk
{
Loop, parse, A_LoopReadLine, %A_Tab%
{
MsgBox, Field number %A_Index% is %A_LoopField%.
}
}
I would really appreciate your help on how I can load my hotstring variables into another script that way.
I plan to encode the hotsrings in subroutines.ahk and than decode the contents of the subroutines.ahk in the second program and load it into memory.
In the above example though i am first trying to figure out how I can loop read the txt file and run the hot strings from there.
Demonstrates the use of hotkeys that map to dynamic values
; Replace hash values with whatever method you use to decrypt your hotstrings
decrypted := {abc: "alpha", def: "beta"}
::abc::
Send % decrypted["abc"]
return
::def::
Send % decrypted["def"]
return
When you type "abc", "alpha" appears instead
# directives are processed only once when the script is launched. To work around this limitation, you can use reload to execute dynamically generated script that includes # directives.
Code sample:
When F1 is pressed, use the contents of "subroutines.ahk" to generate and run code that utilizes # directives:
F1:: reload_hotstrings()
#Include temp.ahk
reload_hotstrings()
{
FileDelete temp.ahk
loop read, subroutines.ahk
{
MsgBox Hotstring number %A_Index% is %A_LoopReadLine%.
FileAppend %A_LoopReadLine%`n, temp.ahk
}
reload
}
Alternatively, if you want to automatically generate hotkeys code whenever autohotkey starts:
I'm using source.dat for your subroutines.ahk file
Code sample:
FileGetTime source_time, source.dat
FileGetTime compiled_time, compiled.ahk
; calculate time difference between source and compiled files
EnvSub source_time, %compiled_time%, Seconds
; compile and run if source newer
if source_time > 0
{
FileDelete compiled.ahk
loop read, source.dat
{
FileAppend %A_LoopReadLine%`n, compiled.ahk
}
reload
}
#Include compiled.ahk

Empty a text file with Autohotkey?

I'm trying to find a way to empty a text file with Autohotkey. Unfortunetely I was unable to find any build in function that allowes you to do just that. The best solution I could come up with would be:
FileRead, TheText, file.txt
StringReplace, NewText, TheText, 'text to search for', 'replacement text', All
FileDelete, file.txt
FileAppend, %NewText%, file.txt
However, this solution won't work for me because I'm writing a tool that will add or delete content from the Windows hosts file. Deleting the hosts file is out of the question ofcourse.
So instead I'm loading the hosts file content into a variable, replace any addition made by my program with an empty string (to restore the original hosts file content) inside this variable and write it back to the hosts file. But in order to do that I need to empty the file first or it would just append the variable to the existing content.
For those who are curious about my program:
The program will enable/disable the automatic Skype login feature on outlook.com or hotmail.com. Ofcourse without cauing any problems for the mail functions or the Skype client.
Here is a way using a fileobject (req ahk 1.1+)
just put this example in a new script file and run it to see how it works
/* String1
* String2
* String3
* String4
* String5
* String6
* String7
* String8
* String10
*/
msgbox % CutString(A_ScriptFullPath, "String7")
CutString(filePath, Needle)
{
fileObj := FileOpen(filePath, "rw"), pointerPos := fileObj.Pos, RegExMatch(fileObj.read(), "O)" needle, match)
fileObj.Seek(0), newText .= fileObj.read(match.pos-1), fileObj.Seek(match.pos + match.Len), newText .= fileObj.read()
fileObj.Seek(pointerPos), fileObj.Write(newText), fileObj.length := StrLen(newText), fileObj.Close()
return match.value
}
This function uses RegEx to find the pos and size of "needle" string in a file and then rewrites and resizes the file content without it.
You can also rewrite the function to simply cut out lines from the file.
I hope this does what your need
What about renaming it? I've done this before.
hosts := "c:\windows\system32\etc\hosts"
fileread, hostsText, %hosts%
stringreplace, newText, %hostsText%, %searchtext%, %replacementtext%, all
fileappend, %hostsText%, %hosts%.bak ;make backup
filedelete, %hosts%.temp ;delete the temp file if it exists
fileappend, %newtext%, %hosts%.temp ;write new text to a blank file
filemove, %hosts%.temp, %hosts%, 1 ;rename the new file to hosts - set flag to 1 to overwrite
Then you need to flush the DNS, no?
Run cmd /c ipconfig /flushdns
Follow the below steps:
Run, notepad.exe invalid_response.txt
Sleep, 200
Send, ^a
Send, {Del}
Sleep, 200
Send, !{F4}
Send, {HOME}
Sendraw, #
Send, ^s
Sleep, 200
Send, !{F4}
Exit

Autohotkey clipboard variable holding values forever?

I have the below simple code, which sends keystrokes for text in the clipboard with a 15ms delay in between characters (I use this to traverse huge lists of treeview elements).
Issue: If I have copied 'text1' to clipboard, followed by 'text2', this script outputs 'text1text2' instead of 'text2' alone.
If I reload the script, then it prints 'text2'.
Is there a mistake in the below code, or is it a bug in implementing %clipboard% in Autohotkey 1.1.14.03 ?
#v::
textToType=" "
textToType=%clipboard%
LoopCount:=StrLen(textToType)
;StringLen, LoopCount, textToType
Array%LoopCount%:=textToType
loop %LoopCount%
{
theChar:=Array%A_Index%
Send %theChar%
sleep 15
}
return
Update: Thanks for pointing out smarter ways of doing this, but I would still like to figure out what is wrong in the above piece of code.
Update 2:
The mistake was in my understanding of the AHK syntax. Array%LoopCount%:=textToType assigns the whole string value in textToType to the (LoopCount)th STRING element of the STRING array named 'Array'.
Update 3:
(Thanks #John Y for clarifying)
Actually, there's no "declared" array at all, in a traditional sense. You just have a bunch of individual variables, dynamically created as needed, that happen to have names with numbers at the end. Array1 and Array2 are not elements in some Array object. They are just two completely independent variables. AutoHotkey provides a way to glue numbers onto the ends of names, so you can use them like an array.
The reason your script doesn't work properly is because you're using a pseudo-array to store different words from your clipboard.
I've commented up your code to explain what it does:
#v::
textToType := "" ; Empty variable
textToType := Clipboard ; Move clipboard into variable
; Get lenght of the word
; Used as array index / loop count
LoopCount := StrLen(textToType)
; Put the clipboard in an array at index 'LoopCount'
Array%LoopCount% := textToType
; Loop through the array as many times
; as the string is long
Loop % LoopCount
{
; Retrieve the word at this index in the array
theChar := Array%A_Index%
; Send the whole word
Send, % theChar
sleep 15
}
return
Instead of sending each character at a time, you're sending whole words from specific indexes in your Array array.
Say you copy the word Dragon, that word is 6 letters long. So you'd put that in Array6, then you'd loop through your array 6 times using the same variable. At which point the loop would take each index at a time and move it into theChar. On your 6th lap in the loop you'd put Array6 into theChar and print the whole word at once.
Then you copy the word Stackoverflow. That's going to go into Array13, and we're going to loop 13 times. On the 6th lap we're going to print out Dragon which is in Array6, and then keep going until we reach 13 where we'll print Stackoverflow since that is in Array13.
So that's why your script isn't doing what you want it to. Hopefully this helps a little.
See the code sample alpha bravo posted, that's the correct way of achieving what you want to do.
keep it simple
#v::
loop Parse, Clipboard
{
Send %A_LoopField%
sleep 15
}
return
There must be a bug in implementation of clipboard assignment in AHK. With the below code, the behaviour of AHK is that everytime the value of dir is accessed, AHK fetches the latest value from clipboard, instead of fetching the value of dir at the time the script was activated.
; Remove all CR+LF's from the clipboard contents:
dir = %clipboard%
sleep 100
dir := StrReplace(dir, "`r`n")
EDIT:
To fix this, I added 1 second sleep before clipboard assignment code:
sleep 1000
; Remove all CR+LF's from the clipboard contents:
dir = %clipboard%
dir := StrReplace(dir, "`r`n")
100 millisecond sleep didn't seem to work.
Accessing value of dir now only gives value of last clipboard assignment at activation.