[x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

Post your working scripts, libraries and tools
Guest_3102018a

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

10 Mar 2018, 15:26

I can't say. All I know is that I experience the same issue as tempuser. Sometimes the script works, sometimes it doesn't. Is it possible that both our issues stem from "OpenProcess" not working? Maybe, but it seems from the warning that iProcessID is blank, which results in OpenProcess not working. Having placed a Msgbox in the code, sometimes hwWindow is blank, which would make "WinGet, iProcessID, PID" blank and so on down the line. My humble opinion is that one of the "ControlGet, hwWindow" commands is failing, which leads the other functions to fail.
User avatar
Flipeador
Posts: 1046
Joined: 15 Nov 2014, 21:31
GitHub: Flipeador
Location: Argentina
Contact:

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

10 Mar 2018, 22:38

It works well on my Windows 10. :)

I have made a version for AHK v2:
Previous code

Code: Select all

/*
    [x64 & x32] DeskIcons - Get/Set Desktop Icon Positions

    Updated by Flipeador for AHKv2
    CREDITS : https://autohotkey.com/boards/viewtopic.php?f=6&t=3529

    Returns an object whose keys correspond to the name of each element on the desktop, and each value is an object with the keys (x, y).
    Reference: { ItemName: { X:0 , Y:0 } }
    If the object is empty, it means that no items have been found on the desktop.

    Other possible return values:
        -1 = The GetDeskListView function has failed.
         0 = Success (when the positions are set).
         1 = The process could not be opened (OpenProcess).
         2 = Can not reserve a region of memory within the virtual address space of the process (VirtualAllocEx).
         3 = WriteProcessMemory Error.
         4 = ReadProcessMemory Error.
         5 = LVM_GETITEMPOSITION Error.
         6 = LVM_GETITEMTEXTW Error.
*/
DeskIcons(Data := 0)
{
    static PROCESS_VM_READ     := 0x0010, PROCESS_VM_OPERATION  := 0x0008, PROCESS_VM_WRITE    := 0x0020
         , MEM_COMMIT          := 0x1000, PAGE_READWRITE        := 0x0004 ;, MEM_RELEASE         := 0x8000
         , LVM_GETITEMCOUNT    := 0x1004, LVM_GETITEMPOSITION   := 0x1010, LVM_SETITEMPOSITION := 0x100F, LVM_GETITEMTEXTW := 0x1073

    local WindowId := GetDeskListView()
    If ( !WindowId )
        return -1

    ; Gets the number of items on the desktop.
    ; https://docs.microsoft.com/es-es/windows/desktop/Controls/lvm-getitemcount
    local ItemCount := DllCall("User32.dll\SendMessageW", "UPtr", WindowId, "UInt", LVM_GETITEMCOUNT, "Ptr", 0, "Ptr", 0, "Ptr")
    if ( !ItemCount )
        return { }  ; If there are no elements, return an empty object.

    ; Retrieves the process identifier from the SysListView32 control.
    local ProcessId := WinGetPID("ahk_id" . WindowId)
    ; Opens the process to perform read and write operations in your memory.
    ; https://docs.microsoft.com/es-es/windows/desktop/api/processthreadsapi/nf-processthreadsapi-openprocess
    local hProcess  := DllCall("Kernel32.dll\OpenProcess", "UInt", PROCESS_VM_READ|PROCESS_VM_OPERATION|PROCESS_VM_WRITE, "Int", FALSE, "UInt", ProcessId, "UPtr")
    if ( !hProcess )
        return 1

    ; We reserve a region of memory within the virtual address space of the process.
    ; The amount of memory to reserve equals the size of the LVITEM structure.
    ; https://docs.microsoft.com/en-us/windows/desktop/api/commctrl/ns-commctrl-taglvitema
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa366890(v=vs.85).aspx
    local pLVITEM := 0, pBuff := 0, Size := 520
    if ( !(pLVITEM := DllCall("Kernel32.dll\VirtualAllocEx", "UPtr", hProcess, "UPtr", 0, "UPtr", A_PtrSize==4?72:88, "UInt", MEM_COMMIT, "UInt", PAGE_READWRITE, "UPtr"))
    ; Reserve another region of memory to store the text of a single element.
      || !(pBuff := DllCall("Kernel32.dll\VirtualAllocEx", "UPtr", hProcess, "UPtr", 0, "UPtr", Size, "UInt", MEM_COMMIT, "UInt", PAGE_READWRITE, "UPtr")) )
        return Error(2)
    
    ; We write certain data in the memory reserved for the LVITEM structure.
    ; https://msdn.microsoft.com/es-es/library/windows/desktop/ms681674(v=vs.85).aspx
    ; "pLVITEM+16+A_PtrSize" = pszText (pointer to a null-terminated string containing the item text).
    if ( !DllCall("Kernel32.dll\WriteProcessMemory", "UPtr", hProcess, "UPtr", pLVITEM+16+A_PtrSize, "UPtrP", pBuff, "UPtr", A_PtrSize, "Ptr", 0)
    ; "pLVITEM+16+A_PtrSize*2" = cchTextMax (Number of TCHARs in the buffer pointed to by pszText, including the terminating NULL).
      || !DllCall("Kernel32.dll\WriteProcessMemory", "UPtr", hProcess, "UPtr", pLVITEM+16+A_PtrSize*2, "IntP", Size//2, "UPtr", 4, "Ptr", 0) )
        return Error(3)

    ; Buffer stores the text of the element.
    local Buffer := ""
    VarSetCapacity(Buffer, Size)

    ; ==============================================================================================================
    ; Save
    ; ==============================================================================================================
    if ( !IsObject(Data) )
    {
        ; POINT stores a POINT structure.
        ; https://docs.microsoft.com/en-us/previous-versions/dd162805(v%3Dvs.85)
        local POINT := ""
        VarSetCapacity(POINT, 8)

        Data := {}
        loop ( ItemCount )
        {
            ; Retrieves the position of the item "A_Index-1".
            ; https://docs.microsoft.com/es-es/windows/desktop/Controls/lvm-getitemposition
            if ( !DllCall("User32.dll\SendMessageW", "UPtr", WindowId, "UInt", LVM_GETITEMPOSITION, "Int", A_Index-1, "UPtr", pLVITEM) )
                return Error(5)

            ; https://msdn.microsoft.com/es-es/library/windows/desktop/ms680553(v=vs.85).aspx
            if ( !DllCall("Kernel32.dll\ReadProcessMemory", "UPtr", hProcess, "UPtr", pLVITEM, "UPtr", &POINT, "UPtr", 8, "Ptr", 0) )
                return Error(4)

            ; Retrieves the text of the item "A_Index-1".
            ; https://docs.microsoft.com/es-es/windows/desktop/Controls/lvm-getitemtext
            if ( !DllCall("User32.dll\SendMessageW", "UPtr", WindowId, "UInt", LVM_GETITEMTEXTW, "Int", A_Index-1, "UPtr", pLVITEM) )
                return Error(6)

            if ( !DllCall("Kernel32.dll\ReadProcessMemory", "UPtr", hProcess, "UPtr", pBuff, "UPtr", &Buffer, "UPtr", 520, "Ptr", 0) )
                return Error(4)
            
            ObjRawSet(Data, StrGet(&Buffer,"UTF-16"), {x:NumGet(&POINT, "Int"),y:Numget(&POINT+4, "Int")} )
        }

        return Error(Data)    ; OK! devolvemos el objeto "Data", cuyas claves contienen el texto de cada elemento (icono) y su valor la posición de este
    }

    ; ==============================================================================================================
    ; Restore
    ; ==============================================================================================================
    local ItemText
    Loop (ItemCount)
    {
        ; Retrieves the text of the element "A_Index-1" and stores it in pBuff.
        ; https://docs.microsoft.com/es-es/windows/desktop/Controls/lvm-getitemtext
        if ( !DllCall("User32.dll\SendMessageW", "UPtr", WindowId, "UInt", LVM_GETITEMTEXTW, "Int", A_Index-1, "UPtr", pLVITEM) )
            return Error(6)

        ; Reads the text that was stored in the memory region of the process.
        if ( !DllCall("Kernel32.dll\ReadProcessMemory", "UPtr", hProcess, "UPtr", pBuff, "UPtr", &Buffer, "UPtr", Size, "Ptr", 0) )
            return Error(4)

        ; StrGet retrieves the text stored in Buffer.
        ; Then it determines if the item is in the object passed as a parameter to the function.
        ; If the item does not exist in the object, we do not touch it.
        if ( Data.HasKey(ItemText := StrGet(&Buffer, "UTF-16")) )
        {
            ; We check that the values ​​are correct (for debug).
            if ( !(Data[ItemText].x . Data[ItemText].y is "Integer") )
                MsgBox(A_ThisFunc . " Incorrect data type."), ExitApp(Error(0))

            ; Sets the position of item "A_Index-1".
            ; The LOWORD specifies the new x-position of the item's upper-left corner, in view coordinates.
            ; The HIWORD specifies the new y-position of the item's upper-left corner, in view coordinates.
            ; https://docs.microsoft.com/es-es/windows/desktop/Controls/lvm-setitemposition
            DllCall("User32.dll\SendMessageW", "Ptr", WindowId, "UInt", LVM_SETITEMPOSITION, "Int", A_Index-1, "UPtr", Data[ItemText].x&0xFFFF|(Data[ItemText].y&0xFFFF)<< 16)
        }
    }
    return Error(0)

    ; ==============================================================================================================
    ; Nested Functions
    ; ==============================================================================================================
    Error(code)
    {
        ; MEM_RELEASE := 0x8000
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa366894(v=vs.85).aspx
        if ( pLVITEM )
            DllCall("Kernel32.dll\VirtualFreeEx", "UPtr", hProcess, "UPtr", pLVITEM, "Ptr", 0, "UInt", 0x8000)
        if ( pBuff )
            DllCall("Kernel32.dll\VirtualFreeEx", "UPtr", hProcess, "UPtr", pBuff, "Ptr", 0, "UInt", 0x8000)
        DllCall("Kernel32.dll\CloseHandle", "UPtr", hProcess)
        return code
    }
}





/*
    Retrieves the handle of the SysListView32 control where the icons are displayed on the desktop.
    Returns zero if the handle could not be retrieved.
*/
GetDeskListView()
{
    local hListView := ControlGetHwnd("SysListView321", "ahk_class Progman")
    if ( hListView )
        return hListView

    if ( hListView := DllCall("User32.dll\FindWindowEx", "UPtr", DllCall("User32.dll\FindWindowEx", "UPtr", DllCall("User32.dll\GetShellWindow","UPtr")
                                                                                                  , "UPtr", 0
                                                                                                  , "Str", "SHELLDLL_DefView"
                                                                                                  , "UPtr", 0
                                                                                                  , "UPtr")
                                                       , "UPtr", 0
                                                       , "Str", "SysListView32"
                                                       , "UPtr", 0
                                                       , "UPtr") )
        return hListView

    local i, hWnd
    for i, hWnd in WinGetList("ahk_class WorkerW")    ; WIN_8 && WIN_10
        if ( hWnd := DllCall("User32.dll\FindWindowEx", "Ptr", hWnd
                                                      , "Ptr", 0
                                                      , "Str", "SHELLDLL_DefView"
                                                      , "Ptr", 0
                                                      , "Ptr") )
            return DllCall("User32.dll\FindWindowEx", "Ptr", hWnd, "Ptr", 0, "Str", "SysListView32", "Ptr", 0, "Ptr")

    return 0  ; error
}





/*
    Sets the position of a single element by its name.
    Returns -2 if the item does not exist. Other possible values ​​include those of the DeskIcons function.
*/
SetDeskIconPos(Name, X, Y)
{
    local DeskIcons := DeskIcons()
    if ( !IsObject(DeskIcons) )
        return DeskIcons

    if ( !DeskIcons.HasKey(Name) )
        return -2

    ObjRawSet( DeskIcons, Name, {x:x,y:y} )
    return DeskIcons(DeskIcons)
}





/*
    Retrieves the position of a single element by its name.
    Returns an object if it succeeded. Other possible values ​​include those of the SetDeskIconPos function.
*/
GetDeskIconPos(Name)
{
    local DeskIcons := DeskIcons()
    if ( !IsObject(DeskIcons) )
        return DeskIcons

    if ( !DeskIcons.HasKey(Name) )
        return -2

    return { x: DeskIcons[Name].x
           , y: DeskIcons[Name].y }
}

Example #1:

Code: Select all

#Warn                    ; Enables all supported warning types
#SingleInstance Force

MsgBox "A_IsAdmin: " . A_IsAdmin

; Get Desktop Icon Positions.
Data := DeskIcons()
if ( !IsObject(Data) )  ; check for error
{
    MsgBox "Error #" . Data  ; shows the error code
    ExitApp
}

; Show Desktop Icon Positions.
Icons := ""
For ItemText, POINT in Data
    Icons .= ItemText . " (" . POINT.x . ";" . POINT.y . ")`n"
MsgBox "DeskIcons()`n------------------------------`n" . Icons
     . "`n------------------------------`nTry moving an icon for the test."

; Restore Desktop Icon Positions.
error_code := DeskIcons(Data)
if ( error_code )  ; check for error
    MsgBox "(set) Error #" . error_code  ; shows the error code

ExitApp
Example #2: https://www.autohotkey.com/boards/viewt ... 55#p258755.


Updated: 01/18/2019 (mdy)
joedf wrote:Thanks for post this Flipeador.
No problem, thanks to you for sharing the code ;)
Windws 1♂ Pro 64-Bits I make scripts for AHKv2 (my v2 compiler) & WIN_7+ Spanish Argentina
(If any of my code written for v2 has stopped working, send me a private message. I appreciate that you correct my English.)
User avatar
joedf
Posts: 6875
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada, Quebec
Contact:

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

12 Mar 2018, 08:29

Thanks for post this Flipeador.
I will link to it in the first post along with some kind of fix when I find the time :b
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500 @ 3.00 GHz, 16GB DDR4 3200 MHz, NVIDIA GTX 1060 6GB | [About Me] | [ASPDM - StdLib Distribution]
[Populate the AHK MiniCity!] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library] | [About the AHK Foundation]
ahbi
Posts: 16
Joined: 15 Jun 2016, 17:11

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

