3

Where are the positions of Windows desktop shortcuts stored?

I am asking about the screen positions of the icons not the actual icons themselves. I know the icons themselves are stored in various DLLs, EXEs etc. The positions are clearly stored in some non-volatile store because they persists through re-boots.

My end goal is to write an app to display, and optionally re-arrange icons on my desktop.
I know this is possible because many available apps do this (e.g., "WinTidy").

I find much talk about "Windows Shell Bags". An interesting article about these are in http://williballethin.com.forensics/shellbags, but that only addresses directories not shortcuts. These are in the registry at various places including

`HKEY_CURRENT_USER/Software/Microsoft/Windows/Shell/Bags/1/Desktop`  
`HKEY_USERS/.DEFAULT/Software/Microsoft/Windows/Shell/Bags/1/Desktop`  

I wrote a program to extract these but the format of the key values is incomprehensible.

Any body know where and how they are stored?

DontPanic
  • 2,164
  • 5
  • 29
  • 56
  • 3
    This is not a programming question, although I can easily understand why a programmer might make use of it. You can find out easily enough yourself. (1) Grab a copy of "SysInternals' Process Monitor" from Microsoft (2) Set a filter to show operations performed by explorer.exe (3) Move one of your desktop icons. – Ben Voigt Sep 27 '19 at 01:42
  • 2
    Ben, this is indeed a programming question. I am trying to develop an app that will display, and optionally re-arrange my desktop by theme (e.g., alphabetically, by date, etc.). I will try your suggestion about SysInternals Process Monitor. – DontPanic Sep 27 '19 at 13:14
  • 1
    What you asked is not a programming question. And will not help you rearrange your desktop. At best it would let you back up the layout and restore it (possibly manually, which is what makes it not a programming question), without ever being able to see inside. To be able to enumerate icons and adjust their positions, see this existing question: https://stackoverflow.com/q/131690/103167 – Ben Voigt Sep 27 '19 at 14:11
  • FWIW, shortcuts on the desktop aren't treated any differently from directories or real files which live inside the desktop directory. – Ben Voigt Sep 27 '19 at 14:13
  • 1
    I respectfully disagree. There are free and commercial apps that do it (e,g. "WinTidy","DesktopOK","ReIcon", etc.) so there *must* be a way to do what I want. – DontPanic Sep 27 '19 at 14:37
  • You misunderstood. I didn't say that the icons locations can't be managed, I even linked a question explaining how. I said that knowing where explorer stores those locations doesn't help. If you know where the locations are stored but don't know the format of the storage, you can't modify it except to rollback to a saved older version. And if you know the API for changing the locations in explorer, you can just let it persist them as normal without knowing where that persistence happens. **Your question is only loosely related to the problem you are trying to solve.** – Ben Voigt Sep 27 '19 at 14:49
  • There are some issues. (1) Are the positions stored in a file or the registry (backed by a file)? Undeniably Yes. (2) Is it a *major* challenge to locate/access/modify these. Most definitely. (3) Is this the best way to reach my goal? Maybe not. May be better to use technique from : http://stackoverflow.com/q/131690/103167 – DontPanic Sep 30 '19 at 18:55
  • I found out the best way to do what I want. See my answer. – DontPanic Oct 04 '19 at 15:30
  • Does this answer your question? [Manipulating the positions of desktop icons](https://stackoverflow.com/questions/49964260/manipulating-the-positions-of-desktop-icons) – Raymond Chen Jul 19 '20 at 14:24

1 Answers1

2

UPDATE 6/3/20
++++++++++++++++++++++++++++++++++++++
I just switched over to a Win10 64-bit machine and find the solution below no longer works. I believe because of a change in the desktop internals. I figured out how to do this. See "WIN10 ADDENDUM" at the end of this answer.
++++++++++++++++++++++++++++++++++++++

I finally figured out how to do what I want (display and re-arrange desktop icons). My original question concerned locating, reading and writing to the file where the icon info is stored, but this is not a useful approach. Here is what I learned:

Explorer.exe displays desktop items in a giant ListView covering the whole desktop with ListView items corresponding to each visible icon. At startup, Explorer reads info from some arcane file and populates the ListView. On exit, it re-writes that file from the ListView. So modifying the file would not help because it would be overwritten on exit.

The right way to manipulate desktop items is to directly manipulate items in the ListView. Any changes are immediately visible on change, and are saved on exit. To access the items, we can use several Windows messages: LVM_GETITEM, LVM_GETITEMCOUNT, LVM_GETITEMPOSITION and LVM_SETITEMPOSITION. These messages are fairly simple to use with one complication: some require pointers to parameter structures. These structures must be in Explorer's address space not my app's, so some trickery is needed. Here's how to do it. I use LVM_GETITEMPOSITION as an example, which requires a pointer to a POINT structure.

  • Declare a POINT structure in your app.

  • Allocate a mirror structure in Explorer's address space using API VirtualAllocEx().

  • Send LVM_GETITEMPOSITION to Explorer specifying a pointer to this structure.

  • Read back the result into your app's POINT using API ReadProcessMemory(). This function can read memory across different address spaces.

I have prototyped these operations and they work as I wanted. My code is quite long but I will post excerpts as soon as I clean it up.

UPDATE 10/4/2019 ------------------------------------

CODE EXCERPTS

A set of commonly used utility functions was created to make code more compact and readable. These are named "exp*()" and are included at the end. A reference can be found at http://ramrodtechnology.com/explorer. Much of the basic technique herein was shamelessly stolen from https://www.codeproject.com/Articles/5570/Stealing-Program-s-Memory

Setup

// COMMONLY USED VARS
HANDLE hProcess;        // explorer process handle
HWND hWndLV;        // explorer main window

// SET UP CONVENIENCE VARS
hProcess = expGetProcessHandle();   // get explorer process handle
if( !hProcess ) exit( 1 );
hWndLV = expGetListView();      // get main ListView of Desktop
if( !hWndLV   ) exit( 1 );

Function to Print All Item Names

//@ Process a list view window and print item names
int
printAllNames()
{
    int ok,icount,indx;
    LVITEM item;                    // point in app space
    LVITEM *_pitem;                 // point in exp space
    char text[512];
    char *_ptext;
    int nr,nwrite;              // count of bytes read/written
    
    printf( "\n" );

    // ALLOC ITEMS IN EXP SPACE
    _pitem = expAlloc( sizeof(LVITEM) );
    _ptext = expAlloc( sizeof(text  ) );


    printf( "  NAME\n" );
    printf( "  ==================================\n" );
    icount = expGetItemCount();
    for( indx=0; indx<icount; indx++ ) {            // for each item in LV

      // SETUP ITEM IN EXP SPACE
      memset( &item, 0, sizeof(LVITEM) );   // preclear
      item.iItem      = indx;       // index of item to read
      item.iSubItem   = 0;          // sub index (always 0)
      item.mask       = LVIF_TEXT;      // component to read
      item.pszText    = _ptext;     // buffer to recv text
      item.cchTextMax = sizeof(text);   // size of buffer

      // WRITE ITEM REQ TO EXP SPACE
      ok = WriteProcessMemory( hProcess, _pitem, &item, sizeof(LVITEM), &nwrite );

      // SEND MESSAGE TO GET ITEM INTO EXP SPACE
      ok = SendMessage( hWndLV, LVM_GETITEM, indx, (LPARAM)_pitem );

      // READ EXP TEXT INTO APP SPACE
      memset( &item, 0, sizeof(LVITEM) );
      ok = ReadProcessMemory( hProcess, _pitem, &item, sizeof(POINT), &nr );
      ok = ReadProcessMemory( hProcess, _ptext, &text, sizeof(text),  &nr );

      // PRINT RESULT
      printf( "  %s\n", text );
    }
    ok = expFree( _pitem );
    ok = expFree( _ptext );

    return( TRUE );
    //r Returns TRUE on success, FALSE on error
}  

Function To Print All Item Positions

//@ Process a list view window and print position
int
printAllPositions()
{
    int ok,icount,indx,nr;
    POINT pt;                   // point in app space
    POINT *_ppt;                    // point in exp space
    
    icount = expGetItemCount();

    _ppt = expAlloc( sizeof(POINT) );
    if( !_ppt ) return( FALSE );

    printf( "   X    Y\n" );
    printf( "---- ----\n" );
    for( indx=0; indx<icount; indx++ ) {        // for each item in LV
      ok = SendMessage( hWndLV, LVM_GETITEMPOSITION, indx, (LPARAM)_ppt );
      ok = ReadProcessMemory( hProcess, _ppt, &pt, sizeof(POINT), &nr );
      printf( "%4d %4d\n", pt.x, pt.y );
    }

    ok = expFree( _ppt );

    return( TRUE );
    //r Returns TRUE on success
}

Function To Move Item
See 'expSetItemPosition' below. UPDATED 10/6/19

Explorer Utility Functions

// EXPLORER UTILITY FUNCTIONS

//@ Allocate a block of memory in explorer space
void *
expAlloc(
  int size)     // size of block
{
    void *p;

    p = VirtualAllocEx( 
        hProcess,
        NULL,
        size,
        MEM_COMMIT, 
        PAGE_READWRITE );
    return( p );
    //r Returns addr of memory in EXPLORER space or NULL on error
}

//@ Free virtual memory in EXPLORER space
int
expFree(
  void *p)  // pointer to free
{
    int ok;
    ok = VirtualFreeEx( hProcess, p, 0, MEM_RELEASE );
    return( ok );
    //r Returns TRUE on success, else FALSE
}

static int  aBiggest;       // biggest area so far
static HWND hWndBiggest;    // hWnd with biggest area

//@ Find main list view of explorer
HWND
expGetListView()
{
    //n Approach: Enumerate all child windows of desktop and find largest.
    //n This will be the main explorer window.

    HWND hWndDesktop;
    hWndDesktop = GetDesktopWindow();
    if( !hWndDesktop ) return( NULL );

    aBiggest    = -1;       // init
    hWndBiggest = NULL;     // init
    EnumChildWindows( hWndDesktop, CallbackDesktopChild, 0 );
    
    return( hWndBiggest );
    //r Returns hWnd of largest explorer list view
}

//@ Callback for EnumChildWindows
BOOL CALLBACK CallbackDesktopChild(
  HWND hWnd,
  LPARAM dwUser)
{
    //n Get size of child. If biggest, save hWnd.

    int i,w,h,a;
    char classname[MAXPATH+1];
    RECT rect;

    i = GetClassName( hWnd, classname, MAXPATH );   // get class
    if( stricmp( classname, "SysListView32" ) ) {   // not a list view?
      return( TRUE );               // skip it
    }

    // CALC SIZE
    i = GetWindowRect( hWnd, &rect );
    w = rect.right - rect.left;
    h = rect.bottom - rect.top;

    // CHECK IF BIGGEST
    a = w * h;
    if( a > aBiggest ) {    // is biggest?
      aBiggest    = a;
      hWndBiggest = hWnd;
    }

    return( TRUE );     // TRUE to continue enumeration
}


//@ Get process handle of explorer.exe
HANDLE
expGetProcessHandle()
{
    //n Approach: take process snapshot and loop through to find "explorer.exe"
    //n Needs tlhelp32.h and comctl32.lib
    
    int i,stat;
    PROCESSENTRY32 pe;
    HANDLE hSnapshot;
    char *name;
    HANDLE h;

    // TAKE A SNAPSHOT
    hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
    if( !hSnapshot ) return( NULL );

    // LOOP THROUGH PROCESSES AND FIND "explorer.exe"
    for( i=0;;i++ ) {
      pe.dwSize = sizeof( PROCESSENTRY32 );
      if( i == 0 ) stat = Process32First( hSnapshot, &pe );
      else         stat = Process32Next ( hSnapshot, &pe );
      if( !stat ) break;                // done or error?
      name = pe.szExeFile;
      if( !stricmp( name, "explorer.exe" ) ) {  // matches?
        h = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID ); 
        return( h );
      }
    }

    return( NULL );
    //r Returns explorer process handle or NULL on error
}

