Jump to content

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

DeskIcons - Get/Set Desktop Icon Positions


  • Please log in to reply
80 replies to this topic
Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008
Function to save desktop icon positions and later restore them. Designed with DynamicDesktop in mind, but will work fine on its own.

Example:
; save positions
coords := DeskIcons()

MsgBox now move the icons around yourself

; load positions
DeskIcons(coords)


DeskIcons.ahk:
/*
	Save and Load desktop icon positions
	based on save/load desktop icon positions by temp01 (http://www.autohotkey.com/forum/viewtopic.php?t=49714)
	
	Example:
		; save positions
		coords := DeskIcons()
		MsgBox now move the icons around yourself
		; load positions
		DeskIcons(coords)
	
	Plans:
		handle more settings (icon sizes, sort order, etc)
			- http://msdn.microsoft.com/en-us/library/ff485961%28v=VS.85%29.aspx
	
*/
DeskIcons(coords="")
{
	Critical
	static MEM_COMMIT := 0x1000, PAGE_READWRITE := 0x04, MEM_RELEASE := 0x8000
	static LVM_GETITEMPOSITION := 0x00001010, LVM_SETITEMPOSITION := 0x0000100F, WM_SETREDRAW := 0x000B
	
	ControlGet, hwWindow, HWND,, SysListView321, ahk_class Progman
	if !hwWindow ; #D mode
		ControlGet, hwWindow, HWND,, SysListView321, A
	IfWinExist ahk_id %hwWindow% ; last-found window set
		WinGet, iProcessID, PID
	hProcess := DllCall("OpenProcess"	, "UInt",	0x438			; PROCESS-OPERATION|READ|WRITE|QUERY_INFORMATION
										, "Int",	FALSE			; inherit = false
										, "UInt",	iProcessID)
	if hwWindow and hProcess
	{	
		ControlGet, list, list,Col1			
		if !coords
		{
			VarSetCapacity(iCoord, 8)
			pItemCoord := DllCall("VirtualAllocEx", "UInt", hProcess, "UInt", 0, "UInt", 8, "UInt", MEM_COMMIT, "UInt", PAGE_READWRITE)
			Loop, Parse, list, `n
			{
				SendMessage, %LVM_GETITEMPOSITION%, % A_Index-1, %pItemCoord%
				DllCall("ReadProcessMemory", "UInt", hProcess, "UInt", pItemCoord, "UInt", &iCoord, "UInt", 8, "UIntP", cbReadWritten)
				ret .= A_LoopField ":" (NumGet(iCoord) & 0xFFFF) | ((Numget(iCoord, 4) & 0xFFFF) << 16) "`n"
			}
			DllCall("VirtualFreeEx", "UInt", hProcess, "UInt", pItemCoord, "UInt", 0, "UInt", MEM_RELEASE)
		}
		else
		{
			SendMessage, %WM_SETREDRAW%,0,0
			Loop, Parse, list, `n
				If RegExMatch(coords,"\Q" A_LoopField "\E:\K.*",iCoord_new)
					SendMessage, %LVM_SETITEMPOSITION%, % A_Index-1, %iCoord_new%
			SendMessage, %WM_SETREDRAW%,1,0
			ret := true
		}
	}
	DllCall("CloseHandle", "UInt", hProcess)
	return ret
}


r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007
Hum, just tried your script under my Win7 x64, and it doesn't work at all. The Windows Explorer crashes when the script tries to save the icon positions, and when it restores them.

I've found that it's the following line that crashes explorer:
ControlGet, list, list,Col1

If I replace it with:
ControlGet, list, list,Col1, SysListView321, ahk_class WorkerW
then it doesn't crash any more when I use the regular AutoHotkey exe to execute it. It continues to crash with AutoHotkey_L.

Furthermore, even when the script doesn't crash, the icons positions are not restored correctly. I don't understand what happens, but sometimes, all the icons are stacked at the right side of the desktop (in their default alphabetical order), but most of the times, they don't move at all. And one time (only!) it has correctly moved the only icon I have moved manually at its right original position.

Note that I have inserted a msgbox in the loop that gets the icon positions, to show the content of A_LoopField, and it is displayed the correct number of times (the number of icons on my desktop), but A_LoopField is always empty. Is it supposed to hold the icon name?
         Loop, Parse, list, `n
         {
            [color=red]msgbox %A_LoopField%[/color]
            SendMessage, %LVM_GETITEMPOSITION%, % A_Index-1, %pItemCoord%
            DllCall("ReadProcessMemory", "UInt", hProcess, "UInt", pItemCoord, "UInt", &iCoord, "UInt", 8, "UIntP", cbReadWritten)
            ret .= A_LoopField ":" (NumGet(iCoord) & 0xFFFF) | ((Numget(iCoord, 4) & 0xFFFF) << 16) "`n"
         }

I guess the problem is caused by some x64 compatibility issues. But I really don't know what to do to fix them. Pity. :-(
r0lZ

Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008
What's the value of coords returned from DeskIcons() ?

Have you tried running the script as administrator? That's the only reason I can think for ControlGet to cause a crash like that.

I don't have an x64 system to test with (yet), so hopefully it is something simple. To help with debugging, you might want to try my Debugging Library (dout). Or at least OutputDebug directly to avoid having to live through a bunch of MsgBoxs.

r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007

What's the value of coords returned from DeskIcons() ?

An array of ":0`n" (one for each icon). Obviously, the icon name is missing, and the coords are wrong.
Same thing when Windows doesn't crash (with the standard version of AHK and my modified script).

Have you tried running the script as administrator? That's the only reason I can think for ControlGet to cause a crash like that.

No, but I did it just right now. I had to compile my modified script (with the bin of the standard AHK), but unfortunately, Explorer crashes also! (The same script doesn't crash when launched uncompiled. Why?!?)

I don't have an x64 system to test with (yet), so hopefully it is something simple. To help with debugging, you might want to try my Debugging Library (dout). Or at least OutputDebug directly to avoid having to live through a bunch of MsgBoxs.

Thanks. I didn't know your debug lib. Will try it. But I suppose that it will not output something else than what is displayed by the current MsgBoxes.
r0lZ

Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008
Find your AutoHotkey.exe and in properties->compatibility you can set it to always run as admin. And thus any scripts will automatically run as admin (no need to compile).

If the problem is with ControlGet, then try this smaller script for debugging:
ControlGet, hwWindow, HWND,, SysListView321, ahk_class Progman
if !hwWindow ; #D mode
	ControlGet, hwWindow, HWND,, SysListView321, A
IfWinExist ahk_id %hwWindow% ; last-found window set
	ControlGet, list, list,Col1
MsgBox %list%

My debug library also has dout_v(), dout_f(), and dout_o() which make printing variables/function calls/objects easier (less typing more debugging). But for this small script, the only real benefits are:[*:3p9abcqh]info printed silently, as in not stopping the script execution with a MsgBox for every message.
[*:3p9abcqh]You could easily copy all the debug info from a single run and paste it back in the forums or keep it for later reference.

r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007
Your test script gives exactly the same result:

Standard AHK, run as normal user: Explorer crashes, Output is blank (but it has the correct number of blank lines).

AHK_Lw, run as normal user: idem

Standard AHK, run as admin: idem

AHK_Lw, run as admin: idem

Modified script:
ControlGet, hwWindow, HWND,, SysListView321, ahk_class Progman
if !hwWindow ; #D mode
   ControlGet, hwWindow, HWND,, SysListView321, A
IfWinExist ahk_id %hwWindow% ; last-found window set
   ControlGet, list, list,Col1[color=red], SysListView321, ahk_class WorkerW[/color]
MsgBox %list%

Standard AHK, run as normal user: Explorer doesn't crash, but the output is blank, and there is only one (blank) line.

AHK_Lw, run as normal user: idem

Standard AHK, run as administrator: idem

AHK_Lw, run as administrator: idem

As you can see, no matter which version of AHK I use and how I launch it, the result is always identical.

Not sure why the full DeskIcons script crashes explorer anyway (even with my modified version) when it is run with AHK_Lw. Must be another function.
r0lZ

r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007
I did another test with a very simple script.

I opened C:\Windows in Explorer, and got the title and classNN of the main listbox with AU3_Spy. Then, I ran this:
ControlGet, list, list, Col1, DirectUIHWND3, C:\Windows
MsgBox %list%
Again, the output is blank, and there is only one blank line. Explorer did not crash.

Obviously, ControlGet doesn't work with listviews under Win7 x64. :-(
r0lZ

Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008
Well, at least I can explain that last part! The DirectUIHWND wrapper thing makes it so you can't access the listview with ControlGet. It has annoyed me ever since Vista came out, as this broke my favorite scripts. Thankfully, I've since found the solution and finally gotten around to releasing it as a simple library (Get paths of selected items in an explorer window). Before that, I was using StExBar as a workaround.

I'll have to think more about the other stuff...

Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008
Hey, does my Get paths of selected items in an explorer window work for you in a normal explorer window at least?

EDIT: and there is this topic: <!-- m -->http://www.autohotke...pic.php?t=60575<!-- m -->

Relevant I think, but I don't have time to look at it right now.

r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007

Hey, does my Get paths of selected items in an explorer window work for you in a normal explorer window at least?


Error at line 61.

Line text: for window in ComObjectCreate("Shell.Application").Windows
Error: This line does not contain a recognized action.
I am supposed to install something particular? COM.ahk? Where is it?

BTW, I'm still using AKH_Lw 1.0.48.05.L51. Is it OK?
r0lZ

Rapte_Of_Suzaku
  • Members
  • 901 posts
  • Last active: Jul 08 2011 02:12 PM
  • Joined: 29 Feb 2008
You need a newer version. COM was integrated into AHKL in revision 53. If you update, you might want to try the previous tests again quickly... The problem may have already been fixed.

r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007
Thanks, and sorry. I should have tried the latest version first.

I'll do it tomorrow. It's late here, in Belgium...
r0lZ

r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007
OK, your "Get paths of selected items in an explorer window" script works well with the unicode x64 version of AHK_L. :-)

It doesn't work with the unicode and ansi x32 versions: Explorer crashes, and the icons names are wrong: only the path is reported (ie, for the desktop, all icons are reported as "C:\Users\me\Desktop\"), and the selected icons report ERROR (normal, since Explorer has crashed, and when it is restarted, no icons are selected any more).

I will now check your DeskIcons library, but I need to reboot because my desktop is ill (too many crashes), and I don't want to lose what I have typed so far...
r0lZ

r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007
OK, checked your DeskIcons lib with AHK_L x64. Explorer doesn't crash any more :-), but the script doesn't work. Luckily, it is easy to fix it.
   ControlGet, hwWindow, HWND,, SysListView321, ahk_class Progman
   if !hwWindow ; #D mode
      ControlGet, hwWindow, HWND,, SysListView321, [color=red]ahk_class WorkerW[/color] ; Why 'A' doesn't work?
...
         VarSetCapacity(iCoord, [color=red]16[/color])
...
            DllCall("ReadProcessMemory", "UInt",hProcess, "UInt",pItemCoord, "[color=red]UInt64[/color]",&iCoord, "UInt",[color=red]16[/color], "UIntP",cbReadWritten)
Honestly, I'm not sure my fix is correct, but it works.

It might be a good idea to check if we're running the x64 version, and adapt the code accordingly. Do you know how I can check if the currently running version is AHK_L x64?
r0lZ

r0lZ
  • Members
  • 192 posts
  • Last active: Feb 11 2012 11:19 PM
  • Joined: 21 Apr 2007
Found a little problem with your lib.

If the folder option "Hide extensions for known file types" is enabled (this is the default), then it is possible to have several icons with the same name. For example, you can have a folder named "test", a text file named "test.txt" and an AHK script named "test.ahk". Since the extensions are hidden, the 3 files have the same "label". Your lib saves the label without the extension. Therefore, when the icons are restored, the first item matching the label is used, and all icons with the same name are restored at the same position. Luckily, Windows is smart enough to move the conflicting icons around so that they are not at the same location, but the positions are not what they should be.

Not sure if it is possible to retrieve the extension when "Hide extensions for known file types" is enabled, or something else to uniquely identify the right icons, but IMO it is important to find a workaround for this problem. (I have supposed that implementing some parts of your Get paths of selected items in an explorer window script should be sufficient to retrieve the extension, but unfortunately, it returns also the paths without the extensions.)

BTW, I have usually the "Hide extensions for known file types" option disabled, and in that case, there is no problem. But I wanted to check that special case, as I know that that was a problem that the author of Desktop Icons Save and Restore has faced. I don't know what he did to fix it.
r0lZ