06 Apr 2018, 23:07

DeskIcons() is nice as far as it goes, but I am at a loss as to how to take the next step, setting arbitrary icon positions.

If I dump out coords I get

Code: Select all

This PC:47318090
Recycle Bin:53150794
Handbrake:64816202
That 64816202 number (for Handbrake) must be a coordinate or related to one.

And by adding the following to DeskIcons()

Code: Select all

DllCall("ReadProcessMemory", "ptr", hProcess, "ptr", pItemCoord, "UInt", &iCoord, "UInt", 8, "UIntP", cbReadWritten)
            THE_ITEMNAME := A_LoopField
            THE_X_COORD := NumGet(iCoord,"Int")
            THE_Y_COORD := Numget(iCoord, 4,"Int")
ret .= A_LoopField ":" (NumGet(iCoord,"Int") & 0xFFFF) | ((Numget(iCoord, 4,"Int") & 0xFFFF) << 16) "`n"
I can see that Handbrake (via iCoord) is at X: 1098 & Y: 989, which are Relative positions. Checking with WinSpy that agrees.

But I am not sure how I arbitrarily set a coordinate for the icon.

If I want to move the Handbrake icon from its original position to ... say .. X: 2000 & Y: 1500, how would I go about that?

What is that packing and bit shifting that occurs with the ret variable?