//@ Get count of items in explorer list view
int
expGetItemCount()
{
    int count;

    count = SendMessage( hWndLV, LVM_GETITEMCOUNT, 0, 0 );
    return( count );
    //r Returns count of item
}

//@ Get position of list view icon by index
int
expGetItemPosition(
  int indx, // index of item
  int *x,   // ptr to int to recv x
  int *y)   // ptr to int to recv y
{
    int i,ok,icount;
    char classname[MAXPATH+1];
    POINT pt;                   // point in app space
    POINT *_ppt;                    // point in exp space
    int nr;                     // count of bytes read
    //int w,h;

    i = GetClassName( hWndLV, classname, MAXPATH );

    // GET COUNT OF ITEMS IN LIST VIEW
    icount = expGetItemCount();
    if( indx < 0 || indx >= icount ) return( FALSE );

    // ALLOC POINT IN EXP SPACE
    _ppt = expAlloc( sizeof(POINT) );
    if( !_ppt ) return( FALSE );

    // SEND MESSAGE TO GET POS INTO EXP SPACE POINT
    ok = SendMessage( hWndLV, LVM_GETITEMPOSITION, indx, (LPARAM)_ppt );
    if( !ok ) return( FALSE );

    // READ EXP SPACE POINT INTO APP SPACE POINT
    ok = ReadProcessMemory( hProcess, _ppt, &pt, sizeof(POINT), &nr );
    if( !ok ) return( FALSE );

    ok = expFree( _ppt );
    if( !ok ) return( FALSE );

    if( x ) *x = pt.x;
    if( y ) *y = pt.y;

    //r Returns TRUE on success
    return( TRUE );  
}  

