├── logo.png ├── .gitignore ├── Lib ├── GetExeMachine.ahk └── AHKType.ahk ├── README.md ├── COPYING ├── ErrorCodes.md ├── IconChanger.ahk ├── Compiler.ahk ├── ScriptParser.ahk └── Ahk2Exe.ahk /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lexikos/Ahk2Exe/HEAD/logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.bin 3 | *.bak 4 | *.db 5 | *.zip 6 | /__debug.ahk -------------------------------------------------------------------------------- /Lib/GetExeMachine.ahk: -------------------------------------------------------------------------------- 1 | ; 2 | ; File encoding: UTF-8 3 | ; 4 | 5 | GetExeMachine(exepath) 6 | { 7 | exe := FileOpen(exepath, "r") 8 | if !exe 9 | return 10 | 11 | exe.Seek(60), exe.Seek(exe.ReadUInt()+4) 12 | return exe.ReadUShort() 13 | } 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ahk2Exe # 2 | 3 | Ahk2Exe is the official AutoHotkey script to EXE converter, which is written itself in AutoHotkey. 4 | 5 | http://www.ahkscript.org/ 6 | 7 | 8 | ## How to Compile ## 9 | 10 | Ahk2Exe can compile itself, just be sure to use a recent AutoHotkey self-contained binary. 11 | 12 | 13 | ## To do ## 14 | 15 | - Handle FileInstall on same-line If* commands. 16 | 17 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /Lib/AHKType.ahk: -------------------------------------------------------------------------------- 1 | ; 2 | ; File encoding: UTF-8 3 | ; 4 | 5 | ; Based on code from SciTEDebug.ahk 6 | AHKType(exeName) 7 | { 8 | FileGetVersion, vert, %exeName% 9 | if !vert 10 | return 11 | 12 | StringSplit, vert, vert, . 13 | vert := vert4 | (vert3 << 8) | (vert2 << 16) | (vert1 << 24) 14 | 15 | exeMachine := GetExeMachine(exeName) 16 | if !exeMachine 17 | return 18 | 19 | if (exeMachine != 0x014C) && (exeMachine != 0x8664) 20 | return 21 | 22 | if !(VersionInfoSize := DllCall("version\GetFileVersionInfoSize", "str", exeName, "uint*", null, "uint")) 23 | return 24 | 25 | VarSetCapacity(VersionInfo, VersionInfoSize) 26 | if !DllCall("version\GetFileVersionInfo", "str", exeName, "uint", 0, "uint", VersionInfoSize, "ptr", &VersionInfo) 27 | return 28 | 29 | if !DllCall("version\VerQueryValue", "ptr", &VersionInfo, "str", "\VarFileInfo\Translation", "ptr*", lpTranslate, "uint*", cbTranslate) 30 | return 31 | 32 | wLanguage := NumGet(lpTranslate+0, "UShort") 33 | wCodePage := NumGet(lpTranslate+2, "UShort") 34 | id := Format("{:04X}{:04X}", wLanguage, wCodePage) 35 | 36 | FileDescription := "" 37 | FileVersion := "" 38 | Loop Parse, % "FileDescription,FileVersion", `, 39 | { 40 | if !DllCall("version\VerQueryValue", "ptr", &VersionInfo, "str", "\StringFileInfo\" id "\" A_LoopField, "ptr*", pField, "uint*", cbField) 41 | return 42 | %A_LoopField% := StrGet(pField, cbField) 43 | } 44 | 45 | Type := { Version: FileVersion 46 | , IsUnicode: InStr(FileDescription, "Unicode") ? 1 : "" 47 | , PtrSize: exeMachine=0x8664 ? 8 : 4 } 48 | 49 | ; We're dealing with a legacy version if it's prior to v1.1 50 | Type.Era := vert >= 0x01010000 ? "Modern" : "Legacy" 51 | 52 | return Type 53 | } 54 | -------------------------------------------------------------------------------- /ErrorCodes.md: -------------------------------------------------------------------------------- 1 | Possible Errors 2 | ========================================= 3 | 4 | ### `(0x00+)` General 5 | 6 | - `(0x0)` Compilation was successful. 7 | - `(0x1)` Error: Unknown error. 8 | - `(0x2)` Compilation was cancelled. 9 | - `(0x3)` Error: Bad parameters 10 | 11 | 12 | ### `(0x10+)` Syntax 13 | 14 | - `(0x1)` Error: The script contains syntax errors. 15 | - `(0x2)` Error: Invalid "FileInstall" syntax found. Note that the first parameter must not be specified using a continuation section. 16 | 17 | 18 | ### `(0x20+)` Not supported & AutoHotkey version dependent 19 | 20 | - `(0x1)` Error: #DerefChar is not supported. 21 | - `(0x2)` Error: #Delimiter is not supported. 22 | - `(0x3)` Error: /NoDecompile is not supported. 23 | - `(0x4)` Error: Password protection is not supported. 24 | - `(0x5)` Error: The AutoHotkey build used for auto-inclusion of library functions is not recognized. 25 | - `(0x6)` Error: Legacy AutoHotkey versions (prior to v1.1) are not allowed as the build used for auto-inclusion of library functions. 26 | 27 | 28 | ### `(0x30+)` File Open & Not found 29 | - `(0x1)` Error: Error opening the destination file. 30 | - `(0x2)` Script or #include cannot be opened. 31 | - `(0x3)` Error: Source file not specified. 32 | - `(0x4)` Error: The selected AutoHotkeySC binary does not exist. 33 | 34 | 35 | ### `(0x40+)` File write 36 | 37 | - `(0x1)` Error: Unable to copy AutoHotkeySC binary file to destination. 38 | - `(0x2)` Error changing icon: Unable to read icon or icon was of the wrong format. 39 | - `(0x3)` Error adding script file 40 | - `(0x4)` Error adding FileInstall file 41 | - `(0x5)` Error: Could not copy final compiled binary file to destination. 42 | 43 | 44 | ### `(0x50+)` Miscellaneous 45 | 46 | - `(0x1)` You cannot drop more than one file into this window! 47 | - `(0x2)` Error: cannot find AutoHotkey help file! 48 | - `(0x3)` Error: Invalid codepage parameter was given. -------------------------------------------------------------------------------- /IconChanger.ahk: -------------------------------------------------------------------------------- 1 | 2 | ; This code is based on Ahk2Exe's changeicon.cpp 3 | 4 | ReplaceAhkIcon(re, IcoFile, ExeFile) 5 | { 6 | global _EI_HighestIconID 7 | static iconID := 159 8 | ids := EnumIcons(ExeFile, iconID) 9 | if !IsObject(ids) 10 | return false 11 | 12 | f := FileOpen(IcoFile, "r") 13 | if !IsObject(f) 14 | return false 15 | 16 | VarSetCapacity(igh, 8), f.RawRead(igh, 6) 17 | if NumGet(igh, 0, "UShort") != 0 || NumGet(igh, 2, "UShort") != 1 18 | return false 19 | 20 | wCount := NumGet(igh, 4, "UShort") 21 | 22 | VarSetCapacity(rsrcIconGroup, rsrcIconGroupSize := 6 + wCount*14) 23 | NumPut(NumGet(igh, "Int64"), rsrcIconGroup, "Int64") ; fast copy 24 | 25 | ige := &rsrcIconGroup + 6 26 | 27 | ; Delete all the images 28 | Loop, % ids.MaxIndex() 29 | DllCall("UpdateResource", "ptr", re, "ptr", 3, "ptr", ids[A_Index], "ushort", 0x409, "ptr", 0, "uint", 0, "uint") 30 | 31 | Loop, %wCount% 32 | { 33 | thisID := ids[A_Index] 34 | if !thisID 35 | thisID := ++ _EI_HighestIconID 36 | 37 | f.RawRead(ige+0, 12) ; read all but the offset 38 | NumPut(thisID, ige+12, "UShort") 39 | 40 | imgOffset := f.ReadUInt() 41 | oldPos := f.Pos 42 | f.Pos := imgOffset 43 | 44 | VarSetCapacity(iconData, iconDataSize := NumGet(ige+8, "UInt")) 45 | f.RawRead(iconData, iconDataSize) 46 | f.Pos := oldPos 47 | 48 | DllCall("UpdateResource", "ptr", re, "ptr", 3, "ptr", thisID, "ushort", 0x409, "ptr", &iconData, "uint", iconDataSize, "uint") 49 | 50 | ige += 14 51 | } 52 | 53 | DllCall("UpdateResource", "ptr", re, "ptr", 14, "ptr", iconID, "ushort", 0x409, "ptr", &rsrcIconGroup, "uint", rsrcIconGroupSize, "uint") 54 | return true 55 | } 56 | 57 | EnumIcons(ExeFile, iconID) 58 | { 59 | ; RT_GROUP_ICON = 14 60 | ; RT_ICON = 3 61 | global _EI_HighestIconID 62 | static pEnumFunc := RegisterCallback("EnumIcons_Enum") 63 | 64 | hModule := DllCall("LoadLibraryEx", "str", ExeFile, "ptr", 0, "ptr", 2, "ptr") 65 | if !hModule 66 | return 67 | 68 | _EI_HighestIconID := 0 69 | if DllCall("EnumResourceNames", "ptr", hModule, "ptr", 3, "ptr", pEnumFunc, "uint", 0) = 0 70 | { 71 | DllCall("FreeLibrary", "ptr", hModule) 72 | return 73 | } 74 | 75 | hRsrc := DllCall("FindResource", "ptr", hModule, "ptr", iconID, "ptr", 14, "ptr") 76 | hMem := DllCall("LoadResource", "ptr", hModule, "ptr", hRsrc, "ptr") 77 | pDirHeader := DllCall("LockResource", "ptr", hMem, "ptr") 78 | pResDir := pDirHeader + 6 79 | 80 | wCount := NumGet(pDirHeader+4, "UShort") 81 | iconIDs := [] 82 | 83 | Loop, %wCount% 84 | { 85 | pResDirEntry := pResDir + (A_Index-1)*14 86 | iconIDs[A_Index] := NumGet(pResDirEntry+12, "UShort") 87 | } 88 | 89 | DllCall("FreeLibrary", "ptr", hModule) 90 | return iconIDs 91 | } 92 | 93 | EnumIcons_Enum(hModule, type, name, lParam) 94 | { 95 | global _EI_HighestIconID 96 | if (name < 0x10000) && name > _EI_HighestIconID 97 | _EI_HighestIconID := name 98 | return 1 99 | } 100 | -------------------------------------------------------------------------------- /Compiler.ahk: -------------------------------------------------------------------------------- 1 | #Include ScriptParser.ahk 2 | #Include IconChanger.ahk 3 | 4 | AhkCompile(ByRef AhkFile, ExeFile="", ByRef CustomIcon="", BinFile="", UseMPRESS="", fileCP="") 5 | { 6 | global ExeFileTmp 7 | AhkFile := Util_GetFullPath(AhkFile) 8 | if AhkFile = 9 | Util_Error("Error: Source file not specified.") 10 | SplitPath, AhkFile,, AhkFile_Dir,, AhkFile_NameNoExt 11 | 12 | if ExeFile = 13 | ExeFile = %AhkFile_Dir%\%AhkFile_NameNoExt%.exe 14 | else 15 | ExeFile := Util_GetFullPath(ExeFile) 16 | 17 | ;ExeFileTmp := ExeFile 18 | ExeFileTmp := Util_TempFile() 19 | 20 | if BinFile = 21 | BinFile = %A_ScriptDir%\AutoHotkeySC.bin 22 | 23 | Util_DisplayHourglass() 24 | 25 | IfNotExist, %BinFile% 26 | Util_Error("Error: The selected AutoHotkeySC binary does not exist.", 1, BinFile) 27 | 28 | try FileCopy, %BinFile%, %ExeFileTmp%, 1 29 | catch 30 | Util_Error("Error: Unable to copy AutoHotkeySC binary file to destination.") 31 | 32 | BinType := AHKType(ExeFileTmp) 33 | DerefIncludeVars.A_AhkVersion := BinType.Version 34 | DerefIncludeVars.A_PtrSize := BinType.PtrSize 35 | DerefIncludeVars.A_IsUnicode := BinType.IsUnicode 36 | 37 | BundleAhkScript(ExeFileTmp, AhkFile, CustomIcon, fileCP) 38 | 39 | if FileExist(A_ScriptDir "\mpress.exe") && UseMPRESS 40 | { 41 | Util_Status("Compressing final executable...") 42 | RunWait, "%A_ScriptDir%\mpress.exe" -q -x "%ExeFileTmp%",, Hide 43 | } 44 | 45 | ; the final step... 46 | try FileCopy, %ExeFileTmp%, %ExeFile%, 1 47 | catch 48 | Util_Error("Error: Could not copy final compiled binary file to destination.") 49 | 50 | Util_HideHourglass() 51 | Util_Status("") 52 | } 53 | 54 | BundleAhkScript(ExeFile, AhkFile, IcoFile="", fileCP="") 55 | { 56 | ; weird bug prevention, for non working default param 'fileCP' 57 | if fileCP is space 58 | fileCP := A_FileEncoding 59 | 60 | try FileEncoding, %fileCP% 61 | catch e 62 | Util_Error("Error: Invalid codepage parameter """ fileCP """ was given.") 63 | 64 | SplitPath, AhkFile,, ScriptDir 65 | 66 | ExtraFiles := [] 67 | PreprocessScript(ScriptBody, AhkFile, ExtraFiles) 68 | ;FileDelete, %ExeFile%.ahk 69 | ;FileAppend, % ScriptBody, %ExeFile%.ahk 70 | VarSetCapacity(BinScriptBody, BinScriptBody_Len := StrPut(ScriptBody, "UTF-8") - 1) 71 | StrPut(ScriptBody, &BinScriptBody, "UTF-8") 72 | 73 | module := DllCall("BeginUpdateResource", "str", ExeFile, "uint", 0, "ptr") 74 | if !module 75 | Util_Error("Error: Error opening the destination file.") 76 | 77 | if IcoFile 78 | { 79 | Util_Status("Changing the main icon...") 80 | if !ReplaceAhkIcon(module, IcoFile, ExeFile) 81 | { 82 | ; Error was already displayed 83 | gosub _EndUpdateResource 84 | Util_Error("Error changing icon: Unable to read icon or icon was of the wrong format.") 85 | } 86 | } 87 | 88 | Util_Status("Compressing and adding: Master Script") 89 | if !DllCall("UpdateResource", "ptr", module, "ptr", 10, "str", IcoFile ? ">AHK WITH ICON<" : ">AUTOHOTKEY SCRIPT<" 90 | , "ushort", 0x409, "ptr", &BinScriptBody, "uint", BinScriptBody_Len, "uint") 91 | goto _FailEnd 92 | 93 | oldWD := A_WorkingDir 94 | SetWorkingDir, %ScriptDir% 95 | for each,file in ExtraFiles 96 | { 97 | Util_Status("Compressing and adding: " file) 98 | StringUpper, resname, file 99 | 100 | IfNotExist, %file% 101 | goto _FailEnd2 102 | 103 | ; This "old-school" method of reading binary files is way faster than using file objects. 104 | FileGetSize, filesize, %file% 105 | VarSetCapacity(filedata, filesize) 106 | FileRead, filedata, *c %file% 107 | if !DllCall("UpdateResource", "ptr", module, "ptr", 10, "str", resname 108 | , "ushort", 0x409, "ptr", &filedata, "uint", filesize, "uint") 109 | goto _FailEnd2 110 | VarSetCapacity(filedata, 0) 111 | } 112 | SetWorkingDir, %oldWD% 113 | 114 | gosub _EndUpdateResource 115 | return 116 | 117 | _FailEnd: 118 | gosub _EndUpdateResource 119 | Util_Error("Error adding script file:`n`n" AhkFile) 120 | 121 | _FailEnd2: 122 | gosub _EndUpdateResource 123 | Util_Error("Error adding FileInstall file:`n`n" file) 124 | 125 | _EndUpdateResource: 126 | if !DllCall("EndUpdateResource", "ptr", module, "uint", 0) 127 | Util_Error("Error: Error opening the destination file.") 128 | return 129 | } 130 | 131 | Util_GetFullPath(path) 132 | { 133 | VarSetCapacity(fullpath, 260 * (!!A_IsUnicode + 1)) 134 | if DllCall("GetFullPathName", "str", path, "uint", 260, "str", fullpath, "ptr", 0, "uint") 135 | return fullpath 136 | else 137 | return "" 138 | } 139 | -------------------------------------------------------------------------------- /ScriptParser.ahk: -------------------------------------------------------------------------------- 1 | 2 | PreprocessScript(ByRef ScriptText, AhkScript, ExtraFiles, FileList="", FirstScriptDir="", Options="", iOption=0) 3 | { 4 | SplitPath, AhkScript, ScriptName, ScriptDir 5 | if !IsObject(FileList) 6 | { 7 | FileList := [AhkScript] 8 | ScriptText := "; `n" 9 | FirstScriptDir := ScriptDir 10 | IsFirstScript := true 11 | Options := { comm: ";", esc: "``" } 12 | 13 | OldWorkingDir := A_WorkingDir 14 | SetWorkingDir, %ScriptDir% 15 | 16 | DerefIncludeVars.A_ScriptFullPath := AhkScript 17 | DerefIncludeVars.A_ScriptName := ScriptName 18 | DerefIncludeVars.A_ScriptDir := ScriptDir 19 | } 20 | oldLineFile := DerefIncludeVars.A_LineFile 21 | DerefIncludeVars.A_LineFile := AhkScript 22 | 23 | IfNotExist, %AhkScript% 24 | if !iOption 25 | Util_Error((IsFirstScript ? "Script" : "#include") " file """ AhkScript """ cannot be opened.") 26 | else return 27 | 28 | cmtBlock := false, contSection := false 29 | Loop, Read, %AhkScript% 30 | { 31 | tline := Trim(A_LoopReadLine) 32 | if !cmtBlock 33 | { 34 | if !contSection 35 | { 36 | if StrStartsWith(tline, Options.comm) 37 | continue 38 | else if tline = 39 | continue 40 | else if StrStartsWith(tline, "/*") 41 | { 42 | cmtBlock := true 43 | continue 44 | } 45 | } 46 | if StrStartsWith(tline, "(") && !IsFakeCSOpening(tline) 47 | contSection := true 48 | else if StrStartsWith(tline, ")") 49 | contSection := false 50 | 51 | tline := RegExReplace(tline, "\s+" RegExEscape(Options.comm) ".*$", "") 52 | if !contSection && RegExMatch(tline, "i)^#Include(Again)?[ \t]*[, \t]?\s+(.*)$", o) 53 | { 54 | IsIncludeAgain := (o1 = "Again") 55 | IgnoreErrors := false 56 | IncludeFile := o2 57 | if RegExMatch(IncludeFile, "\*[iI]\s+?(.*)", o) 58 | IgnoreErrors := true, IncludeFile := Trim(o1) 59 | 60 | if RegExMatch(IncludeFile, "^<(.+)>$", o) 61 | { 62 | if IncFile2 := FindLibraryFile(o1, FirstScriptDir) 63 | { 64 | IncludeFile := IncFile2 65 | goto _skip_findfile 66 | } 67 | } 68 | 69 | IncludeFile := DerefIncludePath(IncludeFile, DerefIncludeVars) 70 | 71 | if InStr(FileExist(IncludeFile), "D") 72 | { 73 | SetWorkingDir, %IncludeFile% 74 | continue 75 | } 76 | 77 | _skip_findfile: 78 | 79 | IncludeFile := Util_GetFullPath(IncludeFile) 80 | 81 | AlreadyIncluded := false 82 | for k,v in FileList 83 | if (v = IncludeFile) 84 | { 85 | AlreadyIncluded := true 86 | break 87 | } 88 | if(IsIncludeAgain || !AlreadyIncluded) 89 | { 90 | if !AlreadyIncluded 91 | FileList.Insert(IncludeFile) 92 | PreprocessScript(ScriptText, IncludeFile, ExtraFiles, FileList, FirstScriptDir, Options, IgnoreErrors) 93 | } 94 | }else if !contSection && tline ~= "i)^FileInstall[, \t]" 95 | { 96 | if tline ~= "^\w+\s+(:=|\+=|-=|\*=|/=|//=|\.=|\|=|&=|\^=|>>=|<<=)" 97 | continue ; This is an assignment! 98 | 99 | ; workaround for `, detection 100 | EscapeChar := Options.esc 101 | EscapeCharChar := EscapeChar EscapeChar 102 | EscapeComma := EscapeChar "," 103 | EscapeTmp := chr(2) 104 | EscapeTmpD := chr(3) 105 | StringReplace, tline, tline, %EscapeCharChar%, %EscapeTmpD%, All 106 | StringReplace, tline, tline, %EscapeComma%, %EscapeTmp%, All 107 | 108 | if !RegExMatch(tline, "i)^FileInstall[ \t]*[, \t][ \t]*([^,]+?)[ \t]*(,|$)", o) || o1 ~= "[^``]%" 109 | Util_Error("Error: Invalid ""FileInstall"" syntax found. Note that the first parameter must not be specified using a continuation section.") 110 | _ := Options.esc 111 | StringReplace, o1, o1, %_%`%, `%, All 112 | StringReplace, o1, o1, %_%`,, `,, All 113 | StringReplace, o1, o1, %_%%_%,, %_%,, All 114 | 115 | ; workaround for `, detection [END] 116 | StringReplace, o1, o1, %EscapeTmp%, `,, All 117 | StringReplace, o1, o1, %EscapeTmpD%, %EscapeChar%, All 118 | StringReplace, tline, tline, %EscapeTmp%, %EscapeComma%, All 119 | StringReplace, tline, tline, %EscapeTmpD%, %EscapeCharChar%, All 120 | 121 | ExtraFiles.Insert(o1) 122 | ScriptText .= tline "`n" 123 | }else if !contSection && RegExMatch(tline, "i)^#CommentFlag\s+(.+)$", o) 124 | Options.comm := o1, ScriptText .= tline "`n" 125 | else if !contSection && RegExMatch(tline, "i)^#EscapeChar\s+(.+)$", o) 126 | Options.esc := o1, ScriptText .= tline "`n" 127 | else if !contSection && RegExMatch(tline, "i)^#DerefChar\s+(.+)$", o) 128 | Util_Error("Error: #DerefChar is not supported.") 129 | else if !contSection && RegExMatch(tline, "i)^#Delimiter\s+(.+)$", o) 130 | Util_Error("Error: #Delimiter is not supported.") 131 | else 132 | ScriptText .= (contSection ? A_LoopReadLine : tline) "`n" 133 | }else if StrStartsWith(tline, "*/") 134 | cmtBlock := false 135 | } 136 | 137 | Loop, % !!IsFirstScript ; equivalent to "if IsFirstScript" except you can break from the block 138 | { 139 | static AhkPath := A_IsCompiled ? A_ScriptDir "\..\AutoHotkey.exe" : A_AhkPath 140 | IfNotExist, %AhkPath% 141 | break ; Don't bother with auto-includes because the file does not exist 142 | 143 | Util_Status("Auto-including any functions called from a library...") 144 | ilibfile = %A_Temp%\_ilib.ahk 145 | IfExist, %ilibfile%, FileDelete, %ilibfile% 146 | AhkType := AHKType(AhkPath) 147 | if !AhkType 148 | Util_Error("Error: The AutoHotkey build used for auto-inclusion of library functions is not recognized.", 1, AhkPath) 149 | if (AhkType.Era = "Legacy") 150 | Util_Error("Error: Legacy AutoHotkey versions (prior to v1.1) are not allowed as the build used for auto-inclusion of library functions.", 1, AhkPath) 151 | tmpErrorLog := Util_TempFile() 152 | RunWait, "%AhkPath%" /iLib "%ilibfile%" /ErrorStdOut "%AhkScript%" 2>"%tmpErrorLog%", %FirstScriptDir%, UseErrorLevel 153 | FileRead,tmpErrorData,%tmpErrorLog% 154 | FileDelete,%tmpErrorLog% 155 | if (ErrorLevel = 2) 156 | Util_Error("Error: The script contains syntax errors.",1,tmpErrorData) 157 | IfExist, %ilibfile% 158 | { 159 | PreprocessScript(ScriptText, ilibfile, ExtraFiles, FileList, FirstScriptDir, Options) 160 | FileDelete, %ilibfile% 161 | } 162 | StringTrimRight, ScriptText, ScriptText, 1 ; remove trailing newline 163 | } 164 | 165 | DerefIncludeVars.A_LineFile := oldLineFile 166 | if OldWorkingDir 167 | SetWorkingDir, %OldWorkingDir% 168 | } 169 | 170 | IsFakeCSOpening(tline) 171 | { 172 | Loop, Parse, tline, %A_Space%%A_Tab% 173 | if !StrStartsWith(A_LoopField, "Join") && InStr(A_LoopField, ")") 174 | return true 175 | return false 176 | } 177 | 178 | FindLibraryFile(name, ScriptDir) 179 | { 180 | libs := [ScriptDir "\Lib", A_MyDocuments "\AutoHotkey\Lib", A_ScriptDir "\..\Lib"] 181 | p := InStr(name, "_") 182 | if p 183 | name_lib := SubStr(name, 1, p-1) 184 | 185 | for each,lib in libs 186 | { 187 | file := lib "\" name ".ahk" 188 | IfExist, %file% 189 | return file 190 | 191 | if !p 192 | continue 193 | 194 | file := lib "\" name_lib ".ahk" 195 | IfExist, %file% 196 | return file 197 | } 198 | } 199 | 200 | StrStartsWith(ByRef v, ByRef w) 201 | { 202 | return SubStr(v, 1, StrLen(w)) = w 203 | } 204 | 205 | RegExEscape(t) 206 | { 207 | static _ := "\.*?+[{|()^$" 208 | Loop, Parse, _ 209 | StringReplace, t, t, %A_LoopField%, \%A_LoopField%, All 210 | return t 211 | } 212 | 213 | Util_TempFile(d:="") 214 | { 215 | if ( !StrLen(d) || !FileExist(d) ) 216 | d:=A_Temp 217 | Loop 218 | tempName := d "\~temp" A_TickCount ".tmp" 219 | until !FileExist(tempName) 220 | return tempName 221 | } 222 | 223 | class DerefIncludeVars 224 | { 225 | static A_IsCompiled := true 226 | } 227 | 228 | DerefIncludePath(path, vars) 229 | { 230 | static SharedVars := {A_AhkPath:1, A_AppData:1, A_AppDataCommon:1, A_ComputerName:1, A_ComSpec:1, A_Desktop:1, A_DesktopCommon:1, A_MyDocuments:1, A_ProgramFiles:1, A_Programs:1, A_ProgramsCommon:1, A_Space:1, A_StartMenu:1, A_StartMenuCommon:1, A_Startup:1, A_StartupCommon:1, A_Tab:1, A_Temp:1, A_UserName:1, A_WinDir:1} 231 | p := StrSplit(path, "%") 232 | path := p[1] 233 | n := 2 234 | while n < p.Length() 235 | { 236 | vn := p[n] 237 | if ObjHasKey(vars, vn) 238 | path .= vars[vn] . p[++n] 239 | else if SharedVars[vn] 240 | path .= %vn% . p[++n] 241 | else 242 | path .= "%" vn 243 | ++n 244 | } 245 | if (n = p.Length()) 246 | path .= "%" p[n] 247 | return path 248 | } 249 | -------------------------------------------------------------------------------- /Ahk2Exe.ahk: -------------------------------------------------------------------------------- 1 | ; 2 | ; File encoding: UTF-8 3 | ; 4 | ; Script description: 5 | ; Ahk2Exe - AutoHotkey Script Compiler 6 | ; Written by fincs - Interface based on the original Ahk2Exe 7 | ; 8 | 9 | #NoEnv 10 | #NoTrayIcon 11 | #SingleInstance Off 12 | #Include %A_ScriptDir% 13 | #Include Compiler.ahk 14 | SendMode Input 15 | 16 | DEBUG := !A_IsCompiled 17 | 18 | gosub BuildBinFileList 19 | gosub LoadSettings 20 | gosub ParseCmdLine 21 | if !UsesCustomBin 22 | gosub CheckAutoHotkeySC 23 | 24 | if CLIMode 25 | { 26 | gosub ConvertCLI 27 | ExitApp, 0 ; Success 28 | } 29 | 30 | IcoFile = %LastIcon% 31 | BinFileId := FindBinFile(LastBinFile) 32 | ScriptFileCP := A_FileEncoding 33 | 34 | #include *i __debug.ahk 35 | 36 | Menu, FileMenu, Add, &Convert, Convert 37 | Menu, FileMenu, Add 38 | Menu, FileMenu, Add, E&xit`tAlt+F4, GuiClose 39 | Menu, HelpMenu, Add, &Help, Help 40 | Menu, HelpMenu, Add 41 | Menu, HelpMenu, Add, &About, About 42 | Menu, MenuBar, Add, &File, :FileMenu 43 | Menu, MenuBar, Add, &Help, :HelpMenu 44 | Gui, Menu, MenuBar 45 | 46 | Gui, +LastFound 47 | GuiHwnd := WinExist("") 48 | Gui, Add, Link, x287 y25, 49 | ( 50 | ©2004-2009 Chris Mallet 51 | ©2008-2011 Steve Gray (Lexikos) 52 | ©2011-%A_Year% fincs 53 | https://autohotkey.com 54 | Note: Compiling does not guarantee source code protection. 55 | ) 56 | Gui, Add, Text, x11 y117 w570 h2 +0x1007 57 | Gui, Add, GroupBox, x11 y124 w570 h86, Required Parameters 58 | Gui, Add, Text, x17 y151, &Source (script file) 59 | Gui, Add, Edit, x137 y146 w315 h23 +Disabled vAhkFile, %AhkFile% 60 | Gui, Add, Button, x459 y146 w53 h23 gBrowseAhk, &Browse 61 | Gui, Add, Text, x17 y180, &Destination (.exe file) 62 | Gui, Add, Edit, x137 y176 w315 h23 +Disabled vExeFile, %Exefile% 63 | Gui, Add, Button, x459 y176 w53 h23 gBrowseExe, B&rowse 64 | Gui, Add, GroupBox, x11 y219 w570 h106, Optional Parameters 65 | Gui, Add, Text, x18 y245, Custom Icon (.ico file) 66 | Gui, Add, Edit, x138 y241 w315 h23 +Disabled vIcoFile, %IcoFile% 67 | Gui, Add, Button, x461 y241 w53 h23 gBrowseIco, Br&owse 68 | Gui, Add, Button, x519 y241 w53 h23 gDefaultIco, D&efault 69 | Gui, Add, Text, x18 y274, Base File (.bin) 70 | Gui, Add, DDL, x138 y270 w315 h23 R10 AltSubmit vBinFileId Choose%BinFileId%, %BinNames% 71 | Gui, Add, CheckBox, x138 y298 w315 h20 vUseMpress Checked%LastUseMPRESS%, Use MPRESS (if present) to compress resulting exe 72 | Gui, Add, Button, x258 y329 w75 h28 Default gConvert, > &Convert < 73 | Gui, Add, Statusbar,, Ready 74 | if !A_IsCompiled 75 | Gui, Add, Pic, x29 y16 w240 h78, %A_ScriptDir%\logo.png 76 | else 77 | gosub AddPicture 78 | GuiControl, Focus, Button1 79 | Gui, Show, w594 h383, Ahk2Exe for AutoHotkey v%A_AhkVersion% -- Script to EXE Converter 80 | return 81 | 82 | GuiClose: 83 | Gui, Submit 84 | gosub SaveSettings 85 | ExitApp 86 | 87 | GuiDropFiles: 88 | if A_EventInfo > 2 89 | Util_Error("You cannot drop more than one file into this window!") 90 | SplitPath, A_GuiEvent,,, dropExt 91 | if dropExt = ahk 92 | GuiControl,, AhkFile, %A_GuiEvent% 93 | else if dropExt = ico 94 | GuiControl,, IcoFile, %A_GuiEvent% 95 | return 96 | 97 | AddPicture: 98 | ; Code based on http://www.autohotkey.com/forum/viewtopic.php?p=147052 99 | Gui, Add, Text, x29 y16 w240 h78 +0xE hwndhPicCtrl 100 | 101 | hRSrc := DllCall("FindResource", "ptr", 0, "str", "LOGO.PNG", "ptr", 10, "ptr") 102 | sData := DllCall("SizeofResource", "ptr", 0, "ptr", hRSrc, "uint") 103 | hRes := DllCall("LoadResource", "ptr", 0, "ptr", hRSrc, "ptr") 104 | pData := DllCall("LockResource", "ptr", hRes, "ptr") 105 | hGlob := DllCall("GlobalAlloc", "uint", 2, "uint", sData, "ptr") ; 2=GMEM_MOVEABLE 106 | pGlob := DllCall("GlobalLock", "ptr", hGlob, "ptr") 107 | DllCall("msvcrt\memcpy", "ptr", pGlob, "ptr", pData, "uint", sData, "CDecl") 108 | DllCall("GlobalUnlock", "ptr", hGlob) 109 | DllCall("ole32\CreateStreamOnHGlobal", "ptr", hGlob, "int", 1, "ptr*", pStream) 110 | 111 | hGdip := DllCall("LoadLibrary", "str", "gdiplus") 112 | VarSetCapacity(si, 16, 0), NumPut(1, si, "UChar") 113 | DllCall("gdiplus\GdiplusStartup", "ptr*", gdipToken, "ptr", &si, "ptr", 0) 114 | DllCall("gdiplus\GdipCreateBitmapFromStream", "ptr", pStream, "ptr*", pBitmap) 115 | DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", pBitmap, "ptr*", hBitmap, "uint", 0) 116 | SendMessage, 0x172, 0, hBitmap,, ahk_id %hPicCtrl% ; 0x172=STM_SETIMAGE, 0=IMAGE_BITMAP 117 | GuiControl, Move, %hPicCtrl%, w240 h78 118 | 119 | DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap) 120 | DllCall("gdiplus\GdiplusShutdown", "ptr", gdipToken) 121 | DllCall("FreeLibrary", "ptr", hGdip) 122 | ObjRelease(pStream) 123 | return 124 | 125 | Never: 126 | FileInstall, logo.png, NEVER 127 | return 128 | 129 | BuildBinFileList: 130 | BinFiles := ["AutoHotkeySC.bin"] 131 | BinNames = (Default) 132 | Loop, %A_ScriptDir%\*.bin 133 | { 134 | SplitPath, A_LoopFileFullPath,,,, n 135 | if n = AutoHotkeySC 136 | continue 137 | FileGetVersion, v, %A_LoopFileFullPath% 138 | BinFiles.Insert(n ".bin") 139 | BinNames .= "|v" v " " n 140 | } 141 | return 142 | 143 | CheckAutoHotkeySC: 144 | IfNotExist, %A_ScriptDir%\AutoHotkeySC.bin 145 | { 146 | ; Check if we can actually write to the compiler dir 147 | try FileAppend, test, %A_ScriptDir%\___.tmp 148 | catch 149 | { 150 | MsgBox, 52, Ahk2Exe Error, 151 | (LTrim 152 | Unable to copy the appropriate binary file as AutoHotkeySC.bin because the current user does not have write/create privileges in the %A_ScriptDir% folder (perhaps you should run this program as administrator?) 153 | 154 | Do you still want to continue? 155 | ) 156 | IfMsgBox, Yes 157 | return 158 | ExitApp, 0x2 ; Compilation cancelled 159 | } 160 | FileDelete, %A_ScriptDir%\___.tmp 161 | 162 | IfNotExist, %A_ScriptDir%\..\AutoHotkey.exe 163 | binFile = %A_ScriptDir%\Unicode 32-bit.bin 164 | else 165 | { 166 | try FileDelete, %A_Temp%\___temp.ahk 167 | FileAppend, ExitApp `% (A_IsUnicode=1) << 8 | (A_PtrSize=8) << 9, %A_Temp%\___temp.ahk 168 | RunWait, "%A_ScriptDir%\..\AutoHotkey.exe" "%A_Temp%\___temp.ahk" 169 | rc := ErrorLevel 170 | FileDelete, %A_Temp%\___temp.ahk 171 | if rc = 0 172 | binFile = %A_ScriptDir%\ANSI 32-bit.bin 173 | else if rc = 0x100 174 | binFile = %A_ScriptDir%\Unicode 32-bit.bin 175 | else if rc = 0x300 176 | binFile = %A_ScriptDir%\Unicode 64-bit.bin 177 | ; else: shouldn't happen 178 | } 179 | 180 | IfNotExist, %binFile% 181 | { 182 | MsgBox, 52, Ahk2Exe Error, 183 | (LTrim 184 | Unable to copy the appropriate binary file as AutoHotkeySC.bin because said file does not exist: 185 | %binFile% 186 | 187 | Do you still want to continue? 188 | ) 189 | IfMsgBox, Yes 190 | return 191 | ExitApp, 0x2 ; Compilation cancelled 192 | } 193 | 194 | FileCopy, %binFile%, %A_ScriptDir%\AutoHotkeySC.bin 195 | } 196 | return 197 | 198 | FindBinFile(name) 199 | { 200 | global BinFiles 201 | for k,v in BinFiles 202 | if (v = name) 203 | return k 204 | return 1 205 | } 206 | 207 | ParseCmdLine: 208 | if 0 = 0 209 | return 210 | 211 | Error_ForceExit := true 212 | 213 | p := [] 214 | Loop, %0% 215 | { 216 | if %A_Index% = /NoDecompile 217 | Util_Error("Error: /NoDecompile is not supported.") 218 | else p.Insert(%A_Index%) 219 | } 220 | 221 | if Mod(p.MaxIndex(), 2) 222 | goto BadParams 223 | 224 | Loop, % p.MaxIndex() // 2 225 | { 226 | p1 := p[2*(A_Index-1)+1] 227 | p2 := p[2*(A_Index-1)+2] 228 | 229 | if p1 not in /in,/out,/icon,/pass,/bin,/mpress,/cp 230 | goto BadParams 231 | 232 | if p1 = /bin 233 | UsesCustomBin := true 234 | 235 | if p1 = /pass 236 | Util_Error("Error: Password protection is not supported.") 237 | 238 | if p2 = 239 | goto BadParams 240 | 241 | StringTrimLeft, p1, p1, 1 242 | gosub _Process%p1% 243 | } 244 | 245 | if !AhkFile 246 | goto BadParams 247 | 248 | if !IcoFile 249 | IcoFile := LastIcon 250 | 251 | if !BinFile 252 | BinFile := A_ScriptDir "\" LastBinFile 253 | 254 | if UseMPRESS = 255 | UseMPRESS := LastUseMPRESS 256 | 257 | CLIMode := true 258 | return 259 | 260 | BadParams: 261 | Util_Info("Command Line Parameters:`n`n" A_ScriptName " /in infile.ahk [/out outfile.exe] [/icon iconfile.ico] [/bin AutoHotkeySC.bin] [/mpress 1 (true) or 0 (false)] [/cp codepage]") 262 | ExitApp, 0x3 263 | 264 | _ProcessIn: 265 | AhkFile := p2 266 | return 267 | 268 | _ProcessOut: 269 | ExeFile := p2 270 | return 271 | 272 | _ProcessIcon: 273 | IcoFile := p2 274 | return 275 | 276 | _ProcessBin: 277 | CustomBinFile := true 278 | BinFile := p2 279 | return 280 | 281 | _ProcessMPRESS: 282 | UseMPRESS := p2 283 | return 284 | 285 | _ProcessCP: ; for example: '/cp 1252' or '/cp UTF-8' 286 | if p2 is number 287 | ScriptFileCP := "CP" p2 288 | else 289 | ScriptFileCP := p2 290 | return 291 | 292 | BrowseAhk: 293 | Gui, +OwnDialogs 294 | FileSelectFile, ov, 1, %LastScriptDir%, Open, AutoHotkey files (*.ahk) 295 | if ErrorLevel 296 | return 297 | GuiControl,, AhkFile, %ov% 298 | return 299 | 300 | BrowseExe: 301 | Gui, +OwnDialogs 302 | FileSelectFile, ov, S16, %LastExeDir%, Save As, Executable files (*.exe) 303 | if ErrorLevel 304 | return 305 | SplitPath, ov,,, ovExt 306 | if !StrLen(ovExt) ;~ append a default file extension is none specified 307 | ov .= ".exe" 308 | GuiControl,, ExeFile, %ov% 309 | return 310 | 311 | BrowseIco: 312 | Gui, +OwnDialogs 313 | FileSelectFile, ov, 1, %LastIconDir%, Open, Icon files (*.ico) 314 | if ErrorLevel 315 | return 316 | GuiControl,, IcoFile, %ov% 317 | return 318 | 319 | DefaultIco: 320 | GuiControl,, IcoFile 321 | return 322 | 323 | Convert: 324 | Gui, +OwnDialogs 325 | Gui, Submit, NoHide 326 | BinFile := A_ScriptDir "\" BinFiles[BinFileId] 327 | ConvertCLI: 328 | AhkCompile(AhkFile, ExeFile, IcoFile, BinFile, UseMpress, ScriptFileCP) 329 | if !CLIMode 330 | Util_Info("Conversion complete.") 331 | else 332 | FileAppend, Successfully compiled: %ExeFile%`n, * 333 | return 334 | 335 | LoadSettings: 336 | RegRead, LastScriptDir, HKCU, Software\AutoHotkey\Ahk2Exe, LastScriptDir 337 | RegRead, LastExeDir, HKCU, Software\AutoHotkey\Ahk2Exe, LastExeDir 338 | RegRead, LastIconDir, HKCU, Software\AutoHotkey\Ahk2Exe, LastIconDir 339 | RegRead, LastIcon, HKCU, Software\AutoHotkey\Ahk2Exe, LastIcon 340 | RegRead, LastBinFile, HKCU, Software\AutoHotkey\Ahk2Exe, LastBinFile 341 | RegRead, LastUseMPRESS, HKCU, Software\AutoHotkey\Ahk2Exe, LastUseMPRESS 342 | if LastBinFile = 343 | LastBinFile = AutoHotkeySC.bin 344 | if LastUseMPRESS 345 | LastUseMPRESS := true 346 | return 347 | 348 | SaveSettings: 349 | SplitPath, AhkFile,, AhkFileDir 350 | if ExeFile 351 | SplitPath, ExeFile,, ExeFileDir 352 | else 353 | ExeFileDir := LastExeDir 354 | if IcoFile 355 | SplitPath, IcoFile,, IcoFileDir 356 | else 357 | IcoFileDir := "" 358 | RegWrite, REG_SZ, HKCU, Software\AutoHotkey\Ahk2Exe, LastScriptDir, %AhkFileDir% 359 | RegWrite, REG_SZ, HKCU, Software\AutoHotkey\Ahk2Exe, LastExeDir, %ExeFileDir% 360 | RegWrite, REG_SZ, HKCU, Software\AutoHotkey\Ahk2Exe, LastIconDir, %IcoFileDir% 361 | RegWrite, REG_SZ, HKCU, Software\AutoHotkey\Ahk2Exe, LastIcon, %IcoFile% 362 | RegWrite, REG_SZ, HKCU, Software\AutoHotkey\Ahk2Exe, LastUseMPRESS, %UseMPRESS% 363 | if !CustomBinFile 364 | RegWrite, REG_SZ, HKCU, Software\AutoHotkey\Ahk2Exe, LastBinFile, % BinFiles[BinFileId] 365 | return 366 | 367 | Help: 368 | helpfile = %A_ScriptDir%\..\AutoHotkey.chm 369 | IfNotExist, %helpfile% 370 | Util_Error("Error: cannot find AutoHotkey help file!") 371 | 372 | VarSetCapacity(ak, ak_size := 8+5*A_PtrSize+4, 0) ; HH_AKLINK struct 373 | NumPut(ak_size, ak, 0, "UInt") 374 | name = Ahk2Exe 375 | NumPut(&name, ak, 8) 376 | DllCall("hhctrl.ocx\HtmlHelp", "ptr", GuiHwnd, "str", helpfile, "uint", 0x000D, "ptr", &ak) ; 0x000D: HH_KEYWORD_LOOKUP 377 | return 378 | 379 | About: 380 | MsgBox, 64, About Ahk2Exe, 381 | ( 382 | Ahk2Exe - Script to EXE Converter 383 | 384 | Original version: 385 | Copyright ©1999-2003 Jonathan Bennett & AutoIt Team 386 | Copyright ©2004-2009 Chris Mallet 387 | Copyright ©2008-2011 Steve Gray (Lexikos) 388 | 389 | Script rewrite: 390 | Copyright ©2011-%A_Year% fincs 391 | ) 392 | return 393 | 394 | Util_Status(s) 395 | { 396 | SB_SetText(s) 397 | } 398 | 399 | Util_Error(txt, doexit=1, extra="") 400 | { 401 | global CLIMode, Error_ForceExit, ExeFileTmp 402 | 403 | if ExeFileTmp && FileExist(ExeFileTmp) 404 | { 405 | FileDelete, %ExeFileTmp% 406 | ExeFileTmp = 407 | } 408 | 409 | if extra 410 | txt .= "`n`nSpecifically: " extra 411 | 412 | Util_HideHourglass() 413 | MsgBox, 16, Ahk2Exe Error, % txt 414 | 415 | if CLIMode 416 | FileAppend, Failed to compile: %ExeFile%`n, * 417 | 418 | Util_Status("Ready") 419 | 420 | if doexit 421 | if !Error_ForceExit 422 | Exit, % Util_ErrorCode(txt) 423 | else 424 | ExitApp, % Util_ErrorCode(txt) 425 | } 426 | 427 | Util_ErrorCode(x) 428 | { 429 | if InStr(x,"Syntax") 430 | if InStr(x,"FileInstall") 431 | return 0x12 432 | else 433 | return 0x11 434 | 435 | if InStr(x,"AutoHotkeySC") 436 | if InStr(x,"copy") 437 | return 0x41 438 | else 439 | return 0x34 440 | 441 | if InStr(x,"file") 442 | if InStr(x,"open") 443 | if InStr(x,"cannot") 444 | return 0x32 445 | else 446 | return 0x31 447 | else if InStr(x,"adding") 448 | if InStr(x,"FileInstall") 449 | return 0x44 450 | else 451 | return 0x43 452 | else if InStr(x,"cannot") 453 | if InStr(x,"drop") 454 | return 0x51 455 | else 456 | return 0x52 457 | else if InStr(x,"final") 458 | return 0x45 459 | else 460 | return 0x33 461 | 462 | 463 | if InStr(x,"Supported") 464 | if InStr(x,"De") 465 | if InStr(x,"#") 466 | if InStr(x,"ref") 467 | return 0x21 468 | else 469 | return 0x22 470 | else 471 | return 0x23 472 | else 473 | return 0x24 474 | 475 | if InStr(x,"build used") 476 | if InStr(x,"Legacy") 477 | return 0x26 478 | else 479 | return 0x25 480 | 481 | if InStr(x,"icon") 482 | return 0x42 483 | 484 | if InStr(x,"codepage") 485 | return 0x53 486 | 487 | return 0x1 ;unknown error 488 | } 489 | 490 | Util_Info(txt) 491 | { 492 | MsgBox, 64, Ahk2Exe, % txt 493 | } 494 | 495 | Util_DisplayHourglass() 496 | { 497 | DllCall("SetCursor", "ptr", DllCall("LoadCursor", "ptr", 0, "ptr", 32514, "ptr")) 498 | } 499 | 500 | Util_HideHourglass() 501 | { 502 | DllCall("SetCursor", "ptr", DllCall("LoadCursor", "ptr", 0, "ptr", 32512, "ptr")) 503 | } 504 | --------------------------------------------------------------------------------