Thanks
User avatar
Flipeador
Posts: 1046
Joined: 15 Nov 2014, 21:31
GitHub: Flipeador
Location: Argentina
Contact:

MAKEWORD | MAKELONG | MAKELONG64 | LOWORD | HIWORD | LOBYTE | HIBYTE | LOLONG | HILONG

07 Apr 2018, 08:28

ahbi wrote:If I want to move the Handbrake icon from its original position to ... say .. X: 2000 & Y: 1500, how would I go about that?
Do you want to set the position of a specific icon?; We need another function for this (Not necessarily, but it would be fine). *I have added SetDeskIconPos to my previous Script.
ahbi wrote:What is that packing and bit shifting that occurs with the ret variable?
See the lParam parameter of the LVM_SETITEMPOSITION message. You need to package the coordinates (x;y) in a 32-bit integer. See MAKELONG macro | Packaging values in DWORD.

Code: Select all

/*
    #define MAKEWORD(a, b)      ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))
    n := MAKEWORD(0, 255), MsgBox("WORD " . n . "; LOBYTE " . LOBYTE(n) . "; HIBYTE " . HIBYTE(n))
*/
MAKEWORD(byte_low, byte_high := 0)
{
    Return (byte_low & 0xFF) | ((byte_high & 0xFF) << 8)
} ; https://msdn.microsoft.com/es-es/library/windows/desktop/ms632663(v=vs.85).aspx

/*
    #define MAKELONG(a, b)      ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16))
    n := MAKELONG(0, 65535), MsgBox("LONG " . n . "; LOWORD " . LOWORD(n) . "; HIWORD " . HIWORD(n))
*/
MAKELONG(short_low, short_high := 0)
{
    Return (short_low & 0xFFFF) | ((short_high & 0xFFFF) << 16)
} ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms632660(v=vs.85).aspx

/*
    #define MAKELONG64(hi, lo)    ((LONGLONG(DWORD(hi) & 0xffffffff) << 32 ) | LONGLONG(DWORD(lo) & 0xffffffff))
    n := MAKELONG64(0, 4294967295), MsgBox("LONG64 " . n . "; LOLONG " . LOLONG(n) . "; HILONG " . HILONG(n))
*/
MAKELONG64(long_low, long_high := 0)
{
    Return (long_low & 0xFFFFFFFF) | ((long_high & 0xFFFFFFFF) << 32)  
}

LOWORD(l)
{
    Return l & 0xFFFF
} ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms632659(v=vs.85).aspx

HIWORD(l)
{
    Return (l >> 16) & 0xFFFF
} ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms632657(v=vs.85).aspx

LOBYTE(w)
{
    Return w & 0xFF
} ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms632658(v=vs.85).aspx

HIBYTE(w)
{
    Return (w >> 8) & 0xFF
} ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms632656(v=vs.85).aspx

LOLONG(ll)
{
    Return ll & 0xFFFFFFFF
}

HILONG(ll)
{
    Return (ll >> 32) & 0xFFFFFFFF
}
Windws 1♂ Pro 64-Bits I make scripts for AHKv2 (my v2 compiler) & WIN_7+ Spanish Argentina
(If any of my code written for v2 has stopped working, send me a private message. I appreciate that you correct my English.)
iPhilip
Posts: 350
Joined: 02 Oct 2013, 12:21

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

09 Jul 2018, 18:27

Hi Folks,

Can this script be adapted to get the dimensions of the icons as well, perhaps through LVM_GETSUBITEMRECT? Ultimately, I am trying to find a way to get the name of the icon under the mouse.

Thank you,

iPhilip
Windows 7 Pro (64 bit) - AutoHotkey v1.1+ (Unicode 32-bit)
User avatar
jeeswg
Posts: 5956
Joined: 19 Dec 2016, 01:58
Location: UK

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

09 Jul 2018, 22:01

- Here's a way to get the name of the file under the cursor via Acc:
Explorer: get name of file under cursor - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=51788

- Some information re. view modes and hence icon sizes:
Reading Win 10 File Explorer View Mode - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=28304
- There are techniques here to get a window object for the Desktop/Explorer windows.
Explorer window interaction (folder windows/Desktop, file/folder enumeration/selection/navigation/creation) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=35041
Last edited by jeeswg on 10 Jul 2018, 00:30, edited 2 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
iPhilip
Posts: 350
Joined: 02 Oct 2013, 12:21

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

10 Jul 2018, 00:11

Thank you jeeswg,

I appreciate your resourcefulness. I look forward to trying that in the morning.

iPhilip
Windows 7 Pro (64 bit) - AutoHotkey v1.1+ (Unicode 32-bit)
vanipede
Posts: 5
Joined: 10 Jan 2019, 17:16

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

10 Jan 2019, 17:22

Does anyone know of a simple way to keep the icon location data persistent between reboots. Every time my desktop gets redirected and I log in the icons are changed, so it would be nice to be able to save the config between boots/exiting the program.
User avatar
joedf
Posts: 6875
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada, Quebec
Contact:

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

13 Jan 2019, 21:02

Yeah I've had that issue before, I had made a script to save the icon positions to a file, and I would simply load it and restore the positions from the file with this function.
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500 @ 3.00 GHz, 16GB DDR4 3200 MHz, NVIDIA GTX 1060 6GB | [About Me] | [ASPDM - StdLib Distribution]
[Populate the AHK MiniCity!] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library] | [About the AHK Foundation]
Walli
Posts: 4
Joined: 14 Jan 2019, 06:19
Location: Germany, Hannover

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

