notepad get/set path (get/set text file path)

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

notepad get/set path (get/set text file path)

03 Apr 2017, 15:37

Notepad get/set path
tested on AHK v1.1 U32/U64
tested on Notepad (Windows XP x32/Windows 7 x32/Windows 7 x64 versions)

Warning: use at your own risk, scripts that retrieve information from external programs can potentially crash them, save any files before testing.

[EDIT:][JEE_NotepadGetPath][updated: 2017-04-06]

Code: Select all

;e.g.
;q::
;WinGet, hWnd, ID, ahk_class Notepad
;WinGetTitle, vWinTitle, % "ahk_id " hWnd
;vPath := JEE_NotepadGetPath(hWnd)
;MsgBox, % "window title: " vWinTitle "`r`n" "file path: " vPath
;return

;JEE_NotepadGetFilename
JEE_NotepadGetPath(hWnd)
{
	WinGetClass, vWinClass, % "ahk_id " hWnd
	if !(vWinClass = "Notepad")
		return
	WinGet, vPID, PID, % "ahk_id " hWnd
	WinGet, vPPath, ProcessPath, % "ahk_id " hWnd
	FileGetVersion, vPVersion, % vPPath

	MAX_PATH := 260
	;PROCESS_QUERY_INFORMATION := 0x400 ;PROCESS_VM_READ := 0x10
	if !hProc := DllCall("kernel32\OpenProcess", UInt,0x410, Int,0, UInt,vPID, Ptr)
		return
	if (vPVersion = "5.1.2600.5512") ;Notepad (Windows XP version)
		vAddress := 0x100A900
	if !vAddress
	{
		VarSetCapacity(MEMORY_BASIC_INFORMATION, A_PtrSize=8?48:28, 0)
		vAddress := 0
		Loop
		{
			if !DllCall("kernel32\VirtualQueryEx", Ptr,hProc, Ptr,vAddress, Ptr,&MEMORY_BASIC_INFORMATION, UPtr,A_PtrSize=8?48:28, UPtr)
				break

			vMbiBaseAddress := NumGet(MEMORY_BASIC_INFORMATION, 0, "Ptr")
			vMbiRegionSize := NumGet(MEMORY_BASIC_INFORMATION, A_PtrSize=8?24:12, "UPtr")
			vMbiState := NumGet(MEMORY_BASIC_INFORMATION, A_PtrSize=8?32:16, "UInt")
			vMbiType := NumGet(MEMORY_BASIC_INFORMATION, A_PtrSize=8?40:24, "UInt")

			vPath := ""
			VarSetCapacity(vPath, MAX_PATH*2)
			DllCall("psapi\GetMappedFileName", Ptr,hProc, Ptr,vMbiBaseAddress, Str,vPath, UInt,MAX_PATH*2, UInt)
			if !(vPath = "")
				SplitPath, vPath, vName
			if (vMbiState & 0x1000) ;MEM_COMMIT := 0x1000
			&& (vMbiType & 0x1000000) ;MEM_IMAGE := 0x1000000
			&& InStr(vName, "notepad")
			{
				;get address where path starts
				if A_Is64bitOS
					DllCall("kernel32\IsWow64Process", Ptr,hProc, IntP,vIsWow64Process)
				if !A_Is64bitOS || vIsWow64Process ;if process is 32-bit
					vAddress := vMbiBaseAddress + 0xCAE0 ;(vMbiBaseAddress + 0xD378 also appears to work)
				else
					vAddress := vMbiBaseAddress + 0x10B40
				break
			}
			vAddress += vMbiRegionSize
			if (vAddress > 2**32-1) ;4 gigabytes
				return
		}
	}
	VarSetCapacity(vPath, MAX_PATH*2, 0)
	DllCall("kernel32\ReadProcessMemory", Ptr,hProc, Ptr,vAddress, Str,vPath, UPtr,MAX_PATH*2, UPtr,0)
	DllCall("kernel32\CloseHandle", Ptr,hProc)
	return vPath
}

Code: Select all

;e.g.
;q::
;WinGet, hWnd, ID, ahk_class Notepad
;vPath := A_ScriptFullPath
;JEE_NotepadSetPath(hWnd, vPath)
;return

JEE_NotepadSetPath(hWnd, vPath)
{
	PostMessage, 0x233, % JEE_DropCreate(vPath), 0, , % "ahk_id " hWnd ;WM_DROPFILES := 0x233
	return
}

;==================================================

;e.g. hDrop := JEE_DropCreate(vPaths)

;where vPaths is an LF/CRLF-separated list
;JEE_HDropSetPaths
JEE_DropCreate(vPaths, vPosX=0, vPosY=0)
{
	;GMEM_ZEROINIT := 0x40, GMEM_MOVEABLE := 0x2
	vWidth := A_IsUnicode?2:1
	hDrop := DllCall("GlobalAlloc", UInt,0x42, UPtr,20+(StrLen(vPaths)+2)*vWidth, Ptr)
	pDrop := DllCall("GlobalLock", Ptr,hDrop)

	;DROPFILES struct
	NumPut(20, pDrop+0, 0, "UInt")
	NumPut(vPosX, pDrop+4, 0, "UInt")
	NumPut(vPosY, pDrop+8, 0, "UInt")
	NumPut(A_IsUnicode?1:0, pDrop+16, 0, "UInt")

	;e.g. CF_HDROP with 3 paths: 'path1 null path2 null path3 null null'
	vOffset := 20
	Loop, Parse, vPaths, `n, `r
		if !(A_LoopField = "")
		{
			DllCall("RtlMoveMemory", UInt,pDrop+vOffset, Str,A_LoopField, UInt,StrLen(A_LoopField)*vWidth)
			vOffset += (StrLen(A_LoopField)+1)*vWidth
		}

	DllCall("GlobalUnlock", Ptr,hDrop)
	return hDrop
}
GET PATH:
To get the path, find the base address for the notepad.exe region in the address space, and then add 0xCAE0 (32-bit) or 0x10B40 (64-bit), the path should be located there. It always seems to be consistent. If it were not consistent, one method would be to grab the window title from the title bar, to get the file name, prepend a backslash, and search for it in the address space.

