Uno dei molti problemi che mi affliggono a lavoro è la naturale propensione di Windows di dimenticare o riposizionare le tante icone che popolano il mio desktop secondo una logica perversa. Mi riferisco almeno alla versione XP, magari in qualche “nuova” versione questo aspetto è stato curato maggiormente.
Questa tendenza naturale è amplificata nel caso occorra, come spesso mi accade, di collegare il portatile a monitor diversi o a video proiettori. Passando da una risoluzione all’altra le icone vengono riposizionate seguendo geometrie non euclidee.
La prima idea che viene in questi casi è quella che, visto e considerato essere un problema comune, probabilmente ci sarà qualche programmino in giro che si occupa di memorizzare la posizione delle icone in un dato istante e di riposizionare le icone ai loro posti su richiesta. Un programmino da quattro soldi che magari si potrebbe trovare come PowerToy o mini utility open source.
Niente.
Beh allora visto che la cosa è così semplice perché non ce lo facciamo da noi.
Comincio a cercare informazioni su qualche API di Windows che faccia al caso mio. Infondo le operazioni di base che mi servono sono:
-
un metodo che mi restituisca il numero di icone presenti sul desktop,
-
per ogni icona un metodo che mi restituisca la posizione sul desktop,
-
uno che mi consenta di impostare la posizione dell’icona,
-
ed infine uno che mi restituisca una stringa con il nome da usare come riferimento.
Devo dire che la cosa non è stata così facile come possa sembrare e per questo motivo adesso che ne sono riuscito a venire a capo ho deciso di scrivere questo articolo. In realtà i motivi sono due: uno perché io ho già fatto questa cosa qualche anno fa ma purtroppo ho perso i sorgenti e adesso che voglio modificare il programma devo ripartire da zero. Grazie anche alla mia scadente memoria, il secondo perché su internet c’è veramente poco, mal spiegato e non in italiano.
Mettiamoci all’opera.
Per prima cosa occorre procurarsi un handler (ovvero l’identificativo che assegna Windows ad ogni oggetto che maneggia). Per poter maneggiare un qualsiasi oggetto con una API di Windows occorre per prima cosa conoscere il suo handler. Quello che interessa a noi è quello della finestra di nome “Program manager”. Ed in particolare di un suo sottofiglio: “SHELLDLL_DefView” e del sottto sotto figlio “SysListView32”. Il tutto con le seguenti tre istruzioni:
progman_wnd = FindWindow(NULL, L”Program Manager”);
desktopview_wnd = FindWindowEx(progman_wnd, NULL, L”SHELLDLL_DefView”, NULL);
listview_wnd = FindWindowEx(desktopview_wnd, NULL, L”SysListView32″, NULL);
Questa list view è quella che contiene tutte le icone del desctop. Per interagire con essa occorrerà mandargli messaggi tramite SendMessage(listview_wnd,MESSAGGIO,PARAMETRO1, PARAMETRO2).
Sembrerebbe finita qui ma non è ancora vinta. I due processi hanno uno spazio dati differente per cui ogni qual volta occorre scambiare dati per riferimento (cosa necessaria ad ottenere la posizione delle icone) tra un processo ed un altro occorre usare una tecnica particolare:
-
Dal processo 1 occorre creare uno spazio di memoria nel processo 2 ed ottenere un riferimento valido per il processo 2. (Riferimento).
-
Se necessario scrivere sull’area creata nel processo 2 a partire dal riferimento ottenuto.
-
Il processo 1 manda il messaggio con il SendMessage passando il riferimento valido.
-
Leggere dall’area del processo 2.
Il messaggio in questione è:
SendMessage(listview_wnd,LVM_GETITEMPOSITION,(WPARAM)(index),(LPARAM)(ptr));
Dove index è la posizione dell’icona nella lista e ptr è il puntatore ottenuto in precedenza.
Come? Non l’abbiamo ancora ottenuto? Ecco la sequenza di istruzioni:
-
Troviamo il proces ID ovvero l’ID che Windows assegna al processo relativo alla listview.
GetWindowThreadProcessId(listview_wnd,&processID);
-
Otteniamo l’handler del processo.
proc = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ| PROCESS_VM_WRITE|PROCESS_QUERY_INFORMATION, FALSE, processID);
-
Noto l’handler proc si può allocare lo spazio sul processo che detiene la list view delle icone ed ottenere un puntatore valido nel suo spazio di memoria.
ptr = VirtualAllocEx(proc,NULL,sizeof(POINT),MEM_COMMIT, PAGE_READWRITE);
Ora che abbiamo il ptr possiamo mandare i messaggi.
SendMessage(listview_wnd,LVM_GETITEMCOUNT,0,0);
Restituisce il numero di icone sul desktop.
SendMessage(listview_wnd,LVM_GETITEMPOSITION,(WPARAM)(index),(LPARAM)(ptr));
Scrive sul puntato da ptr la posizione della icona all’indice index.
SendMessage(listview_wnd,LVM_GETITEMTEXT,(WPARAM)(index),(LPARAM)(ptr));
Per ottenere il nome dell’icona.
SendMessage(listview_wnd,LVM_SETITEMPOSITION,(WPARAM)(index),MAKELPARAM((WORD)X,(WORD)Y));
Per impostare la posizione dell’icona all’indice index alle coordinate X e Y.
Infine per leggere o scrivere nello spazio di memoria dell’altro processo occorre utilizzare le due funzioni:
ReadProcessMemory(proc, ptr, (void*)(&myPtr), SIZE, NULL);
e
WriteProcessMemory(proc,ptr, (void*)(&myPtr), SIZE, NULL);
Dove myPtr è invece un riferimento valido all’interno del processo 1 (ovvero il mio programma).
Beh mettendo tutto questo insieme ci si riesce.
Io il programma l’ho fatto e per non perderne più traccia l’ho anche inserito su sourceforge.
Si chiama XPDesktopIconsManager e questo è il link per raggiungerlo. Ovviamente software libero.