14 Jan 2019, 06:32

Is there any solution for the "OpenProcess" issue?

Edit:
For one machine i got it, i changed following

Code: Select all

	ControlGet, hwWindow, HWND,, SysListView321, ahk_class Progman
	if !hwWindow ; #D mode
		ControlGet, hwWindow, HWND,, SysListView321, A
to:

Code: Select all

	ControlGet, hwWindow, HWND,, SysListView321, ahk_class Progman
	if !hwWindow ; #D mode
		ControlGet, hwWindow, HWND,, SysListView321, A
	if !hwWindow
		ControlGet, hwWindow, HWND,, SysListView321, ahk_class WorkerW
User avatar
joedf
Posts: 6875
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada, Quebec
Contact:

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

14 Jan 2019, 10:42

I’m not sure. I haven’t done much testing. Does it still happen occasionally? Or all the time?
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500 @ 3.00 GHz, 16GB DDR4 3200 MHz, NVIDIA GTX 1060 6GB | [About Me] | [ASPDM - StdLib Distribution]
[Populate the AHK MiniCity!] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library] | [About the AHK Foundation]
Walli
Posts: 4
Joined: 14 Jan 2019, 06:19
Location: Germany, Hannover

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

14 Jan 2019, 13:19

After i added

Code: Select all

	if !hwWindow
		ControlGet, hwWindow, HWND,, SysListView321, ahk_class WorkerW
it works on every machine.

What does not work:
If i'm logged in as normal domainuser and run the script with elevated rights as domainadmin. In that case it looks like the OpenProcess function denies access.
Walli
Posts: 4
Joined: 14 Jan 2019, 06:19
Location: Germany, Hannover

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

14 Jan 2019, 13:56

@vanipede

Code: Select all

Save:
coords := DeskIcons()
FileDelete, %ProgramData%\TEMP\desktopicons.txt
FileAppend, %coords%, %ProgramData%\TEMP\desktopicons.txt 
return

Code: Select all

Restore:
FileRead, coords, %ProgramData%\TEMP\desktopicons.txt
DeskIcons(coords)
return
User avatar
joedf
Posts: 6875
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada, Quebec
Contact:

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

14 Jan 2019, 22:07

Walli wrote:
14 Jan 2019, 13:19
What does not work:
If i'm logged in as normal domainuser and run the script with elevated rights as domainadmin. In that case it looks like the OpenProcess function denies access.
Interesting... perhaps it has something to do with dwDesiredAccess?
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500 @ 3.00 GHz, 16GB DDR4 3200 MHz, NVIDIA GTX 1060 6GB | [About Me] | [ASPDM - StdLib Distribution]
[Populate the AHK MiniCity!] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library] | [About the AHK Foundation]
Walli
Posts: 4
Joined: 14 Jan 2019, 06:19
Location: Germany, Hannover

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

15 Jan 2019, 06:33

I played a little with dwDesiredAccess. The minimum we need is:

Code: Select all

hProcess := DllCall("OpenProcess"	, "UInt",	0x18			; VirtualMemoryOperation|VirtualMemoryRead
									, "Int",	FALSE			; inherit = false
									, "ptr",	iProcessID)
It also works with the max of 0x1F0FFF.

But it doesn't help in my case with elevated rights.

Now i got it:
It works, if you join the normal user to the administrators group.
So i have to make a standalone exe-file, which runs with normal rights.
User avatar
joedf
Posts: 6875
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada, Quebec
Contact:

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

15 Jan 2019, 10:05

Ahhh okay, I see. Weird security rights system with windows :b
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500 @ 3.00 GHz, 16GB DDR4 3200 MHz, NVIDIA GTX 1060 6GB | [About Me] | [ASPDM - StdLib Distribution]
[Populate the AHK MiniCity!] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library] | [About the AHK Foundation]
vanipede
Posts: 5
Joined: 10 Jan 2019, 17:16

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

16 Jan 2019, 12:01

Walli wrote:
14 Jan 2019, 13:56
@vanipede


Thank you for replying so quickly. That is a clean implementation of what I am trying to do!
Unfortunately I get an error everytime I try to run the script.
--------------------------------------------------------------------------------------
Error at line 3.

Line Text: FileDelete, %ProgramData%\TEMP\desktopicons.txt
Error: Function calls require a space or '(', Use comma only between parameters.
The program will exit.
-----------------------------------------------------------------------------------------------------

Code: Select all

Save:
coords := DeskIcons()
FileDelete, %ProgramData%\TEMP\desktopicons.txt
FileAppend, %coords%, %ProgramData%\TEMP\desktopicons.txt 
return

Code: Select all

Restore:
FileRead, coords, %ProgramData%\TEMP\desktopicons.txt
DeskIcons(coords)
return

Code: Select all

; save positions
!s::coords := DeskIcons()
FileDelete, %ProgramData%\TEMP\desktopicons.txt
FileAppend, %coords%, %ProgramData%\TEMP\desktopicons.txt 
return


MsgBox now move the icons around yourself
; load positions
!l::FileRead, coords, %ProgramData%\TEMP\desktopicons.txt
DeskIcons(coords)
return

MsgBox "A_IsAdmin: " . A_IsAdmin
For ItemText, POINT in Data := DeskIcons()
    Icons .= ItemText . " (" . POINT.x . ";" . POINT.y . ")`n"
MsgBox "DeskIcons()`n------------------------------`n" . Icons
DeskIcons(Data)