SET PATH:
Create an hDrop containing the path you want, then send a WM_DROPFILES message to Notepad, via PostMessage, with the hDrop as a parameter. This is a useful general technique that works on many programs including Media Player Classic. It emulates dragging and dropping a file onto a window.

Note: using WM_DROPFILES and SendMessage, instead of PostMessage, didn't work, it just paused and nothing happened.

Note: You can use COM to set the path/url in Explorer/Internet Explorer. I had trouble finding a method to set the path in an Adobe Reader window programmatically, if anyone knows a way.

[EDIT:] If JEE_NotepadGetPath is compatible with Windows 8/Windows 10 or not, or is/isn't compatible with certain language versions, do let me know. Cheers.

==================================================

NOTE: FILE NAME RETRIEVED BUT DIRECTORY OMITTED

Code: Select all

;Let's say you have a folder containing 2 files:
;%A_Desktop%\New Folder\notepad.exe
;%A_Desktop%\New Folder\New Text Document.txt

;If you do this, it will retrieve the file name but no directory:
vDir = %A_Desktop%\New Folder
SetWorkingDir, % vDir
Run, notepad.exe "New Text Document.txt"
WinWait, New Text Document ahk_class Notepad
WinGet, hWnd, ID, New Text Document ahk_class Notepad
MsgBox, % JEE_NotepadGetPath(hWnd)
return
==================================================
Last edited by jeeswg on 06 Apr 2017, 09:00, edited 9 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

Re: notepad get path (get text file path)

03 Apr 2017, 15:59

no way....

INDENTATION?

BLASPHEMY!

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: notepad get path (get text file path)

03 Apr 2017, 16:20

It seems to work well jeeswg, and neatly coded. :thumbup:
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: notepad get/set path (get/set text file path)

03 Apr 2017, 17:03

Great responses, cheers. Yeah, for better or for worse, ... for AutoHotkey, I've agreed to indentation.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
XeroByte
Posts: 26
Joined: 17 Sep 2014, 01:30

Re: notepad get/set path (get/set text file path)

24 Apr 2017, 05:18

I'd all but given up on retrieving the path of an open notepad file, I'm glad to see it's possible.

Unfortunately it doesn't seem to work on my system Win10 x64 running latest ahk version

I get a blank string for the path, and since this code is beyond me I don't really know where to start with debugging it.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: notepad get/set path (get/set text file path)

24 Apr 2017, 10:37

I have occasional access to a Windows 10 computer now, so I might investigate.

To understand how it works. If you use Process Hacker, 'right-click, Properties', on an exe, and go to the Memory tab, you see its list of memory regions. (Warning: this just crashed my Notepad instance, updating might fix this.) You will see different memory regions containing 'notepad.exe'. The path is usually in one of these memory regions at a consistent offset (the offset depends on whether the Notepad instance is x64 or x32).

The problem may be that the offset is consistent but different, or no longer consistent (binary searching would be required), or is kept in a different region, or the region starts at a point beyond 4 gigabytes (change the limit in the script), or a UAC issue maybe, or something else.

Btw here is a fallback method, it gets the Edit control's modification flag and sets it to 1 making Notepad think there are unsaved changes, it tells Notepad to do 'File, New', a prompt comes up that contains the file path, it grabs the file path and closes the prompt, and then sets the modification flag back to 0 if it was originally 0.

Note: As ever, test this on a Notepad window, where you do not risk losing important unsaved changes.

Note: the script requires Acc.
Acc library (MSAA) and AccViewer download links - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=26201

Code: Select all

;e.g.
;q::
;WinGet, hWnd, ID, ahk_class Notepad
;WinGetTitle, vWinTitle, % "ahk_id " hWnd
;vPath := JEE_NotepadGetPath2(hWnd)
;MsgBox, % "window title: " vWinTitle "`r`n" "file path: " vPath
;return

;Notepad get path workaround
;tested on Notepad (Windows XP and Windows 7)
JEE_NotepadGetPath2(hWnd)
{
	WinGetClass, vWinClass, % "ahk_id " hWnd
	if !(vWinClass = "Notepad")
		return
	WinGetTitle, vWinTitle, % "ahk_id " hWnd
	if (vWinTitle = "Untitled - Notepad")
		return
	WinGet, vPID, PID, % "ahk_id " hWnd
	SendMessage, 0xB8, 0, 0, Edit1, % "ahk_id " hWnd ;EM_GETMODIFY := 0xB8
	if !(vIsMod := ErrorLevel)
		SendMessage, 0xB9, 1, 0, Edit1, % "ahk_id " hWnd ;EM_SETMODIFY := 0xB9
	PostMessage, 0x111, 1,,, % "ahk_id " hWnd ;WM_COMMAND := 0x111 ;File, New
	vWinCriteria := "Notepad ahk_class #32770 ahk_pid " vPID
	WinWaitActive, % vWinCriteria
	WinGet, hWnd2, ID, % vWinCriteria
	ControlGet, hCtl, Hwnd,, DirectUIHWND1, % "ahk_id " hWnd2
	if hCtl
	{
		oAcc := Acc_Get("Object", "4.1", 0, "ahk_id " hCtl)
		vText := oAcc.accName(0)
		oAcc := ""
	}
	else
		ControlGetText, vText, Static2, % "ahk_id " hWnd2
	WinClose, % "ahk_id " hWnd2
	if !vIsMod
		SendMessage, 0xB9, 0, 0, Edit1, % "ahk_id " hWnd ;EM_SETMODIFY := 0xB9
	vLen := StrLen(vText)
	if (SubStr(vText, 1, 31) = "Do you want to save changes to ")
	&& (SubStr(vText, vLen) = "?")
		vText := SubStr(vText, 32, -1)
	if (SubStr(vText, 1, 16) = "The text in the ")
	&& (SubStr(vText, vLen+1-52) = " file has changed.`n`nDo you want to save the changes?")
		vText := SubStr(vText, 17, -52)
	return vText
}
Btw I think that being able to easily retrieve the path in Notepad is pretty fundamental, and is one of the reasons, maybe the main reason, why I first started investigating writing a Notepad replacement in AutoHotkey.

