Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

[AHK_H(+dll) / AHK_L / v2] AhkDllThread+AhkExported


  • Please log in to reply
274 replies to this topic
HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
I got it finally, I did not call Free() on correct object :oops:

#Include <_Struct>
Class _MemoryLibrary {
	static MEMORYMODULE :=(A_PtrSize=8?"
	(LTrim
		_MemoryLibrary.IMAGE_NT_HEADERS64 *headers;
	)":"
	(LTrim
		_MemoryLibrary.IMAGE_NT_HEADERS32 *headers;
	)") "
	(LTrim

		uchar *codeBase;
		HMODULE *modules;
		int numModules;
		int initialized;
	)"
	static IMAGE_RESOURCE_DIRECTORY_ENTRY := "
	(LTrim
		union {
			DWORD   Name;
			WORD    Id;
		};
		union {
			DWORD   OffsetToData;
		};
	)"

	static IMAGE_RESOURCE_DATA_ENTRY := "
	(LTrim
		DWORD   OffsetToData;
		DWORD   Size;
		DWORD   CodePage;
		DWORD   Reserved;
	)"

	static IMAGE_RESOURCE_DIRECTORY:="
	(LTrim
		DWORD   Characteristics;
		DWORD   TimeDateStamp;
		WORD    MajorVersion;
		WORD    MinorVersion;
		WORD    NumberOfNamedEntries;
		WORD    NumberOfIdEntries;
	)"
	static IMAGE_BASE_RELOCATION :="
	(LTrim
		DWORD   VirtualAddress;
		DWORD   SizeOfBlock;
	)"

	static _ACTCTX := "
	(LTrim
		ULONG   cbSize;
		DWORD   dwFlags;
		LPCWSTR lpSource;
		USHORT  wProcessorArchitecture;
		LANGID  wLangId;
		LPCTSTR lpAssemblyDirectory;
		LPCTSTR lpResourceName;
		LPCTSTR lpApplicationName;
		HMODULE hModule;
	)"

	static IMAGE_IMPORT_DESCRIPTOR :="
	(LTrim
		union {
			DWORD   Characteristics;            // 0 for terminating null import descriptor
			DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
		};
		DWORD   TimeDateStamp;                  // 0 if not bound,
												// -1 if bound, and real date\time stamp
												//     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
												// O.W. date/time stamp of DLL bound to (Old BIND)

		DWORD   ForwarderChain;                 // -1 if no forwarders
		DWORD   Name;
		DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
	)"

	static IMAGE_DOS_HEADER :="      ; DOS .EXE header
	(LTrim
		WORD   e_magic;                     // Magic number
		WORD   e_cblp;                      // Bytes on last page of file
		WORD   e_cp;                        // Pages in file
		WORD   e_crlc;                      // Relocations
		WORD   e_cparhdr;                   // Size of header in paragraphs
		WORD   e_minalloc;                  // Minimum extra paragraphs needed
		WORD   e_maxalloc;                  // Maximum extra paragraphs needed
		WORD   e_ss;                        // Initial (relative) SS value
		WORD   e_sp;                        // Initial SP value
		WORD   e_csum;                      // Checksum
		WORD   e_ip;                        // Initial IP value
		WORD   e_cs;                        // Initial (relative) CS value
		WORD   e_lfarlc;                    // File address of relocation table
		WORD   e_ovno;                      // Overlay number
		WORD   e_res[4];                    // Reserved words
		WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
		WORD   e_oeminfo;                   // OEM information; e_oemid specific
		WORD   e_res2[10];                  // Reserved words
		LONG   e_lfanew;                    // File address of new exe header
	)"

	static IMAGE_FILE_HEADER :="
	(LTrim
		WORD    Machine;
		WORD    NumberOfSections;
		DWORD   TimeDateStamp;
		DWORD   PointerToSymbolTable;
		DWORD   NumberOfSymbols;
		WORD    SizeOfOptionalHeader;
		WORD    Characteristics;
	)"

	static IMAGE_NUMBEROF_DIRECTORY_ENTRIES:=16

	static IMAGE_DATA_DIRECTORY :="
	(LTrim
		DWORD   VirtualAddress;
		DWORD   Size;
	)"

	static IMAGE_OPTIONAL_HEADER64 := "
	(LTrim
		WORD        Magic;
		BYTE        MajorLinkerVersion;
		BYTE        MinorLinkerVersion;
		DWORD       SizeOfCode;
		DWORD       SizeOfInitializedData;
		DWORD       SizeOfUninitializedData;
		DWORD       AddressOfEntryPoint;
		DWORD       BaseOfCode;
		ULONGLONG   ImageBase;
		DWORD       SectionAlignment;
		DWORD       FileAlignment;
		WORD        MajorOperatingSystemVersion;
		WORD        MinorOperatingSystemVersion;
		WORD        MajorImageVersion;
		WORD        MinorImageVersion;
		WORD        MajorSubsystemVersion;
		WORD        MinorSubsystemVersion;
		DWORD       Win32VersionValue;
		DWORD       SizeOfImage;
		DWORD       SizeOfHeaders;
		DWORD       CheckSum;
		WORD        Subsystem;
		WORD        DllCharacteristics;
		ULONGLONG   SizeOfStackReserve;
		ULONGLONG   SizeOfStackCommit;
		ULONGLONG   SizeOfHeapReserve;
		ULONGLONG   SizeOfHeapCommit;
		DWORD       LoaderFlags;
		DWORD       NumberOfRvaAndSizes;
		_MemoryLibrary.IMAGE_DATA_DIRECTORY DataDirectory[" _MemoryLibrary.IMAGE_NUMBEROF_DIRECTORY_ENTRIES "];
	)"

	static IMAGE_OPTIONAL_HEADER32 := "
	(LTrim
		//
		// Standard fields.
		//

		WORD    Magic;
		BYTE    MajorLinkerVersion;
		BYTE    MinorLinkerVersion;
		DWORD   SizeOfCode;
		DWORD   SizeOfInitializedData;
		DWORD   SizeOfUninitializedData;
		DWORD   AddressOfEntryPoint;
		DWORD   BaseOfCode;
		DWORD   BaseOfData;

		//
		// NT additional fields.
		//

		DWORD   ImageBase;
		DWORD   SectionAlignment;
		DWORD   FileAlignment;
		WORD    MajorOperatingSystemVersion;
		WORD    MinorOperatingSystemVersion;
		WORD    MajorImageVersion;
		WORD    MinorImageVersion;
		WORD    MajorSubsystemVersion;
		WORD    MinorSubsystemVersion;
		DWORD   Win32VersionValue;
		DWORD   SizeOfImage;
		DWORD   SizeOfHeaders;
		DWORD   CheckSum;
		WORD    Subsystem;
		WORD    DllCharacteristics;
		DWORD   SizeOfStackReserve;
		DWORD   SizeOfStackCommit;
		DWORD   SizeOfHeapReserve;
		DWORD   SizeOfHeapCommit;
		DWORD   LoaderFlags;
		DWORD   NumberOfRvaAndSizes;
		_MemoryLibrary.IMAGE_DATA_DIRECTORY DataDirectory[" _MemoryLibrary.IMAGE_NUMBEROF_DIRECTORY_ENTRIES "];
	)"
	static IMAGE_NT_HEADERS64 := "
	(LTrim
		DWORD Signature;
		_MemoryLibrary.IMAGE_FILE_HEADER FileHeader;
		_MemoryLibrary.IMAGE_OPTIONAL_HEADER64 OptionalHeader;
	)"

	static IMAGE_NT_HEADERS32 := "
	(LTrim
		DWORD Signature;
		_MemoryLibrary.IMAGE_FILE_HEADER FileHeader;
		_MemoryLibrary.IMAGE_OPTIONAL_HEADER32 OptionalHeader;
	)"

	static IMAGE_SECTION_HEADER :="
	(LTrim
		BYTE    Name[8];
		union {
				DWORD   PhysicalAddress;
				DWORD   VirtualSize;
		};
		DWORD   VirtualAddress;
		DWORD   SizeOfRawData;
		DWORD   PointerToRawData;
		DWORD   PointerToRelocations;
		DWORD   PointerToLinenumbers;
		WORD    NumberOfRelocations;
		WORD    NumberOfLinenumbers;
		DWORD   Characteristics;
	)"
	static IMAGE_EXPORT_DIRECTORY :="
	(LTrim
		DWORD   Characteristics;
		DWORD   TimeDateStamp;
		WORD    MajorVersion;
		WORD    MinorVersion;
		DWORD   Name;
		DWORD   Base;
		DWORD   NumberOfFunctions;
		DWORD   NumberOfNames;
		DWORD   AddressOfFunctions;     // RVA from base of image
		DWORD   AddressOfNames;         // RVA from base of image
		DWORD   AddressOfNameOrdinals;  // RVA from base of image
	)"
	static IMAGE_IMPORT_BY_NAME :="
	(LTrim
		WORD    Hint;
		BYTE    Name[1];
	)"

	__New(DataPTR){
		static Is64:=A_PtrSize=8?"64":"32"
		If DataPTR is not digit
		{
			FileRead,Data,*c %DataPTR%
			DataPTR:=&Data
		}
		dos_header := new _Struct(this.IMAGE_DOS_HEADER,DataPTR)
		
		if (dos_header.e_magic != IMAGE_DOS_SIGNATURE:=23117)
			return 0
		old_header := new _Struct(this["IMAGE_NT_HEADERS" Is64],DataPTR + dos_header.e_lfanew)
		
		if (old_header.Signature != IMAGE_NT_SIGNATURE:=17744)
			return 0
		; reserve memory for image of library
		code := DllCall("VirtualAlloc","PTR",old_header.OptionalHeader.ImageBase
			,"PTR",old_header.OptionalHeader.SizeOfImage
			,"UINT",MEM_RESERVE:=0x2000
			,"UINT",PAGE_EXECUTE_READWRITE:=0x40,"PTR")

		if (code = 0) {
			; try to allocate memory at arbitrary position
			code := DllCall("VirtualAlloc","PTR",0
				,"PTR",old_header.OptionalHeader.SizeOfImage
				,"UINT",MEM_RESERVE:=8192
				,"UINT",PAGE_EXECUTE_READWRITE:=64,"PTR")
			if (code = 0){
				ListLines
				MsgBox ERROR: Could not allocate memory
				return 0
			}
		}
		
		this.MemoryModule := new _Struct(this.MEMORYMODULE,DllCall("HeapAlloc","PTR",DllCall("GetProcessHeap","PTR"),"UINT", 0,"PTR", sizeof(this.MEMORYMODULE),"PTR"))
		this.MemoryModule.codeBase[""]:=code
		this.MemoryModule.numModules := 0
		this.MemoryModule.Alloc("modules",4)
		this.MemoryModule.initialized := 0

		; XXX: is it correct to commit the complete memory region at once?
		;      calling DllEntry raises an exception if we don't...
		DllCall("VirtualAlloc","PTR",code
			,"PTR",old_header.OptionalHeader.SizeOfImage
			,"UINT",MEM_COMMIT:=4096
			,"UINT",PAGE_EXECUTE_READWRITE:=64,"PTR")

		; commit memory for headers
		headers := DllCall("VirtualAlloc","PTR",code
			,"PTR",dos_header.e_lfanew + old_header.OptionalHeader.SizeOfHeaders
			,"UINT",MEM_COMMIT:=4096
			,"UINT",PAGE_EXECUTE_READWRITE:=64,"PTR")

		; copy PE header to code
		DllCall("RtlMoveMemory","PTR",headers, "PTR",dos_header[""],"PTR", dos_header.e_lfanew + old_header.OptionalHeader.SizeOfHeaders)
		this.MemoryModule.headers[""] := headers + dos_header.e_lfanew
		
		; update position
		this.MemoryModule.headers.OptionalHeader.ImageBase := code
		; copy sections from DLL file block to new memory location
		this.CopySections(DataPTR, old_header)
		; adjust base address of imported data
		locationDelta := code - old_header.OptionalHeader.ImageBase
		if (locationDelta != 0) {
			this.PerformBaseRelocation(locationDelta)
		}
		; load required dlls and adjust function table of imports
		if (!this.BuildImportTable()) {
				ListLines
				MsgBox ERROR: BuildImportTable failed
		}
		; mark memory pages depending on section headers and release
		; sections that are marked as "discardable"
		this.FinalizeSections()

		; get entry point of loaded library
		if (this.MemoryModule.headers.OptionalHeader.AddressOfEntryPoint != 0) {
			DllEntry := code + this.MemoryModule.headers.OptionalHeader.AddressOfEntryPoint
			if (DllEntry = 0) {
				ListLines
				MsgBox ERROR: DllEntry not found
			}
			; notify library about attaching to process
			successfull := DllCall(DllEntry,"PTR",code, "UInt",DLL_PROCESS_ATTACH:=1,"UInt", 0,"CHAR")
			if (!successfull) {
				ListLines
				MsgBox ERROR attaching process
			}
			this.MemoryModule.initialized := 1
		} 

		;~ return this
	}


	GetProcAddress(name)
	{
		codeBase := this.MemoryModule.codeBase[""]
		idx:=-1
		i:=0
		directory := this.MemoryModule.headers.OptionalHeader.DataDirectory[ 1 + IMAGE_DIRECTORY_ENTRY_EXPORT:=0]
		
		if (directory.Size = 0) {
			; no export table found
			return 0
		}

		exports := new _Struct(this.IMAGE_EXPORT_DIRECTORY,codeBase + directory.VirtualAddress)
		
		if (exports.NumberOfNames = 0 || exports.NumberOfFunctions = 0) {
			; DLL doesn't export anything
			return 0
		}
		; search function name in list of exported names
		nameRef := codeBase + exports.AddressOfNames
		ordinal := codeBase + exports.AddressOfNameOrdinals
		;~ MsgBox % exports.NumberOfNames "-" exports.AddressOfNames
		While (i<exports.NumberOfNames) {
			if (name=StrGet(codeBase + NumGet(nameRef+0,"UInt"),"CP0")) {
				idx := NumGet(ordinal+0,"Short")
				break
			}
			i++, nameRef+=4, ordinal+=2
		}
		
		if (idx = -1) {
			; exported symbol not found
			return 0
		}
		
		if (idx > exports.NumberOfFunctions) {
			; name <-> ordinal number don't match
			return 0
		}

		; AddressOfFunctions contains the RVAs to the "real" functions
		return codeBase + NumGet(codeBase + exports.AddressOfFunctions + (idx*4),"UInt")
	}
	Free(){
		i:=0
		if (this.MemoryModule.initialized != 0) {
			; notify library about detaching from process
			DllEntry := this.MemoryModule.codeBase[""] + this.MemoryModule.headers.OptionalHeader.AddressOfEntryPoint
			DllCall(DllEntry,"Ptr",this.MemoryModule.codeBase[""],"UInt", DLL_PROCESS_DETACH, "UInt",0)
			this.MemoryModule.initialized := 0
		}
		
		if (this.MemoryModule.numModules != 0) {
			; free previously opened libraries
			Loop % this.MemoryModule.numModules {
				if (this.MemoryModule.modules[A_Index] != INVALID_HANDLE_VALUE:=-1) {
					DllCall("FreeLibrary","PTR",this.MemoryModule.modules[A_Index])
				}
			}
			this.MemoryModule._SetCapacity("modules",0)
		}

		if (this.MemoryModule.codeBase[""] != 0) {
			;release memory of library
			DllCall("VirtualFree","PTR",this.MemoryModule.codeBase[""],"PTR", 0,"UINT", MEM_RELEASE:=32768)
		}

		DllCall("HeapFree","PTR",DllCall("GetProcessHeap","PTR"),"UINT", 0,"PTR", this.MemoryModule[""])
	}
	
	
	
	
	; Private functions
	CopySections(data, old_headers)
	{
		codeBase := this.MemoryModule.codeBase[""]
		section := new _Struct(this.IMAGE_SECTION_HEADER,this.MemoryModule.headers.OptionalHeader[""] + this.MemoryModule.headers.FileHeader.SizeOfOptionalHeader)
		While ((A_Index-1)<this.MemoryModule.headers.FileHeader.NumberOfSections){
			if (section.SizeOfRawData = 0) {
				; section doesn't contain data in the dll itself, but may define
				; uninitialized data
				size := old_headers.OptionalHeader.SectionAlignment
				if (size > 0) {
					dest := DllCall("VirtualAlloc","PTR",codeBase + section.VirtualAddress
						,"PTR",size
						,"UINT",MEM_COMMIT:=4096
						,"UINT",PAGE_EXECUTE_READWRITE:=64,"PTR")
					section.PhysicalAddress := dest
					DllCall("RtlFillMemory","PTR",dest,"UINT", size,"CHAR", 0)
				}

				; section is empty
				section[]:=section[""]+sizeof(section)
				continue
			}
			; commit memory block and copy data from dll
			dest := DllCall("VirtualAlloc","PTR",codeBase + section.VirtualAddress
								,"PTR",section.SizeOfRawData
								,"UINT",MEM_COMMIT:=4096
								,"UINT",PAGE_EXECUTE_READWRITE:=64,"PTR")
			DllCall("RtlMoveMemory","PTR",dest,"PTR",data + section.PointerToRawData,"PTR", section.SizeOfRawData)
			section.PhysicalAddress := dest
			section[]:=section[""]+sizeof(section)
		}
	}

	FinalizeSections(){
		static ProtectionFlags := [[[PAGE_NOACCESS],[PAGE_READONLY]], [[PAGE_EXECUTE],[PAGE_EXECUTE_READ]]]
		i:=0
		section := new _Struct(this.IMAGE_SECTION_HEADER,this.MemoryModule.headers.OptionalHeader[""] + this.MemoryModule.headers.FileHeader.SizeOfOptionalHeader)
		If (A_PtrSize=8)
			imageOffset := (NumGet(this.MemoryModule.headers.OptionalHeader.ImageBase[""]+4,"UInt") & 0xffffffff)<<32
		else
			imageOffset := 0

		; loop through all sections and change access flags
		While (i<this.MemoryModule.headers.FileHeader.NumberOfSections){
			protect:=0, VarSetCapacity(oldProtect,8), size:=0
			executable := (section.Characteristics & IMAGE_SCN_MEM_EXECUTE:=536870912) != 0
			readable :=   (section.Characteristics & IMAGE_SCN_MEM_READ:=1073741824) != 0
			writeable :=  (section.Characteristics & IMAGE_SCN_MEM_WRITE:=2147483648) != 0
			if (section.Characteristics & IMAGE_SCN_MEM_DISCARDABLE:=33554432) {
				; section is not needed any more and can safely be freed
				DllCall("VirtualFree","PTR",section.PhysicalAddress | imageOffset, "PTR",section.SizeOfRawData,"UINT", MEM_DECOMMIT:=16384)
				i++, section[]:=section[""]+sizeof(section)
				continue
			}

			; determine protection flags based on characteristics
			protect := ProtectionFlags[executable+1,readable+1,writeable+1]
			if (section.Characteristics & IMAGE_SCN_MEM_NOT_CACHED:=67108864) {
				protect |= PAGE_NOCACHE:=512
			}

			; determine size of region
			size := section.SizeOfRawData
			if (size = 0) {
				if (section.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA:=64) {
					size := this.MemoryModule.headers.OptionalHeader.SizeOfInitializedData
				} else if (section.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA:=128) {
					size := this.MemoryModule.headers.OptionalHeader.SizeOfUninitializedData
				}
			}
			if (size > 0) {
				; change memory access flags
				if (DllCall("VirtualProtect","PTR",section.PhysicalAddress | imageOffset,"PTR", size,"UINT", protect,"PTR", &oldProtect) = 0)
					break
			}
			i++, section[]:=section[""]+sizeof(section)
		}
	}

	PerformBaseRelocation(delta){
		codeBase := this.MemoryModule.codeBase[""]
		this.MemoryModule.headers.OptionalHeader.DataDirectory[idx+1]
		directory := this.MemoryModule.headers.OptionalHeader.DataDirectory[ 1 + IMAGE_DIRECTORY_ENTRY_BASERELOC:=5] ; +1 because _Struct is 1 based
		if (directory.Size > 0) {
			relocation := new _Struct(this.IMAGE_BASE_RELOCATION,codeBase + directory.VirtualAddress)
			While (relocation.VirtualAddress > 0 ) {
				dest := codeBase + relocation.VirtualAddress
				relInfo := relocation[""] + IMAGE_SIZEOF_BASE_RELOCATION:=sizeof(this.IMAGE_BASE_RELOCATION)
				While ((A_Index-1)<((relocation.SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2)){
					; the upper 4 bits define the type of relocation
					type := NumGet(relInfo+0,"UShort") >> 12
					; the lower 12 bits define the offset
					offset := NumGet(relInfo+0,"UShort") & 0xfff
					If ((type=IMAGE_REL_BASED_HIGHLOW:=3) || (A_PtrSize=8 && type=IMAGE_REL_BASED_DIR64:=10)){
						; change complete 32/64 bit address
						patchAddrHL := dest + offset
						NumPut(NumGet(patchAddrHL+0,"PTR")+delta,patchAddrHL+0,"PTR")
					}
					relInfo+=2
				}
				; advance to next relocation block
				relocation[] := relocation[""] + relocation.SizeOfBlock
			}
		}
	}

	BuildImportTable(){
		result := 1
		VarSetCapacity(lpCookie,A_PtrSize)
		codeBase := this.MemoryModule.codeBase[""]
		directory := this.MemoryModule.headers.OptionalHeader.DataDirectory[ 1 + IMAGE_DIRECTORY_ENTRY_IMPORT:=1] ; +1 because _Struct is 1 based
		resource := this.MemoryModule.headers.OptionalHeader.DataDirectory[ 1 + IMAGE_DIRECTORY_ENTRY_RESOURCE:=2] ; +1 because _Struct is 1 based
		if (directory.Size > 0) 
		{
			importDesc := new _Struct(this.IMAGE_IMPORT_DESCRIPTOR, codeBase + directory.VirtualAddress)
			; Following will be used to resolve manifest in module
			if (resource.Size)
			{
				resDir := new _Struct(this.IMAGE_RESOURCE_DIRECTORY,codeBase + resource.VirtualAddress)
				resDirTemp := new _Struct(this.IMAGE_RESOURCE_DIRECTORY)
				resDirEntry := new _Struct(this.IMAGE_RESOURCE_DIRECTORY_ENTRY,resDir[""] + sizeof(this.IMAGE_RESOURCE_DIRECTORY))
				resDirEntryTemp := new _Struct(this.IMAGE_RESOURCE_DIRECTORY_ENTRY)
				resDataEntry := new _Struct(this.IMAGE_RESOURCE_DATA_ENTRY)
				; ACTCTX Structure, not used members must be set to 0!
				actctx :=new _Struct(this._ACTCTX)
				actctx.cbSize :=  sizeof(actctx)
			
				; Path to temp directory + our temporary file name
				VarSetCapacity(buf,1024)
				tempPathLength := DllCall("GetTempPath","UINT",256,"PTR", &buf,"UINT")
				DllCall("RtlMoveMemory","PTR",(&buf) + tempPathLength*2,"PTR",&_temp_:="AutoHotkey.MemoryModule.temp.manifest","PTR",74)
				actctx.lpSource[""]:=&buf
				; Enumerate Resources
				i := 0
				
				While (i < resDir.NumberOfIdEntries + resDir.NumberOfNamedEntries)
				{
					; Resolve current entry
					resDirEntry[] := resDir[""] + sizeof(this.IMAGE_RESOURCE_DIRECTORY) + (i*sizeof(this.IMAGE_RESOURCE_DIRECTORY_ENTRY))
					
					; If entry is directory and Id is 24 = RT_MANIFEST
					if ((resDirEntry.OffsetToData & 0x80000000) && resDirEntry.Id = 24)
					{
						resDirEntryTemp[] := resDir[""] + (resDirEntry.OffsetToData & 0x7FFFFFFF) + sizeof(this.IMAGE_RESOURCE_DIRECTORY)
						resDirTemp[] := resDir[""] + (resDirEntryTemp.OffsetToData & 0x7FFFFFFF)
						resDirEntryTemp[] := resDir[""] + (resDirEntryTemp.OffsetToData & 0x7FFFFFFF) + sizeof(this.IMAGE_RESOURCE_DIRECTORY)
						resDataEntry[] := resDir[""] + resDirEntryTemp.OffsetToData
						; Write manifest to temportary file
						; Using FILE_ATTRIBUTE_TEMPORARY will avoid writing it to disk
						; It will be deleted after CreateActCtx has been called.
						hFile := DllCall("CreateFile,","STR",buf,"UINT",GENERIC_WRITE:=1073741824,"UINT",0,"PTR",0,"UINT",CREATE_ALWAYS:=2,"UINT",FILE_ATTRIBUTE_TEMPORARY:=256,"PTR",0,"PTR")
						if (hFile = INVALID_HANDLE_VALUE:=-1)
							break ; failed to create file, continue and try loading without CreateActCtx
						VarSetCapacity(byteswritten,A_PtrSize,0)
						DllCall("WriteFile","PTR",hFile,"PTR",(codeBase + resDataEntry.OffsetToData),"UINT",resDataEntry.Size,"PTR",&byteswritten,"PTR",0)
						DllCall("CloseHandle","PTR",hFile)
						if (NumGet(byteswritten,"PTR") = 0)
							break ; failed to write data, continue and try loading
						
						hActCtx := DllCall("CreateActCtx","PTR",actctx[""],"PTR")
						; Open file and automatically delete on CloseHandle (FILE_FLAG_DELETE_ON_CLOSE)
						hFile := DllCall("CreateFile","STR",buf,"UINT",GENERIC_WRITE:=1073741824,"UINT",FILE_SHARE_DELETE:=4,"PTR",0,"UINT",OPEN_EXISTING:=3,"UINT",FILE_ATTRIBUTE_TEMPORARY:=256|FILE_FLAG_DELETE_ON_CLOSE:=67108864,"PTR",0,"PTR")
						DllCall("CloseHandle","PTR",hFile)
						if (hActCtx = INVALID_HANDLE_VALUE:=-1)
							break ; failed to create context, continue and try loading

						DllCall("ActivateActCtx","PTR",hActCtx,"PTR",&lpCookie) ;  Don't care if this fails since we would countinue anyway
						break ; Break since a dll can have only 1 manifest
					}
					i++
				}
			}
			While (!DllCall("IsBadReadPtr","PTR",importDesc[""], "PTR", sizeof(this.IMAGE_IMPORT_DESCRIPTOR)) && importDesc.Name) 
			{
				if (!(handle := DllCall("LoadLibraryA","PTR",(codeBase + importDesc.Name),"PTR")))
				{
						result := 0
						break
				}
				If (this.Capacity("modules")<NewModulesSize:=(this.MemoryModule.numModules+1)*sizeof("HMODULE")){
					VarSetCapacity(backupModules,this.MemoryModule.numModules*sizeof("HMODULE"))
					DllCall("RtlMoveMemory","PTR",&backupModules,"PTR",this.MemoryModule.modules[""],"PTR",this.MemoryModule.numModules*sizeof("HMODULE"))
					this.MemoryModule.Alloc("modules",NewModulesSize)
					DllCall("RtlMoveMemory","PTR",this.MemoryModule.modules[""],"PTR",&backupModules,"PTR",NewModulesSize)
				}
				if (this.Capacity("modules") = 0) {
					result := 0
					break
				}
				this.MemoryModule.modules[++this.MemoryModule.numModules] := handle
				
				if (importDesc.OriginalFirstThunk) {
					thunkRef := codeBase + importDesc.OriginalFirstThunk
					funcRef := codeBase + importDesc.FirstThunk
				} else {
					; no hint table
					thunkRef := codeBase + importDesc.FirstThunk
					funcRef := codeBase + importDesc.FirstThunk
				}
				thunkData:=new _Struct(this.IMAGE_IMPORT_BY_NAME)
				While NumGet(thunkRef+0,"PTR"){
					if (NumGet(thunkRef+A_PtrSize-1,"UCHAR") & IMAGE_ORDINAL_FLAG_AS_CHAR:=0x80) {
						NumPut(DllCall("GetProcAddress","PTR",handle,"PTR", NumGet(thunkRef+0,"PTR") & 0xffff,"PTR"),funcRef+0,"PTR")
					} else {
						thunkData[] := codeBase + NumGet(thunkRef+0,"PTR")
						NumPut(DllCall("GetProcAddress","PTR",handle,"PTR", thunkData.Name[""],"PTR"),funcRef+0,"PTR")
					}
					if (NumGet(funcRef+0,"PTR") = 0) {
						result := 0
						break
					}
					thunkRef+=A_PtrSize,funcRef+=A_PtrSize
				}

				if (!result) {
					break
				}
				
				importDesc[]:=importDesc[""]+sizeof(importDesc)
			}
		}
		if (NumGet(lpCookie,"PTR"))
			DllCall("DeactivateActCtx","UINT",0,"PTR",&lpCookie)
		return result
	}
}
AhkDllThread_IsH(StringFileInfo="InternalName"){ ; FileGetVersionInfo Written by SKAN modified by HotKeyIt www.autohotkey.com/forum/viewtopic.php?p=233188#233188
 Static HexVal:="msvcrt\s" (A_IsUnicode?"w":"") "printf",Sps="                        "
 If FSz:=DllCall("Version\GetFileVersionInfoSize",Str,A_AhkPath,UInt,0) && VarSetCapacity(FVI,FSz,0) && VarSetCapacity(Trans,8*(A_IsUnicode?2:1))
  && DllCall("Version\GetFileVersionInfo",Str,A_AhkPath,Int,0,UInt,FSz,UInt,&FVI) 
  && DllCall("Version\VerQueryValue",UInt,&FVI,Str,"\VarFileInfo\Translation",UIntP,Translation,UInt,0)
  && DllCall(HexVal,Str,Trans,Str,"%08X",UInt,NumGet(Translation+0))
  && DllCall("Version\VerQueryValue",UInt,&FVI,Str,"\StringFileInfo\" SubStr(Trans,-3) SubStr(Trans,1,4) "\InternalName",UIntP,InfoPtr,UInt,0)
    Return InStr(A_IsUnicode?StrGet(InfoPtr,DllCall("lstrlen" (A_IsUnicode?"W":"A"),UInt,InfoPtr)):DllCall("MulDiv",UInt,InfoPtr,Int,1,Int,1,"Str"),"AutoHotkey_H")
}
AhkDllThread(dll="AutoHotkey.dll",obj=0){
  static init,ahkfunction,hLib,dllptr,libScript,ahkexec,DynaCall:="DynaCall", MemoryLoadLibrary:="MemoryLoadLibrary",MemoryFreeLibrary:="MemoryFreeLibrary"
      ,ResourceLoadLibrary:="ResourceLoadLibrary", MemoryGetProcAddress:="MemoryGetProcAddress"
  static AHK_H:=AhkDllThread_IsH()
  static base:={__Delete:"AhkDllThread"}
  static functions :="
(Join
ahkFunction:s=sssssssssss|ahkPostFunction:i=sssssssssss|
ahkdll:ut=sss|ahktextdll:ut=sss|ahkReady:|ahkReload:ui=|
ahkTerminate:i|addFile:ut=sucuc|addScript:ut=si|ahkExec:ui=s|
ahkassign:ui=ss|ahkExecuteLine:ut=utuiui|ahkFindFunc:ut=s|
ahkFindLabel:ut=s|ahkgetvar:s=sui|ahkLabel:ui=sui|ahkPause:s
)"
  static AhkDllThreadfunc :="
(Join`r`n
" (!A_IsCompiled ? "#include <_MemoryLibrary>" : "") "
#Persistent
SetBatchLines,-1
#NoTrayIcon
Return
AhkDllThreadDLL(dll=""AutoHotkey.dll"",obj=0){
  static functions = ""ahkFunction:s=sssssssssss|ahkPostFunction:i=sssssssssss|""
              . ""ahkdll:ut=sss|ahktextdll:ut=sss|ahkReady:|ahkReload:ui=|""
              . ""ahkTerminate:i|addFile:ut=sucuc|addScript:ut=si|ahkExec:ui=s|""
              . ""ahkassign:ui=ss|ahkExecuteLine:ut=utuiui|ahkFindFunc:ui=s|""
              . ""ahkFindLabel:ui=s|ahkgetvar:s=sui|ahkLabel:ui=sui|ahkPause:s""
  If (dll=""0""){
    object:=""""
    return
  } else If (!FileExist(dll) && obj=0){
    MsgBox File: `%dll`% does not exist`, provide correct path for AutoHotkey.dll
    ExitApp
  }
  object:={"""":new _MemoryLibrary(obj?obj:dll)}
  ;~ MsgBox `% ""created "" ;IsObject(object[""""])
  ;~ MsgBox `% ""dll: "" object[""""].GetProcAddress(""ahktextdll"")
  Loop,Parse,functions,|
  {
    StringSplit,v,A_LoopField,:
    object[map="""" ? v1 ]:=DynaCall(object[""""].GetProcAddress(v1),v2)
  }
  object.base:=Object(" (&base) ")
  return &object
}
)"
  If IsObject(dll){
    dll.ahkterminate()
    If !AHK_H {
      dll[""].Free(),dll:=""
    } else %MemoryFreeLibrary%(dll[""])
    return
  } else if (!FileExist(dll) && !A_IsCompiled){
    MsgBox File: %dll%`ndoes not exist`, provide correct path for AutoHotkey.dll
    ExitApp
  }
  If !AHK_H{
    If (init || ((hLib:=new _MemoryLibrary(A_IsCompiled?(dllptr:=DllCall("LockResource","ptr",DllCall("LoadResource","ptr",lib,"ptr",DllCall("FindResource","ptr",lib:=DllCall("GetModuleHandle","ptr",0,"ptr"),"str",dll,"ptr",10,"ptr"),"ptr"),"ptr")):dll)) && (Init:=hLib.GetProcAddress("ahktextdll")) && (!A_IsCompiled||LibScript:=(!(res:=DllCall("FindResource","ptr",lib:=DllCall("GetModuleHandle","ptr",0,"ptr"),"str",">AUTOHOTKEY SCRIPT<","ptr",10,"ptr"))?(res:=DllCall("FindResource","ptr",lib,"str",">AHK WITH ICON<","ptr",10,"ptr")):res)?StrGet(DllCall("LockResource","ptr",DllCall("LoadResource","ptr",lib,"ptr",res,"ptr"),"ptr"),DllCall("SizeofResource","ptr",lib,"ptr",res,"uint"),"UTF-8"):""))){
      If (ahkfunction || (DllCall(init,"Str",AhkDllThreadfunc "`n" LibScript,"Str","","Str","","Cdecl UInt") && (ahkfunction:=hLib.GetProcAddress("ahkFunction")) && (ahkExec:=hLib.GetProcAddress("ahkExec")))){
        return Object(0+DllCall(ahkfunction,"Str","AhkDllThreadDLL","Str",dll,"Str",A_IsCompiled?dllptr:"","Str","","Str","","Str","","Str","","Str","","Str","","Str","","Str","","CDecl Str"))
        ;reset internal return memory in autoHotkey.dll and release object
        ,DllCall(ahkfunction,"Str","AhkDllThreadDLL","Str","0","Str","","Str","","Str","","Str","","Str","","Str","","Str","","Str","","Str","","CDecl Str")
      } else {
        MsgBox Could not load script in %dll%
        Return 0
      }
    } else {
      MsgBox Could not load %dll%
      Return 0
    }
  }
  object:=IsObject(obj)?obj:{},object[""]:= A_IsCompiled ? %ResourceLoadLibrary%(dll) : %MemoryLoadLibrary%(dll)
  Loop,Parse,functions,|
  {
    StringSplit,v,A_LoopField,:
    object[map="" ? v1 ]:=%DynaCall%(%MemoryGetProcAddress%(object[""],v1),v2)
  }
  return object,object.base:=base
}
EDIT:
Fixed to use no #include when compiled.

EDIT 07.03.2012:
Optimized code in _MemoryLibrary
autoHotkey.net is currently down so I included the code above

EDIT 08.03.2012:
Small fix in AhkDllThread()

  • Guests
  • Last active:
  • Joined: --
Now I get this error with _L when compiled. It runs fine if it's not compiled.

Error at line 1.

Line Text: #include <_MemoryLibrary>
Error: Function library not found.

The program will exit.

I put _MemoryLibrary.ahk in C:\Program Files\AutoHokey\lib. But it doesn't help.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
:oops: Try above again now.

  • Guests
  • Last active:
  • Joined: --
Got it working! Very nice, thanks.

I wonder why this fixed it though
" (!A_IsCompiled ? "#include <_MemoryLibrary>" : "") "
because this means the thread script doesn't know the _MemoryLibrary class when it's compiled. But it seems to be working. Why is it?

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
Because I load it from main script since it is already included there :)
... (!A_IsCompiled||[color=red]LibScript[/color]:=AhkDllThread_LoadScriptResource()) ...

... (DllCall(init,"Str",AhkDllThreadfunc "`n" [color=red]LibScript[/color], ...


  • Guests
  • Last active:
  • Joined: --
I see. Actually I still don't get how it works but good job! :)

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
I have optimized _MemoryLibrary, it should be loading faster now.
I have included _MemoryLibrary in this post because autohotkey.net is currently down.
I have also removed dependency on FileGetVersionInfo_AW(), now AhkDllThread_IsH() does the job.

  • Guests
  • Last active:
  • Joined: --
I get Could not load <dll path> errors in both compiled and non-compiled scripts with the updated functions in _L.

  • Guests
  • Last active:
  • Joined: --

I get Could not load <dll path> errors in both compiled and non-compiled scripts with the updated functions in _L.

It was because #Include <_MemoryLibrary> was missing in AhkDllThread.ahk since I saved them separately. And now it looks faster, nice update.

I wish I didn't have to save _MemoryLibrary.ahk in C:\Program Files\AutoHotkey\lib. Just simply putting it in <ScriptDir>\lib would be great.

  • Guests
  • Last active:
  • Joined: --
A new issue has been found with the new update. When I run the example code posted here for the second time, the script silently crashes/exits. In other words, if I run the script two times, the second instance gets terminated for some reason. It happens in both compiled and non-compiled scripts.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008
That should be fixed now. (Extract the _MemoryLibrary class from above and replace yours in Lib folder)

  • Guests
  • Last active:
  • Joined: --

That should be fixed now. (Extract the _MemoryLibrary class from above and replace yours in Lib folder)

Yap, it looks like working fine now. Thanks.

What do you think about this? Do you think it's feasible?

I wish I didn't have to save _MemoryLibrary.ahk in C:\Program Files\AutoHotkey\lib. Just simply putting it in <ScriptDir>\lib would be great.



HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008

What do you think about this? Do you think it's feasible?

I wish I didn't have to save _MemoryLibrary.ahk in C:\Program Files\AutoHotkey\lib. Just simply putting it in <ScriptDir>\lib would be great.

This has been implemented now :)

I have also uploaded new versions of AhkDllThread() for v1 and v2.

  • Guests
  • Last active:
  • Joined: --
[quote name="HotKeyIt"][/quote]This has been implemented now :)[/quote]Very nice. Seems to be working fine so far with _L, compiled. Now AutoHotkey.dll related lib files can be organized separately per project.

One suggestion. I've been having difficulty testing and organizing your functions because they don't have a version number. What about putting it in comments in the code? That helps the users when something starts not working and needs to go back to the previous version.

HotKeyIt
  • Moderators
  • 7439 posts
  • Last active: Jun 22 2016 09:14 PM
  • Joined: 18 Jun 2008

One suggestion. I've been having difficulty testing and organizing your functions because they don't have a version number. What about putting it in comments in the code? That helps the users when something starts not working and needs to go back to the previous version.

I will keep in mind for future releases :)