DeskIcons(Data := 0)    ; CREDITS : https://autohotkey.com/boards/viewtopic.php?f=6&t=3529
{
    static PROCESS_VM_READ     := 0x0010, PROCESS_VM_OPERATION  := 0x0008, PROCESS_VM_WRITE    := 0x0020
         , MEM_COMMIT          := 0x1000, PAGE_READWRITE        := 0x0004 ;, MEM_RELEASE         := 0x8000
         , LVM_GETITEMCOUNT    := 0x1004, LVM_GETITEMPOSITION   := 0x1010, LVM_SETITEMPOSITION := 0x100F, LVM_GETITEMTEXTW := 0x1073

    ; recuperamos el identificador del control SysListView que muestra los iconos en el escritorio
    Local WindowId := GetDeskListView()    ; ControlGetHwnd("SysListView321", "ahk_class Progman")
    If (!WindowId)
        Return "SysListView321?"

    ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb761044(v=vs.85).aspx
    ; recuperamos la cantidad de iconos en el escritorio
    Local ItemCount := DllCall("User32.dll\SendMessageW", "Ptr", WindowId, "UInt", LVM_GETITEMCOUNT, "Ptr", 0, "Ptr", 0)
    If (!ItemCount)
        Return "ItemCount #0"

    Local ProcessId := WinGetPID("ahk_id" . WindowId)    ; recuperamos el identificador del proceso perteneciente al control SysListView
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspx
        ; abrimos el proceso para realizar operaciones de lectura y escritura en su memoria
        , hProcess  := DllCall("Kernel32.dll\OpenProcess", "UInt", PROCESS_VM_READ|PROCESS_VM_OPERATION|PROCESS_VM_WRITE, "Int", FALSE, "UInt", ProcessId, "Ptr")
    If (!hProcess)
        Return "OpenProcess ERROR"

    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa366890(v=vs.85).aspx
    ; reservamos memoria para el proceso para almacenar datos en ella, conociendo su posición en memoria podemos escribir y leer datos de forma segura
    ; para leer y escribir datos en la memoria de otro proceso, debemos utilizar las funciones WriteProcessMemory y ReadProcessMemory, no podemos hacerlo directamente en una variable de nuestro script
    ; ya que la memoria no es compartida y no podemos acceder a la memoria de otro proceso con las funciones incorporadas NumGet/NumPut, solo haría que nuestro Script terminase abruptamente
    ; entonces, reservamos memoria para utilizar de forma segura en el espacio del proceso deseado, la idea no es escribir en cualquier parte de la memoria de otro proceso sino en una solo para nuestro propósito
    ; las funcion "VirtualAllocEx" es como si llamásemos a "VarSetCapacity(VarBuff, size)" en ese proceso y devolviese la dirección de memoria de "VarBuff".
    Local Address := DllCall("Kernel32.dll\VirtualAllocEx", "Ptr", hProcess, "UPtr", 0, "UPtr", A_PtrSize == 4 ? 72 : 88, "UInt", MEM_COMMIT, "UInt", PAGE_READWRITE, "UPtr")    ; espacio para la estructura LVITEM
    Local   pBuff := DllCall("Kernel32.dll\VirtualAllocEx", "Ptr", hProcess, "UPtr", 0, "UPtr", 520, "UInt", MEM_COMMIT, "UInt", PAGE_READWRITE, "UPtr")    ; espacio para almacenar el texto de un elemento (icono)
    If (!Address || !pBuff)
        Return Error("VirtualAllocEx ERROR")

    ; https://msdn.microsoft.com/es-es/library/windows/desktop/ms681674(v=vs.85).aspx
    ; escribimos ciertos datos en la memoria reservada para la estructura LVITEM
    Local NumberOfBytesWritten, NumberOfBytesRead
    ; almacenamos la dirección de mememoria de pBuff en la estructura LVITEM, que debe estar en la posición "16 + A_PtrSize" a partir de "Address"
    ; utilizamos "UPtrP, pBuff" ya que en este parámetro de "WriteProcessMemory" se debe especificar un puntero (dirección de memoria) que contiene los datos a escribir; por lo tanto, sabiendo que pBuff contiene
    ; la dirección de memoria que queremos escribir en "LVITEM", debemos pasar la dirección de memoria que contiene la dirección de memoria almacenada en pBuff, esto es lo mismo que "UPtr, &pBuff"
    DllCall("Kernel32.dll\WriteProcessMemory", "Ptr", hProcess, "UPtr", Address + 16 + A_PtrSize, "UPtrP", pBuff, "UPtr", A_PtrSize, "UPtrP", NumberOfBytesWritten)
    ; luego almacenamos la cantidad de caracteres (UTF-16) máximos que serán escritos, si reservamos memoria para 520 bytes, en caracteres seria 520//2, lo que nos dá 260
    DllCall("Kernel32.dll\WriteProcessMemory", "Ptr", hProcess, "UPtr", Address + 16 + A_PtrSize*2, "IntP", 260, "UPtr", 4, "UPtrP", NumberOfBytesWritten)


    ; ==============================================================================================================
    ; Save
    ; ==============================================================================================================
    Local Buffer, POINT
    VarSetCapacity(Buffer, 520), VarSetCapacity(POINT, 8)
    If (!IsObject(Data))
    {
        Data := {}
        Loop (ItemCount)
        {
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb761048(v=vs.85).aspx
            ; recuperamos la posición x,y del icono con el índice "A_Index - 1" y le decimos a "SendMessage" que escriba los datos en la dirección de memoria que contiene "Address"
            ; nota: en este caso, utilizamos "Address" para dos propósitos, almacenar la estructura "POINT" (8 bytes) para recuperar la posición del elemento, y a su vez almacenar a la estructura LVITEM,
            ; que utilizamos mas abajo para recuperar el texto (aprovechamos el espacio reservado, ya que la estructura "POINT" (8 bytes) entra en la estructura "LVITEM" (48+A_PtrSize*3 bytes))
            If (!DllCall("User32.dll\SendMessageW", "Ptr", WindowId, "UInt", LVM_GETITEMPOSITION, "Int", A_Index-1, "UPtr", Address))
                Return Error("LVM_GETITEMPOSITION Index #" . A_Index . " ERROR")

            ; https://msdn.microsoft.com/es-es/library/windows/desktop/ms680553(v=vs.85).aspx
            ; la posición son dos valores (x,y) de 4 bytes c/u (int, estructura POINT), por lo tanto le especificamos a la función "ReadProcessMemory" que lea 8 bytes y almacene los datos leídos en "POINT"
            ; que es una variable que declaramos anteriormente en nuestro script y que puede almacenar hasta 8 bytes, exactamente lo que necesitamos; como aclaramos anteriormente, no podemos leer los datos
            ; directamente de la dirección de memoria almacenada en "Address" ya que esa parte de la memoria no pertenece a nuestro Script y no tenemos privilegios para acceder a ella, "ReadProcessMemory" si los tiene
            If (!DllCall("Kernel32.dll\ReadProcessMemory", "Ptr", hProcess, "UPtr", Address, "UPtr", &POINT, "UPtr", 8, "UPtrP", NumberOfBytesRead))
                Return Error("ReadProcessMemory #1 Index #" . A_Index . " ERROR")

            ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb761055(v=vs.85).aspx
            ; recuperamos el texto del elemento (icono) en el control SysListView, "LVM_GETITEMTEXTW" escribe el texto en la dirección de memoria que contiene "pBuff", que almacenamos en la estructura "LVITEM"
            ; la estructura "LVITEM", en este caso, estaría representada por "Address", que contiene la dirección de memoria que apunta al comienzo de esta estructura
            If (!DllCall("User32.dll\SendMessageW", "Ptr", WindowId, "UInt", LVM_GETITEMTEXTW, "Int", A_Index-1, "UPtr", Address))
                Return Error("LVM_GETITEMTEXTW Index #" . A_Index . " ERROR")

            ; https://msdn.microsoft.com/es-es/library/windows/desktop/ms680553(v=vs.85).aspx
            ; leemos el texto que se ha escrito en la dirección de memoria que contiene "pBuff" y la escribimos en la dirección de memoria de "Buffer", nuestra variable a la cual si tenemos acceso
            If (!DllCall("Kernel32.dll\ReadProcessMemory", "Ptr", hProcess, "UPtr", pBuff, "UPtr", &Buffer, "UPtr", 520, "UPtrP", NumberOfBytesRead))
                Return Error("ReadProcessMemory #2 Index #" . A_Index . " ERROR")
            
            ; luego, leemos el texto y posición del elemento para guardarlas en el objeto "Data"
            ObjRawSet(Data, StrGet(&Buffer, "UTF-16"), {x: NumGet(&POINT, "Int"), y: Numget(&POINT + 4, "Int")})
        }

        Return Error(Data)    ; OK! devolvemos el objeto "Data", cuyas claves contienen el texto de cada elemento (icono) y su valor la posición de este
    }


    ; ==============================================================================================================
    ; Restore
    ; ==============================================================================================================
    ; aquí restauramos los iconos a la posición almacenada en "Data", se aplica la misma lógica que arriba
    Local ItemText
    Loop (ItemCount)
    {
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb761055(v=vs.85).aspx
        If (!DllCall("User32.dll\SendMessageW", "Ptr", WindowId, "UInt", LVM_GETITEMTEXTW, "Int", A_Index-1, "UPtr", Address))
            Return Error("LVM_GETITEMTEXTW Index #" . A_Index . " ERROR")

        ; https://msdn.microsoft.com/es-es/library/windows/desktop/ms680553(v=vs.85).aspx
        If (!DllCall("Kernel32.dll\ReadProcessMemory", "Ptr", hProcess, "UPtr", pBuff, "UPtr", &Buffer, "UPtr", 520, "UPtrP", NumberOfBytesRead))
            Return Error("ReadProcessMemory Index #" . A_Index . " ERROR")

        If (ObjHasKey(Data, ItemText := StrGet(&Buffer, "UTF-16")))    ; determinamos si el texto del elemento actual se encuentra en el objeto "Data"
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb761192(v=vs.85).aspx
            ; establecemos la posición del elemento encontrado
            ; The LOWORD specifies the new x-position of the item's upper-left corner, in view coordinates.
            ; The HIWORD specifies the new y-position of the item's upper-left corner, in view coordinates.
            DllCall("User32.dll\SendMessageW", "Ptr", WindowId, "UInt", LVM_SETITEMPOSITION, "Int", A_Index-1, "UPtr", Data[ItemText].x & 0xFFFF | (Data[ItemText].y & 0xFFFF) << 16)
    }
    Return Error(TRUE)    ; OK!


    ; ==============================================================================================================
    ; Nested Functions
    ; ==============================================================================================================
    Error(Msg)
    {
        static MEM_RELEASE := 0x8000
        If (Address)    ; liberamos la memoria reservada Address para la aplicación
            If (!DllCall("Kernel32.dll\VirtualFreeEx", "Ptr", hProcess, "UPtr", Address, "Ptr", 0, "UInt", MEM_RELEASE))
                MsgBox("VirtualFreeEx #1 ERROR!",, 0x2010), ExitApp()
        If (pBuff)    ; liberamos la memoria reservada pBuff para la aplicación
            If (!DllCall("Kernel32.dll\VirtualFreeEx", "Ptr", hProcess, "UPtr", pBuff, "Ptr", 0, "UInt", MEM_RELEASE))
                MsgBox("VirtualFreeEx #2 ERROR!",, 0x2010), ExitApp()
        If (hProcess)    ; cerramos el Handle del proceso
            If (!DllCall("Kernel32.dll\CloseHandle", "Ptr", hProcess))
                MsgBox("CloseHandle ERROR!",, 0x2010), ExitApp()
        Return Msg
    }
}