It's good to see that here and elsewhere people value this functionality.

How do I get the full path of a document open in Windows notepad? - Super User
https://superuser.com/questions/994887/ ... ws-notepad

[EDIT: workaround function changed to support Windows XP and Windows 7 versions of Notepad. Previously it was only tested on the Windows 7 version.]
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
XeroByte
Posts: 26
Joined: 17 Sep 2014, 01:30

Re: notepad get/set path (get/set text file path)

25 Apr 2017, 06:36

Thanks @jeeswg for the explanation and the fallback method.
I've never used Process Hacker before but it looks like a useful tool so thanks for that.

I looked through all the entries in the notepad.exe process in the memory tab but i got a bit lost there.... I opened up the hex display for all every entry that had 'notepad.exe' but I couldn't see a reference to my opened file. I noticed that if i exported the Memory strings then i could find multiple entries showing the open file, however I have no clue if that is of any help at all... And I'm nooby, so I'm not even sure if I understood what you meant when you talked about the offset, so I may be looking in the wrong places. Is there anything that I can post here that will help you to identify what's changed?

The fallback method is pretty nice - I gave it a shot and it definitely works as described, but I think I'll hold off on implementing this into my scripts for the moment as I'd be far happier with your main method that doesn't need to flash the prompt.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: notepad get/set path (get/set text file path)

25 Apr 2017, 15:27

OK I've done some tests, and I have a new version of the function that works on the Windows 10 PC I tested it on. It probably still works with Windows XP and Windows 7.

It works with Notepad v10.0.14393.0, x64 and x32. Notepad (Windows 10) appears to keep the path at a consistent address.

If your Notepad version number is different, you may need to change the version number in the function.

Warning: I need to add in a line that checks if there is memory region at a particular address as a safety check, I can't remember what dll function does that.

Relevant memory regions and addresses:
0x7ff770c52000, Image: Commit, 12 kB, RW, C:\Windows\System32\notepad.exe, 12 kB, 12 kB, , ,
0x7ff770c545c0
0xfbc000, Image: Commit, 12 kB, RW, C:\Windows\SysWOW64\notepad.exe, 12 kB, 12 kB, , ,
0xfbd220
0xfbe000

There appears to be an issue that if AHK is x32 and Notepad is x64, that the function is failing, perhaps the address value is too big for AHK x32 to handle. If this cannot be resolved, one could use AHK x32 and a go-between script run using AHK x64.

Warning: use at your own risk, scripts that retrieve information from external programs can potentially crash them, save any files before testing.

Code: Select all

;e.g.
;q::
WinGet, hWnd, ID, ahk_class Notepad
WinGetTitle, vWinTitle, % "ahk_id " hWnd
WinGet, vPPath, ProcessPath, % "ahk_id " hWnd
FileGetVersion, vPVersion, % vPPath
vPath := JEE_NotepadGetPath(hWnd)
MsgBox, % "window title: " vWinTitle "`r`n" "file path: " vPath "`r`n" "Notepad path: " vPPath "`r`n" "Notepad version: " vPVersion
return