//@ Move item
int
expSetItemPosition(
  char *name,   // icon name
  int x,        // new x coord
  int y)        // new y coord
{
    int ok,indx;
    LPARAM lParam;

    indx = expGetItemIndex( name );
    if( indx < 0 ) return( FALSE );

    lParam = MAKELPARAM( x, y );
    ok = SendMessage( hWndLV, LVM_SETITEMPOSITION, indx, lParam );
    if( !ok ) return( FALSE );

    return( TRUE );
    //r Returns TRUE on success
}

WIN10 ADDENDUM 6/19/20
++++++++++++++++++++++++++++++++++

Under Win10, the solution is much more complicated. You must use various COM objects and interfaces, e.g. IShellWindows, etc. (God, I hate COM). I did not create a library but rather offer a complete working program below. I compiled this using MSVC 2019. Error checking has been omitted for clarity (but you should do it).

//  icons.cpp - Display (and optionally move) desktop icons

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <ShlObj.h>
#include <atlbase.h>

int
main(int argc,char** argv)
{
    CComPtr<IShellWindows> spShellWindows;
    CComPtr<IShellBrowser> spBrowser;
    CComPtr<IDispatch> spDispatch;
    CComPtr<IShellView> spShellView;
    CComPtr<IFolderView>  spView;
    CComPtr<IShellFolder> spFolder;
    CComPtr<IEnumIDList>  spEnum;
    CComHeapPtr<ITEMID_CHILD> spidl;
    CComVariant vtLoc(CLSID_ShellWindows);
    CComVariant vtEmpty;
    STRRET str;

    int count=0;
    HRESULT hr;
    long lhWnd;

    // INITIALIZE COM
    CoInitialize(NULL);
    
    // GET ShellWindows INTERFACE
    hr = spShellWindows.CoCreateInstance(CLSID_ShellWindows);

    // FIND WINDOW
    hr = spShellWindows->FindWindowSW(
        &vtLoc, &vtEmpty, SWC_DESKTOP, &lhWnd, SWFO_NEEDDISPATCH, &spDispatch);

    // GET DISPATCH INTERFACE
    CComQIPtr<IServiceProvider>(spDispatch)->
      QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&spBrowser));

    spBrowser->QueryActiveShellView(&spShellView);
    spShellView->QueryInterface(IID_PPV_ARGS(&spView) );

    hr = spView->GetFolder(IID_PPV_ARGS(&spFolder));

    // GET ENUMERATOR
    spView->Items(SVGIO_ALLVIEW, IID_PPV_ARGS(&spEnum));    // get enumerator

    // ENUMERATE ALL DESKTOP ITEMS
    for (; spEnum->Next(1, &spidl, nullptr) == S_OK; spidl.Free()) {
      // GET/PRINT ICON NAME AND POSITION
      char* name;
      POINT pt;
      spFolder->GetDisplayNameOf(spidl, SHGDN_NORMAL, &str);
      StrRetToStr(&str, spidl, &name);
      spView->GetItemPosition(spidl, &pt);
      printf("%5d %5d \"%s\"\n", pt.x, pt.y, name);

#define MOVE_ICON
#ifdef MOVE_ICON
      // OPTIONAL: MOVE *SINGLE* SELECTED ITEM
      {
        if( !_stricmp(name, "ICON_NAME_TO_MOVE") ) {
        PCITEMID_CHILD apidl[1] = { spidl };
        int numitems = 1;
        // SET pt TO NEW POSITION HERE
        hr = spView->SelectAndPositionItems(numitems, apidl, &pt, 0);
        }
      }
#endif

      count++;
    }
    CoUninitialize();           // release COM

    fprintf(stderr, "enumerated %d desktop icons\n", count);
    fprintf(stderr, "Press any key to exit...\n");
    _getch();
    exit(0 );
}
DontPanic
  • 2,164
  • 5
  • 29
  • 56
  • This isn't an answer to the question you asked. It is a good answer (showing a more complete example) to https://stackoverflow.com/questions/131690/how-can-i-programmatically-manipulate-windows-desktop-icon-locations – Ben Voigt Oct 04 '19 at 17:12
  • I discovered an error using LVM_SETITEMPOSITION. It has been corrected in my answer. – DontPanic Oct 06 '19 at 20:01
  • 1
    Glad to see that Microsoft are finally taking advantage of their liberty to change internal implementation details. The code under *WIN10 ADDENDUM* has always been the correct way to access the Shell's desktop folder. Helpful explanations can be found at [Manipulating the positions of desktop icons](https://devblogs.microsoft.com/oldnewthing/20130318-00/?p=4933) written by Raymond Chen. – IInspectable Sep 28 '20 at 05:47
  • 1
    Maybe "always the correct way" but it's still ghastly code. – DontPanic Sep 28 '20 at 19:58