/*
    Recupera el identificador del control ListView donde se visualizan los iconos en el escritorio.
*/
GetDeskListView()
{
    Local hListView := ControlGetHwnd("SysListView321", "ahk_class Progman")
    If (hListView)
        Return hListView
    If (hListView := DllCall("User32.dll\FindWindowEx", "Ptr", DllCall("User32.dll\FindWindowEx", "Ptr", DllCall("User32.dll\GetShellWindow", "Ptr"), "Ptr", 0, "Str", "SHELLDLL_DefView", "Ptr", 0, "Ptr"), "Ptr", 0, "Str", "SysListView32", "Ptr", 0, "Ptr"))
        Return hListView
    For Each, WindowId in WinGetList("ahk_class WorkerW")    ; WIN_8 && WIN_10
        If (WindowId := DllCall("User32.dll\FindWindowEx", "Ptr", WindowId, "Ptr", 0, "Str", "SHELLDLL_DefView", "Ptr", 0, "Ptr"))
            Return DllCall("User32.dll\FindWindowEx", "Ptr", WindowId, "Ptr", 0, "Str", "SysListView32", "Ptr", 0, "Ptr")
    Return FALSE
}





SetDeskIconPos(ItemName, X, Y)
{
    Local DeskIcons := DeskIcons()
    If (!IsObject(DeskIcons))
        Return DeskIcons

    If (!ObjHasKey(DeskIcons, ItemName))
        Return "ItemName doesn't exist"

    ObjRawSet(DeskIcons, ItemName, {x: x, y: y})
    Return DeskIcons(DeskIcons)
}





GetDeskIconPos(ItemName)
{
    Local DeskIcons := DeskIcons()
    If (!IsObject(DeskIcons))
        Return DeskIcons
    If (!ObjHasKey(DeskIcons, ItemName))
        Return "ItemName doesn't exist"

    Return {X: DeskIcons[ItemName].X, Y: DeskIcons[ItemName].Y}
}
Attachments
error.png
error.png (9.42 KiB) Viewed 526 times
vanipede
Posts: 5
Joined: 10 Jan 2019, 17:16

Re: [x64 & x32 fix] DeskIcons - Get/Set Desktop Icon Positions

16 Jan 2019, 12:01

Walli wrote:
14 Jan 2019, 13:56
@vanipede


Thank you for replying so quickly. That is a clean implementation of what I am trying to do!
Unfortunately I get an error everytime I try to run the script.
--------------------------------------------------------------------------------------
Error at line 3.

Line Text: FileDelete, %ProgramData%\TEMP\desktopicons.txt
Error: Function calls require a space or '(', Use comma only between parameters.
The program will exit.
-----------------------------------------------------------------------------------------------------

Code: Select all

Save:
coords := DeskIcons()
FileDelete, %ProgramData%\TEMP\desktopicons.txt
FileAppend, %coords%, %ProgramData%\TEMP\desktopicons.txt 
return

Code: Select all

Restore:
FileRead, coords, %ProgramData%\TEMP\desktopicons.txt
DeskIcons(coords)
return

Code: Select all

; save positions
!s::coords := DeskIcons()
FileDelete, %ProgramData%\TEMP\desktopicons.txt
FileAppend, %coords%, %ProgramData%\TEMP\desktopicons.txt 
return


MsgBox now move the icons around yourself
; load positions
!l::FileRead, coords, %ProgramData%\TEMP\desktopicons.txt
DeskIcons(coords)
return

MsgBox "A_IsAdmin: " . A_IsAdmin
For ItemText, POINT in Data := DeskIcons()
    Icons .= ItemText . " (" . POINT.x . ";" . POINT.y . ")`n"