;JEE_NotepadGetFilename
JEE_NotepadGetPath(hWnd)
{
	WinGetClass, vWinClass, % "ahk_id " hWnd
	if !(vWinClass = "Notepad")
		return
	WinGet, vPID, PID, % "ahk_id " hWnd
	WinGet, vPPath, ProcessPath, % "ahk_id " hWnd
	FileGetVersion, vPVersion, % vPPath

	MAX_PATH := 260
	;PROCESS_QUERY_INFORMATION := 0x400 ;PROCESS_VM_READ := 0x10
	if !hProc := DllCall("kernel32\OpenProcess", UInt,0x410, Int,0, UInt,vPID, Ptr)
		return
	if (vPVersion = "5.1.2600.5512") ;Notepad (Windows XP version)
		vAddress := 0x100A900
	if !vAddress && A_Is64bitOS
	{
		DllCall("kernel32\IsWow64Process", Ptr,hProc, IntP,vIsWow64Process)
		vPIs64 := !vIsWow64Process
	}
	if (vPVersion = "10.0.14393.0") ;Notepad (Windows 10 version)
	{
		if vPIs64
			vAddress := 0x7FF770C545C0
		else
			vAddress := 0xFBD220 ;(0xFBE000 also appears to work)
	}

	if !vAddress
	{
		VarSetCapacity(MEMORY_BASIC_INFORMATION, A_PtrSize=8?48:28, 0)
		vAddress := 0
		Loop
		{
			if !DllCall("kernel32\VirtualQueryEx", Ptr,hProc, Ptr,vAddress, Ptr,&MEMORY_BASIC_INFORMATION, UPtr,A_PtrSize=8?48:28, UPtr)
				break

			vMbiBaseAddress := NumGet(MEMORY_BASIC_INFORMATION, 0, "Ptr")
			vMbiRegionSize := NumGet(MEMORY_BASIC_INFORMATION, A_PtrSize=8?24:12, "UPtr")
			vMbiState := NumGet(MEMORY_BASIC_INFORMATION, A_PtrSize=8?32:16, "UInt")
			vMbiType := NumGet(MEMORY_BASIC_INFORMATION, A_PtrSize=8?40:24, "UInt")

			vPath := ""
			VarSetCapacity(vPath, MAX_PATH*2)
			DllCall("psapi\GetMappedFileName", Ptr,hProc, Ptr,vMbiBaseAddress, Str,vPath, UInt,MAX_PATH*2, UInt)
			if !(vPath = "")
				SplitPath, vPath, vName
			if (vMbiState & 0x1000) ;MEM_COMMIT := 0x1000
			&& (vMbiType & 0x1000000) ;MEM_IMAGE := 0x1000000
			&& InStr(vName, "notepad")
			{
				;get address where path starts
				if vPIs64
					vAddress := vMbiBaseAddress + 0x10B40
				else
					vAddress := vMbiBaseAddress + 0xCAE0 ;(vMbiBaseAddress + 0xD378 also appears to work)
				break
			}
			vAddress += vMbiRegionSize
			if (vAddress > 2**32-1) ;4 gigabytes
				return
		}
	}
	VarSetCapacity(vPath, MAX_PATH*2, 0)
	DllCall("kernel32\ReadProcessMemory", Ptr,hProc, Ptr,vAddress, Str,vPath, UPtr,MAX_PATH*2, UPtr,0)
	DllCall("kernel32\CloseHandle", Ptr,hProc)
	return vPath
}
Btw in Process Hacker, on the Memory tab, there is a 'Strings...' button, for searching for strings.

On the Memory tab, one expects the strings to be in a region that has in the Use column either: 'C:\Windows\System32\notepad.exe' or 'C:\Windows\SysWOW64\notepad.exe'.

If you have any further problems with getting the path from Notepad, you could copy an older version from an earlier OS, I have the Windows XP version of Notepad working on my Windows 7 PC.

[EDIT:] Btw one trick to confirm that you have found the right location of something in the address space, is to edit the text in the address space, and then do a test. For example, I could open a file in Notepad, make it so that there are unsaved changes e.g. add a space, edit the path in the address space, click New or Close to get the file warning, and see if the path text has the change that I made, then restore the original text in the address space.

Re. Process Hacker, my best recommended tools are listed here:
best utilities + best AutoHotkey scripts (+ useful tips) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=28149

[EDIT:] A note on the fallback method. If there are unsaved changes and you click New or Close, you get a warning prompt. However, if there are unsaved changed, but the window is 'Untitled', i.e. has never been saved before, and the Edit control contains no text, then you get no warning prompt, the New or Close action proceeds without a prompt.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: notepad get/set path (get/set text file path)

25 Apr 2017, 21:37

Some uses:
- rename file prompt (together with JEE_NotepadSetPath to reopen the file once it has a new name)
- jump to previous/next file (together with JEE_NotepadSetPath)
- check if file is already open in Notepad
- run current file in AutoHotkey (v1.0/v.1.1/v2.0 A32/U32/U64)
- compare Edit control text with saved text/backup file (e.g. via WinMerge) (put old contents onto the clipboard)
- open containing folder
- get file details e.g. encoding/size/date
- open with another program
- add to Recent folder
- save but maintain date
- reopen file, 'refresh' (together with JEE_NotepadSetPath)
- do something with current file (e.g.: move file, send to recycle bin, cut/copy to clipboard for Explorer file paste)
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
XeroByte
Posts: 26
Joined: 17 Sep 2014, 01:30

Re: notepad get/set path (get/set text file path)

26 Apr 2017, 06:24

Unfortunately I'm still getting a blank string returned even with your new code :(
I've tried searching for those memory regions, but they don't seem to exist for me.

I looked at all the entries containing C:\Windows\System32\notepad.exe and the one entry that was C:\Windows\SysWOW64\notepad.exe, although I don't really know exactly what I'm looking for - I couldn't see any resemblance of the path to my open file in the hex view of any of those entries.

I've checked my notepad.exe and it is the same version number as yours (10.0.14393.0), I've tried using the 32bit notepad.exe (from my SysWOW64 folder) as well as the 64bit one (in the System32 folder). I can't get it to work for either one.

I am running 32bit Unicode AHK.

Any ideas?
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: notepad get/set path (get/set text file path)

26 Apr 2017, 14:03

I'm just pasting some revised code here, to test on the Windows 10 PC. It adds the safety check I mentioned.

One thing to try would be x64 AHK. Btw when you tick the boxes, on the Memory tab, 'Strings...', tick all of them (Unicode, Private, Image, Mapped), or just try Unicode and Image (in this example, I expect the strings to be an Image memory region).

Code: Select all

;==================================================

;e.g.
;q::
WinGet, hWnd, ID, ahk_class Notepad
WinGetTitle, vWinTitle, % "ahk_id " hWnd
WinGet, vPPath, ProcessPath, % "ahk_id " hWnd
FileGetVersion, vPVersion, % vPPath
vPath := JEE_NotepadGetPath(hWnd)
MsgBox, % "window title: " vWinTitle "`r`n" "file path: " vPath "`r`n" "Notepad path: " vPPath "`r`n" "Notepad version: " vPVersion
return

;JEE_NotepadGetFilename
JEE_NotepadGetPath(hWnd)
{
	WinGetClass, vWinClass, % "ahk_id " hWnd
	if !(vWinClass = "Notepad")
		return
	WinGet, vPID, PID, % "ahk_id " hWnd
	WinGet, vPPath, ProcessPath, % "ahk_id " hWnd
	FileGetVersion, vPVersion, % vPPath

	MAX_PATH := 260
	;PROCESS_QUERY_INFORMATION := 0x400 ;PROCESS_VM_READ := 0x10
	if !hProc := DllCall("kernel32\OpenProcess", UInt,0x410, Int,0, UInt,vPID, Ptr)
		return
	if !vAddress && A_Is64bitOS
	{
		DllCall("kernel32\IsWow64Process", Ptr,hProc, IntP,vIsWow64Process)
		vPIs64 := !vIsWow64Process
	}
	if (vPVersion = "5.1.2600.5512") && !vPIs64 ;Notepad (Windows XP version)
		vAddress := 0x100A900
	if (vPVersion = "10.0.14393.0") ;Notepad (Windows 10 version)
	{
		if vPIs64
			vAddress := 0x7FF770C545C0
		else
			vAddress := 0xFBD220 ;(0xFBE000 also appears to work)
	}

	if !vAddress
	{
		VarSetCapacity(MEMORY_BASIC_INFORMATION, A_PtrSize=8?48:28, 0)
		vAddress := 0
		Loop
		{
			if !DllCall("kernel32\VirtualQueryEx", Ptr,hProc, Ptr,vAddress, Ptr,&MEMORY_BASIC_INFORMATION, UPtr,A_PtrSize=8?48:28, UPtr)
				break

			vMbiBaseAddress := NumGet(MEMORY_BASIC_INFORMATION, 0, "Ptr")
			vMbiRegionSize := NumGet(MEMORY_BASIC_INFORMATION, A_PtrSize=8?24:12, "UPtr")
			vMbiState := NumGet(MEMORY_BASIC_INFORMATION, A_PtrSize=8?32:16, "UInt")
			vMbiType := NumGet(MEMORY_BASIC_INFORMATION, A_PtrSize=8?40:24, "UInt")

			vName := ""
			if (vMbiState & 0x1000) ;MEM_COMMIT := 0x1000
			&& (vMbiType & 0x1000000) ;MEM_IMAGE := 0x1000000
			{
				vPath := ""
				VarSetCapacity(vPath, MAX_PATH*2)
				DllCall("psapi\GetMappedFileName", Ptr,hProc, Ptr,vMbiBaseAddress, Str,vPath, UInt,MAX_PATH*2, UInt)
				if !(vPath = "")
					SplitPath, vPath, vName
			}
			if InStr(vName, "notepad")
			{
				;MsgBox, % Format("0x{:X}", vMbiBaseAddress)
				;get address where path starts
				if vPIs64
					vAddress := vMbiBaseAddress + 0x10B40
				else
					vAddress := vMbiBaseAddress + 0xCAE0 ;(vMbiBaseAddress + 0xD378 also appears to work)
				break
			}
			vAddress += vMbiRegionSize
			if (vAddress > 2**32-1) ;4 gigabytes
			{
				DllCall("kernel32\CloseHandle", Ptr,hProc)
				return
			}
		}
	}

	VarSetCapacity(MEMORY_BASIC_INFORMATION, A_PtrSize=8?48:28, 0)
	if DllCall("kernel32\VirtualQueryEx", Ptr,hProc, Ptr,vAddress, Ptr,&MEMORY_BASIC_INFORMATION, UPtr,A_PtrSize=8?48:28, UPtr)
	{
		vMbiBaseAddress := NumGet(MEMORY_BASIC_INFORMATION, 0, "Ptr")
		vPath := ""
		VarSetCapacity(vPath, MAX_PATH*2)
		DllCall("psapi\GetMappedFileName", Ptr,hProc, Ptr,vMbiBaseAddress, Str,vPath, UInt,MAX_PATH*2, UInt)
		if InStr(vPath, "notepad")
		{
			VarSetCapacity(vPath, MAX_PATH*2, 0)
			DllCall("kernel32\ReadProcessMemory", Ptr,hProc, Ptr,vAddress, Str,vPath, UPtr,MAX_PATH*2, UPtr,0)
		}
	}
	DllCall("kernel32\CloseHandle", Ptr,hProc)
	return vPath
}

;==================================================
[EDIT:] Try the code below (only tested on Notepad (Windows 10 version), works on AHK x32 to Notepad x32, and AHK x64 to Notepad x32/x64):

Code: Select all

;==================================================

;e.g.
;q::
WinGet, hWnd, ID, ahk_class Notepad
WinGetTitle, vWinTitle, % "ahk_id " hWnd
WinGet, vPPath, ProcessPath, % "ahk_id " hWnd
FileGetVersion, vPVersion, % vPPath
vPath := JEE_NotepadGetPath(hWnd)
MsgBox, % "window title: " vWinTitle "`r`n" "file path: " vPath "`r`n" "Notepad path: " vPPath "`r`n" "Notepad version: " vPVersion
return