MsgBox "DeskIcons()`n------------------------------`n" . Icons
DeskIcons(Data)

DeskIcons(Data := 0)    ; CREDITS : https://autohotkey.com/boards/viewtopic.php?f=6&t=3529
{
    static PROCESS_VM_READ     := 0x0010, PROCESS_VM_OPERATION  := 0x0008, PROCESS_VM_WRITE    := 0x0020
         , MEM_COMMIT          := 0x1000, PAGE_READWRITE        := 0x0004 ;, MEM_RELEASE         := 0x8000
         , LVM_GETITEMCOUNT    := 0x1004, LVM_GETITEMPOSITION   := 0x1010, LVM_SETITEMPOSITION := 0x100F, LVM_GETITEMTEXTW := 0x1073

    ; recuperamos el identificador del control SysListView que muestra los iconos en el escritorio
    Local WindowId := GetDeskListView()    ; ControlGetHwnd("SysListView321", "ahk_class Progman")
    If (!WindowId)
        Return "SysListView321?"

    ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb761044(v=vs.85).aspx
    ; recuperamos la cantidad de iconos en el escritorio
    Local ItemCount := DllCall("User32.dll\SendMessageW", "Ptr", WindowId, "UInt", LVM_GETITEMCOUNT, "Ptr", 0, "Ptr", 0)
    If (!ItemCount)
        Return "ItemCount #0"

    Local ProcessId := WinGetPID("ahk_id" . WindowId)    ; recuperamos el identificador del proceso perteneciente al control SysListView
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspx
        ; abrimos el proceso para realizar operaciones de lectura y escritura en su memoria
        , hProcess  := DllCall("Kernel32.dll\OpenProcess", "UInt", PROCESS_VM_READ|PROCESS_VM_OPERATION|PROCESS_VM_WRITE, "Int", FALSE, "UInt", ProcessId, "Ptr")
    If (!hProcess)
        Return "OpenProcess ERROR"

    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa366890(v=vs.85).aspx
    ; reservamos memoria para el proceso para almacenar datos en ella, conociendo su posición en memoria podemos escribir y leer datos de forma segura
    ; para leer y escribir datos en la memoria de otro proceso, debemos utilizar las funciones WriteProcessMemory y ReadProcessMemory, no podemos hacerlo directamente en una variable de nuestro script
    ; ya que la memoria no es compartida y no podemos acceder a la memoria de otro proceso con las funciones incorporadas NumGet/NumPut, solo haría que nuestro Script terminase abruptamente
    ; entonces, reservamos memoria para utilizar de forma segura en el espacio del proceso deseado, la idea no es escribir en cualquier parte de la memoria de otro proceso sino en una solo para nuestro propósito
    ; las funcion "VirtualAllocEx" es como si llamásemos a "VarSetCapacity(VarBuff, size)" en ese proceso y devolviese la dirección de memoria de "VarBuff".
    Local Address := DllCall("Kernel32.dll\VirtualAllocEx", "Ptr", hProcess, "UPtr", 0, "UPtr", A_PtrSize == 4 ? 72 : 88, "UInt", MEM_COMMIT, "UInt", PAGE_READWRITE, "UPtr")    ; espacio para la estructura LVITEM
    Local   pBuff := DllCall("Kernel32.dll\VirtualAllocEx", "Ptr", hProcess, "UPtr", 0, "UPtr", 520, "UInt", MEM_COMMIT, "UInt", PAGE_READWRITE, "UPtr")    ; espacio para almacenar el texto de un elemento (icono)
    If (!Address || !pBuff)
        Return Error("VirtualAllocEx ERROR")

    ; https://msdn.microsoft.com/es-es/library/windows/desktop/ms681674(v=vs.85).aspx
    ; escribimos ciertos datos en la memoria reservada para la estructura LVITEM
    Local NumberOfBytesWritten, NumberOfBytesRead
    ; almacenamos la dirección de mememoria de pBuff en la estructura LVITEM, que debe estar en la posición "16 + A_PtrSize" a partir de "Address"
    ; utilizamos "UPtrP, pBuff" ya que en este parámetro de "WriteProcessMemory" se debe especificar un puntero (dirección de memoria) que contiene los datos a escribir; por lo tanto, sabiendo que pBuff contiene
    ; la dirección de memoria que queremos escribir en "LVITEM", debemos pasar la dirección de memoria que contiene la dirección de memoria almacenada en pBuff, esto es lo mismo que "UPtr, &pBuff"
    DllCall("Kernel32.dll\WriteProcessMemory", "Ptr", hProcess, "UPtr", Address + 16 + A_PtrSize, "UPtrP", pBuff, "UPtr", A_PtrSize, "UPtrP", NumberOfBytesWritten)
    ; luego almacenamos la cantidad de caracteres (UTF-16) máximos que serán escritos, si reservamos memoria para 520 bytes, en caracteres seria 520//2, lo que nos dá 260
    DllCall("Kernel32.dll\WriteProcessMemory", "Ptr", hProcess, "UPtr", Address + 16 + A_PtrSize*2, "IntP", 260, "UPtr", 4, "UPtrP", NumberOfBytesWritten)


    ; ==============================================================================================================
    ; Save
    ; ==============================================================================================================
    Local Buffer, POINT
    VarSetCapacity(Buffer, 520), VarSetCapacity(POINT, 8)
    If (!IsObject(Data))
    {
        Data := {}
        Loop (ItemCount)
        {
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb761048(v=vs.85).aspx
            ; recuperamos la posición x,y del icono con el índice "A_Index - 1" y le decimos a "SendMessage" que escriba los datos en la dirección de memoria que contiene "Address"
            ; nota: en este caso, utilizamos "Address" para dos propósitos, almacenar la estructura "POINT" (8 bytes) para recuperar la posición del elemento, y a su vez almacenar a la estructura LVITEM,
            ; que utilizamos mas abajo para recuperar el texto (aprovechamos el espacio reservado, ya que la estructura "POINT" (8 bytes) entra en la estructura "LVITEM" (48+A_PtrSize*3 bytes))
            If (!DllCall("User32.dll\SendMessageW", "Ptr", WindowId, "UInt", LVM_GETITEMPOSITION, "Int", A_Index-1, "UPtr", Address))
                Return Error("LVM_GETITEMPOSITION Index #" . A_Index . " ERROR")

            ; https://msdn.microsoft.com/es-es/library/windows/desktop/ms680553(v=vs.85).aspx
            ; la posición son dos valores (x,y) de 4 bytes c/u (int, estructura POINT), por lo tanto le especificamos a la función "ReadProcessMemory" que lea 8 bytes y almacene los datos leídos en "POINT"
            ; que es una variable que declaramos anteriormente en nuestro script y que puede almacenar hasta 8 bytes, exactamente lo que necesitamos; como aclaramos anteriormente, no podemos leer los datos
            ; directamente de la dirección de memoria almacenada en "Address" ya que esa parte de la memoria no pertenece a nuestro Script y no tenemos privilegios para acceder a ella, "ReadProcessMemory" si los tiene
            If (!DllCall("Kernel32.dll\ReadProcessMemory", "Ptr", hProcess, "UPtr", Address, "UPtr", &POINT, "UPtr", 8, "UPtrP", NumberOfBytesRead))
                Return Error("ReadProcessMemory #1 Index #" . A_Index . " ERROR")

            ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb761055(v=vs.85).aspx
            ; recuperamos el texto del elemento (icono) en el control SysListView, "LVM_GETITEMTEXTW" escribe el texto en la dirección de memoria que contiene "pBuff", que almacenamos en la estructura "LVITEM"
            ; la estructura "LVITEM", en este caso, estaría representada por "Address", que contiene la dirección de memoria que apunta al comienzo de esta estructura
            If (!DllCall("User32.dll\SendMessageW", "Ptr", WindowId, "UInt", LVM_GETITEMTEXTW, "Int", A_Index-1, "UPtr", Address))
                Return Error("LVM_GETITEMTEXTW Index #" . A_Index . " ERROR")

            ; https://msdn.microsoft.com/es-es/library/windows/desktop/ms680553(v=vs.85).aspx
            ; leemos el texto que se ha escrito en la dirección de memoria que contiene "pBuff" y la escribimos en la dirección de memoria de "Buffer", nuestra variable a la cual si tenemos acceso
            If (!DllCall("Kernel32.dll\ReadProcessMemory", "Ptr", hProcess, "UPtr", pBuff, "UPtr", &Buffer, "UPtr", 520, "UPtrP", NumberOfBytesRead))
                Return Error("ReadProcessMemory #2 Index #" . A_Index . " ERROR")
            
            ; luego, leemos el texto y posición del elemento para guardarlas en el objeto "Data"
            ObjRawSet(Data, StrGet(&Buffer, "UTF-16"), {x: NumGet(&POINT, "Int"), y: Numget(&POINT + 4, "Int")})
        }

        Return Error(Data)    ; OK! devolvemos el objeto "Data", cuyas claves contienen el texto de cada elemento (icono) y su valor la posición de este
    }


    ; ==============================================================================================================
    ; Restore
    ; ==============================================================================================================
    ; aquí restauramos los iconos a la posición almacenada en "Data", se aplica la misma lógica que arriba
    Local ItemText
    Loop (ItemCount)
    {
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb761055(v=vs.85).aspx
        If (!DllCall("User32.dll\SendMessageW", "Ptr", WindowId, "UInt", LVM_GETITEMTEXTW, "Int", A_Index-1, "UPtr", Address))
            Return Error("LVM_GETITEMTEXTW Index #" . A_Index . " ERROR")

        ; https://msdn.microsoft.com/es-es/library/windows/desktop/ms680553(v=vs.85).aspx
        If (!DllCall("Kernel32.dll\ReadProcessMemory", "Ptr", hProcess, "UPtr", pBuff, "UPtr", &Buffer, "UPtr", 520, "UPtrP", NumberOfBytesRead))
            Return Error("ReadProcessMemory Index #" . A_Index . " ERROR")

        If (ObjHasKey(Data, ItemText := StrGet(&Buffer, "UTF-16")))    ; determinamos si el texto del elemento actual se encuentra en el objeto "Data"
            ; https://msdn.microsoft.com/en-us/library/windows/desktop/bb761192(v=vs.85).aspx
            ; establecemos la posición del elemento encontrado
            ; The LOWORD specifies the new x-position of the item's upper-left corner, in view coordinates.
            ; The HIWORD specifies the new y-position of the item's upper-left corner, in view coordinates.
            DllCall("User32.dll\SendMessageW", "Ptr", WindowId, "UInt", LVM_SETITEMPOSITION, "Int", A_Index-1, "UPtr", Data[ItemText].x & 0xFFFF | (Data[ItemText].y & 0xFFFF) << 16)
    }
    Return Error(TRUE)    ; OK!


    ; ==============================================================================================================
    ; Nested Functions
    ; ==============================================================================================================
    Error(Msg)
    {
        static MEM_RELEASE := 0x8000
        If (Address)    ; liberamos la memoria reservada Address para la aplicación
            If (!DllCall("Kernel32.dll\VirtualFreeEx", "Ptr", hProcess, "UPtr", Address, "Ptr", 0, "UInt", MEM_RELEASE))
                MsgBox("VirtualFreeEx #1 ERROR!",, 0x2010), ExitApp()
        If (pBuff)    ; liberamos la memoria reservada pBuff para la aplicación
            If (!DllCall("Kernel32.dll\VirtualFreeEx", "Ptr", hProcess, "UPtr", pBuff, "Ptr", 0, "UInt", MEM_RELEASE))
                MsgBox("VirtualFreeEx #2 ERROR!",, 0x2010), ExitApp()
        If (hProcess)    ; cerramos el Handle del proceso
            If (!DllCall("Kernel32.dll\CloseHandle", "Ptr", hProcess))
                MsgBox("CloseHandle ERROR!",, 0x2010), ExitApp()
        Return Msg
    }
}