;JEE_NotepadGetFilename
JEE_NotepadGetPath(hWnd)
{
	WinGetClass, vWinClass, % "ahk_id " hWnd
	if !(vWinClass = "Notepad")
		return
	WinGet, vPID, PID, % "ahk_id " hWnd
	WinGet, vPPath, ProcessPath, % "ahk_id " hWnd
	FileGetVersion, vPVersion, % vPPath

	MAX_PATH := 260
	;PROCESS_QUERY_INFORMATION := 0x400 ;PROCESS_VM_READ := 0x10
	if !hProc := DllCall("kernel32\OpenProcess", UInt,0x410, Int,0, UInt,vPID, Ptr)
		return
	if !vAddress && A_Is64bitOS
	{
		DllCall("kernel32\IsWow64Process", Ptr,hProc, IntP,vIsWow64Process)
		vPIs64 := !vIsWow64Process
	}
	if (vPVersion = "5.1.2600.5512") && !vPIs64 ;Notepad (Windows XP version)
		vAddress := 0x100A900
	if 0
	if (vPVersion = "10.0.14393.0") ;Notepad (Windows 10 version)
	{
		if vPIs64
			vAddress := 0x7FF770C545C0
		else
			vAddress := 0xFBD220 ;(0xFBE000 also appears to work)
	}

	if !vAddress
	{
		VarSetCapacity(MEMORY_BASIC_INFORMATION, A_PtrSize=8?48:28, 0)
		vAddress := 0
		Loop
		{
			if !DllCall("kernel32\VirtualQueryEx", Ptr,hProc, Ptr,vAddress, Ptr,&MEMORY_BASIC_INFORMATION, UPtr,A_PtrSize=8?48:28, UPtr)
				break

			vMbiBaseAddress := NumGet(MEMORY_BASIC_INFORMATION, 0, "Ptr")
			vMbiRegionSize := NumGet(MEMORY_BASIC_INFORMATION, A_PtrSize=8?24:12, "UPtr")
			vMbiState := NumGet(MEMORY_BASIC_INFORMATION, A_PtrSize=8?32:16, "UInt")
			;vMbiProtect := NumGet(MEMORY_BASIC_INFORMATION, A_PtrSize=8?36:20, "UInt")
			vMbiType := NumGet(MEMORY_BASIC_INFORMATION, A_PtrSize=8?40:24, "UInt")

			vName := ""
			if (vMbiState & 0x1000) ;MEM_COMMIT := 0x1000
			&& (vMbiType & 0x1000000) ;MEM_IMAGE := 0x1000000
			;&& (vMbiProtect = 0x4) ;PAGE_READWRITE := 0x4
			{
				vPath := ""
				VarSetCapacity(vPath, MAX_PATH*2)
				DllCall("psapi\GetMappedFileName", Ptr,hProc, Ptr,vMbiBaseAddress, Str,vPath, UInt,MAX_PATH*2, UInt)
				if !(vPath = "")
					SplitPath, vPath, vName
			}
			if InStr(vName, "notepad") && (vPVersion = "10.0.14393.0")
			{
				;get address where path starts
				if vPIs64
					;vAddress := vMbiBaseAddress + 0x25C0
					vAddress := vMbiBaseAddress + 0x245C0
				else
					vAddress := vMbiBaseAddress + 0x1D220 ;(0x1E000 also appears to work)
				;MsgBox, % Format("0x{:X}", vMbiBaseAddress) "`r`n" Format("0x{:X}", vAddress)
				break
			}
			if InStr(vName, "notepad")
			{
				;MsgBox, % Format("0x{:X}", vMbiBaseAddress)
				;get address where path starts
				if vPIs64
					vAddress := vMbiBaseAddress + 0x10B40
				else
					vAddress := vMbiBaseAddress + 0xCAE0 ;(vMbiBaseAddress + 0xD378 also appears to work)
				break
			}
			vAddress += vMbiRegionSize
			if 0
			if (vAddress > 2**32-1) ;4 gigabytes
			{
				DllCall("kernel32\CloseHandle", Ptr,hProc)
				return
			}
		}
	}

	VarSetCapacity(MEMORY_BASIC_INFORMATION, A_PtrSize=8?48:28, 0)
	if vAddress
	&& DllCall("kernel32\VirtualQueryEx", Ptr,hProc, Ptr,vAddress, Ptr,&MEMORY_BASIC_INFORMATION, UPtr,A_PtrSize=8?48:28, UPtr)
	{
		vMbiBaseAddress := NumGet(MEMORY_BASIC_INFORMATION, 0, "Ptr")
		vPath := ""
		VarSetCapacity(vPath, MAX_PATH*2)
		DllCall("psapi\GetMappedFileName", Ptr,hProc, Ptr,vMbiBaseAddress, Str,vPath, UInt,MAX_PATH*2, UInt)
		if InStr(vPath, "notepad")
		{
			VarSetCapacity(vPath2, MAX_PATH*2, 0)
			DllCall("kernel32\ReadProcessMemory", Ptr,hProc, Ptr,vAddress, Str,vPath2, UPtr,MAX_PATH*2, UPtr,0)
		}
	}
	DllCall("kernel32\CloseHandle", Ptr,hProc)
	return vPath2
}

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
XeroByte
Posts: 26
Joined: 17 Sep 2014, 01:30

Re: notepad get/set path (get/set text file path)

29 Apr 2017, 07:31

Thank you for all your attempts @jeeswg
Unfortunately still no joy with either of your most recent options - tried with both the 32bit and 64bit notepad.exe :(
The one thing I haven't tried is x64 ahk. If I would have to resort to that to make the script work then I'd actually just rather use your fallback method.

I did all that you said with the Memory tab, and I looked at the hex contents of all the strings that contained notepad.exe, but still don't see the path of the open file.

I still don't know what I can post up here to help identify where the issue is.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: notepad get/set path (get/set text file path)

29 Apr 2017, 12:42

Thanks for getting back to me. Btw what I've done so far has been no bother, because I intended to do all of it anyway, including the code in this post as well.

What this code does is allow you to use a function in AHK x32, that temporarily opens a script in AHK x64 that is used to get the path.

Possibly you have a UAC issue. Possibly you should try downloading the x64 version of Process Hacker, if you haven't already. I would recommend at least testing the function in AHK x64, if you haven't already.

Code: Select all

;==================================================

;use in main script
q::
WinGet, hWnd, ID, ahk_class Notepad
vPath := JEE_NotepadGetPathGoBetween(hWnd)
MsgBox, % "2 " vPath
return

JEE_NotepadGetPathGoBetween(hWnd, vPathAhk64="")
{
	if !FileExist(vPathAhk64)
		vPathAhk64 := "C:\Program Files\AutoHotkey\AutoHotkeyU64.exe"
	if !FileExist(vPathAhk64)
		return
	hWnd += 0
	vTarget = "%vPathAhk64%" "%A_ScriptDir%\notepad get path.ahk" "%hWnd%"
	Run, % vTarget,, Hide UseErrorLevel, vPID
	vWinCriteria = %hWnd%| ahk_class AutoHotkey ahk_pid %vPID%
	WinWait, % vWinCriteria,, 5
	if ErrorLevel
		return
	WinGet, hWnd2, ID, % vWinCriteria
	WinGetTitle, vWinTitle, % "ahk_id " hWnd2
	vPath := SubStr(vWinTitle, InStr(vWinTitle, "|")+1)
	PostMessage, 0x111, 65405,,, % "ahk_id " hWnd2 ;WM_COMMAND := 0x111 ;ID_FILE_EXIT := 65405
	return vPath
}

;==================================================

;contents of 'notepad get path.ahk'
#SingleInstance off
#Persistent
DetectHiddenWindows, On
hWnd = %1%
vPIDAhk := DllCall("kernel32\GetCurrentProcessId", UInt)
vPath := JEE_NotepadGetPath(hWnd)
MsgBox, % "1 " vPath
WinSetTitle, % "ahk_id " A_ScriptHwnd,, %hWnd%|%vPath%
return

;==================================================

If the function can be made to work in AHK x32 to handle long addresses, that would be good to know.

If anybody wants to comment about success/failure getting this script to work, on Windows 10, or on any Windows OS, please do.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
XeroByte
Posts: 26
Joined: 17 Sep 2014, 01:30

Re: notepad get/set path (get/set text file path)

01 May 2017, 05:50

I tried directly with AHK x64 and finally had some success!
AHK x64 worked to notepad x64 but not to notepad x32

I would love to see this working on AHK x32 with both x32 and x64 notepad (without needing the clever-but-messy AHK x64 gobetween).
XeroByte
Posts: 26
Joined: 17 Sep 2014, 01:30

Re: notepad get/set path (get/set text file path)

03 May 2017, 07:36

Just a little tip (not that you really need tips from a n00b like me):

Your gobetween function is pretty cool, but I believe that you could do this more efficiently and without using an extra .ahk file. Something like this:

Code: Select all

JEE_NotepadGetPathGoBetween(hWnd){
	hWnd += 0
	vPath := Run_AHK_x64("FileAppend % JEE_NotepadGetPath(""" hWnd """), *")
	return vPath
}

Run_AHK_x64(Script, vPathAhk64:=""){
	if !FileExist(vPathAhk64)
		vPathAhk64 := "C:\Program Files\AutoHotkey\AutoHotkeyU64.exe"
	if !FileExist(vPathAhk64)
		return
	shell := ComObjCreate("WScript.Shell")
	exec := shell.Exec("""" vPathAhk """ *")
	exec.StdIn.Write(script)
	exec.StdIn.Close()
	return exec.StdOut.ReadAll()
}
This assumes that the JEE functions are located in one of the lib files, otherwise this will need an #include in the Run_AHK_x64() function call.

Code above has been adapted from a larger script and has not been tested in this format.
GEV
Posts: 1002
Joined: 25 Feb 2014, 00:50

Re: notepad get/set path (get/set text file path)

06 May 2017, 13:09

This works on my system (Win10 x64):

Code: Select all

q::
Command_line := ""
; https://autohotkey.com/docs/commands/ComObjGet.htm
WinGet pid, pid, ahk_exe notepad.exe
; WinGet pid, pid, A 	; if notepad is active
wmi := ComObjGet("winmgmts:")
queryEnum := wmi.ExecQuery(""
	. "Select * from Win32_Process where ProcessId=" . pid)
	._NewEnum()
if queryEnum[process]
{
	file_path := process.CommandLine
	StringReplace, file_path, file_path, "%A_Space%,,
	StringReplace, file_path, file_path, ",, All
	StringReplace, file_path, file_path, C:\Windows\system32\notepad.exe,,
	StringReplace, file_path, file_path, C:\Windows\notepad.exe,,	
}
else
	MsgBox Process not found!
wmi := queryEnum := process := ""
MsgBox, file path:`n"%file_path%"
return
Last edited by GEV on 07 May 2017, 10:00, edited 1 time in total.
XeroByte
Posts: 26
Joined: 17 Sep 2014, 01:30

Re: notepad get/set path (get/set text file path)

07 May 2017, 07:03

Hey @GEV

Nice bit of code there, but unfortunately it doesn't quite achieve the goal here. What your script does is get the path of the file that notepad.exe was launched with (if any).
If a user opens notepad.exe and goes to 'File > Open' and chooses a file that way then your script will return blank, or if a user launched notepad.exe by opening text1.txt and then went to 'File > Open' and opened text2.txt then your script would still return the path for text1.txt - it won't update to text2.txt.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: notepad get/set path (get/set text file path)

07 May 2017, 10:10

@GEV
Nice code GEV, I've tried many things to 'get Notepad path' in the past. XeroByte has explained the potential problem with that approach, although it can tell you what the *first* path was.

@XeroByte
not that you really need tips from a n00b like me
Haha we're all noob in different ways. What I often find is that people have the skills to write a script I want, but for some reason no-one's written it yet, e.g. every script I share on this forum. Worse, sometimes people *mention* the problem, but no-one comes up with the seemingly simple solution.

I had considered changing my script to use an hDrop (I don't particularly like the WM_COPYDATA approach) to transfer data. I'd used a similar approach to yours for getting output from command line utilities, but hadn't thought to use it with AHK, and your approach does seem better (more direct) than an hDrop or WM_COPYDATA.

It's funny how any reluctance to using a go-between approach may have melted away by now, that's what necessity/benefits of usage does(!). I'm glad you've got it working. I might come back to the JEE_NotepadGetPath function at a future point, i.e. to improve multiple OS support. Do you have many uses for it (getting the Notepad path)?

I found similar go-between code to yours here, when I was investigating 'FileAppend *' which I hadn't used before:
Run / RunWait
https://autohotkey.com/docs/commands/Run.htm

And I have a working go-between example here:

Code: Select all

;q:: ;notepad go-between via WScript.Shell object
vPathExe = C:\Program Files\AutoHotkey\AutoHotkeyU64.exe
WinGet, hWnd, ID, ahk_class Notepad
vScript := "vPath := JEE_NotepadGetPath(" hWnd ")`r`n"
;vScript := "vPath := JEE_NotepadGetPath(""" hWnd """)`r`n" ;btw this could fail unless hWnd += 0 is added to JEE_NotepadGetPath which I might add in
vScript .= "DetectHiddenWindows, On`r`n" ;I might add this to JEE_NotepadGetPath
vScript .= "FileAppend, % vPath, *`r`n"
vScript .= "#Include, " A_Desktop "\MyLibContainingNotepadGetPath.ahk"
oShell := ComObjCreate("WScript.Shell")
oExec := oShell.Exec(vPathExe " /ErrorStdOut *")
MsgBox, % vScript
oExec.StdIn.Write(vScript)
oExec.StdIn.Close()
MsgBox, % oExec.StdOut.ReadAll()
oShell := oExec := ""
return
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
GEV
Posts: 1002
Joined: 25 Feb 2014, 00:50

Re: notepad get/set path (get/set text file path)

07 May 2017, 10:24

XeroByte wrote:If a user opens notepad.exe and goes to 'File > Open' and chooses a file that way then your script will return blank.
To this problem, I know of no solution. I rarely open notepad.exe and go to 'File > Open' and choose a file.
XeroByte wrote:if a user launched notepad.exe by opening text1.txt and then went to 'File > Open' and opened text2.txt then your script would still return the path for text1.txt - it won't update to text2.txt.
Added

Code: Select all

Command_line := ""
to the code.
XeroByte
Posts: 26
Joined: 17 Sep 2014, 01:30

Re: notepad get/set path (get/set text file path)

08 May 2017, 05:44

@GEV
GEV wrote:To this problem, I know of no solution. I rarely open notepad.exe and go to 'File > Open' and choose a file.
I also rarely open files like that - but I'm extremely hesitant to use a script that only works some of the time depending on how the user opened a file. Also (more importantly) I just realised a couple of other situations (that are probably more common - certainly for myself) where the script will fail to return an accurate result:
If somebody opens a blank notepad and then saves a new file - No path will be returned.
Or somebody opens an existing text file in notepad and then saves it to a new name. This also will not work - it will return the original file name and path.
GEV wrote:
XeroByte wrote:if a user launched notepad.exe by opening text1.txt and then went to 'File > Open' and opened text2.txt then your script would still return the path for text1.txt - it won't update to text2.txt.
Added

Code: Select all

Command_line := ""
to the code.
Unfortunately this won't help. The problem is that you are retrieving the command that launched the process. This doesn't change while the process is running, so if you launch notepad with file1.txt then the command that launched the process will still contain file1.txt even after opening another file from 'File > Open'.

@jeeswg
jeeswg wrote:It's funny how any reluctance to using a go-between approach may have melted away by now, that's what necessity/benefits of usage does(!). I'm glad you've got it working. I might come back to the JEE_NotepadGetPath function at a future point, i.e. to improve multiple OS support. Do you have many uses for it (getting the Notepad path)?
Indeed! You've already mentioned a bunch of excellent uses for it. I work a lot with plain text files and I often like to open files in many different editors depending on what I'm doing - so I've got a hotkey setup that grabs the file path of the open file and opens a dialog that allows me to easily choose another program to open that file in my choice of other editors (notepad++, scite, sublime etc.). With another hotkey I can save a quick backup of the open file with a timestamp. The great thing is that I can now make it work in all my editors even the standard notepad.exe. Thanks mate :)
jeeswg wrote:I found similar go-between code to yours here, when I was investigating 'FileAppend *' which I hadn't used before:
Run / RunWait
https://autohotkey.com/docs/commands/Run.htm
Where do you think I got it from ;) Sorry - I definitely should have credited it to the awesome documentation.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: Auntiejack56 and 112 guests