/*
    Recupera el identificador del control ListView donde se visualizan los iconos en el escritorio.
*/
GetDeskListView()
{
    Local hListView := ControlGetHwnd("SysListView321", "ahk_class Progman")
    If (hListView)
        Return hListView
    If (hListView := DllCall("User32.dll\FindWindowEx", "Ptr", DllCall("User32.dll\FindWindowEx", "Ptr", DllCall("User32.dll\GetShellWindow", "Ptr"), "Ptr", 0, "Str", "SHELLDLL_DefView", "Ptr", 0, "Ptr"), "Ptr", 0, "Str", "SysListView32", "Ptr", 0, "Ptr"))
        Return hListView
    For Each, WindowId in WinGetList("ahk_class WorkerW")    ; WIN_8 && WIN_10
        If (WindowId := DllCall("User32.dll\FindWindowEx", "Ptr", WindowId, "Ptr", 0, "Str", "SHELLDLL_DefView", "Ptr", 0, "Ptr"))
            Return DllCall("User32.dll\FindWindowEx", "Ptr", WindowId, "Ptr", 0, "Str", "SysListView32", "Ptr", 0, "Ptr")
    Return FALSE
}





SetDeskIconPos(ItemName, X, Y)
{
    Local DeskIcons := DeskIcons()
    If (!IsObject(DeskIcons))
        Return DeskIcons

    If (!ObjHasKey(DeskIcons, ItemName))
        Return "ItemName doesn't exist"

    ObjRawSet(DeskIcons, ItemName, {x: x, y: y})
    Return DeskIcons(DeskIcons)
}





GetDeskIconPos(ItemName)
{
    Local DeskIcons := DeskIcons()
    If (!IsObject(DeskIcons))
        Return DeskIcons
    If (!ObjHasKey(DeskIcons, ItemName))
        Return "ItemName doesn't exist"

    Return {X: DeskIcons[ItemName].X, Y: DeskIcons[ItemName].Y}
}

Return to “Scripts and Functions”

Who is online

Users browsing this forum: No registered users and 113 guests